+ assert(which_cptr_is_relevant >= 0);
+ cprintf("<iq id=\"unsolicited_%x\" type=\"result\">", ++unsolicited_id);
+ cprintf("<query xmlns=\"jabber:iq:roster\">");
+ xmpp_roster_item(&cptr[which_cptr_is_relevant]);
+ cprintf("</query></iq>");
+
+ /* Transmit presence information */
+ xmpp_indicate_presence(presence_jid);
+ }
+
+ if (visible_sessions == 0) {
+ syslog(LOG_DEBUG, "Telling session %d that <%s> logged out\n",
+ CC->cs_pid, presence_jid);
+ xmpp_destroy_buddy(presence_jid, 0); /* non aggressive presence update */
+ }
+
+ free(cptr);
+}
+
+
+
+void xmpp_fetch_mortuary_backend(long msgnum, void *userdata) {
+ HashList *mortuary = (HashList *) userdata;
+ struct CtdlMessage *msg;
+ char *ptr = NULL;
+ char *lasts = NULL;
+
+ msg = CtdlFetchMessage(msgnum, 1);
+ if (msg == NULL) {
+ return;
+ }
+
+ /* now add anyone we find into the hashlist */
+
+ /* skip past the headers */
+ ptr = strstr(msg->cm_fields[eMesageText], "\n\n");
+ if (ptr != NULL) {
+ ptr += 2;
+ }
+ else {
+ ptr = strstr(msg->cm_fields[eMesageText], "\n\r\n");
+ if (ptr != NULL) {
+ ptr += 3;
+ }
+ }
+
+ /* the remaining lines are addresses */
+ if (ptr != NULL) {
+ ptr = strtok_r(ptr, "\n", &lasts);
+ while (ptr != NULL) {
+ char *pch = strdup(ptr);
+ Put(mortuary, pch, strlen(pch), pch, NULL);
+ ptr = strtok_r(NULL, "\n", &lasts);
+ }
+ }
+
+ CM_Free(msg);
+}
+
+
+
+/*
+ * Fetch the "mortuary" - a list of dead buddies which we keep around forever
+ * so we can remove them from any client's roster that still has them listed
+ */
+HashList *xmpp_fetch_mortuary(void) {
+ HashList *mortuary = NewHash(1, NULL);
+ if (!mortuary) {
+ syslog(LOG_ALERT, "NewHash() failed!\n");
+ return(NULL);
+ }
+
+ if (CtdlGetRoom(&CC->room, USERCONFIGROOM) != 0) {
+ /* no config room exists - no further processing is required. */
+ return(mortuary);
+ }
+ CtdlForEachMessage(MSGS_LAST, 1, NULL, XMPPMORTUARY, NULL,
+ xmpp_fetch_mortuary_backend, (void *)mortuary );
+
+ return(mortuary);
+}
+
+
+
+/*
+ * Fetch the "mortuary" - a list of dead buddies which we keep around forever
+ * so we can remove them from any client's roster that still has them listed
+ */
+void xmpp_store_mortuary(HashList *mortuary) {
+ HashPos *HashPos;
+ long len;
+ void *Value;
+ const char *Key;
+ StrBuf *themsg;
+
+ themsg = NewStrBuf();
+ StrBufPrintf(themsg, "Content-type: " XMPPMORTUARY "\n"
+ "Content-transfer-encoding: 7bit\n"
+ "\n"
+ );
+
+ HashPos = GetNewHashPos(mortuary, 0);
+ while (GetNextHashPos(mortuary, HashPos, &len, &Key, &Value) != 0)
+ {
+ StrBufAppendPrintf(themsg, "%s\n", (char *)Value);
+ }
+ DeleteHashPos(&HashPos);
+
+ /* FIXME temp crap
+ StrBufAppendPrintf(themsg, "foo@bar.com\n");
+ StrBufAppendPrintf(themsg, "baz@quux.com\n");
+ StrBufAppendPrintf(themsg, "haha%c\n", 1);
+ StrBufAppendPrintf(themsg, "baaaz@quux.com\n");
+ StrBufAppendPrintf(themsg, "baaaz@quuuuuux.com\n");
+ */
+
+ /* Delete the old mortuary */
+ CtdlDeleteMessages(USERCONFIGROOM, NULL, 0, XMPPMORTUARY);
+
+ /* And save the new one to disk */
+ quickie_message("Citadel", NULL, NULL, USERCONFIGROOM, ChrPtr(themsg), 4, "XMPP Mortuary");
+ FreeStrBuf(&themsg);
+}
+
+
+
+/*
+ * Upon logout we make an attempt to delete the whole roster, in order to
+ * try to keep "ghost" buddies from remaining in the client-side roster.
+ *
+ * Since the client is probably not still alive, also remember the current
+ * roster for next time so we can delete dead buddies then.
+ */
+void xmpp_massacre_roster(void)
+{
+ struct CitContext *cptr;
+ int nContexts, i;
+ HashList *mortuary = xmpp_fetch_mortuary();
+
+ cptr = CtdlGetContextArray(&nContexts);
+ if (cptr) {