AmendHub

Download:

jcs

/

subtext

/

amendments

/

357

mail: Import FidoNet mail to local users, store fidopkt data


jcs made amendment 357 about 1 year ago
--- db.c Thu Mar 2 08:06:08 2023 +++ db.c Thu Mar 2 17:38:42 2023 @@ -23,6 +23,7 @@ #include "bile.h" #include "db.h" #include "folder.h" +#include "mail.h" #include "user.h" #include "util.h" @@ -659,6 +660,49 @@ db_migrate(struct db *tdb, short is_new, Str255 fullpa db_cache_boards(tdb); + break; + } + case 15: { + /* 15->16, mail gets fido fields */ + Str255 newfullpath; + struct bile *mail_bile; + struct bile_object *o; + struct mail_message mail; + size_t nids, n, size; + unsigned long *ids; + char *data; + + memcpy(newfullpath, fullpath, sizeof(newfullpath)); + PtoCstr(newfullpath); + strlcat((char *)&newfullpath, "-mail", sizeof(newfullpath)); + CtoPstr(newfullpath); + + mail_bile = bile_open(newfullpath, tdb->bile->vrefnum); + if (mail_bile == NULL) + /* no mail here, no problem */ + break; + + nids = bile_ids_by_type(mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, + &ids); + if (ids == NULL) { + bile_close(mail_bile); + break; + } + for (n = 0; n < nids; n++) { + o = bile_find(mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, ids[n]); + if (!o) + panic("can't find message %ld but it's in ids", + ids[n]); + progress("Growing mail message %ld/%ld...", n + 1, nids); + size = o->size + + (sizeof(struct mail_message) - + offsetof(struct mail_message, fidonet_msgid)); + if (bile_resize(mail_bile, o->type, o->id, size) != size) + panic("failed resizing message %ld", o->id); + xfree(&o); + } + xfree(&ids); + bile_close(mail_bile); break; } } --- db.h Tue Feb 28 19:27:37 2023 +++ db.h Thu Mar 2 15:37:19 2023 @@ -19,7 +19,7 @@ #include <time.h> -#define DB_CUR_VERS 15 +#define DB_CUR_VERS 16 #define SUBTEXT_CREATOR 'SUBT' #define DB_TYPE 'STDB' --- mail.c Thu Mar 2 09:28:03 2023 +++ mail.c Sat Mar 4 21:30:22 2023 @@ -20,6 +20,7 @@ #include <string.h> #include "ansi.h" +#include "logger.h" #include "mail.h" #include "subtext.h" #include "session.h" @@ -54,11 +55,27 @@ static const struct bile_object_field mail_object_fiel -1, offsetof(struct mail_message, body_size) }, { offsetof(struct mail_message, parent_message_id), member_size(struct mail_message, parent_message_id), -1 }, + + { 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_dest), + member_size(struct mail_message, fidonet_dest), -1 }, + { offsetof(struct mail_message, fidonet_from), + member_size(struct mail_message, fidonet_from), -1 }, + { offsetof(struct mail_message, fidonet_to), + 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 }, }; static const size_t nmail_object_fields = nitems(mail_object_fields); void mail_free_message_strings(struct mail_message *msg); -short mail_save(struct session *s, struct mail_message *msg); short mail_read(struct session *s, unsigned long id, short idx); void mail_list(struct session *s, size_t nmsgs, unsigned long *mail_ids, size_t page, size_t pages); @@ -385,7 +402,11 @@ mail_compose_start: msg.time = Time; msg.sender_user_id = s->user->id; - mail_save(s, &msg); + if (mail_save(&msg) != 0) { + session_printf(s, "failed!\r\n"); + session_flush(s); + break; + } session_printf(s, " sent!\r\n"); session_flush(s); @@ -412,7 +433,7 @@ void mail_list(struct session *s, size_t nmail_ids, unsigned long *mail_ids, size_t page, size_t pages) { - char time[10]; + char time[10], from[40]; size_t n, size; struct mail_message msg; struct username_cache *user; @@ -420,7 +441,7 @@ mail_list(struct session *s, size_t nmail_ids, unsigne session_printf(s, "{{B}}Private Mail (Page %ld of %ld){{/B}}\r\n", page, pages); - session_printf(s, "%s# Flg Date From Subject%s\r\n", + session_printf(s, "%s# Flg Date From Subject%s\r\n", ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END)); session_flush(s); @@ -433,15 +454,25 @@ mail_list(struct session *s, size_t nmail_ids, unsigne nitems(mail_object_fields), data, size, &msg, sizeof(msg), true); xfree(&data); - user = user_username(msg.sender_user_id); + 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); + } else { + user = user_username(msg.sender_user_id); + strlcpy(from, user ? user->username : "(unknown)", + sizeof(from)); + } + strftime(time, sizeof(time), "%b %d", localtime(&msg.time)); - session_printf(s, "%s%ld %c %s %- 10s {{#}}%- 40s%s\r\n", + session_printf(s, "%s%ld %c %s %-15.15s {{#}}%.40s%s\r\n", msg.read ? "" : ansi(s, ANSI_BOLD, ANSI_END), n + 1, msg.read ? ' ' : 'N', time, - user ? user->username : "(unknown)", + from, msg.subject, msg.read ? "" : ansi(s, ANSI_RESET, ANSI_END)); @@ -491,10 +522,24 @@ mail_read(struct session *s, unsigned long id, short i strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", localtime(&msg.time)); - session_printf(s, "{{B}}From:{{/B}} %s\r\n", - sender ? sender->username : "(unknown)"); - session_printf(s, "{{B}}To:{{/B}} %s\r\n", - recipient ? recipient->username : "(unknown)"); + 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); + } else { + session_printf(s, "{{B}}From:{{/B}} %s\r\n", + sender ? sender->username : "(unknown)"); + } + 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, + msg.fidonet_dest.point); + } else { + session_printf(s, "{{B}}To:{{/B}} %s\r\n", + recipient ? recipient->username : "(unknown)"); + } 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); @@ -506,7 +551,7 @@ mail_read(struct session *s, unsigned long id, short i if (!msg.read) { msg.read = Time; - if (mail_save(s, &msg) != 0) + if (mail_save(&msg) != 0) session_printf(s, "Failed marking message read!\r\n"); } @@ -549,7 +594,7 @@ mail_read(struct session *s, unsigned long id, short i break; case 'u': msg.read = 0; - if (mail_save(s, &msg) == 0) + if (mail_save(&msg) == 0) session_printf(s, "Marked message {{B}}%d{{/B}} unread\r\n", idx); else @@ -579,7 +624,7 @@ mail_read(struct session *s, unsigned long id, short i } short -mail_save(struct session *s, struct mail_message *msg) +mail_save(struct mail_message *msg) { size_t size; char *data; @@ -587,6 +632,8 @@ mail_save(struct session *s, struct mail_message *msg) if (!msg->id) msg->id = bile_next_id(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE); + if (!msg->id) + return -1; ret = bile_marshall_object(db->mail_bile, mail_object_fields, nitems(mail_object_fields), msg, &data, &size); @@ -604,93 +651,214 @@ mail_save(struct session *s, struct mail_message *msg) return 0; } +/* + * return count of all ids for the user (for pagination), set nmail_ids to + * the count of the ids set in mail_ids based on offset/limit/only_unread + * + * nret_mail_ids or ret_mail_ids may be NULL to avoid building an array + */ 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) +mail_find_ids_for_user(struct user *user, size_t *nret_mail_ids, + unsigned long **ret_mail_ids, size_t offset, size_t limit, + bool only_unread) { - unsigned long msg_user_id; struct bile_object *o; struct mail_message msg; - size_t n, nmsgs_for_user, mail_ids_size, id, size; + size_t n, nall_mail_ids, nmail_ids, mail_ids_size, nlimit_mail_ids, + id, size; + bool failed = false; + unsigned long *mail_ids = NULL, *all_mail_ids; short i, j; - char *data; - bool read; - mail_ids_size = sizeof(long) * 16; - if (mail_ids != NULL) - *mail_ids = xmalloc(mail_ids_size); - if (nmail_ids != NULL) - *nmail_ids = 0; + if (ret_mail_ids != NULL) + *ret_mail_ids = NULL; + if (nret_mail_ids != NULL) + *nret_mail_ids = 0; - nmsgs_for_user = 0; - for (n = 0; (o = bile_get_nth_of_type(db->mail_bile, n, - MAIL_SPOOL_MESSAGE_RTYPE)); n++) { - id = o->id; - bile_read(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, id, - (char *)&msg_user_id, sizeof(msg_user_id)); - xfree(&o); + nall_mail_ids = bile_ids_by_type(db->mail_bile, + MAIL_SPOOL_MESSAGE_RTYPE, &all_mail_ids); + + /* narrow all_mail_ids down to mail_ids for this user */ + mail_ids_size = 0; + nmail_ids = 0; + for (n = 0; n < nall_mail_ids; n++) { + /* read only as far as we need to */ + size = offsetof(struct mail_message, read) + + member_size(struct mail_message, read); + if (bile_read(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, + all_mail_ids[n], (char *)&msg, size) != size) { + failed = true; + goto done; + } - if (msg_user_id != user->id) + if (msg.recipient_user_id != user->id) continue; - - if (only_unread) { - size = bile_read_alloc(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, - id, &data); - if (size == 0) - break; - bile_unmarshall_object(db->mail_bile, mail_object_fields, - nmail_object_fields, data, size, &msg, sizeof(msg), false); - xfree(&data); - read = msg.read; - if (read) - continue; - } + if (only_unread && msg.read) + continue; - if (mail_ids != NULL) { - if (!grow_to_fit(mail_ids, &mail_ids_size, - (nmsgs_for_user + 1) * sizeof(long), sizeof(long), - sizeof(long) * 16)) - break; - (*mail_ids)[nmsgs_for_user] = id; + if (ret_mail_ids != NULL) { + 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]; } - nmsgs_for_user++; + nmail_ids++; } - if (mail_ids != NULL) { + if (all_mail_ids != NULL) + xfree(&all_mail_ids); + + if (ret_mail_ids != NULL) { /* sort by message id for consistent ordering */ - for (i = 0; i < nmsgs_for_user; i++) { - for (j = 0; j < nmsgs_for_user - i - 1; j++) { - if ((*mail_ids)[j] > (*mail_ids)[j + 1]) { - id = (*mail_ids)[j]; - (*mail_ids)[j] = (*mail_ids)[j + 1]; - (*mail_ids)[j + 1] = id; + for (i = 0; i < nmail_ids; i++) { + for (j = 0; j < nmail_ids - i - 1; j++) { + if (mail_ids[j] > mail_ids[j + 1]) { + id = mail_ids[j]; + mail_ids[j] = mail_ids[j + 1]; + mail_ids[j + 1] = id; } } } } - if (nmail_ids != NULL) - *nmail_ids = nmsgs_for_user; + nlimit_mail_ids = nmail_ids; if (offset) { - if (offset >= nmsgs_for_user) { - if (nmail_ids != NULL) - *nmail_ids = 0; + if (offset >= nlimit_mail_ids) { if (mail_ids != NULL) - xfree(&(*mail_ids)); + xfree(&mail_ids); + nlimit_mail_ids = 0; } else { if (mail_ids != NULL) { - for (j = offset, i = 0; j < nmsgs_for_user; j++, i++) - (*mail_ids)[i] = (*mail_ids)[j]; + for (j = offset, i = 0; j < nmail_ids; j++, i++) + mail_ids[i] = mail_ids[j]; } - if (nmail_ids != NULL) - *nmail_ids -= offset; + nlimit_mail_ids -= offset; } } - if (nmail_ids != NULL && *nmail_ids > limit) - *nmail_ids = limit; + if (limit && nlimit_mail_ids > limit) + nlimit_mail_ids = limit; + + if (ret_mail_ids != NULL) + *ret_mail_ids = mail_ids; + if (nret_mail_ids != NULL) + *nret_mail_ids = nlimit_mail_ids; + +done: + if (failed) + return 0; + return nmail_ids; +} + +short +mail_ingest_fidopkt(struct fidopkt *fidopkt) +{ + struct user *to = NULL; + struct fidopkt_address *our_address = NULL; + size_t n, nmail_ids, size; + unsigned long *mail_ids = NULL; + struct mail_message msg; + short ret = 1; + char *data; + + if (db->config.binkp_node_addr[0] == 0) { + logger_printf("[mail] local FidoNet node address must be " + "configured in settings"); + return -1; + } - return nmsgs_for_user; + if (!fidopkt_parse_address(db->config.binkp_node_addr, &our_address)) { + logger_printf("[mail] failed parsing local FidoNet node address " + "\"%s\", fix in settings", db->config.binkp_node_addr); + return -1; + } + + if (memcmp(&fidopkt->dest, our_address, + sizeof(struct fidopkt_address)) != 0) { + logger_printf("[mail] message is destined for %d:%d/%d.%d, not " + "us (%d:%d/%d.%d)", + fidopkt->dest.zone, fidopkt->dest.net, + fidopkt->dest.node, fidopkt->dest.point, + our_address->zone, our_address->net, + our_address->node, our_address->point); + ret = 0; + goto done; + } + + to = user_find_by_username(fidopkt->to); + if (to == NULL) { + logger_printf("[mail] no local user \"%s\", discarding message", + fidopkt->to); + ret = 0; + goto done; + } + + mail_find_ids_for_user(to, &nmail_ids, &mail_ids, 0, 0, false); + for (n = 0; n < nmail_ids; n++) { + size = bile_read_alloc(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, + mail_ids[n], &data); + if (size == 0) + break; + bile_unmarshall_object(db->mail_bile, mail_object_fields, + nitems(mail_object_fields), data, size, &msg, sizeof(msg), false); + xfree(&data); + + if (memcmp(&msg.fidonet_msgid, &fidopkt->msgid, + sizeof(struct fidopkt_msgid)) == 0) { + logger_printf("[mail] already have fidonet msg %s (%ld), " + "skipping", fidopkt->msgid_orig, msg.id); + ret = 0; + break; + } + } + if (mail_ids != NULL) + xfree(&mail_ids); + + if (ret < 1) { + xfree(&to); + return ret; + } + + memset(&msg, 0, sizeof(msg)); + msg.recipient_user_id = to->id; + msg.time = fidopkt->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_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) { + logger_printf("[binkp] failed saving mail from %d:%d/%d.%d to " + "local user %s", + fidopkt->orig.zone, fidopkt->orig.net, fidopkt->orig.node, + fidopkt->orig.point, to->username); + ret = -1; + goto done; + } + + logger_printf("[binkp] imported mail from %d:%d/%d.%d to local user %s", + fidopkt->orig.zone, fidopkt->orig.net, fidopkt->orig.node, + fidopkt->orig.point, to->username); + +done: + if (to != NULL) + xfree(&to); + xfree(&our_address); + return ret; } --- mail.h Wed May 25 15:04:00 2022 +++ mail.h Sat Mar 4 21:24:09 2023 @@ -18,6 +18,7 @@ #define __MAIL_H__ #include "bile.h" +#include "fidopkt.h" #include "session.h" struct mail_message { @@ -27,17 +28,30 @@ struct mail_message { unsigned long id; time_t time; time_t read; + /* mail_find_ids_for_user has to be able to read this far without alloc */ + unsigned long sender_user_id; size_t subject_size; char *subject; size_t body_size; char *body; unsigned long parent_message_id; + + struct fidopkt_msgid fidonet_msgid; + struct fidopkt_address fidonet_source; + 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]; }; void mail_menu(struct session *s); void mail_compose(struct session *s, char *to, char *subject, char *body); +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); #endif