mu-conference-0.6.0/ 0040755 0001753 0000144 00000000000 07755324714 013142 5 ustar pere users mu-conference-0.6.0/FAQ 0100644 0001753 0000144 00000004632 07742764347 013504 0 ustar pere users Frequently 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. When I am trying to compile, it complains it can not find the
'platform-settings' file.
A. This is a file generated by the Jabberd configure program, when Jabberd
is first compiled. This dependancy will be removed as soon as the
external component plans are complete (see later question)
---
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 isn't this an external component?
A. This is planned for a later release. For anyone who doesn't understand, an
external component is a standalone program, which connects directly to the
main jabberd via TCP. It doesn't require loading inside a jabberd process.
Update: This has been scheduled for the v0.6.0 release. Thanks to Paul Curtis and the JCR.
---
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.6, now requires pkg_config and glib-2.0
installed.
---
mu-conference-0.6.0/src/ 0040755 0001753 0000144 00000000000 07755324731 013730 5 ustar pere users mu-conference-0.6.0/src/Makefile 0100644 0001753 0000144 00000001701 07755324540 015362 0 ustar pere users include ../../platform-settings
CFLAGS:=$(CFLAGS) -I../../jabberd -I../include `pkg-config --cflags glib-2.0`
LIBS:=$(LIBS) `pkg-config --libs glib-2.0`
# 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=conference.o conference_room.o conference_user.o utils.o xdata.o admin.o roles.o xdb.o hash.o
all: conference
conference: $(conference_OBJECTS)
ifeq ($(__CYGWIN__),1)
$(CC) $(CFLAGS) $(MCFLAGS) -o mu-conference.dll $(conference_OBJECTS) ../../jabberd/jabberd.a $(LDFLAGS) $(LIBS)
else
$(CC) $(CFLAGS) $(MCFLAGS) -o mu-conference.so $(conference_OBJECTS) $(LDFLAGS) $(LIBS)
endif
static: $(conference_OBJECTS)
single: $(conference_OBJECTS)
clean:
ifeq ($(__CYGWIN__),1)
rm -f $(conference_OBJECTS) mu-conference.dll
else
rm -f $(conference_OBJECTS) mu-conference.so *~
endif
mu-conference-0.6.0/src/xdb.c 0100644 0001753 0000144 00000044703 07755324542 014656 0 ustar pere users /*
* 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"
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);
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_new_tag("item");
xmlnode_put_attrib(item, "jid", jabberid);
xmlnode_insert_node(result, item);
xmlnode_free(item);
}
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_new_tag("item");
xmlnode_put_attrib(item, "jid", jabberid);
xmlnode_insert_node(item, info);
xmlnode_insert_node(result, item);
xmlnode_free(item);
}
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, 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->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", 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", 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
{
log_debug(NAME, "[%s] asked to add xdb info \n>%s<\n>%s< \n ", FZONE, xmlnode2str(old), xmlnode2str(item));
xmlnode_hide(item);
item = xmlnode_new_tag("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))));
if(node)
{
xmlnode_insert_node(item, node);
xmlnode_free(node);
}
xmlnode_insert_node(old, item);
xmlnode_free(item);
log_debug(NAME, "[%s] asked to add xdb info \n>%s<\n>%s< \n ", FZONE, xmlnode2str(old), xmlnode2str(item));
}
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;
}
log_debug(NAME, "[%s] xdb user registration disabled (%s)", FZONE, nick);
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;
}
log_debug(NAME, "[%s] xdb user registration disabled", FZONE);
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;
}
mu-conference-0.6.0/src/roles.c 0100644 0001753 0000144 00000023050 07755324541 015214 0 ustar pere users /*
* 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"
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 *key;
char ujid[256];
if(userid == NULL)
{
return -1;
}
snprintf(ujid, 256, "%s@%s", userid->user, userid->server);
key = j_strdup(ujid);
old = g_hash_table_lookup(hash, key);
/* 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)
{
xmlnode_free(store);
free(key);
return 0;
}
}
if(details != NULL)
{
xmlnode_free(store);
store = xmlnode_dup(details);
}
else if(userid->resource != NULL)
{
node = xmlnode_new_tag("item");
xmlnode_put_attrib(node, "jid", jid_full(userid));
xmlnode_insert_node(store, node);
xmlnode_free(node);
}
g_hash_table_insert(hash, key, store);
return 1;
}
int remove_affiliate(GHashTable *hash, jid userid)
{
xmlnode old;
xmlnode store;
xmlnode node;
char *key;
char ujid[256];
if(userid == NULL)
{
return -1;
}
snprintf(ujid, 256, "%s@%s", userid->user, userid->server);
key = j_strdup(ujid);
old = g_hash_table_lookup(hash, key);
free(key);
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);
key = j_strdup(ujid);
g_hash_table_insert(hash, key, store);
return 1;
}
xmlnode revoke_affiliate(cnr room, GHashTable *hash, jid userid)
{
cnu user;
jid jabberid;
xmlnode store;
xmlnode current;
char *userjid;
char *key;
char ujid[256];
if(userid == NULL)
{
return NULL;
}
snprintf(ujid, 256, "%s@%s", userid->user, userid->server);
key = j_strdup(ujid);
store = g_hash_table_lookup(hash, key);
free(key);
if(store == NULL)
return NULL;
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);
}
}
}
}
key = j_strdup(ujid);
g_hash_table_remove(hash, key);
free(key);
return NULL;
}
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[256];
if(affiliation == NULL || sender == NULL || user == NULL)
{
log_warn(NAME, "[%s] Missing attributes", FZONE);
return;
}
snprintf(ujid, 256, "%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 */
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)
{
add_affiliate(room->owner, user, NULL);
}
else if(j_strcmp(affiliation, "admin") == 0)
{
add_affiliate(room->admin, user, NULL);
}
else if(j_strcmp(affiliation, "member") == 0)
{
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);
}
}
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(jid_fix(from->realid))));
xmlnode_put_attrib(data, "nick", xmlnode_get_data(from->nick));
}
else
{
xmlnode_put_attrib(data, "actor", jid_full(jid_fix(by)));
}
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 = j_strdup(jid_full(user->realid));
log_debug(NAME, "[%s] About to revoke role [%s]", FZONE, key);
g_hash_table_remove(hash, key);
free(key);
}
void change_role(char *role, cnu sender, jid user, char *reason)
{
char *key, *result;
cnr room;
cnu data;
jid userid;
trole current;
xmlnode 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;
key = j_strdup(jid_full(user));
data = g_hash_table_lookup(room->remote, key);
free(key);
if(data == NULL)
{
if(user->resource == NULL)
{
userlist = get_roster(room, user);
if(userlist != NULL)
{
for(node = xmlnode_get_firstchild(userlist); node != NULL; node = 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;
}
mu-conference-0.6.0/src/conference_room.c 0100644 0001753 0000144 00000126212 07755324541 017237 0 ustar pere users /*
* MU-Conference - Multi-User Conference Service
* Copyright (c) 2002 David Sutton
*
*
* This program is free software; you can redistribute it and/or drvify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
*/
#include "conference.h"
extern int deliver__flag;
/* 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[50];
size_t timelen = 49;
FILE *logfile;
pool p;
if(message == NULL || room == NULL)
{
log_warn(NAME, "[%s] ERR: Aborting - NULL reference found - [%s][%s]", FZONE, message, room);
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);
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, 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", 3) == 0)
{
output = extractAction(strescape(p, message), p);
fprintf(logfile, "%s * %s%s
\n", timestr, nick, output);
}
else
{
fprintf(logfile, "%s <%s> %s
\n", timestr, nick, strescape(p, message));
}
}
else
{
fprintf(logfile, "%s --- %s
\n", timestr, message);
}
}
else
{
if(nick)
{
if(j_strncmp(message, "/me", 3) == 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;
struct stat fileinfo;
time_t now = time(NULL);
int type;
pool p;
spool sp;
if(room == NULL)
{
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) < 0)
{
log_warn(NAME, "[%s] ERR: unable to open log directory >%s<", FZONE, filename);
return;
}
curdate = dateget(now);
if(type == LOG_XML)
spooler(sp, "/", curdate, ".xml", sp);
else if(type == LOG_XHTML)
spooler(sp, "/", curdate, ".html", sp);
else
spooler(sp, "/", curdate, ".txt", sp);
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
\nLogs for %s, %s\n\n\n", jid_full(room->id), curdate);
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");
fflush(logfile);
}
fclose(room->logfile);
room->logfile = NULL;
}
void con_room_send_invite(cnu sender, xmlnode node)
{
xmlnode result;
xmlnode element;
xmlnode invite;
char *body, *user, *reason, *inviter;
cnr room;
jid from;
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;
from = sender->realid;
invite = xmlnode_get_tag(node, "invite");
user = xmlnode_get_attrib(invite, "to");
reason = xmlnode_get_tag_data(invite, "reason");
if(room->public == 1)
{
inviter = jid_full(jid_user(jid_fix(from)));
}
else
{
inviter = xmlnode_get_data(sender->nick);
}
xmlnode_put_attrib(invite, "from", inviter);
xmlnode_hide_attrib(invite, "to");
p = xmlnode_pool(node);
if(reason == NULL)
{
reason = spools(p, "None given", p);
}
body = spools(p, "You have been invited to the ", jid_full(jid_fix(room->id)), " room by ", inviter, "\nReason: ", reason, p);
result = jutil_msgnew("normal", user , "Invitation", body);
xmlnode_put_attrib(result, "from", jid_full(jid_fix(room->id)));
if(room->secret != NULL)
{
xmlnode_insert_cdata(xmlnode_insert_tag(invite, "password"), room->secret, -1);
}
xmlnode_insert_node(result, node);
element = xmlnode_insert_tag(result, "x");
xmlnode_put_attrib(element, "jid", jid_full(jid_fix(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);
xmlnode_free(node);
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;
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);
}
void _con_room_usernick(gpointer key, gpointer data, gpointer arg)
{
cnu user = (cnu)data;
xmlnode x = (xmlnode)arg;
if(user == NULL || x == NULL)
{
log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE);
return;
}
if(j_strcmp(xmlnode_get_data(x),xmlnode_get_data(user->nick)) == 0)
xmlnode_put_vattrib(x, "u", (void*)user);
}
cnu con_room_usernick(cnr room, char *nick)
{
cnu user;
xmlnode node = xmlnode_new_tag("nick");
if(room == NULL)
{
log_warn(NAME, "[%s] Aborting - NULL attribute found", FZONE);
return NULL;
}
log_debug(NAME, "[%s] searching for nick %s in room %s", FZONE, nick, jid_full(jid_fix(room->id)));
xmlnode_insert_cdata(node, nick, -1);
g_hash_table_foreach(room->local, _con_room_usernick, (void *)node);
user = (cnu)xmlnode_get_vattrib(node,"u");
xmlnode_free(node);
return user;
}
/* 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(jid_fix(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(con_room_usernick(room, 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(con_room_usernick(room, nick) == NULL)
break;
}
if(is_registered(room->master, jid_full(jid_user(jid_fix(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_strncmp(xmlnode_get_name(x),"presence",8) == 0)
{
output = add_extended_presence(from, to, x, NULL, NULL, NULL);
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, xmlnode_get_data(user->nick), JID_RESOURCE);
xmlnode_put_attrib(xml, "category", "user");
xmlnode_put_attrib(xml, "type", "client");
xmlnode_put_attrib(xml, "name", xmlnode_get_data(user->nick));
xmlnode_put_attrib(xml, "jid", jid_full(userjid));
}
void _con_room_discoinfo(cnr room, jpacket jp)
{
xmlnode result;
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(j_strlen(room->secret) > 0)
xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", "muc_password");
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");
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, xmlnode_get_data(user->nick), JID_RESOURCE);
xmlnode_put_attrib(xml, "jid", jid_full(userjid));
}
void con_room_outsider(cnr room, cnu from, jpacket jp)
{
xmlnode q;
int start;
char nstr[10];
time_t t;
char *str;
if(room == NULL)
{
log_warn(NAME, "[%s] Aborting - NULL attribute found -%s- -%s-", FZONE, room);
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))
{
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);
deliver(dpacket_new(jp->x),NULL);
return;
}
else if(NSCHECK(jp->iq, NS_BROWSE))
{
jutil_iqresult(jp->x);
q = xmlnode_insert_tag(jp->x,"item");
xmlnode_put_attrib(q,"category","conference");
if(room->public && room->invitation == 0)
{
xmlnode_put_attrib(q,"type","public");
g_hash_table_foreach(room->local, con_room_browsewalk, (void*)q);
}
else if(room->public && is_member(room, jp->from))
{
xmlnode_put_attrib(q,"type","public");
g_hash_table_foreach(room->local, con_room_browsewalk, (void*)q);
}
else
{
xmlnode_put_attrib(q,"type","private");
}
xmlnode_put_attrib(q,"xmlns",NS_BROWSE);
xmlnode_put_attrib(q,"name",room->name);
xmlnode_put_attrib(q,"version",VERSION);
xmlnode_insert_cdata(xmlnode_insert_tag(q,"ns"),NS_MUC,-1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_DISCO, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_BROWSE, -1);
/* xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_REGISTER, -1); */
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_VERSION, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_LAST, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_TIME, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_VCARD, -1);
deliver(dpacket_new(jp->x), NULL);
return;
}
else if(NSCHECK(jp->iq, NS_DISCO_INFO))
{
log_debug(NAME, "[%s] Outside room packet - Disco Info Request", FZONE);
_con_room_discoinfo(room, jp);
return;
}
else if(NSCHECK(jp->iq, NS_DISCO_ITEMS))
{
log_debug(NAME, "[%s] Outside room packet - Disco Items Request", FZONE);
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"query"), "xmlns", NS_DISCO_ITEMS);
jpacket_reset(jp);
deliver(dpacket_new(jp->x), NULL);
return;
}
else if(NSCHECK(jp->iq, NS_LAST))
{
log_debug(NAME, "[%s] Outside room 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) - room->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_TIME))
{
/* Compliant with JEP-0090 */
log_debug(NAME, "[%s] Server packet - Time Request", FZONE);
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x, "query"), "xmlns", NS_TIME);
jpacket_reset(jp);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "utc"), jutil_timestamp(), -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "tz"), tzname[0], -1);
/* create nice display time */
t = time(NULL);
str = ctime(&t);
str[strlen(str) - 1] = '\0'; /* cut off newline */
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "display"), pstrdup(jp->p, str), -1);
free(str);
deliver(dpacket_new(jp->x),NULL);
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(!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;
}
}
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;
char *str;
time_t t;
int start;
char nstr[10];
xmlnode result, item, x, node;
jid id;
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/%s) to room %s", FZONE, jid_full(from->realid), from->localid->resource, xmlnode_get_data(from->nick), 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(jid_user(from->localid)));
jid_set(jp->from, xmlnode_get_data(from->nick), JID_RESOURCE);
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, xmlnode_get_data(from->nick), -1);
xmlnode_insert_cdata(room->topic, " has set the topic to: ", -1);
xmlnode_insert_cdata(room->topic, xmlnode_get_data(x), -1);
/* 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");
/* 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, xmlnode_get_data(from->nick), xmlnode_get_tag_data(jp->x, "body"));
/* Save from address */
id = jid_new(xmlnode_pool(node), jid_full(from->localid));
jid_set(id, xmlnode_get_data(from->nick), JID_RESOURCE);
xmlnode_put_attrib(node, "from", jid_full(id));
/* store in history */
jutil_delay(node, jid_full(room->id));
if(room->master->history > 0)
{
if(++room->hlast == room->master->history)
room->hlast = 0;
xmlnode_free(room->history[room->hlast]);
room->history[room->hlast] = node;
}
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);
xdata_handler(room, from, jp);
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))
{
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);
deliver(dpacket_new(jp->x),NULL);
return;
}
else if(NSCHECK(jp->iq, NS_BROWSE))
{
jutil_iqresult(jp->x);
result = xmlnode_insert_tag(jp->x,"item");
xmlnode_put_attrib(result,"category", "conference");
xmlnode_put_attrib(result,"xmlns", NS_BROWSE);
xmlnode_put_attrib(result,"name", room->name);
if(room->public)
{
xmlnode_put_attrib(result,"type", "public");
}
else
{
xmlnode_put_attrib(result,"type", "private");
}
xmlnode_insert_cdata(xmlnode_insert_tag(result,"ns"),NS_MUC,-1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_DISCO, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_BROWSE, -1);
/* xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_REGISTER, -1); */
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_VERSION, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_LAST, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_TIME, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_VCARD, -1);
g_hash_table_foreach(room->local, con_room_browsewalk, (void*)result);
deliver(dpacket_new(jp->x), NULL);
return;
}
else if(NSCHECK(jp->iq, NS_DISCO_INFO))
{
log_debug(NAME, "[%s] room packet - Disco Info Request", FZONE);
_con_room_discoinfo(room, jp);
return;
}
else if(NSCHECK(jp->iq, NS_DISCO_ITEMS))
{
log_debug(NAME, "[%s] room packet - Disco Items Request", FZONE);
jutil_iqresult(jp->x);
result = xmlnode_insert_tag(jp->x, "query");
xmlnode_put_attrib(result, "xmlns", NS_DISCO_ITEMS);
g_hash_table_foreach(room->local, _con_room_discoitem, (void*)result);
deliver(dpacket_new(jp->x), NULL);
return;
}
else if(NSCHECK(jp->iq, NS_LAST))
{
log_debug(NAME, "[%s] room 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) - room->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_TIME))
{
/* Compliant with JEP-0090 */
log_debug(NAME, "[%s] Server packet - Time Request", FZONE);
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x, "query"), "xmlns", NS_TIME);
jpacket_reset(jp);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "utc"), jutil_timestamp(), -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "tz"), tzname[0], -1);
/* create nice display time */
t = time(NULL);
str = ctime(&t);
str[strlen(str) - 1] = '\0'; /* cut off newline */
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "display"), pstrdup(jp->p, str), -1);
deliver(dpacket_new(jp->x),NULL);
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;
}
}
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 xdata, 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;
/* Initialise room history */
room->history = pmalloco(p, sizeof(xmlnode) * master->history); /* make array of xmlnodes */
log_debug(NAME, "[%s] Malloc: history = %d", FZONE, sizeof(xmlnode) * 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, ght_remove_key, 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);
if(xdata > 0 )
xdata_room_config(room,admin,1,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);
}
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;
}
output = xmlnode_dup((xmlnode)x);
if(!is_legacy(user))
{
xmlnode_free(output);
return;
}
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);
log_debug(NAME, "[%s] zapping room %s from list", 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;
xmlnode_free(room->history[h]);
if(h == room->hlast)
break;
}
}
}
mu-conference-0.6.0/src/conference_user.c 0100644 0001753 0000144 00000041350 07755324541 017240 0 ustar pere users /*
* MU-Conference - Multi-User Conference Service
* Copyright (c) 2002 David Sutton
*
*
* This program is free software; you can redistribute it and/or drvify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
*/
#include "conference.h"
extern int deliver__flag;
cnu con_user_new(cnr room, jid id)
{
pool p;
cnu user;
char *key;
log_debug(NAME, "[%s] adding user %s to room %s", FZONE, jid_full(jid_fix(id)), jid_full(jid_fix(room->id)));
p = pool_new(); /* Create pool for user struct */
user = pmalloco(p, sizeof(_cnu));
user->p = p;
user->realid = jid_new(user->p, jid_full(jid_fix(id)));
user->room = room;
user->presence = jutil_presnew(JPACKET__AVAILABLE, NULL, NULL);
key = j_strdup(jid_full(user->realid));
g_hash_table_insert(room->remote, key, (void*)user);
/* Add this user to the room roster */
add_roster(room, user->realid);
/* If admin, switch */
if(is_admin(room, user->realid) && !is_moderator(room, user->realid))
{
log_debug(NAME, "[%s] Adding %s to moderator list", FZONE, jid_full(jid_fix(user->realid)));
/* Update affiliate info */
add_affiliate(room->admin, user->realid, NULL);
add_role(room->moderator, user);
}
else if(is_member(room, user->realid) && !is_admin(room, user->realid))
{
/* Update affiliate information */
log_debug(NAME, "[%s] Updating %s in the member list", FZONE, jid_full(user->realid));
add_affiliate(room->member, user->realid, NULL);
add_role(room->participant, user);
}
else if(room->moderated == 1 && room->defaulttype == 1)
{
/* Auto-add to participant list if moderated and participant type is default */
add_role(room->participant, user);
}
return user;
}
void _con_user_history_send(cnu to, xmlnode node)
{
if(to == NULL || node == NULL)
{
return;
}
xmlnode_put_attrib(node, "to", jid_full(to->realid));
deliver(dpacket_new(node), NULL);
return;
}
void _con_user_nick(gpointer key, gpointer data, gpointer arg)
{
cnu to = (cnu)data;
cnu from = (cnu)arg;
char *old, *status, *reason, *actor;
xmlnode node;
xmlnode result;
xmlnode element;
jid fullid;
/* send unavail pres w/ old nick */
if((old = xmlnode_get_attrib(from->nick,"old")) != NULL)
{
if(xmlnode_get_data(from->nick) != NULL)
{
node = jutil_presnew(JPACKET__UNAVAILABLE, jid_full(to->realid), NULL);
}
else
{
node = xmlnode_dup(from->presence);
xmlnode_put_attrib(node, "to", jid_full(to->realid));
}
fullid = jid_new(xmlnode_pool(node), jid_full(from->localid));
jid_set(fullid, old, JID_RESOURCE);
xmlnode_put_attrib(node, "from", jid_full(fullid));
status = xmlnode_get_attrib(from->nick,"status");
log_debug(NAME, "[%s] status = %s", FZONE, status);
reason = xmlnode_get_attrib(from->nick,"reason");
actor = xmlnode_get_attrib(from->nick,"actor");
if(xmlnode_get_data(from->nick) != NULL)
{
log_debug(NAME, "[%s] Extended presence - Nick Change", FZONE);
result = add_extended_presence(from, to, node, STATUS_MUC_NICKCHANGE, NULL, NULL);
}
else
{
log_debug(NAME, "[%s] Extended presence", FZONE);
result = add_extended_presence(from, to, node, status, reason, actor);
}
deliver(dpacket_new(result), NULL);
xmlnode_free(node);
}
/* if there's a new nick, broadcast that too */
if(xmlnode_get_data(from->nick) != NULL)
{
status = xmlnode_get_attrib(from->nick,"status");
log_debug(NAME, "[%s] status = %s/%s", FZONE, status, STATUS_MUC_CREATED);
if(j_strcmp(status, STATUS_MUC_CREATED) == 0)
node = add_extended_presence(from, to, NULL, status, NULL, NULL);
else
node = add_extended_presence(from, to, NULL, NULL, NULL, NULL);
/* Hide x:delay, not needed */
element = xmlnode_get_tag(node, "x?xmlns=jabber:x:delay");
if(element)
xmlnode_hide(element);
xmlnode_put_attrib(node, "to", jid_full(to->realid));
fullid = jid_new(xmlnode_pool(node), jid_full(from->localid));
jid_set(fullid, xmlnode_get_data(from->nick), JID_RESOURCE);
xmlnode_put_attrib(node, "from", jid_full(fullid));
deliver(dpacket_new(node), NULL);
}
}
void con_user_nick(cnu user, char *nick, xmlnode data)
{
xmlnode node;
char *status, *reason, *actor;
cnr room = user->room;
log_debug(NAME, "[%s] in room %s changing nick for user %s to %s from %s", FZONE, jid_full(room->id), jid_full(user->realid), nick, xmlnode_get_data(user->nick));
node = xmlnode_new_tag("n");
xmlnode_put_attrib(node, "old", xmlnode_get_data(user->nick));
if (data)
{
status = xmlnode_get_attrib(data, "status");
reason = xmlnode_get_data(data);
actor = xmlnode_get_attrib(data, "actor");
if(status)
xmlnode_put_attrib(node, "status", status);
if(reason)
xmlnode_put_attrib(node, "reason", reason);
if(actor)
xmlnode_put_attrib(node, "actor", actor);
log_debug(NAME, "[%s] status = %s", FZONE, status);
}
xmlnode_insert_cdata(node,nick,-1);
xmlnode_free(user->nick);
user->nick = node;
deliver__flag = 0;
g_hash_table_foreach(room->local, _con_user_nick, (void*)user);
deliver__flag = 1;
deliver(NULL, NULL);
/* send nick change notice if availble */
if(room->note_rename != NULL && nick != NULL && xmlnode_get_attrib(node, "old") != NULL && j_strlen(room->note_rename) > 0)
con_room_send(room, jutil_msgnew("groupchat", NULL, NULL, spools(xmlnode_pool(node), xmlnode_get_attrib(node, "old"), " ", room->note_rename, " ", nick, xmlnode_pool(node))), SEND_LEGACY);
}
void _con_user_enter(gpointer key, gpointer data, gpointer arg)
{
cnu from = (cnu)data;
cnu to = (cnu)arg;
xmlnode node;
jid fullid;
/* mirror */
if(from == to)
return;
node = add_extended_presence(from, to, NULL, NULL, NULL, NULL);
xmlnode_put_attrib(node, "to", jid_full(to->realid));
fullid = jid_new(xmlnode_pool(node), jid_full(from->localid));
jid_set(fullid, xmlnode_get_data(from->nick), JID_RESOURCE);
xmlnode_put_attrib(node, "from", jid_full(fullid));
deliver(dpacket_new(node), NULL);
}
void con_user_enter(cnu user, char *nick, int created)
{
xmlnode node;
xmlnode message;
char *key;
int h, tflag = 0;
cnr room = user->room;
user->localid = jid_new(user->p, jid_full(room->id));
jid_set(user->localid, shahash(jid_full(user->realid)), JID_RESOURCE);
key = j_strdup(user->localid->resource);
g_hash_table_insert(room->local, key, (void*)user);
room->count++;
log_debug(NAME, "[%s] officiating user %s in room (created = %d) %s as %s/%s", FZONE, jid_full(user->realid), created, jid_full(room->id), nick, user->localid->resource);
/* Send presence back to user to confirm presence received */
if(created == 1)
{
/* Inform if room just created */
node = xmlnode_new_tag("reason");
xmlnode_put_attrib(node, "status", STATUS_MUC_CREATED);
con_user_nick(user, nick, node); /* pushes to everyone (including ourselves) our entrance */
xmlnode_free(node);
}
else
{
con_user_nick(user, nick, NULL); /* pushes to everyone (including ourselves) our entrance */
}
/* Send Room MOTD */
if(j_strlen(room->description) > 0)
{
message = jutil_msgnew("groupchat", jid_full(user->realid), NULL, room->description);
xmlnode_put_attrib(message,"from", jid_full(room->id));
deliver(dpacket_new(message), NULL);
}
/* Send Room protocol message to legacy clients */
if(is_legacy(user))
{
message = jutil_msgnew("groupchat", jid_full(user->realid), NULL, spools(user->p, "This room supports the MUC protocol.", user->p));
xmlnode_put_attrib(message,"from", jid_full(room->id));
deliver(dpacket_new(message), NULL);
}
/* Send Room Lock warning if necessary */
if(room->locked > 0)
{
message = jutil_msgnew("groupchat", jid_full(user->realid), NULL, spools(user->p, "This room is locked from entry until configuration is confirmed.", user->p));
xmlnode_put_attrib(message,"from", jid_full(room->id));
deliver(dpacket_new(message), NULL);
}
/* Update my roster with current users */
g_hash_table_foreach(room->local, _con_user_enter, (void*)user);
/* Switch to queue mode */
deliver__flag = 0;
/* XXX Require new history handler */
/* loop through history and send back */
if(room->master->history > 0)
{
//if (xmlnode_get_attrib(user->history, "maxchars") != NULL )
h = room->hlast;
while(1)
{
h++;
if(h == room->master->history)
h = 0;
_con_user_history_send(user, xmlnode_dup(room->history[h]));
if(xmlnode_get_tag(room->history[h],"subject") != NULL)
tflag = 1;
if(h == room->hlast)
break;
}
}
/* Re-enable delivery */
deliver__flag = 1;
/* Send queued messages */
deliver(NULL, NULL);
/* send last know topic */
if(tflag == 0 && room->topic != NULL)
{
node = jutil_msgnew("groupchat", jid_full(user->realid), xmlnode_get_attrib(room->topic,"subject"), xmlnode_get_data(room->topic));
xmlnode_put_attrib(node, "from", jid_full(room->id));
deliver(dpacket_new(node), NULL);
}
/* send entrance notice if available */
if(room->note_join != NULL && j_strlen(room->note_join) > 0)
con_room_send(room, jutil_msgnew("groupchat", NULL, NULL, spools(user->p, nick, " ", room->note_join, user->p)), SEND_LEGACY);
/* Send 'non-anonymous' message if necessary */
if(room->visible == 1)
con_send_alert(user, NULL, NULL, STATUS_MUC_SHOWN_JID);
}
void con_user_process(cnu to, cnu from, jpacket jp)
{
xmlnode node, element;
cnr room = to->room;
char str[10];
int t;
/* we handle all iq's for this id, it's *our* id */
if(jp->type == JPACKET_IQ)
{
if(NSCHECK(jp->iq,NS_BROWSE))
{
jutil_iqresult(jp->x);
node = xmlnode_insert_tag(jp->x, "item");
xmlnode_put_attrib(node, "category", "user");
xmlnode_put_attrib(node, "xmlns", NS_BROWSE);
xmlnode_put_attrib(node, "name", xmlnode_get_data(to->nick));
element = xmlnode_insert_tag(node, "item");
xmlnode_put_attrib(element, "category", "user");
if(room->visible == 1 || is_moderator(room, from->realid))
xmlnode_put_attrib(element, "jid", jid_full(to->realid));
else
xmlnode_put_attrib(element, "jid", jid_full(to->localid));
if(is_legacy(to))
xmlnode_insert_cdata(xmlnode_insert_tag(node, "ns"), NS_GROUPCHAT, -1);
else
xmlnode_insert_cdata(xmlnode_insert_tag(node, "ns"), NS_MUC, -1);
deliver(dpacket_new(jp->x), NULL);
return;
}
if(NSCHECK(jp->iq,NS_LAST))
{
jutil_iqresult(jp->x);
node = xmlnode_insert_tag(jp->x, "query");
xmlnode_put_attrib(node, "xmlns", NS_LAST);
t = time(NULL) - to->last;
sprintf(str,"%d",t);
xmlnode_put_attrib(node ,"seconds", str);
deliver(dpacket_new(jp->x), NULL);
return;
}
/* deny any other iq's if it's private */
if(to->private == 1)
{
jutil_error(jp->x, TERROR_FORBIDDEN);
deliver(dpacket_new(jp->x), NULL);
return;
}
/* if not, fall through and just forward em on I guess! */
}
/* Block possibly faked groupchat messages - groupchat is not meant for p2p chats */
if(jp->type == JPACKET_MESSAGE)
{
if(jp->subtype == JPACKET__GROUPCHAT)
{
jutil_error(jp->x, TERROR_BAD);
deliver(dpacket_new(jp->x), NULL);
return;
}
if(room->privmsg == 1 && !is_admin(room, from->realid))
{
/* Only error on messages with body, otherwise just drop */
if(xmlnode_get_tag(jp->x, "body") != NULL)
{
jutil_error(jp->x, TERROR_MUC_PRIVMSG);
deliver(dpacket_new(jp->x), NULL);
return;
}
else
{
xmlnode_free(jp->x);
return;
}
}
}
con_user_send(to, from, jp->x);
}
void con_user_send(cnu to, cnu from, xmlnode node)
{
jid fullid;
if(to == NULL || from == NULL || node == NULL)
{
return;
}
fullid = jid_new(xmlnode_pool(node), jid_full(from->localid));
xmlnode_put_attrib(node, "to", jid_full(to->realid));
if(xmlnode_get_attrib(node, "cnu") != NULL)
xmlnode_hide_attrib(node, "cnu");
jid_set(fullid, xmlnode_get_data(from->nick), JID_RESOURCE);
xmlnode_put_attrib(node, "from", jid_full(fullid));
deliver(dpacket_new(node), NULL);
}
void con_user_zap(cnu user, xmlnode data)
{
cnr room;
char *reason;
char *status;
char *key;
if(user == NULL || data == NULL)
{
log_warn(NAME, "Aborting: NULL attribute found", FZONE);
if(data != NULL)
xmlnode_free(data);
return;
}
user->leaving = 1;
key = pstrdup(user->p, jid_full(user->realid));
status = xmlnode_get_attrib(data, "status");
reason = xmlnode_get_data(data);
room = user->room;
if(room == NULL)
{
log_warn(NAME, "[%s] Unable to zap user %s <%s-%s> : Room does not exist", FZONE, jid_full(user->realid), status, reason);
xmlnode_free(data);
return;
}
log_debug(NAME, "[%s] zapping user %s <%s-%s>", FZONE, jid_full(user->realid), status, reason);
if(user->localid != NULL)
{
con_user_nick(user, NULL, data); /* sends unavailve */
log_debug(NAME, "[%s] Removing entry from local list", FZONE);
g_hash_table_remove(room->local, user->localid->resource);
room->count--;
/* send departure notice if available*/
if(room->note_leave != NULL && j_strlen(room->note_leave) > 0)
{
if(reason != NULL)
{
if(j_strcmp(status, STATUS_MUC_KICKED) == 0)
{
con_room_send(room,jutil_msgnew("groupchat",NULL,NULL,spools(user->p, xmlnode_get_attrib(user->nick,"old")," ",room->note_leave,": [Kicked] ", reason, user->p)), SEND_LEGACY);
}
else if(j_strcmp(status, STATUS_MUC_BANNED) == 0)
{
con_room_send(room,jutil_msgnew("groupchat",NULL,NULL,spools(user->p,xmlnode_get_attrib(user->nick,"old")," ",room->note_leave,": [Banned] ", reason, user->p)), SEND_LEGACY);
}
else
{
con_room_send(room,jutil_msgnew("groupchat",NULL,NULL,spools(user->p,xmlnode_get_attrib(user->nick,"old")," ",room->note_leave,": ", reason, user->p)), SEND_LEGACY);
}
}
else
{
con_room_send(room,jutil_msgnew("groupchat",NULL,NULL,spools(user->p,xmlnode_get_attrib(user->nick,"old")," ",room->note_leave,user->p)), SEND_LEGACY);
}
}
}
xmlnode_free(data);
log_debug(NAME, "[%s] Removing any affiliate info from admin list", FZONE);
log_debug(NAME, "[%s] admin list size [%d]", FZONE, g_hash_table_size(room->admin));
remove_affiliate(room->admin, user->realid);
log_debug(NAME, "[%s] Removing any affiliate info from member list", FZONE);
log_debug(NAME, "[%s] member list size [%d]", FZONE, g_hash_table_size(room->member));
remove_affiliate(room->member, user->realid);
log_debug(NAME, "[%s] Removing any role info from moderator list", FZONE);
log_debug(NAME, "[%s] moderator list size [%d]", FZONE, g_hash_table_size(room->moderator));
revoke_role(room->moderator, user);
log_debug(NAME, "[%s] Removing any role info from participant list", FZONE);
log_debug(NAME, "[%s] participant list size [%d]", FZONE, g_hash_table_size(room->participant));
revoke_role(room->participant, user);
log_debug(NAME, "[%s] Removing any roster info from roster list", FZONE);
remove_roster(room, user->realid);
log_debug(NAME, "[%s] Un-alloc presence xmlnode", FZONE);
xmlnode_free(user->presence);
log_debug(NAME, "[%s] Un-alloc nick xmlnode", FZONE);
xmlnode_free(user->nick);
log_debug(NAME, "[%s] Un-alloc history xmlnode", FZONE);
xmlnode_free(user->history);
log_debug(NAME, "[%s] Removing from remote list and un-alloc cnu", FZONE);
g_hash_table_remove(room->remote, jid_full(user->realid));
}
mu-conference-0.6.0/src/conference.c 0100644 0001753 0000144 00000105153 07755324540 016203 0 ustar pere users /*
* MU-Conference - Multi-User Conference Service
* Copyright (c) 2002 David Sutton
*
*
* This program is free software; you can redistribute it and/or drvify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
*/
#include "conference.h"
#include
void con_server_browsewalk(gpointer key, gpointer data, gpointer arg)
{
cnr room = (cnr)data;
jpacket jp = (jpacket)arg;
char users[10];
char maxu[10];
xmlnode x;
if(room == NULL)
{
log_warn(NAME, "[%s] Aborting: NULL room %s", FZONE, key);
return;
}
/* We can only show the private rooms that the user already knows about */
if(room->public == 0 && !in_room(room, jp->to) && !is_admin(room, jp->to) && !is_member(room, jp->to))
return;
/* Unconfigured rooms don't exist either */
if(room->locked == 1)
return;
x = xmlnode_insert_tag(jp->iq, "item");
xmlnode_put_attrib(x, "category", "conference");
if(room->public == 0)
xmlnode_put_attrib(x, "type", "private");
else
xmlnode_put_attrib(x, "type", "public");
xmlnode_put_attrib(x, "jid", jid_full(room->id));
if(room->maxusers > 0)
xmlnode_put_attrib(x, "name", spools(jp->p, room->name, " (", itoa(room->count, users), "/", itoa(room->maxusers, maxu), ")", jp->p));
else
xmlnode_put_attrib(x, "name", spools(jp->p, room->name, " (", itoa(room->count, users), ")", jp->p));
}
void _server_discowalk(gpointer key, gpointer data, gpointer arg)
{
cnr room = (cnr)data;
jpacket jp = (jpacket)arg;
xmlnode x;
if(room == NULL)
{
log_warn(NAME, "[%s] Aborting: NULL room %s", FZONE, key);
return;
}
/* if we're a private server, we can only show the rooms the user already knows about */
if(room->public == 0 && !in_room(room, jp->to) && !is_admin(room, jp->to) && !is_member(room, jp->to))
return;
/* Unconfigured rooms don't exist either */
if(room->locked == 1)
return;
x = xmlnode_insert_tag(jp->iq, "item");
xmlnode_put_attrib(x, "jid", jid_full(room->id));
xmlnode_put_attrib(x, "name", spools(jp->p, room->name, jp->p));
}
void con_server(cni master, jpacket jp)
{
char *str;
struct utsname un;
xmlnode x;
int start, status;
time_t t;
char *from;
char nstr[10];
jid user;
log_debug(NAME, "[%s] server packet", FZONE);
if(jp->type == JPACKET_PRESENCE)
{
log_debug(NAME, "[%s] Server packet: Presence - Not Implemented", FZONE);
jutil_error(jp->x, TERROR_NOTIMPL);
deliver(dpacket_new(jp->x),NULL);
return;
}
if(jp->type != JPACKET_IQ)
{
log_debug(NAME, "[%s] Server packet: Dropping non-IQ packet", FZONE);
xmlnode_free(jp->x);
return;
}
/* Action by subpacket type */
if(jpacket_subtype(jp) == JPACKET__SET)
{
log_debug(NAME, "[%s] Server packet - IQ Set", FZONE);
if(NSCHECK(jp->iq,NS_REGISTER))
{
/* Disabled until checked */
log_debug(NAME, "[%s] TERROR_NOTIMPL", FZONE);
jutil_error(jp->x, TERROR_NOTIMPL);
deliver(dpacket_new(jp->x),NULL);
return;
log_debug(NAME, "[%s] Server packet - Registration Handler", FZONE);
str = xmlnode_get_tag_data(jp->iq, "name");
x = xmlnode_get_tag(jp->iq, "remove");
from = xmlnode_get_attrib(jp->x, "from");
user = jid_new(jp->p, from);
status = is_registered(master, jid_full(jid_user(user)), str);
if(x)
{
log_debug(NAME, "[%s] Server packet - UnReg Submission", FZONE);
set_data(master, str, from, NULL, 1);
jutil_iqresult(jp->x);
}
else if(status == -1)
{
log_debug(NAME, "[%s] Server packet - Registration Submission : Already taken", FZONE);
jutil_error(jp->x, TERROR_MUC_NICKREG);
}
else if(status == 0)
{
log_debug(NAME, "[%s] Server packet - Registration Submission", FZONE);
set_data(master, str, from, NULL, 0);
jutil_iqresult(jp->x);
}
else
{
log_debug(NAME, "[%s] Server packet - Registration Submission : already set", FZONE);
jutil_iqresult(jp->x);
}
deliver(dpacket_new(jp->x), NULL);
}
}
else if(jpacket_subtype(jp) == JPACKET__GET)
{
/* now we're all set */
if(NSCHECK(jp->iq,NS_REGISTER))
{
log_debug(NAME, "[%s] Server packet - Registration Request", FZONE);
/* Disabled until checked */
log_debug(NAME, "[%s] TERROR_NOTIMPL", FZONE);
jutil_error(jp->x, TERROR_NOTIMPL);
deliver(dpacket_new(jp->x),NULL);
return;
x = get_data_byjid(master, xmlnode_get_attrib(jp->x, "from"));
str = xmlnode_get_attrib(x, "nick") ? xmlnode_get_attrib(x, "nick") : "";
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x, "query"), "xmlns", NS_REGISTER);
jpacket_reset(jp);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "instructions"), "Enter the nickname you wish to reserve for this conference service", -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "name"), str, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "key"), jutil_regkey(NULL, jid_full(jp->to)), -1);
if(x != NULL)
xmlnode_insert_tag(jp->iq, "registered");
deliver(dpacket_new(jp->x), NULL);
}
else if(NSCHECK(jp->iq,NS_TIME))
{
/* Compliant with JEP-0090 */
log_debug(NAME, "[%s] Server packet - Time Request", FZONE);
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x, "query"), "xmlns", NS_TIME);
jpacket_reset(jp);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "utc"), jutil_timestamp(), -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "tz"), tzname[0], -1);
/* create nice display time */
t = time(NULL);
str = ctime(&t);
str[strlen(str) - 1] = '\0'; /* cut off newline */
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "display"), pstrdup(jp->p, str), -1);
deliver(dpacket_new(jp->x),NULL);
}
else if(NSCHECK(jp->iq,NS_VERSION))
{
/* Compliant with JEP-0092 */
log_debug(NAME, "[%s] Server packet - Version Request", FZONE);
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x, "query"), "xmlns", NS_VERSION);
jpacket_reset(jp);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "name"), NAME, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "version"), VERSION, -1);
uname(&un);
x = xmlnode_insert_tag(jp->iq,"os");
xmlnode_insert_cdata(x, pstrdup(jp->p, un.sysname),-1);
xmlnode_insert_cdata(x," ",1);
xmlnode_insert_cdata(x,pstrdup(jp->p, un.release),-1);
deliver(dpacket_new(jp->x),NULL);
}
else if(NSCHECK(jp->iq,NS_BROWSE))
{
/* Compliant with JEP-0011 */
log_debug(NAME, "[%s] Server packet - Browse Request", FZONE);
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x, "item"), "xmlns", NS_BROWSE);
jpacket_reset(jp);
xmlnode_put_attrib(jp->iq, "category", "conference");
xmlnode_put_attrib(jp->iq, "type", "public");
xmlnode_put_attrib(jp->iq, "jid", master->i->id);
/* pull name from the server vCard */
xmlnode_put_attrib(jp->iq, "name", xmlnode_get_tag_data(master->config, "vCard/FN"));
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_MUC, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_DISCO, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_BROWSE, -1);
/* xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_REGISTER, -1); */
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_VERSION, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_TIME, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_LAST, -1);
xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq, "ns"), NS_VCARD, -1);
/* Walk room hashtable and report available rooms */
g_hash_table_foreach(master->rooms, con_server_browsewalk, (void*)jp);
deliver(dpacket_new(jp->x), NULL);
}
else if(NSCHECK(jp->iq, NS_DISCO_INFO))
{
log_debug(NAME, "[%s] Server packet - Disco Info Request", FZONE);
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"query"), "xmlns", NS_DISCO_INFO);
jpacket_reset(jp);
x = xmlnode_insert_tag(jp->iq,"identity");
xmlnode_put_attrib(x, "category", "conference");
xmlnode_put_attrib(x, "type", "text");
xmlnode_put_attrib(x, "name", xmlnode_get_tag_data(master->config, "vCard/FN"));
xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_MUC);
xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_DISCO);
xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_BROWSE);
/* xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_REGISTER); */
xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_VERSION);
xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_TIME);
xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_LAST);
xmlnode_put_attrib(xmlnode_insert_tag(jp->iq,"feature"), "var", NS_VCARD);
deliver(dpacket_new(jp->x),NULL);
}
else if(NSCHECK(jp->iq, NS_DISCO_ITEMS))
{
log_debug(NAME, "[%s] Server packet - Disco Items Request", FZONE);
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"query"),"xmlns", NS_DISCO_ITEMS);
jpacket_reset(jp);
g_hash_table_foreach(master->rooms,_server_discowalk, (void*)jp);
deliver(dpacket_new(jp->x),NULL);
}
else if(NSCHECK(jp->iq, NS_LAST))
{
log_debug(NAME, "[%s] Server packet - Last Request", FZONE);
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"query"),"xmlns",NS_LAST);
jpacket_reset(jp);
start = time(NULL) - master->start;
sprintf(nstr,"%d",start);
xmlnode_put_attrib(jp->iq,"seconds", pstrdup(jp->p, nstr));
deliver(dpacket_new(jp->x),NULL);
}
else if(NSCHECK(jp->iq,NS_VCARD))
{
log_debug(NAME, "[%s] Server packet - VCard Request", FZONE);
jutil_iqresult(jp->x);
xmlnode_put_attrib(xmlnode_insert_tag(jp->x,"vCard"),"xmlns",NS_VCARD);
jpacket_reset(jp);
xmlnode_insert_node(jp->iq,xmlnode_get_firstchild(xmlnode_get_tag(master->config,"vCard")));
deliver(dpacket_new(jp->x),NULL);
}
}
else
{
log_debug(NAME, "[%s] TERROR_NOTIMPL", FZONE);
jutil_error(jp->x, TERROR_NOTIMPL);
deliver(dpacket_new(jp->x),NULL);
}
return;
}
void _con_packets(void *arg)
{
jpacket jp = (jpacket)arg;
cni master = (cni)jp->aux1;
cnr room;
cnu u, u2;
char *s, *reason;
xmlnode node;
int priority = -1;
int created = 0;
time_t now = time(NULL);
#ifndef _JCOMP
pth_mutex_acquire(&master->lock, 0, NULL);
#else
g_mutex_lock(master->lock);
#endif
/* first, handle all packets just to the server (browse, vcard, ping, etc) */
if(jp->to->user == NULL)
{
con_server(master, jp);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return;
}
log_debug(NAME, "[%s] processing packet %s", FZONE, xmlnode2str(jp->x));
/* any other packets must have an associated room */
for(s = jp->to->user; *s != '\0'; s++)
*s = tolower(*s); /* lowercase the group name */
if((room = g_hash_table_lookup(master->rooms, jid_full(jid_user(jid_fix(jp->to))))) == NULL)
{
log_debug(NAME, "[%s] Room not found (%s)", FZONE, jid_full(jid_user(jp->to)));
if((master->roomlock == 1 && !is_sadmin(master, jp->from)) || master->loader == 0)
{
log_debug(NAME, "[%s] Room building request denied", FZONE);
jutil_error(jp->x, TERROR_MUC_ROOM);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
deliver(dpacket_new(jp->x),NULL);
return;
}
else if(jp->type == JPACKET_IQ && jpacket_subtype(jp) == JPACKET__GET && NSCHECK(jp->iq, NS_MUC_OWNER))
{
room = con_room_new(master, jid_user(jp->to), jp->from, NULL, NULL, 1, 1, 0);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
xmlnode_free(jp->x);
return;
}
else if(jp->to->resource == NULL)
{
log_debug(NAME, "[%s] Room %s doesn't exist: Returning Bad Request", FZONE, jp->to->user);
jutil_error(jp->x, TERROR_BAD);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
deliver(dpacket_new(jp->x),NULL);
return;
}
else if(jpacket_subtype(jp) == JPACKET__UNAVAILABLE)
{
log_debug(NAME, "[%s] Room %s doesn't exist: dropping unavailable presence", FZONE, jp->to->user);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
xmlnode_free(jp->x);
return;
}
else
{
if(master->dynamic == -1)
room = con_room_new(master, jid_user(jp->to), jp->from, NULL, NULL, 1, 0, 1);
else
room = con_room_new(master, jid_user(jp->to), jp->from, NULL, NULL, 1, 0, 0);
/* fall through, so the presence goes to the room like normal */
created = 1;
}
}
/* get the sending user entry, if any */
u = g_hash_table_lookup(room->remote, jid_full(jid_fix(jp->from)));
/* handle errors */
if(jpacket_subtype(jp) == JPACKET__ERROR)
{
log_debug(NAME, "[%s] Error Handler: init", FZONE);
/* only allow iq errors that are to a resource (direct-chat) */
if(jp->to->resource == NULL || jp->type != JPACKET_IQ)
{
if(u != NULL && u->localid != NULL)
{
log_debug(NAME, "[%s] Error Handler: Zapping user", FZONE);
node = xmlnode_new_tag("reason");
xmlnode_insert_cdata(node, "Lost connection", -1);
con_user_zap(u, node);
}
else
{
log_debug(NAME, "[%s] Error Handler: No cnu/lid found for user", FZONE);
}
}
xmlnode_free(jp->x);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return;
}
/* Block message from users not already in the room */
if(jp->type == JPACKET_MESSAGE && u == NULL)
{
log_debug(NAME, "[%s] Blocking message from outsider (%s)", FZONE, jid_full(jp->to));
jutil_error(jp->x, TERROR_MUC_OUTSIDE);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
deliver(dpacket_new(jp->x),NULL);
return;
}
/* several things use this field below as a flag */
if(jp->type == JPACKET_PRESENCE)
priority = jutil_priority(jp->x);
/* sending available presence will automatically get you a generic user, if you don't have one */
if(u == NULL && priority >= 0)
u = con_user_new(room, jp->from);
/* update tracking stuff */
room->last = now;
room->packets++;
if(u != NULL)
{
u->last = now;
u->packets++;
}
/* handle join/rename */
if(priority >= 0 && jp->to->resource != NULL)
{
u2 = con_room_usernick(room, jp->to->resource); /* existing user w/ this nick? */
/* it's just us updating our presence */
if(u2 == u)
{
jp->to = jid_user(jp->to);
xmlnode_put_attrib(jp->x, "to", jid_full(jp->to));
if(u)
{
xmlnode_free(u->presence);
u->presence = xmlnode_dup(jp->x);
}
con_room_process(room, u, jp);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return;
}
/* User already exists, return conflict Error */
if(u2 != NULL)
{
log_debug(NAME, "[%s] Nick Conflict (%s)", FZONE, jid_full(jid_user(jp->to)));
jutil_error(jp->x, TERROR_MUC_NICK);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
deliver(dpacket_new(jp->x),NULL);
return;
}
/* Nick already registered, return conflict Error */
if(is_registered(master, jid_full(jid_user(jid_fix(u->realid))), jp->to->resource) == -1)
{
log_debug(NAME, "[%s] Nick Conflict with registered nick (%s)", FZONE, jid_full(jid_fix(jp->to)));
jutil_error(jp->x, TERROR_MUC_NICKREG);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
deliver(dpacket_new(jp->x),NULL);
return;
}
/* if from an existing user in the room, change the nick */
if(is_outcast(room, u->realid) && !is_admin(room, u->realid))
{
log_debug(NAME, "[%s] Blocking Banned user (%s)", FZONE, jid_full(jid_user(jid_fix(jp->to))));
jutil_error(jp->x, TERROR_MUC_BANNED);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
deliver(dpacket_new(jp->x),NULL);
return;
}
/* User is not invited, return invitation error */
if(room->invitation == 1 && !is_member(room, u->realid) && !is_owner(room, u->realid))
{
jutil_error(jp->x, TERROR_MUC_INVITED);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
deliver(dpacket_new(jp->x),NULL);
return;
}
/* Room is full, return full room error */
if(room->count >= room->maxusers && room->maxusers != 0 && !is_admin(room, u->realid))
{
log_debug(NAME, "[%s] Room over quota - disallowing entry", FZONE);
jutil_error(jp->x, TERROR_MUC_FULL);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
deliver(dpacket_new(jp->x),NULL);
return;
}
/* Room has been locked against entry */
if(room->locked && !is_owner(room, u->realid))
{
log_debug(NAME, "[%s] Room has been locked", FZONE);
jutil_error(jp->x, TERROR_NOTFOUND);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
deliver(dpacket_new(jp->x),NULL);
return;
}
/* User already in room, simply a nick change */
if(u->localid != NULL)
{
xmlnode_free(u->presence);
u->presence = xmlnode_dup(jp->x);
con_user_nick(u, jp->to->resource, NULL); /* broadcast nick rename */
xmlnode_free(jp->x);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return;
}
else if(room->secret == NULL || is_sadmin(master, jp->from)) /* No password required, just go right in, or you're an sadmin */
{
if(NSCHECK(xmlnode_get_tag(jp->x,"x"), NS_MUC))
{
/* Set legacy value to room value */
u->legacy = 0;
node = xmlnode_get_tag(jp->x,"x");
xmlnode_hide(node);
/* Enable room defaults automatically */
if(master->roomlock == -1)
{
created = 0;
}
}
else
{
u->legacy = 1;
created = 0; /* Override created flag for non-MUC compliant clients */
}
xmlnode_free(u->presence);
u->presence = xmlnode_dup(jp->x);
jutil_delay(u->presence, NULL);
log_debug(NAME, "[%s] About to enter room, legacy<%d>, presence [%s]", FZONE, u->legacy, xmlnode2str(u->presence));
con_user_enter(u, jp->to->resource, created); /* join the room */
xmlnode_free(jp->x);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return;
}
else if(jp->type == JPACKET_PRESENCE) /* Hopefully you are including a password, this room is locked */
{
if(NSCHECK(xmlnode_get_tag(jp->x,"x"), NS_MUC))
{
log_debug(NAME, "[%s] Password?", FZONE);
if(j_strcmp(room->secret, xmlnode_get_tag_data(xmlnode_get_tag(jp->x,"x"), "password")) == 0)
{
/* Set legacy value to room value */
u->legacy = 0;
node = xmlnode_get_tag(jp->x,"x");
xmlnode_hide(node);
xmlnode_free(u->presence);
u->presence = xmlnode_dup(jp->x);
jutil_delay(u->presence, NULL);
con_user_enter(u, jp->to->resource, created); /* join the room */
xmlnode_free(jp->x);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return;
}
}
}
/* No password found, room is password protected. Return password error */
jutil_error(jp->x, TERROR_MUC_PASSWORD);
deliver(dpacket_new(jp->x), NULL);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return;
}
/* kill any user sending unavailable presence */
if(jpacket_subtype(jp) == JPACKET__UNAVAILABLE)
{
log_debug(NAME, "[%s] Calling user zap", FZONE);
if(u != NULL)
{
reason = xmlnode_get_tag_data(jp->x, "status");
xmlnode_free(u->presence);
u->presence = xmlnode_dup(jp->x);
node = xmlnode_new_tag("reason");
if (reason)
xmlnode_insert_cdata(node, reason, -1);
con_user_zap(u, node);
}
xmlnode_free(jp->x);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return;
}
/* not in the room yet? foo */
if(u == NULL || u->localid == NULL)
{
if(u == NULL)
{
log_debug(NAME, "[%s] No cnu found for user", FZONE);
}
else
{
log_debug(NAME, "[%s] No lid found for %s", FZONE, jid_full(u->realid));
}
if(jp->to->resource != NULL)
{
jutil_error(jp->x, TERROR_NOTFOUND);
deliver(dpacket_new(jp->x),NULL);
}
else
{
con_room_outsider(room, u, jp); /* non-participants get special treatment */
}
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return;
}
/* packets to a specific resource? one on one chats, browse lookups, etc */
if(jp->to->resource != NULL)
{
if((u2 = g_hash_table_lookup(room->local, jp->to->resource)) == NULL && (u2 = con_room_usernick(room, jp->to->resource)) == NULL) /* gotta have a recipient */
{
jutil_error(jp->x, TERROR_NOTFOUND);
deliver(dpacket_new(jp->x),NULL);
}
else
{
con_user_process(u2, u, jp);
}
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return;
}
/* finally, handle packets just to a room from a participant, msgs, pres, iq browse/conferencing, etc */
con_room_process(room, u, jp);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
}
/* phandler callback, send packets to another server */
result con_packets(instance i, dpacket dp, void *arg)
{
cni master = (cni)arg;
jpacket jp;
if(dp == NULL)
{
log_warn(NAME, "[%s] Err: Sent a NULL dpacket!", FZONE);
return r_DONE;
}
/* routes are from dnsrv w/ the needed ip */
if(dp->type == p_ROUTE)
{
log_debug(NAME, "[%s] Rejecting ROUTE packet", FZONE);
deliver_fail(dp,"Illegal Packet");
return r_DONE;
}
else
{
jp = jpacket_new(dp->x);
}
/* if the delivery failed */
if(jp == NULL)
{
log_warn(NAME, "[%s] Rejecting Illegal Packet", FZONE);
deliver_fail(dp,"Illegal Packet");
return r_DONE;
}
/* bad packet??? ick */
if(jp->type == JPACKET_UNKNOWN || jp->to == NULL)
{
log_warn(NAME, "[%s] Bouncing Bad Packet", FZONE);
jutil_error(jp->x, TERROR_BAD);
deliver(dpacket_new(jp->x),NULL);
return r_DONE;
}
/* we want things processed in order, and don't like re-entrancy! */
jp->aux1 = (void*)master;
#ifdef _JCOMP
_con_packets((void *)jp);
#else
mtq_send(master->q, jp->p, _con_packets, (void *)jp);
#endif
return r_DONE;
}
/** Save and clean out every room on shutdown */
void _con_shutdown_rooms(gpointer key, gpointer data, gpointer arg)
{
cnr room = (cnr)data;
if(room == NULL)
{
log_warn(NAME, "[%s] SHUTDOWN: Aborting attempt to clear %s", FZONE, key);
return;
}
#ifdef _JCOMP
if(room->persistent == 1)
xdb_room_set(room);
#endif
con_room_cleanup(room);
}
/** Called to clean up system on shutdown */
void con_shutdown(void *arg)
{
cni master = (cni)arg;
if(master->shutdown == 1)
{
log_debug(NAME, "[%s] SHUTDOWN: Already commencing. Aborting attempt", FZONE);
return;
}
else
{
master->shutdown = 1;
}
log_debug(NAME, "[%s] SHUTDOWN: Clearing configuration", FZONE);
xmlnode_free(master->config);
log_debug(NAME, "[%s] SHUTDOWN: Zapping sadmin table", FZONE);
g_hash_table_destroy(master->sadmin);
log_debug(NAME, "[%s] SHUTDOWN: Clear users from rooms", FZONE);
g_hash_table_foreach(master->rooms, _con_shutdown_rooms, NULL);
log_debug(NAME, "[%s] SHUTDOWN: Zapping rooms", FZONE);
g_hash_table_destroy(master->rooms);
free(master->day);
log_debug(NAME, "[%s] SHUTDOWN: Sequence completed", FZONE);
}
/** Function called for walking each user in a room */
void _con_beat_user(gpointer key, gpointer data, gpointer arg)
{
cnu user = (cnu)data;
int now = (int)arg;
if(user == NULL)
{
log_warn(NAME, "[%s] Aborting : NULL cnu for %s", FZONE, key);
return;
}
if(user->localid == NULL && (now - user->last) > 120)
{
log_debug(NAME, "[%s] Marking zombie", FZONE);
g_queue_push_tail(user->room->queue, g_strdup(jid_full(user->realid)));
}
}
/* callback for walking each room */
void _con_beat_idle(gpointer key, gpointer data, gpointer arg)
{
cnr room = (cnr)data;
int now = (int)arg;
xmlnode node;
char *user_name;
log_debug(NAME, "[%s] HBTICK: Idle check for >%s<", FZONE, key);
if(room == NULL)
{
log_warn(NAME, "[%s] Aborting : NULL cnr for %s", FZONE, key);
return;
}
/* Perform zombie user clearout */
room->queue = g_queue_new();
g_hash_table_foreach(room->remote, _con_beat_user, arg); /* makes sure nothing stale is in the room */
while ((user_name = (char *)g_queue_pop_head(room->queue)) != NULL)
{
node = xmlnode_new_tag("reason");
xmlnode_insert_cdata(node, "Clearing zombie", -1);
con_user_zap(g_hash_table_lookup(room->remote, user_name), node);
log_debug(NAME, "[%s] HBTICK: removed zombie '%s' in the queue", FZONE, user_name);
g_free(user_name);
}
g_queue_free(room->queue);
/* Destroy timed-out dynamic room */
if(room->persistent == 0 && room->count == 0 && (now - room->last) > 240)
{
log_debug(NAME, "[%s] HBTICK: Locking room and adding %s to remove queue", FZONE, key, now);
room->locked = 1;
g_queue_push_tail(room->master->queue, g_strdup(jid_full(room->id)));
}
}
/* heartbeat checker for timed out idle rooms */
void _con_beat_logrotate(gpointer key, gpointer data, gpointer arg)
{
cnr room = (cnr)data;
if(room == NULL)
{
log_warn(NAME, "[%s] Aborting : NULL cnr for %s", FZONE, key);
return;
}
if(room->logfile)
{
log_debug(NAME, "[%s] Rotating log for room %s", FZONE, jid_full(room->id));
con_room_log_close(room);
con_room_log_new(room);
}
}
/* heartbeat checker for timed out idle rooms */
void _con_beat_logupdate(gpointer key, gpointer data, gpointer arg)
{
cnr room = (cnr)data;
char *timestamp = (char*)arg;
if(room == NULL)
{
log_warn(NAME, "[%s] Aborting : NULL cnr for %s", FZONE, key);
return;
}
if(room->logformat == LOG_XHTML && room->logfile)
{
log_debug(NAME, "[%s] Adding anchor >%s< for room %s", FZONE, timestamp, jid_full(room->id));
fprintf(room->logfile, "\n", timestamp);
fflush(room->logfile);
}
}
/* heartbeat checker for maintainance */
result con_beat_update(void *arg)
{
cni master = (cni)arg;
time_t t = time(NULL);
int mins = minuteget(t);
char *tstamp = timeget(t);
char *dstamp = dateget(t);
char *room_name;
log_debug(NAME, "[%s] HBTICK", FZONE);
/* Check for timed out idle rooms */
if(mins % 2 == 0)
{
#ifndef _JCOMP
pth_mutex_acquire(&master->lock, 0, NULL);
#else
g_mutex_lock(master->lock);
#endif
log_debug(NAME, "[%s] HBTICK: Idle check started", FZONE);
master->queue = g_queue_new();
g_hash_table_foreach(master->rooms, _con_beat_idle, (void*)t);
while ((room_name = (char *)g_queue_pop_head(master->queue)) != NULL)
{
log_debug(NAME, "[%s] HBTICK: removed room '%s' in the queue", FZONE, room_name);
con_room_zap(g_hash_table_lookup(master->rooms, room_name));
log_debug(NAME, "[%s] HBTICK: removed room '%s' in the queue", FZONE, room_name);
g_free(room_name);
}
g_queue_free(master->queue);
log_debug(NAME, "[%s] HBTICK: Idle check complete", FZONE);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
}
/* Check for logfiles requiring updating */
if(mins % 5 == 0)
{
#ifndef _JCOMP
pth_mutex_acquire(&master->lock, 0, NULL);
#else
g_mutex_lock(master->lock);
#endif
g_hash_table_foreach(master->rooms, _con_beat_logupdate, (void*)tstamp);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
}
/* Release malloc for tstamp */
free(tstamp);
if(j_strcmp(master->day, dstamp) == 0)
{
free(dstamp);
return r_DONE;
}
free(master->day);
master->day = j_strdup(dstamp);
free(dstamp);
#ifndef _JCOMP
pth_mutex_acquire(&master->lock, 0, NULL);
#else
g_mutex_lock(master->lock);
#endif
g_hash_table_foreach(master->rooms, _con_beat_logrotate, NULL);
#ifndef _JCOMP
pth_mutex_release(&master->lock);
#else
g_mutex_unlock(master->lock);
#endif
return r_DONE;
}
/* heartbeat checker for miscellaneous tasks */
result con_beat_housekeep(void *arg)
{
cni master = (cni)arg;
master->loader = 1;
xdb_rooms_get(master);
/* Remove unwanted heartbeat */
return r_UNREG;
}
/*** everything starts here ***/
void conference(instance i, xmlnode x)
{
#ifdef _JCOMP
extern jcr_instance jcr;
#endif
cni master;
xmlnode cfg;
jid sadmin;
xmlnode current;
xmlnode node;
xmlnode tmp;
pool tp;
time_t now = time(NULL);
log_debug(NAME, "[%s] mu-conference loading - Service ID: %s", FZONE, i->id);
/* Temporary pool for temporary jid creation */
tp = pool_new();
/* Allocate space for cni struct and link to instance */
log_debug(NAME, "[%s] Malloc: _cni=%d", FZONE, sizeof(_cni));
master = pmalloco(i->p, sizeof(_cni));
master->i = i;
#ifndef _JCOMP
/* Set up xdb interface */
master->xdbc = xdb_cache(i);
/* get the config */
cfg = xdb_get(master->xdbc, jid_new(xmlnode_pool(x), "config@-internal"), "jabber:config:conference");
/* Parse config and initialise variables */
master->q = mtq_new(i->p);
#else
/* get the config */
cfg = xmlnode_get_tag(jcr->config, "conference");
#endif
master->loader = 0;
master->start = now;
master->rooms = g_hash_table_new_full(g_str_hash, g_str_equal, ght_remove_key, ght_remove_cnr);
master->history = j_atoi(xmlnode_get_tag_data(cfg,"history"),20);
master->config = xmlnode_dup(cfg); /* Store a copy of the config for later usage */
master->day = dateget(now); /* Used to determine when to rotate logs */
master->logdir = xmlnode_get_tag_data(cfg, "logdir"); /* Directory where to store logs */
/* If requested, set default room state to 'public', otherwise will default to 'private */
if(xmlnode_get_tag(cfg,"public"))
master->public = 1;
/* If requested, rooms are given a default configuration */
if(xmlnode_get_tag(cfg,"defaults"))
master->roomlock = -1;
/* If requested, stop any new rooms being created */
if(xmlnode_get_tag(cfg,"roomlock"))
master->roomlock = 1;
/* If requested, stop any new rooms being created */
if(xmlnode_get_tag(cfg,"dynamic"))
master->dynamic = 1;
/* If requested, stop any new rooms being created */
if(xmlnode_get_tag(cfg,"persistent"))
master->dynamic = -1;
master->sadmin = g_hash_table_new_full(g_str_hash,g_str_equal, ght_remove_key, ght_remove_xmlnode);
/* sadmin code */
if(xmlnode_get_tag(cfg, "sadmin"))
{
node = xmlnode_get_tag(cfg, "sadmin");
for(current = xmlnode_get_firstchild(node); current != NULL; current = xmlnode_get_nextsibling(current))
{
sadmin = jid_new(tp, xmlnode_get_data(current));
if(sadmin != NULL)
{
log_debug(NAME, "[%s] Adding sadmin %s", FZONE, jid_full(sadmin));
/* use an xmlnode as the data value */
tmp = xmlnode_new_tag("sadmin");
g_hash_table_insert(master->sadmin, j_strdup(jid_full(jid_user(jid_fix(sadmin)))), (void*)tmp);
}
}
}
#ifndef _JCOMP
register_phandler(i, o_DELIVER, con_packets, (void*)master);
register_shutdown(con_shutdown,(void *) master);
register_beat(60, con_beat_update, (void *)master);
register_beat(1, con_beat_housekeep, (void *)master);
#else
master->lock = g_mutex_new();
master->loader = 1;
xdb_rooms_get(master);
register_phandler(i, o_DELIVER, con_packets, (void*)master);
register_shutdown(con_shutdown,(void *) master);
g_timeout_add(60000, (GSourceFunc)con_beat_update, (void *)master);
#endif
pool_free(tp);
}
mu-conference-0.6.0/src/hash.c 0100644 0001753 0000144 00000003713 07755324541 015017 0 ustar pere users /*
* 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"
gboolean remove_key(gpointer key, gpointer data, gpointer arg)
{
log_debug(NAME, "[%s] Auto-removing key %s", FZONE, key);
free(key);
free(data);
return TRUE;
}
void ght_remove_key(gpointer data)
{
log_debug(NAME, "[%s] Auto-removing key %s", FZONE, data);
free(data);
}
void ght_remove_cnu(gpointer data)
{
cnu node = (cnu)data;
log_debug(NAME, "[%s] Auto-removing cnu %s", FZONE, jid_full(jid_fix(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(jid_fix(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);
}
mu-conference-0.6.0/src/admin.c 0100644 0001753 0000144 00000041216 07755324540 015163 0 ustar pere users /*
* 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 con_get_role_list(gpointer key, gpointer data, gpointer arg)
{
xmlnode node;
xmlnode result;
char *jabberid;
taffil affiliation;
trole role;
jid userid;
cnr room;
result = (xmlnode)arg;
if(result == NULL)
{
log_warn(NAME, "[%s] Aborting: NULL result - <%s>", FZONE, key);
return;
}
room = (cnr)xmlnode_get_vattrib(result ,"cnr");
if(room == NULL)
{
log_warn(NAME, "[%s] Aborting: NULL room - <%s>", FZONE, key);
return;
}
node = xmlnode_new_tag("item");
jabberid = pstrdup(xmlnode_pool(node), key);
userid = jid_new(xmlnode_pool(node), jabberid);
xmlnode_put_attrib(node, "jid", jabberid);
affiliation = affiliation_level(room, userid);
role = role_level(room, userid);
xmlnode_put_attrib(node, "role", role.msg);
xmlnode_put_attrib(node, "affiliation", affiliation.msg);
xmlnode_insert_node(result, node);
xmlnode_free(node);
}
void con_get_affiliate_list(gpointer key, gpointer data, gpointer arg)
{
xmlnode node;
cnr room;
taffil affiliation;
jid userid;
char *jabberid;
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, key);
return;
}
actor = xmlnode_get_attrib(item, "actor");
reason = xmlnode_get_data(item);
room = (cnr)xmlnode_get_vattrib(result ,"cnr");
node = xmlnode_new_tag("item");
jabberid = pstrdup(xmlnode_pool(node), key);
userid = jid_new(xmlnode_pool(node), jabberid);
xmlnode_put_attrib(node, "jid", jabberid);
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);
xmlnode_insert_node(result, node);
xmlnode_free(node);
}
/* 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;
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 = pstrdup(xmlnode_pool(data), STATUS_MUC_BANNED);
else
status = pstrdup(xmlnode_pool(data), STATUS_MUC_KICKED);
xmlnode_put_attrib(data, "status", status);
xmlnode_put_attrib(data, "actor", jid_full(jid_user(user->realid)));
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))
{
/* 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(current, "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(current, "400", "No such list");
continue;
}
if(j_strcmp(affiliation, "admin") == 0 || j_strcmp(affiliation, "owner") == 0)
{
if(j_strcmp(xmlns, NS_MUC_OWNER) != 0)
{
error = 1;
log_debug(NAME, "[%s] Skipping - Badly formed namespace (%s)", FZONE, xmlnode2str(current));
insert_item_error(current, "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(current, "400", "Invalid Namespace");
continue;
}
}
else
{
error = 1;
log_debug(NAME, "[%s] Skipping - Badly formed request (%s)", FZONE, xmlnode2str(current));
insert_item_error(current, "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(current, "400", "Badly formed request - no role or affiliation attribute");
continue;
}
if(jabberid == NULL)
{
user = con_room_usernick(room, 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(current, "400", "Nick not present in room");
continue;
}
}
target = jid_new(jp->p, jabberid);
if(target->user == NULL && role != NULL)
{
error = 1;
log_debug(NAME, "[%s] Skipping - Bad jid (%s)", FZONE, jabberid);
insert_item_error(current, "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(current, "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(current, "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(current, "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(current, "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(current, "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(current, "403", "Forbidden - Admin requested");
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(current, "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(current, "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(current, "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(current, "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(current, "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))
{
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 = con_room_usernick(room, 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->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;
}
mu-conference-0.6.0/src/utils.c 0100644 0001753 0000144 00000046612 07755324541 015241 0 ustar pere users /*
* 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"
/* 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(xmlnode_get_data(from->nick) != NULL)
xmlnode_put_attrib(item, "nick", xmlnode_get_data(from->nick));
/* 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;
}
/* Is the user a Service Admin? */
int is_sadmin(cni master, jid user)
{
char ujid[256];
if(master == NULL || user == NULL)
{
log_warn(NAME, "[%s] ERR: Missing variable in is_sadmin", FZONE);
return 0;
}
snprintf(ujid, 256, "%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[256];
char cjid[256];
if(room == NULL || user == NULL)
{
log_warn(NAME, "[%s] ERR: Missing variable in is_owner", FZONE);
return 0;
}
snprintf(ujid, 256, "%s@%s", user->user, user->server);
if(room->creator)
{
snprintf(cjid, 256, "%s@%s", room->creator->user, room->creator->server);
}
else
{
snprintf(cjid, 256, "@");
}
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[256];
if(room == NULL || user == NULL)
{
log_warn(NAME, "[%s] ERR: Missing variable in is_admin", FZONE);
return 0;
}
snprintf(ujid, 256, "%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[256];
if(room == NULL || user == NULL)
{
log_warn(NAME, "[%s] ERR: Missing variable in is_member", FZONE);
return 0;
}
snprintf(ujid, 256, "%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[256];
if(room == NULL || user == NULL)
{
log_warn(NAME, "[%s] ERR: Missing variable in is_outcast", FZONE);
return 0;
}
snprintf(ujid, 256, "%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, char *status)
{
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)
{
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;
}
}
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, char *status)
{
xmlnode msg = _con_send_alert(user, text, subject, status);
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);
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);
}
/* Splice \n-delimited line into array */
char *linesplit(char **entry)
{
char *line;
char *point;
char *str = "\n";
if(!(*entry))
return NULL;
line = *entry;
if((point = strstr(*entry, str)))
{
*point = 0;
*entry = point+strlen(str);
}
else
*entry = NULL;
return line;
}
/* Integer to String conversion */
char *itoa(int number, char *result)
{
sprintf(result, "%d", number);
return result;
}
/* Custom Debug message */
char *funcstr(char *file, 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 *key;
char ujid[256];
if(room == NULL || userid == NULL)
{
log_warn(NAME, "[%s] ERR: Missing variable", FZONE);
return -1;
}
snprintf(ujid, 256, "%s@%s", userid->user, userid->server);
key = j_strdup(ujid);
old = g_hash_table_lookup(room->roster, key);
/* 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);
free(key);
return 0;
}
}
if(userid->resource != NULL)
{
log_debug(NAME, "[%s] adding entry (%s) for jid (%s)", FZONE, jid_full(userid), ujid);
node = xmlnode_new_tag("item");
xmlnode_put_attrib(node, "jid", jid_full(userid));
xmlnode_insert_node(store, node);
xmlnode_free(node);
}
g_hash_table_insert(room->roster, key, 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 *key;
char ujid[256];
if(room == NULL || userid == NULL)
{
log_warn(NAME, "[%s] ERR: Missing variable", FZONE);
return -1;
}
snprintf(ujid, 256, "%s@%s", userid->user, userid->server);
key = j_strdup(ujid);
old = g_hash_table_lookup(room->roster, key);
if(old == NULL)
{
free(key);
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);
free(key);
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, key);
xmlnode_free(store);
free(key);
}
else
{
log_debug(NAME, "[%s] Removing entry (%s) for jid (%s)", FZONE, jid_full(userid), ujid);
g_hash_table_insert(room->roster, key, store);
}
return 1;
}
/* Get the entries from the room roster hash */
xmlnode get_roster(cnr room, jid userid)
{
xmlnode store;
char *key;
char ujid[256];
if(room == NULL || userid == NULL)
{
log_warn(NAME, "[%s] ERR: Missing variable", FZONE);
return NULL;
}
snprintf(ujid, 256, "%s@%s", userid->user, userid->server);
key = j_strdup(ujid);
store = g_hash_table_lookup(room->roster, key);
free(key);
return store;
}
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;
}
/* Check Primeness for hash functions */
int isPrime(unsigned long n)
{
int prime = 1;
unsigned long p1,p2 , s;
if(n > 3)
{
p1 = 3;
p2 = n - 3;
s = 9;
while((prime = p2 % p1 ) && (s <= p2))
{
p1 += 2;
p2 -= 2;
s <<= 2;
s++;
}
}
return prime;
}
/* Used to check jids and fix case. */
jid jid_fix(jid id)
{
unsigned char *str;
if(id == NULL)
{
log_warn(NAME, "[%s] ERR - id NULL", FZONE);
return NULL;
}
if(id->server == NULL || j_strlen(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;
}
mu-conference-0.6.0/src/xdata.c 0100644 0001753 0000144 00000050350 07755324541 015174 0 ustar pere users /*
* 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"
xmlnode add_xdata_boolean(char *label, char *var, int data)
{
xmlnode node;
char value[4];
snprintf(value, 4, "%i", data);
node = xmlnode_new_tag("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);
return node;
}
xmlnode add_xdata_text(char *label, int type, char *var, char *data)
{
xmlnode node;
node = xmlnode_new_tag("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);
return node;
}
xmlnode add_xdata_desc(char *label)
{
xmlnode node;
node = xmlnode_new_tag("field");
xmlnode_put_attrib(node,"type","fixed");
xmlnode_insert_cdata(xmlnode_insert_tag(node,"value"), label, -1);
return node;
}
void 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];
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;
}
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;
}
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(jid_fix(user->realid)), NULL, spools(packet->p, "Configuration confirmed: This room is now unlocked.", packet->p));
xmlnode_put_attrib(message,"from", jid_full(jid_fix(room->id)));
deliver(dpacket_new(message), NULL);
room->locked = 0;
}
/* Protect text forms from broken clients */
if(xmlnode_get_tag(results,"?var=muc#owner_roomname") != NULL)
{
free(room->name);
room->name = j_strdup(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_roomname"),"value"));
}
if(xmlnode_get_tag(results,"?var=leave") != NULL)
{
free(room->note_leave);
room->note_leave = j_strdup(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=leave"),"value"));
}
if(xmlnode_get_tag(results,"?var=join") != NULL)
{
free(room->note_join);
room->note_join = j_strdup(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=join"),"value"));
}
if(xmlnode_get_tag(results,"?var=rename") != NULL)
{
free(room->note_rename);
room->note_rename = j_strdup(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=rename"),"value"));
}
/* Handle text-multi */
if((node = xmlnode_get_tag(results,"?var=muc#owner_roomdesc")) != NULL)
{
for(current = xmlnode_get_firstchild(node); current != NULL; current = xmlnode_get_nextsibling(current))
{
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 */
room->subjectlock = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_changesubject"),"value"), room->subjectlock);
room->private = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=privacy"),"value"),room->private);
room->public = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_publicroom"),"value"),room->public);
room->maxusers = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_maxusers"),"value"),room->maxusers);
if(room->master->dynamic == 0 || is_sadmin(room->master, user->realid))
room->persistent = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_persistentroom"),"value"),room->persistent);
room->moderated = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_moderatedroom"),"value"),room->moderated);
room->defaulttype = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=defaulttype"),"value"),room->defaulttype);
room->privmsg = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=privmsg"),"value"),room->privmsg);
room->invitation = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_inviteonly"),"value"),room->invitation);
room->invites = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_allowinvites"),"value"),room->invites);
room->legacy = j_atoi(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=legacy"),"value"),room->legacy);
/* Protection against broken clients */
if(xmlnode_get_tag(results,"?var=muc#owner_passwordprotectedroom") != NULL && xmlnode_get_tag(results,"?var=muc#owner_roomsecret") != NULL)
{
/* Is both password set and active? */
if(j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_passwordprotectedroom"),"value"), "1") == 0 && xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_roomsecret"),"value") != NULL)
{
free(room->secret);
room->secret = j_strdup(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_roomsecret"),"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=muc#owner_passwordprotectedroom"),"value"), xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_roomsecret"),"value"));
free(room->secret);
room->secret = NULL;
}
}
if(j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_whois"),"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_SHOWN_JID);
else if(room->visible == 0 && room->visible != visible)
con_send_room_status(room, STATUS_MUC_HIDDEN_JID);
/* Set up log format and restart logging if necessary */
if(xmlnode_get_tag(results,"?var=logformat"))
{
if(j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=logformat"),"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=logformat"),"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 */
if(room->logfile == NULL && j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_enablelogging"),"value"),"1") == 0)
{
con_room_log_new(room);
if (room->logfile == NULL)
log_alert(NULL, "cannot open log file for room %s", jid_full(jid_fix(room->id)));
else
con_room_log(room, NULL, "LOGGING STARTED");
}
if(room->logfile != NULL && j_strcmp(xmlnode_get_tag_data(xmlnode_get_tag(results,"?var=muc#owner_enablelogging"),"value"),"0") == 0)
{
con_room_log(room, NULL, "LOGGING STOPPED");
con_room_log_close(room);
}
if(room->persistent == 1)
{
xdb_room_set(room);
}
else
{
xdb_room_clear(room);
}
}
pool_free(tp);
}
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(jid_fix(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(jid_fix(user->realid)));
xmlnode_put_attrib(msg,"from",jid_full(jid_fix(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);
xmlnode_insert_node(x,add_xdata_text(NULL, 0, "form", "config"));
xmlnode_insert_node(x,add_xdata_text("Natural-Language Room Name", 1, "muc#owner_roomname", room->name));
xmlnode_insert_node(x,add_xdata_text("Short Description of Room", 2, "muc#owner_roomdesc", room->description));
xmlnode_insert_node(x,add_xdata_desc("The following messages are sent to legacy clients."));
xmlnode_insert_node(x,add_xdata_text("Message for user leaving room", 1, "leave", room->note_leave));
xmlnode_insert_node(x,add_xdata_text("Message for user joining room", 1, "join", room->note_join));
xmlnode_insert_node(x,add_xdata_text("Message for user renaming nickname in room", 1, "rename", room->note_rename));
xmlnode_insert_node(x,add_xdata_boolean("Allow Occupants to Change Subject", "muc#owner_changesubject", 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","muc#owner_maxusers");
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);
xmlnode_insert_node(x,add_xdata_boolean("Allow Occupants to query other Occupants?", "privacy", room->private));
xmlnode_insert_node(x,add_xdata_boolean("Allow Public Searching for Room", "muc#owner_publicroom", room->public));
if(room->master->dynamic == 0 || is_sadmin(room->master, user->realid))
xmlnode_insert_node(x,add_xdata_boolean("Make Room Persistent", "muc#owner_persistentroom", room->persistent));
xmlnode_insert_node(x,add_xdata_boolean("Consider all Clients as Legacy (shown messages)", "legacy", room->legacy));
xmlnode_insert_node(x,add_xdata_boolean("Make Room Moderated", "muc#owner_moderatedroom", room->moderated));
xmlnode_insert_node(x,add_xdata_desc("By default, new users entering a moderated room are only visitors"));
xmlnode_insert_node(x,add_xdata_boolean("Make Occupants in a Moderated Room Default to Participant", "defaulttype", room->defaulttype));
xmlnode_insert_node(x,add_xdata_boolean("Ban Private Messages between Occupants", "privmsg", room->privmsg));
xmlnode_insert_node(x,add_xdata_boolean("An Invitation is Required to Enter", "muc#owner_inviteonly", room->invitation));
xmlnode_insert_node(x,add_xdata_desc("By default, only admins can send invites in an invite-only room"));
xmlnode_insert_node(x,add_xdata_boolean("Allow Occupants to Invite Others", "muc#owner_allowinvites", room->invites));
if(room->secret == NULL)
xmlnode_insert_node(x,add_xdata_boolean("A Password is required to enter?", "muc#owner_passwordprotectedroom", 0));
else
xmlnode_insert_node(x,add_xdata_boolean("A Password required to enter", "muc#owner_passwordprotectedroom", 1));
xmlnode_insert_node(x,add_xdata_desc("If a password is required to enter this room, you must specify the password below."));
xmlnode_insert_node(x,add_xdata_text("The Room Password", -1, "muc#owner_roomsecret", 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","muc#owner_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->logfile == NULL)
xmlnode_insert_node(x,add_xdata_boolean("Enable Logging of Room Conversations", "muc#owner_enablelogging", 0));
else
xmlnode_insert_node(x,add_xdata_boolean("Enable Logging of Room Conversations", "muc#owner_enablelogging", 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","logformat");
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);
}
mu-conference-0.6.0/TODO 0100644 0001753 0000144 00000001327 07755324537 013635 0 ustar pere users TODO
(Not in any order)
* Add in support for x-commands (JEP-0050)
- Useful for clearing history
* Add in support for MySQL logging
* Add in user-definable room destruction delay
* add code to handle registration in a room, if can understand
* add code to support storing persistant room configuration to xdb
- All done except room registration, when added
* profanity filter?
- May look into adding this as an external loadable module
* Fix jid checking
- Requires JEP to stablise
* Multiple service handling
- Allow server 'divisions' dependant on requested server jid
* Three Strike rule for pw protected rooms, to limit brute force attacks
Requests:
---------
* ability to lock a nick to the users jid
mu-conference-0.6.0/muc-jcr.xml 0100644 0001753 0000144 00000002425 07747021613 015215 0 ustar pere users
conference.localhost
conference.localhost
localhost
5347
secret
./spool/conference.localhost
./syslogs
./mu-conference.pid
124
Public Chatrooms
This service is for public chatrooms.
http://foo.bar/
40
./logs/
has become available
has left
is now known as
presence@localhost
mu-conference-0.6.0/Makefile 0100644 0001753 0000144 00000000143 07742764436 014602 0 ustar pere users all:
cd src/ ; make
clean:
rm -f *~
rm -f include/*~
rm -f scripts/*~
cd src/ ; make clean
mu-conference-0.6.0/README 0100644 0001753 0000144 00000015644 07755324624 014031 0 ustar pere users Welcome to the Multi-User Conferencing component for Jabber.
------------------------------------------------------------
Introduction:
---
This is based on the existing conference component, now designed
to support JEP-0045 (http://www.jabber.org/jeps/jep-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 JEP
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 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 installed, along with the development package (for packaged based solutions, such as Debian, Mandrake and RedHat)
* You will either need access to the jabberd v1.4.x source code (preferably the same source that the server was compiled with) or a copy of JCR. See the README.jcr file for more details.
New Features:
---
Note: This is not an exhaustive list. For a complete overview of the feature
set, check out the JEP 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:
---
* If compiling against the jabberd 1.4 source code:
Unpack it into your 1.4 server source folder, and type 'make' to build the mu-conference.so.
* See README.jcr for instructions regarding installing with the Jabber Component Runtime
Configuration:
---
Add this section to the browse area of the jsm service to advertise it to your users:
[Note: if you want this service to be accessible from other servers,
change any 'conference.localhost' listed below to a fully
qualified domain name!]
-----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<-----
If you wish to have mu-conference running in a seperate jabberd process (advised)
* Add the following section into the 'Service' section of 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.
* Copy the muc.xml file to the same directory as your jabber.xml file
* Edit muc.xml, and ensure that the settings are the same as you defined in the jabber.xml file
If you wish to have mu-conference loaded into the main jabberd process:
* Add the following section into the 'Service' section of the jabber.xml file:
---8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----
./mu-conference-0.6.0/src/mu-conference.so
Public Chatrooms
This service is for public chatrooms.
http://foo.bar/
20
./logs/
admin@localhost
has become available
has left
is now known as
---8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----
Settings:
---
Note: These will be stored either in the muc.xml file, or the jabber.xml
file, depending on whether you are running MU-Conference in a seperate
jabberd process or the main jabberd process
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.
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:
---
This will depend on how you have installed MU-Conference.
* For installs where MU-Conference is running in a seperate jabberd process:
o Start the main jabberd as normal
o Start an additional jabberd process, with the command:
./jabberd/jabberd -c muc.xml &
(Fixing paths as necessary. Use the main jabberd command as a reference)
* For installs where MU-Conference is running in the main jabberd process:
o Start the main jabberd as normal
---
David Sutton
jid: peregrine@legend.net.uk
mu-conference-0.6.0/scripts/ 0040755 0001753 0000144 00000000000 07755324757 014640 5 ustar pere users mu-conference-0.6.0/scripts/roommaker.pl 0100755 0001753 0000144 00000015140 07755324540 017160 0 ustar pere users #!/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;
}
mu-conference-0.6.0/scripts/README 0100644 0001753 0000144 00000000476 07564233356 015514 0 ustar pere users Scripts:
-------
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
mu-conference-0.6.0/scripts/roomname.pl 0100755 0001753 0000144 00000000413 07563750165 017001 0 ustar pere users #!/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";
}
mu-conference-0.6.0/muc.xml 0100644 0001753 0000144 00000001622 07755324650 014445 0 ustar pere users
127.0.0.1
31518
secret
./mu-conference-0.6.0/src/mu-conference.so
Public Chatrooms
This service is for public chatrooms.
http://conference.localhost/logs/
20
./logs/muc/
user@localhost
has become available
has left
is now known as
./mu-conference.pid
mu-conference-0.6.0/README.jcr 0100644 0001753 0000144 00000001373 07755321341 014571 0 ustar pere users JCR:
JCR, also known as the Jabber Component Runtime, is a way for components originally designed to run under jabberd1.4 to run externally. It has been developed by Paul Curtis, and incorporating some of the functions available in the Jabberd v1.4 server code. MU-Conference has been altered so it can run both ways - as an internal component or external using JCR.
This library will be available from http://jabber.terrapin.com/JCR/. Included with mu-conference is an example configuration file which is a copy of the configuration file I used whilst testing. It is optimised for connection to a Jabberd v2 server, however there is instructions on how to convert it to connect to a Jabberd v1.4 server inside the file. The configuration file is muc-jcr.xml.
mu-conference-0.6.0/AUTHORS 0100644 0001753 0000144 00000000044 07742265242 014201 0 ustar pere users David Sutton
mu-conference-0.6.0/jbox.xml 0100644 0001753 0000144 00000001565 07577072021 014623 0 ustar pere users
-I ../jabberd/
../jabberd/jabberd.h
admin.o
conference.o
conference_room.o
conference_user.o
utils.o
roles.o
xdata.o
xdb.o
hash.o
HEADERS
OBJECTS
mu-conference-0.6.0/ChangeLog 0100644 0001753 0000144 00000031407 07755324537 014721 0 ustar pere users 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
mu-conference-0.6.0/include/ 0040755 0001753 0000144 00000000000 07755324743 014567 5 ustar pere users mu-conference-0.6.0/include/ns.h 0100644 0001753 0000144 00000003124 07742361753 015353 0 ustar pere users /*
* 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
*/
/* 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"
#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
mu-conference-0.6.0/include/conference.h 0100644 0001753 0000144 00000032217 07755324537 017052 0 ustar pere users /*
* 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
#include
#include
#include
#include "hash.h"
#include "ns.h"
#define NAME "MU-Conference"
#undef VERSION
#ifndef _JCOMP
#define VERSION "0.6.0"
#else
#define VERSION "0.6.0-jcr"
#endif
#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_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."}
#define TERROR_MUC_BANNED (terror){403, "You have been banned from this room."}
#define TERROR_MUC_VOICE (terror){403, "You do not have permission to talk in this room."}
#define TERROR_MUC_PRIVMSG (terror){403, "Private messages are not allowed in this room."}
#define TERROR_MUC_ROOM (terror){403, "Room creation is disabled."}
#define TERROR_MUC_CONFIG (terror){405, "You are disallowed access to room configuration"}
#define TERROR_MUC_OUTSIDE (terror){405, "You are not in this room"}
#define TERROR_MUC_INVITED (terror){407, "Invitation required to join this room."}
#define TERROR_MUC_FULL (terror){408, "Room is full."}
#define TERROR_MUC_NICK (terror){409, "Please choose a different nickname."}
#define TERROR_MUC_NICKREG (terror){409, "Reserved Nick - Please choose a different nickname."}
#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;
/* conference instance */
typedef struct cni_struct
{
instance i;
#ifndef _JCOMP
xdbcache xdbc; /* Interface with xdb */
#else
void *xdbc;
#endif
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 */
#ifndef _JCOMP
mtq q;
#endif
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 */
int loader; /* Used to delay loading from xdb */
int roomlock; /* Stop dynamic room creation */
int dynamic; /* Only allows dynamic rooms to be created */
int shutdown; /* Service shutting down */
#ifndef _JCOMP
pth_mutex_t lock; /* Used for hasGHashTable *locking */
#else
GMutex *lock; /* Used for hasGHashTable *locking */
#endif
GQueue *queue; /* used to remove zombie rooms */
} *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 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; /* Topic Here */
xmlnode *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 */
xmlnode history; /* Amount of history requested by client */
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 */
};
/* 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 */
cnr con_room_new(cni c, jid roomid, jid owner, char *name, char *secret, int private, int xdata, 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 */
cnu con_room_usernick(cnr room, char *nick); /* resolves to to a user based on nickname */
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 */
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, char *status);
/* Sends alert message to user */
void con_send_room_status(cnr room, char *status); /* For sending status messages */
char *linesplit(char **entry); /* Splitting '/n' delimited string */
char *funcstr(char *file, 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 */
int isPrime(unsigned long n); /* Check Primeness for hash functions */
jid jid_fix(jid id); /* Check and fix case of jids */
/* Functions in xdata.c */
xmlnode add_xdata_boolean(char *label, char *var, int data);
xmlnode add_xdata_text(char *label, int type, char *var, char *data);
xmlnode add_xdata_desc(char *label);
void add_xdata_add_rid(gpointer key, gpointer data, gpointer arg);
void 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);
xmlnode 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 */
mu-conference-0.6.0/include/hash.h 0100644 0001753 0000144 00000002331 07742764350 015655 0 ustar pere users /*
* 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
*/
gboolean remove_key(gpointer key, gpointer data, gpointer arg);
/* Clean out GHashTable
*/
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 */
mu-conference-0.6.0/COPYING 0100644 0001753 0000144 00000035451 07742265242 014176 0 ustar pere users 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