aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--account.c56
-rw-r--r--account.h6
-rw-r--r--bitlbee.h2
-rw-r--r--chat.c192
-rw-r--r--chat.h51
-rw-r--r--conf.c6
-rwxr-xr-xconfigure49
-rwxr-xr-xdebian/bitlbee.init16
-rw-r--r--debian/changelog40
-rw-r--r--debian/patches/bitlbee.conf.diff13
-rwxr-xr-xdebian/postinst21
-rwxr-xr-xdebian/prerm6
-rwxr-xr-xdebian/rules1
-rw-r--r--doc/CHANGES38
-rw-r--r--doc/user-guide/Makefile2
-rw-r--r--doc/user-guide/commands.xml140
-rw-r--r--doc/user-guide/help.xml2
-rw-r--r--doc/user-guide/misc.xml6
-rw-r--r--doc/user-guide/user-guide.xml2
-rw-r--r--irc.c74
-rw-r--r--irc.h8
-rw-r--r--irc_commands.c35
-rw-r--r--lib/events_libevent.c28
-rw-r--r--lib/http_client.c19
-rw-r--r--lib/http_client.h2
-rw-r--r--lib/ini.c129
-rw-r--r--lib/ini.h12
-rw-r--r--lib/misc.c1
-rw-r--r--lib/proxy.c1
-rw-r--r--lib/xmltree.c2
-rw-r--r--lib/xmltree.h2
-rw-r--r--protocols/jabber/conference.c25
-rw-r--r--protocols/jabber/iq.c5
-rw-r--r--protocols/jabber/jabber.c4
-rw-r--r--protocols/jabber/jabber.h5
-rw-r--r--protocols/jabber/jabber_util.c4
-rw-r--r--protocols/jabber/presence.c10
-rw-r--r--protocols/nogaim.c93
-rw-r--r--protocols/nogaim.h21
-rw-r--r--protocols/oscar/aim.h13
-rw-r--r--protocols/oscar/oscar.c70
-rw-r--r--protocols/yahoo/libyahoo2.c441
-rw-r--r--protocols/yahoo/yahoo.c95
-rw-r--r--protocols/yahoo/yahoo2.h3
-rw-r--r--protocols/yahoo/yahoo2_callbacks.h12
-rw-r--r--protocols/yahoo/yahoo2_types.h21
-rw-r--r--root_commands.c417
-rw-r--r--set.c28
-rw-r--r--set.h10
-rw-r--r--storage.c40
-rw-r--r--storage.h8
-rw-r--r--storage_text.c13
-rw-r--r--storage_xml.c86
-rw-r--r--tests/Makefile2
-rw-r--r--tests/check_set.c8
-rw-r--r--user.c2
-rw-r--r--user.h2
58 files changed, 1854 insertions, 552 deletions
diff --git a/Makefile b/Makefile
index 2aae8c83..b8e9a828 100644
--- a/Makefile
+++ b/Makefile
@@ -9,8 +9,8 @@
-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 dcc.o
-headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.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 protocols/ft.h lib/ftutil.h
+objects = account.o bitlbee.o chat.o crypting.o dcc.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 query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.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/ft.h protocols/nogaim.h
subdirs = lib protocols
ifeq ($(TARGET),i586-mingw32msvc)
@@ -104,7 +104,7 @@ tar:
fakeroot debian/rules clean || make distclean
x=$$(basename $$(pwd)); \
cd ..; \
- tar czf $$x.tar.gz --exclude=debian --exclude=.bzr $$x
+ tar czf $$x.tar.gz --exclude=debian --exclude=.bzr* $$x
$(subdirs):
@$(MAKE) -C $@ $(MAKECMDGOALS)
diff --git a/account.c b/account.c
index 07df69e4..912cba6a 100644
--- a/account.c
+++ b/account.c
@@ -26,6 +26,7 @@
#define BITLBEE_CORE
#include "bitlbee.h"
#include "account.h"
+#include "chat.h"
account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass )
{
@@ -54,7 +55,7 @@ account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass )
s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a );
s = set_add( &a->set, "password", NULL, set_eval_account, a );
- s->flags |= ACC_SET_NOSAVE;
+ s->flags |= ACC_SET_NOSAVE | SET_NULL_OK;
s = set_add( &a->set, "username", NULL, set_eval_account, a );
s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
@@ -76,9 +77,23 @@ char *set_eval_account( set_t *set, char *value )
/* Double-check: We refuse to edit on-line accounts. */
if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic )
- return NULL;
+ return SET_INVALID;
- if( strcmp( set->key, "username" ) == 0 )
+ if( strcmp( set->key, "server" ) == 0 )
+ {
+ g_free( acc->server );
+ if( value && *value )
+ {
+ acc->server = g_strdup( value );
+ return value;
+ }
+ else
+ {
+ acc->server = g_strdup( set->def );
+ return g_strdup( set->def );
+ }
+ }
+ else if( strcmp( set->key, "username" ) == 0 )
{
g_free( acc->user );
acc->user = g_strdup( value );
@@ -86,34 +101,29 @@ char *set_eval_account( set_t *set, char *value )
}
else if( strcmp( set->key, "password" ) == 0 )
{
- g_free( acc->pass );
- acc->pass = g_strdup( value );
- return NULL; /* password shouldn't be visible in plaintext! */
- }
- else if( strcmp( set->key, "server" ) == 0 )
- {
- g_free( acc->server );
- if( *value )
+ if( value )
{
- acc->server = g_strdup( value );
- return value;
+ g_free( acc->pass );
+ acc->pass = g_strdup( value );
+ return NULL; /* password shouldn't be visible in plaintext! */
}
else
{
- acc->server = NULL;
- return g_strdup( set->def );
+ /* NULL can (should) be stored in the set_t
+ variable, but is otherwise not correct. */
+ return SET_INVALID;
}
}
else if( strcmp( set->key, "auto_connect" ) == 0 )
{
if( !is_bool( value ) )
- return NULL;
+ return SET_INVALID;
acc->auto_connect = bool2int( value );
return value;
}
- return NULL;
+ return SET_INVALID;
}
account_t *account_get( irc_t *irc, char *id )
@@ -180,6 +190,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. */
@@ -193,6 +204,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 );
@@ -253,7 +271,7 @@ int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *
p->start = p->start * 10 + *value++ - '0';
/* Sure, call me evil for implementing my own fscanf here, but it's
- dead simple and I'm immediately at the next part to parse. */
+ dead simple and I immediately know where to continue parsing. */
if( *value == 0 )
/* If the string ends now, the delay is constant. */
@@ -286,7 +304,7 @@ char *set_eval_account_reconnect_delay( set_t *set, char *value )
{
struct account_reconnect_delay p;
- return account_reconnect_delay_parse( value, &p ) ? value : NULL;
+ return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID;
}
int account_reconnect_delay( account_t *a )
diff --git a/account.h b/account.h
index 2aef80a2..cf22482c 100644
--- a/account.h
+++ b/account.h
@@ -55,8 +55,8 @@ char *set_eval_account( set_t *set, char *value );
char *set_eval_account_reconnect_delay( set_t *set, char *value );
int account_reconnect_delay( account_t *a );
-#define ACC_SET_NOSAVE 1
-#define ACC_SET_OFFLINE_ONLY 2
-#define ACC_SET_ONLINE_ONLY 4
+#define ACC_SET_NOSAVE 0x01
+#define ACC_SET_OFFLINE_ONLY 0x02
+#define ACC_SET_ONLINE_ONLY 0x04
#endif
diff --git a/bitlbee.h b/bitlbee.h
index 7c8b8a36..46a91419 100644
--- a/bitlbee.h
+++ b/bitlbee.h
@@ -32,7 +32,7 @@
#define _WIN32_WINNT 0x0501
#define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "1.2.1"
+#define BITLBEE_VERSION "1.2.4"
#define VERSION BITLBEE_VERSION
#define MAX_STRING 511
diff --git a/chat.c b/chat.c
new file mode 100644
index 00000000..8c5ce0bc
--- /dev/null
+++ b/chat.c
@@ -0,0 +1,192 @@
+ /********************************************************************\
+ * 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"
+#include "chat.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;
+}
diff --git a/chat.h b/chat.h
new file mode 100644
index 00000000..7196aea8
--- /dev/null
+++ b/chat.h
@@ -0,0 +1,51 @@
+ /********************************************************************\
+ * 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
+*/
+
+#ifndef _CHAT_H
+#define _CHAT_H
+
+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 );
+
+#endif
diff --git a/conf.c b/conf.c
index 19fe7020..e41590d3 100644
--- a/conf.c
+++ b/conf.c
@@ -334,15 +334,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/configure b/configure
index b3a98086..9d92cedf 100755
--- a/configure
+++ b/configure
@@ -266,7 +266,7 @@ EOF
detect_ldap()
{
- TMPFILE=`mktemp`
+ TMPFILE=$(mktemp)
if $CC -o $TMPFILE -shared -lldap 2>/dev/null >/dev/null; then
cat<<EOF>>Makefile.settings
EFLAGS+=-lldap
@@ -281,6 +281,43 @@ EOF
fi
}
+RESOLV_TESTCODE='
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+int main()
+{
+ ns_initparse( NULL, 0, NULL );
+ ns_parserr( NULL, ns_s_an, 0, NULL );
+}
+'
+
+detect_resolv_dynamic()
+{
+ echo "$RESOLV_TESTCODE" | $CC -o /dev/null -x c - -lresolv >/dev/null 2>/dev/null
+ if [ "$?" = "0" ]; then
+ echo 'EFLAGS+=-lresolv' >> Makefile.settings
+ return 0
+ fi
+
+ return 1
+}
+
+detect_resolv_static()
+{
+ for i in $systemlibdirs; do
+ if [ -f $i/libresolv.a ]; then
+ echo "$RESOLV_TESTCODE" | $CC -o /dev/null -x c - -Wl,$i/libresolv.a >/dev/null 2>/dev/null
+ if [ "$?" = "0" ]; then
+ echo 'EFLAGS+='$i'/libresolv.a' >> Makefile.settings
+ return 0
+ fi
+ fi
+ done
+
+ return 1
+}
+
if [ "$ssl" = "auto" ]; then
detect_gnutls
if [ "$ret" = "0" ]; then
@@ -348,13 +385,9 @@ fi;
echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings
-for i in $systemlibdirs; do
- if [ -f $i/libresolv.a ]; then
- echo '#define HAVE_RESOLV_A' >> config.h
- echo 'EFLAGS+='$i'/libresolv.a' >> Makefile.settings
- break
- fi
-done
+if detect_resolv_dynamic || detect_resolv_static; then
+ echo '#define HAVE_RESOLV_A' >> config.h
+fi
STORAGES="text xml"
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/CHANGES b/doc/CHANGES
index ac1f1f02..1bfd71d4 100644
--- a/doc/CHANGES
+++ b/doc/CHANGES
@@ -3,6 +3,44 @@ found in the bzr commit logs, for example you can try:
http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on
+Version 1.2.4:
+- Most important change (and main reason for releasing now): Upgraded Yahoo!
+ module to a newer version to get it working again.
+- join_chat command replaced with the much better chat command:
+ * Similar to how account replaced login/slist/logout. Add a chatroom once,
+ then just /join it like any other room. Also automatic joining at login
+ time is now possible.
+ * Note that the old way of starting groupchats (/join #nickname) is now
+ also deprecated, use "chat with" instead.
+ * See "help chat" and "help chat add" for more information.
+- Rewrote bitlbee.conf parser to be less dumb.
+- Fixed compatibility (hopefully) with AIM mobile messages, certain kinds
+ of Google Talk chatrooms.
+- Fixed numerous stability/reliability bugs over the last year.
+
+Finished 17 Oct 2009
+
+Version 1.2.3:
+- Fixed one more flaw similar to the previous hijacking bug, caused by incon-
+ sistent handling of the USTATUS_IDENTIFIED state. All code touching these
+ variables was reviewed and should be correct now.
+
+Finished 7 Sep 2008
+
+Version 1.2.2:
+- Security bugfix: It was possible to hijack accounts (without gaining access
+ to the old account, it's simply an overwrite)
+- Some more stability improvements.
+- Fixed bug where people with non-lowercase nicks couldn't drop their account.
+- Easier upgrades of non-forking daemon mode servers (using the DEAF
+ command).
+- Can be cross-compiled for Win32 now! (No support for SSL yet though, which
+ makes it less useful for now.)
+- Exponential backoff on auto-reconnect.
+- Changing passwords gives less confusing feedback ("password is empty") now.
+
+Finished 26 Aug 2008
+
Version 1.2.1:
- Fixed proxy support.
- Fixed stalling issues while connecting to Jabber when using the OpenSSL
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 f22f6177..de22641b 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 &lt;account id&gt;</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 &lt;account id&gt;</emphasis>.
</para>
<para>
- For more infomation about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>. For details about the syntax of this command, see <emphasis>help set</emphasis>.
+ For more infomation about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
</para>
<para>
@@ -151,6 +151,93 @@
</bitlbee-command>
</bitlbee-command>
+ <bitlbee-command name="chat">
+ <short-description>Chatroom list maintenance</short-description>
+ <syntax>chat &lt;action&gt; [&lt;arguments&gt;]</syntax>
+
+ <description>
+
+ <para>
+ Available actions: add, del, list, with and set. See <emphasis>help chat &lt;action&gt;</emphasis> for more information.
+ </para>
+
+ </description>
+
+ <bitlbee-command name="add">
+ <syntax>chat add &lt;account&gt; &lt;room&gt; [&lt;channel&gt;]</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>
+
+ <para>
+ Password-protected rooms work exactly like on IRC, by passing the password as an extra argument to /join.
+ </para>
+ </description>
+
+ </bitlbee-command>
+
+ <bitlbee-command name="del">
+ <syntax>chat del &lt;chat id&gt;</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 &lt;nickname&gt;</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 &lt;chat id&gt;</syntax>
+ <syntax>chat set &lt;chat id&gt;/&lt;setting&gt;</syntax>
+ <syntax>chat set &lt;chat id&gt;/&lt;setting&gt; &lt;value&gt;</syntax>
+ <syntax>chat set -del &lt;chat id&gt;/&lt;setting&gt;</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 &lt;setting&gt;</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 &lt;connection&gt; &lt;handle&gt; [&lt;nick&gt;]</syntax>
@@ -302,6 +389,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>
@@ -485,6 +582,15 @@
</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="ops" type="string" scope="global">
<default>both</default>
<possible-values>both, root, user, none</possible-values>
@@ -579,7 +685,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>
@@ -587,7 +693,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>
@@ -853,30 +959,6 @@
</ircexample>
</bitlbee-command>
-
- <bitlbee-command name="join_chat">
- <short-description>Join a named groupchat/conference room</short-description>
- <syntax>join_chat &lt;connection&gt; &lt;room name&gt; [&lt;channel name&gt;] [&lt;room nickname&gt;] [&lt;password&gt;]</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>&amp;bitlbee-help</emphasis> because <emphasis>&amp;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 &amp;bitlbee-help help-me</ircline>
- </ircexample>
-
- </bitlbee-command>
<bitlbee-command name="transfers">
<short-description>Monitor, cancel, or reject file transfers</short-description>
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..68b44e95 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">
diff --git a/irc.c b/irc.c
index fe43cebd..e0e1a52f 100644
--- a/irc.c
+++ b/irc.c
@@ -34,13 +34,19 @@ static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond );
GSList *irc_connection_list = NULL;
-static char *passchange( set_t *set, char *value )
+static char *set_eval_password( set_t *set, char *value )
{
irc_t *irc = set->data;
- irc_setpass( irc, value );
- irc_usermsg( irc, "Password successfully changed" );
- return NULL;
+ if( irc->status & USTATUS_IDENTIFIED && value )
+ {
+ irc_setpass( irc, value );
+ return NULL;
+ }
+ else
+ {
+ return SET_INVALID;
+ }
}
static char *set_eval_charset( set_t *set, char *value )
@@ -77,6 +83,7 @@ irc_t *irc_new( int fd )
irc_t *irc;
struct sockaddr_storage sock;
socklen_t socklen = sizeof( sock );
+ set_t *s;
irc = g_new0( irc_t, 1 );
@@ -136,28 +143,29 @@ irc_t *irc_new( int fd )
irc_connection_list = g_slist_append( irc_connection_list, irc );
- set_add( &irc->set, "away_devoice", "true", set_eval_away_devoice, irc );
- set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
- set_add( &irc->set, "auto_reconnect", "false", set_eval_bool, irc );
- set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc );
- set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
- set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
- set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc );
- set_add( &irc->set, "debug", "false", set_eval_bool, irc );
- set_add( &irc->set, "default_target", "root", NULL, irc );
- set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
- set_add( &irc->set, "handle_unknown", "root", NULL, irc );
- set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
- set_add( &irc->set, "ops", "both", set_eval_ops, irc );
- set_add( &irc->set, "password", NULL, passchange, irc );
- set_add( &irc->set, "private", "true", set_eval_bool, irc );
- set_add( &irc->set, "query_order", "lifo", NULL, irc );
- set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
- set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
- set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
- set_add( &irc->set, "strip_html", "true", NULL, irc );
- set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
- set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc );
+ s = set_add( &irc->set, "away_devoice", "true", set_eval_away_devoice, 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 );
+ s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
+ s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
+ s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc );
+ s = set_add( &irc->set, "debug", "false", set_eval_bool, irc );
+ s = set_add( &irc->set, "default_target", "root", NULL, irc );
+ s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
+ s = set_add( &irc->set, "handle_unknown", "root", NULL, irc );
+ s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
+ s = set_add( &irc->set, "ops", "both", set_eval_ops, irc );
+ s = set_add( &irc->set, "password", NULL, set_eval_password, irc );
+ s->flags |= SET_NULL_OK;
+ s = set_add( &irc->set, "private", "true", set_eval_bool, irc );
+ s = set_add( &irc->set, "query_order", "lifo", NULL, irc );
+ s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
+ s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
+ s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
+ s = set_add( &irc->set, "strip_html", "true", NULL, irc );
+ s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
+ s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc );
conf_loaddefaults( irc );
@@ -231,7 +239,7 @@ void irc_free( irc_t * irc )
log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) )
- if( storage_save( irc, TRUE ) != STORAGE_OK )
+ if( storage_save( irc, NULL, TRUE ) != STORAGE_OK )
irc_usermsg( irc, "Error while saving settings!" );
irc_connection_list = g_slist_remove( irc_connection_list, irc );
@@ -399,10 +407,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 );
}
@@ -477,7 +483,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 ++ );
@@ -773,7 +779,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=(ov)@+ 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=(ov)@+ 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 );
@@ -1014,7 +1022,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 ) ) )
{
diff --git a/irc.h b/irc.h
index 0650245a..a865bde3 100644
--- a/irc.h
+++ b/irc.h
@@ -37,6 +37,7 @@
#define CMODES "nt"
#define CMODE "t"
#define UMODE "s"
+#define CTYPES "&#"
typedef enum
{
@@ -47,11 +48,6 @@ typedef enum
USTATUS_SHUTDOWN = 8
} irc_status_t;
-typedef struct channel
-{
- char *name;
-} channel_t;
-
typedef struct irc
{
int fd;
@@ -87,6 +83,7 @@ typedef struct irc
struct query *queries;
struct account *accounts;
GSList *file_transfers;
+ struct chat *chatrooms;
struct __USER *users;
GHashTable *userhash;
@@ -100,7 +97,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 fb2bc7cf..74334ee9 100644
--- a/irc_commands.c
+++ b/irc_commands.c
@@ -26,6 +26,7 @@
#define BITLBEE_CORE
#include "bitlbee.h"
#include "ipc.h"
+#include "chat.h"
static void irc_cmd_pass( irc_t *irc, char **cmd )
{
@@ -124,7 +125,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 +193,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] );
- }
}
}
@@ -432,8 +415,8 @@ static void irc_cmd_watch( irc_t *irc, char **cmd )
if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
{
- g_free( okey );
g_hash_table_remove( irc->watches, okey );
+ g_free( okey );
irc_reply( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
}
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 );
diff --git a/lib/ini.c b/lib/ini.c
index c63a132e..aa291bb2 100644
--- a/lib/ini.c
+++ b/lib/ini.c
@@ -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,119 @@
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 = "";
+
+ close( fd );
+
+ return ini;
}
+
+ if( fd >= 0 )
+ close( fd );
- return( ini );
+ ini_close( ini );
+
+ 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 )
+ {
+ *e = 0;
+ next = e + 1;
+ }
+ else
{
- sscanf( s, "%[^ =]s", key );
- if( ( t = strchr( key, '.' ) ) )
+ /* 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 )
{
- *t = 0;
- strcpy( file->section, key );
- t ++;
+ *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 )
+ {
+ *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 );
}
diff --git a/lib/ini.h b/lib/ini.h
index 5eab472b..6ae0bde5 100644
--- a/lib/ini.h
+++ b/lib/ini.h
@@ -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/misc.c b/lib/misc.c
index 9d504b75..35e9909b 100644
--- a/lib/misc.c
+++ b/lib/misc.c
@@ -45,6 +45,7 @@
#include <resolv.h>
#endif
+#include "md5.h"
#include "ssl_client.h"
void strip_linefeed(gchar *text)
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..f434c58a 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 ) )
@@ -233,8 +233,10 @@ void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bu
if( ( s = xt_find_attr( c, "xmlns" ) ) &&
( strcmp( s, XMLNS_MUC_USER ) == 0 ) )
{
- c = xt_find_node( c->children, "item" );
- if( ( s = xt_find_attr( c, "jid" ) ) )
+ struct xt_node *item;
+
+ item = xt_find_node( c->children, "item" );
+ if( ( s = xt_find_attr( item, "jid" ) ) )
{
/* Yay, found what we need. :-) */
bud->ext_jid = jabber_normalize( s );
@@ -282,12 +284,15 @@ void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bu
}
else if( type ) /* type can only be NULL or "unavailable" in this function */
{
- s = strchr( bud->ext_jid, '/' );
- if( s ) *s = 0;
- imcb_chat_remove_buddy( chat, bud->ext_jid, NULL );
- if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS )
- imcb_remove_buddy( ic, bud->ext_jid, NULL );
- if( s ) *s = '/';
+ if( ( bud->flags & JBFLAG_IS_CHATROOM ) && bud->ext_jid )
+ {
+ s = strchr( bud->ext_jid, '/' );
+ if( s ) *s = 0;
+ imcb_chat_remove_buddy( chat, bud->ext_jid, NULL );
+ if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS )
+ imcb_remove_buddy( ic, bud->ext_jid, NULL );
+ if( s ) *s = '/';
+ }
if( bud == jc->me )
jabber_chat_free( chat );
diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c
index eacc85af..f17a7bb5 100644
--- a/protocols/jabber/iq.c
+++ b/protocols/jabber/iq.c
@@ -50,10 +50,11 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )
else if( strcmp( type, "get" ) == 0 )
{
if( !( ( c = xt_find_node( node->children, "query" ) ) ||
- ( c = xt_find_node( node->children, "ping" ) ) ) || /* O_o WHAT is wrong with just <query/> ????? */
+ ( c = xt_find_node( node->children, "ping" ) ) ) ||
!( s = xt_find_attr( c, "xmlns" ) ) )
{
- imcb_log( ic, "Warning: Received incomplete IQ-%s packet", type );
+ /* Sigh. Who decided to suddenly invent new elements
+ instead of just sticking with <query/>? */
return XT_HANDLED;
}
diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c
index 48f71ff1..15341c9b 100644
--- a/protocols/jabber/jabber.c
+++ b/protocols/jabber/jabber.c
@@ -69,7 +69,7 @@ static void jabber_init( account_t *acc )
s = set_add( &acc->set, "resource_select", "priority", NULL, acc );
s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
- s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
+ s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc );
s->flags |= ACC_SET_OFFLINE_ONLY;
@@ -438,7 +438,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 cc12bdbb..9f101f83 100644
--- a/protocols/jabber/jabber.h
+++ b/protocols/jabber/jabber.h
@@ -26,8 +26,9 @@
#include <glib.h>
-#include "xmltree.h"
#include "bitlbee.h"
+#include "md5.h"
+#include "xmltree.h"
extern GSList *jabber_connections;
@@ -301,7 +302,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/jabber_util.c b/protocols/jabber/jabber_util.c
index 9579a848..c7f88032 100644
--- a/protocols/jabber/jabber_util.c
+++ b/protocols/jabber/jabber_util.c
@@ -36,10 +36,10 @@ char *set_eval_priority( set_t *set, char *value )
{
/* Priority is a signed 8-bit integer, according to RFC 3921. */
if( i < -128 || i > 127 )
- return NULL;
+ return SET_INVALID;
}
else
- return NULL;
+ return SET_INVALID;
/* Only run this stuff if the account is online ATM,
and if the setting seems to be acceptable. */
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 b6f8d6e4..21f7dcb1 100644
--- a/protocols/nogaim.c
+++ b/protocols/nogaim.c
@@ -32,9 +32,11 @@
*/
#define BITLBEE_CORE
-#include "nogaim.h"
#include <ctype.h>
+#include "nogaim.h"
+#include "chat.h"
+
static int remove_chat_buddy_silent( struct groupchat *b, const char *handle );
GSList *connections;
@@ -248,6 +250,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 +274,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 +322,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 )
{
@@ -491,33 +507,70 @@ void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick )
}
}
-/* prpl.c */
-struct show_got_added_data
+struct imcb_ask_cb_data
{
struct im_connection *ic;
char *handle;
};
-void show_got_added_no( void *data )
+static void imcb_ask_auth_cb_no( void *data )
+{
+ struct imcb_ask_cb_data *cbd = data;
+
+ cbd->ic->acc->prpl->auth_deny( cbd->ic, cbd->handle );
+
+ g_free( cbd->handle );
+ g_free( cbd );
+}
+
+static void imcb_ask_auth_cb_yes( void *data )
+{
+ struct imcb_ask_cb_data *cbd = data;
+
+ cbd->ic->acc->prpl->auth_allow( cbd->ic, cbd->handle );
+
+ g_free( cbd->handle );
+ g_free( cbd );
+}
+
+void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname )
{
- g_free( ((struct show_got_added_data*)data)->handle );
+ struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 );
+ char *s, *realname_ = NULL;
+
+ if( realname != NULL )
+ realname_ = g_strdup_printf( " (%s)", realname );
+
+ s = g_strdup_printf( "The user %s%s wants to add you to his/her buddy list.",
+ handle, realname_ ?: "" );
+
+ g_free( realname_ );
+
+ data->ic = ic;
+ data->handle = g_strdup( handle );
+ query_add( ic->irc, ic, s, imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, data );
+}
+
+
+static void imcb_ask_add_cb_no( void *data )
+{
+ g_free( ((struct imcb_ask_cb_data*)data)->handle );
g_free( data );
}
-void show_got_added_yes( void *data )
+static void imcb_ask_add_cb_yes( void *data )
{
- struct show_got_added_data *sga = data;
+ struct imcb_ask_cb_data *cbd = data;
- sga->ic->acc->prpl->add_buddy( sga->ic, sga->handle, NULL );
- /* imcb_add_buddy( sga->ic, NULL, sga->handle, sga->handle ); */
+ cbd->ic->acc->prpl->add_buddy( cbd->ic, cbd->handle, NULL );
- return show_got_added_no( data );
+ return imcb_ask_add_cb_no( data );
}
-void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname )
+void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname )
{
- struct show_got_added_data *data = g_new0( struct show_got_added_data, 1 );
+ struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 );
char *s;
/* TODO: Make a setting for this! */
@@ -528,7 +581,7 @@ void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname
data->ic = ic;
data->handle = g_strdup( handle );
- query_add( ic->irc, ic, s, show_got_added_yes, show_got_added_no, data );
+ query_add( ic->irc, ic, s, imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data );
}
@@ -698,7 +751,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;
@@ -927,14 +980,10 @@ char *set_eval_away_devoice( set_t *set, char *value )
irc_t *irc = set->data;
int st;
- if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
- st = 1;
- else if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
- st = 0;
- else if( sscanf( value, "%d", &st ) != 1 )
- return( NULL );
+ if( !is_bool( value ) )
+ return SET_INVALID;
- st = st != 0;
+ st = bool2int( value );
/* Horror.... */
@@ -978,7 +1027,7 @@ char *set_eval_away_devoice( set_t *set, char *value )
irc->channel, pm, v, list );
}
- return( set_eval_bool( set, value ) );
+ return value;
}
diff --git a/protocols/nogaim.h b/protocols/nogaim.h
index 5b094b3f..43293c4a 100644
--- a/protocols/nogaim.h
+++ b/protocols/nogaim.h
@@ -38,6 +38,8 @@
#ifndef _NOGAIM_H
#define _NOGAIM_H
+#include <stdint.h>
+
#include "bitlbee.h"
#include "account.h"
#include "proxy.h"
@@ -209,7 +211,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
@@ -225,6 +227,10 @@ struct prpl {
* - Most protocols will just want to set this to g_strcasecmp().*/
int (* handle_cmp) (const char *who1, const char *who2);
+ /* Implement these callbacks if you want to use imcb_ask_auth() */
+ void (* auth_allow) (struct im_connection *, const char *who);
+ void (* auth_deny) (struct im_connection *, const char *who);
+
/* Incoming transfer request */
void (* transfer_request) (struct im_connection *, file_transfer_t *ft, char *handle );
};
@@ -242,7 +248,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 );
@@ -255,13 +261,20 @@ G_MODULE_EXPORT void imc_logout( struct im_connection *ic, int allow_reconnect )
G_MODULE_EXPORT void imcb_log( struct im_connection *ic, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
/* To tell the user an error, ie. before logging out when an error occurs. */
G_MODULE_EXPORT void imcb_error( struct im_connection *ic, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
+
/* To ask a your about something.
* - 'msg' is the question.
* - 'data' can be your custom struct - it will be passed to the callbacks.
* - 'doit' or 'dont' will be called depending of the answer of the user.
*/
G_MODULE_EXPORT void imcb_ask( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont );
-G_MODULE_EXPORT void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname );
+
+/* Two common questions you may want to ask:
+ * - X added you to his contact list, allow?
+ * - X is not in your contact list, want to add?
+ */
+G_MODULE_EXPORT void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname );
+G_MODULE_EXPORT void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname );
/* Buddy management */
/* This function should be called for each handle which are visible to the
@@ -293,7 +306,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/aim.h b/protocols/oscar/aim.h
index 9516996c..d1fc602a 100644
--- a/protocols/oscar/aim.h
+++ b/protocols/oscar/aim.h
@@ -143,6 +143,17 @@ struct client_info_s {
"en", \
}
+#define AIM_CLIENTINFO_KNOWNGOOD_5_1_3036 { \
+ "AOL Instant Messenger, version 5.1.3036/WIN32", \
+ 0x0109, \
+ 0x0005, \
+ 0x0001, \
+ 0x0000, \
+ 0x0bdc, \
+ "us", \
+ "en", \
+}
+
/*
* I would make 4.1.2010 the default, but they seem to have found
* an alternate way of breaking that one.
@@ -151,7 +162,7 @@ struct client_info_s {
* memory test, which may require you to have a WinAIM binary laying
* around. (see login.c::memrequest())
*/
-#define AIM_CLIENTINFO_KNOWNGOOD AIM_CLIENTINFO_KNOWNGOOD_3_5_1670
+#define AIM_CLIENTINFO_KNOWNGOOD AIM_CLIENTINFO_KNOWNGOOD_5_1_3036
#ifndef TRUE
#define TRUE 1
diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c
index 36e03166..1118c26d 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/libyahoo2.c b/protocols/yahoo/libyahoo2.c
index a61955c4..a1755cc9 100644
--- a/protocols/yahoo/libyahoo2.c
+++ b/protocols/yahoo/libyahoo2.c
@@ -88,6 +88,7 @@ char *strchr (), *strrchr ();
#endif
#include "base64.h"
+#include "http_client.h"
#ifdef USE_STRUCT_CALLBACKS
struct yahoo_callbacks *yc=NULL;
@@ -168,6 +169,7 @@ enum yahoo_service { /* these are easier to see in hex */
YAHOO_SERVICE_PING,
YAHOO_SERVICE_GOTGROUPRENAME, /* < 1, 36(old), 37(new) */
YAHOO_SERVICE_SYSMESSAGE = 0x14,
+ YAHOO_SERVICE_SKINNAME = 0x15,
YAHOO_SERVICE_PASSTHROUGH2 = 0x16,
YAHOO_SERVICE_CONFINVITE = 0x18,
YAHOO_SERVICE_CONFLOGON,
@@ -191,16 +193,19 @@ enum yahoo_service { /* these are easier to see in hex */
YAHOO_SERVICE_AUTHRESP = 0x54,
YAHOO_SERVICE_LIST,
YAHOO_SERVICE_AUTH = 0x57,
+ YAHOO_SERVICE_AUTHBUDDY = 0x6d,
YAHOO_SERVICE_ADDBUDDY = 0x83,
YAHOO_SERVICE_REMBUDDY,
YAHOO_SERVICE_IGNORECONTACT, /* > 1, 7, 13 < 1, 66, 13, 0*/
YAHOO_SERVICE_REJECTCONTACT,
YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */
+ YAHOO_SERVICE_Y7_PING = 0x8A, /* 0 - id and that's it?? */
YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/
YAHOO_SERVICE_CHATGOTO,
YAHOO_SERVICE_CHATJOIN, /* > 1 104-room 129-1600326591 62-2 */
YAHOO_SERVICE_CHATLEAVE,
YAHOO_SERVICE_CHATEXIT = 0x9b,
+ YAHOO_SERVICE_CHATADDINVITE = 0x9d,
YAHOO_SERVICE_CHATLOGOUT = 0xa0,
YAHOO_SERVICE_CHATPING,
YAHOO_SERVICE_COMMENT = 0xa8,
@@ -208,7 +213,19 @@ enum yahoo_service { /* these are easier to see in hex */
YAHOO_SERVICE_PICTURE_CHECKSUM = 0xbd,
YAHOO_SERVICE_PICTURE = 0xbe,
YAHOO_SERVICE_PICTURE_UPDATE = 0xc1,
- YAHOO_SERVICE_PICTURE_UPLOAD = 0xc2
+ YAHOO_SERVICE_PICTURE_UPLOAD = 0xc2,
+ YAHOO_SERVICE_Y6_VISIBILITY=0xc5,
+ YAHOO_SERVICE_Y6_STATUS_UPDATE=0xc6,
+ YAHOO_PHOTOSHARE_INIT=0xd2,
+ YAHOO_SERVICE_CONTACT_YMSG13=0xd6,
+ YAHOO_PHOTOSHARE_PREV=0xd7,
+ YAHOO_PHOTOSHARE_KEY=0xd8,
+ YAHOO_PHOTOSHARE_TRANS=0xda,
+ YAHOO_FILE_TRANSFER_INIT_YMSG13=0xdc,
+ YAHOO_FILE_TRANSFER_GET_YMSG13=0xdd,
+ YAHOO_FILE_TRANSFER_PUT_YMSG13=0xde,
+ YAHOO_SERVICE_YMSG15_STATUS=0xf0,
+ YAHOO_SERVICE_YMSG15_BUDDY_LIST=0xf1,
};
struct yahoo_pair {
@@ -732,7 +749,7 @@ static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet
data = y_new0(unsigned char, len + 1);
memcpy(data + pos, "YMSG", 4); pos += 4;
- pos += yahoo_put16(data + pos, 0x000c);
+ pos += yahoo_put16(data + pos, YAHOO_PROTO_VER);
pos += yahoo_put16(data + pos, 0x0000);
pos += yahoo_put16(data + pos, pktlen + extra_pad);
pos += yahoo_put16(data + pos, pkt->service);
@@ -746,7 +763,7 @@ static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet
if( yid->type == YAHOO_CONNECTION_FT )
yahoo_send_data(yid->fd, data, len);
else
- yahoo_add_to_send_queue(yid, data, len);
+ yahoo_add_to_send_queue(yid, data, len);
FREE(data);
}
@@ -1466,9 +1483,15 @@ static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_pack
struct user *u = users->data;
if (u->name != NULL) {
- if (pkt->service == YAHOO_SERVICE_LOGOFF || u->flags == 0) {
+ if (pkt->service == YAHOO_SERVICE_LOGOFF) { /* || u->flags == 0) { Not in YMSG16 */
YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);
} else {
+ /* Key 47 always seems to be 1 for YMSG16 */
+ if(!u->state)
+ u->away = 0;
+ else
+ u->away = 1;
+
YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, u->state, u->msg, u->away, u->idle, u->mobile);
}
}
@@ -1479,6 +1502,64 @@ static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_pack
}
}
+static void yahoo_process_buddy_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ YList *l;
+ int last_packet = 0;
+ char *cur_group = NULL;
+ struct yahoo_buddy *newbud = NULL;
+
+ /* we could be getting multiple packets here */
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch(pair->key) {
+ case 300:
+ case 301:
+ case 302:
+ case 303:
+ if ( 315 == atoi(pair->value) )
+ last_packet = 1;
+ break;
+ case 65:
+ g_free(cur_group);
+ cur_group = strdup(pair->value);
+ break;
+ case 7:
+ newbud = y_new0(struct yahoo_buddy, 1);
+ newbud->id = strdup(pair->value);
+ if(cur_group)
+ newbud->group = strdup(cur_group);
+ else {
+ struct yahoo_buddy *lastbud = (struct yahoo_buddy *)y_list_nth(
+ yd->buddies, y_list_length(yd->buddies)-1)->data;
+ newbud->group = strdup(lastbud->group);
+ }
+
+ yd->buddies = y_list_append(yd->buddies, newbud);
+
+ break;
+ }
+ }
+
+ g_free(cur_group);
+
+ /* we could be getting multiple packets here */
+ if (last_packet)
+ return;
+
+ YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
+
+ /*** We login at the very end of the packet communication */
+ if (!yd->logged_in) {
+ yd->logged_in = TRUE;
+ if(yd->current_status < 0)
+ yd->current_status = yd->initial_status;
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
+ }
+}
+
static void yahoo_process_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
{
struct yahoo_data *yd = yid->yd;
@@ -1548,7 +1629,7 @@ static void yahoo_process_list(struct yahoo_input_data *yid, struct yahoo_packet
yd->cookie_c = getcookie(pair->value);
}
- if(yd->cookie_y && yd->cookie_t && yd->cookie_c)
+ if(yd->cookie_y && yd->cookie_t)
YAHOO_CALLBACK(ext_yahoo_got_cookies)(yd->client_id);
break;
@@ -2225,6 +2306,192 @@ static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *se
free(crypt_hash);
}
+struct yahoo_https_auth_data
+{
+ struct yahoo_input_data *yid;
+ char *token;
+ char *chal;
+};
+
+static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had);
+static void yahoo_https_auth_token_finish(struct http_request *req);
+static void yahoo_https_auth_init(struct yahoo_https_auth_data *had);
+static void yahoo_https_auth_finish(struct http_request *req);
+
+/* Extract a value from a login.yahoo.com response. Assume CRLF-linebreaks
+ and FAIL miserably if they're not there... */
+static char *yahoo_ha_find_key(char *response, char *key)
+{
+ char *s, *end;
+ int len = strlen(key);
+
+ s = response;
+ do {
+ if (strncmp(s, key, len) == 0 && s[len] == '=') {
+ s += len + 1;
+ if ((end = strchr(s, '\r')))
+ return g_strndup(s, end - s);
+ else
+ return g_strdup(s);
+ }
+
+ if ((s = strchr(s, '\n')))
+ s ++;
+ } while (s && *s);
+
+ return NULL;
+}
+
+static enum yahoo_status yahoo_https_status_parse(int code)
+{
+ switch (code)
+ {
+ case 1212: return YAHOO_LOGIN_PASSWD;
+ case 1213: return YAHOO_LOGIN_LOCK;
+ case 1235: return YAHOO_LOGIN_UNAME;
+ default: return (enum yahoo_status) code;
+ }
+}
+
+static void yahoo_process_auth_0x10(struct yahoo_input_data *yid, const char *seed, const char *sn)
+{
+ struct yahoo_https_auth_data *had = g_new0(struct yahoo_https_auth_data, 1);
+
+ had->yid = yid;
+ had->chal = g_strdup(seed);
+
+ yahoo_https_auth_token_init(had);
+}
+
+static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had)
+{
+ struct yahoo_input_data *yid = had->yid;
+ struct yahoo_data *yd = yid->yd;
+ struct http_request *req;
+ char *login, *passwd, *chal;
+ char *url;
+
+ login = g_strndup(yd->user, 3 * strlen(yd->user));
+ http_encode(login);
+ passwd = g_strndup(yd->password, 3 * strlen(yd->password));
+ http_encode(passwd);
+ chal = g_strndup(had->chal, 3 * strlen(had->chal));
+ http_encode(chal);
+
+ url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=%d&login=%s&passwd=%s&chal=%s",
+ (int) time(NULL), login, passwd, chal);
+
+ req = http_dorequest_url(url, yahoo_https_auth_token_finish, had);
+
+ g_free(url);
+ g_free(chal);
+ g_free(passwd);
+ g_free(login);
+}
+
+static void yahoo_https_auth_token_finish(struct http_request *req)
+{
+ struct yahoo_https_auth_data *had = req->data;
+ struct yahoo_input_data *yid = had->yid;
+ struct yahoo_data *yd = yid->yd;
+ int st;
+
+ if (req->status_code != 200) {
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 2000 + req->status_code, NULL);
+ goto fail;
+ }
+
+ if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) {
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, yahoo_https_status_parse(st), NULL);
+ goto fail;
+ }
+
+ if ((had->token = yahoo_ha_find_key(req->reply_body, "ymsgr")) == NULL) {
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 3001, NULL);
+ goto fail;
+ }
+
+ return yahoo_https_auth_init(had);
+
+fail:
+ g_free(had->token);
+ g_free(had->chal);
+ g_free(had);
+}
+
+static void yahoo_https_auth_init(struct yahoo_https_auth_data *had)
+{
+ struct http_request *req;
+ char *url;
+
+ url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=%d&token=%s",
+ (int) time(NULL), had->token);
+
+ req = http_dorequest_url(url, yahoo_https_auth_finish, had);
+
+ g_free(url);
+}
+
+static void yahoo_https_auth_finish(struct http_request *req)
+{
+ struct yahoo_https_auth_data *had = req->data;
+ struct yahoo_input_data *yid = had->yid;
+ struct yahoo_data *yd = yid->yd;
+ struct yahoo_packet *pack;
+ char *crumb;
+ int st;
+
+ md5_byte_t result[16];
+ md5_state_t ctx;
+
+ unsigned char yhash[32];
+
+ if (req->status_code != 200) {
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 2000 + req->status_code, NULL);
+ goto fail;
+ }
+
+ if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) {
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, yahoo_https_status_parse(st), NULL);
+ goto fail;
+ }
+
+ if ((yd->cookie_y = yahoo_ha_find_key(req->reply_body, "Y")) == NULL ||
+ (yd->cookie_t = yahoo_ha_find_key(req->reply_body, "T")) == NULL ||
+ (crumb = yahoo_ha_find_key(req->reply_body, "crumb")) == NULL) {
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 3002, NULL);
+ goto fail;
+ }
+
+ md5_init(&ctx);
+ md5_append(&ctx, (unsigned char*) crumb, 11);
+ md5_append(&ctx, (unsigned char*) had->chal, strlen(had->chal));
+ md5_finish(&ctx, result);
+ to_y64(yhash, result, 16);
+
+ pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id);
+ yahoo_packet_hash(pack, 1, yd->user);
+ yahoo_packet_hash(pack, 0, yd->user);
+ yahoo_packet_hash(pack, 277, yd->cookie_y);
+ yahoo_packet_hash(pack, 278, yd->cookie_t);
+ yahoo_packet_hash(pack, 307, (char*) yhash);
+ yahoo_packet_hash(pack, 244, "524223");
+ yahoo_packet_hash(pack, 2, yd->user);
+ yahoo_packet_hash(pack, 2, "1");
+ yahoo_packet_hash(pack, 98, "us");
+ yahoo_packet_hash(pack, 135, "7.5.0.647");
+
+ yahoo_send_packet(yid, pack, 0);
+
+ yahoo_packet_free(pack);
+
+fail:
+ g_free(crumb);
+ g_free(had->token);
+ g_free(had->chal);
+ g_free(had);
+}
+
static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
{
char *seed = NULL;
@@ -2253,6 +2520,9 @@ static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet
case 1:
yahoo_process_auth_0x0b(yid, seed, sn);
break;
+ case 2:
+ yahoo_process_auth_0x10(yid, seed, sn);
+ break;
default:
/* call error */
WARNING(("unknown auth type %d", m));
@@ -2407,7 +2677,7 @@ static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_pa
bud->real_name = NULL;
yd->buddies = y_list_append(yd->buddies, bud);
-
+
/* Possibly called already, but at least the call above doesn't
seem to happen every time (not anytime I tried). */
YAHOO_CALLBACK(ext_yahoo_contact_added)(yd->client_id, me, who, NULL);
@@ -2416,6 +2686,26 @@ static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_pa
/* YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */
}
+static void yahoo_process_contact_ymsg13(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ char* who=NULL;
+ char* me=NULL;
+ char* msg=NULL;
+ YList *l;
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 4)
+ who = pair->value;
+ else if (pair->key == 5)
+ me = pair->value;
+ else
+ DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value));
+ }
+
+ if(pkt->status==3)
+ YAHOO_CALLBACK(ext_yahoo_contact_auth_request)(yid->yd->client_id, me, who, msg);
+}
+
static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
{
struct yahoo_data *yd = yid->yd;
@@ -2627,7 +2917,7 @@ static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_
char *who = NULL;
YList *l;
- yahoo_dump_unhandled(pkt);
+ // yahoo_dump_unhandled(pkt);
for (l = pkt->hash; l; l = l->next) {
struct yahoo_pair *pair = l->data;
if (pair->key == 5)
@@ -2649,6 +2939,7 @@ static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_
static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
{
DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service));
+ yahoo_dump_unhandled(pkt);
switch (pkt->service)
{
case YAHOO_SERVICE_USERSTAT:
@@ -2660,6 +2951,8 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack
case YAHOO_SERVICE_GAMELOGOFF:
case YAHOO_SERVICE_IDACT:
case YAHOO_SERVICE_IDDEACT:
+ case YAHOO_SERVICE_Y6_STATUS_UPDATE:
+ case YAHOO_SERVICE_YMSG15_STATUS:
yahoo_process_status(yid, pkt);
break;
case YAHOO_SERVICE_NOTIFY:
@@ -2673,6 +2966,7 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack
case YAHOO_SERVICE_NEWMAIL:
yahoo_process_mail(yid, pkt);
break;
+ case YAHOO_SERVICE_REJECTCONTACT:
case YAHOO_SERVICE_NEWCONTACT:
yahoo_process_contact(yid, pkt);
break;
@@ -2713,6 +3007,9 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack
case YAHOO_SERVICE_ADDBUDDY:
yahoo_process_buddyadd(yid, pkt);
break;
+ case YAHOO_SERVICE_CONTACT_YMSG13:
+ yahoo_process_contact_ymsg13(yid,pkt);
+ break;
case YAHOO_SERVICE_REMBUDDY:
yahoo_process_buddydel(yid, pkt);
break;
@@ -2741,7 +3038,6 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack
case YAHOO_SERVICE_CHATLOGON:
case YAHOO_SERVICE_CHATLOGOFF:
case YAHOO_SERVICE_CHATMSG:
- case YAHOO_SERVICE_REJECTCONTACT:
case YAHOO_SERVICE_PEERTOPEER:
WARNING(("unhandled service 0x%02x", pkt->service));
yahoo_dump_unhandled(pkt);
@@ -2755,6 +3051,8 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack
case YAHOO_SERVICE_PICTURE_UPLOAD:
yahoo_process_picture_upload(yid, pkt);
break;
+ case YAHOO_SERVICE_YMSG15_BUDDY_LIST: /* Buddy List */
+ yahoo_process_buddy_list(yid, pkt);
default:
WARNING(("unknown service 0x%02x", pkt->service));
yahoo_dump_unhandled(pkt);
@@ -3538,7 +3836,7 @@ static void (*yahoo_process_connection[])(struct yahoo_input_data *, int over) =
yahoo_process_webcam_master_connection,
yahoo_process_webcam_connection,
yahoo_process_chatcat_connection,
- yahoo_process_search_connection
+ yahoo_process_search_connection,
};
int yahoo_read_ready(int id, int fd, void *data)
@@ -3556,7 +3854,7 @@ int yahoo_read_ready(int id, int fd, void *data)
len = read(fd, buf, sizeof(buf));
} while(len == -1 && errno == EINTR);
- if(len == -1 && errno == EAGAIN) /* we'll try again later */
+ if(len == -1 && (errno == EAGAIN||errno == EINTR)) /* we'll try again later */
return 1;
if (len <= 0) {
@@ -3759,7 +4057,7 @@ void yahoo_send_typing(int id, const char *from, const char *who, int typ)
pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yd->session_id);
yahoo_packet_hash(pkt, 5, who);
- yahoo_packet_hash(pkt, 4, from?from:yd->user);
+ yahoo_packet_hash(pkt, 1, from?from:yd->user);
yahoo_packet_hash(pkt, 14, " ");
yahoo_packet_hash(pkt, 13, typ ? "1" : "0");
yahoo_packet_hash(pkt, 49, "TYPING");
@@ -3774,7 +4072,7 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)
struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
struct yahoo_data *yd;
struct yahoo_packet *pkt = NULL;
- int service;
+ int old_status;
char s[4];
if(!yid)
@@ -3782,38 +4080,45 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)
yd = yid->yd;
- if (msg) {
+ old_status = yd->current_status;
+
+ if (msg && strncmp(msg,"Invisible",9)) {
yd->current_status = YAHOO_STATUS_CUSTOM;
} else {
yd->current_status = state;
}
- if (yd->current_status == YAHOO_STATUS_AVAILABLE)
- service = YAHOO_SERVICE_ISBACK;
- else
- service = YAHOO_SERVICE_ISAWAY;
+ /* Thank you libpurple :) */
+ if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
+ pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBILITY, YAHOO_STATUS_AVAILABLE, 0);
+ yahoo_packet_hash(pkt, 13, "2");
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+
+ return;
+ }
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, yd->current_status, yd->session_id);
+ snprintf(s, sizeof(s), "%d", yd->current_status);
+ yahoo_packet_hash(pkt, 10, s);
- if ((away == 2) && (yd->current_status == YAHOO_STATUS_AVAILABLE)) {
- pkt = yahoo_packet_new(YAHOO_SERVICE_ISAWAY, YAHOO_STATUS_BRB, yd->session_id);
- yahoo_packet_hash(pkt, 10, "999");
- yahoo_packet_hash(pkt, 47, "2");
- }else {
- pkt = yahoo_packet_new(service, YAHOO_STATUS_AVAILABLE, yd->session_id);
- snprintf(s, sizeof(s), "%d", yd->current_status);
- yahoo_packet_hash(pkt, 10, s);
- if (yd->current_status == YAHOO_STATUS_CUSTOM) {
- yahoo_packet_hash(pkt, 19, msg);
- yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0");
- } else {
- yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0");
- }
-
-
-
+ if (yd->current_status == YAHOO_STATUS_CUSTOM) {
+ yahoo_packet_hash(pkt, 19, msg);
+ } else {
+ yahoo_packet_hash(pkt, 19, "");
}
+
+ yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0");
yahoo_send_packet(yid, pkt, 0);
yahoo_packet_free(pkt);
+
+ if(old_status == YAHOO_STATUS_INVISIBLE) {
+ pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBILITY, YAHOO_STATUS_AVAILABLE, 0);
+ yahoo_packet_hash(pkt, 13, "1");
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+ }
}
void yahoo_logoff(int id)
@@ -3828,7 +4133,10 @@ void yahoo_logoff(int id)
LOG(("yahoo_logoff: current status: %d", yd->current_status));
- if(yd->current_status != -1) {
+ if(yd->current_status != -1 && 0) {
+ /* Meh. Don't send this. The event handlers are not going to
+ get to do this so it'll just leak memory. And the TCP
+ connection reset will hopefully be clear enough. */
pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id);
yd->current_status = -1;
@@ -4061,12 +4369,24 @@ void yahoo_add_buddy(int id, const char *who, const char *group, const char *msg
if (!yd->logged_in)
return;
- pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
- yahoo_packet_hash(pkt, 1, yd->user);
- yahoo_packet_hash(pkt, 7, who);
- yahoo_packet_hash(pkt, 65, group);
+ pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YPACKET_STATUS_DEFAULT, yd->session_id);
+
if (msg != NULL) /* add message/request "it's me add me" */
yahoo_packet_hash(pkt, 14, msg);
+ else
+ yahoo_packet_hash(pkt,14,"");
+
+ yahoo_packet_hash(pkt, 65, group);
+ yahoo_packet_hash(pkt, 97, "1");
+ yahoo_packet_hash(pkt, 1, yd->user);
+ yahoo_packet_hash(pkt, 302, "319");
+ yahoo_packet_hash(pkt, 300, "319");
+ yahoo_packet_hash(pkt, 7, who);
+ yahoo_packet_hash(pkt, 334, "0");
+ yahoo_packet_hash(pkt, 301, "319");
+ yahoo_packet_hash(pkt, 303, "319");
+
+
yahoo_send_packet(yid, pkt, 0);
yahoo_packet_free(pkt);
}
@@ -4090,6 +4410,49 @@ void yahoo_remove_buddy(int id, const char *who, const char *group)
yahoo_packet_free(pkt);
}
+void yahoo_accept_buddy_ymsg13(int id,const char* me,const char* who){
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ struct yahoo_packet* pkt=NULL;
+ pkt= yahoo_packet_new(YAHOO_SERVICE_CONTACT_YMSG13,YAHOO_STATUS_AVAILABLE,0);
+
+ yahoo_packet_hash(pkt,1,me ?: yd->user);
+ yahoo_packet_hash(pkt,5,who);
+ yahoo_packet_hash(pkt,13,"1");
+ yahoo_packet_hash(pkt,334,"0");
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_reject_buddy_ymsg13(int id,const char* me,const char* who,const char* msg){
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ struct yahoo_packet* pkt=NULL;
+ pkt= yahoo_packet_new(YAHOO_SERVICE_CONTACT_YMSG13,YAHOO_STATUS_AVAILABLE,0);
+
+ yahoo_packet_hash(pkt,1,me ?: yd->user);
+ yahoo_packet_hash(pkt,5,who);
+// yahoo_packet_hash(pkt,241,YAHOO_PROTO_VER);
+ yahoo_packet_hash(pkt,13,"2");
+ yahoo_packet_hash(pkt,334,"0");
+ yahoo_packet_hash(pkt,97,"1");
+ yahoo_packet_hash(pkt,14,msg?:"");
+
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+
+}
+
void yahoo_reject_buddy(int id, const char *who, const char *msg)
{
struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c
index 8d9e95d8..ac57d4b6 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,31 +242,34 @@ 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 )
{
- GList *m = NULL;
-
- m = g_list_append( m, "Available" );
- m = g_list_append( m, "Be Right Back" );
- m = g_list_append( m, "Busy" );
- m = g_list_append( m, "Not At Home" );
- m = g_list_append( m, "Not At Desk" );
- m = g_list_append( m, "Not In Office" );
- m = g_list_append( m, "On Phone" );
- m = g_list_append( m, "On Vacation" );
- m = g_list_append( m, "Out To Lunch" );
- m = g_list_append( m, "Stepped Out" );
- m = g_list_append( m, "Invisible" );
- m = g_list_append( m, GAIM_AWAY_CUSTOM );
+ static GList *m = NULL;
+
+ if( m == NULL )
+ {
+ m = g_list_append( m, "Available" );
+ m = g_list_append( m, "Be Right Back" );
+ m = g_list_append( m, "Busy" );
+ m = g_list_append( m, "Not At Home" );
+ m = g_list_append( m, "Not At Desk" );
+ m = g_list_append( m, "Not In Office" );
+ m = g_list_append( m, "On Phone" );
+ m = g_list_append( m, "On Vacation" );
+ m = g_list_append( m, "Out To Lunch" );
+ m = g_list_append( m, "Stepped Out" );
+ m = g_list_append( m, "Invisible" );
+ m = g_list_append( m, GAIM_AWAY_CUSTOM );
+ }
return m;
}
@@ -346,6 +350,20 @@ static struct groupchat *byahoo_chat_with( struct im_connection *ic, char *who )
return c;
}
+static void byahoo_auth_allow( struct im_connection *ic, const char *who )
+{
+ struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data;
+
+ yahoo_accept_buddy_ymsg13( yd->y2_id, NULL, who );
+}
+
+static void byahoo_auth_deny( struct im_connection *ic, const char *who )
+{
+ struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data;
+
+ yahoo_reject_buddy_ymsg13( yd->y2_id, NULL, who, NULL );
+}
+
void byahoo_initmodule( )
{
struct prpl *ret = g_new0(struct prpl, 1);
@@ -371,6 +389,9 @@ void byahoo_initmodule( )
ret->handle_cmp = g_strcasecmp;
+ ret->auth_allow = byahoo_auth_allow;
+ ret->auth_deny = byahoo_auth_deny;
+
register_protocol(ret);
}
@@ -450,9 +471,7 @@ gboolean byahoo_write_ready_callback( gpointer data, gint source, b_input_condit
{
struct byahoo_write_ready_data *d = data;
- yahoo_write_ready( d->id, d->fd, d->data );
-
- return FALSE;
+ return yahoo_write_ready( d->id, d->fd, d->data );
}
void ext_yahoo_login_response( int id, int succ, const char *url )
@@ -789,9 +808,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 );
}
@@ -907,11 +939,18 @@ void ext_yahoo_chat_yahooerror( int id, const char *me )
{
}
+void ext_yahoo_contact_auth_request( int id, const char *myid, const char *who, const char *msg )
+{
+ struct im_connection *ic = byahoo_get_ic_by_id( id );
+
+ imcb_ask_auth( ic, who, NULL );
+}
+
void ext_yahoo_contact_added( int id, const char *myid, const char *who, const char *msg )
{
- /* Groups schmoups. If I want to handle groups properly I can get the
- buddy data from some internal libyahoo2 structure. */
- imcb_add_buddy( byahoo_get_ic_by_id( id ), (char*) who, NULL );
+ struct im_connection *ic = byahoo_get_ic_by_id( id );
+
+ imcb_add_buddy( ic, (char*) who, NULL );
}
void ext_yahoo_rejected( int id, const char *who, const char *msg )
diff --git a/protocols/yahoo/yahoo2.h b/protocols/yahoo/yahoo2.h
index e54e09fb..2184a321 100644
--- a/protocols/yahoo/yahoo2.h
+++ b/protocols/yahoo/yahoo2.h
@@ -216,6 +216,9 @@ const char * yahoo_get_profile_url( void );
void yahoo_buddyicon_request(int id, const char *who);
+void yahoo_accept_buddy_ymsg13(int,const char*,const char*);
+void yahoo_reject_buddy_ymsg13(int,const char*,const char*,const char*);
+
#include "yahoo_httplib.h"
#ifdef __cplusplus
diff --git a/protocols/yahoo/yahoo2_callbacks.h b/protocols/yahoo/yahoo2_callbacks.h
index b7f4e99b..e2c8ea42 100644
--- a/protocols/yahoo/yahoo2_callbacks.h
+++ b/protocols/yahoo/yahoo2_callbacks.h
@@ -360,6 +360,18 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_file)(int id, const char *me, const char
/*
+ * Name: ext_yahoo_contact_auth_request
+ * Called when a contact wants to add you to his/her contact list
+ * Params:
+ * id - the id that identifies the server connection
+ * myid - the identity s/he added
+ * who - who did it
+ * msg - any message sent
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_auth_request)(int id, const char *myid, const char *who, const char *msg);
+
+
+/*
* Name: ext_yahoo_contact_added
* Called when a contact is added to your list
* Params:
diff --git a/protocols/yahoo/yahoo2_types.h b/protocols/yahoo/yahoo2_types.h
index df1756eb..3507e13a 100644
--- a/protocols/yahoo/yahoo2_types.h
+++ b/protocols/yahoo/yahoo2_types.h
@@ -56,7 +56,20 @@ enum yahoo_login_status {
YAHOO_LOGIN_PASSWD = 13,
YAHOO_LOGIN_LOCK = 14,
YAHOO_LOGIN_DUPL = 99,
- YAHOO_LOGIN_SOCK = -1
+ YAHOO_LOGIN_SOCK = -1,
+};
+
+enum ypacket_status {
+ YPACKET_STATUS_DISCONNECTED = -1,
+ YPACKET_STATUS_DEFAULT = 0,
+ YPACKET_STATUS_SERVERACK = 1,
+ YPACKET_STATUS_GAME = 0x2,
+ YPACKET_STATUS_AWAY = 0x4,
+ YPACKET_STATUS_CONTINUED = 0x5,
+ YPACKET_STATUS_INVISIBLE = 12,
+ YPACKET_STATUS_NOTIFY = 0x16, /* TYPING */
+ YPACKET_STATUS_WEBLOGIN = 0x5a55aa55,
+ YPACKET_STATUS_OFFLINE = 0x5a55aa56
};
enum yahoo_error {
@@ -84,7 +97,7 @@ enum yahoo_log_level {
YAHOO_LOG_DEBUG
};
-#define YAHOO_PROTO_VER 0x000b
+#define YAHOO_PROTO_VER 0x0010
/* Yahoo style/color directives */
#define YAHOO_COLOR_BLACK "\033[30m"
@@ -114,7 +127,8 @@ enum yahoo_connection_type {
YAHOO_CONNECTION_WEBCAM_MASTER,
YAHOO_CONNECTION_WEBCAM,
YAHOO_CONNECTION_CHATCAT,
- YAHOO_CONNECTION_SEARCH
+ YAHOO_CONNECTION_SEARCH,
+ YAHOO_CONNECTION_AUTH,
};
enum yahoo_webcam_direction_type {
@@ -131,7 +145,6 @@ enum yahoo_stealth_visibility_type {
/* chat member attribs */
#define YAHOO_CHAT_MALE 0x8000
#define YAHOO_CHAT_FEMALE 0x10000
-#define YAHOO_CHAT_FEMALE 0x10000
#define YAHOO_CHAT_DUNNO 0x400
#define YAHOO_CHAT_WEBCAM 0x10
diff --git a/root_commands.c b/root_commands.c
index 93bd86d1..3952371b 100644
--- a/root_commands.c
+++ b/root_commands.c
@@ -28,6 +28,7 @@
#include "crypting.h"
#include "bitlbee.h"
#include "help.h"
+#include "chat.h"
#include <string.h>
@@ -77,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;
@@ -87,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;
}
@@ -130,7 +140,7 @@ static void cmd_account( irc_t *irc, char **cmd );
static void cmd_identify( irc_t *irc, char **cmd )
{
- storage_status_t status = storage_load( irc->nick, cmd[1], irc );
+ storage_status_t status = storage_load( irc, cmd[1] );
char *account_on[] = { "account", "on", NULL };
switch (status) {
@@ -142,6 +152,8 @@ static void cmd_identify( irc_t *irc, char **cmd )
break;
case STORAGE_OK:
irc_usermsg( irc, "Password accepted, settings and accounts loaded" );
+ irc_setpass( irc, cmd[1] );
+ irc->status |= USTATUS_IDENTIFIED;
irc_umode_set( irc, "+R", 1 );
if( set_getbool( &irc->set, "auto_connect" ) )
cmd_account( irc, account_on );
@@ -161,14 +173,14 @@ static void cmd_register( irc_t *irc, char **cmd )
return;
}
- irc_setpass( irc, cmd[1] );
- switch( storage_save( irc, FALSE )) {
+ switch( storage_save( irc, cmd[1], FALSE ) ) {
case STORAGE_ALREADY_EXISTS:
irc_usermsg( irc, "Nick is already registered" );
break;
case STORAGE_OK:
irc_usermsg( irc, "Account successfully created" );
+ irc_setpass( irc, cmd[1] );
irc->status |= USTATUS_IDENTIFIED;
irc_umode_set( irc, "+R", 1 );
break;
@@ -237,6 +249,131 @@ void cmd_account_del_no( void *data )
g_free( data );
}
+static void cmd_showset( irc_t *irc, set_t **head, char *key )
+{
+ char *val;
+
+ if( ( val = set_getstr( head, key ) ) )
+ irc_usermsg( irc, "%s = `%s'", key, val );
+ else
+ 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( s && 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;
@@ -251,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 )
{
@@ -277,11 +410,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" );
}
@@ -409,86 +540,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;
- }
+ MIN_ARGS( 2 );
- 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 );
-
- 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 )
- set_reset( &a->set, set_name );
- else
- set_setstr( &a->set, set_name, cmd[3] );
- }
- if( set_name ) /* else 'forgotten' on purpose.. Must show new value after changing */
- {
- char *s = set_getstr( &a->set, set_name );
- if( s )
- irc_usermsg( irc, "%s = `%s'", set_name, s );
- else
- irc_usermsg( irc, "%s is empty", set_name );
- }
- else
- {
- set_t *s = a->set;
- while( s )
- {
- if( s->value || s->def )
- irc_usermsg( irc, "%s = `%s'", s->key, s->value ? s->value : s->def );
- else
- irc_usermsg( irc, "%s is empty", s->key );
- s = s->next;
- }
- }
-
- 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] );
}
}
@@ -499,6 +557,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 ++;
}
@@ -609,6 +668,8 @@ static void cmd_rename( irc_t *irc, char **cmd )
g_free( irc->mynick );
irc->mynick = g_strdup( cmd[2] );
+ /* If we're called internally (user did "set root_nick"),
+ let's not go O(INF). :-) */
if( strcmp( cmd[0], "set_rename" ) != 0 )
set_setstr( &irc->set, "root_nick", cmd[2] );
}
@@ -632,7 +693,7 @@ char *set_eval_root_nick( set_t *set, char *new_nick )
cmd_rename( irc, cmd );
}
- return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : NULL;
+ return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : SET_INVALID;
}
static void cmd_remove( irc_t *irc, char **cmd )
@@ -816,48 +877,14 @@ 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] )
- {
- if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
- {
- set_reset( &irc->set, cmd[2] );
- set_name = cmd[2];
- }
- else
- {
- set_setstr( &irc->set, cmd[1], cmd[2] );
- }
- }
- if( set_name ) /* else 'forgotten' on purpose.. Must show new value after changing */
- {
- char *s = set_getstr( &irc->set, set_name );
- if( s )
- irc_usermsg( irc, "%s = `%s'", set_name, s );
- else
- irc_usermsg( irc, "%s is empty", 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 )
- {
- if( s->value || s->def )
- irc_usermsg( irc, "%s = `%s'", s->key, s->value ? s->value : s->def );
- else
- irc_usermsg( irc, "%s is empty", s->key );
- s = s->next;
- }
- }
+ cmd_set_real( irc, cmd, NULL, NULL );
}
static void cmd_save( irc_t *irc, char **cmd )
{
- if( storage_save( irc, TRUE ) == STORAGE_OK )
+ if( ( irc->status & USTATUS_IDENTIFIED ) == 0 )
+ irc_usermsg( irc, "Please create an account first" );
+ else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK )
irc_usermsg( irc, "Configuration saved" );
else
irc_usermsg( irc, "Configuration could not be saved!" );
@@ -973,6 +1000,124 @@ 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 )
+ {
+ MIN_ARGS( 2 );
+
+ 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;
@@ -998,7 +1143,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] );
@@ -1041,6 +1186,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
}
static void cmd_transfers( irc_t *irc, char **cmd )
@@ -1128,6 +1274,7 @@ const command_t commands[] = {
{ "nick", 1, cmd_nick, 0 },
{ "qlist", 0, cmd_qlist, 0 },
{ "join_chat", 2, cmd_join_chat, 0 },
+ { "chat", 1, cmd_chat, 0 },
{ "transfers", 0, cmd_transfers, 0 },
{ NULL }
};
diff --git a/set.c b/set.c
index 112e6937..18d5a50d 100644
--- a/set.c
+++ b/set.c
@@ -25,6 +25,9 @@
#define BITLBEE_CORE
#include "bitlbee.h"
+/* Used to use NULL for this, but NULL is actually a "valid" value. */
+char *SET_INVALID = "nee";
+
set_t *set_add( set_t **head, char *key, char *def, set_eval eval, void *data )
{
set_t *s = set_find( head, key );
@@ -113,9 +116,20 @@ int set_setstr( set_t **head, char *key, char *value )
char *nv = value;
if( !s )
+ /*
+ Used to do this, but it never really made sense.
s = set_add( head, key, NULL, NULL, NULL );
+ */
+ return 0;
+
+ if( value == NULL && ( s->flags & SET_NULL_OK ) == 0 )
+ return 0;
- if( s->eval && !( nv = s->eval( s, value ) ) )
+ /* Call the evaluator. For invalid values, evaluators should now
+ return SET_INVALID, but previously this was NULL. Try to handle
+ that too if NULL is not an allowed value for this setting. */
+ if( s->eval && ( ( nv = s->eval( s, value ) ) == SET_INVALID ||
+ ( ( s->flags & SET_NULL_OK ) == 0 && nv == NULL ) ) )
return 0;
if( s->value )
@@ -167,13 +181,15 @@ void set_del( set_t **head, char *key )
}
}
-void set_reset( set_t **head, char *key )
+int set_reset( set_t **head, char *key )
{
set_t *s;
s = set_find( head, key );
if( s )
- set_setstr( head, key, s->def );
+ return set_setstr( head, key, s->def );
+
+ return 0;
}
char *set_eval_int( set_t *set, char *value )
@@ -186,14 +202,14 @@ char *set_eval_int( set_t *set, char *value )
for( ; *s; s ++ )
if( !isdigit( *s ) )
- return NULL;
+ return SET_INVALID;
return value;
}
char *set_eval_bool( set_t *set, char *value )
{
- return is_bool( value ) ? value : NULL;
+ return is_bool( value ) ? value : SET_INVALID;
}
char *set_eval_to_char( set_t *set, char *value )
@@ -225,7 +241,7 @@ char *set_eval_ops( set_t *set, char *value )
irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost,
irc->channel, "-oo", irc->nick, irc->mynick );
else
- return NULL;
+ return SET_INVALID;
return value;
}
diff --git a/set.h b/set.h
index 8c722877..19ea73fb 100644
--- a/set.h
+++ b/set.h
@@ -43,6 +43,10 @@ struct set;
typedef char *(*set_eval) ( struct set *set, char *value );
+extern char *SET_INVALID;
+
+#define SET_NULL_OK 0x0100
+
typedef struct set
{
void *data; /* Here you can save a pointer to the
@@ -60,8 +64,8 @@ typedef struct set
int flags; /* See account.h, for example. set.c doesn't use
this (yet?). */
- /* Eval: Returns NULL if the value is incorrect or exactly the
- passed value variable. When returning a corrected value,
+ /* Eval: Returns SET_INVALID if the value is incorrect or exactly
+ the passed value variable. When returning a corrected value,
set_setstr() should be able to free() the returned string! */
set_eval eval;
struct set *next;
@@ -87,7 +91,7 @@ G_MODULE_EXPORT int set_getbool( set_t **head, char *key );
int set_setstr( set_t **head, char *key, char *value );
int set_setint( set_t **head, char *key, int value );
void set_del( set_t **head, char *key );
-void set_reset( set_t **head, char *key );
+int set_reset( set_t **head, char *key );
/* Two very useful generic evaluators. */
char *set_eval_int( set_t *set, char *value );
diff --git a/storage.c b/storage.c
index 06044f80..f011ade2 100644
--- a/storage.c
+++ b/storage.c
@@ -102,20 +102,21 @@ storage_status_t storage_check_pass (const char *nick, const char *password)
return STORAGE_NO_SUCH_USER;
}
-storage_status_t storage_load (const char *nick, const char *password, irc_t * irc)
+storage_status_t storage_load (irc_t * irc, const char *password)
{
GList *gl;
+ if (irc && irc->status & USTATUS_IDENTIFIED)
+ return STORAGE_OTHER_ERROR;
+
/* Loop until we don't get NO_SUCH_USER */
for (gl = global.storage; gl; gl = gl->next) {
storage_t *st = gl->data;
storage_status_t status;
- status = st->load(nick, password, irc);
- if (status == STORAGE_OK) {
- irc_setpass(irc, password);
+ status = st->load(irc, password);
+ if (status == STORAGE_OK)
return status;
- }
if (status != STORAGE_NO_SUCH_USER)
return status;
@@ -124,9 +125,27 @@ storage_status_t storage_load (const char *nick, const char *password, irc_t * i
return STORAGE_NO_SUCH_USER;
}
-storage_status_t storage_save (irc_t *irc, int overwrite)
+storage_status_t storage_save (irc_t *irc, char *password, int overwrite)
{
- return ((storage_t *)global.storage->data)->save(irc, overwrite);
+ storage_status_t st;
+
+ if (password != NULL) {
+ /* Should only use this in the "register" command. */
+ if (irc->password || overwrite)
+ return STORAGE_OTHER_ERROR;
+
+ irc_setpass(irc, password);
+ } else if ((irc->status & USTATUS_IDENTIFIED) == 0) {
+ return STORAGE_NO_SUCH_USER;
+ }
+
+ st = ((storage_t *)global.storage->data)->save(irc, overwrite);
+
+ if (password != NULL) {
+ irc_setpass(irc, NULL);
+ }
+
+ return st;
}
storage_status_t storage_remove (const char *nick, const char *password)
@@ -142,14 +161,16 @@ storage_status_t storage_remove (const char *nick, const char *password)
storage_status_t status;
status = st->remove(nick, password);
- if (status != STORAGE_NO_SUCH_USER &&
- status != STORAGE_OK)
+ if (status != STORAGE_NO_SUCH_USER && status != STORAGE_OK)
ret = status;
}
return ret;
}
+#if 0
+Not using this yet. Test thoroughly before adding UI hooks to this function.
+
storage_status_t storage_rename (const char *onick, const char *nnick, const char *password)
{
storage_status_t status;
@@ -188,3 +209,4 @@ storage_status_t storage_rename (const char *onick, const char *nnick, const cha
return STORAGE_OK;
}
+#endif
diff --git a/storage.h b/storage.h
index d114dec4..f2e9afce 100644
--- a/storage.h
+++ b/storage.h
@@ -44,7 +44,7 @@ typedef struct {
storage_status_t (*check_pass) (const char *nick, const char *password);
- storage_status_t (*load) (const char *nick, const char *password, irc_t * irc);
+ storage_status_t (*load) (irc_t *irc, const char *password);
storage_status_t (*save) (irc_t *irc, int overwrite);
storage_status_t (*remove) (const char *nick, const char *password);
@@ -54,11 +54,11 @@ typedef struct {
storage_status_t storage_check_pass (const char *nick, const char *password);
-storage_status_t storage_load (const char *nick, const char *password, irc_t * irc);
-storage_status_t storage_save (irc_t *irc, int overwrite);
+storage_status_t storage_load (irc_t * irc, const char *password);
+storage_status_t storage_save (irc_t *irc, char *password, int overwrite);
storage_status_t storage_remove (const char *nick, const char *password);
-storage_status_t storage_rename (const char *onick, const char *nnick, const char *password);
+/* storage_status_t storage_rename (const char *onick, const char *nnick, const char *password); */
void register_storage_backend(storage_t *);
G_GNUC_MALLOC GList *storage_init(const char *primary, char **migrate);
diff --git a/storage_text.c b/storage_text.c
index 78f7e3bd..8ce4edcf 100644
--- a/storage_text.c
+++ b/storage_text.c
@@ -43,7 +43,7 @@ static void text_init (void)
it's read only! */
}
-static storage_status_t text_load ( const char *my_nick, const char* password, irc_t *irc )
+static storage_status_t text_load( irc_t *irc, const char* password )
{
char s[512];
char *line;
@@ -53,10 +53,7 @@ static storage_status_t text_load ( const char *my_nick, const char* password, i
user_t *ru = user_find( irc, ROOT_NICK );
account_t *acc, *acc_lookup[9];
- if( irc->status & USTATUS_IDENTIFIED )
- return( 1 );
-
- g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".accounts" );
+ g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" );
fp = fopen( s, "r" );
if( !fp ) return STORAGE_NO_SUCH_USER;
@@ -68,10 +65,6 @@ static storage_status_t text_load ( const char *my_nick, const char* password, i
return STORAGE_INVALID_PASSWORD;
}
- /* Do this now. If the user runs with AuthMode = Registered, the
- account command will not work otherwise. */
- irc->status |= USTATUS_IDENTIFIED;
-
while( fscanf( fp, "%511[^\n]s", s ) > 0 )
{
fgetc( fp );
@@ -100,7 +93,7 @@ static storage_status_t text_load ( const char *my_nick, const char* password, i
acc_lookup[8] = acc;
}
- g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".nicks" );
+ g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" );
fp = fopen( s, "r" );
if( !fp ) return STORAGE_NO_SUCH_USER;
while( fscanf( fp, "%s %d %s", s, &proto, nick ) > 0 )
diff --git a/storage_xml.c b/storage_xml.c
index 240206f1..b6745c75 100644
--- a/storage_xml.c
+++ b/storage_xml.c
@@ -28,10 +28,13 @@
#include "base64.h"
#include "arc.h"
#include "md5.h"
-#include <glib/gstdio.h>
+#include "chat.h"
-#if !GLIB_CHECK_VERSION(2,8,0)
+#if GLIB_CHECK_VERSION(2,8,0)
+#include <glib/gstdio.h>
+#else
/* GLib < 2.8.0 doesn't have g_access, so just use the system access(). */
+#include <unistd.h>
#define g_access access
#endif
@@ -51,6 +54,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;
@@ -169,7 +174,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 );
@@ -191,6 +205,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,
@@ -211,13 +242,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;
@@ -230,8 +264,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;
}
@@ -255,7 +288,7 @@ static void xml_init( void )
log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to `%s'.", global.conf->configdir );
}
-static storage_status_t xml_load_real( const char *my_nick, const char *password, irc_t *irc, xml_pass_st action )
+static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const char *password, xml_pass_st action )
{
GMarkupParseContext *ctx;
struct xml_parsedata *xd;
@@ -263,9 +296,6 @@ static storage_status_t xml_load_real( const char *my_nick, const char *password
GError *gerr = NULL;
int fd, st;
- if( irc && irc->status & USTATUS_IDENTIFIED )
- return( 1 );
-
xd = g_new0( struct xml_parsedata, 1 );
xd->irc = irc;
xd->given_nick = g_strdup( my_nick );
@@ -317,21 +347,19 @@ static storage_status_t xml_load_real( const char *my_nick, const char *password
if( action == XML_PASS_CHECK_ONLY )
return STORAGE_OK;
- irc->status |= USTATUS_IDENTIFIED;
-
return STORAGE_OK;
}
-static storage_status_t xml_load( const char *my_nick, const char *password, irc_t *irc )
+static storage_status_t xml_load( irc_t *irc, const char *password )
{
- return xml_load_real( my_nick, password, irc, XML_PASS_UNKNOWN );
+ return xml_load_real( irc, irc->nick, password, XML_PASS_UNKNOWN );
}
static storage_status_t xml_check_pass( const char *my_nick, const char *password )
{
/* This is a little bit risky because we have to pass NULL for the
irc_t argument. This *should* be fine, if I didn't miss anything... */
- return xml_load_real( my_nick, password, NULL, XML_PASS_CHECK_ONLY );
+ return xml_load_real( NULL, my_nick, password, XML_PASS_CHECK_ONLY );
}
static int xml_printf( int fd, int indent, char *fmt, ... )
@@ -367,12 +395,6 @@ static storage_status_t xml_save( irc_t *irc, int overwrite )
md5_byte_t pass_md5[21];
md5_state_t md5_state;
- if( irc->password == NULL )
- {
- irc_usermsg( irc, "Please register yourself if you want to save your settings." );
- return STORAGE_OTHER_ERROR;
- }
-
path2 = g_strdup( irc->nick );
nick_lc( path2 );
g_snprintf( path, sizeof( path ) - 2, "%s%s%s", global.conf->configdir, path2, ".xml" );
@@ -405,7 +427,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;
@@ -414,6 +436,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 );
@@ -432,7 +455,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;
@@ -446,6 +469,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;
}
diff --git a/user.c b/user.c
index 26676dd4..4d58f56b 100644
--- a/user.c
+++ b/user.c
@@ -140,7 +140,7 @@ user_t *user_find( irc_t *irc, char *nick )
return( NULL );
}
-user_t *user_findhandle( struct im_connection *ic, char *handle )
+user_t *user_findhandle( struct im_connection *ic, const char *handle )
{
user_t *u;
char *nick;
diff --git a/user.h b/user.h
index c6b933bc..9d8a41a0 100644
--- a/user.h
+++ b/user.h
@@ -55,7 +55,7 @@ typedef struct __USER
user_t *user_add( struct irc *irc, char *nick );
int user_del( irc_t *irc, char *nick );
G_MODULE_EXPORT user_t *user_find( irc_t *irc, char *nick );
-G_MODULE_EXPORT user_t *user_findhandle( struct im_connection *ic, char *handle );
+G_MODULE_EXPORT user_t *user_findhandle( struct im_connection *ic, const char *handle );
void user_rename( irc_t *irc, char *oldnick, char *newnick );
#endif /* __USER_H__ */