/* * list users in chat in this room */ void roomchat_rwho(char *argbuf) { struct CitContext *nptr; int nContexts, i; if ((CC->cs_flags & CS_CHAT) == 0) { cprintf("%d Session is not in chat mode.\n", ERROR); return; } cprintf("%d%c \n", LISTING_FOLLOWS, CtdlCheckExpress() ); nptr = CtdlGetContextArray(&nContexts) ; // grab a copy of the wholist if (nptr) { for (i=0; i<nContexts; i++) { // list the users if ( (nptr[i].room.QRnumber == CC->room.QRnumber) && (nptr[i].cs_flags & CS_CHAT) ) { cprintf("%s\n", nptr[i].user.fullname); } } free(nptr); // free our copy } cprintf("000\n"); }
/* * When a user logs in or out of the local Citadel system, notify all XMPP sessions about it. * THIS FUNCTION HAS A BUG IN IT THAT ENUMERATES THE SESSIONS WRONG. */ void xmpp_presence_notify(char *presence_jid, int event_type) { struct CitContext *cptr; static int unsolicited_id = 12345; int visible_sessions = 0; int nContexts, i; int which_cptr_is_relevant = (-1); if (IsEmptyStr(presence_jid)) return; if (CC->kill_me) return; cptr = CtdlGetContextArray(&nContexts); if (!cptr) { return; } /* Count the visible sessions for this user */ for (i=0; i<nContexts; i++) { if ( (!strcasecmp(cptr[i].cs_inet_email, presence_jid)) && (xmpp_is_visible(&cptr[i], CC)) ) { ++visible_sessions; which_cptr_is_relevant = i; } } syslog(LOG_DEBUG, "%d sessions for <%s> are now visible to session %d\n", visible_sessions, presence_jid, CC->cs_pid); if ( (event_type == XMPP_EVT_LOGIN) && (visible_sessions == 1) ) { syslog(LOG_DEBUG, "Telling session %d that <%s> logged in\n", CC->cs_pid, presence_jid); /* Do an unsolicited roster update that adds a new contact. */ 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 CtdlDisableHouseKeeping(void) { int ActiveBackgroundJobs; int do_housekeeping_now = 0; struct CitContext *nptr; int nContexts, i; retry_block_housekeeping: syslog(LOG_INFO, "trying to disable housekeeping services"); begin_critical_section(S_HOUSEKEEPING); if (housekeeping_in_progress == 0) { do_housekeeping_now = 1; housekeeping_in_progress = 1; } end_critical_section(S_HOUSEKEEPING); if (do_housekeeping_now == 0) { usleep(1000000); goto retry_block_housekeeping; } syslog(LOG_INFO, "checking for running server Jobs"); retry_wait_for_contexts: /* So that we don't keep the context list locked for a long time * we create a copy of it first */ ActiveBackgroundJobs = 0; nptr = CtdlGetContextArray(&nContexts) ; if (nptr) { for (i=0; i<nContexts; i++) { if ((nptr[i].state != CON_SYS) || (nptr[i].IO == NULL) || (nptr[i].lastcmd == 0)) continue; ActiveBackgroundJobs ++; syslog(LOG_INFO, "Job CC[%d] active; use TERM if you don't want to wait for it",nptr[i].cs_pid); } free(nptr); } if (ActiveBackgroundJobs != 0) { syslog(LOG_INFO, "found %d running jobs, need to wait", ActiveBackgroundJobs); usleep(5000000); goto retry_wait_for_contexts; } syslog(LOG_INFO, "Housekeeping disabled now."); }
/* * Initial dump of the entire wholist */ void xmpp_wholist_presence_dump(void) { struct CitContext *cptr = NULL; int nContexts, i; cptr = CtdlGetContextArray(&nContexts); if (!cptr) { return; } for (i=0; i<nContexts; i++) { if (xmpp_is_visible(&cptr[i], CC)) { xmpp_indicate_presence(cptr[i].cs_inet_email); } } free(cptr); }
/* * Return the results for a "jabber:iq:roster:query" * * Since we are not yet managing a roster, we simply return the entire wholist * (minus any entries for this user -- don't tell me about myself) * */ void xmpp_iq_roster_query(void) { struct CitContext *cptr; int nContexts, i; syslog(LOG_DEBUG, "Roster push!"); cprintf("<query xmlns=\"jabber:iq:roster\">"); cptr = CtdlGetContextArray(&nContexts); if (cptr) { for (i=0; i<nContexts; i++) { if (xmpp_is_visible(&cptr[i], CC)) { XMPP_syslog(LOG_DEBUG, "Rosterizing %s\n", cptr[i].user.fullname); xmpp_roster_item(&cptr[i]); } } free (cptr); } cprintf("</query>"); }
/* * Stupidly, XMPP does not specify a way to tell the client to flush its client-side roster * and prepare to receive a new one. So instead we remember every buddy we've ever told the * client about, and push delete operations out at the beginning of a session. * * We omit any users who happen to be online right now, but we still keep them in the mortuary, * which needs to be maintained as a list of every buddy the user has ever seen. We don't know * when they're connecting from the same client and when they're connecting from a different client, * so we have no guarantee of what is in the client side roster at connect time. */ void xmpp_delete_old_buddies_who_no_longer_exist_from_the_client_roster(void) { long len; void *Value; const char *Key; struct CitContext *cptr; int nContexts, i; int online_now = 0; HashList *mortuary = xmpp_fetch_mortuary(); HashPos *HashPos = GetNewHashPos(mortuary, 0); /* we need to omit anyone who is currently online */ cptr = CtdlGetContextArray(&nContexts); /* go through the list of users in the mortuary... */ while (GetNextHashPos(mortuary, HashPos, &len, &Key, &Value) != 0) { online_now = 0; if (cptr) for (i=0; i<nContexts; i++) { if (xmpp_is_visible(&cptr[i], CC)) { if (!strcasecmp(cptr[i].cs_inet_email, (char *)Value)) { online_now = 1; } } } if (!online_now) { xmpp_destroy_buddy((char *)Value, 1); /* aggressive presence update */ } } DeleteHashPos(&HashPos); DeleteHash(&mortuary); free(cptr); }
/* * 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) { for (i=0; i<nContexts; i++) { if (xmpp_is_visible(&cptr[i], CC)) { if (mortuary) { char *buddy = strdup(cptr[i].cs_inet_email); Put(mortuary, buddy, strlen(buddy), buddy, NULL); } } } free (cptr); } if (mortuary) { xmpp_store_mortuary(mortuary); DeleteHash(&mortuary); } }
/* * display who's online */ void cmd_rwho(char *argbuf) { struct CitContext *nptr; int nContexts, i; int spoofed = 0; int user_spoofed = 0; int room_spoofed = 0; int host_spoofed = 0; int aide; char un[40]; char real_room[ROOMNAMELEN], room[ROOMNAMELEN]; char host[64], flags[5]; /* So that we don't keep the context list locked for a long time * we create a copy of it first */ nptr = CtdlGetContextArray(&nContexts) ; if (!nptr) { /* Couldn't malloc so we have to bail but stick to the protocol */ cprintf("%d%c \n", LISTING_FOLLOWS, CtdlCheckExpress() ); cprintf("000\n"); return; } aide = ( (CC->user.axlevel >= AxAideU) || (CC->internal_pgm) ) ; cprintf("%d%c \n", LISTING_FOLLOWS, CtdlCheckExpress() ); for (i=0; i<nContexts; i++) { flags[0] = '\0'; spoofed = 0; user_spoofed = 0; room_spoofed = 0; host_spoofed = 0; if (!aide && nptr[i].state == CON_SYS) continue; if (!aide && nptr[i].kill_me != 0) continue; if (nptr[i].cs_flags & CS_POSTING) strcat(flags, "*"); else strcat(flags, "."); if (nptr[i].fake_username[0]) { strcpy(un, nptr[i].fake_username); spoofed = 1; user_spoofed = 1; } else strcpy(un, nptr[i].curr_user); if (nptr[i].fake_hostname[0]) { strcpy(host, nptr[i].fake_hostname); spoofed = 1; host_spoofed = 1; } else strcpy(host, nptr[i].cs_host); GenerateRoomDisplay(real_room, &nptr[i], CC); if (nptr[i].fake_roomname[0]) { strcpy(room, nptr[i].fake_roomname); spoofed = 1; room_spoofed = 1; } else { strcpy(room, real_room); } if ((aide) && (spoofed)) { strcat(flags, "+"); } if ((nptr[i].cs_flags & CS_STEALTH) && (aide)) { strcat(flags, "-"); } if (((nptr[i].cs_flags&CS_STEALTH)==0) || (aide)) { cprintf("%d|%s|%s|%s|%s|%ld|%s|%s|", nptr[i].cs_pid, un, room, host, nptr[i].cs_clientname, (long)(nptr[i].lastidle), nptr[i].lastcmdname, flags ); if ((user_spoofed) && (aide)) { cprintf("%s|", nptr[i].curr_user); } else { cprintf("|"); } if ((room_spoofed) && (aide)) { cprintf("%s|", real_room); } else { cprintf("|"); } if ((host_spoofed) && (aide)) { cprintf("%s|", nptr[i].cs_host); } else { cprintf("|"); } cprintf("%d\n", nptr[i].logged_in); } } /* release out copy of the context list */ free(nptr); /* Now it's magic time. Before we finish, call any EVT_RWHO hooks * so that external paging modules such as serv_icq can add more * content to the Wholist. */ PerformSessionHooks(EVT_RWHO); cprintf("000\n"); }
/* * 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; struct CitContext *nptr; int nContexts, i; nptr = CtdlGetContextArray(&nContexts) ; /* Make a copy of the current wholist */ begin_critical_section(S_IM_LOGS); while (imlist) { imptr = imlist; imlist = imlist->next; /* For a two party conversation, if one party has logged out, force flush. */ if (nptr) { int user0_is_still_online = 0; int user1_is_still_online = 0; for (i=0; i<nContexts; i++) { if (nptr[i].user.usernum == imptr->usernums[0]) ++user0_is_still_online; if (nptr[i].user.usernum == imptr->usernums[1]) ++user1_is_still_online; } if (imptr->usernums[0] != imptr->usernums[1]) { /* two party conversation */ if ((!user0_is_still_online) || (!user1_is_still_online)) { imptr->lastmsg = 0L; /* force flush */ } } else { /* one party conversation (yes, people do IM themselves) */ if (!user0_is_still_online) { imptr->lastmsg = 0L; /* force flush */ } } } /* Now test this conversation to see if it qualifies for flushing. */ 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); free(nptr); /* 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); /* This will free the string buffer */ imptr = flush_these; flush_these = flush_these->next; free(imptr); } }