+/*
+ * This is the back end for flush_conversations_to_disk()
+ * At this point we've isolated a single conversation (struct imlog)
+ * and are ready to write it to disk.
+ */
+void flush_individual_conversation(struct imlog *im) {
+ struct CtdlMessage *msg;
+ long msgnum = 0;
+ char roomname[ROOMNAMELEN];
+
+ msg = malloc(sizeof(struct CtdlMessage));
+ memset(msg, 0, sizeof(struct CtdlMessage));
+ msg->cm_magic = CTDLMESSAGE_MAGIC;
+ msg->cm_anon_type = MES_NORMAL;
+ msg->cm_format_type = FMT_CITADEL;
+ msg->cm_fields['A'] = strdup("Citadel");
+ msg->cm_fields['O'] = strdup(PAGELOGROOM);
+ msg->cm_fields['N'] = strdup(NODENAME);
+ msg->cm_fields['M'] = strdup(ChrPtr(im->conversation));
+
+ /* Start with usernums[1] because it's guaranteed to be higher than usernums[0],
+ * so if there's only one party, usernums[0] will be zero but usernums[1] won't.
+ * Create the room if necessary. Note that we create as a type 5 room rather
+ * than 4, which indicates that it's a personal room but we've already supplied
+ * the namespace prefix.
+ *
+ * In the unlikely event that usernums[1] is zero, a room with an invalid namespace
+ * prefix will be created. That's ok because the auto-purger will clean it up later.
+ */
+ snprintf(roomname, sizeof roomname, "%010ld.%s", im->usernums[1], PAGELOGROOM);
+ create_room(roomname, 5, "", 0, 1, 1, VIEW_BBS);
+ msgnum = CtdlSubmitMsg(msg, NULL, roomname, 0);
+ CtdlFreeMessage(msg);
+
+ /* If there is a valid user number in usernums[0], save a copy for them too. */
+ if (im->usernums[0] > 0) {
+ snprintf(roomname, sizeof roomname, "%010ld.%s", im->usernums[0], PAGELOGROOM);
+ create_room(roomname, 5, "", 0, 1, 1, VIEW_BBS);
+ CtdlSaveMsgPointerInRoom(roomname, msgnum, 0, NULL);
+ }
+
+ /* Finally, if we're logging instant messages globally, do that now. */
+ if (!IsEmptyStr(config.c_logpages)) {
+ create_room(config.c_logpages, 3, "", 0, 1, 1, VIEW_BBS);
+ CtdlSaveMsgPointerInRoom(config.c_logpages, msgnum, 0, NULL);
+ }
+
+}
+
+/*
+ * Locate instant message conversations which have gone idle
+ * (or, if the server is shutting down, locate *all* conversations)
+ * and flush them to disk (in the participants' log rooms, etc.)
+ */
+void flush_conversations_to_disk(time_t if_older_than) {
+
+ struct imlog *flush_these = NULL;
+ struct imlog *dont_flush_these = NULL;
+ struct imlog *imptr = NULL;
+
+ begin_critical_section(S_IM_LOGS);
+ while (imlist)
+ {
+ imptr = imlist;
+ imlist = imlist->next;
+ if ((time(NULL) - imptr->lastmsg) > if_older_than)
+ {
+ /* This conversation qualifies. Move it to the list of ones to flush. */
+ imptr->next = flush_these;
+ flush_these = imptr;
+ }
+ else {
+ /* Move it to the list of ones not to flush. */
+ imptr->next = dont_flush_these;
+ dont_flush_these = imptr;
+ }
+ }
+ imlist = dont_flush_these;
+ end_critical_section(S_IM_LOGS);
+
+ /* We are now outside of the critical section, and we are the only thread holding a
+ * pointer to a linked list of conversations to be flushed to disk.
+ */
+ while (flush_these) {
+
+ flush_individual_conversation(flush_these);
+ imptr = flush_these;
+ flush_these = flush_these->next;
+ FreeStrBuf(&imptr->conversation);
+ free(imptr);
+ }
+}
+
+
+
+void chat_timer(void) {
+ flush_conversations_to_disk(300); /* Anything that hasn't peeped in more than 5 minutes */
+}
+
+void chat_shutdown(void) {
+ flush_conversations_to_disk(0); /* Get it ALL onto disk NOW. */
+}