jabber-muc-0.8/0000775000175000017500000000000011171216210012640 5ustar bencerbencerjabber-muc-0.8/include/0000775000175000017500000000000011171216166014275 5ustar bencerbencerjabber-muc-0.8/include/jabberd.h0000664000175000017500000000203511160655577016052 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jabberd.h,v 1.1 2005/12/06 14:48:49 peregrine Exp $ */ /* This is the replacement for jabberd.h It contains only the * includes to add the jcomp.h. The real includes are in jcomp.h * and jcomp-compatible.h */ #include "lib.h" #include "jcomp.h" jabber-muc-0.8/include/jcomp-compat.h0000664000175000017500000000406711160655577017061 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jcomp-compat.h,v 1.1 2005/12/06 14:48:49 peregrine Exp $ */ /* The following are Compatibility Definitions from jabberd 1.4.x */ /* packet types */ typedef enum { p_NONE, p_NORM, p_XDB, p_LOG, p_ROUTE } ptype; /* ordering types, me first me first, managerial, engineer, grunt */ typedef enum { o_PRECOND, o_COND, o_PREDELIVER, o_DELIVER } order; /* result types, unregister me, I pass, I should be last, I suck, I rock */ typedef enum { r_UNREG, r_NONE, r_PASS, r_LAST, r_ERR, r_DONE } result; typedef struct instance_struct *instance, _instance; /* packet wrapper, d as in delivery or daemon, whichever pleases you */ typedef struct dpacket_struct { char *host; jid id; ptype type; pool p; xmlnode x; } *dpacket, _dpacket; /* delivery handler function callback definition */ typedef result (*phandler)(instance id, dpacket p, void *arg); /* delivery handler list */ typedef struct handel_struct { pool p; phandler f; void *arg; order o; /* for sorting new handlers as they're inserted */ struct handel_struct *next; } *handel, _handel; /* wrapper around top-level config file sections */ struct instance_struct { char *id; pool p; xmlnode x; ptype type; handel hds; }; int deliver__flag; typedef void(*shutdown_func)(void *arg); jabber-muc-0.8/include/ns.h0000664000175000017500000000353311160655577015105 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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 */ /* Namespace Defines for Conference namespaces */ #ifndef NS_MUC #define NS_MUC "http://jabber.org/protocol/muc" #define NS_MUC_ADMIN "http://jabber.org/protocol/muc#admin" #define NS_MUC_OWNER "http://jabber.org/protocol/muc#owner" #define NS_MUC_USER "http://jabber.org/protocol/muc#user" #define NS_MUC_ROOMINFO "http://jabber.org/protocol/muc#roominfo" #define NS_MUC_UNIQUE "http://jabber.org/protocol/muc#unique" #define NS_MUC_ROOMCONFIG "http://jabber.org/protocol/muc#roomconfig" #endif #ifndef NS_PING #define NS_PING "urn:xmpp:ping" #endif #ifndef NS_DISCO #define NS_DISCO "http://jabber.org/protocol/disco" #define NS_DISCO_INFO "http://jabber.org/protocol/disco#info" #define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" #endif #ifndef NS_GROUPCHAT #define NS_GROUPCHAT "gc-1.0" #endif #ifndef NS_NEGOTIATE #define NS_NEGOTIATE "jabber:iq:negotiate" #endif #ifndef NS_X_CONFERENCE #define NS_X_CONFERENCE "jabber:x:conference" #endif /* Status code defines */ #ifndef NS_DATA #define NS_DATA "jabber:x:data" #endif jabber-muc-0.8/include/hash.h0000664000175000017500000000210611160655577015403 0ustar bencerbencer/* * 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 */ void ght_remove_key(gpointer data); /* Free key */ void ght_remove_cnu(gpointer data); /* Free cnu */ void ght_remove_cnr(gpointer data); /* Free cnr */ void ght_remove_xmlnode(gpointer data); /* Free xmlnode */ jabber-muc-0.8/include/fields.h0000664000175000017500000000350511160655577015732 0ustar bencerbencer/* Contains the constants for fields standardisation */ #define FIELD_ALLOW_INVITE "muc#roomconfig_allowinvites" //Whether to Allow Occupants to Invite Others #define FIELD_ALLOW_SUBJECT "muc#roomconfig_changesubject" //Whether to Allow Occupants to Change Subject #define FIELD_ENABLE_LOGGING "muc#roomconfig_enablelogging" //Whether to Enable Public Logging of Room Conversations #define FIELD_ROOM_LANG "muc#roomconfig_lang" //Natural Language for Room Discussions #define FIELD_MAX_USERS "muc#roomconfig_maxusers" //Maximum Number of Room Occupants #define FIELD_MEMBERS_ONLY "muc#roomconfig_membersonly" //Whether an Make Room Members-Only #define FIELD_MODERATED "muc#roomconfig_moderatedroom" //Whether to Make Room Moderated #define FIELD_PASS_PROTECTED "muc#roomconfig_passwordprotectedroom" //Whether a Password is Required to Enter #define FIELD_PERSISTENT "muc#roomconfig_persistentroom" //Whether to Make Room Persistent #define FIELD_PUBLIC "muc#roomconfig_publicroom" //Whether to Allow Public Searching for Room #define FIELD_DESC "muc#roomconfig_roomdesc" //Short Description of Room #define FIELD_NAME "muc#roomconfig_roomname" //Natural-Language Room Name #define FIELD_PASS "muc#roomconfig_roomsecret" //The Room Password #define FIELD_WHOIS "muc#roomconfig_whois" //Affiliations that May Discover Real JIDs of Occupants #define FIELD_LOG_FORMAT "logformat" //The log format #define FIELD_LEAVE "leave" //The leave text #define FIELD_JOIN "join" //The join text #define FIELD_RENAME "rename" //The nick change text #define FIELD_PRIVACY "privacy" //Allow query between users #define FIELD_DEFAULT_TYPE "defaulttype" //Allow query between users #define FIELD_PRIVATE_MSG "privmsg" //Allow private messages between users #define FIELD_LOCK_NICK "locknicks" //Lock nick to node jid #define FIELD_LEGACY "legacy" //Send messages to all clients jabber-muc-0.8/include/lib.h0000664000175000017500000004417011160655577015235 0ustar bencerbencer #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ** Arrange to use either varargs or stdargs */ #define MAXSHORTSTR 203 /* max short string length */ #define QUAD_T unsigned long long #ifdef __STDC__ #include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap, f) # define VA_END va_end(ap) #else /* __STDC__ */ # include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap) # define VA_END va_end(ap) #endif /* __STDC__ */ #ifndef INCL_LIB_H #define INCL_LIB_H #ifdef __cplusplus extern "C" { #endif #ifndef HAVE_SNPRINTF extern int ap_snprintf(char *, size_t, const char *, ...); #define snprintf ap_snprintf #endif #ifndef HAVE_VSNPRINTF extern int ap_vsnprintf(char *, size_t, const char *, va_list ap); #define vsnprintf ap_vsnprintf #endif #define ZONE zonestr(__FILE__,__LINE__) char *zonestr(char *file, int line); /* --------------------------------------------------------- */ /* */ /* Pool-based memory management routines */ /* */ /* --------------------------------------------------------- */ typedef struct pool_struct { GMutex *lock; GSList *pll; } _pool, *pool; #define pool_heap(i) _pool_new(NULL) #define pool_new() _pool_new(NULL) pool _pool_new(char *zone); /* new pool :) */ void *pmalloc(pool p, int size); /* wrapper around malloc, takes from the pool, cleaned up automatically */ void *pmalloco(pool p, int size); /* YAPW for zeroing the block */ char *pstrdup(pool p, const char *src); /* wrapper around strdup, gains mem from pool */ void pool_free(pool p); /* frees all the data on the pool, and deletes the pool itself */ /* --------------------------------------------------------- */ /* */ /* String management routines */ /* */ /* --------------------------------------------------------- */ char *j_strdup(const char *str); /* provides NULL safe strdup wrapper */ char *j_strcat(char *dest, char *txt); /* strcpy() clone */ int j_strcmp(const char *a, const char *b); /* provides NULL safe strcmp wrapper */ int j_strncmp(const char *a, const char *b, int i); /* provides NULL safe strncmp wrapper */ int j_strncasecmp(const char *a, const char *b, int i); /* provides NULL safe strncasecmp wrapper */ int j_strlen(const char *a); /* provides NULL safe strlen wrapper */ int j_atoi(const char *a, int def); /* checks for NULL and uses default instead, convienence */ /* --------------------------------------------------------- */ /* */ /* SHA calculations */ /* */ /* --------------------------------------------------------- */ #if (SIZEOF_INT == 4) typedef unsigned int uint32; #elif (SIZEOF_SHORT == 4) typedef unsigned short uint32; #else typedef unsigned int uint32; #endif /* HAVEUINT32 */ char *shahash(char *str); /* NOT THREAD SAFE */ void shahash_r(const char* str, char hashbuf[40]); /* USE ME */ int strprintsha(char *dest, int *hashval); /* --------------------------------------------------------- */ /* */ /* XML escaping utils */ /* */ /* --------------------------------------------------------- */ char *strescape(pool p, char *buf); /* Escape <>&'" chars */ /* --------------------------------------------------------- */ /* */ /* String pools (spool) functions */ /* */ /* --------------------------------------------------------- */ struct spool_node { char *c; struct spool_node *next; }; typedef struct spool_struct { pool p; int len; struct spool_node *last; struct spool_node *first; } *spool; spool spool_new(pool p); /* create a string pool */ void spooler(spool s, ...); /* append all the char * args to the pool, terminate args with s again */ char *spool_print(spool s); /* return a big string */ void spool_add(spool s, char *str); /* add a single char to the pool */ char *spools(pool p, ...); /* wrap all the spooler stuff in one function, the happy fun ball! */ /* --------------------------------------------------------- */ /* */ /* xmlnodes - Document Object Model */ /* */ /* --------------------------------------------------------- */ #define NTYPE_TAG 0 #define NTYPE_ATTRIB 1 #define NTYPE_CDATA 2 #define NTYPE_LAST 2 #define NTYPE_UNDEF -1 /* -------------------------------------------------------------------------- Node structure. Do not use directly! Always use accessor macros and methods! -------------------------------------------------------------------------- */ typedef struct xmlnode_t { char* name; unsigned short type; char* data; int data_sz; int complete; pool p; struct xmlnode_t* parent; struct xmlnode_t* firstchild; struct xmlnode_t* lastchild; struct xmlnode_t* prev; struct xmlnode_t* next; struct xmlnode_t* firstattrib; struct xmlnode_t* lastattrib; } _xmlnode, *xmlnode; /* Node creation routines */ xmlnode xmlnode_wrap(xmlnode x,const char* wrapper); xmlnode xmlnode_new_tag(const char* name); xmlnode xmlnode_new_tag_pool(pool p, const char* name); xmlnode xmlnode_insert_tag(xmlnode parent, const char* name); xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size); xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node); void xmlnode_insert_node(xmlnode parent, xmlnode node); xmlnode xmlnode_str(char *str, int len); xmlnode xmlnode_file(char *file); char* xmlnode_file_borked(char *file); /* same as _file but returns the parsing error */ xmlnode xmlnode_dup(xmlnode x); /* duplicate x */ xmlnode xmlnode_dup_pool(pool p, xmlnode x); /* Node Memory Pool */ pool xmlnode_pool(xmlnode node); /* Node editing */ void xmlnode_hide(xmlnode child); void xmlnode_hide_attrib(xmlnode parent, const char *name); /* Node deletion routine, also frees the node pool! */ void xmlnode_free(xmlnode node); /* Locates a child tag by name and returns it */ xmlnode xmlnode_get_tag(xmlnode parent, const char* name); char* xmlnode_get_tag_data(xmlnode parent, const char* name); /* Attribute accessors */ void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value); char* xmlnode_get_attrib(xmlnode owner, const char* name); void xmlnode_put_expat_attribs(xmlnode owner, const char** atts); /* Bastard am I, but these are fun for internal use ;-) */ void xmlnode_put_vattrib(xmlnode owner, const char* name, void *value); void* xmlnode_get_vattrib(xmlnode owner, const char* name); /* Node traversal routines */ xmlnode xmlnode_get_firstattrib(xmlnode parent); xmlnode xmlnode_get_firstchild(xmlnode parent); xmlnode xmlnode_get_lastchild(xmlnode parent); xmlnode xmlnode_get_nextsibling(xmlnode sibling); xmlnode xmlnode_get_prevsibling(xmlnode sibling); xmlnode xmlnode_get_parent(xmlnode node); /* Node information routines */ char* xmlnode_get_name(xmlnode node); char* xmlnode_get_data(xmlnode node); int xmlnode_get_datasz(xmlnode node); int xmlnode_get_type(xmlnode node); int xmlnode_has_children(xmlnode node); int xmlnode_has_attribs(xmlnode node); /* Node-to-string translation */ char* xmlnode2str(xmlnode node); /* Node-to-terminated-string translation -- useful for interfacing w/ scripting langs */ char* xmlnode2tstr(xmlnode node); int xmlnode_cmp(xmlnode a, xmlnode b); /* compares a and b for equality */ int xmlnode2file(char *file, xmlnode node); /* writes node to file */ /* Expat callbacks */ void expat_startElement(void* userdata, const char* name, const char** atts); void expat_endElement(void* userdata, const char* name); void expat_charData(void* userdata, const char* s, int len); /*********************** * XSTREAM Section ***********************/ #define XSTREAM_MAXNODE 1000000 #define XSTREAM_MAXDEPTH 100 #define XSTREAM_ROOT 0 /* root element */ #define XSTREAM_NODE 1 /* normal node */ #define XSTREAM_CLOSE 2 /* closed */ #define XSTREAM_ERR 4 /* parser error */ typedef void (*xstream_onNode)(int type, xmlnode x, void *arg); /* xstream event handler */ typedef struct xstream_struct { XML_Parser parser; xmlnode node; char *cdata; int cdata_len; pool p; xstream_onNode f; void *arg; int status; int depth; } *xstream, _xstream; xstream xstream_new(pool p, xstream_onNode f, void *arg); /* create a new xstream */ int xstream_eat(xstream xs, char *buff, int len); /* parse new data for this xstream, returns last XSTREAM_* status */ /* convience functions */ xmlnode xstream_header(char *namespace, char *to, char *from); char *xstream_header_char(xmlnode x); typedef struct { unsigned long H[5]; unsigned long W[80]; int lenW; unsigned long sizeHi,sizeLo; } j_SHA_CTX; void shaInit(j_SHA_CTX *ctx); void shaUpdate(j_SHA_CTX *ctx, unsigned char *dataIn, int len); void shaFinal(j_SHA_CTX *ctx, unsigned char hashout[20]); void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]); /********** END OLD libxode.h BEGIN OLD jabber.h *************/ /* --------------------------------------------------------- */ /* */ /* JID structures & constants */ /* */ /* --------------------------------------------------------- */ #define JID_RESOURCE 1 #define JID_USER 2 #define JID_SERVER 4 typedef struct jid_struct { pool p; char* resource; char* user; char* server; char* full; struct jid_struct *next; /* for lists of jids */ } *jid; jid jid_new(pool p, const char *idstr); /* Creates a jabber id from the idstr */ void jid_set(jid id, char *str, int item); /* Individually sets jid components */ char* jid_full(jid id); /* Builds a string type=user/resource@server from the jid data */ int jid_cmp(jid a, jid b); /* Compares two jid's, returns 0 for perfect match */ int jid_cmpx(jid a, jid b, int parts); /* Compares just the parts specified as JID_|JID_ */ jid jid_user(jid a); /* returns the same jid but just of the user@host part */ void jid_init_cache(void); /**< initialize the stringprep caches */ void jid_stop_caching(void); /**< free all caches that have been initialized */ void jid_clean_cache(void); /**< check the stringprep caches for expired entries */ /* --------------------------------------------------------- */ /* */ /* JPacket structures & constants */ /* */ /* --------------------------------------------------------- */ #define JPACKET_UNKNOWN 0x00 #define JPACKET_MESSAGE 0x01 #define JPACKET_PRESENCE 0x02 #define JPACKET_IQ 0x04 #define JPACKET_S10N 0x08 #define JPACKET__UNKNOWN 0 #define JPACKET__NONE 1 #define JPACKET__ERROR 2 #define JPACKET__CHAT 3 #define JPACKET__GROUPCHAT 4 #define JPACKET__GET 5 #define JPACKET__SET 6 #define JPACKET__RESULT 7 #define JPACKET__SUBSCRIBE 8 #define JPACKET__SUBSCRIBED 9 #define JPACKET__UNSUBSCRIBE 10 #define JPACKET__UNSUBSCRIBED 11 #define JPACKET__AVAILABLE 12 #define JPACKET__UNAVAILABLE 13 #define JPACKET__PROBE 14 #define JPACKET__HEADLINE 15 #define JPACKET__INVISIBLE 16 typedef struct jpacket_struct { unsigned char type; int subtype; int flag; void* aux1; xmlnode x; jid to; jid from; char* iqns; xmlnode iq; pool p; } *jpacket, _jpacket; jpacket jpacket_new(xmlnode x); /* Creates a jabber packet from the xmlnode */ jpacket jpacket_reset(jpacket p); /* Resets the jpacket values based on the xmlnode */ int jpacket_subtype(jpacket p); /* Returns the subtype value (looks at xmlnode for it) */ /* --------------------------------------------------------- */ /* */ /* Error structures & constants */ /* */ /* --------------------------------------------------------- */ typedef struct terror_struct { int code; char msg[64]; char type[10]; char condition[30]; } terror; #define TERROR_BAD (terror){400, "Bad Request", "modify", "bad-request"} #define TERROR_AUTH (terror){401, "Unauthorized", "auth", "not-authorized"} #define TERROR_PAY (terror){402, "Payment Required", "auth", "payment-required"} #define TERROR_FORBIDDEN (terror){403, "Forbidden", "auth", "forbidden"} #define TERROR_NOTFOUND (terror){404, "Not Found", "cancel", "item-not-found"} #define TERROR_NOTALLOWED (terror){405, "Not Allowed", "cancel", "not-allowed"} #define TERROR_NOTACCEPTABLE (terror){406, "Not Acceptable", "modify", "not-acceptable"} #define TERROR_REGISTER (terror){407, "Registration Required", "auth", "registration-required"} #define TERROR_REQTIMEOUT (terror){408, "Request Timeout", "wait", "remote-server-timeout"} #define TERROR_CONFLICT (terror){409, "Conflict", "cancel", "conflict"} #define TERROR_INTERNAL (terror){500, "Internal Server Error", "wait", "internal-server-error"} #define TERROR_NOTIMPL (terror){501, "Not Implemented", "cancel", "feature-not-implemented"} #define TERROR_EXTERNAL (terror){502, "Remote Server Error", "wait", "service-unavailable"} #define TERROR_UNAVAIL (terror){503, "Service Unavailable", "cancel", "service-unavailable"} #define TERROR_EXTTIMEOUT (terror){504, "Remote Server Timeout", "wait", "remote-server-timeout"} #define TERROR_DISCONNECTED (terror){510, "Disconnected", "cancel", "service-unavailable"} /* --------------------------------------------------------- */ /* */ /* Namespace constants */ /* */ /* --------------------------------------------------------- */ #define NSCHECK(x,n) (j_strcmp(xmlnode_get_attrib(x,"xmlns"),n) == 0) #define NS_CLIENT "jabber:client" #define NS_SERVER "jabber:server" #define NS_AUTH "jabber:iq:auth" #define NS_REGISTER "jabber:iq:register" #define NS_ROSTER "jabber:iq:roster" #define NS_OFFLINE "jabber:x:offline" #define NS_AGENT "jabber:iq:agent" #define NS_AGENTS "jabber:iq:agents" #define NS_DELAY "urn:xmpp:delay" #define NS_DELAY_0091 "jabber:x:delay" #define NS_VERSION "jabber:iq:version" #define NS_TIME "jabber:iq:time" #define NS_VCARD "vcard-temp" #define NS_PRIVATE "jabber:iq:private" #define NS_SEARCH "jabber:iq:search" #define NS_OOB "jabber:iq:oob" #define NS_XOOB "jabber:x:oob" #define NS_ADMIN "jabber:iq:admin" #define NS_FILTER "jabber:iq:filter" #define NS_AUTH_0K "jabber:iq:auth:0k" #define NS_BROWSE "jabber:iq:browse" #define NS_EVENT "jabber:x:event" #define NS_CONFERENCE "jabber:iq:conference" #define NS_SIGNED "jabber:x:signed" #define NS_ENCRYPTED "jabber:x:encrypted" #define NS_GATEWAY "jabber:iq:gateway" #define NS_LAST "jabber:iq:last" #define NS_ENVELOPE "jabber:x:envelope" #define NS_EXPIRE "jabber:x:expire" #define NS_XHTML "http://www.w3.org/1999/xhtml" #define NS_STANZA "urn:ietf:params:xml:ns:xmpp-stanzas" #define NS_XDBGINSERT "jabber:xdb:ginsert" #define NS_XDBNSLIST "jabber:xdb:nslist" /* --------------------------------------------------------- */ /* */ /* JUtil functions */ /* */ /* --------------------------------------------------------- */ xmlnode jutil_presnew(int type, char *to, char *status); /* Create a skeleton presence packet */ xmlnode jutil_iqnew(int type, char *ns); /* Create a skeleton iq packet */ xmlnode jutil_msgnew(char *type, char *to, char *subj, char *body); /* Create a skeleton message packet */ xmlnode jutil_header(char* xmlns, char* server); /* Create a skeleton stream packet */ int jutil_priority(xmlnode x); /* Determine priority of this packet */ void jutil_tofrom(xmlnode x); /* Swaps to/from fields on a packet */ xmlnode jutil_iqresult(xmlnode x); /* Generate a skeleton iq/result, given a iq/query */ char* jutil_timestamp(void); /* Get stringified timestamp */ char* jutil_timestamp_0091(void); /* Same as above, using the XEP-0091 */ void jutil_error(xmlnode x, terror E); /* Append an node to x */ void jutil_delay(xmlnode msg, char *reason); /* Append a delay packet to msg */ char* jutil_regkey(char *key, char *seed); /* pass a seed to generate a key, pass the key again to validate (returns it) */ #ifdef __cplusplus } #endif #endif /* INCL_LIB_H */ jabber-muc-0.8/include/conference.h0000664000175000017500000003634211163505600016560 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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 */ #define _GNU_SOURCE #include #include #include #include #include #include "hash.h" #include "ns.h" #define NAME "MU-Conference" #undef VERSION #define VERSION "0.9-svn" " (" __DATE__ ")" #define FZONE funcstr(__FILE__,__FUNCTION__,__LINE__) /* Status code defines */ #define STATUS_MUC_SHOWN_JID "100" #define STATUS_MUC_AFFCHANGE "101" #define STATUS_MUC_SHOW_MEMBER "102" #define STATUS_MUC_HIDE_MEMBER "103" #define STATUS_MUC_CONF_CHANGE "104" #define STATUS_MUC_HIDDEN_JID "109" #define STATUS_MUC_OWN_PRESENCE "110" #define STATUS_MUC_LOGGING_ON "170" #define STATUS_MUC_LOGGING_OFF "171" #define STATUS_MUC_NON_ANONYM "172" #define STATUS_MUC_SEMI_ANONYM "173" #define STATUS_MUC_CREATED "201" #define STATUS_MUC_BANNED "301" #define STATUS_MUC_NICKCHANGE "303" #define STATUS_MUC_KICKED "307" #define STATUS_MUC_REM_AFFCHG "321" #define STATUS_MUC_REM_NONMEM "322" #define STATUS_MUC_REM_SHUTDOWN "332" /* Error message defines */ #define TERROR_MUC_PASSWORD (terror){401, "Password required to join this room.", "auth", "not-authorized"} #define TERROR_MUC_BANNED (terror){403, "You have been banned from this room.", "auth", "forbidden"} #define TERROR_MUC_VOICE (terror){403, "You do not have permission to talk in this room.", "auth", "forbidden"} #define TERROR_MUC_PRIVMSG (terror){403, "Private messages are not allowed in this room.", "auth", "forbidden"} #define TERROR_MUC_ROOM (terror){403, "Room creation is disabled.", "auth", "forbidden"} #define TERROR_MUC_CONFIG (terror){405, "You are disallowed access to room configuration", "cancel", "not-allowed"} #define TERROR_MUC_OUTSIDE (terror){405, "You are not in this room", "cancel", "not-allowed"} #define TERROR_MUC_INVITED (terror){407, "Invitation required to join this room.", "auth", "registration-required"} #define TERROR_MUC_FULL (terror){503, "Room is full.", "wait", "service-unavailable"} #define TERROR_MUC_NICK (terror){409, "Please choose a different nickname.", "cancel", "conflict"} #define TERROR_MUC_NICKREG (terror){409, "Reserved Nick - Please choose a different nickname.", "cancel", "conflict"} #define TERROR_MUC_NICKLOCKED (terror){406, "Nicknames locked: please use your username instead.", "cancel", "conflict"} #define SEND_ALL 0 #define SEND_LEGACY 1 /* Log Types */ #define LOG_TEXT 0 #define LOG_XML 1 #define LOG_XHTML 2 /* Role List */ typedef struct cnu_struct *cnu, _cnu; typedef struct cnh_struct *cnh, _cnh; /* mysql instance */ #ifdef HAVE_MYSQL #include typedef struct mysql_struct { MYSQL* mysql; char* host; char* user; char* pass; char* database; unsigned int port; char* socket; unsigned long flag; pool p; } *mysql, _mysql; #endif /* conference instance */ typedef struct cni_struct { instance i; void *xdbc; GHashTable *rooms; /* Hash of all rooms available */ GHashTable *extbrowse; /* Hash of external rooms to report via server browse */ xmlnode config; /* config data, mostly for public right now */ int public; /* if we're public or not */ int history; /* max history items */ int start; /* startup time */ char *day; /* To keep track of log rotation information */ GHashTable *sadmin; /* Server admin, able to override admin locks */ char *logdir; /* Directory where to store logs */ char *stylesheet; /* URL of the log stylesheet */ int loader; /* Used to delay loading from xdb */ int roomlock; /* Stop dynamic room creation */ int dynamic; /* If dynamic is -1 then all rooms are persistent. If dynamic is 0 then implicitly created rooms are temporary and reserved rooms are persistent. If dynamic is 1 then all rooms are temporary.*/ int locknicks; /* All rooms have locked nicknames */ int hideempty; /* Empty rooms are not shown on disco/browse */ int shutdown; /* Service shutting down */ GMutex *lock; /* Used for hasGHashTable *locking */ GQueue *queue; /* used to remove zombie rooms */ int flatLogs; /* tell if the logs must be in one dir per room or one dir per day and room */ int logsEnabled; /* tell if the logs are enabled */ #ifdef HAVE_MYSQL mysql sql; /* sql struct */ #endif } *cni, _cni; /* conference room */ typedef struct cnr_struct { pool p; cni master; /* Reference to cni struct for service */ jid id; /* room id */ jid creator; /* room creator */ char *name; /* friendly name of the room */ char *description; /* Short description of the room */ char *secret; /* if there's a secret */ GHashTable *owner; /* Owners of the room */ GHashTable *remote; /* users associated w/ the room, key is remote jid */ GHashTable *local; /* users associated w/ the room, key is local jid */ GHashTable *roster; /* room roster, key is bare remote jid */ GHashTable *admin; /* users associated w/ the room, key is remote jid */ GHashTable *member; /* members invited, key is remote jid */ GHashTable *outcast; /* users banned, key is remote jid */ GHashTable *moderator; /* users with voice ability, key is local jid */ GHashTable *participant; /* users with voice ability, key is local jid */ int start; /* Time room was started */ int created; /* Time room was created */ int last; /* last time there was any traffic to the room */ int private; /* if private is allowed in this room */ int public; /* Is this room publicly searchable */ int subjectlock; /* Is changing subject locked to admins? */ int maxusers; /* Maximum allowed users, 0 = unlimited */ int locknicks; /* Nicknames locked to JID usernames */ int persistent; /* Will this room avoid autocleanup */ int moderated; /* Is this room moderated */ int defaulttype; /* Do users default to members in moderated rooms? */ int visible; /* Are real jid's visible to non-admins */ int invitation; /* Do users require an invite to enter */ int invites; /* Can users send invitations in an invitation-only room */ int locked; /* Stops any users connecting - used for create+config (Creation via IQ) */ int privmsg; /* Are private messages between users forbidden? */ int legacy; /* Are all clients considered legacy? */ int count; /* # of users in the room */ int hlast; /* last history message */ int packets; /* total packets to this room */ xmlnode topic; /* Some Intro Text: room subject */ cnh *history; /* an array of history messages (vattrib cnu='') */ char *note_leave, *note_join, *note_rename; /* notices */ FILE *logfile; /* for logging of this room */ int logformat; /* For log format */ GQueue *queue; /* used to remove zombie users */ } *cnr, _cnr; /* conference user */ struct cnu_struct { cnr room; pool p; jid realid, localid; /* remote and local jids */ xmlnode nick; /* nickname */ xmlnode presence; /* cached presence */ int last; /* last activity to/from user */ int private; /* private flag */ int packets; /* number of packets from this user */ int legacy; /* To denote gc clients */ int leaving; /* To flag user is leaving the room */ }; /* conference room history */ struct cnh_struct { pool p; int content_length; int timestamp; xmlnode x; }; /* Roles and Associations */ typedef struct trole_struct { int code; char msg[64]; } trole; typedef struct taffil_struct { int code; char msg[64]; } taffil; #define TAFFIL_OWNER (taffil){3, "owner"} #define TAFFIL_ADMIN (taffil){2, "admin"} #define TAFFIL_MEMBER (taffil){1, "member"} #define TAFFIL_NONE (taffil){0, "none"} #define TAFFIL_OUTCAST (taffil){-1, "outcast"} #define TROLE_MODERATOR (trole){3, "moderator"} #define TROLE_PARTICIPANT (trole){2, "participant"} #define TROLE_VISITOR (trole){1, "visitor"} #define TROLE_NONE (trole){0, "none"} /* Functions in conference_room.c */ void con_room_log(cnr room, char *nick, char *message); /* Log messages */ void con_room_log_new(cnr room); /* New Log */ void con_room_log_close(cnr room); /* Close logfile */ void con_room_send_invite(cnu sender, xmlnode node); /* Send invites */ void con_room_forward_decline(cnr room, jpacket jp, xmlnode decline); /* Forward declines */ cnr con_room_new(cni c, jid roomid, jid owner, char *name, char *secret, int private, int persist); /* Set up a new room */ void con_room_sendwalk(gpointer key, gpointer data, gpointer arg); /* Used to send to all members of a room */ void con_room_leaveall(gpointer key, gpointer data, gpointer arg); /* used to send destroyed presence to users */ void con_room_process(cnr room, cnu from, jpacket jp); /* process a packet to a room from a participant */ void con_room_outsider(cnr room, cnu from, jpacket jp); /* process a packet to a room from a non-participant */ void con_room_show_config(cnr room, xmlnode msg); /* Results for iq:negotiate request */ void con_room_send(cnr room, xmlnode x, int legacy); /* sends a raw packet from="room@host" to all participants */ void con_room_cleanup(cnr room); /* Clean up room hashes */ void con_room_zap(cnr room); /* kills a room */ void con_room_history_clear(cnr room); /* Wipes a room history */ /* Functions in conference_user.c */ cnu con_user_new(cnr room, jid id); /* new generic user */ void con_user_nick(cnu user, char *nick, xmlnode data); /* broadcast nick change */ void con_user_enter(cnu user, char *nick, int created); /* put user in room and announce */ void con_user_send(cnu to, cnu from, xmlnode x); /* send a packet to a user from other user */ void con_user_zap(cnu user, xmlnode data); /* clean up the user */ void con_user_process(cnu to, cnu from, jpacket jp); /* process packets betweeen users */ /* Functions in utils.c */ xmlnode add_extended_presence(cnu from, cnu to, xmlnode presence, char *status, char *reason, char *actor); /* Adds extended presence info to a presence packet */ void add_status_code(xmlnode presence, char *status); /* add a muc status code to a presence stanza */ void add_room_status_codes(xmlnode presence, cnr room); /* add room specific status codes (logging, anonymous, ...) */ int is_sadmin(cni master, jid user); /* Check if user is server admin */ int is_owner(cnr room, jid user); /* Check if user is room owner */ int is_admin(cnr room, jid user); /* Check if user is room admin */ int is_member(cnr room, jid user); /* Check if user is invited to the room */ int is_outcast(cnr room, jid user); /* Check if user is banned from the room */ int is_moderator(cnr room, jid user); /* Check if user is room admin */ int is_participant(cnr room, jid user); /* Check if user has voice */ int is_visitor(cnr room, jid user); /* Check if user is a visitor */ int in_room(cnr room, jid user); /* Check if user in the room */ int is_legacy(cnu user); /* Check if user is using a legacy client */ int is_leaving(cnr room, jid user); /* Check if user is leaving */ int is_registered(cni master, char *user, char *nick); /* Check if the nick has been reserved */ void con_send_alert(cnu user, char *text, char *subject, const char *status); /* Sends alert message to user */ void con_send_room_status(cnr room, char *status); /* For sending status messages */ char *funcstr(const char *file, const char *function, int line); /* Custom log_debug define */ char *itoa(int number, char *result); /* Reverse of atoi command */ int minuteget(time_t tin); /* Get current minute */ char *timeget(time_t tin); /* Get current time */ char *dateget(time_t tin); /* Get current date */ void update_presence(cnu user); /* Send presence update for a user */ void insert_item_error(xmlnode node, char *code, char *msg); /* Insert error message into item */ int add_roster(cnr room, jid userid); /* Add full jid to room roster */ int remove_roster(cnr room, jid userid); /* Remove full jid from room roster */ xmlnode get_roster(cnr room, jid userid); /* Get all full jids for a user */ char *extractAction(char *origin, pool p); /* extract action from /me string */ jid jid_fix(jid id); /* Check and fix case of jids */ /* Functions in xdata.c */ int xdata_handler(cnr room, cnu user, jpacket packet); void xdata_room_config(cnr room, cnu user, int new, xmlnode query); /* Sends room configuration details */ /* Functions in admin.c */ void con_get_banlist(gpointer key, gpointer data, gpointer arg); void adm_user_kick(cnu user, cnu target, char *reason); void con_parse_item(cnu sender, jpacket jp); /* Functions in roles.c */ taffil affiliation_level(cnr room, jid user); /* Returns current role level */ trole role_level(cnr room, jid user); /* Returns current role level */ int add_affiliate(GHashTable *hash, jid userid, xmlnode details); int remove_affiliate(GHashTable *hash, jid userid); void revoke_affiliate(cnr room, GHashTable *hash, jid userid); void change_affiliate(char *role, cnu sender, jid user, char *reason, jid by); void add_role(GHashTable *hash, cnu user); void revoke_role(GHashTable *hash, cnu user); void change_role(char *role, cnu sender, jid user, char *reason); /* Functions in xdb.c */ int xdb_room_lists_set(cnr room); /* Save room lists */ void xdb_room_set(cnr room); /* Set room config to xdb */ void xdb_rooms_get(cni master); /* Get room config from xdb */ void xdb_room_clear(cnr room); /* Clear room config from xdb */ int set_data(cni master, char *nick, char *jabberid, xmlnode node, int remove); /* Store data */ xmlnode get_data_bynick(cni master, char *nick); /* Retrieved stored data */ xmlnode get_data_byjid(cni master, char *jabberid); /* Retrieved stored data */ /* Functions in iq.c */ void iq_get_version(jpacket jp); void iq_get_time(jpacket jp); void iq_populate_browse(xmlnode item); #ifdef HAVE_MYSQL /* Functions in mysql.c */ mysql sql_mysql_init(cni master, xmlnode config); int sql_mysql_connect(mysql mysql); void sql_mysql_close(mysql mysql); void sql_clear_all(mysql sql); void sql_update_nb_users(mysql sql, cnr room); void sql_update_field(mysql sql, const char * roomId,const char* field, const char * value); void sql_update_room_config(mysql sql, cnr room); void sql_insert_all(mysql sql, GHashTable * rooms); void sql_insert_room_config(mysql sql, cnr room); void sql_insert_lists(mysql sql, GHashTable * rooms); void sql_add_room_lists(mysql sql, cnr room); void sql_destroy_room(mysql sql, char * room_jid); void sql_add_affiliate(mysql sql,cnr room,char * userid,int affil); void sql_remove_affiliate(mysql sql,cnr room,jid userid); #endif jabber-muc-0.8/include/jcomp.h0000664000175000017500000000774711163505600015570 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jcomp.h,v 1.2 2005/12/15 07:48:11 peregrine Exp $ */ /* A global define for a jcomp component */ #ifndef _JCOMP #define _JCOMP #endif #include /* for printf */ #include /* for exit */ #include #include #include #include #include "glib.h" #include "lib.h" /* jabberd/jcomp library */ #include "jcomp-compat.h" #define _JCOMP_NAME "Jabber Component Runtime" #define _JCOMP_COPY "(c) 2003-2004 Paul Curtis" #define _JCOMP_VERS "0.2.4" char *jcr_debug_msg(const char *, const char *, int); #define JDBG jcr_debug_msg(__FILE__,__FUNCTION__,__LINE__) /* The JCR instance structure */ typedef struct jcr_instance_struct { GAsyncQueue *dqueue; /* the packet delivery queue */ GIOChannel *gio; /* the base connect/bind channel */ GMainLoop *gmain; GThread *dthread; guint gmain_watch_source; int in_shutdown; /* A shutdown is in progress */ char *recv_buffer; /* buffer for read off the socket */ int recv_buffer_size; xmlnode config; /* The configuration file xmlnode */ char *spool_dir; /* The spool directory */ char *pid_file; /* The pid file */ char *log_dir; /* The log directory */ FILE *log_stream; /* The log file */ /* The socket connection to the router/server */ int fd; /* This is the GError bit mask for error logging */ int message_mask; /* == 1, Forces stderr for all error logging */ int message_stderr; /* These are the base connect/router nodes and parser */ xmlnode current; XML_Parser parser; int stream_state; /* These are left overs from jabberd 1.4.x */ instance jcr_i; /* The components packet handler and data */ phandler handler; void *pdata; /* The components shutdown handler and data */ shutdown_func shandler; void *sdata; } _jcr_instance, *jcr_instance; #define _STREAM_INIT_STATE 0 #define _STREAM_AUTH_SENT 1 #define _STREAM_AUTH_RCVD 2 #define _STREAM_CONNECTED 3 #define _STREAM_ERROR 4 #define _STREAM_SHUTDOWN 5 jcr_instance jcr; void log_debug(char *, const char *, ...) G_GNUC_PRINTF(2, 3); void log_warn(char *, const char *, ...) G_GNUC_PRINTF(2, 3); void log_error(char *, const char *, ...) G_GNUC_PRINTF(2, 3); void log_alert(char *, const char *, ...) G_GNUC_PRINTF(2, 3); void log_notice(char *, const char *, ...) G_GNUC_PRINTF(2, 3); void jcr_log_handler(const gchar *, GLogLevelFlags, const gchar *, gpointer); void jcr_log_close(void); int jcr_socket_connect(void); void jcr_main_close_stream(void); void jcr_start_element(void *, const char *, const char **); void jcr_end_element(void *, const char *); void jcr_cdata(void *, const char *, int); gboolean jcr_read_data(GIOChannel *, GIOCondition, gpointer *); void jcr_send_start_stream(void); void deliver(dpacket, void *); void deliver_fail(dpacket, char *); void jcr_queue_deliver(void *); int xdb_set(void *, jid, char *, xmlnode); xmlnode xdb_get(void *, jid, char *); dpacket dpacket_new(xmlnode); void register_phandler(void *, int, void *, void *); void register_shutdown(void *, void *); void jcr_server_shutdown(int); void jcr_server_hangup(int); void jcr_main_new_stream(void); void jcr_error_data(GIOChannel *, GIOCondition, gpointer *); void conference(instance i, xmlnode x); jabber-muc-0.8/src/0000775000175000017500000000000011171216166013441 5ustar bencerbencerjabber-muc-0.8/src/jcomp/0000775000175000017500000000000011171216166014551 5ustar bencerbencerjabber-muc-0.8/src/jcomp/jcr_log.c0000664000175000017500000000674411160655600016345 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jcr_log.c,v 1.1 2005/12/06 14:48:49 peregrine Exp $ */ #include "jcomp.h" static GStaticMutex _jcr_log_lock = G_STATIC_MUTEX_INIT; void jcr_log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { extern jcr_instance jcr; char log_str[512]; char *pos; int sz; time_t t; /* timestamp */ t = time(NULL); pos = ctime(&t); sz = strlen(pos); /* chop off the \n */ pos[sz-1]='\0'; if (log_level & jcr->message_mask) { if (jcr->message_stderr == 1) { fprintf(stderr, "%s\n", message); fflush(stderr); } else { if (jcr->log_stream == NULL) { snprintf(log_str, 512, "%s/mu-conference.log", jcr->log_dir); jcr->log_stream = fopen(log_str, "a+"); } if (jcr->log_stream == NULL) { fprintf(stderr, "Unable to open log file %s/mu-conference.log\n",jcr->log_dir); return; } fprintf(jcr->log_stream, "%s %s\n", pos, message); fflush(jcr->log_stream); } } } void jcr_log_close(void) { extern jcr_instance jcr; if (jcr->log_stream != NULL) { fclose(jcr->log_stream); jcr->log_stream = NULL; } } void log_alert(char *zone, const char *fmt, ...) { va_list ap; char logmsg[512] = ""; g_static_mutex_lock(&_jcr_log_lock); va_start(ap, fmt); vsnprintf(logmsg, 512, fmt, ap); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "%s: %s", zone, logmsg); va_end(ap); g_static_mutex_unlock(&_jcr_log_lock); } void log_error(char *zone, const char *fmt, ...) { va_list ap; char logmsg[512] = ""; g_static_mutex_lock(&_jcr_log_lock); va_start(ap, fmt); vsnprintf(logmsg, 512, fmt, ap); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "%s: %s", zone, logmsg); va_end(ap); g_static_mutex_unlock(&_jcr_log_lock); } void log_notice(char *zone, const char *fmt, ...) { va_list ap; char logmsg[512] = ""; g_static_mutex_lock(&_jcr_log_lock); va_start(ap, fmt); vsnprintf(logmsg, 512, fmt, ap); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, "%s: %s", zone, logmsg); va_end(ap); g_static_mutex_unlock(&_jcr_log_lock); } void log_warn(char *zone, const char *fmt, ...) { va_list ap; char logmsg[512] = ""; g_static_mutex_lock(&_jcr_log_lock); va_start(ap, fmt); vsnprintf(logmsg, 512, fmt, ap); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "%s: %s", zone, logmsg); va_end(ap); g_static_mutex_unlock(&_jcr_log_lock); } void log_debug(char *zone, const char *fmt, ...) { va_list ap; char logmsg[512] = ""; g_static_mutex_lock(&_jcr_log_lock); va_start(ap, fmt); vsnprintf(logmsg, 512, fmt, ap); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "%s: %s", zone, logmsg); va_end(ap); g_static_mutex_unlock(&_jcr_log_lock); } jabber-muc-0.8/src/jcomp/jcr_base_connect.c0000664000175000017500000000535211160655600020201 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jcr_base_connect.c,v 1.1 2005/12/06 14:48:49 peregrine Exp $ */ #include "jcomp.h" int jcr_socket_connect(void) { int rc; struct sockaddr_in from; struct hostent *hp; extern jcr_instance jcr; char *host = xmlnode_get_data(xmlnode_get_tag(jcr->config,"ip")); int port = j_atoi(xmlnode_get_data(xmlnode_get_tag(jcr->config,"port")), 5347); log_debug(JDBG, "Attempting connection to %s:%d", host, port); memset(&from, 0, sizeof(from)); from.sin_family = AF_INET; from.sin_port = htons(port); from.sin_addr.s_addr = inet_addr(host); if (from.sin_addr.s_addr == (u_int) - 1) { hp = gethostbyname(host); if (!hp) { log_debug(JDBG, "unknown host \"%s\"\n", host); return (1); } from.sin_family = hp->h_addrtype; #ifdef _WIN32 memcpy(&from.sin_addr, hp->h_addr, hp->h_length); #else memcpy((caddr_t) &from.sin_addr, hp->h_addr, hp->h_length); #endif } jcr->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (jcr->fd == -1) { log_debug(JDBG, "unable to create socket, errno=%d\n",errno); return 1; } rc = connect(jcr->fd, (struct sockaddr *) &from, sizeof(from)); if (rc < 0) { log_debug(JDBG, "connect failed with %d\n", rc); close(jcr->fd); return(1); } return 0; } void jcr_send_start_stream(void) { extern jcr_instance jcr; char buf[512]; char *name = xmlnode_get_data(xmlnode_get_tag(jcr->config,"name")); char *host = xmlnode_get_data(xmlnode_get_tag(jcr->config,"host")); gsize bytes; GError *error = NULL; GIOStatus rc; memset(buf, 0, 512); snprintf(buf, 511, "\n", name, host); rc = g_io_channel_write_chars(jcr->gio, buf, strlen(buf), &bytes, &error); if (rc != G_IO_STATUS_NORMAL) { log_warn(JDBG, "Opening XML stream failed, rc=%d", rc); } else { log_debug(JDBG, "Opening XML stream: sent %d bytes", bytes); } } jabber-muc-0.8/src/jcomp/jcr_main_stream_error.c0000664000175000017500000000423111160655600021261 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jcr_main_stream_error.c,v 1.1 2005/12/06 14:48:49 peregrine Exp $ */ #include "jcomp.h" void jcr_main_new_stream(void) { jcr->current = NULL; if (jcr->parser) XML_ParserFree(jcr->parser); jcr->fd = -1; jcr->stream_state = _STREAM_INIT_STATE; jcr->parser = XML_ParserCreate(NULL); XML_SetElementHandler(jcr->parser, (void *)jcr_start_element, (void *)jcr_end_element); XML_SetCharacterDataHandler(jcr->parser, (void *)jcr_cdata); XML_SetUserData(jcr->parser, NULL); while (jcr_socket_connect()) { sleep(2); } jcr->gio = g_io_channel_unix_new(jcr->fd); g_io_channel_set_encoding(jcr->gio, NULL, NULL); g_io_channel_set_buffered(jcr->gio, FALSE); jcr->dthread = g_thread_create((void *)jcr_queue_deliver, NULL, TRUE, NULL); jcr->gmain_watch_source = g_io_add_watch_full(jcr->gio, G_PRIORITY_HIGH, (G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL), (void *)jcr_read_data, NULL, NULL); jcr_send_start_stream(); log_notice(JDBG, "Server stream connected."); } void jcr_main_close_stream(void) { extern jcr_instance jcr; log_warn(JDBG, "Server stream error, resetting"); jcr->stream_state = _STREAM_ERROR; g_thread_join(jcr->dthread); g_source_remove(jcr->gmain_watch_source); g_io_channel_shutdown(jcr->gio, TRUE, NULL); g_io_channel_unref(jcr->gio); g_io_channel_unref(jcr->gio); close(jcr->fd); sleep(2); jcr_main_new_stream(); } jabber-muc-0.8/src/jcomp/jcr_compatibility.c0000664000175000017500000000322611160655600020425 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jcr_compatibility.c,v 1.2 2005/12/15 07:48:11 peregrine Exp $ */ #include "jcomp.h" dpacket dpacket_new(xmlnode x) { dpacket p; if(x == NULL) return NULL; /* create the new packet */ p = pmalloco(xmlnode_pool(x),sizeof(_dpacket)); p->x = x; p->p = xmlnode_pool(x); p->type = p_NORM; return p; } void register_phandler(void *i, int d, void *phandler, void* pdata) { extern jcr_instance jcr; jcr->handler = phandler; jcr->pdata = pdata; } void register_shutdown(void *shandler, void *sdata) { extern jcr_instance jcr; jcr->shandler = shandler; jcr->sdata = sdata; } /* Custom Debug message */ char *jcr_debug_msg(const char *file, const char *function, int line) { static char buff[128]; int i; i = snprintf(buff,127,"%s:%d (%s)",file,line,function); buff[i] = '\0'; return buff; } jabber-muc-0.8/src/jcomp/jcr_deliver.c0000664000175000017500000000747611160655600017221 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jcr_deliver.c,v 1.1 2005/12/06 14:48:49 peregrine Exp $ */ #include "jcomp.h" void deliver(dpacket d, void *a) { extern jcr_instance jcr; if (d == NULL) return; while (jcr->stream_state != _STREAM_CONNECTED) { log_debug(JDBG, "Stream not ready ... waiting to queue pkts"); sleep(1); } log_debug(JDBG, "queued %d bytes : >>> %s <<<", strlen(xmlnode2str(d->x)), xmlnode2str(d->x)); g_async_queue_push(jcr->dqueue, d); } void jcr_queue_deliver(void *a) { extern jcr_instance jcr; GIOStatus rc = G_IO_STATUS_NORMAL; GString *buffer; gsize bytes; int left, len, pkts; dpacket d; GTimeVal timeout; int buf_size = j_atoi(xmlnode_get_data(xmlnode_get_tag(jcr->config,"send-buffer")), 8192); log_warn(JDBG, "packet delivery thread starting."); buffer = g_string_new(NULL); while(TRUE) { g_string_set_size(buffer, 0); pkts = 0; g_get_current_time(&timeout); g_time_val_add(&timeout, (5 * G_USEC_PER_SEC)); d = (dpacket)g_async_queue_timed_pop(jcr->dqueue, &timeout); if (d == NULL) { if (jcr->stream_state == _STREAM_CONNECTED) continue; else break; } g_string_append(buffer, xmlnode2str(d->x)); xmlnode_free(d->x); d = NULL; left = len = buffer->len; pkts++; while ((g_async_queue_length(jcr->dqueue) > 0) && (buffer->len < buf_size)) { d = (dpacket)g_async_queue_pop(jcr->dqueue); g_string_append(buffer, xmlnode2str(d->x)); xmlnode_free(d->x); d = NULL; left = len = buffer->len; pkts++; } // log_debug(JDBG, "%d '%s'", len, buf); while ((left > 0) && (rc == G_IO_STATUS_NORMAL)) { rc = g_io_channel_write_chars(jcr->gio, (buffer->str+(len - left)), left, &bytes, NULL); left = left - bytes; if (rc != G_IO_STATUS_NORMAL) { log_warn(JDBG, "Send packet failed, dropping packet"); } log_debug(JDBG, "wrote %d packets of %d bytes", pkts, bytes); // fprintf(stderr, "wrote %d packets of %d bytes\n", pkts, bytes); if (left==0){ //queue is empty, flushing the socket g_io_channel_flush(jcr->gio, NULL); } } } log_warn(JDBG, "packet delivery thread exiting."); log_warn(JDBG, " Last DvryQ Buffer='%.*s'", buffer->len, buffer->str); g_string_free(buffer, TRUE); } void deliver_fail(dpacket p, char *err) { terror t; /* normal packet bounce */ if(j_strcmp(xmlnode_get_attrib(p->x,"type"),"error") == 0) { /* can't bounce an error */ log_warn(p->host,"dropping a packet to %s from %s: %s",xmlnode_get_attrib(p->x,"to"),xmlnode_get_attrib(p->x,"from"),err); pool_free(p->p); } else { log_warn(p->host,"bouncing a packet to %s from %s: %s",xmlnode_get_attrib(p->x,"to"),xmlnode_get_attrib(p->x,"from"),err); /* turn into an error */ if(err == NULL) { jutil_error(p->x,TERROR_EXTERNAL); } else { t.code = 502; strcpy(t.msg,err); strcpy(t.condition, "service-unavailable"); strcpy(t.type, "wait"); jutil_error(p->x,t); } deliver(dpacket_new(p->x),NULL); } } jabber-muc-0.8/src/jcomp/jcr_shutdown.c0000664000175000017500000000362711160655600017434 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jcr_shutdown.c,v 1.2 2005/12/15 07:48:11 peregrine Exp $ */ #include "jcomp.h" void jcr_error_data(GIOChannel *src, GIOCondition cond, gpointer *data) { extern jcr_instance jcr; log_warn(JDBG, "I/O Channel Error"); g_io_channel_shutdown(jcr->gio, TRUE, NULL); g_main_loop_quit(jcr->gmain); } void jcr_server_shutdown(int signum) { extern jcr_instance jcr; if (!(jcr->in_shutdown)) jcr->in_shutdown = 1; else return; log_warn(JDBG, "Server shutting down"); if (jcr->shandler != NULL) (jcr->shandler)((void *)jcr->sdata); jcr->stream_state = _STREAM_SHUTDOWN; g_thread_join(jcr->dthread); g_io_channel_shutdown(jcr->gio, TRUE, NULL); g_main_loop_quit(jcr->gmain); pool_free(jcr->jcr_i->p); free(jcr->spool_dir); free(jcr->log_dir); free(jcr->recv_buffer); xmlnode_free(jcr->config); XML_ParserFree(jcr->parser); if (jcr->pid_file) { unlink(jcr->pid_file); free(jcr->pid_file); } exit(0); } void jcr_server_hangup(int signum) { log_warn(JDBG, "server received hangup signal, reopening logs"); jcr_log_close(); log_warn(JDBG, "started new log"); return; } jabber-muc-0.8/src/jcomp/jcr_elements.c0000664000175000017500000001341311160655600017367 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jcr_elements.c,v 1.2 2005/12/15 07:48:11 peregrine Exp $ */ #include "jcomp.h" void jcr_start_element(void *m, const char *name, const char **attrib) { extern jcr_instance jcr; pool p; char hashbuf[41]; xmlnode cur; switch (jcr->stream_state) { case _STREAM_INIT_STATE: if (strncasecmp(name, "stream:stream", 13) == 0) { char *pass = xmlnode_get_data(xmlnode_get_tag(jcr->config,"secret")); int i = 0; if (attrib == NULL) return; while (attrib[i] != '\0') { if (strncasecmp(attrib[i], "id", 2) == 0) break; i += 2; } p = pool_new(); // log_debug(JDBG, "%s = '%s'", attrib[i], attrib[i+1]); shahash_r(spools(p, attrib[i + 1], pass, p), hashbuf); /* Build a handshake packet */ cur = xmlnode_new_tag("handshake"); xmlnode_insert_cdata(cur, hashbuf, -1); /* Transmit handshake */ g_async_queue_push(jcr->dqueue, dpacket_new(cur)); // deliver(dpacket_new(cur), NULL); jcr->stream_state = _STREAM_AUTH_SENT; pool_free(p); break; } case _STREAM_CONNECTED: // if (strncasecmp(name, "stream:error", 12) == 0) { // log_warn(JDBG, "'stream:error' in connected server stream: '%s'", xmlnode2str(jcr->current)); // jcr_main_close_stream(); // return; // } if (jcr->current == NULL) { p = pool_heap(5 * 1024); /* Jer's sizing */ jcr->current = xmlnode_new_tag_pool(p, name); // jcr->current = xmlnode_new_tag(name); xmlnode_put_expat_attribs(jcr->current, attrib); } else { jcr->current = xmlnode_insert_tag(jcr->current, name); xmlnode_put_expat_attribs(jcr->current, attrib); } break; } return; } void jcr_end_element(void *m, const char *name) { extern jcr_instance jcr; xmlnode parent; jid to; dpacket d; switch (jcr->stream_state) { case _STREAM_INIT_STATE: case _STREAM_AUTH_SENT: if (strncasecmp(name, "handshake", 10) == 0) { jcr->stream_state = _STREAM_CONNECTED; log_debug(JDBG, " received"); } break; case _STREAM_CONNECTED: if (jcr->current == NULL) { log_warn(JDBG, "jcr->current == NULL, closing stream"); jcr_main_close_stream(); } else { if (strncasecmp(name, "stream:error", 13) == 0) { log_warn(JDBG, "'stream:error' on server stream: '%s'", xmlnode2str(jcr->current)); // jcr_main_close_stream(); return; } if (strncasecmp(name, "stream", 7) == 0) { log_warn(JDBG, "End of Stream from server: '%s'", xmlnode2str(jcr->current)); jcr_main_close_stream(); return; } if (strncasecmp(name, "stream:stream", 14) == 0) { log_warn(JDBG, "End of Stream from server: '%s'", xmlnode2str(jcr->current)); jcr_main_close_stream(); return; } parent = xmlnode_get_parent(jcr->current); if (parent == NULL) { to = jid_new(jcr->current->p, xmlnode_get_attrib(jcr->current, "to")); if (!to || strncasecmp(jcr->jcr_i->id, to->server, strlen(jcr->jcr_i->id)) == 0) { d = dpacket_new(jcr->current); if (d) (jcr->handler)(jcr->jcr_i, d, (void *)jcr->pdata); else xmlnode_free(jcr->current); } else { log_debug(JDBG, "Dropping to '%s'", xmlnode_get_attrib(jcr->current, "to")); xmlnode_free(jcr->current); } jcr->current = NULL; } else { jcr->current = parent; } } break; } } void jcr_cdata(void *m, const char *cdata, int len) { if (jcr->current != NULL) xmlnode_insert_cdata(jcr->current, cdata, len); } gboolean jcr_read_data(GIOChannel *src, GIOCondition cond, gpointer *data) { gsize bytes_read; extern jcr_instance jcr; GError *gerr = NULL; GIOStatus rc; if (cond & G_IO_ERR) { log_warn(JDBG, "Main Channel Error: G_IO_ERR"); return FALSE; } if (cond & G_IO_HUP) { log_warn(JDBG, "Main Channel Error: G_IO_HUP"); return FALSE; } if (cond & G_IO_NVAL) { log_warn(JDBG, "Main Channel Error: G_IO_NVAL"); return FALSE; } if (cond & G_IO_IN) { memset(jcr->recv_buffer, 0, jcr->recv_buffer_size); bytes_read = 0; rc = g_io_channel_read_chars(src, jcr->recv_buffer, (jcr->recv_buffer_size - 1), &bytes_read, &gerr); if (rc != G_IO_STATUS_NORMAL) { if (gerr != NULL) { log_warn(JDBG, "Main Channel Error: rc=%d, %d '%s' '%s'", rc, gerr->code, gerr->domain, gerr->message); g_error_free(gerr); } else { log_warn(JDBG, "Main Channel Error: rc=%d", rc); } jcr_main_close_stream(); return FALSE; } // if (bytes_read == 0) // jcr_error_data(jcr->gio, G_IO_ERR, NULL); if (XML_Parse(jcr->parser, jcr->recv_buffer, bytes_read, 0) == 0) { log_warn(JDBG, "XML Parsing Error: '%s'", (char *)XML_ErrorString(XML_GetErrorCode(jcr->parser))); log_warn(JDBG, " Last Rcvd Buffer='%.*s'", bytes_read, jcr->recv_buffer); jcr_main_close_stream(); return FALSE; } } return TRUE; } jabber-muc-0.8/src/jcomp/jcr_xdb.c0000664000175000017500000000536611160655600016340 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: jcr_xdb.c,v 1.1 2005/12/06 14:48:49 peregrine Exp $ */ #include "jcomp.h" /** * A replacement for xdb_set * returns 0 if the set succeeded, 1 if not */ int xdb_set(void *noop, jid fn, char *ns, xmlnode x) { extern jcr_instance jcr; char buf[512]; xmlnode n, s, xdb; int rc; n = xdb_get(noop, fn, NULL); if (n == NULL) { memset(buf, 0, 512); snprintf(buf, 511, "%s/%s.xml", jcr->spool_dir, fn->user); xdb = xmlnode_new_tag("xdb"); if (ns != NULL) xmlnode_put_attrib(x, "xdbns", ns); // log_debug(JDBG, "node not found, creating %s", xmlnode2str(x)); xmlnode_insert_node(xdb, x); // log_debug(JDBG, "\nwriting '%s'\n\n", xmlnode2str(xdb)); rc = xmlnode2file(buf, xdb); xmlnode_free(xdb); return rc; } else { s = NULL; // log_debug(JDBG, "node found '%s'", xmlnode2str(n)); if (ns == NULL) { s = xmlnode_get_tag(n, xmlnode_get_name(x)); } else { memset(buf, 0, 512); snprintf(buf, 511, "%s?xdbns=%s", xmlnode_get_name(x), ns); // log_debug(JDBG, "search for '%s'", buf); s = xmlnode_get_tag(n, buf); } // log_debug(JDBG, "removing '%s'", xmlnode2str(s)); xmlnode_hide(s); if (ns != NULL) xmlnode_put_attrib(x, "xdbns", ns); xmlnode_insert_tag_node(n, x); } snprintf(buf, 511, "%s/%s.xml", jcr->spool_dir, fn->user); rc = xmlnode2file(buf, n); xmlnode_free(n); log_debug(JDBG, "xdb_set: rc == %d", rc); return 0; } /** * A replacement for xdb_set * returns 0 if the set succeeded, 1 if not */ xmlnode xdb_get(void *noop, jid fn, char *ns) { extern jcr_instance jcr; char buf[512]; xmlnode x, child; memset(buf, 0, 512); snprintf(buf, 511, "%s/%s.xml", jcr->spool_dir, fn->user); x = xmlnode_file(buf); if (x == NULL) return NULL; if (ns != NULL) { memset(buf, 0, 512); snprintf(buf, 511, "?xdbns=%s", ns); child = xmlnode_get_tag(x, buf); if (child == NULL) xmlnode_free(x); return child; } else { return x; } } jabber-muc-0.8/src/jcomp/Makefile0000664000175000017500000000112011160655600016201 0ustar bencerbencer # $Id: Makefile,v 1.1 2005/12/06 14:48:49 peregrine Exp $ CC=gcc CFLAGS:=$(CFLAGS) -O2 -Wall -I../../include -I. `pkg-config --cflags glib-2.0` -D_REENTRANT LIBS= JCOMP_OBJECTS=jcr_xdb.o \ jcr_log.o \ jcr_deliver.o \ jcr_elements.o \ jcr_main_stream_error.o \ jcr_shutdown.o \ jcr_compatibility.o \ jcr_base_connect.o all: $(JCOMP_OBJECTS) main.o gcc -g $(JCOMP_OBJECTS) main.o ../libjcomp.a `pkg-config --libs gthread-2.0` `pkg-config --libs glib-2.0` -o main lib: $(JCOMP_OBJECTS) @ar rv ../libjcomp.a $(JCOMP_OBJECTS) @ranlib ../libjcomp.a clean: rm -f $(JCOMP_OBJECTS) jabber-muc-0.8/src/jabberd/0000775000175000017500000000000011171216166015032 5ustar bencerbencerjabber-muc-0.8/src/jabberd/expat.c0000664000175000017500000001343311160655601016322 0ustar bencerbencer/* -------------------------------------------------------------------------- * * License * * The contents of this file are subject to the Jabber Open Source License * Version 1.0 (the "JOSL"). You may not copy or use this file, in either * source code or executable form, except in compliance with the JOSL. You * may obtain a copy of the JOSL at http://www.jabber.org/ or at * http://www.opensource.org/. * * Software distributed under the JOSL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL * for the specific language governing rights and limitations under the * JOSL. * * Copyrights * * Portions created by or assigned to Jabber.com, Inc. are * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact * information for Jabber.com, Inc. is available at http://www.jabber.com/. * * Portions Copyright (c) 1998-1999 Jeremie Miller. * * Acknowledgements * * Special thanks to the Jabber Open Source Contributors for their * suggestions and support of Jabber. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 or later (the "GPL"), in which case * the provisions of the GPL are applicable instead of those above. If you * wish to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the JOSL, * indicate your decision by deleting the provisions above and replace them * with the notice and other provisions required by the GPL. If you do not * delete the provisions above, a recipient may use your version of this file * under either the JOSL or the GPL. * * * --------------------------------------------------------------------------*/ #include "lib.h" void expat_startElement(void* userdata, const char* name, const char** atts) { /* get the xmlnode pointed to by the userdata */ xmlnode *x = userdata; xmlnode current = *x; if (current == NULL) { /* allocate a base node */ current = xmlnode_new_tag(name); xmlnode_put_expat_attribs(current, atts); *x = current; } else { *x = xmlnode_insert_tag(current, name); xmlnode_put_expat_attribs(*x, atts); } } void expat_endElement(void* userdata, const char* name) { xmlnode *x = userdata; xmlnode current = *x; current->complete = 1; current = xmlnode_get_parent(current); /* if it's NULL we've hit the top folks, otherwise back up a level */ if(current != NULL) *x = current; } void expat_charData(void* userdata, const char* s, int len) { xmlnode *x = userdata; xmlnode current = *x; xmlnode_insert_cdata(current, s, len); } xmlnode xmlnode_str(char *str, int len) { XML_Parser p; xmlnode *x, node; /* pointer to an xmlnode */ if(NULL == str) return NULL; x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, expat_startElement, expat_endElement); XML_SetCharacterDataHandler(p, expat_charData); if(!XML_Parse(p, str, len, 1)) { /* jdebug(ZONE,"xmlnode_str_error: %s",(char *)XML_ErrorString(XML_GetErrorCode(p)));*/ xmlnode_free(*x); *x = NULL; } node = *x; free(x); XML_ParserFree(p); return node; /* return the xmlnode x points to */ } xmlnode xmlnode_file(char *file) { XML_Parser p; xmlnode *x, node; /* pointer to an xmlnode */ char buf[BUFSIZ]; int done, fd, len; if(NULL == file) return NULL; fd = open(file,O_RDONLY); if(fd < 0) return NULL; x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, expat_startElement, expat_endElement); XML_SetCharacterDataHandler(p, expat_charData); do{ len = read(fd, buf, BUFSIZ); done = len < BUFSIZ; if(!XML_Parse(p, buf, len, done)) { /* jdebug(ZONE,"xmlnode_file_parseerror: %s",(char *)XML_ErrorString(XML_GetErrorCode(p)));*/ xmlnode_free(*x); *x = NULL; done = 1; } }while(!done); node = *x; XML_ParserFree(p); free(x); close(fd); return node; /* return the xmlnode x points to */ } char* xmlnode_file_borked(char *file) { XML_Parser p; char buf[BUFSIZ]; static char err[1024]; int fd, len, done; if(NULL == file) return "no file specified"; fd = open(file,O_RDONLY); if(fd < 0) return "unable to open file"; p = XML_ParserCreate(NULL); while(1) { len = read(fd, buf, BUFSIZ); done = len < BUFSIZ; if(!XML_Parse(p, buf, len, done)) { snprintf(err,1023,"%s at line %d and column %d",XML_ErrorString(XML_GetErrorCode(p)),XML_GetErrorLineNumber(p),XML_GetErrorColumnNumber(p)); XML_ParserFree(p); close(fd); return err; } } } int xmlnode2file(char *file, xmlnode node) { char *doc, *ftmp; int fd, i; if(file == NULL || node == NULL) return -1; ftmp = spools(xmlnode_pool(node),file,".t.m.p",xmlnode_pool(node)); fd = open(ftmp, O_CREAT | O_WRONLY | O_TRUNC, 0600); if(fd < 0) return -1; doc = xmlnode2str(node); i = write(fd,doc,strlen(doc)); if(i < 0) return -1; close(fd); if(rename(ftmp,file) < 0) { unlink(ftmp); return -1; } return 1; } void xmlnode_put_expat_attribs(xmlnode owner, const char** atts) { int i = 0; if (atts == NULL) return; while (atts[i] != '\0') { xmlnode_put_attrib(owner, atts[i], atts[i+1]); i += 2; } } jabber-muc-0.8/src/jabberd/jpacket.c0000664000175000017500000001162511160655601016623 0ustar bencerbencer/* -------------------------------------------------------------------------- * * License * * The contents of this file are subject to the Jabber Open Source License * Version 1.0 (the "JOSL"). You may not copy or use this file, in either * source code or executable form, except in compliance with the JOSL. You * may obtain a copy of the JOSL at http://www.jabber.org/ or at * http://www.opensource.org/. * * Software distributed under the JOSL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL * for the specific language governing rights and limitations under the * JOSL. * * Copyrights * * Portions created by or assigned to Jabber.com, Inc. are * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact * information for Jabber.com, Inc. is available at http://www.jabber.com/. * * Portions Copyright (c) 1998-1999 Jeremie Miller. * * Acknowledgements * * Special thanks to the Jabber Open Source Contributors for their * suggestions and support of Jabber. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 or later (the "GPL"), in which case * the provisions of the GPL are applicable instead of those above. If you * wish to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the JOSL, * indicate your decision by deleting the provisions above and replace them * with the notice and other provisions required by the GPL. If you do not * delete the provisions above, a recipient may use your version of this file * under either the JOSL or the GPL. * * * --------------------------------------------------------------------------*/ #include "lib.h" jpacket jpacket_new(xmlnode x) { jpacket p; if(x == NULL) return NULL; p = pmalloc(xmlnode_pool(x),sizeof(_jpacket)); p->x = x; return jpacket_reset(p); } jpacket jpacket_reset(jpacket p) { char *val; xmlnode x; x = p->x; memset(p,0,sizeof(_jpacket)); p->x = x; p->p = xmlnode_pool(x); if(strcmp(xmlnode_get_name(x),"message") == 0) { p->type = JPACKET_MESSAGE; }else if(strcmp(xmlnode_get_name(x),"presence") == 0) { p->type = JPACKET_PRESENCE; val = xmlnode_get_attrib(x, "type"); if(val == NULL) p->subtype = JPACKET__AVAILABLE; else if(strcmp(val,"unavailable") == 0) p->subtype = JPACKET__UNAVAILABLE; else if(strcmp(val,"probe") == 0) p->subtype = JPACKET__PROBE; else if(strcmp(val,"error") == 0) p->subtype = JPACKET__ERROR; else if(strcmp(val,"invisible") == 0) p->subtype = JPACKET__INVISIBLE; else if(*val == 's' || *val == 'u') p->type = JPACKET_S10N; else p->type = JPACKET_UNKNOWN; }else if(strcmp(xmlnode_get_name(x),"iq") == 0) { p->type = JPACKET_IQ; p->iq = xmlnode_get_tag(x,"?xmlns"); p->iqns = xmlnode_get_attrib(p->iq,"xmlns"); } /* set up the jids if any, flag packet as unknown if they are unparseable */ val = xmlnode_get_attrib(x,"to"); if(val != NULL) if((p->to = jid_new(p->p, val)) == NULL) p->type = JPACKET_UNKNOWN; val = xmlnode_get_attrib(x,"from"); if(val != NULL) if((p->from = jid_new(p->p, val)) == NULL) p->type = JPACKET_UNKNOWN; return p; } int jpacket_subtype(jpacket p) { char *type; int ret = p->subtype; if(ret != JPACKET__UNKNOWN) return ret; ret = JPACKET__NONE; /* default, when no type attrib is specified */ type = xmlnode_get_attrib(p->x, "type"); if(j_strcmp(type,"error") == 0) ret = JPACKET__ERROR; else switch(p->type) { case JPACKET_MESSAGE: if(j_strcmp(type,"chat") == 0) ret = JPACKET__CHAT; else if(j_strcmp(type,"groupchat") == 0) ret = JPACKET__GROUPCHAT; else if(j_strcmp(type,"headline") == 0) ret = JPACKET__HEADLINE; break; case JPACKET_S10N: if(j_strcmp(type,"subscribe") == 0) ret = JPACKET__SUBSCRIBE; else if(j_strcmp(type,"subscribed") == 0) ret = JPACKET__SUBSCRIBED; else if(j_strcmp(type,"unsubscribe") == 0) ret = JPACKET__UNSUBSCRIBE; else if(j_strcmp(type,"unsubscribed") == 0) ret = JPACKET__UNSUBSCRIBED; break; case JPACKET_IQ: if(j_strcmp(type,"get") == 0) ret = JPACKET__GET; else if(j_strcmp(type,"set") == 0) ret = JPACKET__SET; else if(j_strcmp(type,"result") == 0) ret = JPACKET__RESULT; break; } p->subtype = ret; return ret; } jabber-muc-0.8/src/jabberd/snprintf.c0000664000175000017500000006455211160655601017054 0ustar bencerbencer/* ==================================================================== * Copyright (c) 1995-1998 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. * * 5. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see . * * This code is based on, and used with the permission of, the * SIO stdio-replacement strx_* functions by Panos Tsirigotis * for xinetd. */ #include "lib.h" #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) #include #include #include #include #include #include #include #ifdef HAVE_GCVT #define ap_ecvt ecvt #define ap_fcvt fcvt #define ap_gcvt gcvt #else /* * cvt.c - IEEE floating point formatting routines for FreeBSD * from GNU libc-4.6.27 */ /* * ap_ecvt converts to decimal * the number of digits is specified by ndigit * decpt is set to the position of the decimal point * sign is set to 0 for positive, 1 for negative */ #define NDIG 80 static char * ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag) { register int r2; double fi, fj; register char *p, *p1; static char buf[NDIG]; if (ndigits >= NDIG - 1) ndigits = NDIG - 2; r2 = 0; *sign = 0; p = &buf[0]; if (arg < 0) { *sign = 1; arg = -arg; } arg = modf(arg, &fi); p1 = &buf[NDIG]; /* * Do integer part */ if (fi != 0) { p1 = &buf[NDIG]; while (fi != 0) { fj = modf(fi / 10, &fi); *--p1 = (int) ((fj + .03) * 10) + '0'; r2++; } while (p1 < &buf[NDIG]) *p++ = *p1++; } else if (arg > 0) { while ((fj = arg * 10) < 1) { arg = fj; r2--; } } p1 = &buf[ndigits]; if (eflag == 0) p1 += r2; *decpt = r2; if (p1 < &buf[0]) { buf[0] = '\0'; return (buf); } while (p <= p1 && p < &buf[NDIG]) { arg *= 10; arg = modf(arg, &fj); *p++ = (int) fj + '0'; } if (p1 >= &buf[NDIG]) { buf[NDIG - 1] = '\0'; return (buf); } p = p1; *p1 += 5; while (*p1 > '9') { *p1 = '0'; if (p1 > buf) ++ * --p1; else { *p1 = '1'; (*decpt)++; if (eflag == 0) { if (p > buf) *p = '0'; p++; } } } *p = '\0'; return (buf); } static char * ap_ecvt(double arg, int ndigits, int *decpt, int *sign) { return (ap_cvt(arg, ndigits, decpt, sign, 1)); } static char * ap_fcvt(double arg, int ndigits, int *decpt, int *sign) { return (ap_cvt(arg, ndigits, decpt, sign, 0)); } /* * ap_gcvt - Floating output conversion to * minimal length string */ static char * ap_gcvt(double number, int ndigit, char *buf) { int sign, decpt; register char *p1, *p2; int i; p1 = ap_ecvt(number, ndigit, &decpt, &sign); p2 = buf; if (sign) *p2++ = '-'; for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--) ndigit--; if ((decpt >= 0 && decpt - ndigit > 4) || (decpt < 0 && decpt < -3)) { /* use E-style */ decpt--; *p2++ = *p1++; *p2++ = '.'; for (i = 1; i < ndigit; i++) *p2++ = *p1++; *p2++ = 'e'; if (decpt < 0) { decpt = -decpt; *p2++ = '-'; } else *p2++ = '+'; if (decpt / 100 > 0) *p2++ = decpt / 100 + '0'; if (decpt / 10 > 0) *p2++ = (decpt % 100) / 10 + '0'; *p2++ = decpt % 10 + '0'; } else { if (decpt <= 0) { if (*p1 != '0') *p2++ = '.'; while (decpt < 0) { decpt++; *p2++ = '0'; } } for (i = 1; i <= ndigit; i++) { *p2++ = *p1++; if (i == decpt) *p2++ = '.'; } if (ndigit < decpt) { while (ndigit++ < decpt) *p2++ = '0'; *p2++ = '.'; } } if (p2[-1] == '.') p2--; *p2 = '\0'; return (buf); } #endif /* HAVE_CVT */ typedef enum { NO = 0, YES = 1 } boolean_e; #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #define NUL '\0' #define INT_NULL ((int *)0) #define WIDE_INT long typedef WIDE_INT wide_int; typedef unsigned WIDE_INT u_wide_int; typedef int bool_int; #define S_NULL "(null)" #define S_NULL_LEN 6 #define FLOAT_DIGITS 6 #define EXPONENT_LENGTH 10 /* * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions * * XXX: this is a magic number; do not decrease it */ #define NUM_BUF_SIZE 512 /* * Descriptor for buffer area */ struct buf_area { char *buf_end; char *nextb; /* pointer to next byte to read/write */ }; typedef struct buf_area buffy; /* * The INS_CHAR macro inserts a character in the buffer and writes * the buffer back to disk if necessary * It uses the char pointers sp and bep: * sp points to the next available character in the buffer * bep points to the end-of-buffer+1 * While using this macro, note that the nextb pointer is NOT updated. * * NOTE: Evaluation of the c argument should not have any side-effects */ #define INS_CHAR( c, sp, bep, cc ) \ { \ if ( sp < bep ) \ { \ *sp++ = c ; \ cc++ ; \ } \ } #define NUM( c ) ( c - '0' ) #define STR_TO_DEC( str, num ) \ num = NUM( *str++ ) ; \ while ( isdigit((int)*str ) ) \ { \ num *= 10 ; \ num += NUM( *str++ ) ; \ } /* * This macro does zero padding so that the precision * requirement is satisfied. The padding is done by * adding '0's to the left of the string that is going * to be printed. */ #define FIX_PRECISION( adjust, precision, s, s_len ) \ if ( adjust ) \ while ( s_len < precision ) \ { \ *--s = '0' ; \ s_len++ ; \ } /* * Macro that does padding. The padding is done by printing * the character ch. */ #define PAD( width, len, ch ) do \ { \ INS_CHAR( ch, sp, bep, cc ) ; \ width-- ; \ } \ while ( width > len ) /* * Prefix the character ch to the string str * Increase length * Set the has_prefix flag */ #define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES /* * Convert num to its decimal format. * Return value: * - a pointer to a string containing the number (no sign) * - len contains the length of the string * - is_negative is set to TRUE or FALSE depending on the sign * of the number (always set to FALSE if is_unsigned is TRUE) * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ static char * conv_10(register wide_int num, register bool_int is_unsigned, register bool_int * is_negative, char *buf_end, register int *len) { register char *p = buf_end; register u_wide_int magnitude; if (is_unsigned) { magnitude = (u_wide_int) num; *is_negative = FALSE; } else { *is_negative = (num < 0); /* * On a 2's complement machine, negating the most negative integer * results in a number that cannot be represented as a signed integer. * Here is what we do to obtain the number's magnitude: * a. add 1 to the number * b. negate it (becomes positive) * c. convert it to unsigned * d. add 1 */ if (*is_negative) { wide_int t = num + 1; magnitude = ((u_wide_int) - t) + 1; } else magnitude = (u_wide_int) num; } /* * We use a do-while loop so that we write at least 1 digit */ do { register u_wide_int new_magnitude = magnitude / 10; *--p = magnitude - new_magnitude * 10 + '0'; magnitude = new_magnitude; } while (magnitude); *len = buf_end - p; return (p); } /* * Convert a floating point number to a string formats 'f', 'e' or 'E'. * The result is placed in buf, and len denotes the length of the string * The sign is returned in the is_negative argument (and is not placed * in buf). */ static char * conv_fp(register char format, register double num, boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len) { register char *s = buf; register char *p; int decimal_point; if (format == 'f') p = ap_fcvt(num, precision, &decimal_point, is_negative); else /* either e or E format */ p = ap_ecvt(num, precision + 1, &decimal_point, is_negative); /* * Check for Infinity and NaN */ if (isalpha((int)*p)) { *len = strlen(strcpy(buf, p)); *is_negative = FALSE; return (buf); } if (format == 'f') { if (decimal_point <= 0) { *s++ = '0'; if (precision > 0) { *s++ = '.'; while (decimal_point++ < 0) *s++ = '0'; } else if (add_dp) { *s++ = '.'; } } else { while (decimal_point-- > 0) { *s++ = *p++; } if (precision > 0 || add_dp) { *s++ = '.'; } } } else { *s++ = *p++; if (precision > 0 || add_dp) *s++ = '.'; } /* * copy the rest of p, the NUL is NOT copied */ while (*p) *s++ = *p++; if (format != 'f') { char temp[EXPONENT_LENGTH]; /* for exponent conversion */ int t_len; bool_int exponent_is_negative; *s++ = format; /* either e or E */ decimal_point--; if (decimal_point != 0) { p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len); *s++ = exponent_is_negative ? '-' : '+'; /* * Make sure the exponent has at least 2 digits */ if (t_len == 1) *s++ = '0'; while (t_len--) *s++ = *p++; } else { *s++ = '+'; *s++ = '0'; *s++ = '0'; } } *len = s - buf; return (buf); } /* * Convert num to a base X number where X is a power of 2. nbits determines X. * For example, if nbits is 3, we do base 8 conversion * Return value: * a pointer to a string containing the number * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ static char * conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len) { register int mask = (1 << nbits) - 1; register char *p = buf_end; static char low_digits[] = "0123456789abcdef"; static char upper_digits[] = "0123456789ABCDEF"; register char *digits = (format == 'X') ? upper_digits : low_digits; do { *--p = digits[num & mask]; num >>= nbits; } while (num); *len = buf_end - p; return (p); } /* * Do format conversion placing the output in buffer */ static int format_converter(register buffy * odp, const char *fmt, va_list ap) { register char *sp; register char *bep; register int cc = 0; register int i; register char *s = NULL; char *q; int s_len; register int min_width = 0; int precision = 0; enum { LEFT, RIGHT } adjust; char pad_char; char prefix_char; double fp_num; wide_int i_num = (wide_int) 0; u_wide_int ui_num; char num_buf[NUM_BUF_SIZE]; char char_buf[2]; /* for printing %% and % */ /* * Flag variables */ boolean_e is_long; boolean_e alternate_form; boolean_e print_sign; boolean_e print_blank; boolean_e adjust_precision; boolean_e adjust_width; bool_int is_negative; sp = odp->nextb; bep = odp->buf_end; while (*fmt) { if (*fmt != '%') { INS_CHAR(*fmt, sp, bep, cc); } else { /* * Default variable settings */ adjust = RIGHT; alternate_form = print_sign = print_blank = NO; pad_char = ' '; prefix_char = NUL; fmt++; /* * Try to avoid checking for flags, width or precision */ if (isascii((int)*fmt) && !islower((int)*fmt)) { /* * Recognize flags: -, #, BLANK, + */ for (;; fmt++) { if (*fmt == '-') adjust = LEFT; else if (*fmt == '+') print_sign = YES; else if (*fmt == '#') alternate_form = YES; else if (*fmt == ' ') print_blank = YES; else if (*fmt == '0') pad_char = '0'; else break; } /* * Check if a width was specified */ if (isdigit((int)*fmt)) { STR_TO_DEC(fmt, min_width); adjust_width = YES; } else if (*fmt == '*') { min_width = va_arg(ap, int); fmt++; adjust_width = YES; if (min_width < 0) { adjust = LEFT; min_width = -min_width; } } else adjust_width = NO; /* * Check if a precision was specified * * XXX: an unreasonable amount of precision may be specified * resulting in overflow of num_buf. Currently we * ignore this possibility. */ if (*fmt == '.') { adjust_precision = YES; fmt++; if (isdigit((int)*fmt)) { STR_TO_DEC(fmt, precision); } else if (*fmt == '*') { precision = va_arg(ap, int); fmt++; if (precision < 0) precision = 0; } else precision = 0; } else adjust_precision = NO; } else adjust_precision = adjust_width = NO; /* * Modifier check */ if (*fmt == 'l') { is_long = YES; fmt++; } else is_long = NO; /* * Argument extraction and printing. * First we determine the argument type. * Then, we convert the argument to a string. * On exit from the switch, s points to the string that * must be printed, s_len has the length of the string * The precision requirements, if any, are reflected in s_len. * * NOTE: pad_char may be set to '0' because of the 0 flag. * It is reset to ' ' by non-numeric formats */ switch (*fmt) { case 'u': if (is_long) i_num = va_arg(ap, u_wide_int); else i_num = (wide_int) va_arg(ap, unsigned int); /* * The rest also applies to other integer formats, so fall * into that case. */ case 'd': case 'i': /* * Get the arg if we haven't already. */ if ((*fmt) != 'u') { if (is_long) i_num = va_arg(ap, wide_int); else i_num = (wide_int) va_arg(ap, int); }; s = conv_10(i_num, (*fmt) == 'u', &is_negative, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (*fmt != 'u') { if (is_negative) prefix_char = '-'; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; } break; case 'o': if (is_long) ui_num = va_arg(ap, u_wide_int); else ui_num = (u_wide_int) va_arg(ap, unsigned int); s = conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (alternate_form && *s != '0') { *--s = '0'; s_len++; } break; case 'x': case 'X': if (is_long) ui_num = (u_wide_int) va_arg(ap, u_wide_int); else ui_num = (u_wide_int) va_arg(ap, unsigned int); s = conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (alternate_form && i_num != 0) { *--s = *fmt; /* 'x' or 'X' */ *--s = '0'; s_len += 2; } break; case 's': s = va_arg(ap, char *); if (s != NULL) { s_len = strlen(s); if (adjust_precision && precision < s_len) s_len = precision; } else { s = S_NULL; s_len = S_NULL_LEN; } pad_char = ' '; break; case 'f': case 'e': case 'E': fp_num = va_arg(ap, double); s = conv_fp(*fmt, fp_num, alternate_form, (adjust_precision == NO) ? FLOAT_DIGITS : precision, &is_negative, &num_buf[1], &s_len); if (is_negative) prefix_char = '-'; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; break; case 'g': case 'G': if (adjust_precision == NO) precision = FLOAT_DIGITS; else if (precision == 0) precision = 1; /* * * We use &num_buf[ 1 ], so that we have room for the sign */ s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1]); if (*s == '-') prefix_char = *s++; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; s_len = strlen(s); if (alternate_form && (q = strchr(s, '.')) == NULL) s[s_len++] = '.'; if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) *q = 'E'; break; case 'c': char_buf[0] = (char) (va_arg(ap, int)); s = &char_buf[0]; s_len = 1; pad_char = ' '; break; case '%': char_buf[0] = '%'; s = &char_buf[0]; s_len = 1; pad_char = ' '; break; case 'n': *(va_arg(ap, int *)) = cc; break; /* * Always extract the argument as a "char *" pointer. We * should be using "void *" but there are still machines * that don't understand it. * If the pointer size is equal to the size of an unsigned * integer we convert the pointer to a hex number, otherwise * we print "%p" to indicate that we don't handle "%p". */ case 'p': ui_num = (u_wide_int) va_arg(ap, char *); if (sizeof(char *) <= sizeof(u_wide_int)) s = conv_p2(ui_num, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); else { s = "%p"; s_len = 2; } pad_char = ' '; break; case NUL: /* * The last character of the format string was %. * We ignore it. */ continue; /* * The default case is for unrecognized %'s. * We print % to help the user identify what * option is not understood. * This is also useful in case the user wants to pass * the output of format_converter to another function * that understands some other % (like syslog). * Note that we can't point s inside fmt because the * unknown could be preceded by width etc. */ default: char_buf[0] = '%'; char_buf[1] = *fmt; s = char_buf; s_len = 2; pad_char = ' '; break; } if (prefix_char != NUL) { *--s = prefix_char; s_len++; } if (adjust_width && adjust == RIGHT && min_width > s_len) { if (pad_char == '0' && prefix_char != NUL) { INS_CHAR(*s, sp, bep, cc) s++; s_len--; min_width--; } PAD(min_width, s_len, pad_char); } /* * Print the string s. */ for (i = s_len; i != 0; i--) { INS_CHAR(*s, sp, bep, cc); s++; } if (adjust_width && adjust == LEFT && min_width > s_len) PAD(min_width, s_len, pad_char); } fmt++; } odp->nextb = sp; return (cc); } /* * This is the general purpose conversion function. */ static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) { buffy od; int cc; /* * First initialize the descriptor * Notice that if no length is given, we initialize buf_end to the * highest possible address. */ od.buf_end = len ? &buf[len] : (char *) ~0; od.nextb = buf; /* * Do the conversion */ cc = format_converter(&od, format, ap); if (len == 0 || od.nextb <= od.buf_end) *(od.nextb) = '\0'; if (ccp) *ccp = cc; } int ap_snprintf(char *buf, size_t len, const char *format,...) { int cc; va_list ap; va_start(ap, format); strx_printv(&cc, buf, (len - 1), format, ap); va_end(ap); return (cc); } int ap_vsnprintf(char *buf, size_t len, const char *format, va_list ap) { int cc; strx_printv(&cc, buf, (len - 1), format, ap); return (cc); } #endif /* HAVE_SNPRINTF */ jabber-muc-0.8/src/jabberd/jid.c0000664000175000017500000004515611160655601015756 0ustar bencerbencer/* -------------------------------------------------------------------------- * * License * * The contents of this file are subject to the Jabber Open Source License * Version 1.0 (the "JOSL"). You may not copy or use this file, in either * source code or executable form, except in compliance with the JOSL. You * may obtain a copy of the JOSL at http://www.jabber.org/ or at * http://www.opensource.org/. * * Software distributed under the JOSL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL * for the specific language governing rights and limitations under the * JOSL. * * Copyrights * * Portions created by or assigned to Jabber.com, Inc. are * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact * information for Jabber.com, Inc. is available at http://www.jabber.com/. * * Portions Copyright (c) 1998-1999 Jeremie Miller. * * Acknowledgements * * Special thanks to the Jabber Open Source Contributors for their * suggestions and support of Jabber. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 or later (the "GPL"), in which case * the provisions of the GPL are applicable instead of those above. If you * wish to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the JOSL, * indicate your decision by deleting the provisions above and replace them * with the notice and other provisions required by the GPL. If you do not * delete the provisions above, a recipient may use your version of this file * under either the JOSL or the GPL. * * --------------------------------------------------------------------------*/ /** * @file jid.c * @brief representation and normalization of JabberIDs */ #include "lib.h" #ifdef LIBIDN # include # include # include "hash.h" /** * @brief datastructure to build the stringprep caches */ typedef struct _jid_prep_entry_st { char *preped; /**< the result of the preparation, NULL if unchanged */ time_t last_used; /**< when this result has last been successfully used */ unsigned int used_count; /**< how often this result has been successfully used */ int size; /**< the min buffer size needed to hold the result (strlen+1) */ } *_jid_prep_entry_t; /** * @brief string preparation cache */ typedef struct _jid_prep_cache_st { GHashTable *hashtable; /**< the hash table containing the preped strings */ GMutex *mutex; /**< mutex controling the access to the hashtable */ const Stringprep_profile *profile; /**< the stringprep profile used for this cache */ } *_jid_prep_cache_t; /** * stringprep cache containging already preped nodes * * we are using global caches here for two reasons: * - I do not see why different instances would want * to have different caches as we are always doing * the same * - For per instance caches I would have to modify the * interface of the jid_*() functions which would break * compatibility with transports */ static _jid_prep_cache_t _jid_prep_cache_node = NULL; /** * stringprep cache containing already preped domains */ static _jid_prep_cache_t _jid_prep_cache_domain = NULL; /** * stringprep cache containing already preped resources */ static _jid_prep_cache_t _jid_prep_cache_resource = NULL; /** * walker for cleaning up stringprep caches * * @param h the hash we are walking through * @param key the key of this item * @param val the value of this item * @param arg delete entries older as this unix timestamp */ static gboolean _jid_clean_walker(gpointer key, gpointer val, gpointer arg) { time_t *keep_newer_as = (time_t*)arg; _jid_prep_entry_t entry = (_jid_prep_entry_t)val; if (entry == NULL) return FALSE; if (entry->last_used <= *keep_newer_as) { if (entry->preped != NULL) free(entry->preped); free(entry); return TRUE; } return FALSE; } /** * walk through a single stringprep cache and check which entries have expired */ static void _jid_clean_single_cache(_jid_prep_cache_t cache, time_t keep_newer_as) { /* acquire the lock on the cache */ g_mutex_lock(cache->mutex); /* walk over all entries */ g_hash_table_foreach_remove(cache->hashtable, _jid_clean_walker, (void*)&keep_newer_as); /* we're done, release the lock on the cache */ g_mutex_unlock(cache->mutex); } /** * walk through the stringprep caches and check which entries have expired */ void jid_clean_cache(void) { /* XXX make this configurable? */ time_t keep_newer_as = time(NULL) - 900; /* cleanup the nodeprep cache */ _jid_clean_single_cache(_jid_prep_cache_node, keep_newer_as); /* cleanup the domain preparation cache */ _jid_clean_single_cache(_jid_prep_cache_domain, keep_newer_as); /* cleanup the resourceprep cache */ _jid_clean_single_cache(_jid_prep_cache_resource, keep_newer_as); } /** * caching wrapper around a stringprep function * * @param in_out_buffer buffer containing what has to be stringpreped and that gets the result * @param max_len size of the buffer * @param cache the used cache, defining also the used stringprep profile * @return the return code of the stringprep call */ static int _jid_cached_stringprep(char *in_out_buffer, int max_len, _jid_prep_cache_t cache) { _jid_prep_entry_t preped; int result = STRINGPREP_OK; /* check that the cache already exists * we can not do anything as we don't know which profile has to be used */ if (cache == NULL) { return STRINGPREP_UNKNOWN_PROFILE; } /* is there something that has to be stringpreped? */ if (in_out_buffer == NULL) { return STRINGPREP_OK; } /* acquire the lock on the cache */ g_mutex_lock(cache->mutex); /* check if the requested preparation has already been done */ preped = (_jid_prep_entry_t)g_hash_table_lookup(cache->hashtable, in_out_buffer); if (preped != NULL) { /* we already prepared this argument */ if (preped->size <= max_len) { /* we can use the result */ /* update the statistic */ preped->used_count++; preped->last_used = time(NULL); /* do we need to copy the result? */ if (preped->preped != NULL) { /* copy the result */ strcpy(in_out_buffer, preped->preped); } result = STRINGPREP_OK; } else { /* we need a bigger buffer */ result = STRINGPREP_TOO_SMALL_BUFFER; } /* we're done, release the lock on the cache */ g_mutex_unlock(cache->mutex); } else { char *original; /* stringprep needs time, release the lock on the cache for the meantime */ g_mutex_unlock(cache->mutex); /* we have to keep the key */ original = strdup(in_out_buffer); /* try to prepare the string */ result = stringprep(in_out_buffer, max_len, STRINGPREP_NO_UNASSIGNED, cache->profile); /* did we manage to prepare the string? */ if (result == STRINGPREP_OK && original != NULL) { /* generate an entry for the cache */ preped = (_jid_prep_entry_t)malloc(sizeof(struct _jid_prep_entry_st)); if (preped != NULL) { /* has there been modified something? */ if (j_strcmp(in_out_buffer, original) == 0) { /* no, we don't need to store a copy of the original string */ preped->preped = NULL; } else { /* yes, store the stringpreped string */ preped->preped = strdup(in_out_buffer); } preped->last_used = time(NULL); preped->used_count = 1; preped->size = strlen(in_out_buffer)+1; /* acquire the lock on the cache again */ g_mutex_lock(cache->mutex); /* store the entry in the cache */ g_hash_table_insert(cache->hashtable, original, preped); /* we're done, release the lock on the cache */ g_mutex_unlock(cache->mutex); } else { /* we don't need the copy of the key, if there is no memory to store it */ free(original); } } else { /* we don't need the copy of the original value */ if (original != NULL) free(original); } } return result; } /** * free a single stringprep cache * * @param cache the cache to free */ static void _jid_stop_single_cache(_jid_prep_cache_t *cache) { if (*cache == NULL) return; _jid_clean_single_cache(*cache, time(NULL)); g_mutex_lock((*cache)->mutex); g_hash_table_destroy((*cache)->hashtable); g_mutex_free((*cache)->mutex); free(*cache); *cache = NULL; } /** * init a single stringprep cache * * @param cache the cache to init * @param profile profile used to prepare the strings */ static void _jid_init_single_cache(_jid_prep_cache_t *cache, const Stringprep_profile *profile) { /* do not init a cache twice */ if (*cache == NULL) { *cache = (_jid_prep_cache_t)malloc(sizeof(struct _jid_prep_cache_st)); (*cache)->mutex = g_mutex_new(); (*cache)->hashtable = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, NULL); (*cache)->profile = profile; } } /** * free the stringprep caches */ void jid_stop_caching(void) { _jid_stop_single_cache(&_jid_prep_cache_node); _jid_stop_single_cache(&_jid_prep_cache_domain); _jid_stop_single_cache(&_jid_prep_cache_resource); } /** * init the stringprep caches * (do not call this twice at the same time, we do not have the mutexes yet) */ void jid_init_cache(void) { /* init the nodeprep cache */ _jid_init_single_cache(&_jid_prep_cache_node, stringprep_xmpp_nodeprep); /* init the nameprep cache (domains) */ _jid_init_single_cache(&_jid_prep_cache_domain, stringprep_nameprep); /* init the resourceprep cache */ _jid_init_single_cache(&_jid_prep_cache_resource, stringprep_xmpp_resourceprep); } /** * nameprep the domain identifier in a JID and check if it is valid * * @param jid data structure holding the JID * @return 0 if JID is valid, non zero otherwise */ static int _jid_safe_domain(jid id) { int result=0; /* there must be a domain identifier */ if (id->server == NULL || *id->server == '\0') return 1; /* nameprep the domain identifier */ result = _jid_cached_stringprep(id->server, strlen(id->server)+1, _jid_prep_cache_domain); if (result == STRINGPREP_TOO_SMALL_BUFFER) { /* nameprep wants to expand the string, e.g. conversion from ß to ss */ size_t biggerbuffersize = 1024; char *biggerbuffer = pmalloc(id->p, biggerbuffersize); if (biggerbuffer == NULL) return 1; strcpy(biggerbuffer, id->server); result = _jid_cached_stringprep(biggerbuffer, biggerbuffersize, _jid_prep_cache_domain); id->server = biggerbuffer; } if (result != STRINGPREP_OK) return 1; /* the namepreped domain must not be longer than 1023 bytes */ if (j_strlen(id->server) > 1023) return 1; /* if nothing failed, the domain is valid */ return 0; } /** * nodeprep the node identifier in a JID and check if it is valid * * @param jid data structure holding the JID * @return 0 if JID is valid, non zero otherwise */ static int _jid_safe_node(jid id) { int result=0; /* it is valid to have no node identifier in the JID */ if (id->user == NULL) return 0; /* nodeprep */ result = _jid_cached_stringprep(id->user, strlen(id->user)+1, _jid_prep_cache_node); if (result == STRINGPREP_TOO_SMALL_BUFFER) { /* nodeprep wants to expand the string, e.g. conversion from ß to ss */ size_t biggerbuffersize = 1024; char *biggerbuffer = pmalloc(id->p, biggerbuffersize); if (biggerbuffer == NULL) return 1; strcpy(biggerbuffer, id->user); result = _jid_cached_stringprep(biggerbuffer, biggerbuffersize, _jid_prep_cache_node); id->user = biggerbuffer; } if (result != STRINGPREP_OK) return 1; /* the nodepreped node must not be longer than 1023 bytes */ if (j_strlen(id->user) > 1023) return 1; /* check if node was zeroed by stringprep */ if (*id->user == '\0') id->user = NULL; /* if nothing failed, the node is valid */ return 0; } /** * resourceprep the resource identifier in a JID and check if it is valid * * @param jid data structure holding the JID * @return 0 if JID is valid, non zero otherwise */ static int _jid_safe_resource(jid id) { int result=0; /* it is valid to have no resource identifier in the JID */ if (id->resource == NULL) return 0; /* resource prep the resource identifier */ result = _jid_cached_stringprep(id->resource, strlen(id->resource)+1, _jid_prep_cache_resource); if (result == STRINGPREP_TOO_SMALL_BUFFER) { /* resourceprep wants to expand the string, e.g. conversion from ß to ss */ size_t biggerbuffersize = 1024; char *biggerbuffer = pmalloc(id->p, biggerbuffersize); if (biggerbuffer == NULL) return 1; strcpy(biggerbuffer, id->resource); result = _jid_cached_stringprep(id->resource, strlen(id->resource)+1, _jid_prep_cache_resource); id->resource = biggerbuffer; } if (result != STRINGPREP_OK) return 1; /* the resourcepreped resource must not be longer than 1023 bytes */ if (j_strlen(id->resource) > 1023) return 1; /* check if resource was zeroed by stringprep */ if (*id->resource == '\0') id->resource = NULL; /* if nothing failed, the resource is valid */ return 0; } #else /* no LIBIDN */ /** * check if the domain identifier in a JID is valid * * @param jid data structure holding the JID * @return 0 if domain is valid, non zero otherwise */ static int _jid_safe_domain(jid id) { char *str; /* there must be a domain identifier */ if (id->server == NULL || *id->server == '\0') return 1; /* and it must not be longer than 1023 bytes */ if (strlen(id->server) > 1023) return 1; /* lowercase the hostname, make sure it's valid characters */ for(str = id->server; *str != '\0'; str++) { *str = tolower(*str); if(!(isalnum(*str) || *str == '.' || *str == '-' || *str == '_')) return 1; } /* otherwise it's okay as far as we can tell without LIBIDN */ return 0; } /** * check if the node identifier in a JID is valid * * @param jid data structure holding the JID * @return 0 if node is valid, non zero otherwise */ static int _jid_safe_node(jid id) { char *str; /* node identifiers may not be longer than 1023 bytes */ if (j_strlen(id->user) > 1023) return 1; /* check for low and invalid ascii characters in the username */ if(id->user != NULL) for(str = id->user; *str != '\0'; str++) if(*str <= 32 || *str == ':' || *str == '@' || *str == '<' || *str == '>' || *str == '\'' || *str == '"' || *str == '&' || *str == '/') return 1; /* otherwise it's okay as far as we can tell without LIBIDN */ return 0; } /** * check if the resource identifier in a JID is valid * * @param jid data structure holding the JID * @return 0 if resource is valid, non zero otherwise */ static int _jid_safe_resource(jid id) { /* resources may not be longer than 1023 bytes */ if (j_strlen(id->resource) > 1023) return 1; /* otherwise it's okay as far as we can tell without LIBIDN */ return 0; } #endif /** * nodeprep/nameprep/resourceprep the JID and check if it is valid * * @param jid data structure holding the JID * @return NULL if the JID is invalid, pointer to the jid otherwise */ jid jid_safe(jid id) { if (_jid_safe_domain(id)) return NULL; if (_jid_safe_node(id)) return NULL; if (_jid_safe_resource(id)) return NULL; return id; } jid jid_new(pool p, const char *idstr) { char *server, *resource, *type, *str; jid id; if(p == NULL || idstr == NULL || *idstr == '\0') return NULL; /* user@server/resource */ str = pstrdup(p, idstr); id = pmalloco(p,sizeof(struct jid_struct)); id->p = p; resource = strstr(str,"/"); if(resource != NULL) { *resource = '\0'; ++resource; if(*resource != '\0') id->resource = resource; }else{ resource = str + strlen(str); /* point to end */ } type = strstr(str,":"); if(type != NULL && type < resource) { *type = '\0'; ++type; str = type; /* ignore the type: prefix */ } server = strstr(str,"@"); if(server == NULL || server > resource) { /* if there's no @, it's just the server address */ id->server = str; }else{ *server = '\0'; ++server; id->server = server; if(*str != '\0') id->user = str; } return jid_safe(id); } void jid_set(jid id, char *str, int item) { char *old; if(id == NULL) return; /* invalidate the cached copy */ id->full = NULL; switch(item) { case JID_RESOURCE: old = id->resource; if(str != NULL && *str != '\0') id->resource = pstrdup(id->p, str); else id->resource = NULL; if(_jid_safe_resource(id)) id->resource = old; /* revert if invalid */ break; case JID_USER: old = id->user; if(str != NULL && *str != '\0') id->user = pstrdup(id->p, str); else id->user = NULL; if(_jid_safe_node(id)) id->user = old; /* revert if invalid */ break; case JID_SERVER: old = id->server; id->server = pstrdup(id->p, str); if(_jid_safe_domain(id)) id->server = old; /* revert if invalid */ break; } } char *jid_full(jid id) { spool s; if(id == NULL) return NULL; /* use cached copy */ if(id->full != NULL) return id->full; s = spool_new(id->p); if(id->user != NULL) spooler(s, id->user,"@",s); spool_add(s, id->server); if(id->resource != NULL) spooler(s, "/",id->resource,s); id->full = spool_print(s); return id->full; } /* local utils */ static int _jid_nullstrcmp(char *a, char *b) { if(a == NULL && b == NULL) return 0; if(a == NULL || b == NULL) return -1; return strcmp(a,b); } static int _jid_nullstrcasecmp(char *a, char *b) { if(a == NULL && b == NULL) return 0; if(a == NULL || b == NULL) return -1; return strcasecmp(a,b); } int jid_cmp(jid a, jid b) { return jid_cmpx(a, b, JID_RESOURCE | JID_USER | JID_SERVER); } /* suggested by Anders Qvist */ int jid_cmpx(jid a, jid b, int parts) { if(a == NULL || b == NULL) return -1; if(parts & JID_RESOURCE && _jid_nullstrcmp(a->resource, b->resource) != 0) return -1; if(parts & JID_USER && _jid_nullstrcasecmp(a->user, b->user) != 0) return -1; if(parts & JID_SERVER && _jid_nullstrcmp(a->server, b->server) != 0) return -1; return 0; } jid jid_user(jid a) { jid ret; if(a == NULL || a->resource == NULL) return a; ret = pmalloco(a->p,sizeof(struct jid_struct)); ret->p = a->p; ret->user = a->user; ret->server = a->server; return ret; } jabber-muc-0.8/src/jabberd/xmlnode.c0000664000175000017500000005111011160655601016641 0ustar bencerbencer/* -------------------------------------------------------------------------- * * License * * The contents of this file are subject to the Jabber Open Source License * Version 1.0 (the "JOSL"). You may not copy or use this file, in either * source code or executable form, except in compliance with the JOSL. You * may obtain a copy of the JOSL at http://www.jabber.org/ or at * http://www.opensource.org/. * * Software distributed under the JOSL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL * for the specific language governing rights and limitations under the * JOSL. * * Copyrights * * Portions created by or assigned to Jabber.com, Inc. are * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact * information for Jabber.com, Inc. is available at http://www.jabber.com/. * * Portions Copyright (c) 1998-1999 Jeremie Miller. * * Acknowledgements * * Special thanks to the Jabber Open Source Contributors for their * suggestions and support of Jabber. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 or later (the "GPL"), in which case * the provisions of the GPL are applicable instead of those above. If you * wish to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the JOSL, * indicate your decision by deleting the provisions above and replace them * with the notice and other provisions required by the GPL. If you do not * delete the provisions above, a recipient may use your version of this file * under either the JOSL or the GPL. * * * --------------------------------------------------------------------------*/ #include "lib.h" /* Internal routines */ static xmlnode _xmlnode_new(pool p, const char* name, unsigned int type) { xmlnode result = NULL; if (type > NTYPE_LAST) return NULL; if (type != NTYPE_CDATA && name == NULL) return NULL; if (p == NULL) { p = pool_heap(1*1024); } /* Allocate & zero memory */ result = (xmlnode)pmalloco(p, sizeof(_xmlnode)); /* Initialize fields */ if (type != NTYPE_CDATA) result->name = pstrdup(p,name); result->type = type; result->p = p; return result; } static xmlnode _xmlnode_append_sibling(xmlnode lastsibling, const char* name, unsigned int type) { xmlnode result; result = _xmlnode_new(xmlnode_pool(lastsibling), name, type); if (result != NULL) { /* Setup sibling pointers */ result->prev = lastsibling; lastsibling->next = result; } return result; } static xmlnode _xmlnode_insert(xmlnode parent, const char* name, unsigned int type) { xmlnode result; if(parent == NULL || (type != NTYPE_CDATA && name == NULL)) return NULL; /* If parent->firstchild is NULL, simply create a new node for the first child */ if (parent->firstchild == NULL) { result = _xmlnode_new(parent->p, name, type); parent->firstchild = result; } /* Otherwise, append this to the lastchild */ else { result= _xmlnode_append_sibling(parent->lastchild, name, type); } result->parent = parent; parent->lastchild = result; return result; } static xmlnode _xmlnode_search(xmlnode firstsibling, const char* name, unsigned int type) { xmlnode current; /* Walk the sibling list, looking for a NTYPE_TAG xmlnode with the specified name */ current = firstsibling; while (current != NULL) { if ((current->type == type) && (j_strcmp(current->name, name) == 0)) return current; else current = current->next; } return NULL; } static void _xmlnode_merge(xmlnode data) { xmlnode cur; char *merge, *scur; int imerge; /* get total size of all merged cdata */ imerge = 0; for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next) imerge += cur->data_sz; /* copy in current data and then spin through all of them and merge */ scur = merge = pmalloc(data->p,imerge + 1); for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next) { memcpy(scur,cur->data,cur->data_sz); scur += cur->data_sz; } *scur = '\0'; /* this effectively hides all of the merged-in chunks */ data->next = cur; if(cur == NULL) data->parent->lastchild = data; else cur->prev = data; /* reset data */ data->data = merge; data->data_sz = imerge; } static void _xmlnode_hide_sibling(xmlnode child) { if(child == NULL) return; if(child->prev != NULL) child->prev->next = child->next; if(child->next != NULL) child->next->prev = child->prev; } static void _xmlnode_tag2str(spool s, xmlnode node, int flag) { xmlnode tmp; if(flag==0 || flag==1) { spooler(s,"<",xmlnode_get_name(node),s); tmp = xmlnode_get_firstattrib(node); while(tmp) { spooler(s," ",xmlnode_get_name(tmp),"='",strescape(xmlnode_pool(node),xmlnode_get_data(tmp)),"'",s); tmp = xmlnode_get_nextsibling(tmp); } if(flag==0) spool_add(s,"/>"); else spool_add(s,">"); } else { spooler(s,"",s); } } static spool _xmlnode2spool(xmlnode node) { spool s; int level=0,dir=0; xmlnode tmp; if(!node || xmlnode_get_type(node)!=NTYPE_TAG) return NULL; s = spool_new(xmlnode_pool(node)); if(!s) return(NULL); while(1) { if(dir==0) { if(xmlnode_get_type(node) == NTYPE_TAG) { if(xmlnode_has_children(node)) { _xmlnode_tag2str(s,node,1); node = xmlnode_get_firstchild(node); level++; continue; }else{ _xmlnode_tag2str(s,node,0); } }else{ spool_add(s,strescape(xmlnode_pool(node),xmlnode_get_data(node))); } } tmp = xmlnode_get_nextsibling(node); if(!tmp) { node = xmlnode_get_parent(node); level--; if(level>=0) _xmlnode_tag2str(s,node,2); if(level<1) break; dir = 1; }else{ node = tmp; dir = 0; } } return s; } /* External routines */ /* * xmlnode_new_tag -- create a tag node * Automatically creates a memory pool for the node. * * parameters * name -- name of the tag * * returns * a pointer to the tag node * or NULL if it was unsuccessfull */ xmlnode xmlnode_new_tag(const char* name) { return _xmlnode_new(NULL, name, NTYPE_TAG); } /* * xmlnode_new_tag_pool -- create a tag node within given pool * * parameters * p -- previously created memory pool * name -- name of the tag * * returns * a pointer to the tag node * or NULL if it was unsuccessfull */ xmlnode xmlnode_new_tag_pool(pool p, const char* name) { return _xmlnode_new(p, name, NTYPE_TAG); } /* * xmlnode_insert_tag -- append a child tag to a tag * * parameters * parent -- pointer to the parent tag * name -- name of the child tag * * returns * a pointer to the child tag node * or NULL if it was unsuccessfull */ xmlnode xmlnode_insert_tag(xmlnode parent, const char* name) { return _xmlnode_insert(parent, name, NTYPE_TAG); } /* * xmlnode_insert_cdata -- append character data to a tag * * parameters * parent -- parent tag * CDATA -- character data * size -- size of CDATA * or -1 for null-terminated CDATA strings * * returns * a pointer to the child CDATA node * or NULL if it was unsuccessfull */ xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size) { xmlnode result; if(CDATA == NULL || parent == NULL) return NULL; if(size == -1) size = strlen(CDATA); result = _xmlnode_insert(parent, NULL, NTYPE_CDATA); if (result != NULL) { result->data = (char*)pmalloc(result->p, size + 1); memcpy(result->data, CDATA, size); result->data[size] = '\0'; result->data_sz = size; } return result; } /* * xmlnode_get_tag -- find given tag in an xmlnode tree * * parameters * parent -- pointer to the parent tag * name -- "name" for the child tag of that name * "name/name" for a sub child (recurses) * "?attrib" to match the first tag with that attrib defined * "?attrib=value" to match the first tag with that attrib and value * "=cdata" to match the cdata contents of the child * or any combination: "name/name/?attrib", "name=cdata", etc * * results * a pointer to the tag matching search criteria * or NULL if search was unsuccessfull */ xmlnode xmlnode_get_tag(xmlnode parent, const char* name) { char *str, *slash, *qmark, *equals; xmlnode step, ret; if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL; if(strstr(name, "/") == NULL && strstr(name,"?") == NULL && strstr(name, "=") == NULL) return _xmlnode_search(parent->firstchild, name, NTYPE_TAG); str = strdup(name); slash = strstr(str, "/"); qmark = strstr(str, "?"); equals = strstr(str, "="); if(equals != NULL && (slash == NULL || equals < slash) && (qmark == NULL || equals < qmark)) { /* of type =cdata */ *equals = '\0'; equals++; for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step)) { if(xmlnode_get_type(step) != NTYPE_TAG) continue; if(*str != '\0') if(j_strcmp(xmlnode_get_name(step),str) != 0) continue; if(j_strcmp(xmlnode_get_data(step),equals) != 0) continue; break; } free(str); return step; } if(qmark != NULL && (slash == NULL || qmark < slash)) { /* of type ?attrib */ *qmark = '\0'; qmark++; if(equals != NULL) { *equals = '\0'; equals++; } for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step)) { if(xmlnode_get_type(step) != NTYPE_TAG) continue; if(*str != '\0') if(j_strcmp(xmlnode_get_name(step),str) != 0) continue; if(xmlnode_get_attrib(step,qmark) == NULL) continue; if(equals != NULL && j_strcmp(xmlnode_get_attrib(step,qmark),equals) != 0) continue; break; } free(str); return step; } *slash = '\0'; ++slash; for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step)) { if(xmlnode_get_type(step) != NTYPE_TAG) continue; if(j_strcmp(xmlnode_get_name(step),str) != 0) continue; ret = xmlnode_get_tag(step, slash); if(ret != NULL) { free(str); return ret; } } free(str); return NULL; } /* return the cdata from any tag */ char *xmlnode_get_tag_data(xmlnode parent, const char *name) { xmlnode tag; tag = xmlnode_get_tag(parent, name); if(tag == NULL) return NULL; return xmlnode_get_data(tag); } void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value) { xmlnode attrib; if(owner == NULL || name == NULL || value == NULL) return; /* If there are no existing attributs, allocate a new one to start the list */ if (owner->firstattrib == NULL) { attrib = _xmlnode_new(owner->p, name, NTYPE_ATTRIB); owner->firstattrib = attrib; owner->lastattrib = attrib; } else { attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); if(attrib == NULL) { attrib = _xmlnode_append_sibling(owner->lastattrib, name, NTYPE_ATTRIB); owner->lastattrib = attrib; } } /* Update the value of the attribute */ attrib->data_sz = strlen(value); attrib->data = pstrdup(owner->p, value); } char* xmlnode_get_attrib(xmlnode owner, const char* name) { xmlnode attrib; if (owner != NULL && owner->firstattrib != NULL) { attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); if (attrib != NULL) return (char*)attrib->data; } return NULL; } void xmlnode_put_vattrib(xmlnode owner, const char* name, void *value) { xmlnode attrib; if (owner != NULL) { attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); if (attrib == NULL) { xmlnode_put_attrib(owner, name, ""); attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); } if (attrib != NULL) attrib->firstchild = (xmlnode)value; } } void* xmlnode_get_vattrib(xmlnode owner, const char* name) { xmlnode attrib; if (owner != NULL && owner->firstattrib != NULL) { attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); if (attrib != NULL) return (void*)attrib->firstchild; } return NULL; } xmlnode xmlnode_get_firstattrib(xmlnode parent) { if (parent != NULL) return parent->firstattrib; return NULL; } xmlnode xmlnode_get_firstchild(xmlnode parent) { if (parent != NULL) return parent->firstchild; return NULL; } xmlnode xmlnode_get_lastchild(xmlnode parent) { if (parent != NULL) return parent->lastchild; return NULL; } xmlnode xmlnode_get_nextsibling(xmlnode sibling) { if (sibling != NULL) return sibling->next; return NULL; } xmlnode xmlnode_get_prevsibling(xmlnode sibling) { if (sibling != NULL) return sibling->prev; return NULL; } xmlnode xmlnode_get_parent(xmlnode node) { if (node != NULL) return node->parent; return NULL; } char* xmlnode_get_name(xmlnode node) { if (node != NULL) return node->name; return NULL; } char* xmlnode_get_data(xmlnode node) { if(xmlnode_get_type(node) == NTYPE_TAG) /* loop till we find a CDATA in the children */ for(node = xmlnode_get_firstchild(node); node != NULL; node = xmlnode_get_nextsibling(node)) if(xmlnode_get_type(node) == NTYPE_CDATA) break; if(node == NULL) return NULL; /* check for a dirty node w/ unassembled cdata chunks */ if(xmlnode_get_type(node->next) == NTYPE_CDATA) _xmlnode_merge(node); return node->data; } int xmlnode_get_datasz(xmlnode node) { if(xmlnode_get_type(node) != NTYPE_CDATA) return 0; /* check for a dirty node w/ unassembled cdata chunks */ if(xmlnode_get_type(node->next) == NTYPE_CDATA) _xmlnode_merge(node); return node->data_sz; } int xmlnode_get_type(xmlnode node) { if (node != NULL) return node->type; return NTYPE_UNDEF; } int xmlnode_has_children(xmlnode node) { if ((node != NULL) && (node->firstchild != NULL)) return 1; return 0; } int xmlnode_has_attribs(xmlnode node) { if ((node != NULL) && (node->firstattrib != NULL)) return 1; return 0; } pool xmlnode_pool(xmlnode node) { if (node != NULL) return node->p; return (pool)NULL; } void xmlnode_hide(xmlnode child) { xmlnode parent; if(child == NULL || child->parent == NULL) return; parent = child->parent; /* first fix up at the child level */ _xmlnode_hide_sibling(child); /* next fix up at the parent level */ if(parent->firstchild == child) parent->firstchild = child->next; if(parent->lastchild == child) parent->lastchild = child->prev; } void xmlnode_hide_attrib(xmlnode parent, const char *name) { xmlnode attrib; if(parent == NULL || parent->firstattrib == NULL || name == NULL) return; attrib = _xmlnode_search(parent->firstattrib, name, NTYPE_ATTRIB); if(attrib == NULL) return; /* first fix up at the child level */ _xmlnode_hide_sibling(attrib); /* next fix up at the parent level */ if(parent->firstattrib == attrib) parent->firstattrib = attrib->next; if(parent->lastattrib == attrib) parent->lastattrib = attrib->prev; } /* * xmlnode2str -- convert given xmlnode tree into a string * * parameters * node -- pointer to the xmlnode structure * * results * a pointer to the created string * or NULL if it was unsuccessfull */ char *xmlnode2str(xmlnode node) { return spool_print(_xmlnode2spool(node)); } /* * xmlnode2tstr -- convert given xmlnode tree into a newline terminated string * * parameters * node -- pointer to the xmlnode structure * * results * a pointer to the created string * or NULL if it was unsuccessfull */ char* xmlnode2tstr(xmlnode node) { spool s = _xmlnode2spool(node); if (s != NULL) spool_add(s, "\n"); return spool_print(s); } /* loop through both a and b comparing everything, attribs, cdata, children, etc */ int xmlnode_cmp(xmlnode a, xmlnode b) { int ret = 0; while(1) { if(a == NULL && b == NULL) return 0; if(a == NULL || b == NULL) return -1; if(xmlnode_get_type(a) != xmlnode_get_type(b)) return -1; switch(xmlnode_get_type(a)) { case NTYPE_ATTRIB: ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b)); if(ret != 0) return -1; ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b)); if(ret != 0) return -1; break; case NTYPE_TAG: ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b)); if(ret != 0) return -1; ret = xmlnode_cmp(xmlnode_get_firstattrib(a), xmlnode_get_firstattrib(b)); if(ret != 0) return -1; ret = xmlnode_cmp(xmlnode_get_firstchild(a), xmlnode_get_firstchild(b)); if(ret != 0) return -1; break; case NTYPE_CDATA: ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b)); if(ret != 0) return -1; } a = xmlnode_get_nextsibling(a); b = xmlnode_get_nextsibling(b); } } xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node) { xmlnode child; child = xmlnode_insert_tag(parent, xmlnode_get_name(node)); if (xmlnode_has_attribs(node)) xmlnode_insert_node(child, xmlnode_get_firstattrib(node)); if (xmlnode_has_children(node)) xmlnode_insert_node(child, xmlnode_get_firstchild(node)); return child; } /* places copy of node and node's siblings in parent */ void xmlnode_insert_node(xmlnode parent, xmlnode node) { if(node == NULL || parent == NULL) return; while(node != NULL) { switch(xmlnode_get_type(node)) { case NTYPE_ATTRIB: xmlnode_put_attrib(parent, xmlnode_get_name(node), xmlnode_get_data(node)); break; case NTYPE_TAG: xmlnode_insert_tag_node(parent, node); break; case NTYPE_CDATA: xmlnode_insert_cdata(parent, xmlnode_get_data(node), xmlnode_get_datasz(node)); } node = xmlnode_get_nextsibling(node); } } /* produce full duplicate of x with a new pool, x must be a tag! */ xmlnode xmlnode_dup(xmlnode x) { xmlnode x2; if(x == NULL) return NULL; x2 = xmlnode_new_tag(xmlnode_get_name(x)); if (xmlnode_has_attribs(x)) xmlnode_insert_node(x2, xmlnode_get_firstattrib(x)); if (xmlnode_has_children(x)) xmlnode_insert_node(x2, xmlnode_get_firstchild(x)); return x2; } xmlnode xmlnode_dup_pool(pool p, xmlnode x) { xmlnode x2; if(x == NULL) return NULL; x2 = xmlnode_new_tag_pool(p, xmlnode_get_name(x)); if (xmlnode_has_attribs(x)) xmlnode_insert_node(x2, xmlnode_get_firstattrib(x)); if (xmlnode_has_children(x)) xmlnode_insert_node(x2, xmlnode_get_firstchild(x)); return x2; } xmlnode xmlnode_wrap(xmlnode x,const char *wrapper) { xmlnode wrap; if(x==NULL||wrapper==NULL) return NULL; wrap=xmlnode_new_tag_pool(xmlnode_pool(x),wrapper); if(wrap==NULL) return NULL; wrap->firstchild=x; wrap->lastchild=x; x->parent=wrap; return wrap; } void xmlnode_free(xmlnode node) { if(node == NULL) return; pool_free(node->p); } jabber-muc-0.8/src/jabberd/jutil.c0000664000175000017500000002160611160655601016331 0ustar bencerbencer/* -------------------------------------------------------------------------- * * License * * The contents of this file are subject to the Jabber Open Source License * Version 1.0 (the "JOSL"). You may not copy or use this file, in either * source code or executable form, except in compliance with the JOSL. You * may obtain a copy of the JOSL at http://www.jabber.org/ or at * http://www.opensource.org/. * * Software distributed under the JOSL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL * for the specific language governing rights and limitations under the * JOSL. * * Copyrights * * Portions created by or assigned to Jabber.com, Inc. are * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact * information for Jabber.com, Inc. is available at http://www.jabber.com/. * * Portions Copyright (c) 1998-1999 Jeremie Miller. * * Acknowledgements * * Special thanks to the Jabber Open Source Contributors for their * suggestions and support of Jabber. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 or later (the "GPL"), in which case * the provisions of the GPL are applicable instead of those above. If you * wish to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the JOSL, * indicate your decision by deleting the provisions above and replace them * with the notice and other provisions required by the GPL. If you do not * delete the provisions above, a recipient may use your version of this file * under either the JOSL or the GPL. * * * --------------------------------------------------------------------------*/ #include "lib.h" /* util for making presence packets */ xmlnode jutil_presnew(int type, char *to, char *status) { xmlnode pres; pres = xmlnode_new_tag("presence"); switch(type) { case JPACKET__SUBSCRIBE: xmlnode_put_attrib(pres,"type","subscribe"); break; case JPACKET__UNSUBSCRIBE: xmlnode_put_attrib(pres,"type","unsubscribe"); break; case JPACKET__SUBSCRIBED: xmlnode_put_attrib(pres,"type","subscribed"); break; case JPACKET__UNSUBSCRIBED: xmlnode_put_attrib(pres,"type","unsubscribed"); break; case JPACKET__PROBE: xmlnode_put_attrib(pres,"type","probe"); break; case JPACKET__UNAVAILABLE: xmlnode_put_attrib(pres,"type","unavailable"); break; case JPACKET__INVISIBLE: xmlnode_put_attrib(pres,"type","invisible"); break; } if(to != NULL) xmlnode_put_attrib(pres,"to",to); if(status != NULL) xmlnode_insert_cdata(xmlnode_insert_tag(pres,"status"),status,strlen(status)); return pres; } /* util for making IQ packets */ xmlnode jutil_iqnew(int type, char *ns) { xmlnode iq; iq = xmlnode_new_tag("iq"); switch(type) { case JPACKET__GET: xmlnode_put_attrib(iq,"type","get"); break; case JPACKET__SET: xmlnode_put_attrib(iq,"type","set"); break; case JPACKET__RESULT: xmlnode_put_attrib(iq,"type","result"); break; case JPACKET__ERROR: xmlnode_put_attrib(iq,"type","error"); break; } xmlnode_put_attrib(xmlnode_insert_tag(iq,"query"),"xmlns",ns); return iq; } /* util for making message packets */ xmlnode jutil_msgnew(char *type, char *to, char *subj, char *body) { xmlnode msg; msg = xmlnode_new_tag("message"); xmlnode_put_attrib (msg, "type", type); xmlnode_put_attrib (msg, "to", to); if (subj) { xmlnode_insert_cdata (xmlnode_insert_tag (msg, "subject"), subj, strlen (subj)); } if (body) { xmlnode_insert_cdata (xmlnode_insert_tag (msg, "body"), body, strlen (body)); } return msg; } /* util for making stream packets */ xmlnode jutil_header(char* xmlns, char* server) { xmlnode result; if ((xmlns == NULL)||(server == NULL)) return NULL; result = xmlnode_new_tag("stream:stream"); xmlnode_put_attrib(result, "xmlns:stream", "http://etherx.jabber.org/streams"); xmlnode_put_attrib(result, "xmlns", xmlns); xmlnode_put_attrib(result, "to", server); return result; } /* returns the priority on a presence packet */ int jutil_priority(xmlnode x) { char *str; int p; if(x == NULL) return -1; if(xmlnode_get_attrib(x,"type") != NULL) return -1; x = xmlnode_get_tag(x,"priority"); if(x == NULL) return 0; str = xmlnode_get_data((x)); if(str == NULL) return 0; p = atoi(str); if(p >= 0) return p; else return 0; } void jutil_tofrom(xmlnode x) { char *to, *from; to = xmlnode_get_attrib(x,"to"); from = xmlnode_get_attrib(x,"from"); xmlnode_put_attrib(x,"from",to); xmlnode_put_attrib(x,"to",from); } xmlnode jutil_iqresult(xmlnode x) { xmlnode cur; jutil_tofrom(x); xmlnode_put_attrib(x,"type","result"); /* hide all children of the iq, they go back empty */ for(cur = xmlnode_get_firstchild(x); cur != NULL; cur = xmlnode_get_nextsibling(cur)) xmlnode_hide(cur); return x; } //return the timestamp according to XEP-0203 char *jutil_timestamp(void) { time_t t; struct tm *new_time; static char timestamp[21]; int ret; t = time(NULL); if(t == (time_t)-1) return NULL; new_time = gmtime(&t); ret = snprintf(timestamp, 21, "%d-%02d-%02dT%02d:%02d:%02dZ", 1900+new_time->tm_year, new_time->tm_mon+1, new_time->tm_mday, new_time->tm_hour, new_time->tm_min, new_time->tm_sec); if(ret == -1) return NULL; return timestamp; } //return timestamp according to XEP-0091, this method will be deleted in the future char *jutil_timestamp_0091(void) { time_t t; struct tm *new_time; static char timestamp[18]; int ret; t = time(NULL); if(t == (time_t)-1) return NULL; new_time = gmtime(&t); ret = snprintf(timestamp, 18, "%d%02d%02dT%02d:%02d:%02d", 1900+new_time->tm_year, new_time->tm_mon+1, new_time->tm_mday, new_time->tm_hour, new_time->tm_min, new_time->tm_sec); if(ret == -1) return NULL; return timestamp; } void jutil_error(xmlnode x, terror E) { xmlnode err, condition, text; char code[4]; xmlnode_put_attrib(x,"type","error"); err = xmlnode_insert_tag(x,"error"); xmlnode_put_attrib(err,"type",E.type); snprintf(code,4,"%d",E.code); xmlnode_put_attrib(err,"code",code); condition = xmlnode_insert_tag(err, E.condition); xmlnode_put_attrib(condition, "xmlns", NS_STANZA); if(E.msg != NULL) { text = xmlnode_insert_tag(err, "text"); xmlnode_put_attrib(text, "xmlns", NS_STANZA); xmlnode_put_attrib(text, "xml:lang", "en"); xmlnode_insert_cdata(text,E.msg,strlen(E.msg)); } jutil_tofrom(x); } //add delay timestamp to a stanza void jutil_delay(xmlnode msg, char *reason) { xmlnode delay; //tag using XEP-0091 method (should be removed when this XEP become obsolete delay = xmlnode_insert_tag(msg,"x"); xmlnode_put_attrib(delay,"xmlns",NS_DELAY_0091); xmlnode_put_attrib(delay,"from",xmlnode_get_attrib(msg,"to")); xmlnode_put_attrib(delay,"stamp",jutil_timestamp_0091()); if(reason != NULL) xmlnode_insert_cdata(delay,reason,strlen(reason)); //tag using new XEP-203 method delay = xmlnode_insert_tag(msg,"delay"); xmlnode_put_attrib(delay,"xmlns",NS_DELAY); xmlnode_put_attrib(delay,"from",xmlnode_get_attrib(msg,"to")); xmlnode_put_attrib(delay,"stamp", jutil_timestamp()); if(reason != NULL) xmlnode_insert_cdata(delay,reason,strlen(reason)); } #define KEYBUF 100 char *jutil_regkey(char *key, char *seed) { static char keydb[KEYBUF][41]; static char seeddb[KEYBUF][41]; static int last = -1; char *str, strint[32]; int i; /* blanket the keydb first time */ if(last == -1) { last = 0; memset(&keydb,0,KEYBUF*41); memset(&seeddb,0,KEYBUF*41); srand(time(NULL)); } /* creation phase */ if(key == NULL && seed != NULL) { /* create a random key hash and store it */ sprintf(strint,"%d",rand()); strcpy(keydb[last],shahash(strint)); /* store a hash for the seed associated w/ this key */ strcpy(seeddb[last],shahash(seed)); /* return it all */ str = keydb[last]; last++; if(last == KEYBUF) last = 0; return str; } /* validation phase */ str = shahash(seed); for(i=0;ip = p; s->len = 0; s->last = NULL; s->first = NULL; return s; } void spool_add(spool s, char *str) { struct spool_node *sn; int len; if(str == NULL) return; len = strlen(str); if(len == 0) return; sn = pmalloc(s->p, sizeof(struct spool_node)); sn->c = pstrdup(s->p, str); sn->next = NULL; s->len += len; if(s->last != NULL) s->last->next = sn; s->last = sn; if(s->first == NULL) s->first = sn; } void spooler(spool s, ...) { va_list ap; char *arg = NULL; if(s == NULL) return; va_start(ap, s); /* loop till we hit our end flag, the first arg */ while(1) { arg = va_arg(ap,char *); if((spool)arg == s) break; else spool_add(s, arg); } va_end(ap); } char *spool_print(spool s) { char *ret,*tmp; struct spool_node *next; if(s == NULL || s->len == 0 || s->first == NULL) return NULL; ret = pmalloc(s->p, s->len + 1); *ret = '\0'; next = s->first; tmp = ret; while(next != NULL) { tmp = j_strcat(tmp,next->c); next = next->next; } return ret; } /* convenience :) */ char *spools(pool p, ...) { va_list ap; spool s; char *arg = NULL; if(p == NULL) return NULL; s = spool_new(p); va_start(ap, p); /* loop till we hit our end flag, the first arg */ while(1) { arg = va_arg(ap,char *); if((pool)arg == p) break; else spool_add(s, arg); } va_end(ap); return spool_print(s); } char *strescape(pool p, char *buf) { int i,j,oldlen,newlen; char *temp; if (p == NULL || buf == NULL) return(NULL); oldlen = newlen = strlen(buf); for(i=0;i': newlen+=4; break; } } if(oldlen == newlen) return buf; temp = pmalloc(p,newlen+1); if (temp==NULL) return(NULL); for(i=j=0;i': memcpy(&temp[j],">",4); j += 4; break; default: temp[j++] = buf[i]; } } temp[j] = '\0'; return temp; } char *zonestr(char *file, int line) { static char buff[64]; int i; i = snprintf(buff,63,"%s:%d",file,line); buff[i] = '\0'; return buff; } jabber-muc-0.8/src/jabberd/pool.c0000664000175000017500000000174611160655601016156 0ustar bencerbencer #include pool _pool_new(char *zone) { pool p; p = (pool)g_malloc0(sizeof(_pool)); p->lock = g_mutex_new(); p->pll = NULL; return p; } void *pmalloc(pool p, int size) { void *r; if (p == NULL) return NULL; if (size <= 0) return NULL; g_mutex_lock(p->lock); r = (void *)g_malloc0(size); p->pll = g_slist_prepend(p->pll, r); g_mutex_unlock(p->lock); return r; } void *pmalloco(pool p, int size) { return pmalloc(p, size); } char *pstrdup(pool p, const char *src) { char *dest; if (src == NULL) return NULL; dest = pmalloc(p, (strlen(src) + 1)); memcpy(dest, src, strlen(src)); return dest; } static void jcr_internal_pool_free(gpointer data, gpointer user_data) { g_free(data); } void pool_free(pool p) { if (p == NULL) return; g_mutex_lock(p->lock); g_slist_foreach(p->pll, (void *)jcr_internal_pool_free, NULL); g_slist_free(p->pll); g_mutex_unlock(p->lock); g_mutex_free(p->lock); g_free(p); } jabber-muc-0.8/src/jabberd/sha.c0000664000175000017500000001157011160655601015754 0ustar bencerbencer/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is SHA 180-1 Reference Implementation (Compact version) * * The Initial Developer of the Original Code is Paul Kocher of * Cryptography Research. Portions created by Paul Kocher are * Copyright (C) 1995-9 by Cryptography Research, Inc. All * Rights Reserved. * * Contributor(s): * */ #include "lib.h" static void shaHashBlock(j_SHA_CTX *ctx); void shaInit(j_SHA_CTX *ctx) { int i; ctx->lenW = 0; ctx->sizeHi = ctx->sizeLo = 0; /* Initialize H with the magic constants (see FIPS180 for constants) */ ctx->H[0] = 0x67452301L; ctx->H[1] = 0xefcdab89L; ctx->H[2] = 0x98badcfeL; ctx->H[3] = 0x10325476L; ctx->H[4] = 0xc3d2e1f0L; for (i = 0; i < 80; i++) ctx->W[i] = 0; } void shaUpdate(j_SHA_CTX *ctx, unsigned char *dataIn, int len) { int i; /* Read the data into W and process blocks as they get full */ for (i = 0; i < len; i++) { ctx->W[ctx->lenW / 4] <<= 8; ctx->W[ctx->lenW / 4] |= (unsigned long)dataIn[i]; if ((++ctx->lenW) % 64 == 0) { shaHashBlock(ctx); ctx->lenW = 0; } ctx->sizeLo += 8; ctx->sizeHi += (ctx->sizeLo < 8); } } void shaFinal(j_SHA_CTX *ctx, unsigned char hashout[20]) { unsigned char pad0x80 = 0x80; unsigned char pad0x00 = 0x00; unsigned char padlen[8]; int i; /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length */ padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255); padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255); padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255); padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255); padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255); padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255); padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255); padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255); shaUpdate(ctx, &pad0x80, 1); while (ctx->lenW != 56) shaUpdate(ctx, &pad0x00, 1); shaUpdate(ctx, padlen, 8); /* Output hash */ for (i = 0; i < 20; i++) { hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24); ctx->H[i / 4] <<= 8; } /* * Re-initialize the context (also zeroizes contents) */ shaInit(ctx); } void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]) { j_SHA_CTX ctx; shaInit(&ctx); shaUpdate(&ctx, dataIn, len); shaFinal(&ctx, hashout); } #define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL) static void shaHashBlock(j_SHA_CTX *ctx) { int t; unsigned long A,B,C,D,E,TEMP; for (t = 16; t <= 79; t++) ctx->W[t] = SHA_ROTL(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1); A = ctx->H[0]; B = ctx->H[1]; C = ctx->H[2]; D = ctx->H[3]; E = ctx->H[4]; for (t = 0; t <= 19; t++) { TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D) + E + ctx->W[t] + 0x5a827999L) & 0xffffffffL; E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; } for (t = 20; t <= 39; t++) { TEMP = (SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0x6ed9eba1L) & 0xffffffffL; E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; } for (t = 40; t <= 59; t++) { TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdcL) & 0xffffffffL; E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; } for (t = 60; t <= 79; t++) { TEMP = (SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0xca62c1d6L) & 0xffffffffL; E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; } ctx->H[0] += A; ctx->H[1] += B; ctx->H[2] += C; ctx->H[3] += D; ctx->H[4] += E; } /*---------------------------------------------------------------------------- * * This code added by Thomas "temas" Muldowney for Jabber compatability * *---------------------------------------------------------------------------*/ char *shahash(char *str) { static char final[41]; char *pos; unsigned char hashval[20]; int x; if(!str || !*str) return NULL; shaBlock((unsigned char *)str, strlen(str), hashval); pos = final; for(x=0;x<20;x++) { snprintf(pos, 3, "%02x", hashval[x]); pos += 2; } return (char *)final; } void shahash_r(const char* str, char hashbuf[41]) { int x; char *pos; unsigned char hashval[20]; if(!str || !*str) return; shaBlock((unsigned char *)str, strlen(str), hashval); pos = hashbuf; for(x=0;x<20;x++) { snprintf(pos, 3, "%02x", hashval[x]); pos += 2; } return; } jabber-muc-0.8/src/jabberd/Makefile0000664000175000017500000000056711160655601016501 0ustar bencerbencer CC=gcc CFLAGS:=$(CFLAGS) -O2 -Wall -I. -I../../include `pkg-config --cflags glib-2.0` -D_REENTRANT -DLIBIDN LIBS= JCOMP_LIB_OBJECTS=expat.o \ jid.o \ jpacket.o \ jutil.o \ pool.o \ sha.o \ snprintf.o \ str.o \ xmlnode.o all: $(JCOMP_LIB_OBJECTS) ar rv ../libjcomp.a $(JCOMP_LIB_OBJECTS) ranlib ../libjcomp.a clean: rm -f $(JCOMP_LIB_OBJECTS) ../libjcomp.a jabber-muc-0.8/src/utils.c0000664000175000017500000004633411160655601014756 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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" /* Generate extended presence entry */ xmlnode add_extended_presence(cnu from, cnu to, xmlnode presence, char *status, char *reason, char *actor) { xmlnode tag; xmlnode element; xmlnode item; xmlnode output; taffil useraffil; trole userrole; jid user; cnr room; if(presence == NULL) { output = xmlnode_dup(from->presence); } else { output = xmlnode_dup(presence); } if(from == NULL) { log_warn(NAME, "[%s] ERR: Missing user variable in add_extended_presence", FZONE); return output; } user = from->realid; room = from->room; tag = xmlnode_insert_tag(output,"x"); xmlnode_put_attrib(tag, "xmlns", NS_MUC_USER); item = xmlnode_insert_tag(tag, "item"); if(room->visible == 1 || is_admin(room, to->realid)) { xmlnode_put_attrib(item, "jid", jid_full(user)); } useraffil = affiliation_level(room, user); xmlnode_put_attrib(item, "affiliation", useraffil.msg); userrole = role_level(room, user); xmlnode_put_attrib(item, "role", userrole.msg); log_debug(NAME, "[%s] status check: status >%s<", FZONE, status); /* If this is a nick change, include the new nick if available */ if(j_strcmp(status, STATUS_MUC_CREATED) == 0) { room->locked = 1; } if(status != NULL) { log_debug(NAME, "[%s] Adding to epp: status >%s<, reason >%s<", FZONE, status, reason); /* If this is a nick change, include the new nick if available */ if(j_strcmp(status, STATUS_MUC_NICKCHANGE) == 0) if(from->localid->resource != NULL) xmlnode_put_attrib(item, "nick", from->localid->resource); /* Add reason if available */ if(reason != NULL) { element = xmlnode_insert_tag(item, "reason"); xmlnode_insert_cdata(element, reason, -1); } /* Add actor if available */ if(actor != NULL) { element = xmlnode_insert_tag(item, "actor"); xmlnode_put_attrib(element, "jid", actor); } /* Add status code if available */ element = xmlnode_insert_tag(tag, "status"); xmlnode_put_attrib(element,"code", status); } return output; } /* add another status code to an existing extended presence */ void add_status_code(xmlnode presence, char *status) { xmlnode tag, element; char var[100]; snprintf(var, 100, "x?xmlns=%s", NS_MUC_USER); tag = xmlnode_get_tag(presence,var); if (tag == NULL) { tag = xmlnode_insert_tag(presence,"x"); xmlnode_put_attrib(tag, "xmlns", NS_MUC_USER); } element = xmlnode_insert_tag(tag, "status"); xmlnode_put_attrib(element,"code", status); } /* Add all of the room's status codes (logging, anonymous, ...) */ void add_room_status_codes(xmlnode presence, cnr room) { if (room->visible == 1) { add_status_code(presence,STATUS_MUC_SHOWN_JID); } if (room->logfile != NULL) { add_status_code(presence,STATUS_MUC_LOGGING_ON); } } /* Is the user a Service Admin? */ int is_sadmin(cni master, jid user) { char ujid[2048]; if(master == NULL || user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_sadmin", FZONE); return 0; } snprintf(ujid, sizeof(ujid), "%s@%s", user->user, user->server); log_debug(NAME, "[%s] Is sadmin? >%s/%s<", FZONE, jid_full(user), ujid); if(g_hash_table_lookup(master->sadmin, ujid) != NULL ) return 1; else return 0; } /* Is the user an owner for that room */ int is_owner(cnr room, jid user) { char ujid[2048]; char cjid[2048]; if(room == NULL || user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_owner", FZONE); return 0; } snprintf(ujid, sizeof(ujid), "%s@%s", user->user, user->server); if(room->creator) { snprintf(cjid, sizeof(cjid), "%s@%s", room->creator->user, room->creator->server); } else { snprintf(cjid, sizeof(cjid), "@"); } log_debug(NAME, "[%s] Is Owner? >%s<", FZONE, jid_full(user)); /* Server admin can override */ if(is_sadmin(room->master, user)) return 2; else if(j_strcmp(cjid, ujid) == 0) return 1; else if(g_hash_table_lookup(room->owner, ujid) != NULL ) return 1; else return 0; } /* Is the user in the admin affiliation list for that room */ int is_admin(cnr room, jid user) { char ujid[2048]; if(room == NULL || user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_admin", FZONE); return 0; } snprintf(ujid, sizeof(ujid), "%s@%s", user->user, user->server); log_debug(NAME, "[%s] Is Admin? >%s<", FZONE, jid_full(user)); if(is_owner(room, user)) return 2; if(g_hash_table_lookup(room->admin, ujid) != NULL ) return 1; else if(g_hash_table_lookup(room->admin, user->server) != NULL ) return 1; else return 0; } /* Is the user in the moderator role list for that room */ int is_moderator(cnr room, jid user) { if(room == NULL || user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_moderator", FZONE); return 0; } if(is_owner(room, user)) { log_debug(NAME, "[%s] Is Moderator? >%s< - Owner", FZONE, jid_full(user)); return 2; } if(g_hash_table_lookup(room->moderator, jid_full(user)) != NULL ) { log_debug(NAME, "[%s] Is Moderator? >%s< - Moderator", FZONE, jid_full(user)); return 1; } else { log_debug(NAME, "[%s] Is Moderator? >%s< - No", FZONE, jid_full(user)); return 0; } } /* Is the user in the participant role list for that room */ int is_participant(cnr room, jid user) { if(room == NULL || user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_participant", FZONE); return 0; } /* Every non-admin has voice in a non-moderated room */ if(room->moderated == 0) return 1; /* Moderator has voice intrinsic */ if(is_moderator(room, user)) return 2; /* If moderated, check the voice list */ if(g_hash_table_lookup(room->participant, jid_full(user)) != NULL ) return 1; else return 0; } /* Is the user in the member affiliation list for that room */ int is_member(cnr room, jid user) { char ujid[2048]; if(room == NULL || user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_member", FZONE); return 0; } snprintf(ujid, sizeof(ujid), "%s@%s", user->user, user->server); /* Owner is automatically a member */ if(is_owner(room, user)) { log_debug(NAME, "[%s] Is Member? >%s< - Owner", FZONE, jid_full(user)); return 1; } /* Admin is automatically a member */ if(is_admin(room, user)) { log_debug(NAME, "[%s] Is Member? >%s< - Admin", FZONE, jid_full(user)); return 1; } if(g_hash_table_lookup(room->member, ujid) != NULL ) { log_debug(NAME, "[%s] Is Member? >%s< - Yes (case 1)", FZONE, jid_full(user)); return 1; } else if(g_hash_table_lookup(room->member, user->server) != NULL ) { log_debug(NAME, "[%s] Is Member? >%s< - Yes (case 2)", FZONE, jid_full(user)); return 1; } else { log_debug(NAME, "[%s] Is Member? >%s< - No", FZONE, jid_full(user)); return 0; } } /* Is the user in the outcast affiliation list for that room */ int is_outcast(cnr room, jid user) { char ujid[2048]; if(room == NULL || user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_outcast", FZONE); return 0; } snprintf(ujid, sizeof(ujid), "%s@%s", user->user, user->server); if(g_hash_table_lookup(room->outcast, ujid) != NULL ) return 1; else if(g_hash_table_lookup(room->outcast, user->server) != NULL ) return 1; else return 0; } /* Only return 1 if visitor */ int is_visitor(cnr room, jid user) { if(room == NULL || user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_visitor", FZONE); return 0; } if(is_moderator(room, user)) return 0; else if(is_participant(room, user)) return 0; else if(g_hash_table_lookup(room->remote, jid_full(user)) != NULL ) return 1; else return 0; } /* Is user in the room? */ int in_room(cnr room, jid user) { if(room == NULL || user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in in_room", FZONE); return 0; } if(g_hash_table_lookup(room->remote, jid_full(user)) != NULL ) return 1; else return 0; } /* Is this a legacy client? */ int is_legacy(cnu user) { cnr room; if(user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_legacy", FZONE); return 0; } room = user->room; if(room->legacy) return 1; else if(user->legacy) return 1; else return 0; } /* Is user leaving the room? */ int is_leaving(cnr room, jid user) { cnu target; if(room == NULL || user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_leaving", FZONE); return 0; } target = g_hash_table_lookup(room->remote, jid_full(user)); if(target != NULL ) { if(target->leaving == 1) { return 1; } } return 0; } /* Check if user is already registered */ int is_registered(cni master, char *user, char *nick) { xmlnode results; if(user == NULL || nick == NULL) { log_warn(NAME, "[%s] ERR: Missing variable in is_registered", FZONE); return 0; } results = get_data_bynick(master, nick); if(results != NULL) { log_debug(NAME, "[%s] Found %s in Registered Nicks - checking [%s/%s]", FZONE, nick, user, xmlnode_get_attrib(results, "jid")); if(j_strcmp(user, xmlnode_get_attrib(results, "jid")) != 0) { /* User taken by someone else */ xmlnode_free(results); return -1; } else { /* Nick belongs to me */ xmlnode_free(results); return 1; } } else { /* Nick is free */ xmlnode_free(results); return 0; } } /* Generic alert function for user/room */ xmlnode _con_send_alert(cnu user, char *text, char *subject, const char *status, int toAll) { xmlnode msg; xmlnode element; char body[256]; char reason[128]; char *type = NULL; cnr room; char *room_id; if(user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable", FZONE); return NULL; } room = user->room; room_id = jid_full(room->id); if((is_legacy(user) == 0) && (toAll == 0) ) { return NULL; } if(status == NULL) { snprintf(body, 256, "%s", text); } else { if(text == NULL) strcpy(reason, "None given"); else snprintf(reason, 128, "%s", text); if(j_strcmp(status, STATUS_MUC_KICKED) == 0) { type = "normal"; snprintf(body, 256, "You have been kicked from the room %s. \n Reason: %s", room_id, reason); } if(j_strcmp(status, STATUS_MUC_BANNED) == 0) { type = "normal"; snprintf(body, 256, "You have been kicked and outcast from the room %s. \n Reason: %s", room_id, reason); } if(j_strcmp(status, STATUS_MUC_SHOWN_JID) == 0) { type = "groupchat"; snprintf(body, 256, "This room (%s) is not anonymous", room_id); } if(j_strcmp(status, STATUS_MUC_HIDDEN_JID) == 0) { type = "groupchat"; snprintf(body, 256, "This room (%s) is anonymous, except for admins", room_id); status = STATUS_MUC_SHOWN_JID; } if(j_strcmp(status, STATUS_MUC_NON_ANONYM) == 0) { type = "groupchat"; snprintf(body, 256, "This room (%s) is now not anonymous", room_id); } if(j_strcmp(status, STATUS_MUC_SEMI_ANONYM) == 0) { type = "groupchat"; snprintf(body, 256, "This room (%s) is now anonymous, except for admins", room_id); } if(j_strcmp(status, STATUS_MUC_LOGGING_ON) == 0) { type = "groupchat"; snprintf(body, 256, "This room (%s) is now logged", room_id); } if(j_strcmp(status, STATUS_MUC_LOGGING_OFF) == 0) { type = "groupchat"; snprintf(body, 256, "This room (%s) is no more logged", room_id); } } if((is_legacy(user) == 0) && (toAll)) msg = jutil_msgnew(type, jid_full(user->realid) , NULL, NULL); else msg = jutil_msgnew(type, jid_full(user->realid) , subject, body); xmlnode_put_attrib(msg, "from", room_id); if(status != NULL) { element = xmlnode_insert_tag(msg,"x"); xmlnode_put_attrib(element, "xmlns", NS_MUC_USER); xmlnode_put_attrib(xmlnode_insert_tag(element, "status"), "code", status); } return msg; } /* User alert wrapper */ void con_send_alert(cnu user, char *text, char *subject, const char *status) { xmlnode msg = _con_send_alert(user, text, subject, status,0); if(msg) { deliver(dpacket_new(msg), NULL); } } /* Room status/alert wrapper */ void _con_send_room_status(gpointer key, gpointer data, gpointer arg) { char *status = (char*)arg; cnu user = (cnu)data; xmlnode msg = _con_send_alert(user, NULL, NULL, status,1); if(msg) { deliver(dpacket_new(msg), NULL); } } /* Send status change to a room */ void con_send_room_status(cnr room, char *status) { if(room == NULL) { log_warn(NAME, "[%s] ERR: Missing variable", FZONE); return; } g_hash_table_foreach(room->local, _con_send_room_status, (void*)status); } /* Integer to String conversion */ char *itoa(int number, char *result) { sprintf(result, "%d", number); return result; } /* Custom Debug message */ char *funcstr(const char *file, const char *function, int line) { static char buff[128]; int i; i = snprintf(buff,127,"%s:%d (%s)",file,line,function); buff[i] = '\0'; return buff; } /* Return current date for logfile system */ int minuteget(time_t tin) { time_t timef; char timestr[50]; size_t timelen = 49; int results; if(tin) timef = tin; else timef = time(NULL); strftime(timestr, timelen, "%M", localtime(&timef)); results = j_atoi(timestr, -1); return results; } /* Return current date for logfile system */ char *timeget(time_t tin) { time_t timef; char timestr[50]; size_t timelen = 49; if(tin) timef = tin; else timef = time(NULL); strftime(timestr, timelen, "%H:%M", localtime(&timef)); return j_strdup(timestr); } /* Return current date for logfile system */ char *dateget(time_t tin) { time_t timef; char timestr[50]; size_t timelen = 49; if(tin) timef = tin; else timef = time(NULL); strftime(timestr, timelen, "%Y-%m-%d", localtime(&timef)); return j_strdup(timestr); } /* Send presence update for a user to the room */ void update_presence(cnu user) { xmlnode result; cnr room; if(user == NULL) { log_warn(NAME, "[%s] ERR: Missing variable", FZONE); return; } room = user->room; /* Send updated presence packet */ result = xmlnode_dup(user->presence); xmlnode_put_vattrib(result,"cnu",(void*)user); g_hash_table_foreach(room->local, con_room_sendwalk, (void*)result); xmlnode_free(result); return; } /* Generate custom errors for multi-item handler */ void insert_item_error(xmlnode node, char *code, char *msg) { xmlnode element; element = xmlnode_insert_tag(node, "error"); xmlnode_put_attrib(element, "code", code); xmlnode_insert_cdata(element, msg, -1); } /* Add user into the room roster hash */ int add_roster(cnr room, jid userid) { xmlnode store; xmlnode node; xmlnode old; char ujid[2048]; if(room == NULL || userid == NULL) { log_warn(NAME, "[%s] ERR: Missing variable", FZONE); return -1; } snprintf(ujid, sizeof(ujid), "%s@%s", userid->user, userid->server); old = g_hash_table_lookup(room->roster, ujid); /* User not previously registered. Set up */ if(old == NULL) { store = xmlnode_new_tag("users"); } else { store = xmlnode_dup(old); node = xmlnode_get_tag(store, spools(xmlnode_pool(store), "item?jid=", jid_full(userid), xmlnode_pool(store))); /* If already in the node, ignore */ if(node != NULL) { log_debug(NAME, "[%s] DBG: Already in node, ignoring\n", FZONE); xmlnode_free(store); return 0; } } if(userid->resource != NULL) { log_debug(NAME, "[%s] adding entry (%s) for jid (%s)", FZONE, jid_full(userid), ujid); node = xmlnode_insert_tag(store, "item"); xmlnode_put_attrib(node, "jid", jid_full(userid)); } g_hash_table_insert(room->roster, g_strdup(ujid), store); return 1; } /* Remove a user from the room roster hash */ int remove_roster(cnr room, jid userid) { xmlnode store; xmlnode old; xmlnode node; char ujid[2048]; if(room == NULL || userid == NULL) { log_warn(NAME, "[%s] ERR: Missing variable", FZONE); return -1; } snprintf(ujid, sizeof(ujid), "%s@%s", userid->user, userid->server); old = g_hash_table_lookup(room->roster, ujid); if(old == NULL) return 1; store = xmlnode_dup(old); node = xmlnode_get_tag(store, spools(xmlnode_pool(store), "item?jid=", jid_full(userid), xmlnode_pool(store))); if(node == NULL) { log_debug(NAME, "[%s] DBG: Already removed from node, ignoring\n", FZONE); xmlnode_free(store); return 1; } xmlnode_hide(node); node = xmlnode_get_tag(store, "item"); if(node == NULL) { log_debug(NAME, "[%s] Removing empty entry for jid (%s)", FZONE, ujid); g_hash_table_remove(room->roster, ujid); xmlnode_free(store); } else { log_debug(NAME, "[%s] Removing entry (%s) for jid (%s)", FZONE, jid_full(userid), ujid); g_hash_table_insert(room->roster, j_strdup(ujid), store); } return 1; } /* Get the entries from the room roster hash */ xmlnode get_roster(cnr room, jid userid) { char ujid[2048]; if(room == NULL || userid == NULL) { log_warn(NAME, "[%s] ERR: Missing variable", FZONE); return NULL; } snprintf(ujid, sizeof(ujid), "%s@%s", userid->user, userid->server); return g_hash_table_lookup(room->roster, ujid); } char *extractAction(char *origin, pool p) { int i; int end; spool sp; char *output; char in[2]; if(origin == NULL || p == NULL) { log_warn(NAME, "[%s] ERR: Missing variable", FZONE); return NULL; } sp = spool_new(p); end = j_strlen(origin); for (i = 3 ; i <= end ; i++) { in[0] = origin[i]; in[1] = '\0'; log_debug(NAME, "[%s] >%s< saved", FZONE, in); spooler(sp, in, sp); } output = spool_print(sp); return output; } /* Used to check jids and fix case. */ jid jid_fix(jid id) { char *str; if(id == NULL) { log_warn(NAME, "[%s] ERR - id NULL", FZONE); return NULL; } if(id->server == NULL || *id->server == '\0' || j_strlen(id->server) > 255) return NULL; /* lowercase the hostname, make sure it's valid characters */ for(str = id->server; *str != '\0'; str++) { *str = tolower(*str); } /* cut off the user */ //if(id->user != NULL && j_strlen(id->user) > 64) // id->user[64] = '\0'; /* check for low and invalid ascii characters in the username */ //if(id->user != NULL) // for(str = id->user; *str != '\0'; str++) // { // *str = tolower(*str); // } return id; } jabber-muc-0.8/src/xdata.c0000664000175000017500000005241411160655601014713 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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 "fields.h" static void add_xdata_boolean(xmlnode parent, char *label, char *var, int data) { xmlnode node; char value[4]; snprintf(value, 4, "%i", data); node = xmlnode_insert_tag(parent, "field"); xmlnode_put_attrib(node,"type","boolean"); xmlnode_put_attrib(node,"label", label); xmlnode_put_attrib(node,"var", var); xmlnode_insert_cdata(xmlnode_insert_tag(node,"value"), value, -1); } static void add_xdata_text(xmlnode parent, char *label, int type, char *var, char *data) { xmlnode node; node = xmlnode_insert_tag(parent, "field"); if(type > 1) xmlnode_put_attrib(node,"type","text-multi"); else if(type == 1) xmlnode_put_attrib(node,"type","text-single"); else if(type == -1) xmlnode_put_attrib(node,"type","text-private"); else xmlnode_put_attrib(node,"type","hidden"); if(label != NULL) xmlnode_put_attrib(node,"label", label); xmlnode_put_attrib(node,"var", var); xmlnode_insert_cdata(xmlnode_insert_tag(node,"value"), data, -1); } static void add_xdata_desc(xmlnode parent, char *label) { xmlnode node; node = xmlnode_insert_tag(parent, "field"); xmlnode_put_attrib(node,"type","fixed"); xmlnode_insert_cdata(xmlnode_insert_tag(node,"value"), label, -1); } //return 1 if configuration may have changed int xdata_handler(cnr room, cnu user, jpacket packet) { xmlnode results, element, value, current, node, message; pool tp = pool_new(); spool sp = spool_new(tp); int visible = room->visible; char namespace[100]; char var[100]; char var_secret[100]; char var_protected[100]; log_debug(NAME, "[%s] xdata handler", FZONE); results = xmlnode_get_tag(packet->x,"x"); /* Can't find xdata - trying NS_MUC_ADMIN namespace */ if(results == NULL) { snprintf(namespace, 100, "?xmlns=%s", NS_MUC_ADMIN); element = xmlnode_get_tag(packet->x, namespace); results = xmlnode_get_tag(element,"x"); } /* Still no data, try NS_MUC_OWNER namespace */ if(results == NULL) { snprintf(namespace, 100, "?xmlns=%s", NS_MUC_OWNER); element = xmlnode_get_tag(packet->x, namespace); results = xmlnode_get_tag(element,"x"); } /* Still no data, try NS_MUC_USER namespace */ if(results == NULL) { snprintf(namespace, 100, "?xmlns=%s", NS_MUC_USER); element = xmlnode_get_tag(packet->x, namespace); results = xmlnode_get_tag(element,"x"); } /* Still no xdata, just leave */ if(results == NULL) { log_debug(NAME, "[%s] No xdata results found", FZONE); pool_free(tp); return 0; } if(j_strcmp(xmlnode_get_attrib(results, "type"), "cancel") == 0) { log_debug(NAME, "[%s] xdata form was cancelled", FZONE); /* If form cancelled and room locked, this is declaration of room destroy request */ if(room->locked == 1) { if(room->persistent == 1) xdb_room_clear(room); g_hash_table_foreach(room->remote, con_room_leaveall, (void*)NULL); con_room_zap(room); } pool_free(tp); return 0; } value = xmlnode_get_tag(results,"?var=form"); log_debug(NAME, "[%s] Form type: %s", FZONE, xmlnode_get_tag_data(value,"value")); if(is_admin(room, user->realid)) { log_debug(NAME, "[%s] Processing configuration form", FZONE); /* Clear any room locks */ if(room->locked == 1) { message = jutil_msgnew("groupchat", jid_full(user->realid), NULL, spools(packet->p, "Configuration confirmed: This room is now unlocked.", packet->p)); xmlnode_put_attrib(message,"from", jid_full(room->id)); deliver(dpacket_new(message), NULL); room->locked = 0; } /* Protect text forms from broken clients */ snprintf(var, 100, "?var=%s", FIELD_NAME); if(xmlnode_get_tag(results,var) != NULL) { free(room->name); room->name = j_strdup(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value")); } snprintf(var, 100, "?var=%s", FIELD_LEAVE); if(xmlnode_get_tag(results,var) != NULL) { free(room->note_leave); room->note_leave = j_strdup(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value")); } snprintf(var, 100, "?var=%s", FIELD_JOIN); if(xmlnode_get_tag(results,var) != NULL) { free(room->note_join); room->note_join = j_strdup(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value")); } snprintf(var, 100, "?var=%s", FIELD_RENAME); if(xmlnode_get_tag(results,var) != NULL) { free(room->note_rename); room->note_rename = j_strdup(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value")); } /* Handle text-multi */ snprintf(var, 100, "?var=%s", FIELD_DESC); if((node = xmlnode_get_tag(results,var)) != NULL) { for(current = xmlnode_get_firstchild(node); current != NULL; current = xmlnode_get_nextsibling(current)) { //We don't want blank line at end or begining of the field. It's in fact just an hack to avoid that mu-conference add blank line at the begining and the end of the field. if (((xmlnode_get_nextsibling(current) == NULL) || (xmlnode_get_prevsibling(current) == NULL)) && current->type != NTYPE_TAG) continue; spooler(sp, xmlnode_get_data(current), sp); } free(room->description); room->description = j_strdup(spool_print(sp)); } /* Update with results from form if available. If unable, simply use the original value */ snprintf(var, 100, "?var=%s", FIELD_ALLOW_SUBJECT); room->subjectlock = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"), room->subjectlock); snprintf(var, 100, "?var=%s", FIELD_PRIVACY); room->private = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->private); snprintf(var, 100, "?var=%s", FIELD_PUBLIC); room->public = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->public); snprintf(var, 100, "?var=%s", FIELD_MAX_USERS); room->maxusers = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->maxusers); if(room->master->dynamic == 0 || is_sadmin(room->master, user->realid)) { snprintf(var, 100, "?var=%s", FIELD_PERSISTENT); room->persistent = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->persistent); } snprintf(var, 100, "?var=%s", FIELD_MODERATED); room->moderated = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->moderated); snprintf(var, 100, "?var=%s", FIELD_DEFAULT_TYPE); room->defaulttype = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->defaulttype); snprintf(var, 100, "?var=%s", FIELD_PRIVATE_MSG); room->privmsg = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->privmsg); /* Nicknames locked ? Allow owner to choose if master->locknicks != 0 */ if (!room->master->locknicks) { snprintf(var, 100, "?var=%s", FIELD_LOCK_NICK); room->locknicks = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->locknicks); } snprintf(var, 100, "?var=%s", FIELD_MEMBERS_ONLY); room->invitation = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->invitation); snprintf(var, 100, "?var=%s", FIELD_ALLOW_INVITE); room->invites = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->invites); snprintf(var, 100, "?var=%s", FIELD_LEGACY); room->legacy = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),room->legacy); /* Protection against broken clients */ snprintf(var_protected, 100, "?var=%s", FIELD_PASS_PROTECTED); snprintf(var_secret, 100, "?var=%s", FIELD_PASS); if(xmlnode_get_tag(results,var_protected) != NULL && xmlnode_get_tag(results, var_secret) != NULL) { /* Is both password set and active? */ if(j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,var_protected),"value"), "1") == 0 && xmlnode_get_tag_data(xmlnode_get_tag(results,var_secret),"value") != NULL) { free(room->secret); room->secret = j_strdup(xmlnode_get_tag_data(xmlnode_get_tag(results,var_secret),"value")); log_debug(NAME ,"[%s] Switching on room password: %s", FZONE, room->secret); } else { log_debug(NAME, "[%s] Deactivating room password: %s %s", FZONE, xmlnode_get_tag_data(xmlnode_get_tag(results,var_protected),"value"), xmlnode_get_tag_data(xmlnode_get_tag(results,var_secret),"value")); free(room->secret); room->secret = NULL; } } snprintf(var, 100, "?var=%s", FIELD_WHOIS); if(j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"), "anyone") == 0) room->visible = 1; else room->visible = 0; /* Send Status Alert */ if(room->visible == 1 && room->visible != visible) con_send_room_status(room, STATUS_MUC_NON_ANONYM); else if(room->visible == 0 && room->visible != visible) con_send_room_status(room, STATUS_MUC_SEMI_ANONYM); /* Set up log format and restart logging if necessary */ if (room->master->logsEnabled) { snprintf(var, 100, "?var=%s", FIELD_LOG_FORMAT); if(xmlnode_get_tag(results,var)) { if(j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"), "xml") == 0) { if(room->logfile != NULL && room->logformat != LOG_XML) { fclose(room->logfile); room->logformat = LOG_XML; con_room_log_new(room); } else { room->logformat = LOG_XML; } } else if(j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"), "xhtml") == 0) { if(room->logfile != NULL && room->logformat != LOG_XHTML) { fclose(room->logfile); room->logformat = LOG_XHTML; con_room_log_new(room); } else { room->logformat = LOG_XHTML; } } else { if(room->logfile != NULL && room->logformat != LOG_TEXT) { fclose(room->logfile); room->logformat = LOG_TEXT; con_room_log_new(room); } else { room->logformat = LOG_TEXT; } } } /* Set up room logging */ snprintf(var, 100, "?var=%s", FIELD_ENABLE_LOGGING); if(room->logfile == NULL && j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),"1") == 0) { con_room_log_new(room); if (room->logfile == NULL) log_alert(NULL, "cannot open log file for room %s", jid_full(room->id)); else { con_room_log(room, NULL, "LOGGING STARTED"); con_send_room_status(room, STATUS_MUC_LOGGING_ON); } } if(room->logfile != NULL && j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,var),"value"),"0") == 0) { con_room_log(room, NULL, "LOGGING STOPPED"); con_room_log_close(room); con_send_room_status(room, STATUS_MUC_LOGGING_OFF); } } if(room->persistent == 1) { xdb_room_set(room); } else { xdb_room_clear(room); } } pool_free(tp); return 1; } void xdata_room_config(cnr room, cnu user, int new, xmlnode query) { xmlnode msg, iq, element, field, x; char value[4]; if(user == NULL) { log_warn(NAME, "[%s] NULL attribute found", FZONE); return; } log_debug(NAME, "[%s] Configuration form requested by %s", FZONE, jid_full(user->realid)); if(!is_owner(room, user->realid)) { log_debug(NAME, "[%s] Configuration form request denied", FZONE); if(query != NULL) { jutil_error(query,TERROR_MUC_CONFIG); deliver(dpacket_new(query),NULL); } return; } /* Lock room for IQ Registration method. Will release lock when config received */ if(new == 1) room->locked = 1; /* Catchall code, for creating a standalone form */ if( query == NULL ) { msg = xmlnode_new_tag("message"); xmlnode_put_attrib(msg, "to", jid_full(user->realid)); xmlnode_put_attrib(msg,"from",jid_full(room->id)); xmlnode_put_attrib(msg,"type","normal"); xmlnode_insert_cdata(xmlnode_insert_tag(msg,"subject"),"Please setup your room",-1); element = xmlnode_insert_tag(msg,"body"); xmlnode_insert_cdata(element,"Channel ",-1); xmlnode_insert_cdata(element,room->id->user,-1); if(new == 1) xmlnode_insert_cdata(element," has been created",-1); else xmlnode_insert_cdata(element," configuration setting",-1); x = xmlnode_insert_tag(msg,"x"); } else { msg = xmlnode_dup(query); jutil_iqresult(msg); iq = xmlnode_insert_tag(msg,"query"); xmlnode_put_attrib(iq, "xmlns", NS_MUC_OWNER); x = xmlnode_insert_tag(iq,"x"); } xmlnode_put_attrib(x,"xmlns",NS_DATA); xmlnode_put_attrib(x,"type","form"); xmlnode_insert_cdata(xmlnode_insert_tag(x,"title"),"Room configuration",-1); if(new == 1) { field = xmlnode_insert_tag(x,"instructions"); xmlnode_insert_cdata(field,"Your room \"",-1); xmlnode_insert_cdata(field,room->id->user,-1); xmlnode_insert_cdata(field,"\" has been created! The default configuration is as follows:\n", -1); if(room->logfile == NULL) xmlnode_insert_cdata(field,"- No logging\n", -1); else xmlnode_insert_cdata(field,"- logging\n", -1); if(room->moderated == 1) xmlnode_insert_cdata(field,"- Room moderation\n", -1); else xmlnode_insert_cdata(field,"- No moderation\n", -1); if(room->maxusers > 0) { snprintf(value, 4, "%i", room->maxusers); xmlnode_insert_cdata(field,"- Up to ", -1); xmlnode_insert_cdata(field, value, -1); xmlnode_insert_cdata(field, " participants\n", -1); } else { xmlnode_insert_cdata(field,"- Unlimited room size\n", -1); } if(room->secret == NULL) xmlnode_insert_cdata(field,"- No password required\n", -1); else xmlnode_insert_cdata(field,"- Password required\n", -1); if(room->invitation == 0) xmlnode_insert_cdata(field,"- No invitation required\n", -1); else xmlnode_insert_cdata(field,"- Invitation required\n", -1); if(room->persistent == 0) xmlnode_insert_cdata(field,"- Room is not persistent\n", -1); else xmlnode_insert_cdata(field,"- Room is persistent\n", -1); if(room->subjectlock == 0) xmlnode_insert_cdata(field,"- Only admins may change the subject\n", -1); else xmlnode_insert_cdata(field,"- Anyone may change the subject\n", -1); xmlnode_insert_cdata(field,"To accept the default configuration, click OK. To select a different configuration, please complete this form", -1); } else xmlnode_insert_cdata(xmlnode_insert_tag(x,"instructions"),"Complete this form to make changes to the configuration of your room.",-1); add_xdata_text(x, NULL, 0, "FORM_TYPE", NS_MUC_ROOMCONFIG); add_xdata_text(x, "Natural-Language Room Name", 1, FIELD_NAME, room->name); add_xdata_text(x, "Short Description of Room", 2, FIELD_DESC, room->description); add_xdata_desc(x, "The following messages are sent to legacy clients."); add_xdata_text(x, "Message for user leaving room", 1, FIELD_LEAVE, room->note_leave); add_xdata_text(x, "Message for user joining room", 1, FIELD_JOIN, room->note_join); add_xdata_text(x, "Message for user renaming nickname in room", 1, FIELD_RENAME, room->note_rename); add_xdata_boolean(x, "Allow Occupants to Change Subject", FIELD_ALLOW_SUBJECT, room->subjectlock); field = xmlnode_insert_tag(x,"field"); xmlnode_put_attrib(field,"type","list-single"); xmlnode_put_attrib(field,"label","Maximum Number of Room Occupants"); xmlnode_put_attrib(field,"var", FIELD_MAX_USERS); snprintf(value, 4, "%i", room->maxusers); xmlnode_insert_cdata(xmlnode_insert_tag(field, "value"), value, -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "1"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "1", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "10"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "10", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "20"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "20", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "30"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "30", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "40"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "40", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "50"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "50", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "None"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "0", -1); if (!room->master->locknicks) add_xdata_boolean(x, "Lock nicknames to JID usernames?", FIELD_LOCK_NICK, room->locknicks); add_xdata_boolean(x, "Allow Occupants to query other Occupants?", FIELD_PRIVACY, room->private); add_xdata_boolean(x, "Allow Public Searching for Room", FIELD_PUBLIC, room->public); if(room->master->dynamic == 0 || is_sadmin(room->master, user->realid)) add_xdata_boolean(x, "Make Room Persistent", FIELD_PERSISTENT, room->persistent); add_xdata_boolean(x, "Consider all Clients as Legacy (shown messages)", FIELD_LEGACY, room->legacy); add_xdata_boolean(x, "Make Room Moderated", FIELD_MODERATED, room->moderated); add_xdata_desc(x, "By default, new users entering a moderated room are only visitors"); add_xdata_boolean(x, "Make Occupants in a Moderated Room Default to Participant", FIELD_DEFAULT_TYPE, room->defaulttype); add_xdata_boolean(x, "Ban Private Messages between Occupants", FIELD_PRIVATE_MSG, room->privmsg); add_xdata_boolean(x, "An Invitation is Required to Enter", FIELD_MEMBERS_ONLY, room->invitation); add_xdata_desc(x, "By default, only admins can send invites in an invite-only room"); add_xdata_boolean(x, "Allow Occupants to Invite Others", FIELD_ALLOW_INVITE, room->invites); if(room->secret == NULL) add_xdata_boolean(x, "A Password is required to enter", FIELD_PASS_PROTECTED, 0); else add_xdata_boolean(x, "A Password is required to enter", FIELD_PASS_PROTECTED, 1); add_xdata_desc(x, "If a password is required to enter this room, you must specify the password below."); add_xdata_text(x, "The Room Password", -1, FIELD_PASS, room->secret); field = xmlnode_insert_tag(x,"field"); xmlnode_put_attrib(field,"type","list-single"); xmlnode_put_attrib(field,"label","Affiliations that May Discover Real JIDs of Occupants"); xmlnode_put_attrib(field,"var",FIELD_WHOIS); if(room->visible == 0) xmlnode_insert_cdata(xmlnode_insert_tag(field, "value"),"admins", -1); else xmlnode_insert_cdata(xmlnode_insert_tag(field, "value"),"anyone", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "Room Owner and Admins Only"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "admins", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "Anyone"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "anyone", -1); if (room->master->logsEnabled) { if(room->logfile == NULL) add_xdata_boolean(x, "Enable Logging of Room Conversations", FIELD_ENABLE_LOGGING, 0); else add_xdata_boolean(x, "Enable Logging of Room Conversations", FIELD_ENABLE_LOGGING, 1); field = xmlnode_insert_tag(x,"field"); xmlnode_put_attrib(field,"type","list-single"); xmlnode_put_attrib(field,"label","Logfile format"); xmlnode_put_attrib(field,"var",FIELD_LOG_FORMAT); if(room->logformat == LOG_XML) xmlnode_insert_cdata(xmlnode_insert_tag(field, "value"),"xml", -1); else if(room->logformat == LOG_XHTML) xmlnode_insert_cdata(xmlnode_insert_tag(field, "value"),"xhtml", -1); else xmlnode_insert_cdata(xmlnode_insert_tag(field, "value"),"text", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "XML"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "xml", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "XHTML"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "xhtml", -1); element = xmlnode_insert_tag(field, "option"); xmlnode_put_attrib(element, "label", "Plain Text"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "value"), "text", -1); } deliver(dpacket_new(msg),NULL); } jabber-muc-0.8/src/conference_room.c0000664000175000017500000013340211160655601016752 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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; /* call strescape on a string and xhtmlize the string (\n->
) */ char * _con_room_xhtml_strescape(pool p, char * buf) { char *result,*temp; int i,j; int oldlen, newlen; result = strescape(p, buf); oldlen = newlen=strlen(result); for (i=0; i", 6); j+=6; } else temp[j++] = result[i]; } temp[j]='\0'; return temp; } /* Handles logging for each room, simply returning if logfile is not defined */ void con_room_log(cnr room, char *nick, char *message) { time_t t; xmlnode xml; jid user; char *output; char timestr[80]; size_t timelen = 79; FILE *logfile; pool p; if(message == NULL || room == NULL) { log_warn(NAME, "[%s] ERR: Aborting - NULL reference found - ", FZONE); return; } logfile = room->logfile; if(logfile == NULL) { log_debug(NAME, "[%s] Logging not enabled for this room", FZONE); return; } p = pool_heap(1024); /* nicked from mod_time */ t = time(NULL); if(room->logformat == LOG_XHTML) strftime(timestr, timelen, "[%H:%M:%S]", localtime(&t)); else strftime(timestr, timelen, "[%H:%M:%S]", localtime(&t)); if(room->logformat == LOG_XML) { xml = jutil_msgnew("groupchat", jid_full(room->id) , NULL, strescape(p, message)); user = jid_new(xmlnode_pool(xml), jid_full(room->id)); jid_set(user, strescape(p, nick), JID_RESOURCE); xmlnode_put_attrib(xml, "from", jid_full(user)); jutil_delay(xml, NULL); fprintf(logfile, "%s\n", xmlnode2str(xml)); xmlnode_free(xml); } else if(room->logformat == LOG_XHTML) { if(nick) { if(j_strncmp(message, "/me ", 4) == 0) { output = extractAction(_con_room_xhtml_strescape(p, message), p); fprintf(logfile, "%s * %s%s
\n", timestr, strescape(p, nick), output); } else { fprintf(logfile, "%s <%s> %s
\n", timestr, strescape(p, nick), _con_room_xhtml_strescape(p, message)); } } else { fprintf(logfile, "%s --- %s
\n", timestr, message); } } else { if(nick) { if(j_strncmp(message, "/me ", 4) == 0) { output = extractAction(message, p); fprintf(logfile, "%s * %s%s\n", timestr, nick, output); } else { fprintf(logfile, "%s <%s> %s\n", timestr, nick, message); } } else { fprintf(logfile, "%s --- %s\n", timestr, message); } } fflush(logfile); pool_free(p); return; } void con_room_log_new(cnr room) { char *filename; char *curdate; char *dirname; char datePart[5]; struct stat fileinfo; time_t now = time(NULL); int type; pool p; spool sp; if(room == NULL || !room->master->logsEnabled) { log_warn(NAME, "[%s] Aborting - NULL room", FZONE); return; } p = pool_heap(1024); type = room->logformat; dirname = jid_full(room->id); sp = spool_new(p); if(room->master->logdir) { spooler(sp, room->master->logdir, "/", dirname, sp); } else { spooler(sp, "./", dirname, sp); } filename = spool_print(sp); if(stat(filename,&fileinfo) < 0 && mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) { log_warn(NAME, "[%s] ERR: unable to open log directory >%s<", FZONE, filename); return; } curdate = dateget(now); if (room->master->flatLogs) { spooler(sp, "/", curdate, sp); } else { strftime(datePart, 5, "%Y", localtime(&now)); spooler(sp, "/", datePart, sp); filename = spool_print(sp); if(stat(filename,&fileinfo) < 0 && mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) { log_warn(NAME, "[%s] ERR: unable to open log directory >%s<", FZONE, filename); return; } strftime(datePart, 5, "%m", localtime(&now)); spooler(sp, "/", datePart, sp); filename = spool_print(sp); if(stat(filename,&fileinfo) < 0 && mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) { log_warn(NAME, "[%s] ERR: unable to open log directory >%s<", FZONE, filename); return; } strftime(datePart, 5, "%d", localtime(&now)); spooler(sp, "/", datePart, sp); } if(type == LOG_XML) spool_add(sp, ".xml"); else if(type == LOG_XHTML) spool_add(sp, ".html"); else spool_add(sp, ".txt"); filename = spool_print(sp); if(stat(filename,&fileinfo) < 0) { log_debug(NAME, "[%s] New logfile >%s<", FZONE, filename); room->logfile = fopen(filename, "a"); if(type == LOG_XHTML && room->logfile != NULL) { fprintf(room->logfile, "\n\n\nLogs for %s, %s\n\n", jid_full(room->id), curdate); if (room->master->stylesheet!=NULL) fprintf(room->logfile, "\n",room->master->stylesheet); fprintf(room->logfile, "\n

\n"); fflush(room->logfile); } } else { room->logfile = fopen(filename, "a"); } if(room->logfile == NULL) log_warn(NAME, "[%s] ERR: unable to open log file >%s<", FZONE, filename); else log_debug(NAME, "[%s] Opened logfile >%s<", FZONE, filename); pool_free(p); free(curdate); return; } void con_room_log_close(cnr room) { int type; FILE *logfile; if(room == NULL) { log_warn(NAME, "[%s] Aborting - NULL room", FZONE); return; } type = room->logformat; logfile = room->logfile; if(logfile == NULL) { log_warn(NAME, "[%s] Aborting - NULL logfile", FZONE); return; } log_debug(NAME, "[%s] Closing logfile for room >%s<", FZONE, jid_full(room->id)); if(type == LOG_XHTML) { fprintf(logfile, "

\n\n\n"); fflush(logfile); } fclose(room->logfile); room->logfile = NULL; } void con_room_send_invite(cnu sender, xmlnode node) { xmlnode result; xmlnode element; xmlnode invite; xmlnode pass; char *body, *user, *reason, *inviter; cnr room; pool p; if(sender == NULL || node == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); return; } log_debug(NAME, "[%s] Sending room invite", FZONE); room = sender->room; invite = xmlnode_get_tag(node, "invite"); user = xmlnode_get_attrib(invite, "to"); reason = xmlnode_get_tag_data(invite, "reason"); p = xmlnode_pool(node); if(room->visible == 1) { inviter = jid_full(sender->realid); } else { inviter = jid_full(sender->localid); } xmlnode_put_attrib(invite, "from", inviter); xmlnode_hide_attrib(invite, "to"); if(reason == NULL) { reason = spools(p, "None given", p); } body = spools(p, "You have been invited to the ", jid_full(room->id), " room by ", inviter, "\nReason: ", reason, p); result = jutil_msgnew("normal", user , "Invitation", body); xmlnode_put_attrib(result, "from", jid_full(room->id)); xmlnode_insert_node(result, node); if(room->secret != NULL) { pass = xmlnode_get_tag(result, "x"); xmlnode_insert_cdata(xmlnode_insert_tag(pass, "password"), room->secret, -1); } element = xmlnode_insert_tag(result, "x"); xmlnode_put_attrib(element, "jid", jid_full(room->id)); xmlnode_put_attrib(element, "xmlns", NS_X_CONFERENCE); xmlnode_insert_cdata(element, reason, -1); log_debug(NAME, "[%s] >>>%s<<<", FZONE, xmlnode2str(result)); deliver(dpacket_new(result), NULL); return; } void con_room_forward_decline(cnr room, jpacket jp, xmlnode decline) { cnu user; jid user_jid; if (room == NULL || decline == NULL || jp == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); xmlnode_free(jp->x); return; } user_jid=jid_new(decline->p,xmlnode_get_attrib(decline,"to")); if ((room->invitation == 1 && !is_member(room, jp->from) && !is_owner(room, jp->from)) || user_jid == NULL) { log_warn(NAME, "[%s] Aborting - User is not allowed to send a decline", FZONE); jutil_error(jp->x, TERROR_MUC_OUTSIDE); deliver(dpacket_new(jp->x), NULL); return; } if (user_jid->resource == NULL) { log_warn(NAME, "[%s] Aborting - cannot send back decline, bare jid found", FZONE); return; } if (room->visible == 1) { user = g_hash_table_lookup(room->remote, jid_full(jid_fix(user_jid))); } else { user = g_hash_table_lookup(room->local, user_jid->resource); } if (user == NULL){ log_warn(NAME, "[%s] Aborting - Decline recipient is not in the room", FZONE); jutil_error(jp->x, TERROR_MUC_OUTSIDE); deliver(dpacket_new(jp->x), NULL); return; } log_debug(NAME, "[%s] Sending invitation decline", FZONE); xmlnode_put_attrib(decline, "from", jid_full(jp->from)); xmlnode_hide_attrib(decline, "to"); xmlnode_put_attrib(jp->x, "to", jid_full(user->realid)); xmlnode_put_attrib(jp->x, "from", jid_full(room->id)); log_debug(NAME, "[%s] >>>%s<<<", FZONE, xmlnode2str(jp->x)); deliver(dpacket_new(jp->x), NULL); return; } void con_room_leaveall(gpointer key, gpointer data, gpointer arg) { cnu user = (cnu)data; xmlnode info = (xmlnode)arg; char *alt, *reason; xmlnode presence; xmlnode tag; xmlnode element; xmlnode node; xmlnode destroy; log_debug(NAME, "[%s] reason : %s", FZONE, xmlnode2str(info)); if(user == NULL) { log_warn(NAME, "[%s] Aborting - NULL user attribute found", FZONE); return; } presence = jutil_presnew(JPACKET__UNAVAILABLE, NULL, NULL); tag = xmlnode_insert_tag(presence,"x"); xmlnode_put_attrib(tag, "xmlns", NS_MUC_USER); element = xmlnode_insert_tag(tag, "item"); xmlnode_put_attrib(element, "role", "none"); xmlnode_put_attrib(element, "affiliation", "none"); if (info != NULL) { destroy = xmlnode_insert_tag(tag, "destroy"); reason = xmlnode_get_tag_data(info, "reason"); node = xmlnode_insert_tag(destroy, "reason"); if(reason != NULL) { xmlnode_insert_cdata(node, reason, -1); } alt = xmlnode_get_attrib(info, "jid"); if (alt != NULL) { xmlnode_put_attrib(destroy, "jid", alt); } } con_user_send(user, user, presence); } /* returns a valid nick from the list, x is the first foo xmlnode, checks siblings */ char *con_room_nick(cnr room, cnu user, xmlnode x) { char *nick = NULL; xmlnode cur; int count = 1; if(room == NULL || user == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); return NULL; } log_debug(NAME, "[%s] looking for valid nick in room %s from starter %s", FZONE, jid_full(room->id), xmlnode2str(x)); /* make-up-a-nick phase */ if(x == NULL) { nick = pmalloco(user->p, j_strlen(user->realid->user) + 10); log_debug(NAME, "[%s] Malloc: Nick = %d", FZONE, j_strlen(user->realid->user) + 10); sprintf(nick, "%s", user->realid->user); while(g_hash_table_lookup(room->local, nick) != NULL) sprintf(nick, "%s%d", user->realid->user,count++); return nick; } /* scan the list */ for(cur = x; cur != NULL; cur = xmlnode_get_nextsibling(cur)) { if(j_strcmp(xmlnode_get_name(cur),"nick") == 0 && (nick = xmlnode_get_data(cur)) != NULL) if(g_hash_table_lookup(room->local, nick) == NULL) break; } if(is_registered(room->master, jid_full(jid_user(user->realid)), nick) == -1) nick = NULL; return nick; } void con_room_sendwalk(gpointer key, gpointer data, gpointer arg) { xmlnode x = (xmlnode)arg; cnu to = (cnu)data; cnu from; xmlnode output; if(x == NULL || to == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); return; } from = (cnu)xmlnode_get_vattrib(x,"cnu"); if(j_strcmp(xmlnode_get_name(x),"presence") == 0) { output = add_extended_presence(from, to, x, NULL, NULL, NULL); if(jid_cmp(to->realid,from->realid)==0) //own presence { add_status_code(output, STATUS_MUC_OWN_PRESENCE); } con_user_send(to, from, output); } else { con_user_send(to, from, xmlnode_dup(x)); /* Need to send duplicate */ } } /* Browse for members of a room */ void con_room_browsewalk(gpointer key, gpointer data, gpointer arg) { jid userjid; cnu user = (cnu)data; xmlnode q = (xmlnode)arg; xmlnode xml; if(user == NULL || q == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); return; } xml = xmlnode_insert_tag(q, "item"); userjid = jid_new(xmlnode_pool(xml), jid_full(user->room->id)); jid_set(userjid, user->localid->resource, JID_RESOURCE); xmlnode_put_attrib(xml, "category", "user"); xmlnode_put_attrib(xml, "type", "client"); xmlnode_put_attrib(xml, "name", user->localid->resource); xmlnode_put_attrib(xml, "jid", jid_full(userjid)); } void _con_room_discoinfo(cnr room, jpacket jp) { xmlnode result; xmlnode xdata, field; char *topic; char buf[32]; if(room == NULL) { log_warn(NAME, "[%s] Aborting - NULL room attribute found", FZONE); return; } jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"query"), "xmlns", NS_DISCO_INFO); jpacket_reset(jp); result = xmlnode_insert_tag(jp->iq,"identity"); xmlnode_put_attrib(result, "category", "conference"); xmlnode_put_attrib(result, "type", "text"); xmlnode_put_attrib(result, "name", room->name); 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_VERSION); xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_LAST); xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_TIME); xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_VCARD); if(room->secret != NULL && *room->secret != '\0') xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_passwordprotected"); else xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_unsecure"); if(room->public == 1) xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_public"); else xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_hidden"); if(room->persistent == 1) xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_persistent"); else xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_temporary"); if(room->invitation == 1) xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_membersonly"); else xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_open"); if(room->moderated == 1) xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_moderated"); else xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_unmoderated"); if(room->visible == 1) xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_nonanonymous"); else xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_semianonymous"); if(room->legacy == 1) xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc-legacy"); /* return extended disco info per JEP-0128 */ xdata = xmlnode_insert_tag(jp->iq, "x"); xmlnode_put_attrib(xdata, "xmlns", NS_DATA); xmlnode_put_attrib(xdata, "type", "result"); /* FORM_TYPE field */ field = xmlnode_insert_tag(xdata, "field"); xmlnode_put_attrib(field, "var", "FORM_TYPE"); xmlnode_put_attrib(field, "type", "hidden"); xmlnode_insert_cdata(xmlnode_insert_tag(field, "value"), "http://jabber.org/protocol/muc#roominfo", -1); /* muc#roominfo_description field */ if (room->description != NULL && *room->description != '\0') { field = xmlnode_insert_tag(xdata, "field"); xmlnode_put_attrib(field, "var", "muc#roominfo_description"); xmlnode_put_attrib(field, "label", "Description"); xmlnode_insert_cdata(xmlnode_insert_tag(field, "value"), room->description, -1); } /* muc#roominfo_subject field */ topic = xmlnode_get_attrib(room->topic, "subject"); if (topic != NULL && *topic != '\0') { field = xmlnode_insert_tag(xdata, "field"); xmlnode_put_attrib(field, "var", "muc#roominfo_subject"); xmlnode_put_attrib(field, "label", "Subject"); xmlnode_insert_cdata(xmlnode_insert_tag(field, "value"), topic, -1); } /* muc#roominfo_occupants field */ snprintf(buf, 32, "%d", room->count); field = xmlnode_insert_tag(xdata, "field"); xmlnode_put_attrib(field, "var", "muc#roominfo_occupants"); xmlnode_put_attrib(field, "label", "Number of occupants"); xmlnode_insert_cdata(xmlnode_insert_tag(field, "value"), buf, -1); /* FIXME: do something about muc#roominfo_lang field */ deliver(dpacket_new(jp->x), NULL); return; } void _con_room_discoitem(gpointer key, gpointer data, gpointer arg) { jid userjid; cnu user = (cnu)data; xmlnode query = (xmlnode)arg; xmlnode xml; if(user == NULL || query == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); return; } xml = xmlnode_insert_tag(query, "item"); userjid = jid_new(xmlnode_pool(xml), jid_full(user->room->id)); jid_set(userjid, user->localid->resource, JID_RESOURCE); xmlnode_put_attrib(xml, "jid", jid_full(userjid)); } void _con_room_disconick(cnr room, jpacket jp){ char* str; xmlnode x; if (room->locknicks) { str=jp->from->user; } else { x = get_data_byjid(room->master, xmlnode_get_attrib(jp->x, "from")); str = xmlnode_get_attrib(x, "nick") ? xmlnode_get_attrib(x, "nick") : NULL; } jutil_iqresult(jp->x); x = xmlnode_insert_tag(jp->x,"query"); xmlnode_put_attrib(x,"xmlns",NS_DISCO_INFO); xmlnode_put_attrib(x,"node","x-roomuser-item"); jpacket_reset(jp); if (str) { x = xmlnode_insert_tag(jp->x,"identity"); xmlnode_put_attrib(x,"category","conference"); xmlnode_put_attrib(x,"name",str); xmlnode_put_attrib(x,"type","text"); } deliver(dpacket_new(jp->x), NULL); } /* send disco items reply */ void _con_room_discoitems(cnr room, jpacket jp, int in_room){ xmlnode result; if (xmlnode_get_attrib(jp->iq,"node")!= NULL){ //unknow node jutil_error(jp->x, TERROR_UNAVAIL); deliver(dpacket_new(jp->x),NULL); return; } jutil_iqresult(jp->x); result = xmlnode_insert_tag(jp->x, "query"); xmlnode_put_attrib(result, "xmlns", NS_DISCO_ITEMS); if ((in_room == 1) || (is_sadmin(room->master, jp->from))) { g_hash_table_foreach(room->local, _con_room_discoitem, (void*)result); } deliver(dpacket_new(jp->x), NULL); } /* send browse reply */ void _con_room_send_browse_reply(jpacket iq, cnr room, int in_room){ xmlnode node; jutil_iqresult(iq->x); node = xmlnode_insert_tag(iq->x,"item"); xmlnode_put_attrib(node,"category","conference"); if((room->public && room->invitation == 0) || (room->public && is_member(room, iq->from))) { xmlnode_put_attrib(node,"type","public"); if (in_room || (is_sadmin(room->master, iq->from))){ g_hash_table_foreach(room->local, con_room_browsewalk, (void*)node); } } else { xmlnode_put_attrib(node,"type","private"); if (in_room || (is_sadmin(room->master, iq->from))){ g_hash_table_foreach(room->local, con_room_browsewalk, (void*)node); } } xmlnode_put_attrib(node,"xmlns",NS_BROWSE); xmlnode_put_attrib(node,"name",room->name); xmlnode_put_attrib(node,"version",VERSION); iq_populate_browse(node); deliver(dpacket_new(iq->x), NULL); } /* send a response to a iq:last request */ void _con_room_send_last(jpacket jp,cnr room){ int start; char nstr[10]; jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"query"),"xmlns",NS_LAST); jpacket_reset(jp); start = time(NULL) - room->start; sprintf(nstr,"%d",start); xmlnode_put_attrib(jp->iq,"seconds", pstrdup(jp->p, nstr)); deliver(dpacket_new(jp->x),NULL); } void con_room_outsider(cnr room, cnu from, jpacket jp) { if(room == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); return; } log_debug(NAME, "[%s] handling request from outsider %s to room %s", FZONE, jid_full(jp->from), jid_full(room->id)); /* any presence here is just fluff */ if(jp->type == JPACKET_PRESENCE) { log_debug(NAME, "[%s] Dropping presence from outsider", FZONE); xmlnode_free(jp->x); return; } if(jp->type == JPACKET_MESSAGE) { log_debug(NAME, "[%s] Bouncing message from outsider", FZONE); jutil_error(jp->x, TERROR_FORBIDDEN); deliver(dpacket_new(jp->x), NULL); return; } /* public iq requests */ if(jpacket_subtype(jp) == JPACKET__SET) { if(NSCHECK(jp->iq, NS_MUC_OWNER)) { log_debug(NAME, "[%s] IQ Set for owner function", FZONE); if(from && is_owner(room, jp->from)) { xdata_room_config(room, from, room->locked, jp->x); jutil_iqresult(jp->x); deliver(dpacket_new(jp->x), NULL); return; } else { log_debug(NAME, "[%s] IQ Set for owner disallowed", FZONE); jutil_error(jp->x, TERROR_NOTALLOWED); deliver(dpacket_new(jp->x), NULL); return; } } else if(NSCHECK(jp->iq, NS_REGISTER)) { log_debug(NAME, "[%s] IQ Set for Registration function", FZONE); jutil_error(jp->x, TERROR_NOTALLOWED); deliver(dpacket_new(jp->x), NULL); return; } } if(jpacket_subtype(jp) == JPACKET__GET) { if(NSCHECK(jp->iq,NS_VERSION)) { iq_get_version(jp); return; } else if(NSCHECK(jp->iq, NS_BROWSE)) { _con_room_send_browse_reply(jp,room,0); return; } else if(NSCHECK(jp->iq, NS_DISCO_INFO)) { if (xmlnode_get_attrib(jp->iq,"node")== NULL){ log_debug(NAME, "[%s] Outside room packet - Disco Info Request", FZONE); _con_room_discoinfo(room, jp); return; } else { if (j_strcmp(xmlnode_get_attrib(jp->iq,"node"),"x-roomuser-item")==0) { log_debug(NAME, "[%s] Outside room packet - Nickname Discovery Request", FZONE); _con_room_disconick(room,jp); return; } else { //unknow node jutil_error(jp->x, TERROR_UNAVAIL); deliver(dpacket_new(jp->x),NULL); return; } } } else if(NSCHECK(jp->iq, NS_DISCO_ITEMS)) { log_debug(NAME, "[%s] Outside room packet - Disco Items Request", FZONE); _con_room_discoitems(room, jp, 0); return; } else if(NSCHECK(jp->iq, NS_LAST)) { log_debug(NAME, "[%s] Outside room packet - Last Request", FZONE); _con_room_send_last(jp,room); return; } else if(NSCHECK(jp->iq,NS_TIME)) { /* Compliant with JEP-0090 */ log_debug(NAME, "[%s] Server packet - Time Request", FZONE); iq_get_time(jp); return; } else if(NSCHECK(jp->iq, NS_MUC_OWNER)) { if(j_strcmp(xmlnode_get_name(jp->iq),"query") == 0) { log_debug(NAME, "[%s] IQ Get for owner: configuration", FZONE); if(!from || !is_owner(room, from->realid)) { jutil_error(jp->x, TERROR_BAD); deliver(dpacket_new(jp->x), NULL); return; } xdata_room_config(room, from, 0, jp->x); xmlnode_free(jp->x); return; } } else if(NSCHECK(jp->iq, NS_REGISTER)) { log_debug(NAME, "[%s] IQ Get for Registration function", FZONE); jutil_error(jp->x, TERROR_NOTALLOWED); deliver(dpacket_new(jp->x), NULL); return; } else if(NSCHECK(jp->iq,NS_VCARD)) { log_debug(NAME, "[%s] Outside room 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_cdata(xmlnode_insert_tag(jp->iq, "DESC"), room->description, -1); deliver(dpacket_new(jp->x),NULL); return; } } //if this is a subscription packet, drop silently if((jpacket_subtype(jp) == JPACKET__SUBSCRIBE) || (jpacket_subtype(jp) == JPACKET__SUBSCRIBED) || (jpacket_subtype(jp) == JPACKET__UNSUBSCRIBE) || (jpacket_subtype(jp) == JPACKET__UNSUBSCRIBED) ) { log_debug(NAME,"[%s] dropping subscription packet", FZONE); xmlnode_free(jp->x); return; } log_debug(NAME, "[%s] Sending Not Implemented", FZONE); jutil_error(jp->x, TERROR_NOTIMPL); deliver(dpacket_new(jp->x), NULL); return; } void con_room_process(cnr room, cnu from, jpacket jp) { char *nick = NULL; char *key; xmlnode result, item, x, node; jid id; int cont; pool hist_p; cnh hist; if(room == NULL || from == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); return; } log_debug(NAME, "[%s] handling request from participant %s (%s) to room %s", FZONE, jid_full(from->realid), from->localid->resource, jid_full(room->id)); /* presence is just stored and forwarded */ if(jp->type == JPACKET_PRESENCE) { xmlnode_free(from->presence); from->presence = xmlnode_dup(jp->x); jutil_delay(from->presence, NULL); xmlnode_put_vattrib(jp->x, "cnu", (void*)from); g_hash_table_foreach(room->local, con_room_sendwalk, (void*)jp->x); xmlnode_free(jp->x); return; } if(jp->type == JPACKET_MESSAGE) { if(NSCHECK(xmlnode_get_tag(jp->x,"x"),NS_MUC_USER)) { /* Invite */ log_debug(NAME, "[%s] Found invite request", FZONE); if(room->invitation == 1 && room->invites == 0 && !is_admin(room, from->realid)) { log_debug(NAME, "[%s] Forbidden invitation request, returning error", FZONE); jutil_error(jp->x,TERROR_FORBIDDEN); deliver(dpacket_new(jp->x),NULL); return; } item = xmlnode_dup(xmlnode_get_tag(jp->x,"x")); nick = xmlnode_get_attrib(xmlnode_get_tag(item, "invite"), "to"); if( nick == NULL) { log_debug(NAME, "[%s] No receipient, returning error", FZONE); jutil_error(jp->x,TERROR_BAD); deliver(dpacket_new(jp->x),NULL); xmlnode_free(item); return; } if(room->invitation == 1) { id = jid_new(xmlnode_pool(item), nick); key = j_strdup(jid_full(jid_user(jid_fix(id)))); g_hash_table_insert(room->member, key, (void*)item); } else { xmlnode_free(item); } con_room_send_invite(from, xmlnode_get_tag(jp->x,"x")); return; } /* if topic, store it */ if((x = xmlnode_get_tag(jp->x,"subject")) != NULL ) { /* catch the invite hack */ if((nick = xmlnode_get_data(x)) != NULL && j_strncasecmp(nick,"invite:",7) == 0) { nick += 7; if((jp->to = jid_new(jp->p, nick)) == NULL) { jutil_error(jp->x,TERROR_BAD); }else{ xmlnode_put_attrib(jp->x, "to", jid_full(jp->to)); jp->from = jid_new(jp->p, jid_full(from->localid)); xmlnode_put_attrib(jp->x, "from", jid_full(jp->from)); } deliver(dpacket_new(jp->x), NULL); return; } /* Disallow subject change if user does not have high enough level */ if((!is_admin(room, from->realid) && room->subjectlock == 0) || is_visitor(room, from->realid) ) { jutil_error(jp->x,TERROR_FORBIDDEN); deliver(dpacket_new(jp->x),NULL); return; } /* Save room topic for new users */ xmlnode_free(room->topic); room->topic = xmlnode_new_tag("topic"); xmlnode_put_attrib(room->topic, "subject", xmlnode_get_data(x)); xmlnode_insert_cdata(room->topic, from->localid->resource, -1); xmlnode_insert_cdata(room->topic, " has set the topic to: ", -1); xmlnode_insert_cdata(room->topic, xmlnode_get_data(x), -1); #ifdef HAVE_MYSQL sql_update_field(room->master->sql, jid_full(room->id), "topic", xmlnode_get_attrib(room->topic,"subject")); #endif /* Save the room topic if room is persistent */ if(room->persistent == 1) { xdb_room_set(room); } } /* Check if allowed to talk */ if(room->moderated == 1 && !is_participant(room, from->realid)) { /* Attempting to talk in a moderated room without voice intrinsic */ jutil_error(jp->x,TERROR_MUC_VOICE); deliver(dpacket_new(jp->x),NULL); return; } if(jp->subtype != JPACKET__GROUPCHAT) { jutil_error(jp->x, TERROR_BAD); deliver(dpacket_new(jp->x), NULL); return; } /* ensure type="groupchat" */ xmlnode_put_attrib(jp->x,"type","groupchat"); /* check is the message is a discussion history */ cont = 0; for( node = xmlnode_get_firstchild(jp->x); node != NULL; node = xmlnode_get_nextsibling(node)) { if (xmlnode_get_name(node)==NULL || strcmp("x",xmlnode_get_name(node))!=0) continue; // check if the node is a "x" node if(!NSCHECK(node, NS_DELAY)) continue; if ((xmlnode_get_attrib(node, "from")!= NULL) && (xmlnode_get_attrib(node, "stamp")) && (is_owner(room,from->realid))) { cont = 1; break; } } /* Save copy of packet for history */ node = xmlnode_dup(jp->x); /* broadcast */ xmlnode_put_vattrib(jp->x,"cnu",(void*)from); g_hash_table_foreach(room->local, con_room_sendwalk, (void*)jp->x); /* log */ con_room_log(room, from->localid->resource, xmlnode_get_tag_data(jp->x, "body")); /* Save from address */ if (cont == 0) { xmlnode_put_attrib(node, "from", jid_full(from->localid)); } else { xmlnode_put_attrib(node, "from", jid_full(jid_user(from->localid))); } /* store in history */ if (cont == 0) jutil_delay(node, jid_full(room->id)); if(room->master->history > 0) { hist_p = pool_new(); hist = pmalloco(hist_p, sizeof(_cnh)); hist->p = hist_p; hist->x = node; hist->content_length = j_strlen(xmlnode_get_tag_data(node,"body")); hist->timestamp = time(NULL); if(++room->hlast == room->master->history) room->hlast = 0; if (room->history[room->hlast] != NULL) { log_debug(NAME, "[%s] clearing old history entry %d", FZONE, room->hlast); xmlnode_free(room->history[room->hlast]->x); pool_free(room->history[room->hlast]->p); } log_debug(NAME, "[%s] adding history entry %d", FZONE, room->hlast); room->history[room->hlast] = hist; } else { xmlnode_free(node); } xmlnode_free(jp->x); return; } /* public iq requests */ if(jpacket_subtype(jp) == JPACKET__SET) { if(NSCHECK(jp->iq, NS_MUC_ADMIN)) { log_debug(NAME, "[%s] IQ Set for admin function: >%s<", FZONE, xmlnode_get_name(jp->iq)); if(!is_moderator(room, from->realid)) { jutil_error(jp->x, TERROR_FORBIDDEN); deliver(dpacket_new(jp->x), NULL); return; } if(j_strcmp(xmlnode_get_name(jp->iq), "query") == 0) { log_debug(NAME, "[%s] List set requested by admin...", FZONE); result = xmlnode_get_tag(jp->x, "query"); if(NSCHECK(xmlnode_get_tag(result,"x"),NS_DATA)) { log_debug(NAME, "[%s] Received x:data", FZONE); jutil_error(jp->x, TERROR_BAD); deliver(dpacket_new(jp->x), NULL); return; } else { con_parse_item(from, jp); return; } } } else if(NSCHECK(jp->iq, NS_MUC_OWNER)) { if(!is_owner(room, from->realid)) { jutil_error(jp->x, TERROR_NOTALLOWED); deliver(dpacket_new(jp->x), NULL); return; } if(j_strcmp(xmlnode_get_name(jp->iq),"query") == 0) { result = xmlnode_get_tag(jp->x, "query"); node = xmlnode_get_tag(result, "destroy"); if(node) { log_debug(NAME, "[%s] IQ Set for owner: destroy requested", FZONE); /* Remove old xdb registration */ if(room->persistent == 1) { xdb_room_clear(room); } /* inform everyone they have left the room */ g_hash_table_foreach(room->remote, con_room_leaveall, node); con_room_zap(room); jutil_iqresult(jp->x); deliver(dpacket_new(jp->x), NULL); return; } else if(NSCHECK(xmlnode_get_tag(result,"x"),NS_DATA)) { log_debug(NAME, "[%s] Received x:data", FZONE); #ifdef HAVE_MYSQL if (xdata_handler(room, from, jp)>0) sql_update_room_config(room->master->sql,room); #else xdata_handler(room, from, jp); #endif jutil_iqresult(jp->x); deliver(dpacket_new(jp->x), NULL); return; } else { log_debug(NAME, "[%s] IQ Set for owner: configuration set", FZONE); con_parse_item(from, jp); return; } } else { jutil_error(jp->x, TERROR_BAD); deliver(dpacket_new(jp->x), NULL); return; } } else if(NSCHECK(jp->iq, NS_REGISTER)) { log_debug(NAME, "[%s] IQ Set for Registration function", FZONE); jutil_error(jp->x, TERROR_NOTALLOWED); deliver(dpacket_new(jp->x), NULL); return; } jutil_error(jp->x, TERROR_BAD); deliver(dpacket_new(jp->x), NULL); return; } if(jpacket_subtype(jp) == JPACKET__GET) { if(NSCHECK(jp->iq,NS_VERSION)) { iq_get_version(jp); return; } else if(NSCHECK(jp->iq, NS_BROWSE)) { _con_room_send_browse_reply(jp,room,1); return; } else if(NSCHECK(jp->iq, NS_DISCO_INFO)) { if (xmlnode_get_attrib(jp->iq,"node")==NULL) { log_debug(NAME, "[%s] room packet - Disco Info Request", FZONE); _con_room_discoinfo(room, jp); return; } else { if (j_strcmp(xmlnode_get_attrib(jp->iq,"node"),"x-roomuser-item")==0) { log_debug(NAME, "[%s] room packet - Nickname Discovery Request", FZONE); _con_room_disconick(room,jp); return; } else { jutil_error(jp->x, TERROR_UNAVAIL); deliver(dpacket_new(jp->x),NULL); return; } } } else if(NSCHECK(jp->iq, NS_DISCO_ITEMS)) { log_debug(NAME, "[%s] room packet - Disco Items Request", FZONE); _con_room_discoitems(room, jp, 1); return; } else if(NSCHECK(jp->iq, NS_LAST)) { log_debug(NAME, "[%s] room packet - Last Request", FZONE); _con_room_send_last(jp,room); return; } else if(NSCHECK(jp->iq,NS_TIME)) { /* Compliant with JEP-0090 */ log_debug(NAME, "[%s] Server packet - Time Request", FZONE); iq_get_time(jp); return; } else if(NSCHECK(jp->iq, NS_MUC_ADMIN)) { log_debug(NAME, "[%s] IQ Get for admin function, %s", FZONE, xmlnode_get_name(jp->iq)); if(!is_moderator(room, from->realid)) { jutil_error(jp->x, TERROR_FORBIDDEN); deliver(dpacket_new(jp->x), NULL); return; } if(j_strcmp(xmlnode_get_name(jp->iq),"query") == 0) { con_parse_item(from, jp); return; } } else if(NSCHECK(jp->iq, NS_MUC_OWNER)) { log_debug(NAME, "[%s] IQ Get for owner function, %s", FZONE, xmlnode_get_name(jp->iq)); if(!is_owner(room, from->realid)) { jutil_error(jp->x, TERROR_FORBIDDEN); deliver(dpacket_new(jp->x), NULL); return; } if(j_strcmp(xmlnode_get_name(jp->iq),"query") == 0) { con_parse_item(from, jp); return; } } else if(NSCHECK(jp->iq, NS_REGISTER)) { log_debug(NAME, "[%s] IQ Get for Registration function", FZONE); jutil_error(jp->x, TERROR_NOTALLOWED); deliver(dpacket_new(jp->x), NULL); return; } else if(NSCHECK(jp->iq,NS_VCARD)) { log_debug(NAME, "[%s] room 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_cdata(xmlnode_insert_tag(jp->iq, "DESC"), room->description, -1); deliver(dpacket_new(jp->x),NULL); return; } } //if this is a subscription packet, drop silently if((jpacket_subtype(jp) == JPACKET__SUBSCRIBE) || (jpacket_subtype(jp) == JPACKET__SUBSCRIBED) || (jpacket_subtype(jp) == JPACKET__UNSUBSCRIBE) || (jpacket_subtype(jp) == JPACKET__UNSUBSCRIBED) ) { log_debug(NAME,"[%s] dropping subscription packet", FZONE); xmlnode_free(jp->x); return; } log_debug(NAME, "[%s] Sending Bad Request", FZONE); jutil_error(jp->x, TERROR_BAD); deliver(dpacket_new(jp->x), NULL); return; } cnr con_room_new(cni master, jid roomid, jid owner, char *name, char *secret, int private, int persist) { cnr room; pool p; cnu admin; char *key; time_t now = time(NULL); /* Create pool for room struct */ p = pool_new(); room = pmalloco(p, sizeof(_cnr)); log_debug(NAME, "[%s] Malloc: _cnr = %d", FZONE, sizeof(_cnr)); room->p = p; room->master = master; /* room jid */ room->id = jid_new(p, jid_full(jid_fix(roomid))); /* room natural language name for browse/disco */ if(name) room->name = j_strdup(name); else room->name = j_strdup(room->id->user); /* room password */ room->secret = j_strdup(secret); room->private = private; /* lock nicknames - defaults to off (unless overridden by master setting) */ room->locknicks = 0; /* Initialise room history */ room->history = pmalloco(p, sizeof(_cnh) * master->history); /* make array of xmlnodes */ log_debug(NAME, "[%s] Malloc: history = %d", FZONE, sizeof(_cnh) * master->history); /* Room time */ room->start = now; room->created = now; /* user hashes */ room->remote = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, ght_remove_cnu); room->local = g_hash_table_new_full(g_str_hash,g_str_equal, NULL, NULL); room->roster = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, ght_remove_xmlnode); /* Affiliated hashes */ room->owner = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, ght_remove_xmlnode); room->admin = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, ght_remove_xmlnode); room->member = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, ght_remove_xmlnode); room->outcast = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, ght_remove_xmlnode); /* Role hashes */ room->moderator = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, NULL); room->participant = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, NULL); /* Room messages */ room->note_leave = j_strdup(xmlnode_get_tag_data(master->config,"notice/leave")); room->note_join = j_strdup(xmlnode_get_tag_data(master->config,"notice/join")); room->note_rename = j_strdup(xmlnode_get_tag_data(master->config,"notice/rename")); /* Room Defaults */ room->public = master->public; room->subjectlock = 0; room->maxusers = 30; room->persistent = persist; room->moderated = 0; room->defaulttype = 0; room->privmsg = 0; room->invitation = 0; room->invites = 0; room->legacy = 0; room->visible = 0; room->logfile = NULL; room->logformat = LOG_TEXT; room->description = j_strdup(room->name); /* Assign owner to room */ if(owner != NULL) { admin = (void*)con_user_new(room, owner); add_roster(room, admin->realid); room->creator = jid_new(room->p, jid_full(jid_user(admin->realid))); add_affiliate(room->owner, admin->realid, NULL); log_debug(NAME, "[%s] Added new admin: %s to room %s", FZONE, jid_full(jid_fix(owner)), jid_full(room->id)); } key = j_strdup(jid_full(room->id)); g_hash_table_insert(master->rooms, key, (void*)room); log_debug(NAME,"[%s] new room %s (%s/%s/%d)", FZONE, jid_full(room->id),name,secret,private); /* Save room configuration if persistent */ if(room->persistent == 1) { xdb_room_set(room); } #ifdef HAVE_MYSQL sql_insert_room_config(master->sql, room); sql_add_room_lists(master->sql, room); #endif return room; } void _con_room_send(gpointer key, gpointer data, gpointer arg) { cnu user = (cnu)data; xmlnode x = (xmlnode)arg; xmlnode output; if(user == NULL || x == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); return; } output = xmlnode_dup((xmlnode)x); xmlnode_put_attrib(output, "to", jid_full(user->realid)); deliver(dpacket_new(output), NULL); return; } void _con_room_send_legacy(gpointer key, gpointer data, gpointer arg) { cnu user = (cnu)data; xmlnode x = (xmlnode)arg; xmlnode output; if(user == NULL || x == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); return; } if(!is_legacy(user)) return; output = xmlnode_dup((xmlnode)x); xmlnode_put_attrib(output, "to", jid_full(user->realid)); deliver(dpacket_new(output), NULL); return; } void con_room_send(cnr room, xmlnode x, int legacy) { if(room == NULL || x == NULL) { log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE); return; } log_debug(NAME,"[%s] Sending packet from room %s: %s", FZONE, jid_full(room->id), xmlnode2str(x)); /* log */ con_room_log(room, NULL, xmlnode_get_tag_data(x, "body")); xmlnode_put_attrib(x, "from", jid_full(room->id)); deliver__flag = 0; if(legacy) g_hash_table_foreach(room->local, _con_room_send_legacy, (void*)x); else g_hash_table_foreach(room->local, _con_room_send, (void*)x); deliver__flag = 1; deliver(NULL, NULL); xmlnode_free(x); return; } /* Clear up room hashes */ void con_room_cleanup(cnr room) { char *roomid; if(room == NULL) { log_warn(NAME, "[%s] Aborting - NULL room attribute found", FZONE); return; } roomid = j_strdup(jid_full(room->id)); log_debug(NAME, "[%s] cleaning room %s", FZONE, roomid); /* Clear old hashes */ log_debug(NAME, "[%s] zapping list remote in room %s", FZONE, roomid); g_hash_table_destroy(room->remote); log_debug(NAME, "[%s] zapping list local in room %s", FZONE, roomid); g_hash_table_destroy(room->local); log_debug(NAME, "[%s] zapping list roster in room %s", FZONE, roomid); g_hash_table_destroy(room->roster); log_debug(NAME, "[%s] zapping list owner in room %s", FZONE, roomid); g_hash_table_destroy(room->owner); log_debug(NAME, "[%s] zapping list admin in room %s", FZONE, roomid); g_hash_table_destroy(room->admin); log_debug(NAME, "[%s] zapping list member in room %s", FZONE, roomid); g_hash_table_destroy(room->member); log_debug(NAME, "[%s] zapping list outcast in room %s", FZONE, roomid); g_hash_table_destroy(room->outcast); log_debug(NAME, "[%s] zapping list moderator in room %s", FZONE, roomid); g_hash_table_destroy(room->moderator); log_debug(NAME, "[%s] zapping list participant in room %s", FZONE, roomid); g_hash_table_destroy(room->participant); log_debug(NAME, "[%s] closing room log in room %s", FZONE, roomid); if(room->logfile && room->logfile != NULL) fclose(room->logfile); log_debug(NAME, "[%s] Clearing any history in room %s", FZONE, roomid); con_room_history_clear(room); log_debug(NAME, "[%s] Clearing topic in room %s", FZONE, roomid); xmlnode_free(room->topic); log_debug(NAME, "[%s] Clearing strings and legacy messages in room %s", FZONE, roomid); free(room->name); free(room->description); free(room->secret); free(room->note_join); free(room->note_rename); free(room->note_leave); free(roomid); return; } /* Zap room entry */ void con_room_zap(cnr room) { if(room == NULL) { log_warn(NAME, "[%s] Aborting - NULL room attribute found", FZONE); return; } log_debug(NAME, "[%s] cleaning up room %s", FZONE, jid_full(room->id)); con_room_cleanup(room); #ifdef HAVE_MYSQL sql_destroy_room(room->master->sql, jid_full(room->id)); #endif log_debug(NAME, "[%s] zapping room %s from list and freeing pool", FZONE, jid_full(room->id)); g_hash_table_remove(room->master->rooms, jid_full(room->id)); return; } void con_room_history_clear(cnr room) { int h; if(room->master->history > 0) { h = room->hlast; while(1) { h++; if(h == room->master->history) h = 0; if (room->history[h] != NULL) { log_debug(NAME, "[%s] Clearing history entry %d", FZONE, h); xmlnode_free(room->history[h]->x); pool_free(room->history[h]->p); } else { log_debug(NAME, "[%s] skipping history entry %d", FZONE, h); } if(h == room->hlast) break; } } } jabber-muc-0.8/src/hash.c0000664000175000017500000000266111160655601014534 0ustar bencerbencer/* * 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" void ght_remove_key(gpointer data) { log_debug(NAME, "[%s] Auto-removing key %s", FZONE, (char*) data); free(data); } void ght_remove_cnu(gpointer data) { cnu node = (cnu)data; log_debug(NAME, "[%s] Auto-removing cnu %s", FZONE, jid_full(node->realid)); pool_free(node->p); } void ght_remove_cnr(gpointer data) { cnr node = (cnr)data; log_debug(NAME, "[%s] Auto-removing cnr %s", FZONE, jid_full(node->id)); pool_free(node->p); } void ght_remove_xmlnode(gpointer data) { xmlnode node = (xmlnode)data; log_debug(NAME, "[%s] Auto-removing xmlnode (%s)", FZONE, xmlnode2str(node)); pool_free(node->p); } jabber-muc-0.8/src/main.c0000664000175000017500000001547511160655601014544 0ustar bencerbencer/* JCR - Jabber Component Runtime Copyright (C) 2003 Paul Curtis This program is free software; you can redistribute it and/or modify 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, MA 02111-1307 USA $Id: main.c,v 1.2 2005/12/15 07:48:11 peregrine Exp $ */ #include "jcomp.h" #include "lib.h" void usage() { printf("Usage: mu-conference [-B] [-s] [-h] [-d LEVEL] -c FILE\n"); printf(" -B Put the daemon in background\n"); printf(" -s Show debug messages on stderr\n"); printf(" -h Print this help\n"); printf(" -d LEVEL Set the level of debug output\n"); printf(" -c FILE Set the config file, mandatory argument\n"); } int main(int argc, char *argv[]) { extern char *optarg; extern int optind, opterr, optopt; int inBackground = 0; int rc, pid, c; int message_mask_set = 0; int message_stderr_set = 0; int fdlimit, fd; struct sigaction act, act_hup; FILE *pid_stream; struct stat st; char *config_file = NULL; pool p; /* GThread *dthread; */ /* the packet delivery thread */ GMainLoop *gmain; /* the receive packet event loop */ jcr = (jcr_instance)calloc(1, sizeof(_jcr_instance)); g_thread_init(NULL); fprintf(stderr, "%s -- %s\n%s\n\n", _JCOMP_NAME, _JCOMP_VERS, _JCOMP_COPY); while ((c = getopt(argc, argv, "Bshd:c:")) != EOF) switch (c) { case 'B': inBackground = 1; break; case 'c': config_file = optarg; break; case 'd': jcr->message_mask = j_atoi(optarg, 124); message_mask_set = 1; break; case 's': jcr->message_stderr = 1; message_stderr_set = 1; break; case 'h': usage(); return 0; } /* The configuration file must be specified, and there is no default */ if (config_file == NULL) { fprintf(stderr, "%s: Configuration file not specified, exiting.\n", JDBG); usage(); return 1; } /* Parse the XML in the config file -- store it as a node */ jcr->config = xmlnode_file(config_file); if (jcr->config == NULL) { fprintf(stderr, "%s: XML parsing failed in configuration file '%s'\n%s\n", JDBG, config_file, xmlnode_file_borked(config_file)); return 1; } /* The spool directory --- for all xdb calls. */ if ((xmlnode_get_type(xmlnode_get_tag(jcr->config,"spool")) == NTYPE_TAG) == 0) { fprintf(stderr, "%s: No spool directory specified in configuration file '%s'\n", JDBG, config_file); return 1; } jcr->spool_dir = strdup(xmlnode_get_data(xmlnode_get_tag(jcr->config,"spool"))); rc = stat(jcr->spool_dir, &st); if (rc < 0) { /* Directory doesn't seem to exist, we try to create it */ if (mkdir(jcr->spool_dir,S_IRUSR | S_IWUSR | S_IXUSR)==0){ fprintf(stdout, "%s: '%s': directory created.\n", JDBG, jcr->spool_dir); rc = stat(jcr->spool_dir, &st); } else { fprintf(stderr, "%s: '%s': ", JDBG, jcr->spool_dir); perror(NULL); return 1; } } if (!(S_ISDIR(st.st_mode))) { fprintf(stderr, "%s: '%s' is not a directory.\n", JDBG, jcr->spool_dir); return 1; } /* The log directory --- for the log_* functions */ if ((xmlnode_get_type(xmlnode_get_tag(jcr->config,"logdir")) == NTYPE_TAG) == 0) { fprintf(stderr, "%s: No log directory specified in configuration file '%s'\n", JDBG, config_file); return 1; } jcr->log_dir = strdup(xmlnode_get_data(xmlnode_get_tag(jcr->config,"logdir"))); rc = stat(jcr->log_dir, &st); if (rc < 0) { fprintf(stderr, "%s: '%s': ", JDBG, jcr->log_dir); perror(NULL); return 1; } if (!(S_ISDIR(st.st_mode))) { fprintf(stderr, "%s: '%s' is not a directory.\n", JDBG, jcr->log_dir); return 1; } if (!message_mask_set) jcr->message_mask = j_atoi(xmlnode_get_data(xmlnode_get_tag(jcr->config,"loglevel")), 124); if (!message_stderr_set) jcr->message_stderr = (xmlnode_get_type(xmlnode_get_tag(jcr->config,"logstderr")) == NTYPE_TAG); if (inBackground == 1) { if ((pid = fork()) == -1) { fprintf(stderr, "%s: Could not start in background\n", JDBG); exit(1); } if (pid) exit(0); /* in child process .... process and terminal housekeeping */ setsid(); fdlimit = sysconf(_SC_OPEN_MAX); fd = 0; while (fd < fdlimit) close(fd++); open("/dev/null",O_RDWR); dup(0); dup(0); } pid = getpid(); /* We now can initialize the resources */ g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, jcr_log_handler, jcr); log_warn(JDBG, "%s -- %s starting.", _JCOMP_NAME, _JCOMP_VERS); config_file = xmlnode_get_data(xmlnode_get_tag(jcr->config,"pidfile")); if (config_file == NULL) config_file = "./jcr.pid"; pid_stream = fopen(config_file, "w"); if (pid_stream != NULL) { fprintf(pid_stream, "%d", pid); fclose(pid_stream); } jcr->pid_file = j_strdup(config_file); jcr->dqueue = g_async_queue_new(); gmain = g_main_loop_new(NULL, FALSE); jcr->gmain = gmain; jcr->recv_buffer_size = j_atoi(xmlnode_get_data(xmlnode_get_tag(jcr->config,"recv-buffer")), 8192); jcr->recv_buffer = (char *)malloc(jcr->recv_buffer_size); jcr->in_shutdown = 0; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGTERM); sigaddset(&act.sa_mask, SIGINT); sigaddset(&act.sa_mask, SIGKILL); act.sa_handler = jcr_server_shutdown; //act.sa_restorer = NULL; act.sa_flags = 0; sigemptyset(&act_hup.sa_mask); sigaddset(&act_hup.sa_mask, SIGHUP); act_hup.sa_handler = jcr_server_hangup; act_hup.sa_flags = 0; sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGKILL, &act, NULL); sigaction(SIGHUP, &act_hup, NULL); #ifdef LIBIDN /* init the stringprep caches for jid manipulation */ jid_init_cache(); #endif p = pool_new(); jcr->jcr_i = (instance)pmalloc(p, sizeof(_instance)); jcr->jcr_i->p = p; jcr->jcr_i->id = pstrdup(p, xmlnode_get_data(xmlnode_get_tag(jcr->config,"host"))); /* The component call */ conference(jcr->jcr_i, NULL); log_warn(JDBG, "Main loop starting."); jcr_main_new_stream(); g_main_loop_run(gmain); log_warn(JDBG, "Main loop exiting."); #ifdef LIBIDN /* free stringprep caches */ jid_stop_caching(); #endif return 0; } jabber-muc-0.8/src/roles.c0000664000175000017500000002304411160655601014733 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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" taffil affiliation_level(cnr room, jid user) { log_debug(NAME, "[%s] Affiliation Check", FZONE); if(is_owner(room, user)) { return TAFFIL_OWNER; } else if(is_admin(room, user)) { return TAFFIL_ADMIN; } else if(is_member(room, user)) { return TAFFIL_MEMBER; } else if(is_outcast(room, user)) { return TAFFIL_OUTCAST; } return TAFFIL_NONE; } trole role_level(cnr room, jid user) { log_debug(NAME, "[%s] Role Check", FZONE); if(is_leaving(room, user)) { return TROLE_NONE; } else if(is_moderator(room, user)) { return TROLE_MODERATOR; } else if(is_participant(room, user)) { return TROLE_PARTICIPANT; } else if(is_visitor(room, user)) { return TROLE_VISITOR; } return TROLE_NONE; } int add_affiliate(GHashTable *hash, jid userid, xmlnode details) { xmlnode old; xmlnode store; xmlnode node; char ujid[2048]; if(userid == NULL) { return -1; } snprintf(ujid, sizeof(ujid), "%s@%s", userid->user, userid->server); old = g_hash_table_lookup(hash, ujid); /* User not previously registered. Set up */ if(old == NULL) { store = xmlnode_new_tag("users"); } else { node = xmlnode_get_tag(old, spools(xmlnode_pool(old), "item?jid=", jid_full(userid), xmlnode_pool(old))); /* If already in the node, ignore */ if(node != NULL) return 0; store = xmlnode_dup(old); } if(details != NULL) { xmlnode_free(store); store = xmlnode_dup(details); } else if(userid->resource != NULL) { node = xmlnode_insert_tag(store, "item"); xmlnode_put_attrib(node, "jid", jid_full(userid)); } g_hash_table_insert(hash, j_strdup(ujid), store); return 1; } int remove_affiliate(GHashTable *hash, jid userid) { xmlnode old; xmlnode store; xmlnode node; char ujid[2048]; if(userid == NULL) { return -1; } snprintf(ujid, sizeof(ujid), "%s@%s", userid->user, userid->server); old = g_hash_table_lookup(hash, ujid); if(old == NULL) return 1; store = xmlnode_dup(old); node = xmlnode_get_tag(store, spools(xmlnode_pool(store), "item?jid=", jid_full(userid), xmlnode_pool(store))); if(node == NULL) { xmlnode_free(store); return 1; } xmlnode_hide(node); g_hash_table_insert(hash, j_strdup(ujid), store); return 1; } void revoke_affiliate(cnr room, GHashTable *hash, jid userid) { cnu user; jid jabberid; xmlnode store; xmlnode current; char *userjid; char ujid[2048]; if(userid == NULL) { return; } snprintf(ujid, sizeof(ujid), "%s@%s", userid->user, userid->server); store = g_hash_table_lookup(hash, ujid); if(store == NULL) return; current = xmlnode_get_tag(store, "item"); if(current != NULL) { for(current = xmlnode_get_firstchild(store); current != NULL; current = xmlnode_get_nextsibling(current)) { userjid = xmlnode_get_attrib(current, "jid"); if(userjid != NULL) { jabberid = jid_new(xmlnode_pool(store), userjid); user = g_hash_table_lookup(room->remote, jid_full(jabberid)); if(user != NULL) { update_presence(user); } } } } g_hash_table_remove(hash, ujid); } void change_affiliate(char *affiliation, cnu sender, jid user, char *reason, jid by) { cnr room; cnu from; taffil current; xmlnode data, invite, x; char ujid[2048]; if(affiliation == NULL || sender == NULL || user == NULL) { log_warn(NAME, "[%s] Missing attributes", FZONE); return; } snprintf(ujid, sizeof(ujid), "%s@%s", user->user, user->server); room = sender->room; current = affiliation_level(room, user); /* if not changing affiliation, just return */ if(j_strcmp(current.msg, affiliation) == 0) { log_debug(NAME, "[%s] Affiliation not changing - %s == %s ", FZONE, affiliation, current.msg); return; } /* Clear any old affiliation */ #ifdef HAVE_MYSQL sql_remove_affiliate(room->master->sql,room,user); #endif if(j_strcmp(affiliation, "owner") != 0) { revoke_affiliate(room, room->owner, user); } if(j_strcmp(affiliation, "admin") != 0) { revoke_affiliate(room, room->admin, user); } if(j_strcmp(affiliation, "member") != 0) { revoke_affiliate(room, room->member, user); } if(j_strcmp(affiliation, "outcast") != 0) { revoke_affiliate(room, room->outcast, user); } /* Update to new affiliation */ if(j_strcmp(affiliation, "owner") == 0) { #ifdef HAVE_MYSQL sql_add_affiliate(room->master->sql, room,jid_full(user),TAFFIL_OWNER.code); #endif add_affiliate(room->owner, user, NULL); } else if(j_strcmp(affiliation, "admin") == 0) { #ifdef HAVE_MYSQL sql_add_affiliate(room->master->sql, room,jid_full(user),TAFFIL_ADMIN.code); #endif add_affiliate(room->admin, user, NULL); } else if(j_strcmp(affiliation, "member") == 0) { #ifdef HAVE_MYSQL sql_add_affiliate(room->master->sql, room,jid_full(user),TAFFIL_MEMBER.code); #endif add_affiliate(room->member, user, NULL); if(room->invitation == 1 && !in_room(room, user)) { x = xmlnode_new_tag("x"); xmlnode_put_attrib(x, "xmlns", NS_MUC_USER); invite = xmlnode_insert_tag(x, "invite"); xmlnode_put_attrib(invite, "to", ujid); xmlnode_insert_cdata(xmlnode_insert_tag(invite, "reason"), "Added as a member", -1); con_room_send_invite(sender, x); xmlnode_free(x); } } else if(j_strcmp(affiliation, "outcast") == 0) { data = xmlnode_new_tag("reason"); from = g_hash_table_lookup(room->remote, jid_full(jid_fix(by))); if(reason == NULL) { xmlnode_insert_cdata(data, "None given", -1); } else { xmlnode_insert_cdata(data, reason, -1); } if(from != NULL) { xmlnode_put_attrib(data, "actor", jid_full(jid_user(from->realid))); xmlnode_put_attrib(data, "nick", from->localid->resource); } else { xmlnode_put_attrib(data, "actor", jid_full(jid_fix(by))); } #ifdef HAVE_MYSQL sql_add_affiliate(room->master->sql, room,jid_full(user),TAFFIL_OUTCAST.code); #endif add_affiliate(room->outcast, user, data); } if(room->persistent == 1) xdb_room_lists_set(room); return; } void add_role(GHashTable *hash, cnu user) { char *key; key = j_strdup(jid_full(user->realid)); log_debug(NAME, "[%s] About to add role [%s]", FZONE, key); g_hash_table_insert(hash, key, (void*)user); } void revoke_role(GHashTable *hash, cnu user) { char *key; key = jid_full(user->realid); log_debug(NAME, "[%s] About to revoke role [%s]", FZONE, key); g_hash_table_remove(hash, key); } void change_role(char *role, cnu sender, jid user, char *reason) { char *result; cnr room; cnu data; jid userid; trole current; xmlnode next, node, userlist; log_debug(NAME, "[%s] Role change request - %s to %s", FZONE, jid_full(user), role); if(role == NULL || user == NULL) { log_debug(NAME, "[%s] Missing attributes", FZONE); return; } room = sender->room; data = g_hash_table_lookup(room->remote, jid_full(user)); if(data == NULL) { if(user->resource == NULL) { userlist = get_roster(room, user); if(userlist != NULL) { for(node = xmlnode_get_firstchild(userlist); node != NULL; node = next) { next = xmlnode_get_nextsibling(node); result = xmlnode_get_attrib(node, "jid"); userid = jid_new(xmlnode_pool(node), result); change_role(role, sender, userid, reason); } } else { log_debug(NAME, "[%s] User not found", FZONE); } return; } else { log_debug(NAME, "[%s] User not found", FZONE); return; } } current = role_level(room, user); /* if not changing role, just return */ if(j_strcmp(current.msg, role) == 0) { log_debug(NAME, "[%s] Role not changing", FZONE); update_presence(data); return; } /* Clear any old roles */ if(j_strcmp(role, "moderator") != 0) { revoke_role(room->moderator, data); } if(j_strcmp(role, "participant") != 0) { revoke_role(room->participant, data); } /* Update to new role */ if(j_strcmp(role, "moderator") == 0) { add_role(room->moderator, data); log_debug(NAME, "[%s] Added Moderator", FZONE); } else if(j_strcmp(role, "participant") == 0) { add_role(room->participant, data); log_debug(NAME, "[%s] Added Participant", FZONE); } else if(j_strcmp(role, "none") == 0) { if(reason == NULL) { reason = pstrdup(user->p, "None given"); } log_debug(NAME, "[%s] Call kick routine with reason %s", FZONE, reason); data->leaving = 1; adm_user_kick(sender, data, reason); return; } update_presence(data); return; } jabber-muc-0.8/src/conference_user.c0000664000175000017500000005064011160655601016756 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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(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(user->realid)); /* Update affiliate info */ #ifdef HAVE_MYSQL sql_add_affiliate(room->master->sql, room,jid_full(user->realid),TAFFIL_ADMIN.code); #endif 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)); #ifdef HAVE_MYSQL sql_add_affiliate(room->master->sql, room,jid_full(user->realid),TAFFIL_MEMBER.code); #endif 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; } int _con_user_history_send(cnu to, xmlnode node) { if(to == NULL || node == NULL) { return 0; } xmlnode_put_attrib(node, "to", jid_full(to->realid)); deliver(dpacket_new(node), NULL); return 1; } 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((from->localid->resource != NULL) || (j_strcmp(xmlnode_get_attrib(from->presence,"type"),"unavailable") != 0)) //when the last presence received is not of type unavailable, the users didn't ask to leave, so we must create a new presence packet { 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(from->localid->resource != 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); } if(jid_cmp(to->realid,from->realid)==0) //own presence { add_status_code(result, STATUS_MUC_OWN_PRESENCE); } deliver(dpacket_new(result), NULL); xmlnode_free(node); } /* if there's a new nick, broadcast that too */ if(from->localid->resource != 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); if((jid_cmp(to->realid,from->realid)==0) && (xmlnode_get_attrib(from->nick,"old")==NULL)) //own presence to a joining user { //we must add the other status codes add_room_status_codes(node,from->room); } /* Hide x:delay, not needed */ element = xmlnode_get_tag(node, "x?xmlns=jabber:x:delay"); if(element) xmlnode_hide(element); /* Hide urn:xmpp:delay, not needed */ element = xmlnode_get_tag(node, "delay?xmlns=urn:xmpp:delay"); if(element) xmlnode_hide(element); xmlnode_put_attrib(node, "to", jid_full(to->realid)); xmlnode_put_attrib(node, "from", jid_full(from->localid)); if(jid_cmp(to->realid,from->realid)==0) //own presence { add_status_code(node, STATUS_MUC_OWN_PRESENCE); } deliver(dpacket_new(node), NULL); } } void con_user_nick(cnu user, char *nick, xmlnode data) { xmlnode node; char *status, *reason, *actor, *resource; 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; resource = user->localid->resource; jid_set(user->localid, nick, JID_RESOURCE); if (nick != NULL) // if this is not a leave, we update the hash_table with the new nick as the key before sending the presence to everyone { g_hash_table_remove(user->room->local, resource); g_hash_table_insert(user->room->local, user->localid->resource, (void*)user); } deliver__flag = 0; g_hash_table_foreach(room->local, _con_user_nick, (void*)user); deliver__flag = 1; if (nick == NULL) //if this is a leave, we just remove the nick from the hashtable, we must do this after sending the presence to everyone (otherwise this user won't be notified that he has left the room) { g_hash_table_remove(user->room->local, resource); } deliver(NULL, NULL); /* send nick change notice if available */ if(room->note_rename != NULL && nick != NULL && xmlnode_get_attrib(node, "old") != NULL && *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; xmlnode element; /* mirror */ if(from == to) return; node = add_extended_presence(from, to, NULL, NULL, NULL, NULL); xmlnode_put_attrib(node, "to", jid_full(to->realid)); xmlnode_put_attrib(node, "from", jid_full(from->localid)); /* Hide x:delay, not needed */ element = xmlnode_get_tag(node, "x?xmlns=jabber:x:delay"); if(element) xmlnode_hide(element); /* Hide urn:xmpp:delay, not needed */ element = xmlnode_get_tag(node, "delay?xmlns=urn:xmpp:delay"); if(element) xmlnode_hide(element); deliver(dpacket_new(node), NULL); } void con_user_enter(cnu user, char *nick, int created) { xmlnode node; xmlnode message; xmlnode p_x_history; int h, tflag = 0; cnr room = user->room; int num_stanzas = 0; int max_stanzas; int max_chars; int max_chars_stanzas = 0; int seconds; int since = -1; char *since_str; struct tm *since_tm; int num_chars = 0; user->localid = jid_new(user->p, jid_full(room->id)); jid_set(user->localid, nick, JID_RESOURCE); room->count++; #ifdef HAVE_MYSQL sql_update_nb_users(room->master->sql, room); #endif log_debug(NAME, "[%s] officiating user %s in room (created = %d) %s as %s", FZONE, jid_full(user->realid), created, jid_full(room->id), nick); /* Update my roster with current users */ g_hash_table_foreach(room->local, _con_user_enter, (void*)user); /* 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(room->description != NULL && *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); } /* Switch to queue mode */ deliver__flag = 0; p_x_history = xmlnode_get_tag(user->presence,"x/history"); log_debug(NAME,"x->maxstanzas: %i",j_atoi(xmlnode_get_attrib(p_x_history,"maxstanzas"),-1)); log_debug(NAME,"x->maxchars: %i",j_atoi(xmlnode_get_attrib(p_x_history,"maxchars"),-1)); /* loop through history and send back */ if(room->master->history > 0) { max_stanzas = j_atoi(xmlnode_get_attrib(p_x_history,"maxstanzas"),room->master->history); max_chars = j_atoi(xmlnode_get_attrib(p_x_history,"maxchars"),-1); seconds = j_atoi(xmlnode_get_attrib(p_x_history,"seconds"),-1); since_str = xmlnode_get_attrib(p_x_history,"since"); if (since_str != NULL) { since_tm = malloc(sizeof(struct tm)); bzero(since_tm,sizeof(struct tm)); /* calc unix time */ strptime(since_str,"%Y-%m-%dT%T%z",since_tm); since = mktime(since_tm); free(since_tm); } if (max_stanzas > room->master->history) max_stanzas = room->master->history; if (max_chars>0) { /* loop (backwards) through history to get num of stanzas to reach num_chars */ h = (room->hlast + room->master->history - max_stanzas) % room->master->history; while(1) { if (room->history[h] != NULL) num_chars += room->history[h]->content_length; if (num_chars <= max_chars) max_chars_stanzas++; else break; if (h==0) h = room->master->history; h--; if (h == room->hlast) break; } if (max_chars_stanzas < max_stanzas) // choose the smaller one max_stanzas = max_chars_stanzas; } if (max_chars == 0) max_stanzas = 0; log_debug(NAME, "room->hlast: %i, room->master->history: %i, maxstanzas: %i", room->hlast, room->master->history, max_stanzas); h = (room->hlast + room->master->history - max_stanzas) % room->master->history; while(1) { log_debug(NAME, "h: %i",h); if (num_stanzas >= max_stanzas) break; h++; if(h == room->master->history) h = 0; if (room->history[h] != NULL) { /* skip messages that older than requested */ if (!(seconds >= 0 && (time(NULL) - room->history[h]->timestamp) > seconds) && !(since >= 0 && room->history[h]->timestamp < since)) { num_stanzas +=_con_user_history_send(user, xmlnode_dup(room->history[h]->x)); if(xmlnode_get_tag(room->history[h]->x,"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 && *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", to->localid->resource); 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) { if(to == NULL || from == NULL || node == NULL) { return; } xmlnode_put_attrib(node, "to", jid_full(to->realid)); if(xmlnode_get_attrib(node, "cnu") != NULL) xmlnode_hide_attrib(node, "cnu"); xmlnode_put_attrib(node, "from", jid_full(from->localid)); deliver(dpacket_new(node), NULL); } void con_user_zap(cnu user, xmlnode data) { cnr room; char *reason; char *status; char *nick; char *key; if(user == NULL || data == NULL) { log_warn(NAME, "[%s]: 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"); nick = xmlnode_get_attrib(data, "actnick"); 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) { log_debug(NAME, "[%s] Removing entry from local list, and sending unavailable presence", FZONE); con_user_nick(user, NULL, data); /* sends unavailable */ room->count--; #ifdef HAVE_MYSQL sql_update_nb_users(room->master->sql, room); #endif /* send departure notice if available*/ if(room->note_leave != NULL && *room->note_leave != '\0') { if(reason != NULL) { if(j_strcmp(status, STATUS_MUC_KICKED) == 0) { if(nick != NULL) { con_room_send(room,jutil_msgnew("groupchat",NULL,NULL,spools(user->p, xmlnode_get_attrib(user->nick,"old")," ",room->note_leave,": [Kicked by ", nick, "] ", 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,": [Kicked by a user] ", reason, user->p)), SEND_LEGACY); } } else if(j_strcmp(status, STATUS_MUC_BANNED) == 0) { if(nick != NULL) { con_room_send(room,jutil_msgnew("groupchat",NULL,NULL,spools(user->p,xmlnode_get_attrib(user->nick,"old")," ",room->note_leave,": [Banned by ", nick ,"] ", 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,": [Banned by a user] ", 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] Removing from remote list and un-alloc cnu", FZONE); g_hash_table_remove(room->remote, jid_full(user->realid)); } jabber-muc-0.8/src/mysql.c0000664000175000017500000002654611163505600014762 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2007 Gregoire Menuel * * * 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" #ifdef HAVE_MYSQL # include # include //Size of the buffer used to escape characters #define BUFFER_SIZE 2049 //Size given to mysql_real_escape_char, BUFFER_SIZE must be at least BUFFER_CHAR_SIZE*2+1 #define BUFFER_CHAR_SIZE 1024 static char escaped[BUFFER_SIZE] = ""; /* Initialize a mysql instance */ mysql sql_mysql_init(cni master, xmlnode config){ xmlnode node; mysql mysql = malloc(sizeof(_mysql)); mysql->mysql=mysql_init(NULL); mysql->host = NULL; mysql->user = NULL; mysql->pass = NULL; mysql->database = NULL; if ((node = xmlnode_get_tag(config, "host")) != NULL) { mysql->host = xmlnode_get_data(node); } if ((node = xmlnode_get_tag(config, "user")) != NULL) { mysql->user = xmlnode_get_data(node); } if ((node = xmlnode_get_tag(config, "pass")) != NULL) { mysql->pass = xmlnode_get_data(node); } if ((node = xmlnode_get_tag(config, "database")) != NULL) { mysql->database = xmlnode_get_data(node); } mysql->port=0; mysql->flag=0; mysql->socket=NULL; if (sql_mysql_connect(mysql)==0) { mysql_close(mysql->mysql); free(mysql); return NULL; } return mysql; } /* establishe a connection with a mysql server */ int sql_mysql_connect(mysql mysql) { if (mysql == NULL) return 0; if (mysql_real_connect(mysql->mysql,mysql->host,mysql->user,mysql->pass,mysql->database,mysql->port,mysql->socket,mysql->flag)==NULL){ log_warn(NAME, "[%s] ERR: Unable to connect to the database", FZONE); return 0; } return 1; } /* close the connection with the server and destroy the mysql object */ void sql_mysql_close(mysql sql) { if (sql == NULL) return; mysql_close(sql->mysql); free(sql); sql = NULL; } /* execute a sql query */ static int sql_mysql_execute(mysql sql, char* query) { int ret = 0; unsigned int query_err=0; if (sql == NULL) return 1; log_debug(NAME,"[%s] Trying to execute query : %s", FZONE, query); ret = mysql_query(sql->mysql,query); if (ret) { query_err = mysql_errno(sql->mysql); if (query_err == CR_SERVER_LOST || query_err == CR_SERVER_GONE_ERROR) { log_warn(NAME, "[%s] ERR: sql connection lost, trying to reconnect", FZONE); sql_mysql_connect(sql); ret = mysql_query(sql->mysql, query); if (ret == 0){ log_warn(NAME, "[%s] sql connection has been lost and reestablished", FZONE); } } } if (ret != 0){ //an error occured log_warn(NAME, "[%s] ERR: mysql query (%s) failed: %s", FZONE, query, mysql_error(sql->mysql)); return 1; } return 0; } /* escape a string using mysql_real_escape. str is the string to escape, buffer is a previously created buffer, limit is the size of the buffer. * Return: the buffer */ char * _sql_escape_string(MYSQL * mysql, char * str, char * buffer, int limit) { int len; if (str == NULL) return NULL; len = strlen(str); if (len > limit) { len = limit; } mysql_real_escape_string(mysql, buffer, str, len); return buffer; } /* clear both tables (rooms and rooms_lists) */ void sql_clear_all(mysql sql) { if (sql == NULL) return; sql_mysql_execute(sql,"DELETE FROM rooms"); sql_mysql_execute(sql,"DELETE FROM rooms_lists"); } /* update a field in the rooms table */ void sql_update_field(mysql sql, const char * roomId,const char* field, const char * value){ pool p=NULL; spool s=NULL; if (sql == NULL) return; p = pool_new(); s = spool_new(p); spool_add(s, "UPDATE rooms SET "); spool_add(s, (char*)field); spool_add(s, "='"); spool_add(s, _sql_escape_string(sql->mysql, (char*)value, escaped, BUFFER_CHAR_SIZE)); spool_add(s, "' WHERE jid='"); spool_add(s, _sql_escape_string(sql->mysql, (char*)roomId, escaped, BUFFER_CHAR_SIZE)); spool_add(s, "'"); sql_mysql_execute(sql,spool_print(s)); pool_free(p); } /* update the users number of a room */ void sql_update_nb_users(mysql sql, cnr room){ if (sql == NULL) return; char users[10]=""; sprintf(users,"%d",room->count); sql_update_field(sql, jid_full(room->id), "users", users); } /* insert a room config in the rooms table */ void sql_insert_room_config(mysql sql, cnr room) { char * x=NULL; char buffer[10]=""; pool p = NULL; spool s = NULL; if ((room == NULL) || (room->id == NULL) || (sql == NULL)) { return; } log_debug(NAME, "[%s] Inserting SQL record for room %s", FZONE, jid_full(room->id)); p = pool_new(); s = spool_new(p); spool_add(s, "INSERT INTO rooms (`jid`, `name`, `desc`, `topic`, `users`, `public`, `open`, `secret`) VALUES ('"); spool_add(s, _sql_escape_string(sql->mysql, jid_full(room->id), escaped, BUFFER_CHAR_SIZE)); spool_add(s, "', '"); spool_add(s, _sql_escape_string(sql->mysql, room->name, escaped, BUFFER_CHAR_SIZE)); spool_add(s, "', '"); spool_add(s, _sql_escape_string(sql->mysql, room->description, escaped, BUFFER_CHAR_SIZE)); spool_add(s, "', '"); if ((x=xmlnode_get_attrib(room->topic,"subject")) != NULL) { spool_add(s, _sql_escape_string(sql->mysql, x, escaped, BUFFER_CHAR_SIZE)); } spooler(s, "', '0', '", itoa(room->public, buffer), "', '",s); if (room->invitation || room->locked){ spool_add(s, "0"); } else { spool_add(s, "1"); } spool_add(s, "', '"); if (room->secret!=NULL){ spool_add(s, _sql_escape_string(sql->mysql, room->secret, escaped, BUFFER_CHAR_SIZE)); } spool_add(s, "')"); sql_mysql_execute(sql,spool_print(s)); pool_free(p); } /* callback of the hash table lookup of the function sql_insert_all */ void _sql_insert_room_config(gpointer key, gpointer data, gpointer arg) { cnr room=(cnr) data; mysql sql = (mysql) arg; sql_insert_room_config(sql,room); } /* insert all rooms config in the db */ void sql_insert_all(mysql sql, GHashTable * rooms) { if (sql == NULL) return; g_hash_table_foreach(rooms, _sql_insert_room_config, (void*)sql); } /* update a room config (all fields) */ void sql_update_room_config(mysql sql, cnr room) { char buffer[10]=""; pool p = NULL; spool s = NULL; if (sql == NULL) return; log_debug(NAME, "[%s] Updating SQL record for room %s", FZONE, jid_full(room->id)); p = pool_new(); s = spool_new(p); spool_add(s, "UPDATE rooms SET `name`='"); spool_add(s, _sql_escape_string(sql->mysql, room->name, escaped, BUFFER_CHAR_SIZE)); spool_add(s, "', `desc`='"); spool_add(s, _sql_escape_string(sql->mysql, room->description, escaped, BUFFER_CHAR_SIZE)); spooler(s, "', `public`='", itoa(room->public, buffer), "', `open`='",s); //check if anyone can enter the room if (room->invitation || room->locked){ spool_add(s, "0"); } else { spool_add(s, "1"); } spool_add(s, "', secret='"); if (room->secret!=NULL){ spool_add(s, _sql_escape_string(sql->mysql, room->secret, escaped, BUFFER_CHAR_SIZE)); } spool_add(s, "' WHERE `jid`='"); spool_add(s, _sql_escape_string(sql->mysql, jid_full(room->id), escaped, BUFFER_CHAR_SIZE)); spool_add(s, "'"); sql_mysql_execute(sql,spool_print(s)); pool_free(p); } /* destroy a room */ void sql_destroy_room(mysql sql, char * room_jid) { pool p = NULL; static char eroom[1025] = ""; if (sql == NULL) return; p = pool_new(); _sql_escape_string(sql->mysql, room_jid, eroom, 1025); sql_mysql_execute(sql,spools(p, "DELETE FROM rooms WHERE jid='", eroom, "' LIMIT 1",p)); sql_mysql_execute(sql,spools(p, "DELETE FROM rooms_lists WHERE jid_room='", eroom, "'",p)); pool_free(p); } /* struct used to pass arguments to the functions (for the owner, admin, member and outcast lists) */ typedef struct s_lists { int affil; cnr room; mysql sql; pool p; } *lists, _lists; /* return the string associated with an affiliation code */ char * _sql_affil_string(int affil) { if (affil==TAFFIL_OWNER.code) { return "owner"; } else if (affil==TAFFIL_ADMIN.code) { return "administrator"; } else if (affil == TAFFIL_MEMBER.code) { return "member"; } else if (affil == TAFFIL_OUTCAST.code) { return "outcast"; } return NULL; } /* add a user to the list in the db */ void sql_add_affiliate(mysql sql,cnr room,char * userid,int affil) { pool p = NULL; static char euser[1025] = ""; static char eroom[1025] = ""; char * affil_s = NULL; if (sql == NULL) return; affil_s = _sql_affil_string(affil); if (affil_s == NULL) { return; } _sql_escape_string(sql->mysql, userid, euser, 1025); _sql_escape_string(sql->mysql, jid_full(room->id), eroom, 1025); log_debug(NAME, "[%s] Add affiliation in db for user %s in room %s", FZONE, userid,jid_full(room->id)); p = pool_new(); sql_mysql_execute(sql, spools(p, "INSERT INTO rooms_lists (jid_room, jid_user, affil) VALUES ('",eroom, "', '", euser, "', '", affil_s, "')", p)); pool_free(p); } void _sql_add_list(gpointer key, gpointer data, gpointer arg) { lists list = (lists) arg; sql_add_affiliate(list->sql, list->room,(char *) key, list->affil); } /* insert an affiliation list in the db */ void _sql_add_lists(mysql sql, cnr room, GHashTable * list_users, int affil) { log_debug(NAME, "[%s] Inserting users for room %s and affiliation %s", FZONE, jid_full(room->id), _sql_affil_string(affil)); lists list = (lists)malloc(sizeof(_lists)); list->affil=affil; list->sql=sql; list->p = pool_new(); list->room=room; g_hash_table_foreach(list_users, _sql_add_list, (void*)list); pool_free(list->p); free(list); } /* add all lists of a room in the db */ void sql_add_room_lists(mysql sql, cnr room) { if (sql == NULL) return; _sql_add_lists(sql, room, room->owner, TAFFIL_OWNER.code); _sql_add_lists(sql, room, room->member, TAFFIL_MEMBER.code); _sql_add_lists(sql, room, room->admin, TAFFIL_ADMIN.code); _sql_add_lists(sql, room, room->outcast, TAFFIL_OUTCAST.code); } void _sql_add_all_lists(gpointer key, gpointer data, gpointer arg) { sql_add_room_lists((mysql) arg, (cnr) data); } /* insert all lists of all rooms in the db */ void sql_insert_lists(mysql sql, GHashTable * rooms) { if (sql == NULL) return; sql_mysql_execute(sql, "DELETE FROM rooms_lists"); g_hash_table_foreach(rooms, _sql_add_all_lists, (void*)sql); } /* remove the bd entry of an affiliated user */ void sql_remove_affiliate(mysql sql,cnr room,jid userid) { pool p = NULL; static char euser[1025] = ""; static char eroom[1025] = ""; if (sql == NULL) return; _sql_escape_string(sql->mysql, jid_full(userid), euser, 1025); _sql_escape_string(sql->mysql, jid_full(room->id), eroom, 1025); log_debug(NAME, "[%s] Remove affiliation in db for user %s in room %s", FZONE, jid_full(userid),jid_full(room->id)); p = pool_new(); sql_mysql_execute(sql, spools(p, "DELETE FROM rooms_lists WHERE jid_room='",eroom,"' AND jid_user='", euser,"'", p)); pool_free(p); } #endif jabber-muc-0.8/src/conference.c0000664000175000017500000010425211160655601015717 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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" 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, (char *) 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; /* Hide empty rooms when requested */ if(room->master->hideempty && room->count < 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; char users[10]; char maxu[10]; xmlnode x; if(room == NULL) { log_warn(NAME, "[%s] Aborting: NULL room %s", FZONE, (char *) 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; /* Hide empty rooms when requested */ if(room->master->hideempty && room->count < 1) return; x = xmlnode_insert_tag(jp->iq, "item"); 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)); } char* _generate_unique_id(jid jid) { time_t current_time; char buffer[150]; char jid_node[21];//not the full jid, just a part of the node char* ret; strncpy(jid_node,jid->user,21); if (jid_node[20]!='\0') { jid_node[20]='\0'; } current_time = time(NULL); sprintf(buffer,"%s%i%i",jid_node,(int)current_time,rand()); ret=malloc((strlen(buffer)+1)*sizeof(char)); sprintf(ret,"%s",buffer); printf("Generating : %s\n",ret); return ret; } /* Handle packects send directly to the server (jid with no username) */ void con_server(cni master, jpacket jp) { char *str; xmlnode x; int start, status; 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)) { 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); return; } } 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); 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); return; } else if(NSCHECK(jp->iq,NS_TIME)) { /* Compliant with JEP-0090 */ log_debug(NAME, "[%s] Server packet - Time Request", FZONE); iq_get_time(jp); return; } else if(NSCHECK(jp->iq,NS_VERSION)) { /* Compliant with JEP-0092 */ log_debug(NAME, "[%s] Server packet - Version Request", FZONE); iq_get_version(jp); return; } 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")); iq_populate_browse(jp->iq); /* Walk room hashtable and report available rooms */ g_hash_table_foreach(master->rooms, con_server_browsewalk, (void*)jp); deliver(dpacket_new(jp->x), NULL); return; } else if(NSCHECK(jp->iq, NS_DISCO_INFO)) { log_debug(NAME, "[%s] Server packet - Disco Info Request", FZONE); if (xmlnode_get_attrib(jp->iq,"node")!= NULL){ jutil_error(jp->x, TERROR_UNAVAIL); deliver(dpacket_new(jp->x),NULL); return; } 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_MUC_UNIQUE); 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); xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_PING); deliver(dpacket_new(jp->x),NULL); return; } else if(NSCHECK(jp->iq, NS_DISCO_ITEMS)) { log_debug(NAME, "[%s] Server packet - Disco Items Request", FZONE); if (xmlnode_get_attrib(jp->iq,"node")!= NULL){ jutil_error(jp->x, TERROR_UNAVAIL); deliver(dpacket_new(jp->x),NULL); return; } 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); return; } 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); return; } 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); return; } else if(NSCHECK(jp->iq,NS_MUC_UNIQUE)) { //generate a unique identifier and send it back log_debug(NAME, "[%s] Server packet - Unique Request", FZONE); str=_generate_unique_id(jp->from); jutil_iqresult(jp->x); xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"unique"),"xmlns",NS_MUC_UNIQUE); jpacket_reset(jp); xmlnode_insert_cdata(jp->iq,str,strlen(str)); free(str); deliver(dpacket_new(jp->x),NULL); return; } else if(NSCHECK(jp->iq,NS_PING)) { log_debug(NAME, "[%s] Server packet - Ping", FZONE); jutil_iqresult(jp->x); deliver(dpacket_new(jp->x),NULL); return; } } //if we're here, it's because we don't know this packet 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); if((jid_fix(jp->from) == NULL) || (jid_fix(jp->to) == NULL)) { log_debug(NAME, "[%s] ignoring packets, invalid to or from", FZONE); return; } g_mutex_lock(master->lock); /* first, handle all packets just to the server (browse, vcard, ping, etc) */ if(jp->to->user == NULL) { con_server(master, jp); g_mutex_unlock(master->lock); 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); g_mutex_unlock(master->lock); 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, 0); xdata_room_config(room,g_hash_table_lookup(room->remote, jid_full(jid_fix(jp->from))),1,jp->x); g_mutex_unlock(master->lock); xmlnode_free(jp->x); return; } else if(jp->type == JPACKET_IQ && jpacket_subtype(jp) == JPACKET__SET && NSCHECK(jp->iq, NS_MUC_OWNER) && xmlnode_get_tag(jp->iq,"x?xmlns=jabber:x:data")) { //create instant room room = con_room_new(master, jid_user(jp->to), jp->from, NULL, NULL, 1, 0); //instant room are always non browsable room->public=0; jutil_iqresult(jp->x); deliver(dpacket_new(jp->x),NULL); g_mutex_unlock(master->lock); 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); g_mutex_unlock(master->lock); 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); g_mutex_unlock(master->lock); xmlnode_free(jp->x); return; } else { if(master->dynamic == -1) room = con_room_new(master, jid_user(jp->to), jp->from, NULL, NULL, 1, 1); else room = con_room_new(master, jid_user(jp->to), jp->from, NULL, NULL, 1, 0); /* fall through, so the presence goes to the room like normal */ created = 1; } } /* get the sending user entry, if any */ log_debug(NAME, "[%s] %s", FZONE, jid_full(jid_fix(jp->from))); 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) { /* allow errors if they aren't delivery related errors */ node = xmlnode_get_tag(jp->x, "error"); for((node != NULL) && (node = xmlnode_get_firstchild(node)); node != NULL; node = xmlnode_get_nextsibling(node)) { if(!NSCHECK(node, NS_STANZA)) continue; s = xmlnode_get_name(node); if ((j_strcmp(s, "gone") == 0) || (j_strcmp(s, "item-not-found") == 0) || (j_strcmp(s, "recipient-unavailable") == 0) || (j_strcmp(s, "redirect") == 0) || (j_strcmp(s, "remote-server-not-found") == 0) || (j_strcmp(s, "remote-server-timeout") == 0) || (j_strcmp(s, "service-unavailable")) || (j_strcmp(s, "jid-malformed"))) { 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); xmlnode_free(jp->x); g_mutex_unlock(master->lock); return; } } } else { log_debug(NAME, "[%s] Error Handler: No cnu/lid found for user", FZONE); xmlnode_free(jp->x); g_mutex_unlock(master->lock); return; } } } /* Block message from users not already in the room */ if(jp->type == JPACKET_MESSAGE && u == NULL) { //check if this is a decline to an invitation if (( node = xmlnode_get_tag(xmlnode_get_tag(jp->x,"x?xmlns=http://jabber.org/protocol/muc#user"),"decline")) != NULL); if (node!=NULL) { con_room_forward_decline(room,jp,node); g_mutex_unlock(master->lock); return; } log_debug(NAME, "[%s] Blocking message from outsider (%s)", FZONE, jid_full(jp->to)); jutil_error(jp->x, TERROR_MUC_OUTSIDE); g_mutex_unlock(master->lock); deliver(dpacket_new(jp->x),NULL); return; } /* several things use this field below as a flag */ if(jp->type == JPACKET_PRESENCE) { if(jpacket_subtype(jp) == JPACKET__INVISIBLE) { xmlnode_hide_attrib(jp->x, "type"); } 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 = g_hash_table_lookup(room->local, 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); g_mutex_unlock(master->lock); return; } /* Don't allow user if locknicks is set and resource != JID user */ if ( ((master->locknicks || room->locknicks) && (j_strcmp(jp->to->resource, jp->from->user) != 0)) && !is_sadmin(master, jp->from) ) { log_debug(NAME, "[%s] Nicknames locked - Requested nick %s doesn't match required username %s", FZONE, jp->to->resource, jp->from->user); /* Send a nickname refused error message */ jutil_error(jp->x, TERROR_MUC_NICKLOCKED); g_mutex_unlock(master->lock); deliver(dpacket_new(jp->x),NULL); 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); g_mutex_unlock(master->lock); deliver(dpacket_new(jp->x),NULL); return; } /* Nick already registered, return conflict Error */ if(is_registered(master, jid_full(jid_user(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); g_mutex_unlock(master->lock); deliver(dpacket_new(jp->x),NULL); return; } 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); g_mutex_unlock(master->lock); 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); g_mutex_unlock(master->lock); 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); g_mutex_unlock(master->lock); 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); g_mutex_unlock(master->lock); deliver(dpacket_new(jp->x),NULL); return; } /* User already in room, simply a nick change */ if(u->localid != NULL) { g_hash_table_remove(u->room->local, u->localid->resource); jid_set(u->localid, jp->to->resource, JID_RESOURCE); g_hash_table_insert(u->room->local, u->localid->resource, u); 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); g_mutex_unlock(master->lock); return; } else if(room->secret == NULL || is_sadmin(master, jp->from)) /* No password required, just go right in, or you're an sadmin */ { //the client is legacy unless we're told otherwise u->legacy = 1; for( node = xmlnode_get_firstchild(jp->x); node != NULL; node = xmlnode_get_nextsibling(node)) { if (xmlnode_get_name(node)==NULL || strcmp("x",xmlnode_get_name(node))!=0) continue; // check if the node is a "x" node if(NSCHECK(node, NS_MUC)) { /* Set legacy value to room value */ u->legacy = 0; // xmlnode_hide(node); /* Enable room defaults automatically */ if(master->roomlock == -1) { created = 0; } } } if (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); g_mutex_unlock(master->lock); return; } else if(jp->type == JPACKET_PRESENCE) /* Hopefully you are including a password, this room is locked */ { for( node = xmlnode_get_firstchild(jp->x); node != NULL; node = xmlnode_get_nextsibling(node)) { if (xmlnode_get_name(node)==NULL || strcmp("x",xmlnode_get_name(node))!=0) continue; // check if the node is a "x" node if(NSCHECK(node, NS_MUC)) { log_debug(NAME, "[%s] Password?", FZONE); if(j_strcmp(room->secret, xmlnode_get_tag_data(node, "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); g_mutex_unlock(master->lock); return; } } } } /* No password found, room is password protected. Return password error */ jutil_error(jp->x, TERROR_MUC_PASSWORD); deliver(dpacket_new(jp->x), NULL); g_mutex_unlock(master->lock); 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); g_mutex_unlock(master->lock); 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 */ } g_mutex_unlock(master->lock); 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) /* gotta have a recipient */ { jutil_error(jp->x, TERROR_NOTFOUND); deliver(dpacket_new(jp->x),NULL); } else { con_user_process(u2, u, jp); } g_mutex_unlock(master->lock); return; } /* finally, handle packets just to a room from a participant, msgs, pres, iq browse/conferencing, etc */ con_room_process(room, u, jp); g_mutex_unlock(master->lock); } /* 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; } jp = jpacket_new(dp->x); /* if the delivery failed */ if(jp == NULL || jp->to == NULL || jp->from == 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; _con_packets((void *)jp); 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; xmlnode destroy; if(room == NULL) { log_warn(NAME, "[%s] SHUTDOWN: Aborting attempt to clear %s", FZONE, (char *) key); return; } if(room->persistent == 1) xdb_room_set(room); destroy=xmlnode_new_tag("destroy"); xmlnode_insert_cdata(xmlnode_insert_tag(destroy,"reason"),"The conference component is shutting down",-1); g_hash_table_foreach(room->remote, con_room_leaveall, destroy); xmlnode_free(destroy); 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); g_mutex_free(master->lock); #ifdef HAVE_MYSQL if (master->sql != NULL) { sql_clear_all(master->sql); sql_mysql_close(master->sql); } #endif 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; time_t t = *(time_t *)arg; if(user == NULL) { log_warn(NAME, "[%s] Aborting : NULL cnu for %s", FZONE, (char *) key); return; } if(user->localid == NULL && (t - 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; time_t t = *(time_t *)arg; xmlnode node; char *user_name; log_debug(NAME, "[%s] HBTICK: Idle check for >%s<", FZONE, (char*) key); if(room == NULL) { log_warn(NAME, "[%s] Aborting : NULL cnr for %s", FZONE, (char*) key); return; } /* Perform zombie user clearout */ room->queue = g_queue_new(); g_hash_table_foreach(room->remote, _con_beat_user, &t); /* 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 && (t - room->last) > 240) { log_debug(NAME, "[%s] HBTICK: Locking room and adding %s to remove queue", FZONE, (char*) key); 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, (char*) 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 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); /* Clean the jid cache */ jid_clean_cache(); /* Check for timed out idle rooms */ if(mins % 2 == 0) { g_mutex_lock(master->lock); log_debug(NAME, "[%s] HBTICK: Idle check started", FZONE); master->queue = g_queue_new(); g_hash_table_foreach(master->rooms, _con_beat_idle, &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); g_mutex_unlock(master->lock); } /* 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); g_mutex_lock(master->lock); g_hash_table_foreach(master->rooms, _con_beat_logrotate, NULL); g_mutex_unlock(master->lock); return r_DONE; } /*** everything starts here ***/ void conference(instance i, xmlnode x) { extern jcr_instance jcr; cni master; xmlnode cfg; jid sadmin; xmlnode current; xmlnode node; 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; /* get the config */ cfg = xmlnode_get_tag(jcr->config, "conference"); 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 */ if (xmlnode_get_tag(cfg, "logdir") == NULL) { master->logsEnabled = 0; master->logdir = NULL; } else { master->logsEnabled = 1; master->logdir = xmlnode_get_tag_data(cfg, "logdir"); /* Directory where to store logs */ } master->stylesheet = xmlnode_get_tag_data(cfg, "stylesheet"); /* Stylesheet of log files */ if (xmlnode_get_tag(cfg, "logsubdirs") == NULL) { master->flatLogs = 1; } else { master->flatLogs = 0; } /* 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; /* If requested, make room nicknames locked by default */ if(xmlnode_get_tag(cfg,"locknicks")) master->locknicks = 1; else master->locknicks = 0; /* If requested, hide rooms with no participants */ if(xmlnode_get_tag(cfg,"hideempty")) master->hideempty = 1; else master->hideempty = 0; master->sadmin = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, NULL); /* 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)); g_hash_table_insert(master->sadmin, j_strdup(jid_full(jid_user(jid_fix(sadmin)))), GINT_TO_POINTER(1)); } } } master->lock = g_mutex_new(); master->loader = 1; xdb_rooms_get(master); #ifdef HAVE_MYSQL if ((node = xmlnode_get_tag(cfg, "mysql"))) { master->sql=sql_mysql_init(master,node); sql_clear_all(master->sql); sql_insert_all(master->sql, master->rooms); sql_insert_lists(master->sql, master->rooms); } #endif 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); pool_free(tp); } jabber-muc-0.8/src/admin.c0000664000175000017500000004227311163505600014700 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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" void con_get_role_list(gpointer key, gpointer data, gpointer arg) { xmlnode node; xmlnode result; taffil affiliation; trole role; jid userid; cnr room; result = (xmlnode)arg; if(result == NULL) { log_warn(NAME, "[%s] Aborting: NULL result - <%s>", FZONE, (char*) key); return; } room = (cnr)xmlnode_get_vattrib(result ,"cnr"); if(room == NULL) { log_warn(NAME, "[%s] Aborting: NULL room - <%s>", FZONE, (char*) key); return; } node = xmlnode_insert_tag(result, "item"); userid = jid_new(xmlnode_pool(node), key); xmlnode_put_attrib(node, "jid", key); affiliation = affiliation_level(room, userid); role = role_level(room, userid); xmlnode_put_attrib(node, "role", role.msg); xmlnode_put_attrib(node, "affiliation", affiliation.msg); } void con_get_affiliate_list(gpointer key, gpointer data, gpointer arg) { xmlnode node; cnr room; taffil affiliation; jid userid; char *actor; char *reason; xmlnode result = (xmlnode)arg; xmlnode item = (xmlnode)data; if(result == NULL || item == NULL) { log_warn(NAME, "[%s] Aborting: NULL attribute(s) - <%s>", FZONE, (char *) key); return; } actor = xmlnode_get_attrib(item, "actor"); reason = xmlnode_get_data(item); room = (cnr)xmlnode_get_vattrib(result ,"cnr"); node = xmlnode_insert_tag(result, "item"); userid = jid_new(xmlnode_pool(node), key); xmlnode_put_attrib(node, "jid", key); affiliation = affiliation_level(room, userid); xmlnode_put_attrib(node, "affiliation", affiliation.msg); if(reason != NULL) xmlnode_insert_cdata(xmlnode_insert_tag(node, "reason"), reason, -1); if(actor != NULL) xmlnode_insert_cdata(xmlnode_insert_tag(node, "actor"), actor, -1); } /* Generate x:data form for configuring lists */ xmlnode con_gen_list(cnr room, char *namespace, char *type) { xmlnode result; result = xmlnode_new_tag("query"); xmlnode_put_attrib(result,"xmlns", namespace); if (room == NULL) { log_warn(NAME, "[%s] NULL room attribute", FZONE); return result; } xmlnode_put_vattrib(result, "cnr", (void*)room); if(j_strcmp(type, "owner") == 0) g_hash_table_foreach(room->owner, con_get_affiliate_list, (void*)result); else if(j_strcmp(type, "admin") == 0) g_hash_table_foreach(room->admin, con_get_affiliate_list, (void*)result); else if(j_strcmp(type, "moderator") == 0) g_hash_table_foreach(room->moderator, con_get_role_list, (void*)result); else if(j_strcmp(type, "member") == 0) { log_debug(NAME, "[%s] member list size [%d]", FZONE, g_hash_table_size(room->member)); g_hash_table_foreach(room->member, con_get_affiliate_list, (void*)result); } else if(j_strcmp(type, "participant") == 0) g_hash_table_foreach(room->participant, con_get_role_list, (void*)result); else if(j_strcmp(type, "outcast") == 0) g_hash_table_foreach(room->outcast, con_get_affiliate_list, (void*)result); xmlnode_hide_attrib(result, "cnr"); return result; } void adm_user_kick(cnu user, cnu target, char *reason) { cnr room; xmlnode data; xmlnode pres; const char *status; if(user == NULL || target == NULL || reason == NULL) { log_warn(NAME, "[%s] Aborting: NULL attribute found", FZONE); return; } room = target->room; data = xmlnode_new_tag("reason"); if(is_outcast(room, target->realid)) status = STATUS_MUC_BANNED; else status = STATUS_MUC_KICKED; xmlnode_put_attrib(data, "status", status); xmlnode_put_attrib(data, "actor", jid_full(jid_user(user->realid))); xmlnode_put_attrib(data, "actnick", user->localid->resource); xmlnode_insert_cdata(data, reason, -1); pres = jutil_presnew(JPACKET__UNAVAILABLE, jid_full(target->realid), NULL); target->presence = pres; log_debug(NAME, "[%s] Kick/Ban requested. Status code=%s", FZONE, status); con_send_alert(target, reason, NULL, status); con_user_zap(target, data); return; } void con_parse_item(cnu sender, jpacket jp) { xmlnode current; xmlnode result; xmlnode node; jid target; jid from; char *xmlns; char *role; char *jabberid; char *nick; char *reason; char *affiliation; cnu user; cnr room; int error = 0; if(sender == NULL) { log_warn(NAME, "[%s] Aborting - NULL sender", FZONE); return; } user = NULL; from = sender->realid; room = sender->room; node = xmlnode_get_tag(jp->x, "query"); xmlns = xmlnode_get_attrib(node, "xmlns"); /* Check for configuration request */ if(j_strcmp(xmlns, NS_MUC_OWNER) == 0 && xmlnode_get_tag(node, "item") == NULL) { if(is_owner(room, from)) { user = g_hash_table_lookup(room->remote, jid_full(from)); if(user) { xdata_room_config(room, user, room->locked, jp->x); xmlnode_free(jp->x); } else { jutil_error(jp->x, TERROR_BAD); deliver(dpacket_new(jp->x), NULL); } return; } else { jutil_error(jp->x, TERROR_NOTALLOWED); deliver(dpacket_new(jp->x), NULL); return; } } /* Parse request for errors */ for(current = xmlnode_get_firstchild(node); current != NULL; current = xmlnode_get_nextsibling(current)) { if (xmlnode_get_name(current)==NULL) continue; /* Extract data */ jabberid = xmlnode_get_attrib(current, "jid"); nick = xmlnode_get_attrib(current, "nick"); role = xmlnode_get_attrib(current, "role"); affiliation = xmlnode_get_attrib(current, "affiliation"); reason = xmlnode_get_tag_data(current, "reason"); if(jabberid == NULL && nick == NULL && role == NULL && affiliation == NULL) { error = 1; log_debug(NAME, "[%s] Skipping - Badly formed request (%s)", FZONE, xmlnode2str(current)); insert_item_error(current, "400", "Badly formed request"); continue; } if(jpacket_subtype(jp) == JPACKET__GET) { if(jabberid == NULL && nick == NULL) { if(!is_admin(room, from)) { error = 1; log_debug(NAME, "[%s] Skipping - Insufficent level to request admin list", FZONE); insert_item_error(jp->x, "403", "Forbidden list retrieval"); continue; } if(role != NULL && affiliation != NULL) { error = 1; log_debug(NAME, "[%s] Skipping - Badly formed request (%s)", FZONE, xmlnode2str(current)); insert_item_error(jp->x, "400", "Badly formatted list request"); continue; } if(j_strcmp(affiliation, "admin") != 0 && j_strcmp(role, "participant") != 0 && j_strcmp(affiliation, "member") != 0 && j_strcmp(role, "moderator") != 0 && j_strcmp(affiliation, "outcast") != 0 && j_strcmp(affiliation, "owner") != 0) { error = 1; log_debug(NAME, "[%s] Skipping - No such list (%s)", FZONE, xmlnode2str(current)); insert_item_error(jp->x, "400", "No such list"); continue; } if(j_strcmp(affiliation, "admin") == 0 || j_strcmp(affiliation, "owner") == 0) { if(j_strcmp(xmlns, NS_MUC_ADMIN) != 0) { error = 1; log_debug(NAME, "[%s] Skipping - Badly formed namespace : %s (%s)", FZONE, xmlns,xmlnode2str(current)); insert_item_error(jp->x, "400", "Invalid Namespace"); continue; } if(!is_owner(room, from)) { error = 1; log_debug(NAME, "[%s] Skipping - Insufficent level to request %s list", FZONE, affiliation); insert_item_error(jp->x, "403", "Forbidden list retrieval"); continue; } } else if(j_strcmp(xmlns, NS_MUC_ADMIN) != 0) { error = 1; log_debug(NAME, "[%s] Skipping - Badly formed namespace (%s)", FZONE, xmlnode2str(current)); insert_item_error(jp->x, "400", "Invalid Namespace"); continue; } } else { error = 1; log_debug(NAME, "[%s] Skipping - Badly formed request (%s)", FZONE, xmlnode2str(current)); insert_item_error(jp->x, "400", "Badly formed request - extra attributes found"); continue; } } else if(jpacket_subtype(jp) == JPACKET__SET) { if(role == NULL && affiliation == NULL) { error = 1; log_debug(NAME, "[%s] Skipping - no role or affiliation given (%s)", FZONE, xmlnode2str(current)); insert_item_error(jp->x, "400", "Badly formed request - no role or affiliation attribute"); continue; } if(nick == NULL && jabberid == NULL) { error = 1; log_debug(NAME, "[%s] Skipping - no jid or nick given (%s)", FZONE, xmlnode2str(current)); insert_item_error(jp->x, "400", "Badly formed request - no nick or jid attribute"); continue; } if(jabberid == NULL) { user = g_hash_table_lookup(room->local, nick); if(user) { jabberid = jid_full(user->realid); } else { error = 1; log_debug(NAME, "[%s] Skipping - can't find jid (%s)", FZONE, xmlnode2str(current)); insert_item_error(jp->x, "400", "Nick not present in room"); continue; } } target = jid_new(jp->p, jabberid); if((target == NULL || target->user == NULL) && role != NULL) { error = 1; log_debug(NAME, "[%s] Skipping - Bad jid (%s)", FZONE, jabberid); insert_item_error(jp->x, "400", "Badly formed JID"); continue; } if(role != NULL && affiliation != NULL) { /* Check requesting user has minimum affiliation level */ error = 1; log_debug(NAME, "[%s] Skipping - Attempting to change role and affiliation (%s)", FZONE, jabberid); insert_item_error(jp->x, "400", "Bad request - trying to change role and affiliation"); continue; } /* Affiliation changes */ if(affiliation != NULL) { if(!is_admin(room, from)) { /* Check requesting user has minimum affiliation level */ error = 1; log_debug(NAME, "[%s] Skipping - affiliation role requested by non-admin(%s)", FZONE, jabberid); insert_item_error(jp->x, "403", "Forbidden - No affiliation requested by non-admin"); continue; } else if(!is_owner(room, from) && is_admin(room, target)) { /* Stop admins altering other admins */ error = 1; log_debug(NAME, "[%s] Skipping - affiliation role requested by non-admin(%s)", FZONE, jabberid); insert_item_error(jp->x, "403", "Forbidden - No affiliation request between admins"); continue; } else if(j_strcmp(affiliation, "owner") == 0) { if(!is_owner(room, from)) { error = 1; log_debug(NAME, "[%s] Skipping - affiliation role requested by non-owner(%s)", FZONE, jabberid); insert_item_error(jp->x, "403", "Forbidden - Owner requested"); continue; } } else if(j_strcmp(affiliation, "admin") == 0) { if(!is_owner(room, from)) { error = 1; log_debug(NAME, "[%s] Skipping - affiliation role requested by non-owner(%s)", FZONE, jabberid); insert_item_error(jp->x, "403", "Forbidden - Admin requested"); continue; } } else if(j_strcmp(affiliation, "outcast") == 0) { if(is_admin(room, target)) { error = 1; log_debug(NAME, "[%s] Skipping - affiliation role requested by non-owner(%s)", FZONE, jabberid); insert_item_error(jp->x, "403", "Forbidden - Admin requested"); continue; } } else if ((j_strcmp(affiliation, "owner") != 0) && (is_owner(room, target)) && (g_hash_table_size(room->owner) == 1)) { //trying to remove the last owner error = 1; log_debug(NAME, "[%s] Skipping - room %s must have at least one owner", FZONE, room->name); insert_item_error(jp->x, "409", "Conflict - Can't remove last admin"); continue; } else if(j_strcmp(affiliation, "member") != 0 && j_strcmp(affiliation, "none") != 0) { error = 1; log_debug(NAME, "[%s] Skipping - affiliation unknown(%s/%s)", FZONE, jabberid, affiliation); insert_item_error(jp->x, "400", "Unknown affiliation"); continue; } } /* role changes */ if(role != NULL) { if(!is_admin(room, from) && !is_moderator(room, from)) { error = 1; log_debug(NAME, "[%s] Skipping - Forbidden role change (%s)", FZONE, jabberid); insert_item_error(jp->x, "403", "Forbidden role change request by non-admin"); continue; } else if(j_strcmp(role, "moderator") == 0) { if(!is_admin(room, from)) { error = 1; log_debug(NAME, "[%s] Skipping - Forbidden moderater request (%s)", FZONE, jabberid); insert_item_error(jp->x, "403", "Forbidden moderator request by non-admin"); continue; } } else if(j_strcmp(role, "none") == 0) { if(is_admin(room, target) || is_moderator(room, target)) { error = 1; log_debug(NAME, "[%s] Skipping - Forbidden kick request (%s)", FZONE, jabberid); insert_item_error(jp->x, "403", "Forbidden Kick request against admin"); continue; } } else if(j_strcmp(role, "participant") != 0 && j_strcmp(role, "visitor") != 0) { error = 1; log_debug(NAME, "[%s] Skipping - role unknown(%s)", FZONE, jabberid); insert_item_error(jp->x, "400", "Unknown role"); continue; } } } log_debug(NAME, "[%s] Ok (%s)", FZONE, xmlnode2str(current)); } /* If theres an error, return */ if(error == 1) { jutil_iqresult(jp->x); xmlnode_put_attrib(jp->x, "type", "error"); xmlnode_insert_node(jp->x, node); deliver(dpacket_new(jp->x), NULL); return; } /* Now process the checked results */ result = xmlnode_new_tag("query"); xmlnode_put_attrib(result, "xmlns", xmlns); for(current = xmlnode_get_firstchild(node); current != NULL; current = xmlnode_get_nextsibling(current)) { if (xmlnode_get_name(current)==NULL) continue; jabberid = xmlnode_get_attrib(current, "jid"); nick = xmlnode_get_attrib(current, "nick"); role = xmlnode_get_attrib(current, "role"); affiliation = xmlnode_get_attrib(current, "affiliation"); reason = xmlnode_get_tag_data(current, "reason"); if(jpacket_subtype(jp) == JPACKET__GET) { if(role != NULL) result = con_gen_list(room, xmlns, role); else result = con_gen_list(room, xmlns, affiliation); break; } else { /* Find jabberid for this user */ if(jabberid == NULL) { user = g_hash_table_lookup(room->local, nick); jabberid = jid_full(user->realid); } else if(user == NULL) { user = g_hash_table_lookup(room->remote, jabberid); } /* Convert jabberid into a jid struct */ target = jid_new(jp->p, jabberid); if(role == NULL && affiliation != NULL) { log_debug(NAME, "[%s] Requesting affiliation change for %s to (%s)", FZONE, jabberid, affiliation); change_affiliate(affiliation, sender, target, reason, from); if(target != NULL && target->user != NULL) { if(j_strcmp(affiliation, "owner") == 0) { change_role("moderator", sender, target, reason); } else if(j_strcmp(affiliation, "admin") == 0) { change_role("moderator", sender, target, reason); } else if(j_strcmp(affiliation, "outcast") == 0) { change_role("none", sender, target, reason); } else { change_role("participant", sender, target, reason); } } } else if(role != NULL && affiliation == NULL) { log_debug(NAME, "[%s] Requesting role change for %s to (%s)", FZONE, jabberid, role); change_role(role, sender, target, reason); } else { log_debug(NAME, "[%s] Request: role %s, affiliation %s, for %s", FZONE, role, affiliation, jabberid); } } } jutil_iqresult(jp->x); if(result) { xmlnode_insert_node(jp->x, result); xmlnode_free(result); } deliver(dpacket_new(jp->x), NULL); return; } jabber-muc-0.8/src/iq.c0000664000175000017500000000557511160655601014231 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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 */ /* Functions used be both conference.c and conference_room.c to respond to IQ requests */ #include "conference.h" #include /* Take an iq request for version and send the response directly */ void iq_get_version(jpacket jp){ struct utsname un; xmlnode x; 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); return; } /* send a response to a iq:time request */ void iq_get_time(jpacket jp){ time_t t; char *str; 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); } /* Add commons features ns to the item node of a browse reply * Take the item node in argument */ void iq_populate_browse(xmlnode item){ xmlnode_insert_cdata(xmlnode_insert_tag(item, "ns"), NS_MUC, -1); xmlnode_insert_cdata(xmlnode_insert_tag(item, "ns"), NS_DISCO, -1); xmlnode_insert_cdata(xmlnode_insert_tag(item, "ns"), NS_BROWSE, -1); xmlnode_insert_cdata(xmlnode_insert_tag(item, "ns"), NS_REGISTER, -1); xmlnode_insert_cdata(xmlnode_insert_tag(item, "ns"), NS_VERSION, -1); xmlnode_insert_cdata(xmlnode_insert_tag(item, "ns"), NS_TIME, -1); xmlnode_insert_cdata(xmlnode_insert_tag(item, "ns"), NS_LAST, -1); xmlnode_insert_cdata(xmlnode_insert_tag(item, "ns"), NS_VCARD, -1); } jabber-muc-0.8/src/Makefile0000664000175000017500000000225511160655601015104 0ustar bencerbencerCC:=gcc CFLAGS:=$(CFLAGS) -O2 -Wall -I../../lib -I../include `pkg-config --cflags glib-2.0` -D_JCOMP -D_REENTRANT -DLIBIDN #CFLAGS:=$(CFLAGS) -O2 -Wall -I../../lib -I../include `pkg-config --cflags glib-2.0` -D_JCOMP -D_REENTRANT -DLIBIDN -DHAVE_MYSQL LIBS:=$(LIBS) -ljcomp -lm `pkg-config --libs glib-2.0` `pkg-config --libs gthread-2.0` -lexpat -lidn #LIBS:=$(LIBS) -ljcomp -lm `pkg-config --libs glib-2.0` `pkg-config --libs gthread-2.0` -lexpat -lidn `mysql_config --libs` LDFLAGS:=-L. # Debug/Experimental #CFLAGS:=$(CFLAGS) -pipe -Os -I../../jabberd -I../include #LIBS:=$(LIBS) /usr/local/lib/ccmalloc-gcc.o -lccmalloc #LIBS:=$(LIBS) -lmemusage #LIBS:=$(LIBS) -lmcheck conference_OBJECTS=libjcomp.a conference.o conference_room.o conference_user.o utils.o xdata.o admin.o roles.o xdb.o hash.o iq.o main.o mysql.o all: mu-conference mu-conference: $(conference_OBJECTS) $(CC) $(CFLAGS) $(MCFLAGS) -o mu-conference $(conference_OBJECTS) $(LDFLAGS) $(LIBS) libjcomp.a: cd jabberd ; $(MAKE) cd jcomp ; $(MAKE) lib static: $(conference_OBJECTS) single: $(conference_OBJECTS) clean: rm -f $(conference_OBJECTS) mu-conference *~ cd jcomp ; $(MAKE) clean cd jabberd ; $(MAKE) clean jabber-muc-0.8/src/xdb.c0000664000175000017500000004311511160655601014365 0ustar bencerbencer/* * MU-Conference - Multi-User Conference Service * Copyright (c) 2002-2005 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" int xdb_room_config(cnr room) { char *roomid; char *host; char temp[10]; cni master; int status; jid store; xmlnode node; xmlnode element; if(room == NULL) { log_error(NAME, "[%s] Aborting: NULL room result", FZONE); return -1; } master = room->master; roomid = jid_full(room->id); host = room->id->server; log_debug(NAME, "[%s] Writing Room config.. - <%s>", FZONE, roomid); node = xmlnode_new_tag("room"); store = jid_new(xmlnode_pool(node), spools(xmlnode_pool(node), shahash(roomid), "@", host, xmlnode_pool(node))); xmlnode_insert_cdata(xmlnode_insert_tag(node, "name"), room->name, -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "secret"), room->secret, -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "description"), room->description, -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "subject"), xmlnode_get_attrib(room->topic,"subject"), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "creator"), jid_full(room->creator), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "private"), itoa(room->private, temp), -1); element = xmlnode_insert_tag(node, "notice"); xmlnode_insert_cdata(xmlnode_insert_tag(element, "leave"), room->note_leave, -1); xmlnode_insert_cdata(xmlnode_insert_tag(element, "join"), room->note_join, -1); xmlnode_insert_cdata(xmlnode_insert_tag(element, "rename"), room->note_rename, -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "public"), itoa(room->public, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "subjectlock"), itoa(room->subjectlock, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "maxusers"), itoa(room->maxusers, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "persistent"), itoa(room->persistent, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "moderated"), itoa(room->moderated, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "defaulttype"), itoa(room->defaulttype, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "privmsg"), itoa(room->privmsg, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "invitation"), itoa(room->invitation, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "invites"), itoa(room->invites, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "legacy"), itoa(room->legacy, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "visible"), itoa(room->visible, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "logformat"), itoa(room->logformat, temp), -1); xmlnode_insert_cdata(xmlnode_insert_tag(node, "locknicks"), itoa(room->locknicks, temp), -1); if(room->logfile) xmlnode_insert_cdata(xmlnode_insert_tag(node, "logging"), "1", -1); else xmlnode_insert_cdata(xmlnode_insert_tag(node, "logging"), "0", -1); status = xdb_set(master->xdbc, store, "muc:room:config", node); xmlnode_free(node); return status; } void _xdb_put_list(gpointer key, gpointer data, gpointer arg) { xmlnode result = (xmlnode)arg; xmlnode item; jid id; char *jabberid; jabberid = pstrdup(xmlnode_pool(result), key); /* cnu is only available if resource defined in jabber id */ id = jid_new(xmlnode_pool(result), jabberid); if(id == NULL) { log_warn(NAME, "[%s] Somethings not right here - <%s>", FZONE, jabberid); return; } item = xmlnode_insert_tag(result, "item"); xmlnode_put_attrib(item, "jid", jabberid); } void _xdb_put_outcast_list(gpointer key, gpointer data, gpointer arg) { xmlnode result = (xmlnode)arg; xmlnode info = (xmlnode)data; xmlnode item; jid id; char *jabberid; jabberid = pstrdup(xmlnode_pool(result), key); /* cnu is only available if resource defined in jabber id */ id = jid_new(xmlnode_pool(result), jabberid); if(id == NULL) { log_warn(NAME, "[%s] Somethings not right here - <%s>", FZONE, jabberid); return; } item = xmlnode_insert_tag(result, "item"); xmlnode_put_attrib(item, "jid", jabberid); xmlnode_insert_node(item, info); } int xdb_room_lists_set(cnr room) { char *roomid; char *host; cni master; jid store; xmlnode node; pool p; if(room == NULL) { return -1; } p = pool_new(); master = room->master; roomid = jid_full(room->id); host = room->id->server; log_debug(NAME, "[%s] Writing Room lists.. - <%s>", FZONE, roomid); store = jid_new(p, spools(p, shahash(roomid), "@", host, p)); node = xmlnode_new_tag("list"); g_hash_table_foreach(room->owner, _xdb_put_list, (void*)node); xdb_set(master->xdbc, store, "muc:list:owner", node); xmlnode_free(node); node = xmlnode_new_tag("list"); g_hash_table_foreach(room->admin, _xdb_put_list, (void*)node); xdb_set(master->xdbc, store, "muc:list:admin", node); xmlnode_free(node); node = xmlnode_new_tag("list"); g_hash_table_foreach(room->member, _xdb_put_list, (void*)node); xdb_set(master->xdbc, store, "muc:list:member", node); xmlnode_free(node); node = xmlnode_new_tag("list"); g_hash_table_foreach(room->outcast, _xdb_put_outcast_list, (void*)node); xdb_set(master->xdbc, store, "muc:list:outcast", node); xmlnode_free(node); pool_free(p); return 1; } void xdb_room_set(cnr room) { pool p; char *host; jid fulljid; jid roomid; cni master; xmlnode node; xmlnode item; if(room == NULL) { return; } p = pool_new(); master = room->master; host = room->id->server; fulljid = jid_new(p, spools(p, "rooms@", host, p)); roomid = jid_new(p, spools(p, shahash(jid_full(room->id)),"@", host, p)); node = xdb_get(master->xdbc, fulljid, "muc:room:list"); if(node == NULL) { node = xmlnode_new_tag("registered"); } item = xmlnode_get_tag(node, spools(p, "?jid=", jid_full(jid_fix(roomid)), p)); if(item == NULL) { item = xmlnode_insert_tag(node, "item"); xmlnode_put_attrib(item, "name", jid_full(room->id)); xmlnode_put_attrib(item, "jid", jid_full(jid_fix(roomid))); xdb_set(master->xdbc, fulljid, "muc:room:list", node); } xdb_room_config(room); xdb_room_lists_set(room); xmlnode_free(node); pool_free(p); return; } void _xdb_add_list(GHashTable *hash, xmlnode node) { char *user; xmlnode current; jid userid; if(node == NULL) { return; } for(current = xmlnode_get_firstchild(node); current != NULL; current = xmlnode_get_nextsibling(current)) { user = xmlnode_get_attrib(current, "jid"); if (user) { userid = jid_new(xmlnode_pool(node), user); add_affiliate(hash, userid, xmlnode_get_tag(current, "reason")); } } xmlnode_free(current); return; } int xdb_room_lists_get(cnr room) { char *roomid; char *host; cni master; jid store; xmlnode node; pool p; if(room == NULL) { return -1; } log_debug(NAME, "[%s] asked to restore rooms lists for %s ", FZONE, jid_full(room->id)); p = pool_new(); master = room->master; roomid = jid_full(room->id); host = room->id->server; store = jid_new(p, spools(p, shahash(roomid), "@", host, p)); node = xdb_get(master->xdbc, store, "muc:list:owner"); _xdb_add_list(room->owner, node); xmlnode_free(node); node = xdb_get(master->xdbc, store, "muc:list:admin"); _xdb_add_list(room->admin, node); xmlnode_free(node); node = xdb_get(master->xdbc, store, "muc:list:member"); _xdb_add_list(room->member, node); xmlnode_free(node); node = xdb_get(master->xdbc, store, "muc:list:outcast"); _xdb_add_list(room->outcast, node); xmlnode_free(node); pool_free(p); return 1; } void xdb_rooms_get(cni master) { char *file, *roomid, *subject; cnr room; jid jidroom; jid fulljid; xmlnode node = NULL; xmlnode current = NULL; xmlnode result = NULL; pool p; if(master == NULL) { return; } p = pool_new(); fulljid = jid_new(p, spools(p, "rooms@", master->i->id, p)); log_debug(NAME, "[%s] asked to get rooms from xdb ", FZONE); /* Get master room list */ node = xdb_get(master->xdbc, fulljid, "muc:room:list"); if(node != NULL) { xmlnode_free(current); for(current = xmlnode_get_firstchild(node); current != NULL; current = xmlnode_get_nextsibling(current)) { if(xmlnode_get_attrib(current, "name") == 0) { log_debug(NAME, "[%s] skipping .. no name", FZONE); continue; } roomid = xmlnode_get_attrib(current, "name"); log_debug(NAME, "[%s] asked to get room %s from xdb ", FZONE, roomid); file = xmlnode_get_attrib(current, "jid"); if(roomid == NULL || file == NULL) { log_debug(NAME, "[%s] skipping .. no room/file", FZONE); continue; } fulljid = jid_new(xmlnode_pool(node), spools(xmlnode_pool(node), file, xmlnode_pool(node))); jidroom = jid_new(xmlnode_pool(node), spools(xmlnode_pool(node), roomid, xmlnode_pool(node))); result = xdb_get(master->xdbc, fulljid, "muc:room:config"); if(result == NULL) { log_debug(NAME, "[%s] skipping .. no room config", FZONE); continue; } room = con_room_new(master, jidroom, NULL, xmlnode_get_tag_data(result,"name"), xmlnode_get_tag_data(result, "secret"), j_atoi(xmlnode_get_tag_data(result, "private"), 0), 0); room->subjectlock = j_atoi(xmlnode_get_tag_data(result, "subjectlock"), 0); room->maxusers = j_atoi(xmlnode_get_tag_data(result, "maxusers"), 30); room->moderated = j_atoi(xmlnode_get_tag_data(result, "moderated"), 0); room->defaulttype = j_atoi(xmlnode_get_tag_data(result, "defaulttype"), 0); room->privmsg = j_atoi(xmlnode_get_tag_data(result, "privmsg"), 0); room->invitation = j_atoi(xmlnode_get_tag_data(result, "invitation"), 0); room->invites = j_atoi(xmlnode_get_tag_data(result, "invites"), 0); room->locknicks = j_atoi(xmlnode_get_tag_data(result, "locknicks"), 0); room->legacy = j_atoi(xmlnode_get_tag_data(result, "legacy"), 1); room->public = j_atoi(xmlnode_get_tag_data(result, "public"), room->master->public); room->visible = j_atoi(xmlnode_get_tag_data(result, "visible"), 0); /* correct spelling overrides for old config files */ room->persistent = j_atoi(xmlnode_get_tag_data(result, "persistant"), 0); room->persistent = j_atoi(xmlnode_get_tag_data(result, "persistent"), 0); room->logformat = j_atoi(xmlnode_get_tag_data(result, "logformat"), LOG_TEXT); if(j_strcmp(xmlnode_get_tag_data(result, "logging"), "1") == 0) { con_room_log_new(room); if (room->logfile == NULL) log_alert(NULL, "cannot open log file for room %s", jid_full(room->id)); else con_room_log(room, NULL, "LOGGING STARTED"); } room->creator = jid_new(room->p, xmlnode_get_tag_data(result, "creator")); free(room->description); room->description = j_strdup(xmlnode_get_tag_data(result, "description")); free(room->name); room->name = j_strdup(xmlnode_get_tag_data(result, "name")); free(room->note_join); room->note_join = j_strdup(xmlnode_get_tag_data(result, "notice/join")); free(room->note_rename); room->note_rename = j_strdup(xmlnode_get_tag_data(result, "notice/rename")); free(room->note_leave); room->note_leave = j_strdup(xmlnode_get_tag_data(result, "notice/leave")); subject = pstrdup(room->p, xmlnode_get_tag_data(result, "subject")); xmlnode_free(room->topic); room->topic = xmlnode_new_tag("topic"); xmlnode_put_attrib(room->topic, "subject", subject); xmlnode_insert_cdata(room->topic, "The topic has been set to: ", -1); xmlnode_insert_cdata(room->topic, subject, -1); xdb_room_lists_get(room); xmlnode_free(result); } } else { log_debug(NAME, "[%s] skipping .. no results", FZONE); /* Set XDB, just in case */ xdb_set(master->xdbc, fulljid, "muc:room:list", NULL); } xmlnode_free(node); xmlnode_free(current); pool_free(p); } void xdb_room_clear(cnr room) { char *roomid; char *host; cni master; jid store; jid fulljid; xmlnode node; xmlnode item; pool p; if(room == NULL) { return; } p = pool_new(); master = room->master; roomid = jid_full(room->id); host = room->id->server; fulljid = jid_new(p, spools(p, "rooms@", host, p)); store = jid_new(p, spools(p, shahash(roomid), "@", host, p)); log_debug(NAME, "[%s] asked to clear a room from xdb (%s)", FZONE, jid_full(room->id)); /* Remove from rooms db */ node = xdb_get(master->xdbc, fulljid, "muc:room:list"); if(node != NULL) { item = xmlnode_get_tag(node, spools(p, "?jid=", jid_full(jid_fix(store)), p)); if(item) { log_debug(NAME, "[%s] Found (%s) in rooms.xml - removing, %s", FZONE, jid_full(room->id), jid_full(jid_fix(store))); xmlnode_hide(item); xdb_set(master->xdbc, fulljid, "muc:room:list", node); } else { log_debug(NAME, "[%s] (%s) not found in rooms.xml - ignoring, %s", FZONE, jid_full(room->id), jid_full(jid_fix(store))); } } /* Clear lists */ xdb_set(master->xdbc, store, "muc:list:owner", NULL); xdb_set(master->xdbc, store, "muc:list:admin", NULL); xdb_set(master->xdbc, store, "muc:list:member", NULL); xdb_set(master->xdbc, store, "muc:list:outcast", NULL); /* Clear room config */ xdb_set(master->xdbc, store, "muc:room:config", NULL); xmlnode_free(node); pool_free(p); return; } int set_data(cni master, char *nick, char *jabberid, xmlnode node, int remove) { xmlnode item; xmlnode old; int status; jid fulljid, userjid; char *current = NULL; char *user = NULL; char *host = NULL; pool p; if(master == NULL || ( nick == NULL && remove != 1 ) || jabberid == NULL) { return 0; } p = pool_new(); host = master->i->id; fulljid = jid_new(p, spools(p, "registration@", host, p)); userjid = jid_new(p, jabberid); if(nick) { log_debug(NAME, "[%s] asked to manage xdb nick(%s)", FZONE, nick); user = pstrdup(p, nick); for(current = user; *current != '\0'; current++) *current = tolower(*current); /* lowercase the group name */ } xmlnode_put_attrib(node, "xmlns", "muc:data"); old = xdb_get(master->xdbc, fulljid, "muc:data"); item = xmlnode_get_tag(old, spools(p, "?jid=", jid_full(jid_user(jid_fix(userjid))), p)); if(old == NULL) old = xmlnode_new_tag("registered"); if(remove == 1) { log_debug(NAME, "[%s] asked to remove xdb info \n>%s<\n>%s< \n ", FZONE, xmlnode2str(old), xmlnode2str(item)); if(item) xmlnode_hide(item); } else { xmlnode_hide(item); item = xmlnode_insert_tag(old, "item"); xmlnode_put_attrib(item, "nick", nick); xmlnode_put_attrib(item, "keynick", user); xmlnode_put_attrib(item, "jid", jid_full(jid_user(jid_fix(userjid)))); log_debug(NAME, "[%s] asked to add xdb info \n>%s<\n>%s< \n ", FZONE, xmlnode2str(old), xmlnode2str(item)); if(node) { xmlnode_insert_node(item, node); xmlnode_free(node); } } status = xdb_set(master->xdbc, fulljid, "muc:data", old); log_debug(NAME, "[%s] xdb status(%d)", FZONE, status); xmlnode_free(old); pool_free(p); return status; } xmlnode get_data_bynick(cni master, char *nick) { xmlnode node; xmlnode result; jid fulljid; char *current, *user, *host; pool p; log_debug(NAME, "[%s] asked to find xdb nick (%s)", FZONE, nick); if(master == NULL || nick == NULL) { return NULL; } p = pool_new(); user = pstrdup(p, nick); host = master->i->id; for(current = user; *current != '\0'; current++) *current = tolower(*current); /* lowercase the group name */ fulljid = jid_new(p, spools(p, "registration@", host, p)); node = xdb_get(master->xdbc, fulljid, "muc:data"); /* Set blank data in case file doesn't exist */ if(node == NULL) { log_debug(NAME, "[%s] DBG: blank data", FZONE); xdb_set(master->xdbc, fulljid, "muc:data", NULL); pool_free(p); return NULL; } result = xmlnode_dup(xmlnode_get_tag(node, spools(p, "?keynick=", user, p))); log_debug(NAME, "[%s] asked to find xdb nick for %s - (%s)", FZONE, user, xmlnode2str(result)); xmlnode_free(node); pool_free(p); return result; } xmlnode get_data_byjid(cni master, char *jabberid) { xmlnode node; xmlnode result; jid fulljid, userjid; char *host; pool p; log_debug(NAME, "[%s] asked to find xdb jid (%s)", FZONE, jabberid); if(master == NULL || jabberid == NULL) { return NULL; } p = pool_new(); host = master->i->id; userjid = jid_new(p, jabberid); fulljid = jid_new(p, spools(p, "registration@", host, p)); node = xdb_get(master->xdbc, fulljid, "muc:data"); /* Set blank data in case file doesn't exist */ if(node == NULL) { xdb_set(master->xdbc, fulljid, "muc:data", NULL); pool_free(p); return NULL; } result = xmlnode_dup(xmlnode_get_tag(node, spools(p, "?jid=", jid_full(jid_user(jid_fix(userjid))), p))); log_debug(NAME, "[%s] asked to find xdb jid for %s - (%s)", FZONE, jid_full(jid_user(jid_fix(userjid))), xmlnode2str(result)); xmlnode_free(node); pool_free(p); return result; } jabber-muc-0.8/scripts/0000775000175000017500000000000011171216166014341 5ustar bencerbencerjabber-muc-0.8/scripts/roommaker.pl0000775000175000017500000001514011160655601016675 0ustar bencerbencer#!/usr/bin/perl # # roommaker.pl: Predefine persistent rooms for MU-Conference # Requires: Digest::SHA1, XML::Simple # use strict; use Digest::SHA1 qw(sha1_hex); use XML::Simple; # Declare variables my $uid; my $gid; my $check; my $name; my $server; my $output; my $roomcfg; my $noticecfg; my $ownerlist; my $adminlist; my $memberlist; my $outcastlist; my $roomsconfig; my $FH; # # Get spool directory print "Please enter spool directory path (e.g. /usr/local/jabber/spool): "; my $spooldir = <>; chomp $spooldir; if( $spooldir eq "") { print "No spool directory\n"; exit; } elsif( ! -d $spooldir ) { print "Spool directory does not exist. Exiting \n"; exit; } # Fix spooldir variable, if necessary $spooldir =~ s/\/$//; # Get uid/gid from spool $uid = (stat($spooldir))[4]; $gid = (stat($spooldir))[5]; umask "0027"; # # Get room jid print "Please enter jid for the room: "; my $jid = <>; chomp $jid; if( !($jid =~ /\w@\w/) ) { print "Bad JID - Exiting\n"; exit; } ($name, $server) = split(/@/, $jid); my $hash = sha1_hex($jid); # # Check if directory exists if( ! -d "$spooldir/$server/" ) { print "$spooldir/$server/ doesn't exist - Create? (Y/N) "; my $input = <>; if( $input =~ /^[Y|y]/ ) { print "Creating Directory\n"; mkdir("$spooldir/$server", 0777); chown $uid, $gid, "$spooldir/$server"; } else { print "Unable to continue. Exiting\n"; exit } } # # Print Header print "\nConfiguring room $jid\n"; print "Filename: $spooldir/$server/$hash.xml\n"; # # Check if room already defined if( -f "$spooldir/$server/$hash.xml") { print "Room already defined. Exiting\n"; exit; } $roomcfg->{xdbns} = "muc:room:config"; print "\nGeneral Options\n---\n"; $roomcfg->{name} = [getText("Room name", $name)]; $roomcfg->{secret} = [getText("Password", "")]; $roomcfg->{description} = [getText("Room description/MOTD", "")]; $roomcfg->{subject} = [getText("Room subject", "")]; $roomcfg->{creator} = [getText("Bare JID of room creator", "")]; $roomcfg->{public} = [getBoolean("Is room public", 0)]; $roomcfg->{maxusers} = [getValue("Maximum Users ", 0)]; $roomcfg->{persistent} = [1]; # Has to be persistent print "\nPermission Options\n---\n"; $roomcfg->{visible} = [getBoolean("Allow non-admins to see real jids", 0)]; $roomcfg->{subjectlock} = [getBoolean("Can users change subject", 0)]; $roomcfg->{private} = [getBoolean("Allow users to IQ query other users", 0)]; print "\nLegacy Options:\n---\n"; $roomcfg->{legacy} = [getBoolean("Consider all clients legacy", 0)]; $noticecfg->{join} = [getText("Legacy join message", "")]; $noticecfg->{leave} = [getText("Legacy leave message", "")]; $noticecfg->{rename} = [getText("Legacy rename message", "")]; $roomcfg->{notice} = [$noticecfg]; print "\nModeration Options:\n---\n"; $roomcfg->{moderated} = [getBoolean("Is room moderated", 0)]; if($roomcfg->{moderated}[0] == 0) { print "Skipping Moderation options\n"; $roomcfg->{defaulttype} = [0]; $roomcfg->{privmsg} = [0]; } else { $roomcfg->{defaulttype} = [getBoolean("Default entry type of participant", 0)]; $roomcfg->{privmsg} = [getBoolean("Default entry type of participant", 0)]; } print "\nMember-Only Options:\n---\n"; $roomcfg->{invitation} = [getBoolean("Make room member-only", 0)]; if($roomcfg->{invitation}[0] == 0) { print "Skipping Moderation options\n"; $roomcfg->{invites} = [0]; } else { $roomcfg->{invites} = [getBoolean("Allow members to send invites", 0)]; } print "\nLogging Options:\n---\n"; $roomcfg->{logging} = [getBoolean("Enable native room logging", 0)]; if($roomcfg->{logging}[0] == 0) { print "Skipping Logging options\n"; $roomcfg->{logformat} = [0]; } else { $roomcfg->{logformat} = [getOption("Log Format\n0] Plain Text\n1] XML\n2] XHTML\n", 0)]; } print "\nOwner List:\n---\n"; $ownerlist->{xdbns} = "muc:list:owner"; $ownerlist->{item} = [getList("JID of owner")]; print "\nAdmin List:\n---\n"; $adminlist->{xdbns} = "muc:list:admin"; $adminlist->{item} = [getList("JID of admin")]; print "\nMember List:\n---\n"; $memberlist->{xdbns} = "muc:list:member"; $memberlist->{item} = [getList("JID of member")]; print "\nOutcast List:\n---\n"; $outcastlist->{xdbns} = "muc:list:outcast"; $outcastlist->{item} = [getList("JID of outcast")]; $output->{room} = $roomcfg; $output->{list} = [$ownerlist, $adminlist, $memberlist, $outcastlist]; print "\nWriting Room definition file\n"; open(DATA, ">$spooldir/$server/$hash.xml"); print DATA XMLout($output, rootname => "xdb"); close(DATA); if( ! -f "$spooldir/$server/rooms.xml") { print "Room registry not found. Creating\n"; my $list; my $roomitem; $roomitem->{name} = $jid; $roomitem->{jid} = "$hash\@$server"; $list->{item} = [$roomitem]; $list->{xdbns} = "muc:room:list"; $roomsconfig->{registered} = [$list]; } else { my $list; my $roomitem; $roomsconfig = XMLin("$spooldir/$server/rooms.xml"); $roomitem->{name} = $jid; $roomitem->{jid} = "$hash\@$server"; $roomsconfig->{registered}->{item} = [$roomsconfig->{registered}->{item}, $roomitem]; } print "\nWriting updated Room registry file\n"; open(DATA, ">$spooldir/$server/rooms.xml"); print DATA XMLout($roomsconfig, rootname => "xdb"); close(DATA); exit; # #Functions sub getText { my $text = shift; my $default = shift; print "$text (text) [Default: $default]: "; my $value = <>; chomp $value; if($value eq "") { return $default; } else { return $value; } } sub getBoolean { my $text = shift; my $default = shift; print "$text (0/1) [Default: $default]: "; my $value = <>; chomp $value; if($value eq "" or !( $value =~ /^[1|0]$/)) { return $default; } else { return $value; } } sub getValue { my $text = shift; my $default = shift; print "$text (value) [Default: $default]: "; my $value = <>; chomp $value; if($value eq "" or !( $value =~ /^(\d*)$/)) { return $default; } else { return $value; } } sub getOption { my $text = shift; my $default = shift; print "$text [Default: $default]: "; my $value = <>; chomp $value; if($value eq "" or !( $value =~ /^(\d*)$/)) { return $default; } else { return $value; } } sub getList { my $text = shift; my $data = 1; my @list; while($data) { print "$text (Empty line to exit): "; my $value = <>; chomp $value; if($value eq "") { $data = 0; } else { my %users; $users{jid} = $value; push @list, \%users; } } return @list; } jabber-muc-0.8/scripts/roomname.pl0000775000175000017500000000041311160655601016513 0ustar bencerbencer#!/usr/bin/perl # # roomname.pl : Perl Utility to create sha1 hash for a jid # # Requires: Digest::SHA1 # use Digest::SHA1 qw(sha1_hex); my $data; my $digest; while(@ARGV) { $data = shift @ARGV; $digest = sha1_hex($data); print "$digest <= $data\n"; } jabber-muc-0.8/scripts/README0000664000175000017500000000047611160655601015227 0ustar bencerbencerScripts: ------- Included in this directory are a number of scripts designed to help with maintaining MU-Conference: roomname.pl: Takes a list of jids and returns the sha1 hash. Used as the room filename for the spool roommaker.pl: Allows you to create predefined persistent rooms without first starting the service jabber-muc-0.8/doc/0000775000175000017500000000000011171216166013417 5ustar bencerbencerjabber-muc-0.8/LICENSE0000664000175000017500000003544711160655601013673 0ustar bencerbencer GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS jabber-muc-0.8/mu-conference.sql0000664000175000017500000000176111160655601016125 0ustar bencerbencer-- phpMyAdmin SQL Dump -- version 2.10.0.2 -- http://www.phpmyadmin.net -- -- Host: localhost -- Generation Time: Mar 30, 2007 at 09:31 PM -- Server version: 5.0.37 -- PHP Version: 5.2.1 SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; -- -- Database: `chat` -- -- -------------------------------------------------------- -- -- Table structure for table `rooms` -- CREATE TABLE `rooms` ( `jid` varchar(512) NOT NULL, `name` text NOT NULL, `desc` text NOT NULL, `topic` text NOT NULL, `users` int(11) NOT NULL default '0', `public` tinyint(1) NOT NULL, `open` tinyint(1) NOT NULL, `secret` text NOT NULL, UNIQUE KEY `jid` (`jid`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- -------------------------------------------------------- -- -- Table structure for table `rooms_lists` -- CREATE TABLE `rooms_lists` ( `jid_room` varchar(512) NOT NULL, `jid_user` varchar(512) NOT NULL, `affil` enum('administrator','owner','member','outcast') NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; jabber-muc-0.8/AUTHORS0000664000175000017500000000016111160655601013717 0ustar bencerbencerDavid Sutton Gregoire Menuel jabber-muc-0.8/ChangeLog0000664000175000017500000004200611163505600014421 0ustar bencerbencerv0.8 * Optimization: A bunch of patches by M. Doliner (see svn log for more details) * Feature: Allow the service admin to see the occupants of every rooms * Feature: Allow the service admin to enter a room with nicknames locked even if his nickname isn't the one needed * Feature: option to disable room logging on the whole component * Feature: option to save room log files in subdirectories according to date * Feature: Patch by Smoku to hide empty rooms from disco/browse lists * Bugfix: Two vulnerabilities in mysql module * Bugfix: send code=110 when needed according to XEP-0045 * Bugfix: Fix crash when changing roles (M. Doliner) * Bugfix: Fixed a bug when entering/leaving a room, it was considered as a nick change * Bugfix: Corrected the errors sent by mu-conference * Bugfix: Better error code when choosing a nick not conform with the room policy * Bugfix: Fixed a segfault in the decline messages handler * Bugfix: Minors memleak fixed by M. Doliner (#11300 and #10862) * Bugfix: Avoid a segfault when asking unique room name with a too big user jid * Bugfix: going in an infinite loop if the user invite the jid "" * Bugfix: Changed error handling - Don't kick a user if the message error is not delivery-related, otherwise user could be kicked when refusing a file transfer for example - If the user is not kicked, don't discard the error, send it to the other user/chatroom * Bugfix: Hide XEP-0203 delay nodes when sending back presences stanzas (#12592, patch by Garrett Kuchta) * Bugfix: Fix a segfault with badly formed packets * Bugfix: Corrected a bug which allowed to have html in nickname in the logs (A. Verrier) v0.7 More XEP compliance (should now support everythings that is required in XEP-0045), including: Support for decline messages Reply to disco requests to an unknown node with an error (not available) Send status code when entering a logged or a non-anonymous room Merged jabberd14 libidn stringprep jid validation, jid are now really stringpreped The tag wasn't always in the good position (it must be the first child of the ) We can no more remove all the owner of a room Implementation of the room name request Support for "Discovering Reserved Room Nickname" Feature: Added a support for mysql integration. This is has nothing to do with storing the rooms information, but it's more like a copy of the current states of the rooms. It allows to integrate easily mu-conference in a webpage. Feature: The numbers of participant in a conference is now written when doing a disco#items request Feature: It is now possible to register a nick with the composant Feature: it's possible to add a stylesheet to the xhtml logs of a room Bugfix: Many small optimisations (many thanks to Mark Doliner for this) Bugfix: xhtml log file is now really xhtml conform Bugfix: Corrected a bug which prevents kicking, banning, ... with psi Bugfix: It was impossible to fetch the owner and admin list Bugfix: It was possible to be in the room, but not in the clients room roster Bugfix: MUC compliant cients are correctly detected Bugfix: mu-conference doesn't crash anymore when someone try to send a file to another members of the chatroom Bugfix: There shouldn't be anymore ghost, i.e. when an user doesn't quit cleanly a room, he is correctly removed Bugfix: No more white lines are added to the room description 20051215 DS: Minor memory leak fixes DS: Minor compile fixes 20051207 DS: Fixed password field for invite 20051206 DS: Initial work to integrate JCR DS: Added room history patch (Thanks Stefan Strigler) 20051202 DS: Fixed bug in external room config request (Thanks Magnus Henoch) 20051201 DS: Fixed text headers in main source files DS: Corrected service discovery features DS: Added in patch to change format of invite response to match JEP DS: Added patch for extended room info (Thanks Ralph Giles ) 20050314 DS: Resuming work since JEP has stabilized more DS: Added log fix (Thanks Ilja Booij) DS: Added Nick locking patch (Thanks Stephen Marquard) 20031220 DS: Fixed logs to display nick of user who kicked/banned 20031219 DS: Fixed an issue with possibly trying to use a NULL struct 20031116 DS: Attempting to isolate an error in the admin code v0.6.0 20031114 DS: Bumped version for release. 20031113 (rc4) DS: Fix for mishandling Route packets (Thanks Peter Millard) DS: Fix to Makefile to support cygwin (Thanks Frank Seesink) 20031111 (rc3) DS: Removed leak from con_room_sendwalk (Thanks Paul) DS: Reworked affiliation checks to remove jid_user 20031105 (rc2) DS: Fixed a bug where chat would only be translated DS: Fixed bug in room iq:time response 20031102 (rc1) DS: Changed history to reduce running memory usage a little DS: Fixed persistent room creation by sadmin when using dynamic 20031101 DS: Disabled user reg completely until after v0.6.0 is released DS: Fixed several memory leaks in roster handler 20031026 DS: Fixed a potential key corruption in the roster code 20031025 DS: Auto-voice members connecting to a moderated room DS: Changed how strings in cnr are allocated DS: Cleanup of xdb list get 20031024 DS: Found and fixed bug in XHTML log creation DS: Cleanup of xdb_set calls 20031023 DS: Disabled IQ:Register support for now DS: Removed suspect free() from conf.c:204 20031022 DS: Fixed a memory leak in 0 history setups 20031020 DS: Fixed packet error handler issue (Thanks Paul Curtis & Peter Millard) 20031018 DS: Compile fixes for FreeBSD v4.8 20031017 DS: More sanity checking of data in functions DS: Added room support for iq:last, iq:time & vCard 20031015 DS: Fixed bug in role revoking. DS: Fixed double free in iq:last DS: Tidied up xdata form titles DS: Added in escaping and translation of html for xml/xhtml logs 20031013 DS: Fixed room destroy segfault DS: Reworked a 'walk and remove' situation DS: Write out config + lists on shutdown (Thanks Paul Curtis) DS: Fixed a corruption bug in the affiliate handler DS: Added in shutdown failsafe to prevent multiple tidyup 20031012 DS: Moved ns #def's to seperate file DS: Blocked message from outsiders DS: Added patches for JCR support (Thanks Paul Curtis) DS: Added bug fixes to room removal (Thanks Paul Curtis) 20031011 DS: Fixed headers DS: Cleaned up documentation 20031009 DS: Changed Configure form to match JEP (Titles and vars) 20031008 DS: Added dependancy on glib-2.0 DS: Replaced htb with GHashTable 20030922 DS: Found and fixed a possible memleak in conference.c 20030919 DS: Added mutex locking into update and packet handlers DS: Fixed handling of '0' sized history DS: Fixed XHTML logging - username and brackets 20030504 DS: Commit created changes (this time) v0.5.2 20030422 DS: Updated FAQ with recent questions DS: Repaired Room Destroy code DS: Fixed XDB handling of room description config. DS: Added debug code to output malloc sizes 20030318 DS: Added flag as per request 20030316 DS: Fixed bug in invite handler v0.5.1 20030313 DS: Fixed xmlnode being freed early in invites 20030311 DS: Fixed double free in conference.c DS: Added ability for sadmin to enter a pw protected room DS: Fixed support for messages to legacy clients 20030310 DS: Applied delivery queue to messages sent to room v0.5 20030309 DS: Cleanup of xmlnode alloc/dealloc DS: Fixed bug in admin handler (deallocating jp->x by accident) DS: Testing new delivery trick 20030308 DS: Found memory loss by forgetting to free j_strdup v0.4 (Internal release only) 20030227 DS: Added flag to config DS: Fixed xdb room removal code, to remove from rooms.xml 20030224 DS: Tidied up number of new pools created DS: Correctly cleanup structs on shutdown 20030221 DS: Fixed room config code DS: Added loading room->public #1487 (thanks glen@ldscn.com) DS: Allowed moderator with no affil to use certain commands. #1530 (thanks info@graphite-works.com) 20030220 DS: Re-enabled all of browsing code, now jep-compliant 20030216 DS: Re-enabled browsing for list of rooms, now jep-compliant DS: Fixed htb_free routine to remove coredump DS: Fixed a memory leak in room creation/deletion 20030213 DS: Changed memory allocation method in several places to use a local pool, rather than an existing pool, so can free memory usage sooner. DS: Temporarily removed browse code until I can rework it as a configurable option 20030211 DS: Modified Browse and Disco requests as per request 20030206 DS: Modified how time is handled to request fewer system calls. DS: Reworked to only require one main heartbeat 20030131 DS: Cleaning the hashtable functions 20030129 DS: Isolated and fixed register issue. Also found and repaired three other potential crashes, all related. DS: Fixed handling for service registration 20030128 DS: Added a fix into conference error handler 20030120 DS: Added case normalisation for all jid checks/handling For user@server - resource is case sensitive DS: Added debug code into hash DS: Fixed debug code in hash code 20030119 DS: Resync'd disco code to match JEP 20030103 DS: Removed form field variable dependancy from x:data handler DS: Fixed room log closing DS: Added additional debugging to hashtable system. 20021215 DS: Removed and replaced all xhash references. 20021211 DS: Added checking for maxhash variable 20021119 DS: Fixed roommaker script to generate correct rooms.xml DS: Added topic saving to persistent rooms DS: Allow sadmins to override dynamic tag 20021117 DS: Changed field to 'name' in service registration DS: Reworked legacy code to work correctly 20021115 DS: Added 'creator' and handling code so room creator is always admin DS: Changed 'Locked' room so owner can re-enter 20021114 DS: Fixed kick presence code and fixed kick message for legacy DS: Added disco#info item for legacy rooms DS: Fixed xhtml log format DS: Fixed /me handling for plain text format logs DS: Rewrote Legacy client handler DS: Added 'room unlocked' message when room is configured DS: Locked room building until rooms loaded from xdb DS: Support for "wildcard" affiliation lists (except owner) 20021113 DS: Changed error for 'room locked' to 404 as per JEP DS: Unconfigured rooms don't show up under disco/browse 20021112 DS: Added roommaker script to scripts 20021111 DS: Added code to work around xdb_file missing file warnings DS: Fixed version numbering in ChangeLog DS: Added scripts directory for administration scripts DS: Sync'd disco#info support with JEP DS: Added hooks for iq:register in rooms. Generates 405 as per JEP. May flesh out at a later date. 20021110 DS: Fixed bug in presence handler DS: Added multiple sadmin support DS: Added Owner list support 20021109 DS: Found segfault in presence handler. Fixed. 20021108 DS: Adding modification to presence handling code - please test 20021107 DS: Modified leave message to remove extra space as per request DS: Added hooks for disco#items DS: Added sadmin override to room creation lock DS: Modified enter code so self-presence is returned first 20021106 DS: Changed strftime %F to %Y-%m-%d in util.c to workaround lack of support in FreeBSD < v4.6 v0.3 20021104 DS: Added support for canceling configuration form 20021102 DS: Fixed extended presence format 20021031 DS: Fixed invitation support DS: Added sadmin support 20021028 DS: Added Disco support for main service DS: Added room affiliate list restore code DS: Added basic Disco support for existing rooms DS: Changed to mu-conference.so DS: Added restart to room logging on rebuilding of a persistent room DS: Added option to disallow any new rooms DS: Added option to only allow non-persistent rooms DS: Modified subject code to not remove old history entries if the user disconnects. (per request) 20021027 DS: Fixed iq pnp parser to reject role+affiliation changes DS: Fixed Kick/Ban status codes 20021026 DS: Fixed x:data text-multi handler DS: Fixed banned private messages 20021025 DS: Fixed logupdate crash bug 20021024 DS: Fixed created flag and room locks DS: Added MUC protocol message to non-compliant clients DS: persistant rooms are now loaded. 20021023 DS: Added 'actor' support DS: Fixed admin/outcast checks (again) DS: Fixed error messages from parser 20021022 DS: Added initial work on persistant rooms via xdb DS: Added rest of destroy code to show alt and reason DS: Disallowed browsing member-only room roster if not a member DS: Fixed invitation code to use jabber:x:conference, not jabber:iq:conference DS: Fixed code to auto-boot demoted admin/member in a member-only room. DS: Fixed invite to match JEP v0.16 specs DS: Automatically kick demoted members in a member-only room 20021021 DS: Finished role='none' support DS: Fixed ban and added recursion to change_role 20021020 DS: More work on roles/affiliations. only role='none' left to go 20021018 DS: Started rewrite of the role system, to reflect JEP v0.14 20021017 DS: Added processing of iq get into parser DS: Changed 'destroy' to reflect JEP v0.13 DS: Added NS tags 20021016 DS: Started work on major rewrite of the IQ system DS: Wrote IQ parser v0.2 (Not formally released) 20021015 DS: Fixed several typos. Never comit when tired. DS: Started source cleanup DS: Added ability to ban private messages in a room DS: Added code to add anchors to html logs every 5 minutes DS: Added Registration system 20021014 DS: Send reason to room when kicked DS: Added permission check on kick DS: Added permission check on ban DS: Added room destroy code DS: Added room logging code 20021013 DS: Start of support for persistant admins DS: Updated debug messages DS: Fixed all xhash_put statements 20021012 DS: Fixed issue with xdata banning. Needed to encapsulate the item tag DS: Added custom leave messages DS: Added NS tag to room browse DS: Fixed configure room support 20021011 DS: Added admin list code DS: Changed all *_GC_* references to *_MUC_* DS: Added Server admin - a user able to control any room DS: Added legacy flag for identifying old clients 20021010 DS: Fixed room subject permission check DS: Added support for disabling room join/rename/leave messages (simply remove the text) DS: Added default role option for moderated rooms 20021009 DS: Added invitation list, handler and xdata handler DS: Added Room MOTD. DS: Allow owner to enter room even if not invited DS: Added more comments to various files DS: Added most of room logging handling DS: Namespace change to reflect JEP v0.7.5 DS: Added room enter code to reflect JEP v0.7.6 20021008 DS: Added sending invitations. Invitations not currently stored. DS: Modified browse code to support dynamic public/private rooms. Private rooms are only seen if you are in the room in question DS: Modified browse to show room size limits if in place 20021007 DS: Fixed security check on iq get using NS_GC_ADMIN DS: Added action code for text-multi fields on voice and ban. 20021006 DS: Changed member check so default role is member in non-moderated rooms. DS: Added more comments to utils.c DS: Changed iq:negotate code to use instead of , as per JEP v0.7.2, and extended with more possible features. DS: Added more options into room configuration for fine-tuning the room 20021005 DS: Fixed bug in nick changing code 20021004 DS: Added room size limits. Need offical error code DS: Added support for password protected rooms, following the JEP DS: Removed support, due to JEP change DS: Changed nick renaming to reflect JEP v0.7.1 DS: Added blocking of directed groupchat messages DS: Fixed Error message of illegal subject changes to match JEP 20021003 DS: Managed to get basic ban/voice support going, both adding and removing. Added new text-multi handling code, just need to write the action code. 20021001 DS: Moved all str* references to j_str* for better NULL protection. DS: Initial xdata handle installed for voice/ban 20020930 DS: Spent ages trying to fix a segfault. Found it, now need to understand why it occured 20020928 DS: Added TODO file for project tracking DS: Added error checking to xdata parser (configuration) 20020927 DS: Added support for display of banned and voice (member) lists DS: Added basic support for adding/removing ban 20020926 DS: Changed login for initial support of jep 20020925 DS: Changed role names to reflect JEP changes DS: Fixed Segfault in configuration request. Admin and Member status is removed on room exit DS: Added rename status code DS: Reworked extended presence addition code 20020924 DS: More x:data abstraction DS: Fixed allocation bug DS: Added more protocol from the JEP DS: Added whois support 20020922 DS: Added extended presence code DS: Added jabber:x:data form. Still need to write reply handler 20020920 DS: Added admin and voice hashes v0.1 (never released) 20020918 DS: Removed all 'conference' protocol, and removed concept of legacy jabber-muc-0.8/style.css0000664000175000017500000000056111160655601014525 0ustar bencerbencerhtml { background-color: #efefef; } body { margin: 20px 20px 20px 20px; padding: 10px 10px 10px 10px; border: 1px solid black; background-color: #fffff2; color: #464543; font-family : Verdana, Arial, Helvetica, sans-serif; font-size: 12pt; } span.time { color: #8b8986; } span.time a{ color: #8b8986; text-decoration: none; } span.nick { color: black; } jabber-muc-0.8/README.sql0000664000175000017500000000107011160655601014325 0ustar bencerbencerMu-conference can now write real time data in a MySQL database. This can be used to integrate mu-conference in a web page. For example it's easy to display the list of rooms in a webpage. Mu-conference still need a spool directory, the data stored in the database are just a copy of the current state of the rooms. To create the database tables you can use the file mu-conference.sql. This feature is disabled by default, to enable it, comment the second and fourth lines in src/Makefile, and uncomment the third and the fifth Currently this only works with MySQL. jabber-muc-0.8/README0000664000175000017500000001261211163505600013527 0ustar bencerbencerWelcome to the Multi-User Conferencing component for Jabber. ------------------------------------------------------------ Introduction: --- This is based on the existing conference component, now designed to support XEP-0045 (http://www.xmpp.org/extensions/xep-0045.html) The currently existing components are conference v0.4 and conference-v2. These both implement the GroupChat protocol (gc-1.0) and a test conferencing protocol which never really was adopted. The aim of the XEP and this project was to take the gc-1.0 protocol and extend it to become a more flexible and featureful conferencing system, superceeding the implementations that came before. Thanks to Peter Saint-Andre for getting this cleanup done. Thanks to Alexey Shchepin for the support in tkabber during development Thanks to Paul Curtis for debug help, the JCR runtime and running the alpha copy for testing Thanks to Peter Millard for help debugging whilst adding support in Exodus Requirements: --- * You will need a copy of libglib-2, expat and libidn11 installed, along with the development package (for packaged based solutions, such as Debian, Mandrake and RedHat) * You will also need pkg-config New Features: --- Note: This is not an exhaustive list. For a complete overview of the feature set, check out the XEP link given in the introduction * Backward compatibility with gc-1.0 * User 'levels' (known as roles and affiliations) * Native Room logging * Dynamic room configuration * Moderated rooms * Password protected rooms * Non-anonymous rooms * Member-only rooms * Room bans * "Kick" user * Persistant reconfigurable rooms Comments: --- Unlike the original conference component, rooms can now be dynamically set public or private. * A Public room is a room which can be found by anyone browsing the conference component. * A Private room is a room which can only be browsed for by a user already in the room. Installation: --- Unpack it and type 'make' to build the mu-conference binary. Configuration: --- [Note: if you want this service to be accessible from other servers, change any 'conference.localhost' listed below to a fully qualified domain name!] * Enable your Jabber server to advertise the service to your users. On jabberd14 server it is done by adding the following line in the browse section of the jsm : -----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- http://jabber.org/protocol/muc -----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- On jabberd2 server, there's nothing to do here. * Authorize the service to connect to the Jabber server. On jabberd14, this is done by adding the following section in the jabber.xml file: ---8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- conference.localhost 127.0.0.1 31518 secret ---8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- * Change the port and secret to your own preferences. On Jabberd2, you don't have to do anything, the default port to use is 5347, you must just set the in the file router.xml * Copy the file muc-default.xml to muc.xml * Edit muc.xml, and ensure that the settings are the same as you defined in your jabber server configuration Settings: --- Global Settings: * The tag makes all rooms default to Public when first created. Simply remove this tag for rooms to default to Private * The tag specifies the maximum number of lines to be held as a room history. * The tag specifies the direction in which log files will be stored. The directory must exist, and will default to the current working directory. * The tag makes a room have defaults set up automatically, so the the room creator doesn't have to submit the configuration form to unlock the room. * The tag stops users from creating any new rooms. Only persistent rooms will be created on startup. Note: the service admin (sadmin) is unaffected by this directive. This flag overrides the flag, if set. * The tag specifies that no persistent rooms can be created. Rooms will exist only as long as there is at least one user in the room. Once a room is empty, the room will be destroyed after approximately 5 minutes. * The tag specifies that persistent rooms will be created rather than dynamic ones. This flag will override the flag, if set. * The tag specifies users who are considered an owner of all rooms. You can specify multiple tags. Each tag must contain the bare jid of the user. * The tag enforces that a user must use the name part of their jid as their nick. Notice Settings: * The tag specifies the message shown when someone enters a room * The tag specifies the message shown when someone leaves a room * The tag specifies the message shown when someone changes their nick Note: All persistant rooms are now stored when created. This replaces the old system of configuring persistant rooms in the jabber.xml file Starting: --- o Start the mu-conference process, with the command: src/mu-conference -c muc.xml & --- jabber-muc-0.8/XEP0045_SUPPORT0000664000175000017500000000517211160655601015002 0ustar bencerbencer6. Entity Use Cases (100%) 6.1 Discovering Component Support for MUC : Yes 6.2 Discovering Rooms : Yes 6.3 Querying for Room Information : Yes 6.4 Querying for Room Items : Yes, only available for jid inside the room 6.5 Querying a Room Occupant : Yes, configurable for each room 6.6 Discovering Client Support for MUC : Not a component feature 7. Occupant Use Cases (85%) 7.1.1 Groupchat 1.0 Protocol : Yes, but the error is just bad request 7.1.2 Basic MUC Protocol : Yes 7.1.3 Presence Broadcast : Yes 7.1.4 Default Roles : Yes 7.1.5 Non-Anonymous Rooms : Yes 7.1.6 Semi-Anonymous Rooms : Yes 7.1.7 Password-Protected Rooms : Yes 7.1.8 Members-Only Rooms : Yes 7.1.9 Banned Users : Yes 7.1.10 Nickname Conflict : Yes 7.1.11 Max Users : Yes 7.1.12 Locked Room : Yes 7.1.13 Nonexistent Room : Yes 7.1.14 Room Logging : Yes 7.1.15 Discussion History : Yes 7.1.16 Managing Discussion History : Yes 7.2 Exiting a Room : Yes 7.3 Changing Nickname : Yes 7.4 Changing Availability Status : Yes 7.5 Inviting Another User to a Room : Yes 7.6 Converting a One-to-One Chat Into a Conference : Yes 7.7 Occupant Modification of the Room Subject : Yes 7.8 Sending a Private Message : Yes 7.9 Sending a Message to All Occupants : Yes 7.10 Registering with a Room : No 7.11 Discovering Reserved Room Nickname : Yes 7.12 Requesting Voice : No 8. Moderator Use Cases (83%) 8.1 Modifying the Room Subject : Yes 8.2 Kicking an Occupant : Yes 8.3 Granting Voice to a Visitor : Yes 8.4 Revoking Voice from a Participant : Yes 8.5 Modifying the Voice List : Yes 8.6 Approving Voice Requests : No 9. Admin Use Cases (88%/100%) 9.1 Banning a User : Yes 9.2 Modifying the Ban List : Yes 9.3 Granting Membership : Yes 9.4 Revoking Membership : Yes 9.5 Modifying the Member List : Yes 9.6 Granting Moderator Privileges : Yes 9.7 Revoking Moderator Privileges : Yes 9.8 Modifying the Moderator List : Yes 9.9 Approving Registration Requests : No 10. Owner Use Cases (100%) 10.1 Creating a Room 10.1.2 Creating an Instant Room : Yes 10.1.3 Creating a Reserved Room : Yes 10.1.4 Requesting a Unique Room Name : Yes 10.2 Subsequent Room Configuration : Yes 10.2.1 Notification of Configuration Changes : Yes (but don't send 104 on every change) 10.3 Granting Ownership Privileges : Yes 10.4 Revoking Ownership Privileges : Yes 10.5 Modifying the Owner List : Yes 10.6 Granting Administrative Privileges : Yes 10.7 Revoking Administrative Privileges : Yes 10.8 Modifying the Admin List : Yes 10.9 Destroying a Room : Yes 12. Internationalization Considerations : Not at the moment jabber-muc-0.8/muc-default.xml0000664000175000017500000000603511160655601015605 0ustar bencerbencer conference.localhost conference.localhost localhost 7009 secret ./spool/chat.localhost ./syslogs ./mu-conference.pid 124 Public Chatrooms This service is for public chatrooms. http://foo.bar/ 40 ./logs/ ../style.css has become available has left is now known as admin@localhost jabber-muc-0.8/TODO0000664000175000017500000000103711160655601013342 0ustar bencerbencerTODO High: * Refactoring * Do all the required stuffs of the XEP - Add code to handle registration in a room - Add code to handle for asking to get voiced - ... Medium: * Add in support for ad-hoc commands (XEP-0050) - Useful for clearing history * Add in support for MySQL logging Others: (Not in any order) * Add in user-definable room destruction delay * profanity filter? - May look into adding this as an external loadable module * Multiple service handling - Allow server 'divisions' dependant on requested server jid jabber-muc-0.8/FAQ0000664000175000017500000000337211160655601013210 0ustar bencerbencerFrequently Asked Questions: -------------------------- Q. Is this compatible with existing clients which don't support the MUC protocol? A. Yes. MU-Conference is backwardly compatible with the Groupchat protocol already in use in most clients. Note: there are a number of clients currently in use which use an old protocol known as the 'conferencing' or iq-conference protocol. This was part of an investigation into a new conferencing protocol, but was never documented or made a standard. As far as things are concerned, at the time of writing this (Apr 2003), there are two standard groupchat/conferencing protocols - GroupChat and MultiUser Chat (MUC) --- Q. What happened to having predefined rooms in the jabber.xml configuration file? A. The new system uses the concept of 'Persistent Rooms' When a new room is created and configured, you have the option to make the room persistent. This saves all the room configuration and affliation lists into the spool of the hosting jabber server. All persistent rooms are then recreated automatically whenever the service is restarted. These rooms will persist until the owner destroys it. --- Q. Why are my room logs not appearing? A. Make sure that the directory exists, and that room logging is enabled for that room. --- Q. How are the strange filenames, used in the storage of persistent room information, generated? A. The filename is a SHA1 hex digest of the room jid. There is a utility in the scripts directory which takes a list of jids as the command line options, and returns the SHA1 hex digests. --- Q. Is there any other dependancies? A. Yes, MU-Conference, as of v0.7, now requires pkg_config, libexpat, libidn and glib-2.0 installed. --- jabber-muc-0.8/COPYING0000664000175000017500000003545111160655601013714 0ustar bencerbencer GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS jabber-muc-0.8/Makefile0000664000175000017500000000015111160655601014306 0ustar bencerbencerall: cd src/ ; $(MAKE) clean: rm -f *~ rm -f include/*~ rm -f scripts/*~ cd src/ ; $(MAKE) clean