AmendHub

Download:

jcs

/

subtext

/

amendments

/

364

binkp+mail: Add support for sending FidoNet packets

When mail is addressed to something with @ and a parseable FidoNet
node, save it and mark it for pending dispatch. When binkp runs,
scan all mail and find messages for dispatch and send them out.

jcs made amendment 364 about 1 year ago
--- binkp.c Sat Mar 4 16:26:30 2023 +++ binkp.c Tue Mar 7 21:23:09 2023 @@ -35,6 +35,10 @@ static short binkp_fidopkt_skipped, binkp_fidopkt_impo static bool binkp_temp_fail = false; struct uthread *binkp_thread = NULL; +size_t binkp_send_frame(struct binkp_connection *conn, short command, + char *data, size_t data_size); +bool binkp_read_frame(struct binkp_connection *conn); +bool binkp_login(struct binkp_connection *conn); void binkp_fetch(void); void binkp_ingest(void); bool binkp_zip_decider(char *filename, size_t size); @@ -43,6 +47,8 @@ void binkp_fidopkt_processor(char *filename, unsigned bool binkp_ingest_post(struct fidopkt *fidopkt, struct board *board); bool binkp_ingest_mail(struct fidopkt *fidopkt); size_t binkp_buffer_file(Str255 path, char **data); +bool binkp_send_buf_as_file(struct binkp_connection *conn, char *filename, + char *pkt_buf, size_t pkt_buf_size); void binkp_process(struct uthread *uthread, void *arg) @@ -113,21 +119,75 @@ binkp_atexit(void) void binkp_fetch(void) { + char filename[16]; + struct fidopkt_address *our_address = NULL; + size_t nmail_ids, n, pkt_buf_size; + char *pkt_buf; + unsigned long *mail_ids = NULL; + unsigned long started = Time, elapsed; + + if (!fidopkt_parse_address(db->config.fidonet_node_addr, + &our_address)) { + logger_printf("[binkp] failed parsing local FidoNet node address " + "\"%s\", fix in settings", db->config.fidonet_node_addr); + return; + } + + nmail_ids = mail_ids_needing_fidonet_dispatch(&mail_ids); + binkpc = binkp_connect(db->config.binkp_hostname, db->config.binkp_port); - if (!binkpc) + if (!binkpc) { + xfree(&our_address); return; - + } + if (!binkp_login(binkpc)) - return; + goto done; - while (binkpc != NULL && !binkpc->done) { + while (binkpc != NULL && !binkpc->done_receiving) { binkp_read_frame(binkpc); if (binkpc == NULL) break; uthread_yield(); } + for (n = 0; n < nmail_ids; n++) { + pkt_buf_size = mail_encode_as_fidopkt(mail_ids[n], our_address, + &pkt_buf); + if (pkt_buf == NULL) { + logger_printf("[binkp] failed encoding mail %ld as FidoNet " + "packet", mail_ids[n]); + continue; + } + + snprintf(filename, sizeof(filename), "f%07ld.pkt", mail_ids[n]); + if (!binkp_send_buf_as_file(binkpc, filename, pkt_buf, + pkt_buf_size)) { + xfree(&pkt_buf); + binkpc->done_sending = true; + break; + } + + xfree(&pkt_buf); + + mail_mark_fidonet_dispatched(mail_ids[n]); + } + + if (!binkp_send_frame(binkpc, BINKP_COMMAND_M_EOB, "", 0)) + logger_printf("[binkp] failed sending M_EOB"); + + binkpc->done_sending = true; + + _TCPClose(&binkpc->tcp_send_iopb, binkpc->tcp_stream, nil, nil, false); + + elapsed = Time - started; + logger_printf("[binkp] done transferring in %ld sec%s", elapsed, + elapsed == 1 ? "" : "s"); + +done: + xfree(&our_address); + if (binkpc != NULL) binkp_connection_free(&binkpc); } @@ -144,21 +204,21 @@ binkp_connect(char *hostname, unsigned short port) if (_TCPInit() != noErr) panic("Failed initializing MacTCP"); - conn = calloc(sizeof(struct binkp_connection), 1); + conn = xcalloc(sizeof(struct binkp_connection), 1); if (conn == NULL) { logger_printf("[binkp] failed allocating connection of size %ld", sizeof(struct binkp_connection)); goto error; } conn->buf_size = 1024; - conn->buf = malloc(conn->buf_size); + conn->buf = xmalloc(conn->buf_size); if (conn->buf == NULL) { logger_printf("[binkp] failed allocating connection buffer of " "size %ld", conn->buf_size); goto error; } conn->tcp_buf_size = (4 * 1500) + conn->buf_size; - conn->tcp_buf = malloc(conn->tcp_buf_size); + conn->tcp_buf = xmalloc(conn->tcp_buf_size); if (conn->tcp_buf == NULL) { logger_printf("[binkp] failed allocating TCP buffer of " "size %ld", conn->tcp_buf_size); @@ -208,20 +268,19 @@ binkp_connection_free(struct binkp_connection **ptr) return; if (conn->tcp_stream) - _TCPRelease(&conn->tcp_read_iopb, conn->tcp_stream, nil, nil, + _TCPRelease(&conn->tcp_send_iopb, conn->tcp_stream, nil, nil, false); if (conn->tcp_buf != NULL) - free(conn->tcp_buf); + xfree(&conn->tcp_buf); if (conn->buf != NULL) - free(conn->buf); + xfree(&conn->buf); - free(conn); - *ptr = NULL; + xfree(ptr); } size_t -binkp_send_command(struct binkp_connection *conn, char *data, +binkp_send_frame(struct binkp_connection *conn, short command, char *data, size_t data_size) { u_int16_t flen; @@ -231,8 +290,8 @@ binkp_send_command(struct binkp_connection *conn, char return 0; if (data_size + 2 + 1 > conn->buf_size) { - logger_printf("[binkp] binkp_connection_send_frame: frame too " - "large (%ld)", data_size + 2 + 1); + logger_printf("[binkp] binkp_send_frame: frame too large (%ld)", + data_size + 2 + 1); return 0; } @@ -240,28 +299,47 @@ binkp_send_command(struct binkp_connection *conn, char /* previous _TCPSend has not completed yet */ uthread_yield(); - flen = data_size; - /* BINKP_TYPE_COMMAND */ - flen |= 0x8000; + if (command == BINKP_DATA) { + conn->buf[0] = (data_size >> 8) & 0xff; + conn->buf[1] = data_size & 0xff; + flen = data_size + 2; + memcpy(conn->buf + 2, data, data_size); + } else { + conn->buf[0] = (((data_size + 1) | 0x8000) >> 8) & 0xff; + conn->buf[1] = (data_size + 1) & 0xff; + conn->buf[2] = command; + flen = data_size + 3; + memcpy(conn->buf + 3, data, data_size); + /* not sent, just for debugging */ + conn->buf[3 + data_size] = '\0'; + } - conn->buf[0] = (flen >> 8) & 0xff; - conn->buf[1] = flen & 0xff; - - memcpy(conn->buf + 2, data, data_size); - /* not sent, just for debugging */ - conn->buf[2 + data_size] = '\0'; - memset(&conn->tcp_wds, 0, sizeof(conn->tcp_wds)); conn->tcp_wds[0].ptr = (Ptr)conn->buf; - conn->tcp_wds[0].length = data_size + 2; + conn->tcp_wds[0].length = flen; #ifdef BINKP_DEBUG - if (data[0] == BINKP_COMMAND_M_PWD) + switch (command) { + case BINKP_COMMAND_M_PWD: logger_printf("[binkp] sending password command of size %ld", data_size); - else - logger_printf("[binkp] sending command of size %ld: %s", data_size, - conn->buf + 3); + break; + case BINKP_COMMAND_M_FILE: + logger_printf("[binkp] sending new file: %s", conn->buf + 3); + break; + case BINKP_COMMAND_M_EOB: + logger_printf("[binkp] sending EOB"); + break; + case BINKP_COMMAND_M_GOT: + logger_printf("[binkp] sending GOT for %s", conn->cur_file.filename); + break; + case BINKP_DATA: + logger_printf("[binkp] sending data of size %ld", data_size); + break; + default: + logger_printf("[binkp] sending command %d of size %ld: %s", + command, data_size, conn->buf + 3); + } #endif error = _TCPSend(&conn->tcp_send_iopb, conn->tcp_stream, conn->tcp_wds, @@ -347,8 +425,7 @@ binkp_read_frame(struct binkp_connection *conn) conn->buf_size); } - if (rlen > conn->tcp_status_pb.amtUnreadData) - rlen = conn->tcp_status_pb.amtUnreadData; + rlen = MIN(rlen, conn->tcp_status_pb.amtUnreadData); error = _TCPRcv(&conn->tcp_read_iopb, conn->tcp_stream, read_dest, &rlen, nil, nil, false); @@ -372,7 +449,7 @@ binkp_read_frame(struct binkp_connection *conn) logger_printf("[binkp] error writing %d to %s: %d", rlen, PtoCstr(conn->cur_file.pfilename), error); CtoPstr(conn->cur_file.pfilename); - conn->done = true; + conn->done_receiving = conn->done_sending = true; goto failed_read; } } @@ -397,7 +474,7 @@ binkp_read_frame(struct binkp_connection *conn) switch (conn->cur_frame.command_id) { case BINKP_COMMAND_M_NUL: if (strncmp(conn->buf + 1, "SYS ", 4) == 0) - logger_printf("[binkp] fetching from %s", + logger_printf("[binkp] connected to %s", conn->buf + 1 + 4); break; case BINKP_COMMAND_M_FILE: @@ -465,16 +542,15 @@ binkp_read_frame(struct binkp_connection *conn) } break; case BINKP_COMMAND_M_EOB: - logger_printf("[binkp] end of batch, disconnecting"); - conn->done = true; + conn->done_receiving = true; break; case BINKP_COMMAND_M_ERR: logger_printf("[binkp] error from remote: %s", conn->buf + 1); - conn->done = true; + conn->done_receiving = conn->done_sending = true; break; case BINKP_COMMAND_M_BSY: logger_printf("[binkp] remote is busy: %s", conn->buf + 1); - conn->done = true; + conn->done_receiving = conn->done_sending = true; break; } } else { @@ -484,16 +560,17 @@ binkp_read_frame(struct binkp_connection *conn) conn->cur_file.data_read, conn->cur_file.size); #endif if (conn->cur_file.data_read == conn->cur_file.size) { +#ifdef BINKP_DEBUG logger_printf("[binkp] done reading file %s (%ld)", conn->cur_file.filename, conn->cur_file.size); - +#endif FSClose(conn->cur_file.frefnum); conn->cur_file.frefnum = 0; - len = snprintf(tmp, sizeof(tmp), "%c%s %ld %ld", - BINKP_COMMAND_M_GOT, conn->cur_file.filename, - conn->cur_file.size, conn->cur_file.mtime); - if (!binkp_send_command(conn, tmp, len)) + len = snprintf(tmp, sizeof(tmp), "%s %ld %ld", + conn->cur_file.filename, conn->cur_file.size, + conn->cur_file.mtime); + if (!binkp_send_frame(conn, BINKP_COMMAND_M_GOT, tmp, len)) logger_printf("[binkp] failed sending M_GOT %s", conn->cur_file.filename); @@ -508,7 +585,7 @@ failed_read: FSClose(conn->cur_file.frefnum); conn->cur_file.frefnum = 0; } - conn->done = true; + conn->done_receiving = conn->done_sending = true; return false; } @@ -521,34 +598,29 @@ binkp_login(struct binkp_connection *conn) while (binkp_read_frame(conn)) ; - len = snprintf(command, sizeof(command), "%cSYS %s", - BINKP_COMMAND_M_NUL, db->config.name); - if (!binkp_send_command(conn, command, len)) + len = snprintf(command, sizeof(command), "SYS %s", db->config.name); + if (!binkp_send_frame(conn, BINKP_COMMAND_M_NUL, command, len)) return false; - len = snprintf(command, sizeof(command), "%cLOC %s", - BINKP_COMMAND_M_NUL, db->config.location); - if (!binkp_send_command(conn, command, len)) + len = snprintf(command, sizeof(command), "LOC %s", db->config.location); + if (!binkp_send_frame(conn, BINKP_COMMAND_M_NUL, command, len)) return false; - len = snprintf(command, sizeof(command), "%cNDL 14400,TCP,BINKP", - BINKP_COMMAND_M_NUL); - if (!binkp_send_command(conn, command, len)) + len = snprintf(command, sizeof(command), "NDL 14400,TCP,BINKP"); + if (!binkp_send_frame(conn, BINKP_COMMAND_M_NUL, command, len)) return false; - len = snprintf(command, sizeof(command), "%cVER Subtext binkp/1.1", - BINKP_COMMAND_M_NUL); - if (!binkp_send_command(conn, command, len)) + len = snprintf(command, sizeof(command), "VER Subtext/%s binkp/1.1", + get_version(false)); + if (!binkp_send_frame(conn, BINKP_COMMAND_M_NUL, command, len)) return false; - len = snprintf(command, sizeof(command), "%c%s", - BINKP_COMMAND_M_ADR, db->config.binkp_node_addr); - if (!binkp_send_command(conn, command, len)) + if (!binkp_send_frame(conn, BINKP_COMMAND_M_ADR, + db->config.fidonet_node_addr, strlen(db->config.fidonet_node_addr))) return false; - len = snprintf(command, sizeof(command), "%c%s", - BINKP_COMMAND_M_PWD, db->config.binkp_password); - if (!binkp_send_command(conn, command, len)) + if (!binkp_send_frame(conn, BINKP_COMMAND_M_PWD, + db->config.binkp_password, strlen(db->config.binkp_password))) return false; for (;;) { @@ -565,13 +637,13 @@ binkp_login(struct binkp_connection *conn) if (conn->cur_frame.command_id == BINKP_COMMAND_M_OK) { logger_printf("[binkp] logged in successfully as %s", - db->config.binkp_node_addr); + db->config.fidonet_node_addr); return true; } if (conn->cur_frame.command_id == BINKP_COMMAND_M_ERR) { logger_printf("[binkp] login as %s failed, disconnecting", - db->config.binkp_node_addr); + db->config.fidonet_node_addr); binkp_connection_free(&conn); return false; } @@ -580,6 +652,43 @@ binkp_login(struct binkp_connection *conn) return false; } +bool +binkp_send_buf_as_file(struct binkp_connection *conn, char *filename, + char *pkt_buf, size_t pkt_buf_size) +{ + char command[50]; + size_t len; + + len = snprintf(command, sizeof(command), "%s %lu 1 0", filename, + pkt_buf_size); + if (!binkp_send_frame(conn, BINKP_COMMAND_M_FILE, command, len)) + return false; + + if (!binkp_send_frame(conn, BINKP_DATA, pkt_buf, pkt_buf_size)) + return false; + + for (;;) { + if (!binkp_read_frame(conn)) { + uthread_yield(); + continue; + } + + if (conn->cur_frame.type != BINKP_TYPE_COMMAND) + continue; + + if (conn->cur_frame.command_id == BINKP_COMMAND_M_GOT) { + logger_printf("[binkp] successfully sent file"); + return true; + } + + logger_printf("[binkp] unrecognized command in response to M_FILE"); + binkp_connection_free(&conn); + return false; + } + + return true; +} + void binkp_ingest(void) { @@ -589,7 +698,7 @@ binkp_ingest(void) CMovePBRec cmpbr = { 0 }; Str255 file_name_c, abs_path, done_abs_path; short dir_id, error; - unsigned long started = Time; + unsigned long started = Time, elapsed; size_t data_size; long n, j; char *data; @@ -690,7 +799,12 @@ try_rename: } } - logger_printf("[binkp] done importing in %ld sec(s)", Time - started); + if (binkp_fidopkt_skipped == 0 && binkp_fidopkt_imported == 0) + return; + + elapsed = Time - started; + logger_printf("[binkp] done importing in %ld sec%s", elapsed, + elapsed == 1 ? "" : "s"); } bool --- binkp.h Tue Feb 28 18:32:02 2023 +++ binkp.h Mon Mar 6 21:09:38 2023 @@ -24,6 +24,7 @@ struct binkp_frame { u_int16_t data_size; char command_id; +#define BINKP_DATA -1 #define BINKP_COMMAND_M_NUL 0 #define BINKP_COMMAND_M_ADR 1 #define BINKP_COMMAND_M_PWD 2 @@ -63,7 +64,8 @@ struct binkp_connection { size_t buf_size; struct binkp_frame cur_frame; struct binkp_file cur_file; - bool done; + bool done_receiving; + bool done_sending; }; extern struct uthread *binkp_thread; @@ -72,11 +74,5 @@ void binkp_process(struct uthread *uthread, void *arg) struct binkp_connection * binkp_connect(char *, unsigned short); void binkp_connection_free(struct binkp_connection **); void binkp_atexit(void); -size_t binkp_send_frame(struct binkp_connection *conn, - struct binkp_frame *frame); -size_t binkp_send_command(struct binkp_connection *conn, char *data, - size_t data_len); -bool binkp_read_frame(struct binkp_connection *conn); -bool binkp_login(struct binkp_connection *conn); #endif --- mail.c Sat Mar 4 21:30:22 2023 +++ mail.c Tue Mar 7 16:50:21 2023 @@ -58,8 +58,8 @@ static const struct bile_object_field mail_object_fiel { offsetof(struct mail_message, fidonet_msgid), member_size(struct mail_message, fidonet_msgid), -1 }, - { offsetof(struct mail_message, fidonet_source), - member_size(struct mail_message, fidonet_source), -1 }, + { offsetof(struct mail_message, fidonet_orig), + member_size(struct mail_message, fidonet_orig), -1 }, { offsetof(struct mail_message, fidonet_dest), member_size(struct mail_message, fidonet_dest), -1 }, { offsetof(struct mail_message, fidonet_from), @@ -68,10 +68,10 @@ static const struct bile_object_field mail_object_fiel member_size(struct mail_message, fidonet_to), -1 }, { offsetof(struct mail_message, fidonet_msgid_orig), member_size(struct mail_message, fidonet_msgid_orig), -1 }, - { offsetof(struct mail_message, fidonet_origin), - member_size(struct mail_message, fidonet_origin), -1 }, { offsetof(struct mail_message, fidonet_reply), member_size(struct mail_message, fidonet_reply), -1 }, + { offsetof(struct mail_message, fidonet_need_dispatch), + member_size(struct mail_message, fidonet_need_dispatch), -1 }, }; static const size_t nmail_object_fields = nitems(mail_object_fields); @@ -191,7 +191,7 @@ mail_menu(struct session *s) handle_opt: switch (c) { case 'm': - mail_compose(s, NULL, NULL, NULL); + mail_compose(s, NULL, NULL, NULL, NULL); break; case 'l': show_list = true; @@ -252,11 +252,12 @@ handle_opt: void mail_compose(struct session *s, char *initial_to, char *initial_subject, - char *initial_body) + char *initial_body, char *fidonet_reply_msgid) { struct user *to_user = NULL; struct mail_message msg = { 0 }; - char *to_username = NULL, *tmp; + struct fidopkt_address *fidonet_to = NULL; + char *to_username = NULL, *tmp, *at; char c; if (!s->user) { @@ -291,27 +292,49 @@ mail_compose_start: session_printf(s, "{{B}}To: {{/B}} "); session_flush(s); - tmp = session_field_input(s, DB_USERNAME_LENGTH + 1, - DB_USERNAME_LENGTH + 1, to_username, false, 0); + tmp = session_field_input(s, 50, 50, to_username, false, 0); if (to_username != NULL) xfree(&to_username); to_username = tmp; - session_output(s, "\r\n", 2); - session_flush(s); - if (to_username == NULL || to_username[0] == '\0') + if (to_username == NULL || to_username[0] == '\0') { + session_output(s, "\r\n", 2); + session_flush(s); goto mail_compose_done; + } - to_user = user_find_by_username(to_username); - if (to_user == NULL) { - session_printf(s, "{{B}}Error:{{/B}}{{#}} No such user \"%s\" " - "(^C to cancel)\r\n", to_username); + if ((at = strstr(to_username, "@")) != NULL) { + if (!fidopkt_parse_address(at + 1, &fidonet_to)) { + session_printf(s, "\r\n{{B}}Error:{{/B}}{{#}} Invalid " + "FidoNet destination \"%s\" (^C to cancel)\r\n", at + 1); + session_flush(s); + xfree(&to_username); + continue; + } + memcpy(&msg.fidonet_dest, fidonet_to, sizeof(msg.fidonet_dest)); + xfree(&fidonet_to); + strlcpy(msg.fidonet_to, to_username, sizeof(msg.fidonet_to)); + if ((at = strstr(msg.fidonet_to, "@")) != NULL) + at[0] = '\0'; + strlcpy(msg.fidonet_from, s->user->username, + sizeof(msg.fidonet_from)); + msg.fidonet_need_dispatch = 1; + session_printf(s, " (FidoNet)\r\n"); session_flush(s); - xfree(&to_username); - to_username = NULL; - continue; + } else { + session_output(s, "\r\n", 2); + session_flush(s); + to_user = user_find_by_username(to_username); + if (to_user == NULL) { + session_printf(s, "{{B}}Error:{{/B}}{{#}} No such user " + "\"%s\" (^C to cancel)\r\n", to_username); + session_flush(s); + xfree(&to_username); + continue; + } + msg.recipient_user_id = to_user->id; + xfree(&to_user); + msg.fidonet_need_dispatch = 0; } - msg.recipient_user_id = to_user->id; - xfree(&to_user); break; } @@ -319,7 +342,7 @@ mail_compose_start: session_printf(s, "{{B}}Subject:{{/B}} "); session_flush(s); - tmp = session_field_input(s, 50, 50, msg.subject, false, 0); + tmp = session_field_input(s, 60, 60, msg.subject, false, 0); if (msg.subject != NULL) xfree(&msg.subject); msg.subject = tmp; @@ -336,7 +359,6 @@ mail_compose_start: "be blank (^C to cancel)\r\n"); session_flush(s); xfree(&msg.subject); - msg.subject = NULL; continue; } msg.subject_size = strlen(msg.subject) + 1; @@ -374,8 +396,9 @@ mail_compose_start: } for (;;) { - session_printf(s, "\r\n{{B}}(S){{/B}}end message, " - "{{B}}(E){{/B}}dit again, or {{B}}(C){{/B}}ancel? "); + session_printf(s, "\r\n{{B}}(S){{/B}}end %smessage, " + "{{B}}(E){{/B}}dit again, or {{B}}(C){{/B}}ancel? ", + msg.fidonet_need_dispatch ? "FidoNet " : ""); session_flush(s); c = session_input_char(s); @@ -397,18 +420,29 @@ mail_compose_start: case '\n': case '\r': /* send */ - session_printf(s, "Sending mail..."); + if (msg.fidonet_need_dispatch) + session_printf(s, "Queueing mail for FidoNet delivery..."); + else + session_printf(s, "Sending mail..."); session_flush(s); msg.time = Time; msg.sender_user_id = s->user->id; + + if (fidonet_reply_msgid != NULL && fidonet_reply_msgid[0]) + strlcpy(msg.fidonet_reply, fidonet_reply_msgid, + sizeof(fidonet_reply_msgid)); + if (mail_save(&msg) != 0) { session_printf(s, "failed!\r\n"); session_flush(s); break; } - session_printf(s, " sent!\r\n"); + if (msg.fidonet_need_dispatch) + session_printf(s, " done!\r\n"); + else + session_printf(s, " sent!\r\n"); session_flush(s); goto mail_compose_done; @@ -456,9 +490,9 @@ mail_list(struct session *s, size_t nmail_ids, unsigne if (msg.fidonet_from[0] && msg.recipient_user_id) { snprintf(from, sizeof(from), "%s@%d:%d/%d.%d", - msg.fidonet_from, msg.fidonet_source.zone, - msg.fidonet_source.net, msg.fidonet_source.node, - msg.fidonet_source.point); + msg.fidonet_from, msg.fidonet_msgid.zone, + msg.fidonet_msgid.net, msg.fidonet_msgid.node, + msg.fidonet_msgid.point); } else { user = user_username(msg.sender_user_id); strlcpy(from, user ? user->username : "(unknown)", @@ -496,9 +530,10 @@ mail_read(struct session *s, unsigned long id, short i char time[32]; char prompt[24]; char title[50]; + char from[50], to[50]; size_t size; struct mail_message msg; - struct username_cache *sender, *recipient; + struct username_cache *user; short ret = MAIL_READ_RETURN_DONE; char *data, *reply_subject; bool done = false, show_help = false; @@ -516,30 +551,41 @@ mail_read(struct session *s, unsigned long id, short i nitems(mail_object_fields), data, size, &msg, sizeof(msg), true); xfree(&data); - sender = user_username(msg.sender_user_id); - recipient = user_username(msg.recipient_user_id); - - strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", - localtime(&msg.time)); - - if (msg.fidonet_from[0] && msg.recipient_user_id) { - session_printf(s, "{{B}}From:{{/B}} %s@%d:%d/%d.%d", - msg.fidonet_from, msg.fidonet_source.zone, - msg.fidonet_source.net, msg.fidonet_source.node, - msg.fidonet_source.point); + if (msg.fidonet_from[0]) { + size = snprintf(from, sizeof(from), "%s@%d:%d/%d", + msg.fidonet_from, msg.fidonet_msgid.zone, + msg.fidonet_msgid.net, msg.fidonet_msgid.node); + if (msg.fidonet_msgid.point) + snprintf(from + size, sizeof(from) - size, ".%d", + msg.fidonet_msgid.point); } else { - session_printf(s, "{{B}}From:{{/B}} %s\r\n", - sender ? sender->username : "(unknown)"); + user = user_username(msg.sender_user_id); + if (user) + strlcpy(from, user->username, sizeof(from)); + else + strlcpy(from, "(Unknown)", sizeof(from)); } - if (msg.fidonet_to[0] && msg.sender_user_id) { - session_printf(s, "{{B}}To:{{/B}} %s@%d:%d/%d.%d", - msg.fidonet_to, msg.fidonet_dest.zone, - msg.fidonet_dest.net, msg.fidonet_dest.node, + + if (msg.fidonet_to[0]) { + snprintf(to, sizeof(to), "%s@%d:%d/%d", + msg.fidonet_to, msg.fidonet_dest.zone, + msg.fidonet_dest.net, msg.fidonet_dest.node); + if (msg.fidonet_dest.point) + snprintf(to + size, sizeof(to) - size, ".%d", msg.fidonet_dest.point); } else { - session_printf(s, "{{B}}To:{{/B}} %s\r\n", - recipient ? recipient->username : "(unknown)"); + user = user_username(msg.recipient_user_id); + if (user) + strlcpy(to, user->username, sizeof(to)); + else + strlcpy(to, "(Unknown)", sizeof(to)); } + + strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", + localtime(&msg.time)); + + session_printf(s, "{{B}}From:{{/B}} %s\r\n", from); + session_printf(s, "{{B}}To:{{/B}} %s\r\n", to); session_printf(s, "{{B}}Date:{{/B}} %s %s\r\n", time, db->config.timezone); session_printf(s, "{{B}}Subject:{{/B}}{{#}} %s\r\n", msg.subject); @@ -565,9 +611,6 @@ mail_read(struct session *s, unsigned long id, short i switch (c) { case 'r': - if (!sender) - break; - reply_subject = xmalloc(strlen(msg.subject) + 5); if (reply_subject == NULL) break; @@ -577,8 +620,8 @@ mail_read(struct session *s, unsigned long id, short i else sprintf(reply_subject, "Re: %s", msg.subject); - mail_compose(s, sender->username, reply_subject, NULL); - + mail_compose(s, from, reply_subject, NULL, + msg.fidonet_msgid_orig[0] ? msg.fidonet_msgid_orig : NULL); xfree(&reply_subject); break; case 'd': @@ -766,15 +809,16 @@ mail_ingest_fidopkt(struct fidopkt *fidopkt) short ret = 1; char *data; - if (db->config.binkp_node_addr[0] == 0) { + if (db->config.fidonet_node_addr[0] == 0) { logger_printf("[mail] local FidoNet node address must be " "configured in settings"); return -1; } - if (!fidopkt_parse_address(db->config.binkp_node_addr, &our_address)) { + if (!fidopkt_parse_address(db->config.fidonet_node_addr, + &our_address)) { logger_printf("[mail] failed parsing local FidoNet node address " - "\"%s\", fix in settings", db->config.binkp_node_addr); + "\"%s\", fix in settings", db->config.fidonet_node_addr); return -1; } @@ -826,21 +870,22 @@ mail_ingest_fidopkt(struct fidopkt *fidopkt) memset(&msg, 0, sizeof(msg)); msg.recipient_user_id = to->id; - msg.time = fidopkt->time; + if (fidopkt->time) + msg.time = fidopkt->time; + else + msg.time = Time; msg.subject = xstrdup(fidopkt->subject); msg.subject_size = strlen(msg.subject) + 1; msg.body = fidopkt->message; msg.body_size = fidopkt->message_len + 1; msg.fidonet_msgid = fidopkt->msgid; - msg.fidonet_source = fidopkt->orig; + msg.fidonet_orig = fidopkt->orig; msg.fidonet_dest = fidopkt->dest; strlcpy(msg.fidonet_from, fidopkt->from, sizeof(msg.fidonet_from)); strlcpy(msg.fidonet_msgid_orig, fidopkt->msgid_orig, sizeof(msg.fidonet_msgid_orig)); - strlcpy(msg.fidonet_origin, fidopkt->origin, - sizeof(msg.fidonet_origin)); strlcpy(msg.fidonet_reply, fidopkt->reply, sizeof(msg.fidonet_reply)); if (mail_save(&msg) != 0) { @@ -861,4 +906,125 @@ done: xfree(&to); xfree(&our_address); return ret; +} + +size_t +mail_ids_needing_fidonet_dispatch(unsigned long **ids) +{ + struct mail_message msg; + size_t nmail_ids, nall_mail_ids, mail_ids_size, size, n; + unsigned long *mail_ids = NULL, *all_mail_ids = NULL; + char *data; + bool failed = false; + + *ids = NULL; + + nall_mail_ids = bile_ids_by_type(db->mail_bile, + MAIL_SPOOL_MESSAGE_RTYPE, &all_mail_ids); + + if (nall_mail_ids == 0) + return 0; + + mail_ids_size = 0; + nmail_ids = 0; + for (n = 0; n < nall_mail_ids; n++) { + size = bile_read_alloc(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, + all_mail_ids[n], &data); + if (size == 0) { + failed = true; + goto done; + } + bile_unmarshall_object(db->mail_bile, mail_object_fields, + nitems(mail_object_fields), data, size, &msg, sizeof(msg), + false); + xfree(&data); + if (!msg.fidonet_need_dispatch) + continue; + + if (!grow_to_fit(&mail_ids, &mail_ids_size, + (nmail_ids + 1) * sizeof(long), sizeof(long), + sizeof(long) * 16)) { + failed = true; + goto done; + } + mail_ids[nmail_ids] = all_mail_ids[n]; + nmail_ids++; + } + +done: + xfree(&all_mail_ids); + if (failed) { + if (mail_ids != NULL) + xfree(&mail_ids); + return 0; + } + *ids = mail_ids; + return nmail_ids; +} + +size_t +mail_encode_as_fidopkt(unsigned long id, struct fidopkt_address *orig_node, + char **pkt_buf) +{ + struct mail_message msg; + size_t size; + char *data; + short ret; + + *pkt_buf = NULL; + + size = bile_read_alloc(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, id, + &data); + if (size == 0) { + logger_printf("[mail] failed encoding message %ld: %d", id, + bile_error(db->mail_bile)); + return 0; + } + + bile_unmarshall_object(db->mail_bile, mail_object_fields, + nitems(mail_object_fields), data, size, &msg, sizeof(msg), true); + xfree(&data); + + if (msg.fidonet_msgid.id == 0) { + msg.fidonet_msgid.id = id; + msg.fidonet_msgid.zone = orig_node->zone; + msg.fidonet_msgid.net = orig_node->net; + msg.fidonet_msgid.node = orig_node->node; + msg.fidonet_msgid.point = orig_node->point; + + if ((ret = mail_save(&msg)) != 0) { + logger_printf("[mail] failed saving fidonet msgid for %ld: %d", + id, ret); + return 0; + } + } + + return fidopkt_encode(pkt_buf, db->config.fidonet_pkt_password, + orig_node, &msg.fidonet_dest, &msg.fidonet_msgid, + msg.time, db->config.timezone_utcoff, msg.fidonet_from, + msg.fidonet_to, NULL, msg.fidonet_reply, msg.subject, msg.body); +} + +bool +mail_mark_fidonet_dispatched(unsigned long id) +{ + struct mail_message msg; + size_t size; + char *data; + + size = bile_read_alloc(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, id, + &data); + if (size == 0) + return false; + + bile_unmarshall_object(db->mail_bile, mail_object_fields, + nitems(mail_object_fields), data, size, &msg, sizeof(msg), true); + xfree(&data); + + msg.fidonet_need_dispatch = 0; + + if (mail_save(&msg) != 0) + return false; + + return true; } --- mail.h Sat Mar 4 21:24:09 2023 +++ mail.h Tue Mar 7 16:46:58 2023 @@ -38,20 +38,25 @@ struct mail_message { unsigned long parent_message_id; struct fidopkt_msgid fidonet_msgid; - struct fidopkt_address fidonet_source; + struct fidopkt_address fidonet_orig; struct fidopkt_address fidonet_dest; char fidonet_from[32]; char fidonet_to[32]; char fidonet_msgid_orig[64]; - char fidonet_origin[64]; char fidonet_reply[32]; + short fidonet_need_dispatch; }; void mail_menu(struct session *s); -void mail_compose(struct session *s, char *to, char *subject, char *body); +void mail_compose(struct session *s, char *to, char *subject, char *body, + char *fidonet_reply_msgid); short mail_save(struct mail_message *msg); size_t mail_find_ids_for_user(struct user *user, size_t *nmail_ids, unsigned long **mail_ids, size_t offset, size_t limit, bool only_unread); short mail_ingest_fidopkt(struct fidopkt *fidopkt); +size_t mail_ids_needing_fidonet_dispatch(unsigned long **ids); +size_t mail_encode_as_fidopkt(unsigned long id, + struct fidopkt_address *orig_node, char **pkt_buf); +bool mail_mark_fidonet_dispatched(unsigned long id); #endif