AmendHub

Download:

jcs

/

subtext

/

amendments

/

330

binkp+board: On second thought, store FidoNet messages differently

These are different enough from our own posts/threads that it makes
more sense to store them in separate formats and just adapt board
viewer to show both.
 
Add a config setting to set whether binkp will delete files after
processing or move them to a binkp-processed directory. This allows
keeping files for re-importing later, but then being able to turn
the setting on to delete files after processing to avoid filling up
the disk.
 
Also use non-fatal malloc/calloc in binkp processor since it's not
critical and can just retry later when we have memory.

jcs made amendment 330 about 1 year ago
--- binkp.c Sat Feb 25 17:57:57 2023 +++ binkp.c Wed Mar 1 09:30:06 2023 @@ -29,19 +29,26 @@ /* #define BINKP_DEBUG */ static struct binkp_connection *binkpc = NULL; -static Str255 binkp_dir; +static Str255 binkp_dir, binkp_done_dir; static short binkp_fidopkt_skipped, binkp_fidopkt_imported; +static unsigned long *binkp_touched_boards = NULL; +static unsigned long binkp_ntouched_boards = 0; +static size_t binkp_touched_boards_size = 0; +static bool binkp_temp_fail = false; void binkp_fetch(void); void binkp_ingest(void); bool binkp_zip_decider(char *filename, size_t size); void binkp_zip_processor(char *filename, size_t size, unsigned char *data); +struct uthread *binkp_thread = NULL; void -binkp_thread(struct uthread *uthread, void *arg) +binkp_process(struct uthread *uthread, void *arg) { short newdirid, error; + binkp_thread = uthread; + /* make sure working dir is there */ if (getpath(db->bile->vrefnum, db->bile->filename, &binkp_dir, false) != 0) @@ -56,6 +63,24 @@ binkp_thread(struct uthread *uthread, void *arg) panic("Failed creating %s: %d", PtoCstr(binkp_dir), error); } + if (!db->config.binkp_delete_done) { + /* and processed dir */ + if (getpath(db->bile->vrefnum, db->bile->filename, &binkp_done_dir, + false) != 0) + panic("getpath failed on %s", PtoCstr(db->bile->filename)); + PtoCstr(binkp_done_dir); + strlcat((char *)binkp_done_dir, ":binkp-processed", + sizeof(binkp_done_dir)); + CtoPstr(binkp_done_dir); + + if (!FIsDir(binkp_done_dir)) { + error = DirCreate(db->bile->vrefnum, 0, binkp_done_dir, + &newdirid); + if (error) + panic("Failed creating %s: %d", PtoCstr(binkp_dir), error); + } + } + for (;;) { if (db->config.binkp_hostname[0] != '\0' && db->config.binkp_port != 0) @@ -72,6 +97,8 @@ binkp_thread(struct uthread *uthread, void *arg) break; uthread_msleep(1000UL * db->config.binkp_interval_seconds); } + + binkp_thread = NULL; } void @@ -115,22 +142,38 @@ binkp_connect(char *hostname, unsigned short port) if (_TCPInit() != noErr) panic("Failed initializing MacTCP"); - conn = xmalloczero(sizeof(struct binkp_connection), "binkp_connection"); + conn = calloc(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 = xmalloc(conn->buf_size, "binkp buf"); + conn->buf = malloc(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 = xmalloc(conn->tcp_buf_size, "binkp tcp buf"); + conn->tcp_buf = malloc(conn->tcp_buf_size); + if (conn->tcp_buf == NULL) { + logger_printf("[binkp] failed allocating TCP buffer of " + "size %ld", conn->tcp_buf_size); + goto error; + } error = TCPResolveName(&hostname, &host_ip); if (error) { - warn("Couldn't resolve host %s (%d)", hostname, error); + logger_printf("[binkp] failed resolving binkp host %s: %d", + hostname, error); goto error; } error = _TCPCreate(&conn->tcp_send_iopb, &conn->tcp_stream, (Ptr)conn->tcp_buf, conn->tcp_buf_size, nil, nil, nil, false); if (error) { - warn("TCPCreate failed: %d", error); + logger_printf("[binkp] failed creating TCP stream: %d", error); goto error; } @@ -142,7 +185,7 @@ binkp_connect(char *hostname, unsigned short port) error = _TCPActiveOpen(&conn->tcp_send_iopb, conn->tcp_stream, host_ip, port, &local_ip, &local_port, nil, nil, false); if (error) { - warn("Failed connecting to %s (%s) port %d: %d", + logger_printf("[binkp] failed connecting to %s (%s) port %d: %d", hostname, ip_s, port, error); goto error; } @@ -157,8 +200,8 @@ error: void binkp_connection_free(struct binkp_connection **ptr) { - struct binkp_connection *conn = (struct binkp_connection *)*ptr; - + struct binkp_connection *conn = *ptr; + if (conn == NULL) return; @@ -166,10 +209,13 @@ binkp_connection_free(struct binkp_connection **ptr) _TCPRelease(&conn->tcp_read_iopb, conn->tcp_stream, nil, nil, false); - xfree(&conn->tcp_buf); - xfree(&conn->buf); + if (conn->tcp_buf != NULL) + free(conn->tcp_buf); + if (conn->buf != NULL) + free(conn->buf); - xfree(ptr); + free(conn); + *ptr = NULL; } size_t @@ -183,7 +229,8 @@ binkp_send_command(struct binkp_connection *conn, char return 0; if (data_size + 2 + 1 > conn->buf_size) { - warn("binkp_connection_send_frame: frame too large"); + logger_printf("[binkp] binkp_connection_send_frame: frame too " + "large (%ld)", data_size + 2 + 1); return 0; } @@ -207,8 +254,12 @@ binkp_send_command(struct binkp_connection *conn, char conn->tcp_wds[0].length = data_size + 2; #ifdef BINKP_DEBUG - logger_printf("[binkp] sending command of size %ld: %s", data_size, - conn->buf + 3); + if (data[0] == 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); #endif error = _TCPSend(&conn->tcp_send_iopb, conn->tcp_stream, conn->tcp_wds, @@ -219,6 +270,8 @@ binkp_send_command(struct binkp_connection *conn, char return 0; } + uthread_yield(); + return conn->tcp_wds[0].length; } @@ -249,9 +302,12 @@ binkp_read_frame(struct binkp_connection *conn) if (error) return false; - if (conn->cur_frame.data_size == 0) - warn("bogus frame?"); - + if (conn->cur_frame.data_size == 0) { + logger_printf("[binkp] binkp_read_frame: bogus frame, " + "no data_size"); + return false; + } + conn->cur_frame.type = (conn->cur_frame.data_size & (1 << 15)) == 0 ? BINKP_TYPE_DATA : BINKP_TYPE_COMMAND; conn->cur_frame.data_size &= 0x7fff; @@ -311,7 +367,7 @@ binkp_read_frame(struct binkp_connection *conn) len = rlen; error = FSWrite(conn->cur_file.frefnum, &len, conn->buf); if (error) { - warn("error writing %d to %s: %d", + logger_printf("[binkp] error writing %d to %s: %d", rlen, PtoCstr(conn->cur_file.pfilename), error); CtoPstr(conn->cur_file.pfilename); conn->done = true; @@ -359,7 +415,8 @@ binkp_read_frame(struct binkp_connection *conn) logger_printf("[binkp] new file \"%s\" size %ld", conn->cur_file.filename, conn->cur_file.size); if (off != 0) - warn("non-zero file fetch not supported"); + logger_printf("[binkp] non-zero file fetch not " + "supported"); conn->cur_file.data_read = 0; PtoCstr(binkp_dir); @@ -472,7 +529,7 @@ binkp_login(struct binkp_connection *conn) if (!binkp_send_command(conn, command, len)) return false; - len = snprintf(command, sizeof(command), "%cNDL 28800,TCP,BINKP", + len = snprintf(command, sizeof(command), "%cNDL 14400,TCP,BINKP", BINKP_COMMAND_M_NUL); if (!binkp_send_command(conn, command, len)) return false; @@ -527,53 +584,114 @@ binkp_ingest(void) CInfoPBRec cipbr = { 0 }; HFileInfo *fpb = (HFileInfo *)&cipbr; DirInfo *dpb = (DirInfo *)&cipbr; - short error, n; - Str255 file_name, abs_path; - short dir_id; + CMovePBRec cmpbr = { 0 }; + Str255 file_name, abs_path, done_abs_path; + short dir_id, error; unsigned long started = Time; + long n, j; + if (binkp_touched_boards != NULL) + xfree(&binkp_touched_boards); + binkp_ntouched_boards = binkp_touched_boards_size = 0; + fpb->ioVRefNum = 0; fpb->ioNamePtr = binkp_dir; error = PBGetCatInfo(&cipbr, false); if (error) { - warn("PBGetCatInfo on binkp dir failed: %d", error); + logger_printf("[binkp] PBGetCatInfo on binkp dir failed: %d", + error); return; } dir_id = dpb->ioDrDirID; + PtoCstr(binkp_dir); + fpb->ioNamePtr = file_name; - for (n = 1; ; n++) { + for (;;) { file_name[0] = 0; fpb->ioDirID = dir_id; - fpb->ioFDirIndex = n; + /* + * Keep requesting the first item in the directory since we're + * deleting or moving each file as we process it + */ + fpb->ioFDirIndex = 1; error = PBGetCatInfo(&cipbr, false); if (error) break; + + PtoCstr(file_name); - PtoCstr(binkp_dir); snprintf((char *)abs_path, sizeof(abs_path), "%s:%s", - binkp_dir, PtoCstr(file_name)); - CtoPstr(binkp_dir); -#ifdef BINKP_DEBUG - logger_printf("[binkp] scanning zip file %s", (char *)abs_path); -#endif - + binkp_dir, file_name); + logger_printf("[binkp] processing zip file %s", (char *)abs_path); + CtoPstr(abs_path); + binkp_fidopkt_skipped = 0; binkp_fidopkt_imported = 0; - CtoPstr(abs_path); zip_read_file(abs_path, binkp_zip_decider, binkp_zip_processor); + if (binkp_temp_fail) { + logger_printf("[binkp] temporary failure during processing, " + "aborting"); + return; + } + logger_printf("[binkp] %s: imported %d, skipped %d", (char *)file_name, binkp_fidopkt_imported, binkp_fidopkt_skipped); - - if ((error = FSDelete(abs_path, 0)) != 0) - logger_printf("[binkp] failed deleting %s: %d", - PtoCstr(abs_path), error); + + PtoCstr(binkp_done_dir); + snprintf((char *)done_abs_path, sizeof(done_abs_path), + "%s:%s", binkp_done_dir, file_name); + CtoPstr(done_abs_path); + CtoPstr(binkp_done_dir); + + if (db->config.binkp_delete_done) { + if ((error = FSDelete(done_abs_path, 0)) != 0) { + logger_printf("[binkp] failed deleting %s: %d", + PtoCstr(done_abs_path), error); + break; + } + } else { +try_rename: + cmpbr.ioNamePtr = abs_path; + cmpbr.ioNewName = binkp_done_dir; + cmpbr.ioDirID = 0; + cmpbr.ioNewDirID = 0; + cmpbr.ioVRefNum = 0; + error = PBCatMove(&cmpbr, false); + if (error == dupFNErr) { + if ((error = FSDelete(done_abs_path, 0)) != 0) { + logger_printf("[binkp] failed deleting %s: %d", + PtoCstr(done_abs_path), error); + break; + } + goto try_rename; + } else if (error) { + logger_printf("[binkp] failed moving %s to %s: %d", + PtoCstr(abs_path), PtoCstr(binkp_done_dir), error); + CtoPstr(binkp_done_dir); + break; + } + } } + CtoPstr(binkp_dir); + CtoPstr(binkp_done_dir); + + for (n = 0; n < binkp_ntouched_boards; n++) { + for (j = 0; j < db->nboards; j++) { + if (db->boards[j].id == binkp_touched_boards[n]) { + logger_printf("[binkp] rebuilding message index for %s", + db->boards[j].name); + uthread_yield(); + board_index_sorted_post_ids(&db->boards[j], NULL); + } + } + } + logger_printf("[binkp] done importing in %ld sec(s)", Time - started); } @@ -591,16 +709,21 @@ binkp_zip_decider(char *filename, size_t size) void binkp_zip_processor(char *filename, size_t size, unsigned char *data) { + struct board_fidonet_msgid_cache { + unsigned long id; + struct board_fidonet_msgid msgid; + } *msgid_cache = NULL; struct fidopkt *fidopkt; struct bile_object *o; struct board *board = NULL; - struct board_post post = { 0 }; - struct board_post_fidonet_msgid fidonet_msgid; - struct board_thread thread = { 0 }; + struct board_fidonet_post post; + struct board_fidonet_msgid msgid; + unsigned long *post_ids; char *pdata; - size_t psize; + size_t asize, cache_size, psize, npost_ids; ssize_t count; - short n; + short n, ret; + bool found, dirty_cache = false; uthread_yield(); @@ -610,7 +733,7 @@ binkp_zip_processor(char *filename, size_t size, unsig for (n = 0; n < db->nboards; n++) { if (db->boards[n].fidonet_area[0] && - strcmp(fidopkt->area, db->boards[n].fidonet_area) == 0) { + strcasecmp(fidopkt->area, db->boards[n].fidonet_area) == 0) { board = &db->boards[n]; break; } @@ -625,29 +748,54 @@ binkp_zip_processor(char *filename, size_t size, unsig goto done; } - fidonet_msgid.id = fidopkt->msgid; - fidonet_msgid.zone = fidopkt->msgid_zone; - fidonet_msgid.net = fidopkt->msgid_net; - fidonet_msgid.node = fidopkt->msgid_node; - fidonet_msgid.point = fidopkt->msgid_point; + msgid.id = fidopkt->msgid; + msgid.zone = fidopkt->msgid_zone; + msgid.net = fidopkt->msgid_net; + msgid.node = fidopkt->msgid_node; + msgid.point = fidopkt->msgid_point; - /* TODO: add an index of id->msgid to avoid searching every message */ - /* search backwards to check newest messages first */ - count = bile_count_by_type(board->bile, BOARD_POST_RTYPE); - for (; count > 0 && (o = bile_get_nth_of_type(board->bile, - count - 1, BOARD_POST_RTYPE)); count--) { - psize = bile_read_alloc(board->bile, o->type, o->id, &pdata); - bile_unmarshall_object(board->bile, board_post_object_fields, - nboard_post_object_fields, pdata, psize, &post, sizeof(post), - false, "binkp post"); + o = bile_find(board->bile, BOARD_FIDONET_MSGID_CACHE_RTYPE, 1); + if (o) { + /* allocate its size plus one entry that we may add */ + asize = o->size + sizeof(struct board_fidonet_msgid_cache); + msgid_cache = malloc(asize); + if (msgid_cache == NULL) { + logger_printf("[fidopkt] malloc(%ld) failed", asize); + binkp_temp_fail = true; + goto done; + } + bile_read(board->bile, o->type, o->id, msgid_cache, o->size); + npost_ids = o->size / sizeof(struct board_fidonet_msgid_cache); xfree(&o); - xfree(&pdata); - - if (memcmp(&post.fidonet_msgid, &fidonet_msgid, - sizeof(post.fidonet_msgid)) == 0) { + } else { + npost_ids = bile_ids_by_type(board->bile, BOARD_FIDONET_POST_RTYPE, + &post_ids); + msgid_cache = calloc(sizeof(struct board_fidonet_msgid_cache), + npost_ids + 1); + if (msgid_cache == NULL) { + logger_printf("[fidopkt] cmalloc failed"); + binkp_temp_fail = true; + goto done; + } + uthread_yield(); + for (n = 0; n < npost_ids; n++) { + bile_read(board->bile, BOARD_FIDONET_POST_RTYPE, post_ids[n], + &post, offsetof(struct board_fidonet_post, msgid) + + member_size(struct board_fidonet_post, msgid)); + + msgid_cache[n].id = post.id; + memcpy(&msgid_cache[n].msgid, &post.msgid, + sizeof(post.msgid)); + } + uthread_yield(); + dirty_cache = true; + } + + for (n = 0; n < npost_ids; n++) { + if (memcmp(&msgid_cache[n].msgid, &msgid, sizeof(msgid)) == 0) { #ifdef BINKP_DEBUG - logger_printf("[fidopkt] already have %s in %s, skipping", - fidopkt->msgid_orig, board->name); + logger_printf("[fidopkt] already have %s in %s (%ld), skipping", + fidopkt->msgid_orig, board->name, msgid_cache[n].id); #endif binkp_fidopkt_skipped++; goto done; @@ -658,35 +806,74 @@ binkp_zip_processor(char *filename, size_t size, unsig post.time = fidopkt->time; /* TODO: add/sub local offset */ post.body_size = fidopkt->message_len + 1; post.body = fidopkt->message; - strlcpy(post.via, "FidoNet", sizeof(post.via)); - strlcpy(post.fidonet_reply, fidopkt->reply, sizeof(post.fidonet_reply)); - strlcpy(post.fidonet_from, fidopkt->from, sizeof(post.fidonet_from)); - strlcpy(post.fidonet_to, fidopkt->to, sizeof(post.fidonet_to)); - strlcpy(post.fidonet_origin, fidopkt->origin, - sizeof(post.fidonet_origin)); - strlcpy(post.fidonet_msgid_orig, fidopkt->msgid_orig, - sizeof(post.fidonet_msgid_orig)); - memcpy(&post.fidonet_msgid, &fidonet_msgid, - sizeof(post.fidonet_msgid)); + strlcpy(post.reply, fidopkt->reply, sizeof(post.reply)); + strlcpy(post.from, fidopkt->from, sizeof(post.from)); + strlcpy(post.subject, fidopkt->subject, sizeof(post.subject)); + strlcpy(post.to, fidopkt->to, sizeof(post.to)); + strlcpy(post.origin, fidopkt->origin, sizeof(post.origin)); + strlcpy(post.msgid_orig, fidopkt->msgid_orig, sizeof(post.msgid_orig)); + memcpy(&post.msgid, &msgid, sizeof(post.msgid)); - /* - * TODO: find parent and set post.parent_post_id and/or join existing - * thread - */ - thread.subject = fidopkt->subject; - thread.subject_size = strlen(fidopkt->subject) + 1; + post.id = bile_next_id(board->bile, BOARD_FIDONET_POST_RTYPE); - if (board_post_create(board, &thread, &post) == 0) { + ret = bile_marshall_object(board->bile, + board_fidonet_post_object_fields, + nboard_fidonet_post_object_fields, &post, &pdata, &psize, "post"); + if (ret != 0 || psize == 0) { + logger_printf("[fidopkt] failed to marshall new post %s %s", + fidopkt->area, fidopkt->msgid_orig); + binkp_temp_fail = true; + goto done; + } + if (bile_write(board->bile, BOARD_FIDONET_POST_RTYPE, post.id, pdata, + psize) != psize) { + logger_printf("[fidopkt] failed to save new post %s %s: %d", + fidopkt->area, fidopkt->msgid_orig, bile_error(board->bile)); + binkp_temp_fail = true; + xfree(&pdata); + goto done; + } + xfree(&pdata); + + /* we already allocated one empty space at the end of the cache */ + msgid_cache[npost_ids].id = post.id; + memcpy(&msgid_cache[npost_ids].msgid, &post.msgid, sizeof(post.msgid)); + npost_ids++; + dirty_cache = true; + + found = false; + for (n = 0; n < binkp_ntouched_boards; n++) { + if (binkp_touched_boards[n] == board->id) { + found = true; + break; + } + } + if (!found) { + EXPAND_TO_FIT(binkp_touched_boards, + binkp_touched_boards_size, (binkp_ntouched_boards * sizeof(long)), + sizeof(long), sizeof(long)); + binkp_touched_boards[binkp_ntouched_boards++] = board->id; + } + #ifdef BINKP_DEBUG - logger_printf("[fidopkt] imported %s %s as %ld", - fidopkt->area, fidopkt->msgid_orig, post.id); + logger_printf("[fidopkt] imported %s %s as %ld", + fidopkt->area, fidopkt->msgid_orig, post.id); #endif - binkp_fidopkt_imported++; - } else { - logger_printf("[fidopkt] failed importing %s %s", - fidopkt->area, fidopkt->msgid_orig); + binkp_fidopkt_imported++; + uthread_yield(); + +done: + if (dirty_cache) { + cache_size = npost_ids * sizeof(struct board_fidonet_msgid_cache); + if (bile_write(board->bile, BOARD_FIDONET_MSGID_CACHE_RTYPE, 1, + msgid_cache, cache_size) != cache_size) { + logger_printf("[fidopkt] failed to save msgid cache for %s: %d", + fidopkt->area, bile_error(board->bile)); + binkp_temp_fail = true; + } } + if (msgid_cache != NULL) + free(msgid_cache); -done: fidopkt_free(&fidopkt); } --- binkp.h Thu Feb 23 08:48:55 2023 +++ binkp.h Tue Feb 28 18:32:02 2023 @@ -66,7 +66,8 @@ struct binkp_connection { bool done; }; -void binkp_thread(struct uthread *uthread, void *arg); +extern struct uthread *binkp_thread; +void binkp_process(struct uthread *uthread, void *arg); struct binkp_connection * binkp_connect(char *, unsigned short); void binkp_connection_free(struct binkp_connection **); --- board.c Sat Feb 25 15:41:11 2023 +++ board.c Tue Feb 28 17:51:52 2023 @@ -96,20 +96,35 @@ const struct bile_object_field board_post_object_field member_size(struct board_post, parent_post_id), -1 }, { offsetof(struct board_post, via), member_size(struct board_post, via), -1 }, - { offsetof(struct board_post, fidonet_reply), - member_size(struct board_post, fidonet_reply), -1 }, - { offsetof(struct board_post, fidonet_from), - member_size(struct board_post, fidonet_from), -1 }, - { offsetof(struct board_post, fidonet_to), - member_size(struct board_post, fidonet_to), -1 }, - { offsetof(struct board_post, fidonet_origin), - member_size(struct board_post, fidonet_origin), -1 }, - { offsetof(struct board_post, fidonet_msgid_orig), - member_size(struct board_post, fidonet_msgid_orig), -1 }, - { offsetof(struct board_post, fidonet_msgid), - member_size(struct board_post, fidonet_msgid), -1 }, +}; +const size_t nboard_post_object_fields = nitems(board_post_object_fields); + +const struct bile_object_field board_fidonet_post_object_fields[] = { + { offsetof(struct board_fidonet_post, id), + member_size(struct board_fidonet_post, id), -1 }, + { offsetof(struct board_fidonet_post, msgid), + member_size(struct board_fidonet_post, msgid), -1 }, + { offsetof(struct board_fidonet_post, time), + member_size(struct board_fidonet_post, time), -1 }, + { offsetof(struct board_fidonet_post, from), + member_size(struct board_fidonet_post, from), -1 }, + { offsetof(struct board_fidonet_post, subject), + member_size(struct board_fidonet_post, subject), -1 }, + { offsetof(struct board_fidonet_post, msgid_orig), + member_size(struct board_fidonet_post, msgid_orig), -1 }, + { offsetof(struct board_fidonet_post, origin), + member_size(struct board_fidonet_post, origin), -1 }, + { offsetof(struct board_fidonet_post, reply), + member_size(struct board_fidonet_post, reply), -1 }, + { offsetof(struct board_fidonet_post, to), + member_size(struct board_fidonet_post, to), -1 }, + { offsetof(struct board_fidonet_post, body_size), + member_size(struct board_fidonet_post, body_size), -1 }, + { offsetof(struct board_fidonet_post, body), + -1, offsetof(struct board_fidonet_post, body_size) }, }; -const size_t nboard_post_object_fields = nitems(board_post_object_fields); +const size_t nboard_fidonet_post_object_fields = + nitems(board_fidonet_post_object_fields); const struct bile_object_field board_thread_object_fields[] = { { offsetof(struct board_thread, thread_id), @@ -136,8 +151,9 @@ void board_list_posts(struct session *s, struct board size_t npost_Ids, unsigned long *post_ids, size_t page, size_t pages); short board_post_read(struct session *s, struct board *board, unsigned long id, short idx); -size_t board_find_post_ids(struct board *board, size_t *npost_ids, - unsigned long **post_ids, size_t offset, size_t limit); +size_t board_find_post_ids(struct session *s, struct board *board, + size_t *npost_ids, unsigned long **post_ids, size_t offset, + size_t limit); void board_show(struct session *s, short id) @@ -189,7 +205,7 @@ board_show(struct session *s, short id) ppp = POSTS_PER_PAGE; if (s->terminal_lines < ppp + 3) ppp = BOUND(ppp, 5, s->terminal_lines - 3); - nall_post_ids = board_find_post_ids(board, &npost_ids, + nall_post_ids = board_find_post_ids(s, board, &npost_ids, &post_ids, page * ppp, ppp); /* ceil(nall_post_ids / ppp) */ pages = (nall_post_ids + ppp - 1) / ppp; @@ -293,6 +309,7 @@ board_list_posts(struct session *s, struct board *boar struct username_cache *user; struct board_thread thread = { 0 }; struct board_post post; + struct board_fidonet_post fpost; short indent, j, k; char *data; @@ -309,77 +326,96 @@ board_list_posts(struct session *s, struct board *boar } for (n = 0; n < npost_ids; n++) { - size = bile_read_alloc(board->bile, BOARD_POST_RTYPE, post_ids[n], - &data); - bile_unmarshall_object(board->bile, board_post_object_fields, - nboard_post_object_fields, data, size, &post, sizeof(post), - false, "board_list_posts"); - xfree(&data); + if (board->fidonet_area[0]) { + size = bile_read_alloc(board->bile, BOARD_FIDONET_POST_RTYPE, + post_ids[n], &data); + bile_unmarshall_object(board->bile, + board_fidonet_post_object_fields, + nboard_fidonet_post_object_fields, data, size, &fpost, + sizeof(fpost), false, "board_list_posts"); + xfree(&data); + + strftime(time, sizeof(time), "%b %d", localtime(&fpost.time)); + + session_printf(s, "%s%2ld %c %s %- 10s {{#}}%.40s%s\r\n", + true ? "" : ansi(s, ANSI_BOLD, ANSI_END), + n + 1, + true ? ' ' : 'N', + time, + fpost.from, + fpost.subject, + true ? "" : ansi(s, ANSI_RESET, ANSI_END)); + } else { + size = bile_read_alloc(board->bile, BOARD_POST_RTYPE, + post_ids[n], &data); + bile_unmarshall_object(board->bile, board_post_object_fields, + nboard_post_object_fields, data, size, &post, sizeof(post), + false, "board_list_posts"); + xfree(&data); - if (post.thread_id != thread.thread_id) { - if (thread.thread_id) { - if (thread.subject != NULL) - xfree(&thread.subject); - if (thread.post_ids != NULL) - xfree(&thread.post_ids); - if (thread.parent_post_ids != NULL) - xfree(&thread.parent_post_ids); + if (post.thread_id != thread.thread_id) { + if (thread.thread_id) { + if (thread.subject != NULL) + xfree(&thread.subject); + if (thread.post_ids != NULL) + xfree(&thread.post_ids); + if (thread.parent_post_ids != NULL) + xfree(&thread.parent_post_ids); + } + size = bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, + post.thread_id, &data); + bile_unmarshall_object(board->bile, + board_thread_object_fields, nboard_thread_object_fields, + data, size, &thread, sizeof(thread), true, + "board_list_posts"); + xfree(&data); + + for (j = 0; j < nitems(indent_parent_ids); j++) + indent_parent_ids[j] = 0; } - size = bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, - post.thread_id, &data); - bile_unmarshall_object(board->bile, board_thread_object_fields, - nboard_thread_object_fields, data, size, &thread, - sizeof(thread), true, "board_list_posts"); - xfree(&data); - for (j = 0; j < nitems(indent_parent_ids); j++) - indent_parent_ids[j] = 0; - } - - if (board->fidonet_area[0]) - user = NULL; - else user = user_username(post.sender_user_id); - strftime(time, sizeof(time), "%b %d", localtime(&post.time)); - - if (post.parent_post_id == 0) { - indent_s[0] = '\0'; - } else { - indent = -1; - for (j = 0; j < nitems(indent_parent_ids); j++) { - if (indent_parent_ids[j] == post.parent_post_id || - indent_parent_ids[j] == 0) { - indent = j; - indent_parent_ids[j] = post.parent_post_id; - for (k = j + 1; k < nitems(indent_parent_ids); k++) { - if (indent_parent_ids[k] == 0) - break; - indent_parent_ids[k] = 0; + strftime(time, sizeof(time), "%b %d", localtime(&post.time)); + + if (post.parent_post_id == 0) { + indent_s[0] = '\0'; + } else { + indent = -1; + for (j = 0; j < nitems(indent_parent_ids); j++) { + if (indent_parent_ids[j] == post.parent_post_id || + indent_parent_ids[j] == 0) { + indent = j; + indent_parent_ids[j] = post.parent_post_id; + for (k = j + 1; k < nitems(indent_parent_ids); + k++) { + if (indent_parent_ids[k] == 0) + break; + indent_parent_ids[k] = 0; + } + break; } - break; } + if (indent == -1) + indent = nitems(indent_parent_ids) - 1; + + for (j = 0; j < indent; j++) { + indent_s[j] = ' '; + } + indent_s[j] = '`'; + indent_s[j + 1] = '-'; + indent_s[j + 2] = '>'; + indent_s[j + 3] = '\0'; } - if (indent == -1) - indent = nitems(indent_parent_ids) - 1; - - for (j = 0; j < indent; j++) { - indent_s[j] = ' '; - } - indent_s[j] = '`'; - indent_s[j + 1] = '-'; - indent_s[j + 2] = '>'; - indent_s[j + 3] = '\0'; + session_printf(s, "%s%2ld %c %s %- 10s %s{{#}}%.40s%s\r\n", + true ? "" : ansi(s, ANSI_BOLD, ANSI_END), + n + 1, + true ? ' ' : 'N', + time, + user ? user->username : "(unknown)", + post.parent_post_id != 0 && n == 0 ? "Re: " : "", + post.parent_post_id == 0 || n == 0 ? thread.subject : indent_s, + true ? "" : ansi(s, ANSI_RESET, ANSI_END)); } - session_printf(s, "%s%2ld %c %s %- 10s %s{{#}}%.40s%s\r\n", - true ? "" : ansi(s, ANSI_BOLD, ANSI_END), - n + 1, - true ? ' ' : 'N', - time, - user ? user->username : - (post.fidonet_from[0] ? post.fidonet_from : "(unknown)"), - post.parent_post_id != 0 && n == 0 ? "Re: " : "", - post.parent_post_id == 0 || n == 0 ? thread.subject : indent_s, - true ? "" : ansi(s, ANSI_RESET, ANSI_END)); } session_flush(s); @@ -407,6 +443,12 @@ board_compose(struct session *s, struct board *board, return 0; } + if (board->restricted_posting && !s->user->is_sysop) { + session_printf(s, "Posting is not available on this board.\r\n"); + session_flush(s); + return 0; + } + if (initial_body) post.body = xstrdup(initial_body, "board_compose body"); if (thread) { @@ -567,6 +609,7 @@ board_post_read(struct session *s, struct board *board char time[32], prompt[7 + member_size(struct board, name) + 8]; struct board_thread thread; struct board_post post; + struct board_fidonet_post fpost; struct username_cache *sender; struct session_menu_option *dopts = NULL; size_t size; @@ -575,62 +618,89 @@ board_post_read(struct session *s, struct board *board char *data; short cc; bool done = false, show_help = false; - - size = bile_read_alloc(board->bile, BOARD_POST_RTYPE, id, &data); - if (size == 0) - panic("failed fetching message %ld: %d", id, - bile_error(board->bile)); - bile_unmarshall_object(board->bile, board_post_object_fields, - nboard_post_object_fields, data, size, &post, sizeof(post), true, - "board_post_read post"); - xfree(&data); - size = bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, - post.thread_id, &data); - if (size == 0) - panic("failed fetching thread %ld: %d", post.thread_id, - bile_error(board->bile)); - bile_unmarshall_object(board->bile, board_thread_object_fields, - nboard_thread_object_fields, data, size, &thread, sizeof(thread), - true, "board_post_read thread"); - xfree(&data); - dopts = xmalloc(sizeof(opts), "board_post_read opts"); memcpy(dopts, opts, sizeof(opts)); - if (!(s->user && (s->user->is_sysop || - s->user->id == post.sender_user_id))) { - /* disable deleting */ - dopts[1].key[0] = '\0'; - } - sender = user_username(post.sender_user_id); - - strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", - localtime(&post.time)); - if (board->fidonet_area[0]) { - session_printf(s, "{{B}}From:{{/B}} %s\r\n", - post.fidonet_from); - session_printf(s, "{{B}}Origin:{{/B}} %s\r\n", - post.fidonet_origin); - session_printf(s, "{{B}}To:{{/B}} %s@%s\r\n", - post.fidonet_to, board->name); + size = bile_read_alloc(board->bile, BOARD_FIDONET_POST_RTYPE, id, + &data); + if (size == 0) + panic("failed fetching message %ld: %d", id, + bile_error(board->bile)); + + bile_unmarshall_object(board->bile, + board_fidonet_post_object_fields, + nboard_fidonet_post_object_fields, data, size, &fpost, + sizeof(fpost), true, "board_post_read post"); + xfree(&data); + + if (!(s->user && s->user->is_sysop)) { + /* disable deleting */ + dopts[1].key[0] = '\0'; + } + + strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", + localtime(&fpost.time)); + + session_printf(s, "{{B}}From:{{/B}} %s\r\n", fpost.from); + session_printf(s, "{{B}}Origin:{{/B}} %s\r\n", fpost.origin); + session_printf(s, "{{B}}To:{{/B}} %s@%s\r\n", fpost.to, + board->name); + session_printf(s, "{{B}}Date:{{/B}} %s %s\r\n", time, + db->config.timezone); + session_printf(s, "{{B}}Subject:{{/B}}{{#}} %s\r\n", + fpost.subject); + session_flush(s); + session_printf(s, "\r\n"); + session_output(s, fpost.body, fpost.body_size); + session_printf(s, "\r\n"); } else { + size = bile_read_alloc(board->bile, BOARD_POST_RTYPE, id, &data); + if (size == 0) + panic("failed fetching message %ld: %d", id, + bile_error(board->bile)); + bile_unmarshall_object(board->bile, board_post_object_fields, + nboard_post_object_fields, data, size, &post, sizeof(post), true, + "board_post_read post"); + xfree(&data); + + size = bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, + post.thread_id, &data); + if (size == 0) + panic("failed fetching thread %ld: %d", post.thread_id, + bile_error(board->bile)); + bile_unmarshall_object(board->bile, board_thread_object_fields, + nboard_thread_object_fields, data, size, &thread, + sizeof(thread), true, "board_post_read thread"); + xfree(&data); + + if (!(s->user && (s->user->is_sysop || + s->user->id == post.sender_user_id))) { + /* disable deleting */ + dopts[1].key[0] = '\0'; + } + + sender = user_username(post.sender_user_id); + + strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", + localtime(&post.time)); + session_printf(s, "{{B}}From:{{/B}} %s", sender ? sender->username : "(unknown)"); if (post.via[0]) session_printf(s, " (via %s)", post.via); session_printf(s, "\r\n"); session_printf(s, "{{B}}To:{{/B}} %s\r\n", board->name); + session_printf(s, "{{B}}Date:{{/B}} %s %s\r\n", time, + db->config.timezone); + session_printf(s, "{{B}}Subject:{{/B}}{{#}} %s%s\r\n", + (post.parent_post_id ? "Re: " : ""), thread.subject); + session_flush(s); + session_printf(s, "\r\n"); + session_output(s, post.body, post.body_size); + session_printf(s, "\r\n"); } - session_printf(s, "{{B}}Date:{{/B}} %s %s\r\n", time, - db->config.timezone); - session_printf(s, "{{B}}Subject:{{/B}}{{#}} %s%s\r\n", - (post.parent_post_id ? "Re: " : ""), thread.subject); - session_flush(s); - session_printf(s, "\r\n"); - session_output(s, post.body, post.body_size); - session_printf(s, "\r\n"); snprintf(prompt, sizeof(prompt), "Boards:%s:%d", board->name, idx); @@ -657,10 +727,16 @@ board_post_read(struct session *s, struct board *board session_printf(s, "%c\r\n", cc); session_flush(s); - board_delete_post(s, board, &post, &thread); - - session_logf(s, "Deleted post %ld (thread %ld)", post.id, - thread.thread_id); + if (board->fidonet_area[0]) { + board_delete_fidonet_post(s, board, &fpost); + session_logf(s, "Deleted FidoNet post %ld", + post.id); + } else { + board_delete_post(s, board, &post, &thread); + + session_logf(s, "Deleted post %ld (thread %ld)", + post.id, thread.thread_id); + } session_printf(s, "\r\n{{B}}Post deleted!{{/B}}\r\n"); session_flush(s); @@ -696,117 +772,63 @@ board_post_read(struct session *s, struct board *board xfree(&dopts); - if (post.body != NULL) - xfree(&post.body); - if (thread.subject != NULL) - xfree(&thread.subject); - if (thread.post_ids != NULL) - xfree(&thread.post_ids); - if (thread.parent_post_ids != NULL) - xfree(&thread.parent_post_ids); - + if (board->fidonet_area[0]) { + if (fpost.body != NULL) + xfree(&fpost.body); + } else { + if (post.body != NULL) + xfree(&post.body); + if (thread.subject != NULL) + xfree(&thread.subject); + if (thread.post_ids != NULL) + xfree(&thread.post_ids); + if (thread.parent_post_ids != NULL) + xfree(&thread.parent_post_ids); + } + return ret; } size_t -board_find_post_ids(struct board *board, size_t *npost_ids, - unsigned long **post_ids, size_t offset, size_t limit) +board_find_post_ids(struct session *s, struct board *board, + size_t *npost_ids, unsigned long **post_ids, size_t offset, size_t limit) { - struct board_thread_map { - unsigned long id; - time_t time; - size_t nposts; - }; - struct bile_object *o; - struct board_thread_map *thread_map = NULL, tmp_map; - struct board_thread thread; - size_t nthreads, nall_post_ids, n, post_ids_size, size, seen_posts; - short i, j; - char *data; - - post_ids_size = 0; + unsigned long *all_post_ids; + size_t n, size, nall_post_ids; + *post_ids = NULL; *npost_ids = 0; - nall_post_ids = 0; - - nthreads = bile_count_by_type(board->bile, BOARD_THREAD_RTYPE); - if (nthreads == 0) - return 0; - - thread_map = xcalloc(sizeof(struct board_thread_map), nthreads, - "board_find_post_ids thread_map"); - - for (n = 0; (o = bile_get_nth_of_type(board->bile, n, - BOARD_THREAD_RTYPE)); n++) { - if (n >= nthreads) { - xfree(&o); - break; - } - bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, o->id, &data); - bile_unmarshall_object(board->bile, board_thread_object_fields, - nboard_thread_object_fields, data, o->size, &thread, - sizeof(thread), false, "board_find_post_ids 1"); - xfree(&data); - xfree(&o); - thread_map[n].id = thread.thread_id; - thread_map[n].time = thread.last_post_at; - thread_map[n].nposts = thread.nposts; - nall_post_ids += thread.nposts; - } + size = bile_read_alloc(board->bile, BOARD_SORTED_IDS_RTYPE, 1, + &all_post_ids); + if (all_post_ids == NULL) { + session_printf(s, "%sPlease wait, re-indexing board posts...%s", + ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END)); + session_flush(s); + nall_post_ids = board_index_sorted_post_ids(board, &all_post_ids); + session_output(s, "\r\n", 2); + session_flush(s); + if (nall_post_ids == 0) + return 0; + } else + nall_post_ids = size / sizeof(long); + + *post_ids = xcalloc(sizeof(long), MIN(limit, nall_post_ids), + "post_ids"); - if (offset >= nall_post_ids) - goto done; + for (n = 0; n < nall_post_ids; n++) { + if (n < offset) + continue; + + (*post_ids)[*npost_ids] = all_post_ids[n]; + (*npost_ids)++; - /* sort by last post date descending */ - for (i = 0; i < nthreads; i++) { - for (j = 0; j < nthreads - i - 1; j++) { - if (thread_map[j].time < thread_map[j + 1].time) { - tmp_map = thread_map[j]; - thread_map[j] = thread_map[j + 1]; - thread_map[j + 1] = tmp_map; - } - } - } - - /* gather threads until we run out of space for posts */ - seen_posts = 0; - for (j = 0; j < nthreads; j++) { - size = bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, - thread_map[j].id, &data); - bile_unmarshall_object(board->bile, board_thread_object_fields, - nboard_thread_object_fields, data, size, &thread, sizeof(thread), - true, "board_find_post_ids 2"); - xfree(&data); - - for (i = 0; i < thread.nposts; i++) { - if (offset > 0 && seen_posts++ < offset) - continue; - - EXPAND_TO_FIT(*post_ids, post_ids_size, - ((*npost_ids) + 1) * sizeof(long), sizeof(long), - sizeof(long) * 16); - (*post_ids)[*npost_ids] = thread.post_ids[i]; - (*npost_ids)++; - - if (*npost_ids >= limit) - break; - } - - if (thread.subject != NULL) - xfree(&thread.subject); - if (thread.post_ids != NULL) - xfree(&thread.post_ids); - if (thread.parent_post_ids != NULL) - xfree(&thread.parent_post_ids); - if (*npost_ids >= limit) break; } -done: - if (thread_map != NULL) - xfree(&thread_map); + if (all_post_ids != NULL) + xfree(&all_post_ids); return nall_post_ids; } @@ -1004,4 +1026,166 @@ board_delete_post(struct session *s, struct board *boa } xfree(&data); +} + +void +board_delete_fidonet_post(struct session *s, struct board *board, + struct board_fidonet_post *post) +{ + size_t size, npost_ids, n; + unsigned long *post_ids; + + bile_delete(board->bile, BOARD_FIDONET_POST_RTYPE, post->id, + BILE_DELETE_FLAG_PURGE); + + size = bile_read_alloc(board->bile, BOARD_SORTED_IDS_RTYPE, 1, + &post_ids); + if (size == 0 || post_ids == NULL) + return; + + npost_ids = size / sizeof(long); + + for (n = 0; n < npost_ids; n++) { + if (post_ids[n] == post->id) { + for (; n < npost_ids - 1; n++) + post_ids[n] = post_ids[n + 1]; + npost_ids--; + break; + } + } + + bile_write(board->bile, BOARD_SORTED_IDS_RTYPE, 1, post_ids, + sizeof(long) * npost_ids); +} + +size_t +board_index_sorted_post_ids(struct board *board, + unsigned long **sorted_ids) +{ + struct board_id_time_map { + unsigned long id; + time_t time; + size_t nposts; + }; + struct board_fidonet_post fpost; + struct board_thread thread; + size_t size, i, j, n, npost_ids, nthread_ids; + unsigned long *post_ids, *thread_ids, tmp_id; + struct board_id_time_map *id_map, *thread_map, tmp_map; + char *data; + + if (board->fidonet_area[0]) { + npost_ids = bile_ids_by_type(board->bile, BOARD_FIDONET_POST_RTYPE, + &post_ids); + if (npost_ids == 0) + goto write_out; + + id_map = xcalloc(sizeof(struct board_id_time_map), + npost_ids, "board_id_time_map"); + + for (n = 0; n < npost_ids; n++) { + /* only read as far as the time */ + bile_read(board->bile, BOARD_FIDONET_POST_RTYPE, + post_ids[n], &fpost, + offsetof(struct board_fidonet_post, time) + + member_size(struct board_fidonet_post, time)); + + id_map[n].id = fpost.id; + id_map[n].time = fpost.time; + } + + /* sort by date descending */ + for (i = 0; i < npost_ids; i++) { + for (j = 0; j < npost_ids - i - 1; j++) { + if (id_map[j].time < id_map[j + 1].time) { + tmp_map = id_map[j]; + id_map[j] = id_map[j + 1]; + id_map[j + 1] = tmp_map; + } + } + } + + for (j = 0; j < npost_ids; j++) + post_ids[j] = id_map[j].id; + + xfree(&id_map); + } else { + nthread_ids = bile_ids_by_type(board->bile, BOARD_THREAD_RTYPE, + &thread_ids); + if (nthread_ids == 0) + goto write_out; + + thread_map = xcalloc(sizeof(struct board_id_time_map), + nthread_ids, "board_id_time_map"); + + npost_ids = 0; + for (n = 0; n < nthread_ids; n++) { + size = bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, + thread_ids[n], &data); + bile_unmarshall_object(board->bile, board_thread_object_fields, + nboard_thread_object_fields, data, size, &thread, + sizeof(thread), false, "board_id_time_map"); + xfree(&data); + + thread_map[n].id = thread.thread_id; + thread_map[n].time = thread.last_post_at; + thread_map[n].nposts = thread.nposts; + npost_ids += thread.nposts; + } + + xfree(&thread_ids); + + /* sort by last post date descending */ + for (i = 0; i < nthread_ids; i++) { + for (j = 0; j < nthread_ids - i - 1; j++) { + if (thread_map[j].time < thread_map[j + 1].time) { + tmp_map = thread_map[j]; + thread_map[j] = thread_map[j + 1]; + thread_map[j + 1] = tmp_map; + } + } + } + + post_ids = xcalloc(sizeof(long), npost_ids, "post_ids"); + + npost_ids = 0; + for (j = 0; j < nthread_ids; j++) { + size = bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, + thread_map[j].id, &data); + bile_unmarshall_object(board->bile, board_thread_object_fields, + nboard_thread_object_fields, data, size, &thread, + sizeof(thread), true, "board_index_post_Ids"); + xfree(&data); + + /* these are already sorted, and we want to keep thread sort */ + for (i = 0; i < thread.nposts; i++) + post_ids[npost_ids++] = thread.post_ids[i]; + + if (thread.subject != NULL) + xfree(&thread.subject); + if (thread.post_ids != NULL) + xfree(&thread.post_ids); + if (thread.parent_post_ids != NULL) + xfree(&thread.parent_post_ids); + } + + free(&thread_map); + } + +write_out: + if (npost_ids == 0) { + bile_delete(board->bile, BOARD_SORTED_IDS_RTYPE, 1, + BILE_DELETE_FLAG_PURGE); + if (sorted_ids != NULL) + *sorted_ids = NULL; + return 0; + } + + bile_write(board->bile, BOARD_SORTED_IDS_RTYPE, 1, post_ids, + sizeof(long) * npost_ids); + if (sorted_ids == NULL) + xfree(&post_ids); + else + *sorted_ids = post_ids; + return npost_ids; } --- board.h Sat Feb 25 17:22:21 2023 +++ board.h Tue Feb 28 15:25:18 2023 @@ -22,19 +22,6 @@ #include "session.h" #include "settings.h" -struct board_post_fidonet_msgid { - unsigned long id; - u_int16_t zone; - u_int16_t net; - u_int16_t node; - u_int16_t point; -}; - -struct board_fidonet_msgid_id_map_entry { - struct board_post_fidonet_msgid msgid; - unsigned long id; -}; - struct board { unsigned long id; char name[32]; @@ -47,8 +34,6 @@ struct board { char fidonet_area[32]; struct bile *bile; - struct board_fidonet_msgid_id_map_entry *fidonet_msgid_map; - size_t nfidonet_msgid_map_entries; }; extern const struct struct_field board_fields[]; @@ -56,6 +41,14 @@ extern const size_t nboard_fields; extern const struct bile_object_field board_object_fields[]; extern const size_t nboard_object_fields; +struct board_fidonet_msgid { + unsigned long id; + u_int16_t zone; + u_int16_t net; + u_int16_t node; + u_int16_t point; +}; + struct board_post { unsigned long id; unsigned long thread_id; @@ -65,16 +58,26 @@ struct board_post { char *body; unsigned long parent_post_id; char via[10]; - char fidonet_reply[32]; - char fidonet_from[32]; - char fidonet_to[32]; - char fidonet_origin[72]; - char fidonet_msgid_orig[40]; - struct board_post_fidonet_msgid fidonet_msgid; }; extern const struct bile_object_field board_post_object_fields[]; extern const size_t nboard_post_object_fields; +struct board_fidonet_post { + unsigned long id; + struct board_fidonet_msgid msgid; + time_t time; + char from[32]; + char subject[80]; + char msgid_orig[64]; + char origin[64]; + char reply[32]; + char to[32]; + size_t body_size; + char *body; +}; +extern const struct bile_object_field board_fidonet_post_object_fields[]; +extern const size_t nboard_fidonet_post_object_fields; + struct board_thread { unsigned long thread_id; time_t last_post_at; @@ -92,5 +95,9 @@ short board_post_create(struct board *board, struct bo struct board_post *post); void board_delete_post(struct session *s, struct board *board, struct board_post *post, struct board_thread *thread); +void board_delete_fidonet_post(struct session *s, struct board *board, + struct board_fidonet_post *post); +size_t board_index_sorted_post_ids(struct board *board, + unsigned long **sorted_ids); #endif --- db.c Sat Feb 25 17:21:42 2023 +++ db.c Tue Feb 28 14:54:15 2023 @@ -89,21 +89,25 @@ struct struct_field config_fields[] = { offsetof(struct config, session_log_days), 1, USHRT_MAX }, - { "FidoNet Binkp Host", CONFIG_TYPE_STRING, + { "FidoNet Binkp Hub Hostname", CONFIG_TYPE_STRING, offsetof(struct config, binkp_hostname), 0, member_size(struct config, binkp_hostname) }, - { "FidoNet Binkp Port", CONFIG_TYPE_SHORT, + { "FidoNet Binkp Hub Port", CONFIG_TYPE_SHORT, offsetof(struct config, binkp_port), 0, 65535 }, { "FidoNet Binkp Node Address", CONFIG_TYPE_STRING, offsetof(struct config, binkp_node_addr), 0, member_size(struct config, binkp_node_addr) }, - { "FidoNet Binkp Password", CONFIG_TYPE_STRING, + { "FidoNet Binkp Password", CONFIG_TYPE_PASSWORD, offsetof(struct config, binkp_password), 0, member_size(struct config, binkp_password) }, { "FidoNet Binkp Fetch Seconds", CONFIG_TYPE_LONG, offsetof(struct config, binkp_interval_seconds), - 1, ULONG_MAX } + 1, ULONG_MAX }, + { "FidoNet Binkp Delete After Processing", CONFIG_TYPE_BOOLEAN, + offsetof(struct config, binkp_delete_done), + 0, 0 }, + }; size_t nconfig_fields = nitems(config_fields); @@ -337,6 +341,7 @@ db_migrate(struct db *tdb, short is_new, Str255 fullpa tdb->config.session_log_days = 21; tdb->config.binkp_port = 24554; tdb->config.binkp_interval_seconds = (60 * 60 * 3); + tdb->config.binkp_delete_done = 0; db_config_save(tdb); /* create a default sysop user */ @@ -577,6 +582,7 @@ db_migrate(struct db *tdb, short is_new, Str255 fullpa new_config.binkp_node_addr[0] = '\0'; new_config.binkp_password[0] = '\0'; new_config.binkp_interval_seconds = (60 * 60 * 3); + new_config.binkp_delete_done = 0; bile_write(tdb->bile, DB_CONFIG_RTYPE, 1, &new_config, sizeof(new_config)); @@ -628,10 +634,10 @@ db_migrate(struct db *tdb, short is_new, Str255 fullpa break; } case 14: { - /* 14->15, boards and posts get fido fields */ + /* 14->15, boards get a fido field */ struct bile_object *o; struct board *board; - size_t nids, grow, size, n, j; + size_t nids, grow, size, n; unsigned long *ids; grow = member_size(struct board, fidonet_area); @@ -649,32 +655,6 @@ db_migrate(struct db *tdb, short is_new, Str255 fullpa db_cache_boards(tdb); - grow = member_size(struct board_post, fidonet_reply) + - member_size(struct board_post, fidonet_from) + - member_size(struct board_post, fidonet_to) + - member_size(struct board_post, fidonet_origin) + - member_size(struct board_post, fidonet_msgid_orig) + - member_size(struct board_post, fidonet_msgid); - - for (n = 0; n < tdb->nboards; n++) { - board = &tdb->boards[n]; - nids = bile_sorted_ids_by_type(board->bile, - BOARD_POST_RTYPE, &ids); - if (!nids) - continue; - for (j = 0; j < nids; j++) { - o = bile_find(board->bile, BOARD_POST_RTYPE, ids[j]); - progress("Growing board %ld post %ld/%ld...", - n + 1, j + 1, nids); - size = o->size + grow; - if (bile_resize(board->bile, o->type, o->id, - size) != size) - panic("failed resizing board %ld post %ld", - board->id, o->id); - xfree(&o); - } - xfree(&ids); - } break; } } @@ -733,8 +713,6 @@ db_cache_boards(struct db *tdb) for (n = 0; n < tdb->nboards; n++) { if (tdb->boards[n].bile) bile_close(tdb->boards[n].bile); - if (tdb->boards[n].fidonet_msgid_map != NULL) - xfree(&tdb->boards[n].fidonet_msgid_map); } xfree(&tdb->boards); } --- db.h Sat Feb 25 16:52:58 2023 +++ db.h Tue Feb 28 19:27:37 2023 @@ -53,7 +53,9 @@ #define BOARD_FILENAME_EXT "brd" #define BOARD_THREAD_RTYPE 'BDTH' #define BOARD_POST_RTYPE 'BDPS' -#define BOARD_FIDONET_MSGID_CACHE_RTYPE 'BDFC' +#define BOARD_FIDONET_POST_RTYPE 'BDFP' +#define BOARD_FIDONET_MSGID_CACHE_RTYPE 'BDMC' +#define BOARD_SORTED_IDS_RTYPE 'BDDX' #define FOLDER_FILENAME_EXT "fld" #define FOLDER_FILE_RTYPE 'FILE' @@ -91,6 +93,7 @@ struct config { unsigned short binkp_port; char binkp_password[32]; unsigned long binkp_interval_seconds; + short binkp_delete_done; }; extern struct struct_field config_fields[];