diff options
author | Sven Moritz Hallberg <pesco@khjk.org> | 2009-03-12 20:33:28 +0100 |
---|---|---|
committer | Sven Moritz Hallberg <pesco@khjk.org> | 2009-03-12 20:33:28 +0100 |
commit | 673a54c5a78afd1dd41b4cd8811df5ab65042583 (patch) | |
tree | bffaa961139ac2be20f0875ef0ed37c87d6b18a9 | |
parent | 823de9d44f262ea2364ac8ec6a1e18e0f7dab658 (diff) | |
parent | 9e768da723b4a770967efa0d4dcaf58ccef8917f (diff) |
pretty blind try at merging in the latest trunk
42 files changed, 1008 insertions, 360 deletions
@@ -9,7 +9,7 @@ -include Makefile.settings # Program variables -objects = account.o bitlbee.o crypting.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o +objects = account.o bitlbee.o chat.o crypting.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h otr.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h subdirs = lib protocols @@ -189,6 +189,7 @@ account_t *account_get( irc_t *irc, char *id ) void account_del( irc_t *irc, account_t *acc ) { account_t *a, *l = NULL; + struct chat *c, *nc; if( acc->ic ) /* Caller should have checked, accounts still in use can't be deleted. */ @@ -202,6 +203,13 @@ void account_del( irc_t *irc, account_t *acc ) else irc->accounts = a->next; + for( c = irc->chatrooms; c; c = nc ) + { + nc = c->next; + if( acc == c->acc ) + chat_del( irc, c ); + } + while( a->set ) set_del( &a->set, a->set->key ); @@ -131,6 +131,7 @@ #include "commands.h" #include "account.h" #include "nick.h" +#include "chat.h" #include "conf.h" #include "log.h" #include "ini.h" @@ -0,0 +1,191 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2008 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Keep track of chatrooms the user is interested in */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "bitlbee.h" + +struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ) +{ + struct chat *c, *l; + set_t *s; + + if( acc->prpl->chat_join == NULL || !chat_chanok( channel ) || + chat_chancmp( channel, irc->channel ) == 0 ) + { + return NULL; + } + + for( c = irc->chatrooms; c; c = c->next ) + { + if( chat_chancmp( channel, c->channel ) == 0 ) + return NULL; + + if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) + return NULL; + + l = c; + } + + if( irc->chatrooms == NULL ) + irc->chatrooms = c = g_new0( struct chat, 1 ); + else + l->next = c = g_new0( struct chat, 1 ); + + c->acc = acc; + c->handle = g_strdup( handle ); + c->channel = g_strdup( channel ); + + s = set_add( &c->set, "auto_join", "false", set_eval_bool, c ); + /* s = set_add( &c->set, "auto_rejoin", "false", set_eval_bool, c ); */ + s = set_add( &c->set, "nick", NULL, NULL, c ); + s->flags |= SET_NULL_OK; + + return c; +} + +struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ) +{ + struct chat *c; + + for( c = irc->chatrooms; c; c = c->next ) + { + if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) + break; + } + + return c; +} + +struct chat *chat_bychannel( irc_t *irc, char *channel ) +{ + struct chat *c; + + for( c = irc->chatrooms; c; c = c->next ) + { + if( chat_chancmp( channel, c->channel ) == 0 ) + break; + } + + return c; +} + +struct chat *chat_get( irc_t *irc, char *id ) +{ + struct chat *c, *ret = NULL; + int nr; + + if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) + { + for( c = irc->chatrooms; c; c = c->next ) + if( ( nr-- ) == 0 ) + return c; + + return NULL; + } + + for( c = irc->chatrooms; c; c = c->next ) + { + if( strstr( c->handle, id ) ) + { + if( !ret ) + ret = c; + else + return NULL; + } + else if( strstr( c->channel, id ) ) + { + if( !ret ) + ret = c; + else + return NULL; + } + } + + return ret; +} + +int chat_del( irc_t *irc, struct chat *chat ) +{ + struct chat *c, *l = NULL; + + for( c = irc->chatrooms; c; c = (l=c)->next ) + if( c == chat ) + break; + + if( c == NULL ) + return 0; + else if( l == NULL ) + irc->chatrooms = c->next; + else + l->next = c->next; + + while( c->set ) + set_del( &c->set, c->set->key ); + + g_free( c->handle ); + g_free( c->channel ); + g_free( c ); + + return 1; +} + +int chat_chancmp( char *a, char *b ) +{ + if( !chat_chanok( a ) || !chat_chanok( b ) ) + return 0; + + if( a[0] == b[0] ) + return nick_cmp( a + 1, b + 1 ); + else + return -1; +} + +int chat_chanok( char *a ) +{ + if( strchr( CTYPES, a[0] ) != NULL ) + return nick_ok( a + 1 ); + else + return 0; +} + +int chat_join( irc_t *irc, struct chat *c, const char *password ) +{ + struct groupchat *gc; + char *nick = set_getstr( &c->set, "nick" ); + + if( c->acc->ic == NULL || c->acc->prpl->chat_join == NULL ) + return 0; + + if( nick == NULL ) + nick = irc->nick; + + if( ( gc = c->acc->prpl->chat_join( c->acc->ic, c->handle, nick, password ) ) ) + { + g_free( gc->channel ); + gc->channel = g_strdup( c->channel ); + return 1; + } + + return 0; +} @@ -0,0 +1,46 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2008 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Keep track of chatrooms the user is interested in */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +struct chat +{ + account_t *acc; + + char *handle; + char *channel; + set_t *set; + + struct chat *next; +}; + +struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ); +struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ); +struct chat *chat_bychannel( irc_t *irc, char *channel ); +struct chat *chat_get( irc_t *irc, char *id ); +int chat_del( irc_t *irc, struct chat *chat ); + +int chat_chancmp( char *a, char *b ); +int chat_chanok( char *a ); + +int chat_join( irc_t *irc, struct chat *c, const char *password ); @@ -78,7 +78,7 @@ conf_t *conf_load( int argc, char *argv[] ) at a *valid* configuration file. */ } - while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hu:" ) ) >= 0 ) + while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hR:u:" ) ) >= 0 ) /* ^^^^ Just to make sure we skip this step from the REHASH handler. */ { if( opt == 'i' ) @@ -313,15 +313,15 @@ static int conf_loadini( conf_t *conf, char *file ) } else { - fprintf( stderr, "Error: Unknown setting `%s` in configuration file.\n", ini->key ); + fprintf( stderr, "Error: Unknown setting `%s` in configuration file (line %d).\n", ini->key, ini->line ); return 0; /* For now just ignore unknown keys... */ } } else if( g_strcasecmp( ini->section, "defaults" ) != 0 ) { - fprintf( stderr, "Error: Unknown section [%s] in configuration file. " - "BitlBee configuration must be put in a [settings] section!\n", ini->section ); + fprintf( stderr, "Error: Unknown section [%s] in configuration file (line %d). " + "BitlBee configuration must be put in a [settings] section!\n", ini->section, ini->line ); return 0; } } diff --git a/debian/bitlbee.init b/debian/bitlbee.init index f8fac49c..1ab1bc43 100755 --- a/debian/bitlbee.init +++ b/debian/bitlbee.init @@ -31,8 +31,6 @@ if [ -r /etc/default/$NAME ]; then . /etc/default/$NAME fi -[ "$BITLBEE_DISABLED" = "1" ] && exit 0 - # # Function that starts the daemon/service. @@ -40,9 +38,17 @@ fi d_start() { # Make sure BitlBee can actually write its PID... touch /var/run/bitlbee.pid - chown bitlbee /var/run/bitlbee.pid + chown bitlbee: /var/run/bitlbee.pid - start-stop-daemon --start --quiet --pidfile $PIDFILE \ + # Clean up after the bug between 1.2-5 and 1.2.1-2 where BitlBee ran + # as root. (#494656 and #495877) Fixing this in the postinst script + # is not enough since the user will restart his BitlBee after up- + # grading the package, and the BitlBee running as root will then + # save its settings, re-setting ownership of the file to root. + # TODO: Remove this after a few revisions. + find /var/lib/bitlbee -uid 0 -name '*.xml' -exec chown bitlbee: {} \; + + start-stop-daemon --start --quiet \ --exec $DAEMON -- -p $BITLBEE_PORT -P $PIDFILE $BITLBEE_OPTS } @@ -57,6 +63,8 @@ d_stop() { case "$1" in start) + [ "$BITLBEE_DISABLED" = "1" ] && exit 0 + echo -n "Starting $DESC: $NAME" d_start echo "." diff --git a/debian/changelog b/debian/changelog index a569f4f8..a11a67b8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,42 @@ -bitlbee (1.2-6) UNRELEASED; urgency=low +bitlbee (1.2.2-1) unstable; urgency=critical - * Add Homepage and Vcs-Bzr fields. + * New upstream version. + * Fixes a security issue (account hijacking), hence the high priority. + * Using a patch to set the User setting in bitlbee.conf properly to keep + upstream and Debian properly separated in my bzr tree. + + -- Wilmer van der Gaast <wilmer@gaast.net> Wed, 27 Aug 2008 23:59:50 +0100 + +bitlbee (1.2.1-3) unstable; urgency=high + + * chown /var/lib/bitlbee/*.xml to bitlbee:bitlbee to clean up after + 1.2-5 and the bugfix in 1.2.1-2. (Closes: #495877) + * Moved BITLBEE_DISABLED check to only check when trying to *start* + the daemon. (Closes: #488611) + + -- Wilmer van der Gaast <wilmer@gaast.net> Sat, 23 Aug 2008 18:53:54 +0100 + +bitlbee (1.2.1-2) unstable; urgency=low + + * Properly set the User= line to something sensible so BitlBee won't + run as root anymore. 1.2-5 was a bad upload. :-( (Closes: #494656) + + -- Wilmer van der Gaast <wilmer@gaast.net> Tue, 12 Aug 2008 00:36:03 +0100 + +bitlbee (1.2.1-1.1) unstable; urgency=low + + * Non-Maintainer Upload + * Use invoke-rc.d as per policy. (Closes: #492637) [Thanks to Matt + Kraai] + + -- Don Armstrong <don@debian.org> Wed, 06 Aug 2008 06:57:18 -0700 + +bitlbee (1.2.1-1) unstable; urgency=low + + * New upstream release. + * Add Homepage and Vcs-Bzr fields. (From Jelmer.) - -- Jelmer Vernooij <jelmer@samba.org> Sun, 11 May 2008 14:18:16 +0200 + -- Wilmer van der Gaast <wilmer@gaast.net> Thu, 26 Jun 2008 00:07:50 +0100 bitlbee (1.2-5) unstable; urgency=low diff --git a/debian/patches/bitlbee.conf.diff b/debian/patches/bitlbee.conf.diff new file mode 100644 index 00000000..b80bcb4c --- /dev/null +++ b/debian/patches/bitlbee.conf.diff @@ -0,0 +1,13 @@ +=== modified file 'bitlbee.conf' +--- debian/bitlbee/etc/bitlbee/bitlbee.conf 2008-08-26 22:33:54 +0000 ++++ debian/bitlbee/etc/bitlbee/bitlbee.conf 2008-08-27 23:18:13 +0000 +@@ -23,7 +23,7 @@ + ## If BitlBee is started by root as a daemon, it can drop root privileges, + ## and change to the specified user. + ## +-# User = bitlbee ++User = bitlbee + + ## DaemonPort/DaemonInterface: + ## + diff --git a/debian/postinst b/debian/postinst index 80249bfe..db324b65 100755 --- a/debian/postinst +++ b/debian/postinst @@ -63,12 +63,23 @@ if [ -e /usr/share/bitlbee/help.upgrading ]; then fi fi +if ! grep -qi '^User *= *' /etc/bitlbee/bitlbee.conf; then + echo 'Updating configuration file, enabling User-setting...' + if ! sed -i -e 's/# *User *= *.*/User = bitlbee/i' /etc/bitlbee/bitlbee.conf; then + echo 'Failed! BitlBee may run as root now, please check your configs.' + fi +fi + if [ -n "$2" -a "$BITLBEE_UPGRADE_DONT_RESTART" != "1" ]; then - /etc/init.d/bitlbee restart + if which invoke-rc.d >/dev/null 2>&1; then + invoke-rc.d bitlbee restart + else + /etc/init.d/bitlbee restart + fi fi ## If we're upgrading, we'll probably skip this next part -if [ -d $CONFDIR ] && chown -R bitlbee $CONFDIR; then +if [ -d $CONFDIR ] && chown -R bitlbee: $CONFDIR; then echo 'BitlBee (probably) already installed, skipping user/configdir installation' exit 0 fi @@ -90,5 +101,9 @@ else fi if [ -z "$2" ]; then - /etc/init.d/bitlbee start + if which invoke-rc.d >/dev/null 2>&1; then + invoke-rc.d bitlbee start + else + /etc/init.d/bitlbee start + fi fi diff --git a/debian/prerm b/debian/prerm index 5272e273..8426ab3a 100755 --- a/debian/prerm +++ b/debian/prerm @@ -9,5 +9,9 @@ if [ "$1" = "upgrade" ]; then mv /usr/share/bitlbee/help.txt /usr/share/bitlbee/help.upgrading fi else - /etc/init.d/bitlbee stop || exit 0 + if which invoke-rc.d >/dev/null 2>&1; then + invoke-rc.d bitblee stop || exit 0 + else + /etc/init.d/bitlbee stop || exit 0 + fi fi diff --git a/debian/rules b/debian/rules index 661cf30e..788e5006 100755 --- a/debian/rules +++ b/debian/rules @@ -54,6 +54,7 @@ binary-arch: build-arch install-arch cp doc/CHANGES debian/bitlbee/usr/share/doc/bitlbee/changelog cp utils/* debian/bitlbee/usr/share/doc/bitlbee/examples/ cp debian/bitlbee.init debian/bitlbee/etc/init.d/bitlbee + patch -p0 < debian/patches/bitlbee.conf.diff cd debian/bitlbee/usr/share/; \ gzip -9 doc/bitlbee/changelog.Debian doc/bitlbee/changelog doc/bitlbee/user-guide.txt \ doc/bitlbee/examples/* man/man8/bitlbee.8 man/man5/bitlbee.conf.5 diff --git a/doc/user-guide/Makefile b/doc/user-guide/Makefile index eb31fc0f..9841de8d 100644 --- a/doc/user-guide/Makefile +++ b/doc/user-guide/Makefile @@ -12,7 +12,7 @@ all: user-guide.txt user-guide.html help.txt # user-guide.pdf user-guide.ps user mv $*.db.txt $@ %.html: %.db.xml - xsltproc --output $@ http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< + xsltproc --output $@ http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl $< %.pdf: %.db.xml xmlto --skip-validation pdf $< diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 3a7bd5a8..c8f758fb 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -124,7 +124,7 @@ <description> <para> - This command gives you a list of all the accounts known by BitlBee, including the numbers you'll need for most account commands. + This command gives you a list of all the accounts known by BitlBee. </para> </description> </bitlbee-command> @@ -137,11 +137,11 @@ <description> <para> - This account can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing <emphasis>account set <account id></emphasis>. + This command can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing <emphasis>account set <account id></emphasis>. </para> <para> - For more infomation about a setting, see <emphasis>help set <setting></emphasis>. For details about the syntax of this command, see <emphasis>help set</emphasis>. + For more infomation about a setting, see <emphasis>help set <setting></emphasis>. </para> <para> @@ -151,6 +151,89 @@ </bitlbee-command> </bitlbee-command> + <bitlbee-command name="chat"> + <short-description>Chatroom list maintenance</short-description> + <syntax>chat <action> [<arguments>]</syntax> + + <description> + + <para> + Available actions: add, del, list, with and set. See <emphasis>help chat <action></emphasis> for more information. + </para> + + </description> + + <bitlbee-command name="add"> + <syntax>chat add <account> <room> [<channel>]</syntax> + + <description> + <para> + Add a chatroom to the list of chatrooms you're interested in. BitlBee needs this list to map room names to a proper IRC channel name. + </para> + + <para> + After adding a room to your list, you can simply use the IRC /join command to enter the room. Also, you can tell BitlBee to automatically join the room when you log in. (See <emphasis>chat set</emphasis>) + </para> + </description> + + </bitlbee-command> + + <bitlbee-command name="del"> + <syntax>chat del <chat id></syntax> + + <description> + <para> + This commands deletes an chatroom from your list. + </para> + + <para> + The room ID can be a number (see <emphasis>chat list</emphasis>), or (part of) the name of the room/channel. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="list"> + <syntax>chat list</syntax> + + <description> + <para> + This command gives you a list of all the chatrooms known by BitlBee. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="with"> + <syntax>chat with <nickname></syntax> + + <description> + <para> + While most <emphasis>chat</emphasis> subcommands are about named chatrooms, this command can be used to open an unnamed groupchat with one or more persons. This command is what <emphasis>/join #nickname</emphasis> used to do in older BitlBee versions. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="set"> + <syntax>chat set <chat id></syntax> + <syntax>chat set <chat id>/<setting></syntax> + <syntax>chat set <chat id>/<setting> <value></syntax> + <syntax>chat set -del <chat id>/<setting></syntax> + + <description> + <para> + This command can be used to change various settings for chatrooms. + </para> + + <para> + For more infomation about a setting, see <emphasis>help set <setting></emphasis>. + </para> + + <para> + The room ID can be a number (see <emphasis>chat list</emphasis>), or (part of) the name of the room/channel. + </para> + </description> + </bitlbee-command> + </bitlbee-command> + <bitlbee-command name="add"> <short-description>Add a buddy to your contact list</short-description> <syntax>add <connection> <handle> [<nick>]</syntax> @@ -451,6 +534,16 @@ </description> </bitlbee-setting> + <bitlbee-setting name="auto_join" type="boolean" scope="chat"> + <default>false</default> + + <description> + <para> + With this option enabled, BitlBee will automatically join this chatroom when you log in. + </para> + </description> + </bitlbee-setting> + <bitlbee-setting name="auto_reconnect" type="boolean" scope="both"> <default>false</default> @@ -711,6 +804,15 @@ </description> </bitlbee-setting> + <bitlbee-setting name="nick" type="string" scope="chat"> + + <description> + <para> + You can use this option to set your nickname in a chatroom. You won't see this nickname yourself, but other people in the room will. By default, BitlBee will use your username as the chatroom nickname. + </para> + </description> + </bitlbee-setting> + <bitlbee-setting name="password" type="string" scope="both"> <description> <para> @@ -790,7 +892,7 @@ <bitlbee-setting name="resource_select" type="string" scope="account"> <default>priority</default> - <possible-values>priority, time</possible-values> + <possible-values>priority, activity</possible-values> <description> <para> @@ -798,7 +900,7 @@ </para> <para> - Normally it's set to <emphasis>priority</emphasis> which means messages will always be delivered to the buddy's resource with the highest priority. If the setting is set to <emphasis>time</emphasis>, messages will be delivered to the resource that was last used to send you a message (or the resource that most recently connected). + Normally it's set to <emphasis>priority</emphasis> which means messages will always be delivered to the buddy's resource with the highest priority. If the setting is set to <emphasis>activity</emphasis>, messages will be delivered to the resource that was last used to send you a message (or the resource that most recently connected). </para> </description> </bitlbee-setting> @@ -1080,28 +1182,4 @@ </ircexample> </bitlbee-command> - - <bitlbee-command name="join_chat"> - <short-description>Join a named groupchat/conference room</short-description> - <syntax>join_chat <connection> <room name> [<channel name>] [<room nickname>] [<password>]</syntax> - - <description> - <para> - On most IM-networks groupchats can be started using the /join command. (<emphasis>/join #foo</emphasis> to start a chatroom with you and <emphasis>foo</emphasis>) This doesn't work with names groupchats though (which exist on Jabber networks and AIM, for example), instead you can use this command. - </para> - - <para> - The first two arguments are required. <emphasis>room name</emphasis> is the name of the chatroom on the IM-network. <emphasis>channel name</emphasis> is the IRC channel name BitlBee should map this to. <emphasis>room nickname</emphasis> is the nickname you want to have in this channel. If you don't give these options, BitlBee will do the right guesses. - </para> - - <para> - The following command will join you to the chatroom called <emphasis>bitlbee@conference.bitlbee.org</emphasis>. The channel will be called <emphasis>&bitlbee-help</emphasis> because <emphasis>&bitlbee</emphasis> will already be in use. Your nickname will be <emphasis>help-me</emphasis>. - </para> - </description> - - <ircexample> - <ircline nick="wilmer">join_chat jabber bitlbee@conference.bitlbee.org &bitlbee-help help-me</ircline> - </ircexample> - - </bitlbee-command> </chapter> diff --git a/doc/user-guide/help.xml b/doc/user-guide/help.xml index 34fdb9e2..7487a841 100644 --- a/doc/user-guide/help.xml +++ b/doc/user-guide/help.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="iso-8859-1"?> +<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> <book id="BitlBee-Help" xmlns:xi="http://www.w3.org/2003/XInclude"> diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml index b55a8915..3102b8b0 100644 --- a/doc/user-guide/misc.xml +++ b/doc/user-guide/misc.xml @@ -68,15 +68,15 @@ Of course you can also create your own groupchats. Type <emphasis>help groupchat <title>Creating groupchats</title> <para> -If you want to start a groupchat with the person <emphasis>lisa_msn</emphasis> in it, just join the channel <emphasis>#lisa_msn</emphasis>. BitlBee will refuse to join you to the channel with that name, but it will create a new virtual channel with root, you and lisa_msn in it. +To open a groupchat, use the <emphasis>chat with</emphasis> command. For example, to start a groupchat with the person <emphasis>lisa_msn</emphasis> in it, just type <emphasis>chat with lisa_msn</emphasis>. BitlBee will create a new virtual channel with root, you and lisa_msn in it. </para> <para> -Of course a channel with only two people isn't really exciting yet. So the next step is to invite some other people to the channel. For this, you can use the <emphasis>/invite</emphasis> command of your IRC client. Please do keep in mind that all the people have to be on the same network and contact list! You can't invite Yahoo! buddies into an MSN groupchat. +Then, just use the ordinary IRC <emphasis>/invite</emphasis> command to invite more people. Please do keep in mind that all the people have to be on the same network and contact list! You can't invite Yahoo! buddies into an MSN groupchat. </para> <para> -Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>join_chat</emphasis> command to join them. See <emphasis>help join_chat</emphasis> for more information. +Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>chat add</emphasis> command to join them. See <emphasis>help chat_add</emphasis> for more information. </para> </sect1> diff --git a/doc/user-guide/user-guide.xml b/doc/user-guide/user-guide.xml index 5b881fb2..5e1c8fe0 100644 --- a/doc/user-guide/user-guide.xml +++ b/doc/user-guide/user-guide.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="iso-8859-1"?> +<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> <book id="BitlBee-User-Guide" xmlns:xi="http://www.w3.org/2003/XInclude"> @@ -145,6 +145,7 @@ irc_t *irc_new( int fd ) irc_connection_list = g_slist_append( irc_connection_list, irc ); + s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc ); s = set_add( &irc->set, "auto_reconnect", "false", set_eval_bool, irc ); s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc ); @@ -417,10 +418,8 @@ void irc_process( irc_t *irc ) lines[i] = conv; } - if( lines[i] ) + if( lines[i] && ( cmd = irc_parse_line( lines[i] ) ) ) { - if( ( cmd = irc_parse_line( lines[i] ) ) == NULL ) - continue; irc_exec( irc, cmd ); g_free( cmd ); } @@ -495,7 +494,7 @@ char **irc_parse_line( char *line ) /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */ if( line[0] == ':' ) { - for( i = 0; line[i] != ' '; i ++ ); + for( i = 0; line[i] && line[i] != ' '; i ++ ); line = line + i; } for( i = 0; line[i] == ' '; i ++ ); @@ -820,7 +819,9 @@ void irc_login( irc_t *irc ) irc_reply( irc, 2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost ); irc_reply( irc, 3, ":%s", IRCD_INFO ); irc_reply( irc, 4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES ); - irc_reply( irc, 5, "PREFIX=(ohv)@%%+ CHANTYPES=#& CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", CMODES, MAX_NICK_LENGTH - 1 ); + irc_reply( irc, 5, "PREFIX=(ohv)@%%+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee " + "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", + CTYPES, CMODES, MAX_NICK_LENGTH - 1 ); irc_motd( irc ); irc->umode[0] = '\0'; irc_umode_set( irc, "+" UMODE, 1 ); @@ -1084,7 +1085,7 @@ int irc_send( irc_t *irc, char *nick, char *s, int flags ) struct groupchat *c = NULL; user_t *u = NULL; - if( *nick == '#' || *nick == '&' ) + if( strchr( CTYPES, *nick ) ) { if( !( c = irc_chat_by_channel( irc, nick ) ) ) { @@ -39,6 +39,7 @@ #define CMODES "nt" #define CMODE "t" #define UMODE "s" +#define CTYPES "&#" typedef enum { @@ -49,11 +50,6 @@ typedef enum USTATUS_SHUTDOWN = 8 } irc_status_t; -typedef struct channel -{ - char *name; -} channel_t; - typedef struct irc { int fd; @@ -88,6 +84,7 @@ typedef struct irc struct query *queries; struct account *accounts; + struct chat *chatrooms; struct __USER *users; GHashTable *userhash; @@ -103,7 +100,6 @@ typedef struct irc } irc_t; #include "user.h" -// #include "nick.h" extern GSList *irc_connection_list; diff --git a/irc_commands.c b/irc_commands.c index 6a47007a..09289a55 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -124,7 +124,7 @@ static void irc_cmd_oper( irc_t *irc, char **cmd ) static void irc_cmd_mode( irc_t *irc, char **cmd ) { - if( *cmd[1] == '#' || *cmd[1] == '&' ) + if( strchr( CTYPES, *cmd[1] ) ) { if( cmd[2] ) { @@ -192,32 +192,14 @@ static void irc_cmd_join( irc_t *irc, char **cmd ) RFC doesn't have any reply for that though? */ else if( cmd[1] ) { - if( ( cmd[1][0] == '#' || cmd[1][0] == '&' ) && cmd[1][1] ) - { - user_t *u = user_find( irc, cmd[1] + 1 ); - - if( u && u->ic && u->ic->acc->prpl->chat_with ) - { - irc_reply( irc, 403, "%s :Initializing groupchat in a different channel", cmd[1] ); - - if( !u->ic->acc->prpl->chat_with( u->ic, u->handle ) ) - { - irc_usermsg( irc, "Could not open a groupchat with %s.", u->nick ); - } - } - else if( u ) - { - irc_reply( irc, 403, "%s :Groupchats are not possible with %s", cmd[1], cmd[1]+1 ); - } - else - { - irc_reply( irc, 403, "%s :No such nick", cmd[1] ); - } - } + struct chat *c; + + if( strchr( CTYPES, cmd[1][0] ) == NULL || cmd[1][1] == 0 ) + irc_reply( irc, 479, "%s :Invalid channel name", cmd[1] ); + else if( ( c = chat_bychannel( irc, cmd[1] ) ) && c->acc && c->acc->ic ) + chat_join( irc, c, cmd[2] ); else - { irc_reply( irc, 403, "%s :No such channel", cmd[1] ); - } } } diff --git a/lib/events_libevent.c b/lib/events_libevent.c index d3403152..cf616576 100644 --- a/lib/events_libevent.c +++ b/lib/events_libevent.c @@ -36,9 +36,11 @@ #include "proxy.h" static void b_main_restart(); -static guint id_next = 1; +static guint id_next = 1; /* Next ID to be allocated to an event handler. */ +static guint id_cur = 0; /* Event ID that we're currently handling. */ +static guint id_dead; /* Set to 1 if b_event_remove removes id_cur. */ static GHashTable *id_hash; -static int quitting = 0; +static int quitting = 0; /* Prepare to quit, stop handling events. */ /* Since libevent doesn't handle two event handlers for one fd-condition very well (which happens sometimes when BitlBee changes event handlers @@ -118,7 +120,7 @@ static void b_event_passthrough( int fd, short event, void *data ) { struct b_event_data *b_ev = data; b_input_condition cond = 0; - int id; + gboolean st; if( fd >= 0 ) { @@ -132,21 +134,30 @@ static void b_event_passthrough( int fd, short event, void *data ) /* Since the called function might cancel this handler already (which free()s b_ev), we have to remember the ID here. */ - id = b_ev->id; + id_cur = b_ev->id; + id_dead = 0; if( quitting ) { - b_event_remove( id ); + b_event_remove( id_cur ); return; } - if( !b_ev->function( b_ev->data, fd, cond ) ) + st = b_ev->function( b_ev->data, fd, cond ); + if( id_dead ) + { + /* This event was killed already, don't touch it! */ + return; + } + else if( !st ) { event_debug( "Handler returned FALSE: " ); - b_event_remove( id ); + b_event_remove( id_cur ); } else if( fd == -1 ) { + /* fd == -1 means it was a timer. These can't be auto-repeated + so it has to be recreated every time. */ struct timeval tv; tv.tv_sec = b_ev->timeout / 1000; @@ -235,6 +246,9 @@ void b_event_remove( gint id ) event_debug( "b_event_remove( %d )\n", id ); if( b_ev ) { + if( id == id_cur ) + id_dead = TRUE; + g_hash_table_remove( id_hash, &b_ev->id ); if( b_ev->evinfo.ev_fd >= 0 ) { diff --git a/lib/http_client.c b/lib/http_client.c index b00fcf98..aae5645b 100644 --- a/lib/http_client.c +++ b/lib/http_client.c @@ -58,8 +58,8 @@ void *http_dorequest( char *host, int port, int ssl, char *request, http_input_f if( error ) { - g_free( req ); - return( NULL ); + http_free( req ); + return NULL; } req->func = func; @@ -159,10 +159,7 @@ error: req->status_string = g_strdup( "Error while writing HTTP request" ); req->func( req ); - - g_free( req->request ); - g_free( req ); - + http_free( req ); return FALSE; } @@ -443,11 +440,15 @@ cleanup: closesocket( req->fd ); req->func( req ); - + http_free( req ); + return FALSE; +} + +void http_free( struct http_request *req ) +{ g_free( req->request ); g_free( req->reply_headers ); g_free( req->status_string ); g_free( req ); - - return FALSE; } + diff --git a/lib/http_client.h b/lib/http_client.h index 78d6dbd1..d73894a4 100644 --- a/lib/http_client.h +++ b/lib/http_client.h @@ -80,3 +80,5 @@ struct http_request are also supported (using ssl_client). */ void *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data ); void *http_dorequest_url( char *url_string, http_input_function func, gpointer data ); + +void http_free( struct http_request *req ); @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2005 Wilmer van der Gaast and others * + * Copyright 2002-2008 Wilmer van der Gaast and others * \********************************************************************/ /* INI file reading code */ @@ -27,64 +27,115 @@ ini_t *ini_open( char *file ) { - ini_t *ini = g_new0( ini_t, 1 ); + int fd; + ini_t *ini = NULL; + struct stat fi; - if( ( ini->fp = fopen( file, "r" ) ) == NULL ) + if( ( fd = open( file, O_RDONLY ) ) != -1 && + fstat( fd, &fi ) == 0 && + fi.st_size <= 16384 && + ( ini = g_malloc( sizeof( ini_t ) + fi.st_size + 1 ) ) && + read( fd, ini->file, fi.st_size ) == fi.st_size ) { - g_free( ini ); - return( NULL ); + memset( ini, 0, sizeof( ini_t ) ); + ini->size = fi.st_size; + ini->file[ini->size] = 0; + ini->cur = ini->file; + ini->c_section = ""; + return ini; } - return( ini ); + g_free( ini ); + if( fd >= 0 ) + close( fd ); + + return NULL; +} + +/* Strips leading and trailing whitespace and returns a pointer to the first + non-ws character of the given string. */ +static char *ini_strip_whitespace( char *in ) +{ + char *e; + + while( isspace( *in ) ) + in++; + + e = in + strlen( in ) - 1; + while( e > in && isspace( *e ) ) + e--; + e[1] = 0; + + return in; } int ini_read( ini_t *file ) { - char key[MAX_STRING], s[MAX_STRING], *t; - int i; + char *s; - while( !feof( file->fp ) ) + while( file->cur && file->cur < file->file + file->size ) { - *s = 0; - fscanf( file->fp, "%127[^\n#]s", s ); - fscanf( file->fp, "%*[^\n]s" ); - fgetc( file->fp ); /* Skip newline */ - file->line ++; - if( strchr( s, '=' ) ) + char *e, *next; + + file->line++; + + /* Find the end of line */ + if( ( e = strchr( file->cur, '\n' ) ) != NULL ) { - sscanf( s, "%[^ =]s", key ); - if( ( t = strchr( key, '.' ) ) ) + *e = 0; + next = e + 1; + } + else + { + /* No more lines. */ + e = file->cur + strlen( file->cur ); + next = NULL; + } + + /* Comment? */ + if( ( s = strchr( file->cur, '#' ) ) != NULL ) + *s = 0; + + file->cur = ini_strip_whitespace( file->cur ); + + if( *file->cur == '[' ) + { + file->cur++; + if( ( s = strchr( file->cur, ']' ) ) != NULL ) + { + *s = 0; + file->c_section = file->cur; + } + } + else if( ( s = strchr( file->cur, '=' ) ) != NULL ) + { + *s = 0; + file->key = ini_strip_whitespace( file->cur ); + file->value = ini_strip_whitespace( s + 1 ); + + if( ( s = strchr( file->key, '.' ) ) != NULL ) { - *t = 0; - strcpy( file->section, key ); - t ++; + *s = 0; + file->section = file->key; + file->key = s + 1; } else { - strcpy( file->section, file->c_section ); - t = key; + file->section = file->c_section; } - sscanf( t, "%s", file->key ); - t = strchr( s, '=' ) + 1; - for( i = 0; t[i] == ' '; i ++ ); - strcpy( file->value, &t[i] ); - for( i = strlen( file->value ) - 1; file->value[i] == 32; i -- ) - file->value[i] = 0; - return( 1 ); - } - else if( ( t = strchr( s, '[' ) ) ) - { - strcpy( file->c_section, t + 1 ); - t = strchr( file->c_section, ']' ); - *t = 0; + file->cur = next; + return 1; } + /* else: noise/comment/etc, let's just ignore it. */ + + file->cur = next; } - return( 0 ); + + return 0; } void ini_close( ini_t *file ) { - fclose( file->fp ); g_free( file ); } @@ -28,12 +28,14 @@ typedef struct { - FILE *fp; int line; - char c_section[MAX_STRING]; - char section[MAX_STRING]; - char key[MAX_STRING]; - char value[MAX_STRING]; + char *c_section; + char *section; + char *key; + char *value; + int size; + char *cur, *tok; + char file[]; } ini_t; ini_t *ini_open( char *file ); diff --git a/lib/proxy.c b/lib/proxy.c index 91493557..e52837fe 100644 --- a/lib/proxy.c +++ b/lib/proxy.c @@ -557,7 +557,6 @@ int proxy_connect(const char *host, int port, b_event_handler func, gpointer dat else if (proxytype == PROXY_SOCKS5) return proxy_connect_socks5(host, port, phb); - if (phb->host) g_free(phb); g_free(phb); return -1; } diff --git a/lib/xmltree.c b/lib/xmltree.c index e65b4f41..67fe46e1 100644 --- a/lib/xmltree.c +++ b/lib/xmltree.c @@ -471,7 +471,7 @@ char *xt_find_attr( struct xt_node *node, const char *key ) return node->attr[i].value; } -struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children ) +struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children ) { struct xt_node *node, *c; diff --git a/lib/xmltree.h b/lib/xmltree.h index 10677412..34e3be68 100644 --- a/lib/xmltree.h +++ b/lib/xmltree.h @@ -89,7 +89,7 @@ void xt_free( struct xt_parser *xt ); struct xt_node *xt_find_node( struct xt_node *node, const char *name ); char *xt_find_attr( struct xt_node *node, const char *key ); -struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children ); +struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children ); void xt_add_child( struct xt_node *parent, struct xt_node *child ); void xt_add_attr( struct xt_node *node, const char *key, const char *value ); int xt_remove_attr( struct xt_node *node, const char *key ); diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index 79fdd053..480006bd 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -25,7 +25,7 @@ static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); -struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password ) +struct groupchat *jabber_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password ) { struct jabber_chat *jc; struct xt_node *node; @@ -35,9 +35,9 @@ struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char * roomjid = g_strdup_printf( "%s/%s", room, nick ); node = xt_new_node( "x", NULL, NULL ); xt_add_attr( node, "xmlns", XMLNS_MUC ); - node = jabber_make_packet( "presence", NULL, roomjid, node ); if( password ) xt_add_child( node, xt_new_node( "password", password, NULL ) ); + node = jabber_make_packet( "presence", NULL, roomjid, node ); jabber_cache_add( ic, node, jabber_chat_join_failed ); if( !jabber_write_packet( ic, node ) ) diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 518c9506..6f7e1c05 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -424,7 +424,7 @@ static void jabber_remove_buddy( struct im_connection *ic, char *who, char *grou presence_send_request( ic, who, "unsubscribe" ); } -static struct groupchat *jabber_chat_join_( struct im_connection *ic, char *room, char *nick, char *password ) +static struct groupchat *jabber_chat_join_( struct im_connection *ic, const char *room, const char *nick, const char *password ) { if( strchr( room, '@' ) == NULL ) imcb_error( ic, "Invalid room name: %s", room ); diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 904bf0c4..ee453144 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -239,7 +239,7 @@ xt_status sasl_pkt_result( struct xt_node *node, gpointer data ); gboolean sasl_supported( struct im_connection *ic ); /* conference.c */ -struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password ); +struct groupchat *jabber_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password ); struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name ); void jabber_chat_free( struct groupchat *c ); int jabber_chat_msg( struct groupchat *ic, char *message, int flags ); diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index 6fc360b7..939bc888 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -48,8 +48,9 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data ) { if( !( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT | GET_BUDDY_CREAT ) ) ) { - if( set_getbool( &ic->irc->set, "debug" ) ) - imcb_log( ic, "Warning: Could not handle presence information from JID: %s", from ); + /* + imcb_log( ic, "Warning: Could not handle presence information from JID: %s", from ); + */ return XT_HANDLED; } @@ -105,8 +106,9 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data ) { if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL ) { - if( set_getbool( &ic->irc->set, "debug" ) ) - imcb_log( ic, "Warning: Received presence information from unknown JID: %s", from ); + /* + imcb_log( ic, "Warning: Received presence information from unknown JID: %s", from ); + */ return XT_HANDLED; } diff --git a/protocols/nogaim.c b/protocols/nogaim.c index e6a89dfd..9dbf71ab 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -248,6 +248,8 @@ static gboolean send_keepalive( gpointer d, gint fd, b_input_condition cond ) void imcb_connected( struct im_connection *ic ) { + irc_t *irc = ic->irc; + struct chat *c; user_t *u; /* MSN servers sometimes redirect you to a different server and do @@ -270,6 +272,15 @@ void imcb_connected( struct im_connection *ic ) /* Apparently we're connected successfully, so reset the exponential backoff timer. */ ic->acc->auto_reconnect_delay = 0; + + for( c = irc->chatrooms; c; c = c->next ) + { + if( c->acc != ic->acc ) + continue; + + if( set_getbool( &c->set, "auto_join" ) ) + chat_join( irc, c, NULL ); + } } gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond ) @@ -309,6 +320,9 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) ic->acc->prpl->logout( ic ); b_event_remove( ic->inpa ); + g_free( ic->away ); + ic->away = NULL; + u = irc->users; while( u ) { @@ -715,7 +729,7 @@ void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) } } -struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle ) +struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) { struct groupchat *c; diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 7e14c560..83091c02 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -212,7 +212,7 @@ struct prpl { * your protocol does not support publicly named group chats, then do * not implement this. */ struct groupchat * - (* chat_join) (struct im_connection *, char *room, char *nick, char *password); + (* chat_join) (struct im_connection *, const char *room, const char *nick, const char *password); /* Change the topic, if supported. Note that BitlBee expects the IM server to confirm the topic change with a regular topic change event. If it doesn't do that, you have to fake it to make it @@ -242,7 +242,7 @@ G_MODULE_EXPORT void register_protocol( struct prpl * ); /* You will need this function in prpl->login() to get an im_connection from * the account_t parameter. */ G_MODULE_EXPORT struct im_connection *imcb_new( account_t *acc ); -G_MODULE_EXPORT void imcb_free( struct im_connection *ic ); +G_MODULE_EXPORT void imc_free( struct im_connection *ic ); /* Once you're connected, you should call this function, so that the user will * see the success. */ G_MODULE_EXPORT void imcb_connected( struct im_connection *ic ); @@ -293,7 +293,7 @@ G_MODULE_EXPORT void imcb_chat_invited( struct im_connection *ic, char *handle, * - After you have a groupchat pointer, you should add the handles, finally * the user her/himself. At that point the group chat will be visible to the * user, too. */ -G_MODULE_EXPORT struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle ); +G_MODULE_EXPORT struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ); G_MODULE_EXPORT void imcb_chat_add_buddy( struct groupchat *b, char *handle ); /* To remove a handle from a group chat. Reason can be NULL. */ G_MODULE_EXPORT void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason ); diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 28207103..bb9734c8 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -90,7 +90,7 @@ struct oscar_data { GSList *oscar_chats; - gboolean killme; + gboolean killme, no_reconnect; gboolean icq; GSList *evilhack; @@ -180,6 +180,7 @@ static struct chat_connection *find_oscar_chat_by_conn(struct im_connection *ic, static int gaim_parse_auth_resp (aim_session_t *, aim_frame_t *, ...); static int gaim_parse_login (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_logout (aim_session_t *, aim_frame_t *, ...); static int gaim_handle_redirect (aim_session_t *, aim_frame_t *, ...); static int gaim_parse_oncoming (aim_session_t *, aim_frame_t *, ...); static int gaim_parse_offgoing (aim_session_t *, aim_frame_t *, ...); @@ -293,7 +294,7 @@ static gboolean oscar_callback(gpointer data, gint source, if (aim_get_command(odata->sess, conn) >= 0) { aim_rxdispatch(odata->sess); if (odata->killme) - imc_logout(ic, TRUE); + imc_logout(ic, !odata->no_reconnect); } else { if ((conn->type == AIM_CONN_TYPE_BOS) || !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) { @@ -519,6 +520,7 @@ static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { break; case 0x18: /* connecting too frequently */ + od->no_reconnect = TRUE; imcb_error(ic, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer.")); break; case 0x1c: @@ -571,6 +573,7 @@ static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parseaiminfo, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parsemtn, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_parse_logout, 0); ((struct oscar_data *)ic->proto_data)->conn = bosconn; for (i = 0; i < (int)strlen(info->bosip); i++) { @@ -750,6 +753,30 @@ static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) { return 1; } +static int gaim_parse_logout(aim_session_t *sess, aim_frame_t *fr, ...) { + struct im_connection *ic = sess->aux_data; + struct oscar_data *odata = ic->proto_data; + int code; + va_list ap; + + va_start(ap, fr); + code = va_arg(ap, int); + va_end(ap); + + imcb_error( ic, "Connection aborted by server: %s", code == 1 ? + "someone else logged in with your account" : + "unknown reason" ); + + /* Tell BitlBee to disable auto_reconnect if code == 1, since that + means a concurrent login somewhere else. */ + odata->no_reconnect = code == 1; + + /* DO NOT log out here! Just tell the callback to do it. */ + odata->killme = TRUE; + + return 1; +} + static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) { struct im_connection *ic = sess->aux_data; struct chat_connection *chatcon; @@ -1938,8 +1965,7 @@ static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL); - if (ic->away) - g_free(ic->away); + g_free(ic->away); ic->away = NULL; if (!message) { @@ -1959,55 +1985,53 @@ static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, static void oscar_set_away_icq(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message) { - const char *msg = NULL; + const char *msg = NULL; gboolean no_message = FALSE; /* clean old states */ - if (ic->away) { - g_free(ic->away); - ic->away = NULL; - } + g_free(ic->away); + ic->away = NULL; od->sess->aim_icq_state = 0; /* if no message, then use an empty message */ - if (message) { - msg = message; - } else { - msg = ""; + if (message) { + msg = message; + } else { + msg = ""; no_message = TRUE; - } + } if (!g_strcasecmp(state, "Online")) { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL); } else if (!g_strcasecmp(state, "Away")) { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY); - ic->away = g_strdup(msg); + ic->away = g_strdup(msg); od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY; } else if (!g_strcasecmp(state, "Do Not Disturb")) { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY); - ic->away = g_strdup(msg); + ic->away = g_strdup(msg); od->sess->aim_icq_state = AIM_MTYPE_AUTODND; } else if (!g_strcasecmp(state, "Not Available")) { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY); - ic->away = g_strdup(msg); + ic->away = g_strdup(msg); od->sess->aim_icq_state = AIM_MTYPE_AUTONA; } else if (!g_strcasecmp(state, "Occupied")) { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY); - ic->away = g_strdup(msg); + ic->away = g_strdup(msg); od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY; } else if (!g_strcasecmp(state, "Free For Chat")) { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT); - ic->away = g_strdup(msg); + ic->away = g_strdup(msg); od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC; } else if (!g_strcasecmp(state, "Invisible")) { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE); - ic->away = g_strdup(msg); + ic->away = g_strdup(msg); } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) { if (no_message) { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL); } else { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY); - ic->away = g_strdup(msg); + ic->away = g_strdup(msg); od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY; } } @@ -2019,7 +2043,7 @@ static void oscar_set_away(struct im_connection *ic, char *state, char *message) { struct oscar_data *od = (struct oscar_data *)ic->proto_data; - oscar_set_away_aim(ic, od, state, message); + oscar_set_away_aim(ic, od, state, message); if (od->icq) oscar_set_away_icq(ic, od, state, message); @@ -2580,7 +2604,7 @@ void oscar_chat_leave(struct groupchat *c) oscar_chat_kill(c->ic, c->data); } -struct groupchat *oscar_chat_join(struct im_connection * ic, char * room, char * nick, char * password ) +struct groupchat *oscar_chat_join(struct im_connection * ic, const char * room, const char * nick, const char * password ) { struct oscar_data * od = (struct oscar_data *)ic->proto_data; aim_conn_t * cur; diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 8feb6639..5aa5033c 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -196,13 +196,14 @@ static int byahoo_send_typing( struct im_connection *ic, char *who, int typing ) static void byahoo_set_away( struct im_connection *ic, char *state, char *msg ) { struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data; + char *away; - ic->away = NULL; + away = NULL; if( state && msg && g_strcasecmp( state, msg ) != 0 ) { yd->current_status = YAHOO_STATUS_CUSTOM; - ic->away = ""; + away = ""; } else if( state ) { @@ -211,11 +212,11 @@ static void byahoo_set_away( struct im_connection *ic, char *state, char *msg ) away state. */ msg = NULL; - ic->away = ""; + away = ""; if( g_strcasecmp( state, "Available" ) == 0 ) { yd->current_status = YAHOO_STATUS_AVAILABLE; - ic->away = NULL; + away = NULL; } else if( g_strcasecmp( state, "Be Right Back" ) == 0 ) yd->current_status = YAHOO_STATUS_BRB; @@ -241,13 +242,13 @@ static void byahoo_set_away( struct im_connection *ic, char *state, char *msg ) { yd->current_status = YAHOO_STATUS_AVAILABLE; - ic->away = NULL; + away = NULL; } } else yd->current_status = YAHOO_STATUS_AVAILABLE; - yahoo_set_away( yd->y2_id, yd->current_status, msg, ic->away != NULL ? 2 : 0 ); + yahoo_set_away( yd->y2_id, yd->current_status, msg, away != NULL ? 2 : 0 ); } static GList *byahoo_away_states( struct im_connection *ic ) @@ -790,9 +791,22 @@ int ext_yahoo_connect(const char *host, int port) static void byahoo_accept_conf( void *data ) { struct byahoo_conf_invitation *inv = data; + struct groupchat *b; + + for( b = inv->ic->groupchats; b; b = b->next ) + if( b == inv->c ) + break; + + if( b != NULL ) + { + yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name ); + imcb_chat_add_buddy( inv->c, inv->ic->acc->user ); + } + else + { + imcb_log( inv->ic, "Duplicate/corrupted invitation to `%s'.", inv->name ); + } - yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name ); - imcb_chat_add_buddy( inv->c, inv->ic->acc->user ); g_free( inv->name ); g_free( inv ); } diff --git a/root_commands.c b/root_commands.c index ffa163bb..37dcf203 100644 --- a/root_commands.c +++ b/root_commands.c @@ -78,6 +78,18 @@ void root_command_string( irc_t *irc, user_t *u, char *command, int flags ) root_command( irc, cmd ); } +#define MIN_ARGS( x, y... ) \ + do \ + { \ + int blaat; \ + for( blaat = 0; blaat <= x; blaat ++ ) \ + if( cmd[blaat] == NULL ) \ + { \ + irc_usermsg( irc, "Not enough parameters given (need %d).", x ); \ + return y; \ + } \ + } while( 0 ) + void root_command( irc_t *irc, char *cmd[] ) { int i; @@ -88,11 +100,8 @@ void root_command( irc_t *irc, char *cmd[] ) for( i = 0; commands[i].command; i++ ) if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 ) { - if( !cmd[commands[i].required_parameters] ) - { - irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters ); - return; - } + MIN_ARGS( commands[i].required_parameters ); + commands[i].execute( irc, cmd ); return; } @@ -250,6 +259,121 @@ static void cmd_showset( irc_t *irc, set_t **head, char *key ) irc_usermsg( irc, "%s is empty", key ); } +typedef set_t** (*cmd_set_findhead)( irc_t*, char* ); +typedef int (*cmd_set_checkflags)( irc_t*, set_t *set ); + +static int cmd_set_real( irc_t *irc, char **cmd, cmd_set_findhead findhead, cmd_set_checkflags checkflags ) +{ + char *set_full = NULL, *set_name = NULL, *tmp; + set_t **head; + + if( cmd[1] && g_strncasecmp( cmd[1], "-del", 4 ) == 0 ) + { + MIN_ARGS( 2, 0 ); + set_full = cmd[2]; + } + else + set_full = cmd[1]; + + if( findhead == NULL ) + { + set_name = set_full; + + head = &irc->set; + } + else + { + char *id; + + if( ( tmp = strchr( set_full, '/' ) ) ) + { + id = g_strndup( set_full, ( tmp - set_full ) ); + set_name = tmp + 1; + } + else + { + id = g_strdup( set_full ); + } + + if( ( head = findhead( irc, id ) ) == NULL ) + { + g_free( id ); + irc_usermsg( irc, "Could not find setting." ); + return 0; + } + g_free( id ); + } + + if( cmd[1] && cmd[2] && set_name ) + { + set_t *s = set_find( head, set_name ); + int st; + + if( checkflags && checkflags( irc, s ) == 0 ) + return 0; + + if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 ) + st = set_reset( head, set_name ); + else + st = set_setstr( head, set_name, cmd[2] ); + + if( set_getstr( head, set_name ) == NULL ) + { + if( st ) + irc_usermsg( irc, "Setting changed successfully" ); + else + irc_usermsg( irc, "Failed to change setting" ); + } + else + { + cmd_showset( irc, head, set_name ); + } + } + else if( set_name ) + { + cmd_showset( irc, head, set_name ); + } + else + { + set_t *s = *head; + while( s ) + { + cmd_showset( irc, &s, s->key ); + s = s->next; + } + } + + return 1; +} + +static set_t **cmd_account_set_findhead( irc_t *irc, char *id ) +{ + account_t *a; + + if( ( a = account_get( irc, id ) ) ) + return &a->set; + else + return NULL; +} + +static int cmd_account_set_checkflags( irc_t *irc, set_t *s ) +{ + account_t *a = s->data; + + if( a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY ) + { + irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "off" ); + return 0; + } + else if( !a->ic && s && s->flags & ACC_SET_ONLINE_ONLY ) + { + irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "on" ); + return 0; + } + + return 1; +} + static void cmd_account( irc_t *irc, char **cmd ) { account_t *a; @@ -264,13 +388,9 @@ static void cmd_account( irc_t *irc, char **cmd ) { struct prpl *prpl; - if( cmd[2] == NULL || cmd[3] == NULL || cmd[4] == NULL ) - { - irc_usermsg( irc, "Not enough parameters" ); - return; - } + MIN_ARGS( 4 ); - prpl = find_protocol(cmd[2]); + prpl = find_protocol( cmd[2] ); if( prpl == NULL ) { @@ -294,11 +414,9 @@ static void cmd_account( irc_t *irc, char **cmd ) } else if( g_strcasecmp( cmd[1], "del" ) == 0 ) { - if( !cmd[2] ) - { - irc_usermsg( irc, "Not enough parameters given (need %d)", 2 ); - } - else if( !( a = account_get( irc, cmd[2] ) ) ) + MIN_ARGS( 2 ); + + if( !( a = account_get( irc, cmd[2] ) ) ) { irc_usermsg( irc, "Invalid account" ); } @@ -426,92 +544,13 @@ static void cmd_account( irc_t *irc, char **cmd ) } else if( g_strcasecmp( cmd[1], "set" ) == 0 ) { - char *acc_handle, *set_name = NULL, *tmp; - - if( !cmd[2] ) - { - irc_usermsg( irc, "Not enough parameters given (need %d)", 2 ); - return; - } - - if( g_strncasecmp( cmd[2], "-del", 4 ) == 0 ) - acc_handle = g_strdup( cmd[3] ); - else - acc_handle = g_strdup( cmd[2] ); - - if( !acc_handle ) - { - irc_usermsg( irc, "Not enough parameters given (need %d)", 3 ); - return; - } - - if( ( tmp = strchr( acc_handle, '/' ) ) ) - { - *tmp = 0; - set_name = tmp + 1; - } - - if( ( a = account_get( irc, acc_handle ) ) == NULL ) - { - g_free( acc_handle ); - irc_usermsg( irc, "Invalid account" ); - return; - } - - if( cmd[3] && set_name ) - { - set_t *s = set_find( &a->set, set_name ); - int st; - - if( a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY ) - { - g_free( acc_handle ); - irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "off" ); - return; - } - else if( !a->ic && s && s->flags & ACC_SET_ONLINE_ONLY ) - { - g_free( acc_handle ); - irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "on" ); - return; - } - - if( g_strncasecmp( cmd[2], "-del", 4 ) == 0 ) - st = set_reset( &a->set, set_name ); - else - st = set_setstr( &a->set, set_name, cmd[3] ); - - if( set_getstr( &a->set, set_name ) == NULL ) - { - if( st ) - irc_usermsg( irc, "Setting changed successfully" ); - else - irc_usermsg( irc, "Failed to change setting" ); - } - else - { - cmd_showset( irc, &a->set, set_name ); - } - } - else if( set_name ) - { - cmd_showset( irc, &a->set, set_name ); - } - else - { - set_t *s = a->set; - while( s ) - { - cmd_showset( irc, &s, s->key ); - s = s->next; - } - } + MIN_ARGS( 2 ); - g_free( acc_handle ); + cmd_set_real( irc, cmd + 1, cmd_account_set_findhead, cmd_account_set_checkflags ); } else { - irc_usermsg( irc, "Unknown command: account %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[1] ); + irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[1] ); } } @@ -522,6 +561,7 @@ static void cmd_add( irc_t *irc, char **cmd ) if( g_strcasecmp( cmd[1], "-tmp" ) == 0 ) { + MIN_ARGS( 3 ); add_on_server = 0; cmd ++; } @@ -841,54 +881,7 @@ static void cmd_yesno( irc_t *irc, char **cmd ) static void cmd_set( irc_t *irc, char **cmd ) { - char *set_name = cmd[1]; - - if( cmd[1] && cmd[2] ) - { - int st; - - if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 ) - { - st = set_reset( &irc->set, cmd[2] ); - set_name = cmd[2]; - } - else - { - st = set_setstr( &irc->set, cmd[1], cmd[2] ); - } - - /* Normally we just show the variable's new/unchanged - value as feedback to the user, but this has always - caused confusion when changing the password. Give - other feedback instead: */ - if( set_getstr( &irc->set, set_name ) == NULL ) - { - if( st ) - irc_usermsg( irc, "Setting changed successfully" ); - else - irc_usermsg( irc, "Failed to change setting" ); - } - else - { - cmd_showset( irc, &irc->set, set_name ); - } - } - else if( set_name ) - { - cmd_showset( irc, &irc->set, set_name ); - - if( strchr( set_name, '/' ) ) - irc_usermsg( irc, "Warning: / found in setting name, you're probably looking for the `account set' command." ); - } - else - { - set_t *s = irc->set; - while( s ) - { - cmd_showset( irc, &s, s->key ); - s = s->next; - } - } + cmd_set_real( irc, cmd, NULL, NULL ); } static void cmd_save( irc_t *irc, char **cmd ) @@ -1011,6 +1004,122 @@ static void cmd_qlist( irc_t *irc, char **cmd ) static void cmd_join_chat( irc_t *irc, char **cmd ) { + irc_usermsg( irc, "This command is now obsolete. " + "Please try the `chat' command instead." ); +} + +static set_t **cmd_chat_set_findhead( irc_t *irc, char *id ) +{ + struct chat *c; + + if( ( c = chat_get( irc, id ) ) ) + return &c->set; + else + return NULL; +} + +static void cmd_chat( irc_t *irc, char **cmd ) +{ + account_t *acc; + struct chat *c; + + if( g_strcasecmp( cmd[1], "add" ) == 0 ) + { + char *channel, *s; + + MIN_ARGS( 3 ); + + if( !( acc = account_get( irc, cmd[2] ) ) ) + { + irc_usermsg( irc, "Invalid account" ); + return; + } + + if( cmd[4] == NULL ) + { + channel = g_strdup( cmd[3] ); + if( ( s = strchr( channel, '@' ) ) ) + *s = 0; + } + else + { + channel = g_strdup( cmd[4] ); + } + + if( strchr( CTYPES, channel[0] ) == NULL ) + { + s = g_strdup_printf( "%c%s", CTYPES[0], channel ); + g_free( channel ); + channel = s; + } + + if( ( c = chat_add( irc, acc, cmd[3], channel ) ) ) + irc_usermsg( irc, "Chatroom added successfully." ); + else + irc_usermsg( irc, "Could not add chatroom." ); + + g_free( channel ); + } + else if( g_strcasecmp( cmd[1], "list" ) == 0 ) + { + int i = 0; + + if( strchr( irc->umode, 'b' ) ) + irc_usermsg( irc, "Chatroom list:" ); + + for( c = irc->chatrooms; c; c = c->next ) + { + irc_usermsg( irc, "%2d. %s(%s) %s, %s", i, c->acc->prpl->name, + c->acc->user, c->handle, c->channel ); + + i ++; + } + irc_usermsg( irc, "End of chatroom list" ); + } + else if( g_strcasecmp( cmd[1], "set" ) == 0 ) + { + cmd_set_real( irc, cmd + 1, cmd_chat_set_findhead, NULL ); + } + else if( g_strcasecmp( cmd[1], "del" ) == 0 ) + { + MIN_ARGS( 2 ); + + if( ( c = chat_get( irc, cmd[2] ) ) ) + { + chat_del( irc, c ); + } + else + { + irc_usermsg( irc, "Could not remove chat." ); + } + } + else if( g_strcasecmp( cmd[1], "with" ) == 0 ) + { + user_t *u; + + MIN_ARGS( 2 ); + + if( ( u = user_find( irc, cmd[2] ) ) && u->ic && u->ic->acc->prpl->chat_with ) + { + if( !u->ic->acc->prpl->chat_with( u->ic, u->handle ) ) + { + irc_usermsg( irc, "(Possible) failure while trying to open " + "a groupchat with %s.", u->nick ); + } + } + else + { + irc_usermsg( irc, "Can't open a groupchat with %s.", cmd[2] ); + } + } + else + { + irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] ); + } + + + +#if 0 account_t *a; struct im_connection *ic; char *chat, *channel, *nick = NULL, *password = NULL; @@ -1036,7 +1145,7 @@ static void cmd_join_chat( irc_t *irc, char **cmd ) chat = cmd[2]; if( cmd[3] ) { - if( cmd[3][0] != '#' && cmd[3][0] != '&' ) + if( strchr( CTYPES, cmd[3][0] ) == NULL ) channel = g_strdup_printf( "&%s", cmd[3] ); else channel = g_strdup( cmd[3] ); @@ -1079,6 +1188,7 @@ static void cmd_join_chat( irc_t *irc, char **cmd ) irc_usermsg( irc, "Tried to join chat, not sure if this was successful" ); g_free( channel ); } +#endif } const command_t commands[] = { @@ -1102,5 +1212,6 @@ const command_t commands[] = { { "qlist", 0, cmd_qlist, 0 }, { "join_chat", 2, cmd_join_chat, 0 }, { "otr", 1, cmd_otr, 0 }, + { "chat", 1, cmd_chat, 0 }, { NULL } }; @@ -266,7 +266,7 @@ char *set_eval_mode_buddies( set_t *set, char *value, char modeflag ) else if(!strcmp(value, "notaway")) mode=3; else - return NULL; + return SET_INVALID; /* sorry for calling them op/deop - too lazy for search+replace :P */ op[0]='\0'; @@ -120,7 +120,6 @@ storage_status_t storage_load (irc_t * irc, const char *password) otr_load(irc); return status; } - if (status != STORAGE_NO_SUCH_USER) return status; } diff --git a/storage_xml.c b/storage_xml.c index 09e2f328..b78c3661 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -37,11 +37,6 @@ #define g_access access #endif -#if !GLIB_CHECK_VERSION(2,8,0) -/* GLib < 2.8.0 doesn't have g_access, so just use the system access(). */ -#define g_access access -#endif - typedef enum { XML_PASS_CHECK_ONLY = -1, @@ -58,6 +53,8 @@ struct xml_parsedata irc_t *irc; char *current_setting; account_t *current_account; + struct chat *current_chat; + set_t **current_set_head; char *given_nick; char *given_pass; xml_pass_st pass_st; @@ -176,7 +173,16 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na } if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) ) + { + if( xd->current_chat != NULL ) + xd->current_set_head = &xd->current_chat->set; + else if( xd->current_account != NULL ) + xd->current_set_head = &xd->current_account->set; + else + xd->current_set_head = &xd->irc->set; + xd->current_setting = g_strdup( setting ); + } else g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Missing attributes for %s element", element_name ); @@ -198,6 +204,23 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na "Missing attributes for %s element", element_name ); } } + else if( g_strcasecmp( element_name, "chat" ) == 0 ) + { + char *handle, *channel; + + handle = xml_attr( attr_names, attr_values, "handle" ); + channel = xml_attr( attr_names, attr_values, "channel" ); + + if( xd->current_account && handle && channel ) + { + xd->current_chat = chat_add( xd->irc, xd->current_account, handle, channel ); + } + else + { + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Missing attributes for %s element", element_name ); + } + } else { g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, @@ -218,13 +241,16 @@ static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name { xd->current_account = NULL; } + else if( g_strcasecmp( element_name, "chat" ) == 0 ) + { + xd->current_chat = NULL; + } } static void xml_text( GMarkupParseContext *ctx, const gchar *text_orig, gsize text_len, gpointer data, GError **error ) { char text[text_len+1]; struct xml_parsedata *xd = data; - irc_t *irc = xd->irc; strncpy( text, text_orig, text_len ); text[text_len] = 0; @@ -237,8 +263,7 @@ static void xml_text( GMarkupParseContext *ctx, const gchar *text_orig, gsize te } else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 && xd->current_setting ) { - set_setstr( xd->current_account ? &xd->current_account->set : &irc->set, - xd->current_setting, (char*) text ); + set_setstr( xd->current_set_head, xd->current_setting, (char*) text ); g_free( xd->current_setting ); xd->current_setting = NULL; } @@ -401,7 +426,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) g_free( pass_buf ); for( set = irc->set; set; set = set->next ) - if( set->value && set->def ) + if( set->value ) if( !xml_printf( fd, 1, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) ) goto write_error; @@ -410,6 +435,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) unsigned char *pass_cr; char *pass_b64; int pass_len; + struct chat *c; pass_len = arc_encode( acc->pass, strlen( acc->pass ), (unsigned char**) &pass_cr, irc->password, 12 ); pass_b64 = base64_encode( pass_cr, pass_len ); @@ -428,7 +454,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) goto write_error; for( set = acc->set; set; set = set->next ) - if( set->value && set->def && !( set->flags & ACC_SET_NOSAVE ) ) + if( set->value && !( set->flags & ACC_SET_NOSAVE ) ) if( !xml_printf( fd, 2, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) ) goto write_error; @@ -442,6 +468,25 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) if( g_hash_table_find( acc->nicks, xml_save_nick, & fd ) ) goto write_error; + for( c = irc->chatrooms; c; c = c->next ) + { + if( c->acc != acc ) + continue; + + if( !xml_printf( fd, 2, "<chat handle=\"%s\" channel=\"%s\" type=\"%s\">\n", + c->handle, c->channel, "room" ) ) + goto write_error; + + for( set = c->set; set; set = set->next ) + if( set->value && !( set->flags & ACC_SET_NOSAVE ) ) + if( !xml_printf( fd, 3, "<setting name=\"%s\">%s</setting>\n", + set->key, set->value ) ) + goto write_error; + + if( !xml_printf( fd, 2, "</chat>\n" ) ) + goto write_error; + } + if( !xml_printf( fd, 1, "</account>\n" ) ) goto write_error; } diff --git a/tests/Makefile b/tests/Makefile index db145503..1bcf8f72 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -10,7 +10,7 @@ clean: distclean: clean -main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o +main_objs = account.o bitlbee.o chat.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o check_jabber_sasl.o check_jabber_util.o diff --git a/tests/check_set.c b/tests/check_set.c index b1ea973d..29e3c8c8 100644 --- a/tests/check_set.c +++ b/tests/check_set.c @@ -95,13 +95,6 @@ START_TEST(test_setstr) fail_unless(strcmp(set_getstr(&s, "name"), "bloe") == 0); END_TEST -START_TEST(test_setstr_implicit) - void *data = "data"; - set_t *s = NULL, *t; - set_setstr(&s, "name", "bloe"); - fail_unless(set_find(&s, "name") != NULL); -END_TEST - START_TEST(test_set_get_int_unknown) set_t *s = NULL; fail_unless(set_getint(&s, "foo") == 0); @@ -125,6 +118,5 @@ Suite *set_suite (void) tcase_add_test (tc_core, test_set_get_int_unknown); tcase_add_test (tc_core, test_setint); tcase_add_test (tc_core, test_setstr); - tcase_add_test (tc_core, test_setstr_implicit); return s; } |