AmendHub

jcs

/

subtext

/

amendments

/

99

settings: Move to interactive text-based menu for sysops

This will allow remote administration and have a reusable component
for editing structs for users, boards, etc.

jcs made amendment 99 8 months ago
--- db.c Wed Apr 20 21:59:38 2022 +++ db.c Thu May 12 15:29:53 2022 @@ -24,6 +24,34 @@ #include "user.h" #include "util.h" +struct struct_field config_fields[] = { + { "BBS Name", CONFIG_TYPE_STRING, + offsetof(struct config, name), + 1, member_size(struct config, name) }, + { "Phone Number", CONFIG_TYPE_STRING, + offsetof(struct config, phone_number), + 1, member_size(struct config, phone_number) }, + { "Location", CONFIG_TYPE_STRING, + offsetof(struct config, location), + 1, member_size(struct config, location) }, + { "Hostname", CONFIG_TYPE_STRING, + offsetof(struct config, hostname), + 1, member_size(struct config, hostname) }, + { "Telnet Port", CONFIG_TYPE_SHORT, + offsetof(struct config, telnet_port), + 0, 65535 }, + { "Modem Port", CONFIG_TYPE_SHORT, + offsetof(struct config, modem_port), + 0, 2 }, + { "Modem Init String", CONFIG_TYPE_STRING, + offsetof(struct config, modem_init), + 1, member_size(struct config, modem_init) }, + { "Max Idle Minutes", CONFIG_TYPE_SHORT, + offsetof(struct config, max_idle_minutes), + 1, 65535 }, +}; +size_t nconfig_fields = nitems(config_fields); + struct db * db_init(Str255 file, short vrefnum, struct bile *bile); short db_migrate(struct db *tdb, short is_new); void db_config_load(struct db *tdb); --- db.h Sun Apr 10 09:19:45 2022 +++ db.h Thu May 12 15:30:48 2022 @@ -19,10 +19,6 @@ #include <time.h> -#include "subtext.h" -#include "bile.h" -#include "sha2.h" - #define SUBTEXT_CREATOR 'SUBT' #define DB_TYPE 'STDB' @@ -46,9 +42,17 @@ #define DB_USERNAME_LENGTH 16 +#define DB_BOARD_NAME_LENGTH 32 +#define DB_BOARD_DESCR_LENGTH 100 + #define SL_TYPE 'STSL' #define SL_LOG_RTYPE 'SLOG' +#include "subtext.h" +#include "settings.h" +#include "bile.h" +#include "sha2.h" + struct config { char name[32]; char phone_number[32]; @@ -60,6 +64,9 @@ struct config { short max_idle_minutes; }; +extern struct struct_field config_fields[]; +extern size_t nconfig_fields; + struct user { /* keep this first so we can quickly read it during caching */ char username_key[DB_USERNAME_LENGTH + 1]; @@ -71,6 +78,17 @@ struct user { unsigned long created_at; unsigned long last_seen_at; short is_sysop; +}; + +struct board { + unsigned long id; + char name[DB_BOARD_NAME_LENGTH]; + char description[DB_BOARD_DESCR_LENGTH]; + short restricted_posting; + short restricted_viewing; + short retention_days; + unsigned long last_post_at; + unsigned long post_count; }; struct user_map { --- mail.c Thu Apr 28 17:05:12 2022 +++ mail.c Mon May 9 15:48:26 2022 @@ -78,7 +78,7 @@ short mail_get_message_id(struct session *s, size_t nmsgs, char *prompt, short initial) { - char *tmp; + char *tmp = NULL; char start_s[10] = { 0 }; short ret; @@ -87,22 +87,26 @@ mail_get_message_id(struct session *s, size_t nmsgs, c get_id: if (prompt) - session_printf(s, "{{B}}%s:{{/}} ", prompt); + session_printf(s, "{{B}}%s:{{/B}} ", prompt); tmp = session_field_input(s, 5, 5, start_s, 0); - if (tmp == NULL) - return -1; + if (tmp == NULL || s->ending) { + ret = -1; + goto get_id_done; + } session_output(s, "\r\n", 2); session_flush(s); if (sscanf(tmp, "%d", &ret) != 1 || ret < 1 || ret > nmsgs) { - session_printf(s, "{{B}}Invalid message ID{{/}} (^C to cancel)\r\n"); + session_printf(s, "{{B}}Invalid message ID{{/B}} (^C to cancel)\r\n"); session_flush(s); free(tmp); goto get_id; } - - free(tmp); + +get_id_done: + if (tmp) + free(tmp); return ret; } @@ -124,15 +128,15 @@ mail_menu(struct session *s) return; } - session_output_template(s, "{{B}}Private Mail{{/}} " - "('{{B}}?{{/}}' for help)\r\n" - "{{B}}--------------------{{/}}\r\n"); + session_output_template(s, "{{B}}Private Mail{{/B}} " + "('{{B}}?{{/B}}' for help)\r\n" + "{{B}}--------------------{{/B}}\r\n"); session_flush(s); mail_list(s, false, &mail_ids, &nmsgs); while (!done) { - session_output_template(s, "{{B}}Mail>{{/}} "); + session_output_template(s, "{{B}}Mail>{{/B}} "); session_flush(s); get_menu_option: @@ -195,7 +199,7 @@ get_menu_option: break; default: session_output_template(s, - "Invalid option ({{B}}?{{/}} for help)\r\n"); + "Invalid option ({{B}}?{{/B}} for help)\r\n"); session_flush(s); } } @@ -205,12 +209,12 @@ void mail_help(struct session *s) { session_output_template(s, - "{{B}}L{{/}}: List mail messages\r\n" - "{{B}}#{{/}}: Read mail message [#]\r\n" - "{{B}}D #{{/}}: Delete mail message [#]\r\n" - "{{B}}U #{{/}}: Mark mail message [#] unread\r\n" - "{{B}}C{{/}}: Compose new mail message\r\n" - "{{B}}Q{{/}}: Return to main menu\r\n"); + "{{B}}L{{/B}}: List mail messages\r\n" + "{{B}}#{{/B}}: Read mail message [#]\r\n" + "{{B}}D #{{/B}}: Delete mail message [#]\r\n" + "{{B}}U #{{/B}}: Mark mail message [#] unread\r\n" + "{{B}}C{{/B}}: Compose new mail message\r\n" + "{{B}}Q{{/B}}: Return to main menu\r\n"); session_flush(s); } @@ -230,14 +234,14 @@ mail_compose(struct session *s, char *initial_to, char 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_output_template(s, "{{B}}Compose New Private Mail{{/B}}\r\n" + "{{B}}------------{{/B}}\r\n"); + session_printf(s, "{{B}}From: {{/B}} %s\r\n", s->user->username); session_flush(s); mail_compose_start: for (;;) { - session_output_template(s, "{{B}}To: {{/}} "); + session_output_template(s, "{{B}}To: {{/B}} "); session_flush(s); tmp = session_field_input(s, DB_USERNAME_LENGTH + 1, @@ -252,7 +256,7 @@ mail_compose_start: to_user = user_find_by_username(to_username); if (to_user == NULL) { - session_printf(s, "{{B}}Error:{{/}} No such user \"%s\" " + session_printf(s, "{{B}}Error:{{/B}}{{#}} No such user \"%s\" " "(^C to cancel)\r\n", to_username); session_flush(s); free(to_username); @@ -265,7 +269,7 @@ mail_compose_start: } for (;;) { - session_output_template(s, "{{B}}Subject:{{/}} "); + session_output_template(s, "{{B}}Subject:{{/B}} "); session_flush(s); tmp = session_field_input(s, 50, 50, msg.subject, 0); @@ -277,7 +281,7 @@ mail_compose_start: if (msg.subject == NULL) goto mail_compose_done; if (msg.subject[0] == '\0') { - session_output_template(s, "{{B}}Error:{{/}} Subject cannot " + session_output_template(s, "{{B}}Error:{{/B}} Subject cannot " "be blank (^C to cancel)\r\n"); session_flush(s); free(msg.subject); @@ -290,7 +294,7 @@ mail_compose_start: for (;;) { session_output_template(s, - "{{B}}Message (^D when finished):{{/}}\r\n"); + "{{B}}Message (^D when finished):{{/B}}\r\n"); session_flush(s); tmp = session_multiline_input(s, s->terminal_columns - 1, @@ -303,7 +307,7 @@ mail_compose_start: if (msg.body == NULL) goto mail_compose_done; if (msg.body[0] == '\0') { - session_output_template(s, "{{B}}Error:{{/}} Message cannot " + session_output_template(s, "{{B}}Error:{{/B}} Message cannot " "be blank (^C to cancel)\r\n"); session_flush(s); free(msg.body); @@ -315,8 +319,8 @@ mail_compose_start: } for (;;) { - session_output_template(s, "\r\n{{B}}(S){{/}}end message, " - "{{B}}(E){{/}}dit again, or {{B}}(C){{/}}ancel? "); + session_output_template(s, "\r\n{{B}}(S){{/B}}end message, " + "{{B}}(E){{/B}}dit again, or {{B}}(C){{/B}}ancel? "); session_flush(s); c = session_input_char(s); @@ -393,7 +397,7 @@ mail_list(struct session *s, bool sent, unsigned long strftime(time, sizeof(time), "%Y-%m-%d %H:%M", localtime(&msg.time)); - session_printf(s, "%s%c [%- 3ld] %s %- 10s %- 40s%s\r\n", + session_printf(s, "%s%c [%- 3ld] %s %- 10s {{#}}%- 40s%s\r\n", msg.read ? "" : ansi(s, ANSI_BOLD, ANSI_END), msg.read ? ' ' : 'N', n + 1, @@ -426,12 +430,12 @@ mail_read(struct session *s, unsigned long id) strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", localtime(&msg.time)); - session_printf(s, "{{B}}From:{{/}} %s\r\n", + session_printf(s, "{{B}}From:{{/B}} %s\r\n", sender ? sender->username : "(unknown)"); - session_printf(s, "{{B}}To:{{/}} %s\r\n", + session_printf(s, "{{B}}To:{{/B}} %s\r\n", recipient ? recipient->username : "(unknown)"); - session_printf(s, "{{B}}Date:{{/}} %s\r\n", time); - session_printf(s, "{{B}}Subject:{{/}} %s\r\n", msg.subject); + session_printf(s, "{{B}}Date:{{/B}} %s\r\n", time); + session_printf(s, "{{B}}Subject:{{/B}}{{#}} %s\r\n", msg.subject); session_flush(s); session_output_string(s, "\r\n"); session_output(s, msg.body, msg.body_size); @@ -455,14 +459,14 @@ mail_delete(struct session *s, unsigned long idx) nmsgs = mail_find_for_user_id(s->user->id, &mail_ids); if (idx > nmsgs) { - session_printf(s, "Invalid message id {{B}}%ld{{/}}\r\n", idx); + session_printf(s, "Invalid message id {{B}}%ld{{/B}}\r\n", idx); goto delete_done; } if (bile_delete(db->bile, DB_MESSAGE_RTYPE, mail_ids[idx - 1]) == 0) - session_printf(s, "Deleted message {{B}}%ld{{/}}\r\n", idx); + session_printf(s, "Deleted message {{B}}%ld{{/B}}\r\n", idx); else - session_printf(s, "Failed deleting message {{B}}%ld{{/}}! " + session_printf(s, "Failed deleting message {{B}}%ld{{/B}}! " "(%d)\r\n", idx, bile_error(db->bile)); delete_done: @@ -480,7 +484,7 @@ mail_mark_unread(struct session *s, unsigned long idx) nmsgs = mail_find_for_user_id(s->user->id, &mail_ids); if (idx > nmsgs) { - session_printf(s, "Invalid message id {{B}}%ld{{/}}\r\n", idx); + session_printf(s, "Invalid message id {{B}}%ld{{/B}}\r\n", idx); goto unread_done; } @@ -490,10 +494,10 @@ mail_mark_unread(struct session *s, unsigned long idx) nitems(mail_object_fields), data, size, &msg); msg.read = 0; if (mail_save(s, &msg) == 0) - session_printf(s, "Marked message {{B}}%ld{{/}} unread\r\n", + session_printf(s, "Marked message {{B}}%ld{{/B}} unread\r\n", idx); else - session_printf(s, "Failed marking message {{B}}%ld{{/}} " + session_printf(s, "Failed marking message {{B}}%ld{{/B}} " "unread!\r\n", idx); mail_free_message_strings(&msg); --- main.c Thu Apr 14 15:33:17 2022 +++ main.c Mon May 9 15:39:21 2022 @@ -279,9 +279,6 @@ handle_menu(long menu_id) break; case BBS_MENU_ID: switch (LoWord(menu_id)) { - case BBS_MENU_SETTINGS_ID: - settings_edit(); - break; case BBS_MENU_OPEN_CONSOLE_ID: console_init(); break; --- session.c Wed Apr 27 17:41:47 2022 +++ session.c Thu May 12 17:40:55 2022 @@ -20,10 +20,12 @@ #include <string.h> #include "ansi.h" +#include "board.h" #include "chat.h" #include "mail.h" #include "subtext.h" #include "session.h" +#include "settings.h" #include "signup.h" #include "user.h" #include "user_settings.h" @@ -40,7 +42,11 @@ char sessions_tally_day[9] = { 0 }; void session_run(struct uthread *uthread, void *arg); short session_login(struct session *s); -size_t session_expand_var(struct session *session, char *ivar, char **ret); +size_t session_expand_var(struct session *session, char *ivar, char **ret, + bool *end_expansion); +void session_page_sysop(struct session *s); +void session_answer_page(struct session *s); +void sysop_edit_settings(struct session *s); struct session * session_create(char *node, char *via, struct node_funcs *node_funcs) @@ -81,7 +87,7 @@ session_run(struct uthread *uthread, void *arg) /* until we negotiate otherwise */ s->terminal_columns = DEFAULT_TERMINAL_COLUMNS; s->terminal_lines = DEFAULT_TERMINAL_LINES; - snprintf(s->terminal_type, sizeof(s->terminal_type), "xterm-color"); + snprintf(s->terminal_type, sizeof(s->terminal_type), "vt100"); if (s->node_funcs->setup) s->node_funcs->setup(s); @@ -124,7 +130,7 @@ session_run(struct uthread *uthread, void *arg) sessions_tally++; session_printf(s, "\r\n" - "Welcome, {{B}}%s{{/}}, you are the %d%s caller today.\r\n", + "Welcome, {{B}}%s{{/B}}, you are the %d%s caller today.\r\n", s->user ? s->user->username : GUEST_USERNAME, sessions_tally, ordinal(sessions_tally)); session_flush(s); @@ -135,7 +141,7 @@ main_menu: session_flush(s); while (!done && !s->ending) { - session_printf(s, "{{B}}Main Menu>{{/}} "); + session_printf(s, "{{B}}Main Menu>{{/B}} "); session_flush(s); get_another_char: @@ -170,13 +176,20 @@ get_another_char: case 'M': mail_menu(s); break; + case 'p': + case 'P': + if (s->user && s->user->is_sysop) + session_answer_page(s); + else + session_page_sysop(s); + break; case 's': case 'S': if (s->user) user_settings_menu(s); else if ((s->user = signup(s))) { session_printf(s, "\r\n" - "Welcome, {{B}}%s{{/}}!\r\n", s->user->username); + "Welcome, {{B}}%s{{/B}}!\r\n", s->user->username); goto main_menu; } break; @@ -185,6 +198,26 @@ get_another_char: /* who's online */ session_who(s); break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '0': + board_view(s, c - '0'); + break; + case '!': + if (!s->user || !s->user->is_sysop) { + session_output_string(s, "Invalid option\r\n"); + session_flush(s); + break; + } + sysop_edit_settings(s); + break; case '?': /* short menu */ if (!session_output_view(s, DB_TEXT_SHORTMENU_ID)) { @@ -427,7 +460,7 @@ wait_for_char: return 0; } - if (session->ibuf[0] == '\33') { + if (session->ibuf[0] == ESC) { /* look for a VT100 escape sequence */ if (session->ibuflen == 1) { waiting_for = 2; @@ -723,7 +756,7 @@ session_login(struct session *s) session_output_string(s, "login: "); if (s->autologin_username[0]) { - session_printf(s, "%s\r\n", s->autologin_username); + session_printf(s, "{{#}}%s\r\n", s->autologin_username); session_flush(s); username = xstrdup(s->autologin_username); } else { @@ -900,6 +933,7 @@ session_expand_template(struct session *session, const size_t vallen; short n, quote, invar = 0, varlen = 0, doif, sep; char *varseek, *curvarpos, *val; + bool end_expansion = false; retsize = 0; retpos = 0; @@ -915,7 +949,7 @@ session_expand_template(struct session *session, const goto expand_var; } else if (varlen < sizeof(curvar) - 2) curvar[varlen++] = tmpl[n]; - } else if (tmpl[n] == '{' && tmpl[n + 1] == '{') { + } else if (!end_expansion && tmpl[n] == '{' && tmpl[n + 1] == '{') { invar = 1; varlen = 0; n++; @@ -983,9 +1017,11 @@ expand_var: while (matchvar[sep - 1] == ' ') matchvar[--sep] = '\0'; - vallen = session_expand_var(session, matchvar, &val); + vallen = session_expand_var(session, matchvar, &val, + &end_expansion); } else { - vallen = session_expand_var(session, curvar, &val); + vallen = session_expand_var(session, curvar, &val, + &end_expansion); } if (vallen) { @@ -1000,7 +1036,8 @@ expand_var: } size_t -session_expand_var(struct session *session, char *ivar, char **ret) +session_expand_var(struct session *session, char *ivar, char **ret, + bool *end_expansion) { static char var[128], retval[128]; size_t retsize, retlen, varlen, mcount; @@ -1034,9 +1071,12 @@ session_expand_var(struct session *session, char *ivar if (strcmp(var, "B") == 0) { retlen = strlcpy(retval, ansi(session, ANSI_BOLD, ANSI_END), retsize); - } else if (strcmp(var, "/") == 0) { + } else if (strcmp(var, "/B") == 0) { retlen = strlcpy(retval, ansi(session, ANSI_RESET, ANSI_END), retsize); + } else if (strcmp(var, "#") == 0) { + *end_expansion = true; + retlen = 0; } else if (strcmp(var, "node") == 0) { retlen = strlcpy(retval, session->node, retsize); } else if (strcmp(var, "phone_number") == 0) { @@ -1080,7 +1120,7 @@ session_pause_return(struct session *s, short enforce, { unsigned char c; - session_output_template(s, "{{/}}Press {{B}}<Enter>{{/}} "); + session_output_template(s, "{{/B}}Press {{B}}<Enter>{{/B}} "); if (msg) session_output_string(s, msg); else @@ -1099,6 +1139,44 @@ session_pause_return(struct session *s, short enforce, } void +session_page_sysop(struct session *s) +{ + char *message = NULL; + + session_output_template(s, "{{B}}Page Sysop{{/B}} " + "(^C to cancel)\r\n" + "{{B}}-------------------------{{/B}}\r\n"); + session_output_view(s, VIEWS_SUBMENU_PAGE_SYSOP_ID); + session_flush(s); + + session_output_template(s, "{{B}}Message:{{/B}} "); + session_flush(s); + message = session_field_input(s, 64, 64, NULL, 0); + session_output(s, "\r\n", 2); + session_flush(s); + + if (message == NULL || s->ending) + goto page_done; + + /* TODO: show message on the screen */ + + /* TODO: enter chat with sysop */ + + session_output_template(s, "{{B}}TODO!{{/B}}\r\n"); + session_flush(s); + +page_done: + if (message) + free(message); +} + +void +session_answer_page(struct session *s) +{ + /* TODO */ +} + +void session_recents(struct session *s) { struct bile_object *slog_obj; @@ -1109,9 +1187,9 @@ session_recents(struct session *s) char sdate[12]; short printed; - session_output_template(s, "{{B}}Recent Logins{{/}}\r\n"); + session_output_template(s, "{{B}}Recent Logins{{/B}}\r\n"); session_output_template(s, - "{{B}}Date Node User Via Speed{{/}}\r\n"); + "{{B}}Date Node User Via Speed{{/B}}\r\n"); session_flush(s); scount = bile_sorted_ids_by_type(db->sessions_bile, SL_LOG_RTYPE, @@ -1152,9 +1230,9 @@ session_who(struct session *s) unsigned long idle; short n; - session_output_template(s, "{{B}}Who's Online{{/}}\r\n"); + session_output_template(s, "{{B}}Who's Online{{/B}}\r\n"); session_output_template(s, - "{{B}}Node User Via Speed Idle{{/}}\r\n"); + "{{B}}Node User Via Speed Idle{{/B}}\r\n"); session_flush(s); for (n = 0; n < nsessions; n++) { @@ -1180,4 +1258,27 @@ session_who(struct session *s) session_output(s, "\r\n", 2); session_flush(s); session_pause_return(s, 0, NULL); -} +} + +void +sysop_edit_settings(struct session *s) +{ + struct config *new_config; + short ret; + + if (!s->user || !s->user->is_sysop) + return; + + ret = struct_editor(s, config_fields, nconfig_fields, &db->config, + sizeof(struct config), (void *)&new_config, "BBS Settings"); + if (ret != 0) { + session_printf(s, "No changes made\r\n"); + return; + } + + memcpy(&db->config, new_config, sizeof(db->config)); + db_config_save(db); + + session_printf(s, "Successfully saved changes to BBS Settings\r\n"); + free(new_config); +} --- settings.c Thu Apr 14 16:36:54 2022 +++ settings.c Thu May 12 16:56:04 2022 @@ -24,53 +24,6 @@ #include "tetab.h" #include "util.h" -enum { - CONFIG_TYPE_STRING, - CONFIG_TYPE_SHORT -}; - -struct config_field { - char name[32]; - short type; - unsigned short off; - unsigned short min; - unsigned short max; - short ditl_id; -} config_fields[] = { - { "BBS Name", CONFIG_TYPE_STRING, - offsetof(struct config, name), - 1, member_size(struct config, name), - SETTINGS_BBS_NAME_ID }, - { "Phone Number", CONFIG_TYPE_STRING, - offsetof(struct config, phone_number), - 1, member_size(struct config, phone_number), - SETTINGS_PHONE_ID }, - { "Location", CONFIG_TYPE_STRING, - offsetof(struct config, location), - 1, member_size(struct config, location), - SETTINGS_LOCATION_ID }, - { "Hostname", CONFIG_TYPE_STRING, - offsetof(struct config, hostname), - 1, member_size(struct config, hostname), - SETTINGS_HOSTNAME_ID }, - { "Telnet Port", CONFIG_TYPE_SHORT, - offsetof(struct config, telnet_port), - 0, 65535, - SETTINGS_TELNET_PORT_ID }, - { "Modem Port", CONFIG_TYPE_SHORT, - offsetof(struct config, modem_port), - 0, 2, - SETTINGS_MODEM_PORT_ID }, - { "Modem Init String", CONFIG_TYPE_STRING, - offsetof(struct config, modem_init), - 1, member_size(struct config, modem_init), - SETTINGS_MODEM_INIT_ID }, - { "Max Idle Minutes", CONFIG_TYPE_SHORT, - offsetof(struct config, max_idle_minutes), - 1, 65535, - SETTINGS_MAX_IDLE_MINS_ID }, -}; - struct view_editor { WindowPtr win; size_t view_id; @@ -87,103 +40,173 @@ void view_editor_update(struct focusable *focusable, E void view_editor_close(struct focusable *focusable, EventRecord *event); void view_editor_save(struct focusable *focusable, EventRecord *event); -void -settings_edit(void) +short +struct_editor(struct session *s, struct struct_field *fields, + size_t nfields, void *data, size_t dsize, void **result, char *title) { - Str255 txt; Handle ihandle; - struct config new_config; + struct struct_field *sf; size_t len; - DialogPtr dlg; - Rect irect; - short done = 0, itype, hit, n; + long lval; + char co, initial[20]; + char *input = NULL, *new_data; + short itype, hit, n, sval; + bool found, any_changes = false; + unsigned short c; - new_config = db->config; + session_printf(s, "{{B}}Editor: %s{{/B}}\r\n", title); + session_flush(s); - if ((dlg = GetNewDialog(SETTINGS_DLOG_ID, nil, (WindowPtr)-1)) == NULL) - panic("Can't find settings DLOG %d", SETTINGS_DLOG_ID); - - for (n = 0; n < nitems(config_fields); n++) { - struct config_field *cf = &config_fields[n]; - short sval; - - GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect); + new_data = xmalloc(dsize); + memcpy(new_data, data, dsize); + +print_options: + for (n = 0; n < nfields; n++) { + sf = &fields[n]; - switch (cf->type) { + if (n > 9) + co = 'A' + (n - 9); + else + co = '0' + n; + + session_printf(s, "{{B}}%c{{/B}}: %s [", co, sf->name); + + switch (sf->type) { case CONFIG_TYPE_STRING: - strlcpy((char *)&txt, (char *)&new_config + cf->off, - sizeof(txt)); + session_output_string(s, new_data + sf->off); break; case CONFIG_TYPE_SHORT: - sval = (((char *)&new_config)[cf->off] << 8) | - ((char *)&new_config)[cf->off + 1]; - snprintf((char *)&txt, sizeof(txt), "%d", sval); + sval = (new_data[sf->off] << 8) | new_data[sf->off + 1]; + session_printf(s, "%d", sval); break; } - CtoPstr(txt); - SetIText(ihandle, txt); + session_output_string(s, "]\r\n"); } - - ShowWindow(dlg); - while (!done) { -get_input: - ModalDialog(ModalDialogFilter, &hit); - switch (hit) { - case OK: - done = 1; + session_printf(s, "{{B}}S{{/B}}: Save and return to main menu\r\n"); + session_printf(s, "{{B}}Q{{/B}}: Discard changes and return to main menu\r\n"); + session_printf(s, "{{B}}?{{/B}}: Print menu of options\r\n"); + + for (;;) { + session_output_template(s, "{{B}}Editor>{{/B}} "); + session_flush(s); + +get_menu_option: + c = session_input_char(s); + if (s->ending) { + any_changes = false; + goto done; + } + if (c == '\r' || c == 0 || c > 255) + goto get_menu_option; + + session_printf(s, "%c\r\n", c); + session_flush(s); + + if (c == '?') + goto print_options; + if (c == 'S' || c == 's') break; - case Cancel: - goto cancel; + if (c == 'Q' || c == 'q') { + any_changes = false; + goto done; } + + found = false; + sf = NULL; + for (n = 0; n < nfields; n++) { + sf = &fields[n]; - if (!done) + if (n > 9) + co = 'A' + (n - 9); + else + co = '0' + n; + + if (c == co) { + found = true; + break; + } + } + + if (!found) { + session_output_template(s, + "Invalid option ({{B}}?{{/B}} for help)\r\n"); + session_flush(s); continue; + } - for (n = 0; n < nitems(config_fields); n++) { - struct config_field *cf = &config_fields[n]; - long lval; - short sval; - - GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect); - GetIText(ihandle, txt); - PtoCstr(txt); +get_input: + session_printf(s, "{{B}}%s:{{/B}} ", sf->name); + session_flush(s); - switch (cf->type) { - case CONFIG_TYPE_STRING: - if (strlen((char *)txt) >= cf->max) { - warn("%s is too long (%d max)", cf->name, cf->max - 1); - done = 0; - goto get_input; - } - strlcpy((char *)&new_config + cf->off, (char *)&txt, - cf->max); + switch (sf->type) { + case CONFIG_TYPE_STRING: + input = session_field_input(s, sf->max, 50, new_data + sf->off, + 0); + session_output(s, "\r\n", 2); + session_flush(s); + if (input == NULL || s->ending) break; - case CONFIG_TYPE_SHORT: - lval = atol((char *)txt); - if (lval < cf->min) { - warn("%s must be at least %d", cf->name, cf->min); - done = 0; - goto get_input; - } - if (lval > cf->max) { - warn("%s must be less than %d", cf->name, cf->max); - done = 0; - goto get_input; - } - sval = lval; - memcpy((char *)&new_config + cf->off, &sval, sizeof(short)); + + if (strlen(input) >= sf->max) { + session_printf(s, + "%s is too long (%d max, ^C to cancel)", sf->name, + sf->max - 1); + free(input); + goto get_input; + } + strlcpy(new_data + sf->off, input, sf->max); + any_changes = true; + break; + case CONFIG_TYPE_SHORT: + sval = (new_data[sf->off] << 8) | new_data[sf->off + 1]; + snprintf(initial, sizeof(initial), "%d", sval); + input = session_field_input(s, 10, 10, initial, 0); + session_output(s, "\r\n", 2); + session_flush(s); + if (input == NULL || s->ending) break; + + lval = atol(input); + if (lval < sf->min) { + session_printf(s, + "%s must be at least %d (^C to cancel)\r\n", sf->name, + sf->min); + free(input); + goto get_input; } + if (lval > sf->max) { + session_printf(s, + "%s must be less than %d (^C to cancel)\r\n", sf->name, + sf->max); + free(input); + goto get_input; + } + sval = lval; + new_data[sf->off] = (sval >> 8) & 0xff; + new_data[sf->off + 1] = sval & 0xff; + any_changes = true; + break; } + + if (input != NULL) + free(input); + if (s->ending) { + any_changes = false; + goto done; + } } - - db->config = new_config; - db_config_save(db); -cancel: - DisposeDialog(dlg); +done: + if (any_changes) { + *result = new_data; + return 0; + } else { + free(new_data); + *result = NULL; + return -1; + } } void --- settings.h Tue Jan 18 08:07:32 2022 +++ settings.h Tue May 10 15:45:23 2022 @@ -17,7 +17,23 @@ #ifndef __SETTINGS_H__ #define __SETTINGS_H__ -void settings_edit(void); +#include "session.h" + +enum { + CONFIG_TYPE_STRING, + CONFIG_TYPE_SHORT +}; + +struct struct_field { + char name[32]; + short type; + unsigned short off; + unsigned short min; + unsigned short max; +}; + +short struct_editor(struct session *s, struct struct_field *fields, + size_t nfields, void *data, size_t dsize, void **result, char *title); void view_editor_show(size_t id, char *title); #endif --- signup.c Tue Apr 26 15:40:07 2022 +++ signup.c Mon May 9 15:49:09 2022 @@ -37,13 +37,14 @@ signup(struct session *s) session_log(s, "Signing up for an account"); - session_output_template(s, "{{B}}Create Account{{/}} " + session_output_template(s, "{{B}}Create Account{{/B}} " "(^C to cancel)\r\n" - "{{B}}----------------------------{{/}}\r\n"); + "{{B}}----------------------------{{/B}}\r\n"); + session_output_view(s, VIEWS_SUBMENU_SIGNUP_ID); session_flush(s); for (;;) { - session_printf(s, "{{B}}Username{{/}} (Up to %d letters/numbers): ", + session_printf(s, "{{B}}Username{{/B}} (Up to %d letters/numbers): ", DB_USERNAME_LENGTH); session_flush(s); @@ -56,7 +57,7 @@ signup(struct session *s) goto signup_done; if (user_valid_username(username, &error) != 1) { - session_printf(s, "{{B}}Error:{{/}} %s\r\n", error); + session_printf(s, "{{B}}Error:{{/B}} %s\r\n", error); free(error); continue; } @@ -65,7 +66,7 @@ signup(struct session *s) } for (;;) { - session_output_template(s, "{{B}}Password:{{/}} "); + session_output_template(s, "{{B}}Password:{{/B}} "); session_flush(s); password = session_field_input(s, 64, 64, NULL, '*'); session_output(s, "\r\n", 2); @@ -75,13 +76,13 @@ signup(struct session *s) goto signup_done; if (password[0] == '\0') { - session_printf(s, "{{B}}Error:{{/}} " + session_printf(s, "{{B}}Error:{{/B}} " "Password cannot be blank\r\n"); free(password); continue; } - session_output_template(s, "{{B}}Password (again):{{/}} "); + session_output_template(s, "{{B}}Password (again):{{/B}} "); session_flush(s); password_confirm = session_field_input(s, 64, 64, NULL, '*'); session_output(s, "\r\n", 2); @@ -91,7 +92,7 @@ signup(struct session *s) goto signup_done; if (strcmp(password_confirm, password) != 0) { - session_printf(s, "{{B}}Error:{{/}} " + session_printf(s, "{{B}}Error:{{/B}} " "Passwords do not match\r\n"); free(password); free(password_confirm); --- subtext.π.r Wed Jan 5 21:02:58 2022 +++ subtext.π.r Thu May 12 17:46:17 2022 @@ -9,8 +9,33 @@ data 'MENU' (129) { $"696C 6504 5175 6974 0051 0000 00" /* ile.Quit.Q... */ }; +data 'MENU' (130) { + $"0082 0000 0000 0000 0000 FFFF FFF7 0342" /* .Ç.............B */ + $"4253 0855 7365 7273 2E2E 2E00 5500 0005" /* BS.Users....U... */ + $"5669 6577 7300 1B84 0001 2D00 0000 000C" /* Views..Ñ..-..... */ + $"4F70 656E 2043 6F6E 736F 6C65 004E 0000" /* Open Console.N.. */ + $"00" /* . */ +}; + +data 'MENU' (131) { + $"0083 0000 0000 0000 0000 FFFF FFFF 0445" /* .É.............E */ + $"6469 7403 4375 7400 5800 0004 436F 7079" /* dit.Cut.X...Copy */ + $"0043 0000 0550 6173 7465 0056 0000 00" /* .C...Paste.V... */ +}; + +data 'MENU' (132) { + $"0084 0000 0000 0000 0000 FFFF FFFF 0556" /* .Ñ.............V */ + $"6965 7773 1450 7265 2D4C 6F67 696E 2028" /* iews.Pre-Login ( */ + $"4973 7375 6529 2E2E 2E00 0000 000C 4D61" /* Issue)........Ma */ + $"696E 204D 656E 752E 2E2E 0000 0000 0D53" /* in Menu.......¬S */ + $"686F 7274 204D 656E 752E 2E2E 0000 0000" /* hort Menu....... */ + $"1141 6363 6F75 6E74 2053 6967 6E75 702E" /* .Account Signup. */ + $"2E2E 0000 0000 0D50 6167 6520 5379 736F" /* ......¬Page Syso */ + $"702E 2E2E 0000 0000 00" /* p........ */ +}; + data 'MBAR' (128) { - $"0002 0080 0081" /* ...Ä.Å */ + $"0004 0080 0081 0083 0082" /* ...Ä.Å.É.Ç */ }; data 'ALRT' (129) { --- subtext.h Tue Apr 12 16:35:04 2022 +++ subtext.h Mon May 9 15:37:21 2022 @@ -35,17 +35,16 @@ #define EDIT_MENU_PASTE_ID 3 #define BBS_MENU_ID 130 -#define BBS_MENU_BOARDS_ID 1 -#define BBS_MENU_FILES_ID 2 -#define BBS_MENU_USERS_ID 3 -#define BBS_MENU_VIEWS_ID 4 -#define BBS_MENU_SETTINGS_ID 5 -#define BBS_MENU_OPEN_CONSOLE_ID 7 +#define BBS_MENU_USERS_ID 1 +#define BBS_MENU_VIEWS_ID 2 +#define BBS_MENU_OPEN_CONSOLE_ID 4 #define VIEWS_SUBMENU_ID 132 -#define VIEWS_SUBMENU_MAIN_MENU_ID 1 -#define VIEWS_SUBMENU_SHORT_MENU_ID 2 -#define VIEWS_SUBMENU_ISSUE_ID 3 +#define VIEWS_SUBMENU_ISSUE_ID 1 +#define VIEWS_SUBMENU_MAIN_MENU_ID 2 +#define VIEWS_SUBMENU_SHORT_MENU_ID 3 +#define VIEWS_SUBMENU_SIGNUP_ID 4 +#define VIEWS_SUBMENU_PAGE_SYSOP_ID 5 #define STR_LAST_DB 128 @@ -59,7 +58,13 @@ #define SETTINGS_MODEM_INIT_ID 9 #define SETTINGS_MAX_IDLE_MINS_ID 10 -#define SETTINGS_EDIT_VIEW_DLOG_ID 131 +#define BOARD_DLOG_ID 131 +#define BOARD_BOARD_ID 3 +#define BOARD_NAME_ID 4 +#define BOARD_DESCRIPTION_ID 5 +#define BOARD_RESTRICTED_POSTING_ID 6 +#define BOARD_RESTRICTED_VIEWING_ID 7 +#define BOARD_RETENTION_ID 8 #include "db.h" #include "logger.h" --- tetab.c Fri Oct 29 17:17:44 2021 +++ tetab.c Mon May 9 15:55:04 2022 @@ -214,6 +214,7 @@ TETabHitTestHook(void) if (poffset <= (wpos * cwidth)) { found = 1; + cpos++; break; } } --- user_settings.c Thu Apr 28 14:13:17 2022 +++ user_settings.c Mon May 9 15:49:26 2022 @@ -34,13 +34,13 @@ void user_settings_help(struct session *s) { session_output_template(s, - "{{B}}R{{/}}: Renegotiate terminal\r\n"); + "{{B}}R{{/B}}: Renegotiate terminal\r\n"); if (s->user) - session_output_template(s, "{{B}}P{{/}}: Change password\r\n"); + session_output_template(s, "{{B}}P{{/B}}: Change password\r\n"); session_output_template(s, - "{{B}}Q{{/}}: Return to main menu\r\n"); + "{{B}}Q{{/B}}: Return to main menu\r\n"); session_flush(s); } @@ -107,13 +107,16 @@ get_terminal_size: return; session_output(s, "\r\n", 2); session_flush(s); - if (tsize[0] == '\0') + if (tsize[0] == '\0') { + free(tsize); return; + } if (sscanf(tsize, "%dx%d", &cols, &lines) != 2) { session_printf(s, "Invalid response, must be in format %dx%d\r\n", s->terminal_columns, s->terminal_lines); session_flush(s); + free(tsize); goto get_terminal_size; } @@ -121,6 +124,7 @@ get_terminal_size: session_printf(s, "Terminal too small, must be at least " "%dx%d\r\n", MIN_TERMINAL_COLUMNS, MIN_TERMINAL_LINES); session_flush(s); + free(tsize); goto get_terminal_size; } @@ -135,13 +139,13 @@ user_settings_password(struct session *s) char *password = NULL, *password_confirm = NULL; if (!s->user) { - session_output_template(s, "{{B}}Error{{/}}: Guest accounts " + session_output_template(s, "{{B}}Error{{/B}}: Guest accounts " "cannot change passwords\r\n"); return; } for (;;) { - session_output_template(s, "{{B}}New Password:{{/}} "); + session_output_template(s, "{{B}}New Password:{{/B}} "); session_flush(s); password = session_field_input(s, 64, 64, NULL, '*'); session_output(s, "\r\n", 2); @@ -151,13 +155,13 @@ user_settings_password(struct session *s) break; if (password[0] == '\0') { - session_output_template(s, "{{B}}Error:{{/}} " + session_output_template(s, "{{B}}Error:{{/B}} " "Password cannot be blank\r\n"); free(password); continue; } - session_output_template(s, "{{B}}New Password (again):{{/}} "); + session_output_template(s, "{{B}}New Password (again):{{/B}} "); session_flush(s); password_confirm = session_field_input(s, 64, 64, NULL, '*'); session_output(s, "\r\n", 2); @@ -167,7 +171,7 @@ user_settings_password(struct session *s) break; if (strcmp(password_confirm, password) != 0) { - session_output_template(s, "{{B}}Error:{{/}} " + session_output_template(s, "{{B}}Error:{{/B}} " "Passwords do not match\r\n"); free(password); free(password_confirm); @@ -177,7 +181,7 @@ user_settings_password(struct session *s) user_set_password(s->user, password); user_save(s->user); session_output_template(s, "{{B}}Your password has been " - "changed{{/}}\r\n"); + "changed{{/B}}\r\n"); break; } @@ -195,12 +199,12 @@ user_settings_menu(struct session *s) session_log(s, "User settings menu"); - session_output_template(s, "{{B}}Settings{{/}}\r\n" - "{{B}}--------{{/}}\r\n"); + session_output_template(s, "{{B}}Settings{{/B}}\r\n" + "{{B}}--------{{/B}}\r\n"); user_settings_help(s); while (!done) { - session_output_template(s, "{{B}}Settings>{{/}} "); + session_output_template(s, "{{B}}Settings>{{/B}} "); session_flush(s); get_another_char: