AmendHub

Download:

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