jcs
/subtext
/amendments
/69
mail: Start on private mail support, among other minor changes
jcs made amendment 69 over 2 years ago
--- chat.c Sun Jan 2 10:39:22 2022
+++ chat.c Wed Jan 26 14:34:23 2022
@@ -182,7 +182,7 @@ chat_start(struct session *s, char *with_node)
for (;;) {
input = session_field_input(s, CHAT_MAX_INPUT - 1,
- s->terminal_columns - 1, 0);
+ s->terminal_columns - 1, NULL, 0);
if (!input)
break;
session_printf(s, "\r%s", ansi(s, ANSI_ERASE_LINE, ANSI_END));
--- console.c Sat Jan 22 20:52:46 2022
+++ console.c Thu Jan 27 13:41:36 2022
@@ -238,9 +238,20 @@ console_key_down(struct focusable *focusable, EventRec
k = (event->message & charCodeMask);
if (event->modifiers & optionKey) {
- /* this is only correct on a US keyboard */
- if (k == 0xc2) /* opt+l */
+ /* these are only correct on a US keyboard */
+ switch (k) {
+ case 0x8d: /* opt+C */
+ /* ^C */
+ console->session->ibuf[console->session->ibuflen++] = CONTROL_C;
+ break;
+ case 0xb6: /* opt+D */
+ /* ^D */
+ console->session->ibuf[console->session->ibuflen++] = CONTROL_D;
+ break;
+ case 0xc2: /* opt+L */
console_redraw(console, CONSOLE_FORCE_REDRAW);
+ break;
+ }
} else {
console->session->ibuf[console->session->ibuflen++] = k;
if (k == '\r')
--- db.c Sat Jan 22 20:30:23 2022
+++ db.c Sat Jan 29 18:43:39 2022
@@ -147,8 +147,6 @@ db_init(Str255 path, short vrefnum, struct bile *bile)
if (!was_new)
db_config_load(tdb);
- user_update_cache_map(tdb);
-
return tdb;
}
@@ -180,12 +178,12 @@ db_migrate(struct db *tdb, short is_new)
db_config_save(tdb);
/* create a default sysop user */
- user = user_build(tdb, "sysop", &error);
+ user = user_build("sysop", &error);
if (!user)
panic("Failed creating initial sysop user: %s", error);
- user_set_password(tdb, user, "p4ssw0rd");
+ user_set_password(user, "p4ssw0rd");
user->is_sysop = DB_TRUE;
- user_save(tdb, user);
+ user_save(user);
} else {
if (bile_read(tdb->bile, DB_VERS_RTYPE, 1, &ver, 1) != 1)
ver = 1;
--- db.h Sat Jan 22 20:23:41 2022
+++ db.h Fri Jan 28 14:50:34 2022
@@ -37,6 +37,7 @@
#define DB_BOARD_RTYPE 'BORD'
#define DB_FILEAREA_RTYPE 'FILA'
#define DB_VERS_RTYPE 'VERS'
+#define DB_MESSAGE_RTYPE 'PMSG'
#define DB_TEXT_TYPE 'TEXT'
#define DB_TEXT_MENU_ID 1
@@ -57,7 +58,7 @@ struct user {
/* keep this first so we can quickly read it during caching */
char username_key[DB_USERNAME_LENGTH + 1];
- short id;
+ unsigned long id;
char username[DB_USERNAME_LENGTH + 1];
char password_hash[SHA256_DIGEST_STRING_LENGTH + 1]; /* 66 */
char password_salt[SHA256_DIGEST_STRING_LENGTH + 1];
@@ -67,7 +68,7 @@ struct user {
};
struct user_map {
- short id;
+ unsigned long id;
char username_key[DB_USERNAME_LENGTH + 1];
};
--- mail.c Sat Jan 29 18:36:52 2022
+++ mail.c Sat Jan 29 18:36:52 2022
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2022 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ansi.h"
+#include "mail.h"
+#include "subtext.h"
+#include "session.h"
+#include "user.h"
+#include "uthread.h"
+#include "util.h"
+
+struct bile_object_field mail_object_fields[] = {
+ { offsetof(struct private_message, recipient_user_id),
+ member_size(struct private_message, recipient_user_id), -1 },
+ { offsetof(struct private_message, id),
+ member_size(struct private_message, id), -1 },
+ { offsetof(struct private_message, time),
+ member_size(struct private_message, time), -1 },
+ { offsetof(struct private_message, sender_user_id),
+ member_size(struct private_message, sender_user_id), -1 },
+ { offsetof(struct private_message, subject_size),
+ member_size(struct private_message, subject_size), -1 },
+ { offsetof(struct private_message, subject),
+ -1, offsetof(struct private_message, subject_size) },
+ { offsetof(struct private_message, body_size),
+ member_size(struct private_message, body_size), -1 },
+ { offsetof(struct private_message, body),
+ -1, offsetof(struct private_message, body_size) },
+ { offsetof(struct private_message, parent_message_id),
+ member_size(struct private_message, parent_message_id), -1 },
+};
+
+short mail_save(struct session *s, struct private_message *msg);
+size_t mail_find_for_user_id(unsigned long user_id,
+ unsigned long **mail_ids);
+
+void
+mail_menu(struct session *s)
+{
+ bool done = false;
+ unsigned char c;
+
+ if (!s->user) {
+ session_output_string(s, "Mail is not available to guests.\r\n"
+ "Please create an account first.\r\n");
+ session_flush(s);
+ return;
+ }
+
+ session_output_template(s, "{{B}}Private Mail Menu{{/}}\r\n"
+ "{{B}}---------{{/}}\r\n");
+
+mail_menu:
+ session_output_template(s, "({{B}}L{{/}})ist mail messages\r\n"
+ "({{B}}C{{/}})ompose new message\r\n"
+ "({{B}}Q{{/}})uit to main menu\r\n");
+ session_flush(s);
+
+ while (!done) {
+ session_output_template(s, "{{B}}Mail>{{/}} ");
+ session_flush(s);
+
+get_another_char:
+ c = session_input_char(s);
+ if (s->ending)
+ return;
+ if (c == '\r' || c == 0)
+ goto get_another_char;
+
+ session_printf(s, "%c\r\n", c);
+ session_flush(s);
+
+ switch (c) {
+ case 'c':
+ case 'C':
+ case 's':
+ case 'S':
+ mail_compose(s, NULL, NULL, NULL);
+ break;
+ case 'l':
+ case 'L':
+ mail_list(s, false);
+ break;
+ case 'Q':
+ case 'q':
+ case 'X':
+ case 'x':
+ done = true;
+ break;
+ case '?':
+ goto mail_menu;
+ }
+ }
+}
+
+void
+mail_compose(struct session *s, char *initial_to, char *initial_subject,
+ char *initial_body)
+{
+ struct user *to_user = NULL;
+ struct private_message msg = { 0 };
+ char *to_username = NULL, *tmp;
+ char c;
+
+ if (initial_to)
+ to_username = xstrdup(initial_to);
+ if (initial_subject)
+ msg.subject = xstrdup(initial_subject);
+ if (initial_body)
+ msg.body = xstrdup(initial_body);
+
+ session_output_template(s, "{{B}}Compose New Private Mail{{/}}\r\n"
+ "{{B}}------------{{/}}\r\n");
+ session_printf(s, "{{B}}From: {{/}} %s\r\n", s->user->username);
+ session_flush(s);
+
+mail_compose_start:
+ for (;;) {
+ session_output_template(s, "{{B}}To: {{/}} ");
+ session_flush(s);
+
+ tmp = session_field_input(s, DB_USERNAME_LENGTH + 1,
+ DB_USERNAME_LENGTH + 1, to_username, 0);
+ if (to_username != NULL)
+ free(to_username);
+ to_username = tmp;
+ session_output(s, "\r\n", 2);
+ session_flush(s);
+ if (to_username == NULL || to_username[0] == '\0')
+ goto mail_compose_done;
+
+ to_user = user_find_by_username(to_username);
+ if (to_user == NULL) {
+ session_printf(s, "{{B}}Error:{{/}} No such user \"%s\" "
+ "(^C to cancel)\r\n", to_username);
+ session_flush(s);
+ free(to_username);
+ to_username = NULL;
+ continue;
+ }
+ msg.recipient_user_id = to_user->id;
+ free(to_user);
+ break;
+ }
+
+ for (;;) {
+ session_output_template(s, "{{B}}Subject:{{/}} ");
+ session_flush(s);
+
+ tmp = session_field_input(s, 50, 50, msg.subject, 0);
+ if (msg.subject != NULL)
+ free(msg.subject);
+ msg.subject = tmp;
+ session_output(s, "\r\n", 2);
+ session_flush(s);
+ if (msg.subject == NULL)
+ goto mail_compose_done;
+ if (msg.subject[0] == '\0') {
+ session_output_template(s, "{{B}}Error:{{/}} Subject cannot "
+ "be blank (^C to cancel)\r\n");
+ session_flush(s);
+ free(msg.subject);
+ msg.subject = NULL;
+ continue;
+ }
+ msg.subject_size = strlen(msg.subject) + 1;
+ break;
+ }
+
+ for (;;) {
+ session_output_template(s,
+ "{{B}}Message (^D when finished):{{/}}\r\n");
+ session_flush(s);
+
+ tmp = session_multiline_input(s, s->terminal_columns - 1,
+ msg.body);
+ if (msg.body != NULL)
+ free(msg.body);
+ msg.body = tmp;
+ session_output(s, "\r\n", 2);
+ session_flush(s);
+ if (msg.body == NULL)
+ goto mail_compose_done;
+ if (msg.body[0] == '\0') {
+ session_output_template(s, "{{B}}Error:{{/}} Message cannot "
+ "be blank (^C to cancel)\r\n");
+ session_flush(s);
+ free(msg.body);
+ msg.body = NULL;
+ continue;
+ }
+ msg.body_size = strlen(msg.body) + 1;
+ break;
+ }
+
+ for (;;) {
+ session_output_template(s, "\r\n{{B}}(S){{/}}end message, "
+ "{{B}}(E){{/}}dit again, or {{B}}(C){{/}}ancel? ");
+ session_flush(s);
+
+ c = session_input_char(s);
+ if (c == 0 && s->obuflen > 0) {
+ s->node_funcs->output(s);
+ uthread_yield();
+ if (s->ending)
+ goto mail_compose_done;
+ continue;
+ }
+
+ session_printf(s, "%c\r\n", c);
+ session_flush(s);
+
+ switch (c) {
+ case 's':
+ case 'S':
+ case 'y':
+ case '\n':
+ case '\r':
+ /* send */
+ session_output_string(s, "Sending mail...\r\n");
+ session_flush(s);
+
+ msg.time = Time;
+ msg.sender_user_id = s->user->id;
+ mail_save(s, &msg);
+
+ goto mail_compose_done;
+ break;
+ case 'e':
+ case 'E':
+ goto mail_compose_start;
+ case 'c':
+ case 'C':
+ case CONTROL_C:
+ goto mail_compose_done;
+ }
+ }
+
+mail_compose_done:
+ if (to_username)
+ free(to_username);
+ if (msg.subject)
+ free(msg.subject);
+ if (msg.body)
+ free(msg.body);
+}
+
+void
+mail_list(struct session *s, bool sent)
+{
+ unsigned long *mail_ids = NULL;
+ size_t nmsgs, n, size;
+ struct private_message msg;
+ struct user *user;
+ char *data;
+
+ nmsgs = mail_find_for_user_id(s->user->id, &mail_ids);
+ if (nmsgs == 0) {
+ session_output_string(s, "No messages.\r\n");
+ session_flush(s);
+ if (mail_ids)
+ free(mail_ids);
+ return;
+ }
+
+ for (n = 0; n < nmsgs; n++) {
+ size = bile_read_alloc(db->bile, DB_MESSAGE_RTYPE, mail_ids[n],
+ &data);
+ bile_unmarshall_object(db->bile, mail_object_fields,
+ nitems(mail_object_fields), data, size, &msg);
+ user = user_find(msg.sender_user_id);
+
+ session_printf(s, "%s%c [% 3ld] %s % 16s %s%s\r\n",
+ msg.read ? ansi(s, ANSI_BOLD, ANSI_END) : "",
+ msg.read ? 'N' : ' ',
+ n + 1,
+ "(time)",
+ user ? user->username : "(unknown)",
+ msg.subject,
+ msg.read ? ansi(s, ANSI_RESET, ANSI_END) : "");
+
+ if (msg.subject)
+ free(msg.subject);
+ if (msg.body)
+ free(msg.body);
+ free(data);
+ }
+
+ free(mail_ids);
+}
+
+short
+mail_save(struct session *s, struct private_message *msg)
+{
+ size_t size;
+ char *data;
+ short ret;
+
+ if (!msg->id)
+ msg->id = bile_next_id(db->bile, DB_MESSAGE_RTYPE);
+
+ ret = bile_marshall_object(db->bile, mail_object_fields,
+ nitems(mail_object_fields), msg, &data, &size);
+ if (ret != 0 || size == 0) {
+ warn("mail_save: failed to marshall object");
+ return -1;
+ }
+
+ bile_write(db->bile, DB_MESSAGE_RTYPE, msg->id, data, size);
+}
+
+size_t
+mail_find_for_user_id(unsigned long user_id, unsigned long **mail_ids)
+{
+ unsigned long msg_user_id;
+ struct user_map *muser;
+ struct bile_object *o;
+ size_t nmsg, msgs_for_user, mail_ids_size;
+
+ mail_ids_size = 256;
+ *mail_ids = xmalloc(mail_ids_size);
+
+ msgs_for_user = 0;
+ nmsg = 0;
+ while ((o = bile_get_nth_of_type(db->bile, nmsg, DB_MESSAGE_RTYPE))) {
+ bile_read(db->bile, DB_MESSAGE_RTYPE, o->id, (char *)&msg_user_id,
+ sizeof(msg_user_id));
+ if (msg_user_id == user_id) {
+ EXPAND_TO_FIT(*mail_ids, mail_ids_size,
+ msgs_for_user * sizeof(long), sizeof(long), 256);
+ (*mail_ids)[msgs_for_user++] = o->id;
+ }
+ free(o);
+ nmsg++;
+ }
+
+ return msgs_for_user;
+}
--- mail.h Sat Jan 29 17:29:30 2022
+++ mail.h Sat Jan 29 17:29:30 2022
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __MAIL_H__
+#define __MAIL_H__
+
+#include "bile.h"
+#include "session.h"
+
+struct private_message {
+ /* key */
+ unsigned long recipient_user_id;
+
+ unsigned long id;
+ time_t time;
+ time_t read;
+ unsigned long sender_user_id;
+ size_t subject_size;
+ char *subject;
+ size_t body_size;
+ char *body;
+ unsigned long parent_message_id;
+};
+
+extern struct bile_object_field mail_object_fields[];
+
+void mail_menu(struct session *s);
+void mail_compose(struct session *s, char *to, char *subject, char *body);
+void mail_list(struct session *s, bool sent);
+
+#endif
--- main.c Sun Jan 23 13:45:16 2022
+++ main.c Sat Jan 29 18:46:06 2022
@@ -23,6 +23,7 @@
#include "session.h"
#include "settings.h"
#include "telnet.h"
+#include "user.h"
#include "uthread.h"
#include "util.h"
@@ -85,7 +86,9 @@ main(void)
if (!db)
panic("Failed to open database");
-
+
+ user_update_cache_map();
+
if (db->config.telnet_port)
telnet_init();
@@ -96,6 +99,7 @@ main(void)
uthread_coordinate();
+ SystemTask();
if (!GetNextEvent(everyEvent, &event))
continue;
--- session.c Wed Jan 26 13:38:30 2022
+++ session.c Sat Jan 29 18:38:39 2022
@@ -30,13 +30,6 @@
#include "console.h"
-#define EXPAND_TO_FIT(var, size, pos, len) { \
- if (((pos) + (len)) >= (size)) { \
- (size) += 256; \
- (var) = xrealloc((var), (size)); \
- } \
-}
-
struct session **sessions = NULL;
short nsessions = 0;
@@ -88,7 +81,7 @@ session_run(struct uthread *uthread, void *arg)
if (s->node_funcs->setup)
s->node_funcs->setup(s);
-
+
if (!session_output_view(s, DB_TEXT_ISSUE_ID)) {
session_printf(s, "\r\n"
"Welcome to %s (%s)\r\n"
@@ -342,7 +335,7 @@ wait_for_char:
return 0;
if (session->ibuflen != 0)
break;
- if (session->obuflen != 0)
+ if (session->obuflen != 0 && session->chatting)
return 0;
uthread_yield();
}
@@ -372,7 +365,7 @@ wait_for_char:
*/
char *
session_field_input(struct session *session, unsigned short size,
- unsigned short width, char mask)
+ unsigned short width, char *initial_input, char mask)
{
short ilen = 0, ipos = 0, lastlen = 0;
char *field;
@@ -382,6 +375,13 @@ session_field_input(struct session *session, unsigned
field = xmalloczero(size);
+ if (initial_input) {
+ ipos = strlcpy(field, initial_input, size);
+ /* TODO: handle initial value being longer than width */
+ session_output_string(session, field);
+ session_flush(session);
+ }
+
for (;;) {
c = session_input_char(session);
if (session->ending || session->abort_input)
@@ -395,6 +395,8 @@ session_field_input(struct session *session, unsigned
goto field_input_bail;
}
break;
+ case CONTROL_C: /* ^C */
+ goto field_input_bail;
case 8: /* backspace / ^H */
case 127: /* delete (through telnet) */
if (ipos == 0)
@@ -446,6 +448,86 @@ field_input_bail:
return NULL;
}
+char *
+session_multiline_input(struct session *session, short cols,
+ char *initial_body)
+{
+ size_t size = 0, length = 0;
+ size_t pos = 0;
+ char *field = NULL;
+ unsigned char c, redraw = 0;
+
+ session->abort_input = 0;
+
+ if (initial_body) {
+ size = length = strlen(initial_body);
+ field = xmalloc(size + 1);
+ pos = strlcpy(field, initial_body, size + 1);
+ /* TODO: handle initial value being longer than width */
+ session_output_string(session, field);
+ session_flush(session);
+ }
+
+ for (;;) {
+ c = session_input_char(session);
+ if (session->ending || session->abort_input)
+ goto multiline_input_bail;
+ switch (c) {
+ case 0: /* session_input_char bailed */
+ if (session->obuflen > 0) {
+ session->node_funcs->output(session);
+ uthread_yield();
+ if (session->ending)
+ goto multiline_input_bail;
+ }
+ break;
+ case CONTROL_C: /* ^C */
+ goto multiline_input_bail;
+ case CONTROL_D: /* ^D */
+ return field;
+ case 8: /* backspace / ^H */
+ case 127: /* delete (through telnet) */
+ if (pos == 0)
+ continue;
+
+ if (pos < length) {
+ /* l:3 p:2 f:ab[cursor]c -> l:2 p:2 f:a[cursor]c */
+ redraw = 1;
+ BlockMove((Ptr)(field + pos), (Ptr)(field + pos - 1),
+ length - 1);
+ }
+
+ pos--;
+ length--;
+ field[pos] = '\0';
+
+ if (redraw) {
+ /* TODO */
+ } else {
+ session_output_string(session,
+ ansi(session, ANSI_BACKSPACE, ANSI_END));
+ session_flush(session);
+ }
+
+ break;
+ default:
+ if (c < 32 || c > 127)
+ break;
+ EXPAND_TO_FIT(field, size, length, 1, 64);
+ field[pos] = c;
+ pos++;
+ length++;
+ field[pos] = '\0';
+ session_output(session, (char *)&c, 1);
+ session_flush(session);
+ }
+ }
+
+multiline_input_bail:
+ free(field);
+ return NULL;
+}
+
short
session_login(struct session *s)
{
@@ -465,7 +547,7 @@ session_login(struct session *s)
} else {
session_flush(s);
username = session_field_input(s, DB_USERNAME_LENGTH + 1,
- DB_USERNAME_LENGTH, 0);
+ DB_USERNAME_LENGTH, NULL, 0);
session_output(s, "\r\n", 2);
session_flush(s);
@@ -488,7 +570,7 @@ session_login(struct session *s)
strcmp(username, "new") == 0) {
/* TODO: check for open signups */
} else {
- user = user_find(db, username);
+ user = user_find_by_username(username);
}
if (s->autologin_username[0]) {
@@ -504,7 +586,7 @@ session_login(struct session *s)
session_output_string(s, "Password: ");
session_flush(s);
- password = session_field_input(s, 64, 64, '*');
+ password = session_field_input(s, 64, 64, NULL, '*');
session_output(s, "\r\n", 2);
session_flush(s);
@@ -512,7 +594,7 @@ session_login(struct session *s)
goto login_bail;
if (user) {
- if (user_authenticate(db, user, password) == AUTH_USER_OK)
+ if (user_authenticate(user, password) == AUTH_USER_OK)
s->user = user;
} else
/* kill some time */
@@ -646,11 +728,11 @@ session_expand_template(struct session *session, const
varlen = 0;
n++;
} else if (tmpl[n] == '\r' && tmpl[n + 1] != '\n') {
- EXPAND_TO_FIT(*ret, retsize, retpos, 2);
+ EXPAND_TO_FIT(*ret, retsize, retpos, 2, 64);
(*ret)[retpos++] = '\r';
(*ret)[retpos++] = '\n';
} else {
- EXPAND_TO_FIT(*ret, retsize, retpos, 1);
+ EXPAND_TO_FIT(*ret, retsize, retpos, 1, 64);
(*ret)[retpos++] = tmpl[n];
}
@@ -750,7 +832,7 @@ expand_var:
curvar[vallen++] = ' ';
curvar[vallen] = '\0';
}
- EXPAND_TO_FIT(*ret, retsize, retpos, vallen);
+ EXPAND_TO_FIT(*ret, retsize, retpos, vallen, 128);
memcpy(*ret + retpos, curvar, vallen);
retpos += vallen;
}
--- session.h Mon Jan 24 11:07:42 2022
+++ session.h Thu Jan 27 13:53:41 2022
@@ -33,6 +33,10 @@ enum session_input_state {
#define GUEST_USERNAME "guest"
+#define CONTROL_C 3
+#define CONTROL_D 4
+#define CONTROL_L 12
+
struct node_funcs {
void (*setup)(struct session *session);
short (*input)(struct session *session);
@@ -83,7 +87,7 @@ struct session {
extern struct session **sessions;
extern short nsessions;
-struct session *session_create(char *node, char *via,
+struct session * session_create(char *node, char *via,
struct node_funcs *node_funcs);
void session_close(struct session *session);
void session_idle(struct session *session);
@@ -96,9 +100,11 @@ size_t session_output_view(struct session *session, sh
size_t session_output_template(struct session *session, const char *str);
size_t session_expand_template(struct session *session, const char *str,
char **ret);
-char *session_field_input(struct session *session, unsigned short size,
- unsigned short width, char mask);
-char *session_bar(struct session *s, char *left_str, char *right_str);
+char * session_field_input(struct session *session, unsigned short size,
+ unsigned short width, char *initial_input, char mask);
+char * session_multiline_input(struct session *session, short cols,
+ char *initial_body);
+char * session_bar(struct session *s, char *left_str, char *right_str);
void session_pause_return(struct session *s, short enforce, char *msg);
void session_who(struct session *s);
--- settings.c Mon Jan 24 10:32:19 2022
+++ settings.c Thu Jan 27 16:00:22 2022
@@ -28,8 +28,6 @@ enum {
CONFIG_TYPE_SHORT
};
-#define member_size(type, member) sizeof(((type *)0)->member)
-
struct config_field {
char name[32];
short type;
--- user.c Fri Jan 14 20:43:31 2022
+++ user.c Sat Jan 29 18:41:08 2022
@@ -25,29 +25,29 @@
#include "util.h"
void
-user_update_cache_map(struct db *tdb)
+user_update_cache_map(void)
{
struct user_map *muser;
struct bile_object *o;
size_t nuser, len, j, n;
- if (tdb->user_map != NULL)
- free(tdb->user_map);
+ if (db->user_map != NULL)
+ free(db->user_map);
- tdb->nusers = bile_count_by_type(tdb->bile, DB_USER_RTYPE);
- tdb->user_map = xmalloczero(sizeof(struct user_map) * tdb->nusers);
+ db->nusers = bile_count_by_type(db->bile, DB_USER_RTYPE);
+ db->user_map = xmalloczero(sizeof(struct user_map) * db->nusers);
nuser = 0;
- while ((o = bile_get_nth_of_type(tdb->bile, nuser, DB_USER_RTYPE))) {
- muser = &tdb->user_map[nuser];
+ while ((o = bile_get_nth_of_type(db->bile, nuser, DB_USER_RTYPE))) {
+ muser = &db->user_map[nuser];
muser->id = o->id;
/* the lowercased username should be the first bytes of each rec */
- len = bile_read(tdb->bile, DB_USER_RTYPE, o->id,
+ len = bile_read(db->bile, DB_USER_RTYPE, o->id,
muser->username_key, sizeof(muser->username_key));
if (len != sizeof(muser->username_key))
panic("user_update_cache_map: can't read user %lu: %d", o->id,
- bile_error(tdb->bile));
+ bile_error(db->bile));
free(o);
nuser++;
@@ -55,7 +55,7 @@ user_update_cache_map(struct db *tdb)
}
struct user *
-user_build(struct db *tdb, char *username, char **error)
+user_build(char *username, char **error)
{
struct user suser;
struct user *user;
@@ -75,7 +75,7 @@ user_build(struct db *tdb, char *username, char **erro
}
}
- if ((user = user_find(tdb, username))) {
+ if ((user = user_find_by_username(username))) {
free(user);
*error = xstrdup("username already exists");
return NULL;
@@ -89,12 +89,12 @@ user_build(struct db *tdb, char *username, char **erro
}
void
-user_save(struct db *tdb, struct user *user)
+user_save(struct user *user)
{
size_t len, n;
if (!user->id) {
- user->id = bile_next_id(tdb->bile, DB_USER_RTYPE);
+ user->id = bile_next_id(db->bile, DB_USER_RTYPE);
user->created_at = Time;
}
@@ -104,17 +104,36 @@ user_save(struct db *tdb, struct user *user)
for (n = 0; n < len; n++)
user->username_key[n] = tolower(user->username[n]);
- len = bile_write(tdb->bile, DB_USER_RTYPE, user->id,
+ len = bile_write(db->bile, DB_USER_RTYPE, user->id,
user, sizeof(struct user));
if (len != sizeof(struct user))
- panic("user_save: failed to write: %d", bile_error(tdb->bile));
+ panic("user_save: failed to write: %d", bile_error(db->bile));
- user_update_cache_map(tdb);
+ user_update_cache_map();
}
struct user *
-user_find(struct db *tdb, char *username)
+user_find(unsigned long id)
{
+ struct user *user;
+ char *data;
+ size_t len;
+
+ len = bile_read_alloc(db->bile, DB_USER_RTYPE, id, &data);
+ if (len == 0 || data == NULL)
+ return NULL;
+ if (len != sizeof(struct user))
+ panic("user_find: bad user record size %lu != %lu", len,
+ sizeof(struct user));
+ user = xmalloczero(sizeof(struct user));
+ memcpy(user, data, sizeof(struct user));
+ free(data);
+ return user;
+}
+
+struct user *
+user_find_by_username(const char *username)
+{
struct user suser, *user;
struct user_map *muser;
char lusername[DB_USERNAME_LENGTH + 1] = { 0 };
@@ -130,27 +149,18 @@ user_find(struct db *tdb, char *username)
for (n = 0; n < len; n++)
lusername[n] = tolower(username[n]);
- for (n = 0; n < tdb->nusers; n++) {
- muser = &tdb->user_map[n];
+ for (n = 0; n < db->nusers; n++) {
+ muser = &db->user_map[n];
- if (strcmp(muser->username_key, lusername) == 0) {
- len = bile_read_alloc(tdb->bile, DB_USER_RTYPE, muser->id,
- &data);
- if (len != sizeof(struct user))
- panic("user_find: bad user record size %lu != %lu",
- len, sizeof(struct user));
- user = xmalloczero(sizeof(struct user));
- memcpy(user, data, sizeof(struct user));
- free(data);
- return user;
- }
+ if (strcmp(muser->username_key, lusername) == 0)
+ return user_find(muser->id);
}
return NULL;
}
short
-user_authenticate(struct db *tdb, struct user *user, char *password)
+user_authenticate(struct user *user, const char *password)
{
char hash[SHA256_DIGEST_STRING_LENGTH];
char *salted;
@@ -182,7 +192,7 @@ user_authenticate(struct db *tdb, struct user *user, c
}
void
-user_set_password(struct db *tdb, struct user *user, char *password)
+user_set_password(struct user *user, const char *password)
{
char hash[SHA256_DIGEST_STRING_LENGTH];
char salt[SHA256_DIGEST_STRING_LENGTH];
--- user.h Wed Jan 5 20:54:49 2022
+++ user.h Sat Jan 29 18:32:10 2022
@@ -22,11 +22,12 @@
#define AUTH_USER_OK 1
#define AUTH_USER_FAILED 2
-void user_update_cache_map(struct db *tdb);
-struct user * user_build(struct db *tdb, char *username, char **error);
-void user_save(struct db *tdb, struct user *user);
-struct user * user_find(struct db *tdb, char *username);
-short user_authenticate(struct db *tdb, struct user *user, char *password);
-void user_set_password(struct db *tdb, struct user *user, char *password);
+void user_update_cache_map(void);
+struct user * user_build(char *username, char **error);
+void user_save(struct user *user);
+struct user * user_find(unsigned long id);
+struct user * user_find_by_username(const char *username);
+short user_authenticate(struct user *user, const char *password);
+void user_set_password(struct user *user, const char *password);
#endif