/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002 David Sutton * * * This program is free software; you can redistribute it and/or drvify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA */ #include "conference.h" #include void con_server_browsewalk(gpointer key, gpointer data, gpointer arg) { cnr room = (cnr)data; jpacket jp = (jpacket)arg; char users[10]; char maxu[10]; xmlnode x; if(room == NULL) { log_warn(NAME, "[%s] Aborting: NULL room %s", FZONE, key); return; } /* We can only show the private rooms that the user already knows about */ if(room->public == 0 && !in_room(room, jp->to) && !is_admin(room, jp->to) && !is_member(room, jp->to)) return; /* Unconfigured rooms don't exist either */ if(room->locked == 1) return; x = xmlnode_insert_tag(jp->iq, "item"); xmlnode_put_attrib(x, "category", "conference"); if(room->public == 0) xmlnode_put_attrib(x, "type", "private"); else xmlnode_put_attrib(x, "type", "public"); xmlnode_put_attrib(x, "jid", jid_full(room->id)); if(room->maxusers > 0) xmlnode_put_attrib(x, "name", spools(jp->p, room->name, " (", itoa(room->count, users), "/", itoa(room->maxusers, maxu), ")", jp->p)); else xmlnode_put_attrib(x, "name", spools(jp->p, room->name, " (", itoa(room->count, users), ")", jp->p)); } void _server_discowalk(gpointer key, gpointer data, gpointer arg) { cnr room = (cnr)data; jpacket jp = (jpacket)arg; xmlnode x; if(room == NULL) { log_warn(NAME, "[%s] Aborting: NULL room %s", FZONE, key); return; } /* if we're a private server, we can only show the rooms the user already knows about */ if(room->public == 0 && !in_room(room, jp->to) && !is_admin(room, jp->to) && !is_member(room, jp->to)) return; /* Unconfigured rooms don't exist either */ if(room->locked == 1) return; x = xmlnode_insert_tag(jp->iq, "item"); xmlnode_put_attrib(x, "jid", jid_full(room->id)); xmlnode_put_attrib(x, "name", spools(jp->p, room->name, jp->p)); } void con_server(cni master, jpacket jp) { char *str; struct utsname un; xmlnode x; int start, status; time_t t; char *from; char nstr[10]; jid user; log_debug(NAME, "[%s] server packet", FZONE); if(jp->type == JPACKET_PRESENCE) { log_debug(NAME, "[%s] Server packet: Presence - Not Implemented", FZONE); jutil_error(jp->x, TERROR_NOTIMPL); deliver(dpacket_new(jp->x),NULL); return; } if(jp->type != JPACKET_IQ) { log_debug(NAME, "[%s] Server packet: Dropping non-IQ packet", FZONE); xmlnode_free(jp->x); return; } /* Action by subpacket type */ if(jpacket_subtype(jp) == JPACKET__SET) { log_debug(NAME, "[%s] Server packet - IQ Set", FZONE); if(NSCHECK(jp->iq,NS_REGISTER)) { /* Disabled until checked */ log_debug(NAME, "[%s] TERROR_NOTIMPL", FZONE); jutil_error(jp->x, TERROR_NOTIMPL); deliver(dpacket_new(jp->x),NULL); return; log_debug(NAME, "[%s] Server packet - Registration Handler", FZONE); str = xmlnode_get_tag_data(jp->iq, "name"); x = xmlnode_get_tag(jp->iq, "remove"); from = xmlnode_get_attrib(jp->x, "from"); user = jid_new(jp->p, from); status = is_registered(master, jid_full(jid_user(user)), str); if(x) { log_debug(NAME, "[%s] Server packet - UnReg Submission", FZONE); set_data(master, str, from, NULL, 1); jutil_iqresult(jp->x); } else if(status == -1) { log_debug(NAME, "[%s] Server packet - Registration Submission : Already taken", FZONE); jutil_error(jp->x, TERROR_MUC_NICKREG); } else if(status == 0) { log_debug(NAME, "[%s] Server packet - Registration Submission", FZONE); set_data(master, str, from, NULL, 0); jutil_iqresult(jp->x); } else { log_debug(NAME, "[%s] Server packet - Registration Submission : already set", FZONE); jutil_iqresult(jp->x); } deliver(dpacket_new(jp->x), NULL); } } else if(jpacket_subtype(jp) == JPACKET__GET) { /* now we're all set */ if(NSCHECK(jp->iq,NS_REGISTER)) { log_debug(NAME, "[%s] Server packet - Registration Request", FZONE); /* Disabled until checked */ log_debug(NAME, "[%s] TERROR_NOTIMPL", FZONE); jutil_error(jp->x, TERROR_NOTIMPL); deliver(dpacket_new(jp->x),NULL); return; x = get_data_byjid(master, xmlnode_get_attrib(jp->x, "from")); str = xmlnode_get_attrib(x, "nick") ? xmlnode_get_attrib(x, "nick") : ""; jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x, "query"), "xmlns", NS_REGISTER); jpacket_reset(jp); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "instructions"), "Enter the nickname you wish to reserve for this conference service", -1); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "name"), str, -1); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "key"), jutil_regkey(NULL, jid_full(jp->to)), -1); if(x != NULL) xmlnode_insert_tag(jp->iq, "registered"); deliver(dpacket_new(jp->x), NULL); } else if(NSCHECK(jp->iq,NS_TIME)) { /* Compliant with JEP-0090 */ log_debug(NAME, "[%s] Server packet - Time Request", FZONE); jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x, "query"), "xmlns", NS_TIME); jpacket_reset(jp); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "utc"), jutil_timestamp(), -1); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "tz"), tzname[0], -1); /* create nice display time */ t = time(NULL); str = ctime(&t); str[strlen(str) - 1] = '\0'; /* cut off newline */ xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "display"), pstrdup(jp->p, str), -1); deliver(dpacket_new(jp->x),NULL); } else if(NSCHECK(jp->iq,NS_VERSION)) { /* Compliant with JEP-0092 */ log_debug(NAME, "[%s] Server packet - Version Request", FZONE); jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x, "query"), "xmlns", NS_VERSION); jpacket_reset(jp); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "name"), NAME, -1); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "version"), VERSION, -1); uname(&un); x = xmlnode_insert_tag(jp->iq,"os"); xmlnode_insert_cdata(x, pstrdup(jp->p, un.sysname),-1); xmlnode_insert_cdata(x," ",1); xmlnode_insert_cdata(x,pstrdup(jp->p, un.release),-1); deliver(dpacket_new(jp->x),NULL); } else if(NSCHECK(jp->iq,NS_BROWSE)) { /* Compliant with JEP-0011 */ log_debug(NAME, "[%s] Server packet - Browse Request", FZONE); jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x, "item"), "xmlns", NS_BROWSE); jpacket_reset(jp); xmlnode_put_attrib(jp->iq, "category", "conference"); xmlnode_put_attrib(jp->iq, "type", "public"); xmlnode_put_attrib(jp->iq, "jid", master->i->id); /* pull name from the server vCard */ xmlnode_put_attrib(jp->iq, "name", xmlnode_get_tag_data(master->config, "vCard/FN")); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_MUC, -1); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_DISCO, -1); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_BROWSE, -1); /* xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_REGISTER, -1); */ xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_VERSION, -1); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_TIME, -1); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_LAST, -1); xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_VCARD, -1); /* Walk room hashtable and report available rooms */ g_hash_table_foreach(master->rooms, con_server_browsewalk, (void*)jp); deliver(dpacket_new(jp->x), NULL); } else if(NSCHECK(jp->iq, NS_DISCO_INFO)) { log_debug(NAME, "[%s] Server packet - Disco Info Request", FZONE); jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"query"), "xmlns", NS_DISCO_INFO); jpacket_reset(jp); x = xmlnode_insert_tag(jp->iq,"identity"); xmlnode_put_attrib(x, "category", "conference"); xmlnode_put_attrib(x, "type", "text"); xmlnode_put_attrib(x, "name", xmlnode_get_tag_data(master->config, "vCard/FN")); xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_MUC); xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_DISCO); xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_BROWSE); /* xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_REGISTER); */ xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_VERSION); xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_TIME); xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_LAST); xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_VCARD); deliver(dpacket_new(jp->x),NULL); } else if(NSCHECK(jp->iq, NS_DISCO_ITEMS)) { log_debug(NAME, "[%s] Server packet - Disco Items Request", FZONE); jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"query"),"xmlns", NS_DISCO_ITEMS); jpacket_reset(jp); g_hash_table_foreach(master->rooms,_server_discowalk, (void*)jp); deliver(dpacket_new(jp->x),NULL); } else if(NSCHECK(jp->iq, NS_LAST)) { log_debug(NAME, "[%s] Server packet - Last Request", FZONE); jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"query"),"xmlns",NS_LAST); jpacket_reset(jp); start = time(NULL) - master->start; sprintf(nstr,"%d",start); xmlnode_put_attrib(jp->iq,"seconds", pstrdup(jp->p, nstr)); deliver(dpacket_new(jp->x),NULL); } else if(NSCHECK(jp->iq,NS_VCARD)) { log_debug(NAME, "[%s] Server packet - VCard Request", FZONE); jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"vCard"),"xmlns",NS_VCARD); jpacket_reset(jp); xmlnode_insert_node(jp->iq,xmlnode_get_firstchild(xmlnode_get_tag(master->config,"vCard"))); deliver(dpacket_new(jp->x),NULL); } } else { log_debug(NAME, "[%s] TERROR_NOTIMPL", FZONE); jutil_error(jp->x, TERROR_NOTIMPL); deliver(dpacket_new(jp->x),NULL); } return; } void _con_packets(void *arg) { jpacket jp = (jpacket)arg; cni master = (cni)jp->aux1; cnr room; cnu u, u2; char *s, *reason; xmlnode node; int priority = -1; int created = 0; time_t now = time(NULL); #ifndef _JCOMP pth_mutex_acquire(&master->lock, 0, NULL); #else g_mutex_lock(master->lock); #endif /* first, handle all packets just to the server (browse, vcard, ping, etc) */ if(jp->to->user == NULL) { con_server(master, jp); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return; } log_debug(NAME, "[%s] processing packet %s", FZONE, xmlnode2str(jp->x)); /* any other packets must have an associated room */ for(s = jp->to->user; *s != '\0'; s++) *s = tolower(*s); /* lowercase the group name */ if((room = g_hash_table_lookup(master->rooms, jid_full(jid_user(jid_fix(jp->to))))) == NULL) { log_debug(NAME, "[%s] Room not found (%s)", FZONE, jid_full(jid_user(jp->to))); if((master->roomlock == 1 && !is_sadmin(master, jp->from)) || master->loader == 0) { log_debug(NAME, "[%s] Room building request denied", FZONE); jutil_error(jp->x, TERROR_MUC_ROOM); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif deliver(dpacket_new(jp->x),NULL); return; } else if(jp->type == JPACKET_IQ && jpacket_subtype(jp) == JPACKET__GET && NSCHECK(jp->iq, NS_MUC_OWNER)) { room = con_room_new(master, jid_user(jp->to), jp->from, NULL, NULL, 1, 1, 0); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif xmlnode_free(jp->x); return; } else if(jp->to->resource == NULL) { log_debug(NAME, "[%s] Room %s doesn't exist: Returning Bad Request", FZONE, jp->to->user); jutil_error(jp->x, TERROR_BAD); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif deliver(dpacket_new(jp->x),NULL); return; } else if(jpacket_subtype(jp) == JPACKET__UNAVAILABLE) { log_debug(NAME, "[%s] Room %s doesn't exist: dropping unavailable presence", FZONE, jp->to->user); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif xmlnode_free(jp->x); return; } else { if(master->dynamic == -1) room = con_room_new(master, jid_user(jp->to), jp->from, NULL, NULL, 1, 0, 1); else room = con_room_new(master, jid_user(jp->to), jp->from, NULL, NULL, 1, 0, 0); /* fall through, so the presence goes to the room like normal */ created = 1; } } /* get the sending user entry, if any */ u = g_hash_table_lookup(room->remote, jid_full(jid_fix(jp->from))); /* handle errors */ if(jpacket_subtype(jp) == JPACKET__ERROR) { log_debug(NAME, "[%s] Error Handler: init", FZONE); /* only allow iq errors that are to a resource (direct-chat) */ if(jp->to->resource == NULL || jp->type != JPACKET_IQ) { if(u != NULL && u->localid != NULL) { log_debug(NAME, "[%s] Error Handler: Zapping user", FZONE); node = xmlnode_new_tag("reason"); xmlnode_insert_cdata(node, "Lost connection", -1); con_user_zap(u, node); } else { log_debug(NAME, "[%s] Error Handler: No cnu/lid found for user", FZONE); } } xmlnode_free(jp->x); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return; } /* Block message from users not already in the room */ if(jp->type == JPACKET_MESSAGE && u == NULL) { log_debug(NAME, "[%s] Blocking message from outsider (%s)", FZONE, jid_full(jp->to)); jutil_error(jp->x, TERROR_MUC_OUTSIDE); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif deliver(dpacket_new(jp->x),NULL); return; } /* several things use this field below as a flag */ if(jp->type == JPACKET_PRESENCE) priority = jutil_priority(jp->x); /* sending available presence will automatically get you a generic user, if you don't have one */ if(u == NULL && priority >= 0) u = con_user_new(room, jp->from); /* update tracking stuff */ room->last = now; room->packets++; if(u != NULL) { u->last = now; u->packets++; } /* handle join/rename */ if(priority >= 0 && jp->to->resource != NULL) { u2 = con_room_usernick(room, jp->to->resource); /* existing user w/ this nick? */ /* it's just us updating our presence */ if(u2 == u) { jp->to = jid_user(jp->to); xmlnode_put_attrib(jp->x, "to", jid_full(jp->to)); if(u) { xmlnode_free(u->presence); u->presence = xmlnode_dup(jp->x); } con_room_process(room, u, jp); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return; } /* User already exists, return conflict Error */ if(u2 != NULL) { log_debug(NAME, "[%s] Nick Conflict (%s)", FZONE, jid_full(jid_user(jp->to))); jutil_error(jp->x, TERROR_MUC_NICK); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif deliver(dpacket_new(jp->x),NULL); return; } /* Nick already registered, return conflict Error */ if(is_registered(master, jid_full(jid_user(jid_fix(u->realid))), jp->to->resource) == -1) { log_debug(NAME, "[%s] Nick Conflict with registered nick (%s)", FZONE, jid_full(jid_fix(jp->to))); jutil_error(jp->x, TERROR_MUC_NICKREG); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif deliver(dpacket_new(jp->x),NULL); return; } /* if from an existing user in the room, change the nick */ if(is_outcast(room, u->realid) && !is_admin(room, u->realid)) { log_debug(NAME, "[%s] Blocking Banned user (%s)", FZONE, jid_full(jid_user(jid_fix(jp->to)))); jutil_error(jp->x, TERROR_MUC_BANNED); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif deliver(dpacket_new(jp->x),NULL); return; } /* User is not invited, return invitation error */ if(room->invitation == 1 && !is_member(room, u->realid) && !is_owner(room, u->realid)) { jutil_error(jp->x, TERROR_MUC_INVITED); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif deliver(dpacket_new(jp->x),NULL); return; } /* Room is full, return full room error */ if(room->count >= room->maxusers && room->maxusers != 0 && !is_admin(room, u->realid)) { log_debug(NAME, "[%s] Room over quota - disallowing entry", FZONE); jutil_error(jp->x, TERROR_MUC_FULL); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif deliver(dpacket_new(jp->x),NULL); return; } /* Room has been locked against entry */ if(room->locked && !is_owner(room, u->realid)) { log_debug(NAME, "[%s] Room has been locked", FZONE); jutil_error(jp->x, TERROR_NOTFOUND); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif deliver(dpacket_new(jp->x),NULL); return; } /* User already in room, simply a nick change */ if(u->localid != NULL) { xmlnode_free(u->presence); u->presence = xmlnode_dup(jp->x); con_user_nick(u, jp->to->resource, NULL); /* broadcast nick rename */ xmlnode_free(jp->x); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return; } else if(room->secret == NULL || is_sadmin(master, jp->from)) /* No password required, just go right in, or you're an sadmin */ { if(NSCHECK(xmlnode_get_tag(jp->x,"x"), NS_MUC)) { /* Set legacy value to room value */ u->legacy = 0; node = xmlnode_get_tag(jp->x,"x"); xmlnode_hide(node); /* Enable room defaults automatically */ if(master->roomlock == -1) { created = 0; } } else { u->legacy = 1; created = 0; /* Override created flag for non-MUC compliant clients */ } xmlnode_free(u->presence); u->presence = xmlnode_dup(jp->x); jutil_delay(u->presence, NULL); log_debug(NAME, "[%s] About to enter room, legacy<%d>, presence [%s]", FZONE, u->legacy, xmlnode2str(u->presence)); con_user_enter(u, jp->to->resource, created); /* join the room */ xmlnode_free(jp->x); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return; } else if(jp->type == JPACKET_PRESENCE) /* Hopefully you are including a password, this room is locked */ { if(NSCHECK(xmlnode_get_tag(jp->x,"x"), NS_MUC)) { log_debug(NAME, "[%s] Password?", FZONE); if(j_strcmp(room->secret, xmlnode_get_tag_data(xmlnode_get_tag(jp->x,"x"), "password")) == 0) { /* Set legacy value to room value */ u->legacy = 0; node = xmlnode_get_tag(jp->x,"x"); xmlnode_hide(node); xmlnode_free(u->presence); u->presence = xmlnode_dup(jp->x); jutil_delay(u->presence, NULL); con_user_enter(u, jp->to->resource, created); /* join the room */ xmlnode_free(jp->x); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return; } } } /* No password found, room is password protected. Return password error */ jutil_error(jp->x, TERROR_MUC_PASSWORD); deliver(dpacket_new(jp->x), NULL); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return; } /* kill any user sending unavailable presence */ if(jpacket_subtype(jp) == JPACKET__UNAVAILABLE) { log_debug(NAME, "[%s] Calling user zap", FZONE); if(u != NULL) { reason = xmlnode_get_tag_data(jp->x, "status"); xmlnode_free(u->presence); u->presence = xmlnode_dup(jp->x); node = xmlnode_new_tag("reason"); if (reason) xmlnode_insert_cdata(node, reason, -1); con_user_zap(u, node); } xmlnode_free(jp->x); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return; } /* not in the room yet? foo */ if(u == NULL || u->localid == NULL) { if(u == NULL) { log_debug(NAME, "[%s] No cnu found for user", FZONE); } else { log_debug(NAME, "[%s] No lid found for %s", FZONE, jid_full(u->realid)); } if(jp->to->resource != NULL) { jutil_error(jp->x, TERROR_NOTFOUND); deliver(dpacket_new(jp->x),NULL); } else { con_room_outsider(room, u, jp); /* non-participants get special treatment */ } #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return; } /* packets to a specific resource? one on one chats, browse lookups, etc */ if(jp->to->resource != NULL) { if((u2 = g_hash_table_lookup(room->local, jp->to->resource)) == NULL && (u2 = con_room_usernick(room, jp->to->resource)) == NULL) /* gotta have a recipient */ { jutil_error(jp->x, TERROR_NOTFOUND); deliver(dpacket_new(jp->x),NULL); } else { con_user_process(u2, u, jp); } #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return; } /* finally, handle packets just to a room from a participant, msgs, pres, iq browse/conferencing, etc */ con_room_process(room, u, jp); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif } /* phandler callback, send packets to another server */ result con_packets(instance i, dpacket dp, void *arg) { cni master = (cni)arg; jpacket jp; if(dp == NULL) { log_warn(NAME, "[%s] Err: Sent a NULL dpacket!", FZONE); return r_DONE; } /* routes are from dnsrv w/ the needed ip */ if(dp->type == p_ROUTE) { log_debug(NAME, "[%s] Rejecting ROUTE packet", FZONE); deliver_fail(dp,"Illegal Packet"); return r_DONE; } else { jp = jpacket_new(dp->x); } /* if the delivery failed */ if(jp == NULL) { log_warn(NAME, "[%s] Rejecting Illegal Packet", FZONE); deliver_fail(dp,"Illegal Packet"); return r_DONE; } /* bad packet??? ick */ if(jp->type == JPACKET_UNKNOWN || jp->to == NULL) { log_warn(NAME, "[%s] Bouncing Bad Packet", FZONE); jutil_error(jp->x, TERROR_BAD); deliver(dpacket_new(jp->x),NULL); return r_DONE; } /* we want things processed in order, and don't like re-entrancy! */ jp->aux1 = (void*)master; #ifdef _JCOMP _con_packets((void *)jp); #else mtq_send(master->q, jp->p, _con_packets, (void *)jp); #endif return r_DONE; } /** Save and clean out every room on shutdown */ void _con_shutdown_rooms(gpointer key, gpointer data, gpointer arg) { cnr room = (cnr)data; if(room == NULL) { log_warn(NAME, "[%s] SHUTDOWN: Aborting attempt to clear %s", FZONE, key); return; } #ifdef _JCOMP if(room->persistent == 1) xdb_room_set(room); #endif con_room_cleanup(room); } /** Called to clean up system on shutdown */ void con_shutdown(void *arg) { cni master = (cni)arg; if(master->shutdown == 1) { log_debug(NAME, "[%s] SHUTDOWN: Already commencing. Aborting attempt", FZONE); return; } else { master->shutdown = 1; } log_debug(NAME, "[%s] SHUTDOWN: Clearing configuration", FZONE); xmlnode_free(master->config); log_debug(NAME, "[%s] SHUTDOWN: Zapping sadmin table", FZONE); g_hash_table_destroy(master->sadmin); log_debug(NAME, "[%s] SHUTDOWN: Clear users from rooms", FZONE); g_hash_table_foreach(master->rooms, _con_shutdown_rooms, NULL); log_debug(NAME, "[%s] SHUTDOWN: Zapping rooms", FZONE); g_hash_table_destroy(master->rooms); free(master->day); log_debug(NAME, "[%s] SHUTDOWN: Sequence completed", FZONE); } /** Function called for walking each user in a room */ void _con_beat_user(gpointer key, gpointer data, gpointer arg) { cnu user = (cnu)data; int now = (int)arg; if(user == NULL) { log_warn(NAME, "[%s] Aborting : NULL cnu for %s", FZONE, key); return; } if(user->localid == NULL && (now - user->last) > 120) { log_debug(NAME, "[%s] Marking zombie", FZONE); g_queue_push_tail(user->room->queue, g_strdup(jid_full(user->realid))); } } /* callback for walking each room */ void _con_beat_idle(gpointer key, gpointer data, gpointer arg) { cnr room = (cnr)data; int now = (int)arg; xmlnode node; char *user_name; log_debug(NAME, "[%s] HBTICK: Idle check for >%s<", FZONE, key); if(room == NULL) { log_warn(NAME, "[%s] Aborting : NULL cnr for %s", FZONE, key); return; } /* Perform zombie user clearout */ room->queue = g_queue_new(); g_hash_table_foreach(room->remote, _con_beat_user, arg); /* makes sure nothing stale is in the room */ while ((user_name = (char *)g_queue_pop_head(room->queue)) != NULL) { node = xmlnode_new_tag("reason"); xmlnode_insert_cdata(node, "Clearing zombie", -1); con_user_zap(g_hash_table_lookup(room->remote, user_name), node); log_debug(NAME, "[%s] HBTICK: removed zombie '%s' in the queue", FZONE, user_name); g_free(user_name); } g_queue_free(room->queue); /* Destroy timed-out dynamic room */ if(room->persistent == 0 && room->count == 0 && (now - room->last) > 240) { log_debug(NAME, "[%s] HBTICK: Locking room and adding %s to remove queue", FZONE, key, now); room->locked = 1; g_queue_push_tail(room->master->queue, g_strdup(jid_full(room->id))); } } /* heartbeat checker for timed out idle rooms */ void _con_beat_logrotate(gpointer key, gpointer data, gpointer arg) { cnr room = (cnr)data; if(room == NULL) { log_warn(NAME, "[%s] Aborting : NULL cnr for %s", FZONE, key); return; } if(room->logfile) { log_debug(NAME, "[%s] Rotating log for room %s", FZONE, jid_full(room->id)); con_room_log_close(room); con_room_log_new(room); } } /* heartbeat checker for timed out idle rooms */ void _con_beat_logupdate(gpointer key, gpointer data, gpointer arg) { cnr room = (cnr)data; char *timestamp = (char*)arg; if(room == NULL) { log_warn(NAME, "[%s] Aborting : NULL cnr for %s", FZONE, key); return; } if(room->logformat == LOG_XHTML && room->logfile) { log_debug(NAME, "[%s] Adding anchor >%s< for room %s", FZONE, timestamp, jid_full(room->id)); fprintf(room->logfile, "\n", timestamp); fflush(room->logfile); } } /* heartbeat checker for maintainance */ result con_beat_update(void *arg) { cni master = (cni)arg; time_t t = time(NULL); int mins = minuteget(t); char *tstamp = timeget(t); char *dstamp = dateget(t); char *room_name; log_debug(NAME, "[%s] HBTICK", FZONE); /* Check for timed out idle rooms */ if(mins % 2 == 0) { #ifndef _JCOMP pth_mutex_acquire(&master->lock, 0, NULL); #else g_mutex_lock(master->lock); #endif log_debug(NAME, "[%s] HBTICK: Idle check started", FZONE); master->queue = g_queue_new(); g_hash_table_foreach(master->rooms, _con_beat_idle, (void*)t); while ((room_name = (char *)g_queue_pop_head(master->queue)) != NULL) { log_debug(NAME, "[%s] HBTICK: removed room '%s' in the queue", FZONE, room_name); con_room_zap(g_hash_table_lookup(master->rooms, room_name)); log_debug(NAME, "[%s] HBTICK: removed room '%s' in the queue", FZONE, room_name); g_free(room_name); } g_queue_free(master->queue); log_debug(NAME, "[%s] HBTICK: Idle check complete", FZONE); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif } /* Check for logfiles requiring updating */ if(mins % 5 == 0) { #ifndef _JCOMP pth_mutex_acquire(&master->lock, 0, NULL); #else g_mutex_lock(master->lock); #endif g_hash_table_foreach(master->rooms, _con_beat_logupdate, (void*)tstamp); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif } /* Release malloc for tstamp */ free(tstamp); if(j_strcmp(master->day, dstamp) == 0) { free(dstamp); return r_DONE; } free(master->day); master->day = j_strdup(dstamp); free(dstamp); #ifndef _JCOMP pth_mutex_acquire(&master->lock, 0, NULL); #else g_mutex_lock(master->lock); #endif g_hash_table_foreach(master->rooms, _con_beat_logrotate, NULL); #ifndef _JCOMP pth_mutex_release(&master->lock); #else g_mutex_unlock(master->lock); #endif return r_DONE; } /* heartbeat checker for miscellaneous tasks */ result con_beat_housekeep(void *arg) { cni master = (cni)arg; master->loader = 1; xdb_rooms_get(master); /* Remove unwanted heartbeat */ return r_UNREG; } /*** everything starts here ***/ void conference(instance i, xmlnode x) { #ifdef _JCOMP extern jcr_instance jcr; #endif cni master; xmlnode cfg; jid sadmin; xmlnode current; xmlnode node; xmlnode tmp; pool tp; time_t now = time(NULL); log_debug(NAME, "[%s] mu-conference loading - Service ID: %s", FZONE, i->id); /* Temporary pool for temporary jid creation */ tp = pool_new(); /* Allocate space for cni struct and link to instance */ log_debug(NAME, "[%s] Malloc: _cni=%d", FZONE, sizeof(_cni)); master = pmalloco(i->p, sizeof(_cni)); master->i = i; #ifndef _JCOMP /* Set up xdb interface */ master->xdbc = xdb_cache(i); /* get the config */ cfg = xdb_get(master->xdbc, jid_new(xmlnode_pool(x), "config@-internal"), "jabber:config:conference"); /* Parse config and initialise variables */ master->q = mtq_new(i->p); #else /* get the config */ cfg = xmlnode_get_tag(jcr->config, "conference"); #endif master->loader = 0; master->start = now; master->rooms = g_hash_table_new_full(g_str_hash, g_str_equal, ght_remove_key, ght_remove_cnr); master->history = j_atoi(xmlnode_get_tag_data(cfg,"history"),20); master->config = xmlnode_dup(cfg); /* Store a copy of the config for later usage */ master->day = dateget(now); /* Used to determine when to rotate logs */ master->logdir = xmlnode_get_tag_data(cfg, "logdir"); /* Directory where to store logs */ /* If requested, set default room state to 'public', otherwise will default to 'private */ if(xmlnode_get_tag(cfg,"public")) master->public = 1; /* If requested, rooms are given a default configuration */ if(xmlnode_get_tag(cfg,"defaults")) master->roomlock = -1; /* If requested, stop any new rooms being created */ if(xmlnode_get_tag(cfg,"roomlock")) master->roomlock = 1; /* If requested, stop any new rooms being created */ if(xmlnode_get_tag(cfg,"dynamic")) master->dynamic = 1; /* If requested, stop any new rooms being created */ if(xmlnode_get_tag(cfg,"persistent")) master->dynamic = -1; master->sadmin = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, ght_remove_xmlnode); /* sadmin code */ if(xmlnode_get_tag(cfg, "sadmin")) { node = xmlnode_get_tag(cfg, "sadmin"); for(current = xmlnode_get_firstchild(node); current != NULL; current = xmlnode_get_nextsibling(current)) { sadmin = jid_new(tp, xmlnode_get_data(current)); if(sadmin != NULL) { log_debug(NAME, "[%s] Adding sadmin %s", FZONE, jid_full(sadmin)); /* use an xmlnode as the data value */ tmp = xmlnode_new_tag("sadmin"); g_hash_table_insert(master->sadmin, j_strdup(jid_full(jid_user(jid_fix(sadmin)))), (void*)tmp); } } } #ifndef _JCOMP register_phandler(i, o_DELIVER, con_packets, (void*)master); register_shutdown(con_shutdown,(void *) master); register_beat(60, con_beat_update, (void *)master); register_beat(1, con_beat_housekeep, (void *)master); #else master->lock = g_mutex_new(); master->loader = 1; xdb_rooms_get(master); register_phandler(i, o_DELIVER, con_packets, (void*)master); register_shutdown(con_shutdown,(void *) master); g_timeout_add(60000, (GSourceFunc)con_beat_update, (void *)master); #endif pool_free(tp); }