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[];