/* * 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" extern int deliver__flag; cnu con_user_new(cnr room, jid id) { pool p; cnu user; char *key; log_debug(NAME, "[%s] adding user %s to room %s", FZONE, jid_full(jid_fix(id)), jid_full(jid_fix(room->id))); p = pool_new(); /* Create pool for user struct */ user = pmalloco(p, sizeof(_cnu)); user->p = p; user->realid = jid_new(user->p, jid_full(jid_fix(id))); user->room = room; user->presence = jutil_presnew(JPACKET__AVAILABLE, NULL, NULL); key = j_strdup(jid_full(user->realid)); g_hash_table_insert(room->remote, key, (void*)user); /* Add this user to the room roster */ add_roster(room, user->realid); /* If admin, switch */ if(is_admin(room, user->realid) && !is_moderator(room, user->realid)) { log_debug(NAME, "[%s] Adding %s to moderator list", FZONE, jid_full(jid_fix(user->realid))); /* Update affiliate info */ add_affiliate(room->admin, user->realid, NULL); add_role(room->moderator, user); } else if(is_member(room, user->realid) && !is_admin(room, user->realid)) { /* Update affiliate information */ log_debug(NAME, "[%s] Updating %s in the member list", FZONE, jid_full(user->realid)); add_affiliate(room->member, user->realid, NULL); add_role(room->participant, user); } else if(room->moderated == 1 && room->defaulttype == 1) { /* Auto-add to participant list if moderated and participant type is default */ add_role(room->participant, user); } return user; } void _con_user_history_send(cnu to, xmlnode node) { if(to == NULL || node == NULL) { return; } xmlnode_put_attrib(node, "to", jid_full(to->realid)); deliver(dpacket_new(node), NULL); return; } void _con_user_nick(gpointer key, gpointer data, gpointer arg) { cnu to = (cnu)data; cnu from = (cnu)arg; char *old, *status, *reason, *actor; xmlnode node; xmlnode result; xmlnode element; jid fullid; /* send unavail pres w/ old nick */ if((old = xmlnode_get_attrib(from->nick,"old")) != NULL) { if(xmlnode_get_data(from->nick) != NULL) { node = jutil_presnew(JPACKET__UNAVAILABLE, jid_full(to->realid), NULL); } else { node = xmlnode_dup(from->presence); xmlnode_put_attrib(node, "to", jid_full(to->realid)); } fullid = jid_new(xmlnode_pool(node), jid_full(from->localid)); jid_set(fullid, old, JID_RESOURCE); xmlnode_put_attrib(node, "from", jid_full(fullid)); status = xmlnode_get_attrib(from->nick,"status"); log_debug(NAME, "[%s] status = %s", FZONE, status); reason = xmlnode_get_attrib(from->nick,"reason"); actor = xmlnode_get_attrib(from->nick,"actor"); if(xmlnode_get_data(from->nick) != NULL) { log_debug(NAME, "[%s] Extended presence - Nick Change", FZONE); result = add_extended_presence(from, to, node, STATUS_MUC_NICKCHANGE, NULL, NULL); } else { log_debug(NAME, "[%s] Extended presence", FZONE); result = add_extended_presence(from, to, node, status, reason, actor); } deliver(dpacket_new(result), NULL); xmlnode_free(node); } /* if there's a new nick, broadcast that too */ if(xmlnode_get_data(from->nick) != NULL) { status = xmlnode_get_attrib(from->nick,"status"); log_debug(NAME, "[%s] status = %s/%s", FZONE, status, STATUS_MUC_CREATED); if(j_strcmp(status, STATUS_MUC_CREATED) == 0) node = add_extended_presence(from, to, NULL, status, NULL, NULL); else node = add_extended_presence(from, to, NULL, NULL, NULL, NULL); /* Hide x:delay, not needed */ element = xmlnode_get_tag(node, "x?xmlns=jabber:x:delay"); if(element) xmlnode_hide(element); xmlnode_put_attrib(node, "to", jid_full(to->realid)); fullid = jid_new(xmlnode_pool(node), jid_full(from->localid)); jid_set(fullid, xmlnode_get_data(from->nick), JID_RESOURCE); xmlnode_put_attrib(node, "from", jid_full(fullid)); deliver(dpacket_new(node), NULL); } } void con_user_nick(cnu user, char *nick, xmlnode data) { xmlnode node; char *status, *reason, *actor; cnr room = user->room; log_debug(NAME, "[%s] in room %s changing nick for user %s to %s from %s", FZONE, jid_full(room->id), jid_full(user->realid), nick, xmlnode_get_data(user->nick)); node = xmlnode_new_tag("n"); xmlnode_put_attrib(node, "old", xmlnode_get_data(user->nick)); if (data) { status = xmlnode_get_attrib(data, "status"); reason = xmlnode_get_data(data); actor = xmlnode_get_attrib(data, "actor"); if(status) xmlnode_put_attrib(node, "status", status); if(reason) xmlnode_put_attrib(node, "reason", reason); if(actor) xmlnode_put_attrib(node, "actor", actor); log_debug(NAME, "[%s] status = %s", FZONE, status); } xmlnode_insert_cdata(node,nick,-1); xmlnode_free(user->nick); user->nick = node; deliver__flag = 0; g_hash_table_foreach(room->local, _con_user_nick, (void*)user); deliver__flag = 1; deliver(NULL, NULL); /* send nick change notice if availble */ if(room->note_rename != NULL && nick != NULL && xmlnode_get_attrib(node, "old") != NULL && j_strlen(room->note_rename) > 0) con_room_send(room, jutil_msgnew("groupchat", NULL, NULL, spools(xmlnode_pool(node), xmlnode_get_attrib(node, "old"), " ", room->note_rename, " ", nick, xmlnode_pool(node))), SEND_LEGACY); } void _con_user_enter(gpointer key, gpointer data, gpointer arg) { cnu from = (cnu)data; cnu to = (cnu)arg; xmlnode node; jid fullid; /* mirror */ if(from == to) return; node = add_extended_presence(from, to, NULL, NULL, NULL, NULL); xmlnode_put_attrib(node, "to", jid_full(to->realid)); fullid = jid_new(xmlnode_pool(node), jid_full(from->localid)); jid_set(fullid, xmlnode_get_data(from->nick), JID_RESOURCE); xmlnode_put_attrib(node, "from", jid_full(fullid)); deliver(dpacket_new(node), NULL); } void con_user_enter(cnu user, char *nick, int created) { xmlnode node; xmlnode message; char *key; int h, tflag = 0; cnr room = user->room; user->localid = jid_new(user->p, jid_full(room->id)); jid_set(user->localid, shahash(jid_full(user->realid)), JID_RESOURCE); key = j_strdup(user->localid->resource); g_hash_table_insert(room->local, key, (void*)user); room->count++; log_debug(NAME, "[%s] officiating user %s in room (created = %d) %s as %s/%s", FZONE, jid_full(user->realid), created, jid_full(room->id), nick, user->localid->resource); /* Send presence back to user to confirm presence received */ if(created == 1) { /* Inform if room just created */ node = xmlnode_new_tag("reason"); xmlnode_put_attrib(node, "status", STATUS_MUC_CREATED); con_user_nick(user, nick, node); /* pushes to everyone (including ourselves) our entrance */ xmlnode_free(node); } else { con_user_nick(user, nick, NULL); /* pushes to everyone (including ourselves) our entrance */ } /* Send Room MOTD */ if(j_strlen(room->description) > 0) { message = jutil_msgnew("groupchat", jid_full(user->realid), NULL, room->description); xmlnode_put_attrib(message,"from", jid_full(room->id)); deliver(dpacket_new(message), NULL); } /* Send Room protocol message to legacy clients */ if(is_legacy(user)) { message = jutil_msgnew("groupchat", jid_full(user->realid), NULL, spools(user->p, "This room supports the MUC protocol.", user->p)); xmlnode_put_attrib(message,"from", jid_full(room->id)); deliver(dpacket_new(message), NULL); } /* Send Room Lock warning if necessary */ if(room->locked > 0) { message = jutil_msgnew("groupchat", jid_full(user->realid), NULL, spools(user->p, "This room is locked from entry until configuration is confirmed.", user->p)); xmlnode_put_attrib(message,"from", jid_full(room->id)); deliver(dpacket_new(message), NULL); } /* Update my roster with current users */ g_hash_table_foreach(room->local, _con_user_enter, (void*)user); /* Switch to queue mode */ deliver__flag = 0; /* XXX Require new history handler */ /* loop through history and send back */ if(room->master->history > 0) { //if (xmlnode_get_attrib(user->history, "maxchars") != NULL ) h = room->hlast; while(1) { h++; if(h == room->master->history) h = 0; _con_user_history_send(user, xmlnode_dup(room->history[h])); if(xmlnode_get_tag(room->history[h],"subject") != NULL) tflag = 1; if(h == room->hlast) break; } } /* Re-enable delivery */ deliver__flag = 1; /* Send queued messages */ deliver(NULL, NULL); /* send last know topic */ if(tflag == 0 && room->topic != NULL) { node = jutil_msgnew("groupchat", jid_full(user->realid), xmlnode_get_attrib(room->topic,"subject"), xmlnode_get_data(room->topic)); xmlnode_put_attrib(node, "from", jid_full(room->id)); deliver(dpacket_new(node), NULL); } /* send entrance notice if available */ if(room->note_join != NULL && j_strlen(room->note_join) > 0) con_room_send(room, jutil_msgnew("groupchat", NULL, NULL, spools(user->p, nick, " ", room->note_join, user->p)), SEND_LEGACY); /* Send 'non-anonymous' message if necessary */ if(room->visible == 1) con_send_alert(user, NULL, NULL, STATUS_MUC_SHOWN_JID); } void con_user_process(cnu to, cnu from, jpacket jp) { xmlnode node, element; cnr room = to->room; char str[10]; int t; /* we handle all iq's for this id, it's *our* id */ if(jp->type == JPACKET_IQ) { if(NSCHECK(jp->iq,NS_BROWSE)) { jutil_iqresult(jp->x); node = xmlnode_insert_tag(jp->x, "item"); xmlnode_put_attrib(node, "category", "user"); xmlnode_put_attrib(node, "xmlns", NS_BROWSE); xmlnode_put_attrib(node, "name", xmlnode_get_data(to->nick)); element = xmlnode_insert_tag(node, "item"); xmlnode_put_attrib(element, "category", "user"); if(room->visible == 1 || is_moderator(room, from->realid)) xmlnode_put_attrib(element, "jid", jid_full(to->realid)); else xmlnode_put_attrib(element, "jid", jid_full(to->localid)); if(is_legacy(to)) xmlnode_insert_cdata(xmlnode_insert_tag(node, "ns"), NS_GROUPCHAT, -1); else xmlnode_insert_cdata(xmlnode_insert_tag(node, "ns"), NS_MUC, -1); deliver(dpacket_new(jp->x), NULL); return; } if(NSCHECK(jp->iq,NS_LAST)) { jutil_iqresult(jp->x); node = xmlnode_insert_tag(jp->x, "query"); xmlnode_put_attrib(node, "xmlns", NS_LAST); t = time(NULL) - to->last; sprintf(str,"%d",t); xmlnode_put_attrib(node ,"seconds", str); deliver(dpacket_new(jp->x), NULL); return; } /* deny any other iq's if it's private */ if(to->private == 1) { jutil_error(jp->x, TERROR_FORBIDDEN); deliver(dpacket_new(jp->x), NULL); return; } /* if not, fall through and just forward em on I guess! */ } /* Block possibly faked groupchat messages - groupchat is not meant for p2p chats */ if(jp->type == JPACKET_MESSAGE) { if(jp->subtype == JPACKET__GROUPCHAT) { jutil_error(jp->x, TERROR_BAD); deliver(dpacket_new(jp->x), NULL); return; } if(room->privmsg == 1 && !is_admin(room, from->realid)) { /* Only error on messages with body, otherwise just drop */ if(xmlnode_get_tag(jp->x, "body") != NULL) { jutil_error(jp->x, TERROR_MUC_PRIVMSG); deliver(dpacket_new(jp->x), NULL); return; } else { xmlnode_free(jp->x); return; } } } con_user_send(to, from, jp->x); } void con_user_send(cnu to, cnu from, xmlnode node) { jid fullid; if(to == NULL || from == NULL || node == NULL) { return; } fullid = jid_new(xmlnode_pool(node), jid_full(from->localid)); xmlnode_put_attrib(node, "to", jid_full(to->realid)); if(xmlnode_get_attrib(node, "cnu") != NULL) xmlnode_hide_attrib(node, "cnu"); jid_set(fullid, xmlnode_get_data(from->nick), JID_RESOURCE); xmlnode_put_attrib(node, "from", jid_full(fullid)); deliver(dpacket_new(node), NULL); } void con_user_zap(cnu user, xmlnode data) { cnr room; char *reason; char *status; char *key; if(user == NULL || data == NULL) { log_warn(NAME, "Aborting: NULL attribute found", FZONE); if(data != NULL) xmlnode_free(data); return; } user->leaving = 1; key = pstrdup(user->p, jid_full(user->realid)); status = xmlnode_get_attrib(data, "status"); reason = xmlnode_get_data(data); room = user->room; if(room == NULL) { log_warn(NAME, "[%s] Unable to zap user %s <%s-%s> : Room does not exist", FZONE, jid_full(user->realid), status, reason); xmlnode_free(data); return; } log_debug(NAME, "[%s] zapping user %s <%s-%s>", FZONE, jid_full(user->realid), status, reason); if(user->localid != NULL) { con_user_nick(user, NULL, data); /* sends unavailve */ log_debug(NAME, "[%s] Removing entry from local list", FZONE); g_hash_table_remove(room->local, user->localid->resource); room->count--; /* send departure notice if available*/ if(room->note_leave != NULL && j_strlen(room->note_leave) > 0) { if(reason != NULL) { if(j_strcmp(status, STATUS_MUC_KICKED) == 0) { con_room_send(room,jutil_msgnew("groupchat",NULL,NULL,spools(user->p, xmlnode_get_attrib(user->nick,"old")," ",room->note_leave,": [Kicked] ", reason, user->p)), SEND_LEGACY); } else if(j_strcmp(status, STATUS_MUC_BANNED) == 0) { con_room_send(room,jutil_msgnew("groupchat",NULL,NULL,spools(user->p,xmlnode_get_attrib(user->nick,"old")," ",room->note_leave,": [Banned] ", reason, user->p)), SEND_LEGACY); } else { con_room_send(room,jutil_msgnew("groupchat",NULL,NULL,spools(user->p,xmlnode_get_attrib(user->nick,"old")," ",room->note_leave,": ", reason, user->p)), SEND_LEGACY); } } else { con_room_send(room,jutil_msgnew("groupchat",NULL,NULL,spools(user->p,xmlnode_get_attrib(user->nick,"old")," ",room->note_leave,user->p)), SEND_LEGACY); } } } xmlnode_free(data); log_debug(NAME, "[%s] Removing any affiliate info from admin list", FZONE); log_debug(NAME, "[%s] admin list size [%d]", FZONE, g_hash_table_size(room->admin)); remove_affiliate(room->admin, user->realid); log_debug(NAME, "[%s] Removing any affiliate info from member list", FZONE); log_debug(NAME, "[%s] member list size [%d]", FZONE, g_hash_table_size(room->member)); remove_affiliate(room->member, user->realid); log_debug(NAME, "[%s] Removing any role info from moderator list", FZONE); log_debug(NAME, "[%s] moderator list size [%d]", FZONE, g_hash_table_size(room->moderator)); revoke_role(room->moderator, user); log_debug(NAME, "[%s] Removing any role info from participant list", FZONE); log_debug(NAME, "[%s] participant list size [%d]", FZONE, g_hash_table_size(room->participant)); revoke_role(room->participant, user); log_debug(NAME, "[%s] Removing any roster info from roster list", FZONE); remove_roster(room, user->realid); log_debug(NAME, "[%s] Un-alloc presence xmlnode", FZONE); xmlnode_free(user->presence); log_debug(NAME, "[%s] Un-alloc nick xmlnode", FZONE); xmlnode_free(user->nick); log_debug(NAME, "[%s] Un-alloc history xmlnode", FZONE); xmlnode_free(user->history); log_debug(NAME, "[%s] Removing from remote list and un-alloc cnu", FZONE); g_hash_table_remove(room->remote, jid_full(user->realid)); }