aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile12
-rw-r--r--account.c162
-rw-r--r--account.h19
-rw-r--r--bitlbee.c16
-rw-r--r--bitlbee.conf5
-rw-r--r--bitlbee.h2
-rw-r--r--chat.c192
-rw-r--r--chat.h51
-rw-r--r--conf.c18
-rwxr-xr-xconfigure56
-rw-r--r--crypting.c60
-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.xml197
-rw-r--r--doc/user-guide/help.xml2
-rw-r--r--doc/user-guide/misc.xml28
-rw-r--r--doc/user-guide/user-guide.xml2
-rw-r--r--irc.c97
-rw-r--r--irc.h8
-rw-r--r--irc_commands.c44
-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.c2
-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/io.c38
-rw-r--r--protocols/jabber/iq.c36
-rw-r--r--protocols/jabber/jabber.c24
-rw-r--r--protocols/jabber/jabber.h11
-rw-r--r--protocols/jabber/jabber_util.c13
-rw-r--r--protocols/jabber/presence.c27
-rw-r--r--protocols/msn/msn.c79
-rw-r--r--protocols/msn/msn.h7
-rw-r--r--protocols/msn/msn_util.c8
-rw-r--r--protocols/msn/ns.c49
-rw-r--r--protocols/msn/sb.c42
-rw-r--r--protocols/msn/tables.c37
-rw-r--r--protocols/nogaim.c219
-rw-r--r--protocols/nogaim.h28
-rw-r--r--protocols/oscar/aim.h13
-rw-r--r--protocols/oscar/oscar.c109
-rw-r--r--protocols/yahoo/libyahoo2.c725
-rw-r--r--protocols/yahoo/yahoo.c121
-rw-r--r--protocols/yahoo/yahoo2.h3
-rw-r--r--protocols/yahoo/yahoo2_callbacks.h12
-rw-r--r--protocols/yahoo/yahoo2_types.h44
-rw-r--r--root_commands.c450
-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--unix.c71
67 files changed, 2550 insertions, 1141 deletions
diff --git a/Makefile b/Makefile
index 5309adf4..337fcf63 100644
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@
-include Makefile.settings
# Program variables
-objects = account.o bitlbee.o crypting.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o
+objects = account.o bitlbee.o chat.o crypting.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o
headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h
subdirs = lib protocols
@@ -48,7 +48,7 @@ Makefile.settings:
@echo
clean: $(subdirs)
- rm -f *.o $(OUTFILE) core utils/bitlbeed encode decode
+ rm -f *.o $(OUTFILE) core utils/bitlbeed
$(MAKE) -C tests clean
distclean: clean $(subdirs)
@@ -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)
@@ -123,11 +123,5 @@ ifndef DEBUG
@-$(STRIP) $(OUTFILE)
endif
-encode: crypting.c
- $(CC) crypting.c lib/md5.c $(CFLAGS) -o encode -DCRYPTING_MAIN $(CFLAGS) $(EFLAGS) $(LFLAGS)
-
-decode: encode
- cp encode decode
-
ctags:
ctags `find . -name "*.c"` `find . -name "*.h"`
diff --git a/account.c b/account.c
index 2c6e1069..a844d229 100644
--- a/account.c
+++ b/account.c
@@ -1,7 +1,7 @@
/********************************************************************\
* BitlBee -- An IRC to other IM-networks gateway *
* *
- * Copyright 2002-2004 Wilmer van der Gaast and others *
+ * Copyright 2002-2010 Wilmer van der Gaast and others *
\********************************************************************/
/* Account management functions */
@@ -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 )
{
@@ -53,8 +54,10 @@ 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, "nick_source", "handle", NULL, 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;
@@ -67,7 +70,16 @@ account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass )
if( prpl->init )
prpl->init( a );
- return( a );
+ s = set_add( &a->set, "away", NULL, set_eval_account, a );
+ s->flags |= SET_NULL_OK;
+
+ if( a->flags & ACC_FLAG_STATUS_MESSAGE )
+ {
+ s = set_add( &a->set, "status", NULL, set_eval_account, a );
+ s->flags |= SET_NULL_OK;
+ }
+
+ return a;
}
char *set_eval_account( set_t *set, char *value )
@@ -76,9 +88,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 +112,44 @@ 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;
}
+ else if( strcmp( set->key, "away" ) == 0 ||
+ strcmp( set->key, "status" ) == 0 )
+ {
+ if( acc->ic && acc->ic->flags & OPT_LOGGED_IN )
+ {
+ /* If we're currently on-line, set the var now already
+ (bit of a hack) and send an update. */
+ g_free( set->value );
+ set->value = g_strdup( value );
+
+ imc_away_send_update( acc->ic );
+ }
+
+ return value;
+ }
- return NULL;
+ return SET_INVALID;
}
account_t *account_get( irc_t *irc, char *id )
@@ -180,6 +216,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 +230,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 );
@@ -233,3 +277,83 @@ void account_off( irc_t *irc, account_t *a )
cancel_auto_reconnect( a );
}
}
+
+struct account_reconnect_delay
+{
+ int start;
+ char op;
+ int step;
+ int max;
+};
+
+int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p )
+{
+ memset( p, 0, sizeof( *p ) );
+ /* A whole day seems like a sane "maximum maximum". */
+ p->max = 86400;
+
+ /* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */
+ while( *value && isdigit( *value ) )
+ p->start = p->start * 10 + *value++ - '0';
+
+ /* Sure, call me evil for implementing my own fscanf here, but it's
+ dead simple and I immediately know where to continue parsing. */
+
+ if( *value == 0 )
+ /* If the string ends now, the delay is constant. */
+ return 1;
+ else if( *value != '+' && *value != '*' )
+ /* Otherwise allow either a + or a * */
+ return 0;
+
+ p->op = *value++;
+
+ /* + or * the delay by this number every time. */
+ while( *value && isdigit( *value ) )
+ p->step = p->step * 10 + *value++ - '0';
+
+ if( *value == 0 )
+ /* Use the default maximum (one day). */
+ return 1;
+ else if( *value != '<' )
+ return 0;
+
+ p->max = 0;
+ value ++;
+ while( *value && isdigit( *value ) )
+ p->max = p->max * 10 + *value++ - '0';
+
+ return p->max > 0;
+}
+
+char *set_eval_account_reconnect_delay( set_t *set, char *value )
+{
+ struct account_reconnect_delay p;
+
+ return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID;
+}
+
+int account_reconnect_delay( account_t *a )
+{
+ char *setting = set_getstr( &a->irc->set, "auto_reconnect_delay" );
+ struct account_reconnect_delay p;
+
+ if( account_reconnect_delay_parse( setting, &p ) )
+ {
+ if( a->auto_reconnect_delay == 0 )
+ a->auto_reconnect_delay = p.start;
+ else if( p.op == '+' )
+ a->auto_reconnect_delay += p.step;
+ else if( p.op == '*' )
+ a->auto_reconnect_delay *= p.step;
+
+ if( a->auto_reconnect_delay > p.max )
+ a->auto_reconnect_delay = p.max;
+ }
+ else
+ {
+ a->auto_reconnect_delay = 0;
+ }
+
+ return a->auto_reconnect_delay;
+}
diff --git a/account.h b/account.h
index a81ca928..984dcfe6 100644
--- a/account.h
+++ b/account.h
@@ -34,7 +34,9 @@ typedef struct account
char *server;
int auto_connect;
+ int auto_reconnect_delay;
int reconnect;
+ int flags;
set_t *set;
GHashTable *nicks;
@@ -51,9 +53,20 @@ void account_on( irc_t *irc, account_t *a );
void account_off( irc_t *irc, account_t *a );
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
+typedef enum
+{
+ ACC_SET_NOSAVE = 0x01, /* Don't save this setting (i.e. stored elsewhere). */
+ ACC_SET_OFFLINE_ONLY = 0x02, /* Allow changes only if the acct is offline. */
+ ACC_SET_ONLINE_ONLY = 0x04, /* Allow changes only if the acct is online. */
+} account_set_flag_t;
+
+typedef enum
+{
+ ACC_FLAG_AWAY_MESSAGE = 0x01, /* Supports away messages instead of just states. */
+ ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */
+} account_flag_t;
#endif
diff --git a/bitlbee.c b/bitlbee.c
index b31c31fe..a96cff03 100644
--- a/bitlbee.c
+++ b/bitlbee.c
@@ -108,11 +108,17 @@ int bitlbee_daemon_init()
chdir( "/" );
- /* Sometimes std* are already closed (for example when we're in a RESTARTed
- BitlBee process. So let's only close TTY-fds. */
- if( isatty( 0 ) ) close( 0 );
- if( isatty( 1 ) ) close( 1 );
- if( isatty( 2 ) ) close( 2 );
+ i = close( 0 ) == 0;
+ i += close( 1 ) == 0;
+ i += close( 2 ) == 0;
+ /* To avoid that something important ends up on one of those
+ fd's, open them for something bogus. Otherwise RESTART
+ may cause troubles. */
+ while( i > 0 )
+ {
+ open( "/dev/null", O_WRONLY );
+ i --;
+ }
}
#endif
diff --git a/bitlbee.conf b/bitlbee.conf
index 5fce2820..4a3bbddf 100644
--- a/bitlbee.conf
+++ b/bitlbee.conf
@@ -54,9 +54,8 @@
## AuthPassword
##
## Password the user should enter when logging into a closed BitlBee server.
-## You can also have an MD5-encrypted password here. Format: "md5:", followed
-## by a hash as generated for the <user password=""> attribute in a BitlBee
-## XML file (for now there's no easier way to generate the hash).
+## You can also have a BitlBee-style MD5 hash here. Format: "md5:", followed
+## by a hash as generated by "bitlbee -x hash <password>".
##
# AuthPassword = ItllBeBitlBee ## Heh.. Our slogan. ;-)
## or
diff --git a/bitlbee.h b/bitlbee.h
index a09ebc5f..326e944d 100644
--- a/bitlbee.h
+++ b/bitlbee.h
@@ -34,7 +34,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 909d331a..c8cfaad8 100644
--- a/conf.c
+++ b/conf.c
@@ -77,7 +77,7 @@ conf_t *conf_load( int argc, char *argv[] )
at a *valid* configuration file. */
}
- while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hu:" ) ) >= 0 )
+ while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hR:u:" ) ) >= 0 )
/* ^^^^ Just to make sure we skip this step from the REHASH handler. */
{
if( opt == 'i' )
@@ -126,7 +126,7 @@ conf_t *conf_load( int argc, char *argv[] )
else if( opt == 'h' )
{
printf( "Usage: bitlbee [-D/-F [-i <interface>] [-p <port>] [-n] [-v]] [-I]\n"
- " [-c <file>] [-d <dir>] [-h]\n"
+ " [-c <file>] [-d <dir>] [-x] [-h]\n"
"\n"
"An IRC-to-other-chat-networks gateway\n"
"\n"
@@ -142,9 +142,17 @@ conf_t *conf_load( int argc, char *argv[] )
" -v Be verbose (only works in combination with -n)\n"
" -c Load alternative configuration file\n"
" -d Specify alternative user configuration directory\n"
+ " -x Command-line interface to password encryption/hashing\n"
" -h Show this help page.\n" );
return NULL;
}
+ else if( opt == 'R' )
+ {
+ /* Backward compatibility; older BitlBees passed this
+ info using a command-line flag. Allow people to
+ upgrade from such a version for now. */
+ setenv( "_BITLBEE_RESTART_STATE", optarg, 0 );
+ }
else if( opt == 'u' )
{
g_free( conf->user );
@@ -300,15 +308,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 5be64a84..1daf0a99 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
@@ -322,11 +359,11 @@ elif [ "$ssl" = "bogus" ]; then
echo 'Using bogus SSL code. This means some features will not work properly.'
## Yes, you, at the console! How can you authenticate if you don't have any SSL!?
- if [ "$msn" = "1" ]; then
+ if [ "$msn" = "1" -o "$yahoo" = "1" ]; then
echo
- echo 'Real SSL support is necessary for MSN authentication, will build without'
- echo 'MSN protocol support.'
+ echo 'WARNING: The MSN and Yahoo! modules will not work without SSL. Disabling.'
msn=0
+ yahoo=0
fi
ret=1
@@ -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"
@@ -495,6 +528,7 @@ GNU/* )
*BSD )
;;
Darwin )
+ echo 'STRIP=\# skip strip' >> Makefile.settings
;;
IRIX )
;;
diff --git a/crypting.c b/crypting.c
index 34b99034..0a5c937e 100644
--- a/crypting.c
+++ b/crypting.c
@@ -131,63 +131,3 @@ char *deobfucrypt (char *line, const char *password)
return (rv);
}
-
-#ifdef CRYPTING_MAIN
-
-/* A little main() function for people who want a stand-alone program to
- encode/decode BitlCrypted files. */
-
-int main( int argc, char *argv[] )
-{
- char *hash, *action, line[256];
- char* (*func)( char *, const char * );
-
- if( argc < 2 )
- {
- fprintf( stderr, "Usage: %s <password>\n\n"
- "Reads from stdin, writes to stdout.\n"
- "Call as \"encode\" to encode, \"decode\" to decode.\n", argv[0] );
- return( 1 );
- }
-
- hash = hashpass( argv[1] );
- action = argv[0] + strlen( argv[0] ) - strlen( "encode" );
-
- if( strcmp( action, "encode" ) == 0 )
- {
- fwrite( hash, 32, 1, stdout );
- func = obfucrypt;
- }
- else if( strcmp( action, "decode" ) == 0 )
- {
- char hash2[32];
-
- fread( hash2, 32, 1, stdin );
- if( memcmp( hash, hash2, 32 ) != 0 )
- {
- fprintf( stderr, "Passwords don't match. Can't decode.\n" );
- return( 1 );
- }
- func = deobfucrypt;
- }
- else
- {
- return( main( 0, NULL ) );
- }
-
- while( fscanf( stdin, "%[^\n]255s", line ) > 0 )
- {
- char *out;
-
- /* Flush the newline */
- fgetc( stdin );
-
- out = func( line, argv[1] );
- printf( "%s\n", out );
- g_free( out );
- }
-
- return( 0 );
-}
-
-#endif
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 6d77f8cd..931608ee 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>
@@ -320,12 +417,16 @@
</description>
</bitlbee-setting>
- <bitlbee-setting name="auto_reconnect_delay" type="integer" scope="global">
- <default>300</default>
+ <bitlbee-setting name="auto_reconnect_delay" type="string" scope="global">
+ <default>5*3&lt;900</default>
<description>
<para>
- Tell BitlBee after how many seconds it should attempt to bring an IM-connection back up after a crash. It's not a good idea to set this value very low, it will cause too much useless traffic when an IM-server is down for a few hours.
+ Tell BitlBee after how many seconds it should attempt to bring a broken IM-connection back up.
+ </para>
+
+ <para>
+ This can be one integer, for a constant delay. One can also set it to something like &quot;10*10&quot;, which means wait for ten seconds on the first reconnect, multiply it by ten on every failure. Once successfully connected, this delay is re-set to the initial value. With &lt; you can give a maximum delay.
</para>
<para>
@@ -334,6 +435,22 @@
</description>
</bitlbee-setting>
+ <bitlbee-setting name="away" type="string" scope="both">
+ <description>
+ <para>
+ To mark yourself as away, it is recommended to just use <emphasis>/away</emphasis>, like on normal IRC networks. If you want to mark yourself as away on only one IM network, you can use this per-account setting.
+ </para>
+
+ <para>
+ You can set it to any value and BitlBee will try to map it to the most appropriate away state for every open IM connection, or set it as a free-form away message where possible.
+ </para>
+
+ <para>
+ Any per-account away setting will override globally set away states. To un-set the setting, use <emphasis>set -del away</emphasis>.
+ </para>
+ </description>
+ </bitlbee-setting>
+
<bitlbee-setting name="away_devoice" type="boolean" scope="global">
<default>true</default>
@@ -481,6 +598,30 @@
</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="nick_source" type="string" scope="account">
+ <default>handle</default>
+ <possible-values>handle, full_name, first_name</possible-values>
+
+ <description>
+ <para>
+ By default, BitlBee generates a nickname for every contact by taking its handle and chopping off everything after the @. In some cases, this gives very inconvenient nicknames. The Facebook XMPP server is a good example, as all Facebook XMPP handles are numeric.
+ </para>
+
+ <para>
+ With this setting set to <emphasis>full_name</emphasis>, the person's full name is used to generate a nickname. Or if you don't like long nicknames, set this setting to <emphasis>first_name</emphasis> instead and only the first word will be used. Note that the full name can be full of non-ASCII characters which will be stripped off.
+ </para>
+ </description>
+ </bitlbee-setting>
+
<bitlbee-setting name="ops" type="string" scope="global">
<default>both</default>
<possible-values>both, root, user, none</possible-values>
@@ -575,7 +716,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>
@@ -583,7 +724,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>
@@ -636,6 +777,22 @@
</description>
</bitlbee-setting>
+ <bitlbee-setting name="status" type="string" scope="both">
+ <description>
+ <para>
+ Certain protocols (like Jabber/XMPP) support status messages, similar to away messages. They can be used to indicate things like your location or activity, without showing up as away/busy.
+ </para>
+
+ <para>
+ This setting can be used to set such a message. It will be available as a per-account setting for protocols that support it, and also as a global setting (which will then automatically be used for all protocols that support it).
+ </para>
+
+ <para>
+ Away states set using <emphasis>/away</emphasis> or the <emphasis>away</emphasis> setting will override this setting. To un-set the setting, use <emphasis>set -del status</emphasis>.
+ </para>
+ </description>
+ </bitlbee-setting>
+
<bitlbee-setting name="strip_html" type="boolean" scope="global">
<default>true</default>
@@ -849,28 +1006,4 @@
</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>
</chapter>
diff --git a/doc/user-guide/help.xml b/doc/user-guide/help.xml
index 34fdb9e2..7487a841 100644
--- a/doc/user-guide/help.xml
+++ b/doc/user-guide/help.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
+<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<book id="BitlBee-Help" xmlns:xi="http://www.w3.org/2003/XInclude">
diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml
index b55a8915..a926775a 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>
@@ -85,31 +85,35 @@ Some protocols (like Jabber) also support named groupchats. BitlBee now supports
<title>Away states</title>
<para>
-As you might've expected, you can just use the <emphasis>/away</emphasis> command in your IRC client to set an away-state. BitlBee supports most away-states supported by the protocols.
+To mark yourself as away, you can just use the <emphasis>/away</emphasis> command in your IRC client. BitlBee supports most away-states supported by the protocols.
</para>
<para>
-Not all away states are supported by all protocols, and some protocols have different names for them. BitlBee will try to pick the best available alias from this list for every connection:
+Away states have different names accross different protocols. BitlBee will try to pick the best available option for every connection:
</para>
<simplelist>
- <member>Away from computer, Away, Extended away</member>
- <member>NA, N/A, Not available</member>
- <member>Busy, Do not disturb, DND, Occupied</member>
- <member>Be right back, BRB</member>
- <member>On the phone, Phone, On phone</member>
- <member>Out to lunch, Lunch, Food</member>
+ <member>Away</member>
+ <member>NA</member>
+ <member>Busy, DND</member>
+ <member>BRB</member>
+ <member>Phone</member>
+ <member>Lunch, Food</member>
<member>Invisible, Hidden</member>
</simplelist>
<para>
-So <emphasis>/away Food</emphasis> will set your state to "Out to lunch" on your MSN connection, and for most other connections the default, "Away" or "Away from computer" will be chosen.
+So <emphasis>/away Food</emphasis> will set your state to "Out to lunch" on your MSN connection, and for most other connections the default, "Away" will be chosen.
</para>
<para>
You can also add more information to your away message. Setting it to "Busy - Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away message will be more descriptive for people on IRC. Most IM-protocols can also show this additional information to your buddies.
</para>
+<para>
+If you want to set an away state for only one of your connections, you can use the per-account <emphasis>away</emphasis> setting. See <emphasis>help set away</emphasis>.
+</para>
+
</sect1>
</chapter>
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 ea6098a1..ee7b4b0d 100644
--- a/irc.c
+++ b/irc.c
@@ -33,13 +33,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 )
@@ -71,11 +77,31 @@ static char *set_eval_charset( set_t *set, char *value )
return value;
}
+static char *set_eval_away_status( set_t *set, char *value )
+{
+ irc_t *irc = set->data;
+ account_t *a;
+
+ g_free( set->value );
+ set->value = g_strdup( value );
+
+ for( a = irc->accounts; a; a = a->next )
+ {
+ struct im_connection *ic = a->ic;
+
+ if( ic && ic->flags & OPT_LOGGED_IN )
+ imc_away_send_update( ic );
+ }
+
+ return value;
+}
+
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 );
@@ -135,28 +161,33 @@ 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", "300", set_eval_int, 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", NULL, set_eval_away_status, irc );
+ s->flags |= SET_NULL_OK;
+ 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", "true", 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, "status", NULL, set_eval_away_status, irc );
+ s->flags |= SET_NULL_OK;
+ 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 );
@@ -230,7 +261,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 );
@@ -398,10 +429,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 );
}
@@ -476,7 +505,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 ++ );
@@ -772,7 +801,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 );
@@ -1013,7 +1044,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 b8c52925..f9b2a5b9 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;
@@ -86,6 +82,7 @@ typedef struct irc
struct query *queries;
struct account *accounts;
+ struct chat *chatrooms;
struct __USER *users;
GHashTable *userhash;
@@ -99,7 +96,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..750bbcf5 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" );
}
@@ -464,7 +447,6 @@ static void irc_cmd_away( irc_t *irc, char **cmd )
{
user_t *u = user_find( irc, irc->nick );
char *away = cmd[1];
- account_t *a;
if( !u ) return;
@@ -491,13 +473,7 @@ static void irc_cmd_away( irc_t *irc, char **cmd )
irc_reply( irc, 305, ":Welcome back" );
}
- for( a = irc->accounts; a; a = a->next )
- {
- struct im_connection *ic = a->ic;
-
- if( ic && ic->flags & OPT_LOGGED_IN )
- imc_set_away( ic, u->away );
- }
+ set_setstr( &irc->set, "away", u->away );
}
static void irc_cmd_whois( irc_t *irc, char **cmd )
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 c5087f75..fe2ff17c 100644
--- a/lib/misc.c
+++ b/lib/misc.c
@@ -46,6 +46,7 @@
#include <resolv.h>
#endif
+#include "md5.h"
#include "ssl_client.h"
void strip_linefeed(gchar *text)
@@ -88,6 +89,7 @@ static const htmlentity_t ent[] =
{ "lt", "<" },
{ "gt", ">" },
{ "amp", "&" },
+ { "apos", "'" },
{ "quot", "\"" },
{ "aacute", "á" },
{ "eacute", "é" },
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/io.c b/protocols/jabber/io.c
index 10efad37..4a790f27 100644
--- a/protocols/jabber/io.c
+++ b/protocols/jabber/io.c
@@ -374,39 +374,23 @@ static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )
}
if( ( c = xt_find_node( node->children, "bind" ) ) )
- {
- reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
- xt_add_attr( reply, "xmlns", XMLNS_BIND );
- reply = jabber_make_packet( "iq", "set", NULL, reply );
- jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
-
- if( !jabber_write_packet( ic, reply ) )
- return XT_ABORT;
-
- jd->flags |= JFLAG_WAIT_BIND;
- }
+ jd->flags |= JFLAG_WANT_BIND;
if( ( c = xt_find_node( node->children, "session" ) ) )
- {
- reply = xt_new_node( "session", NULL, NULL );
- xt_add_attr( reply, "xmlns", XMLNS_SESSION );
- reply = jabber_make_packet( "iq", "set", NULL, reply );
- jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
-
- if( !jabber_write_packet( ic, reply ) )
- return XT_ABORT;
-
- jd->flags |= JFLAG_WAIT_SESSION;
- }
+ jd->flags |= JFLAG_WANT_SESSION;
/* This flag is already set if we authenticated via SASL, so now
we can resume the session in the new stream, if we don't have
to bind/initialize the session. */
- if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
+ if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WANT_BIND | JFLAG_WANT_SESSION ) ) == 0 )
{
if( !jabber_get_roster( ic ) )
return XT_ABORT;
}
+ else if( jd->flags & JFLAG_AUTHENTICATED )
+ {
+ return jabber_pkt_bind_sess( ic, NULL, NULL );
+ }
return XT_HANDLED;
}
@@ -440,6 +424,7 @@ static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data )
imcb_log( ic, "Converting stream to TLS" );
+ jd->flags |= JFLAG_STARTTLS_DONE;
jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, ic );
return XT_HANDLED;
@@ -530,9 +515,10 @@ gboolean jabber_start_stream( struct im_connection *ic )
if( jd->r_inpa <= 0 )
jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, ic );
- greet = g_strdup_printf( "<?xml version='1.0' ?>"
- "<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
- "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", jd->server );
+ greet = g_strdup_printf( "%s<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
+ "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">",
+ ( jd->flags & JFLAG_STARTTLS_DONE ) ? "" : "<?xml version='1.0' ?>",
+ jd->server );
st = jabber_write( ic, greet, strlen( greet ) );
diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c
index 38c5a5a9..21e52da6 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;
}
@@ -296,24 +297,43 @@ static xt_status jabber_finish_iq_auth( struct im_connection *ic, struct xt_node
xt_status jabber_pkt_bind_sess( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
{
struct jabber_data *jd = ic->proto_data;
- struct xt_node *c;
+ struct xt_node *c, *reply = NULL;
char *s;
- if( ( c = xt_find_node( node->children, "bind" ) ) )
+ if( node && ( c = xt_find_node( node->children, "bind" ) ) )
{
c = xt_find_node( c->children, "jid" );
if( c && c->text_len && ( s = strchr( c->text, '/' ) ) &&
strcmp( s + 1, set_getstr( &ic->acc->set, "resource" ) ) != 0 )
imcb_log( ic, "Server changed session resource string to `%s'", s + 1 );
- jd->flags &= ~JFLAG_WAIT_BIND;
+ jd->flags &= ~JFLAG_WANT_BIND;
}
- else
+ else if( node && ( c = xt_find_node( node->children, "session" ) ) )
{
- jd->flags &= ~JFLAG_WAIT_SESSION;
+ jd->flags &= ~JFLAG_WANT_SESSION;
}
- if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
+ if( jd->flags & JFLAG_WANT_BIND )
+ {
+ reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
+ xt_add_attr( reply, "xmlns", XMLNS_BIND );
+ }
+ else if( jd->flags & JFLAG_WANT_SESSION )
+ {
+ reply = xt_new_node( "session", NULL, NULL );
+ xt_add_attr( reply, "xmlns", XMLNS_SESSION );
+ }
+
+ if( reply != NULL )
+ {
+ reply = jabber_make_packet( "iq", "set", NULL, reply );
+ jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
+
+ if( !jabber_write_packet( ic, reply ) )
+ return XT_ABORT;
+ }
+ else if( ( jd->flags & ( JFLAG_WANT_BIND | JFLAG_WANT_SESSION ) ) == 0 )
{
if( !jabber_get_roster( ic ) )
return XT_ABORT;
diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c
index c9c1d0a0..eca7d2d3 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;
@@ -79,6 +79,8 @@ static void jabber_init( account_t *acc )
s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc );
s->flags |= ACC_SET_OFFLINE_ONLY;
+
+ acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE;
}
static void jabber_generate_id_hash( struct jabber_data *jd );
@@ -363,10 +365,11 @@ static void jabber_get_info( struct im_connection *ic, char *who )
while( bud )
{
- imcb_log( ic, "Buddy %s (%d) information:\nAway state: %s\nAway message: %s",
- bud->full_jid, bud->priority,
- bud->away_state ? bud->away_state->full_name : "(none)",
- bud->away_message ? : "(none)" );
+ imcb_log( ic, "Buddy %s (%d) information:", bud->full_jid, bud->priority );
+ if( bud->away_state )
+ imcb_log( ic, "Away state: %s", bud->away_state->full_name );
+ imcb_log( ic, "Status message: %s", bud->away_message ? : "(none)" );
+
bud = bud->next;
}
@@ -376,11 +379,12 @@ static void jabber_get_info( struct im_connection *ic, char *who )
static void jabber_set_away( struct im_connection *ic, char *state_txt, char *message )
{
struct jabber_data *jd = ic->proto_data;
- struct jabber_away_state *state;
- /* Save all this info. We need it, for example, when changing the priority setting. */
- state = (void *) jabber_away_state_by_name( state_txt );
- jd->away_state = state ? state : (void *) jabber_away_state_list; /* Fall back to "Away" if necessary. */
+ /* state_txt == NULL -> Not away.
+ Unknown state -> fall back to the first defined away state. */
+ jd->away_state = state_txt ? jabber_away_state_by_name( state_txt )
+ ? : jabber_away_state_list : NULL;
+
g_free( jd->away_message );
jd->away_message = ( message && *message ) ? g_strdup( message ) : NULL;
@@ -424,7 +428,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 61238a30..8e3bf036 100644
--- a/protocols/jabber/jabber.h
+++ b/protocols/jabber/jabber.h
@@ -26,9 +26,9 @@
#include <glib.h>
-#include "xmltree.h"
#include "bitlbee.h"
#include "md5.h"
+#include "xmltree.h"
extern GSList *jabber_connections;
@@ -39,12 +39,13 @@ typedef enum
JFLAG_AUTHENTICATED = 2, /* Set when we're successfully authenticatd. */
JFLAG_STREAM_RESTART = 4, /* Set when we want to restart the stream (after
SASL or TLS). */
- JFLAG_WAIT_SESSION = 8, /* Set if we sent a <session> tag and need a reply
+ JFLAG_WANT_SESSION = 8, /* Set if the server wants a <session/> tag
before we continue. */
- JFLAG_WAIT_BIND = 16, /* ... for <bind> tag. */
+ JFLAG_WANT_BIND = 16, /* ... for <bind> tag. */
JFLAG_WANT_TYPING = 32, /* Set if we ever sent a typing notification, this
activates all XEP-85 related code. */
JFLAG_XMLCONSOLE = 64, /* If the user added an xmlconsole buddy. */
+ JFLAG_STARTTLS_DONE = 128, /* If a plaintext session was converted to TLS. */
} jabber_flags_t;
typedef enum
@@ -83,7 +84,7 @@ struct jabber_data
/* After changing one of these two (or the priority setting), call
presence_send_update() to inform the server about the changes. */
- struct jabber_away_state *away_state;
+ const struct jabber_away_state *away_state;
char *away_message;
md5_state_t cached_id_prefix;
@@ -240,7 +241,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 1bee5009..185d3878 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. */
@@ -227,10 +227,9 @@ xt_status jabber_cache_handle_packet( struct im_connection *ic, struct xt_node *
const struct jabber_away_state jabber_away_state_list[] =
{
{ "away", "Away" },
- { "chat", "Free for Chat" },
+ { "chat", "Free for Chat" }, /* WTF actually uses this? */
{ "dnd", "Do not Disturb" },
{ "xa", "Extended Away" },
- { "", "Online" },
{ "", NULL }
};
@@ -238,6 +237,9 @@ const struct jabber_away_state *jabber_away_state_by_code( char *code )
{
int i;
+ if( code == NULL )
+ return NULL;
+
for( i = 0; jabber_away_state_list[i].full_name; i ++ )
if( g_strcasecmp( jabber_away_state_list[i].code, code ) == 0 )
return jabber_away_state_list + i;
@@ -249,6 +251,9 @@ const struct jabber_away_state *jabber_away_state_by_name( char *name )
{
int i;
+ if( name == NULL )
+ return NULL;
+
for( i = 0; jabber_away_state_list[i].full_name; i ++ )
if( g_strcasecmp( jabber_away_state_list[i].full_name, name ) == 0 )
return jabber_away_state_list + i;
diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c
index 6fc360b7..28aaea1b 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;
}
@@ -187,13 +189,12 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )
{
int is_away = 0;
- if( send_presence->away_state && !( *send_presence->away_state->code == 0 ||
- strcmp( send_presence->away_state->code, "chat" ) == 0 ) )
+ if( send_presence->away_state &&
+ strcmp( send_presence->away_state->code, "chat" ) != 0 )
is_away = OPT_AWAY;
imcb_buddy_status( ic, send_presence->bare_jid, OPT_LOGGED_IN | is_away,
- ( is_away && send_presence->away_state ) ?
- send_presence->away_state->full_name : NULL,
+ is_away ? send_presence->away_state->full_name : NULL,
send_presence->away_message );
}
@@ -206,17 +207,15 @@ int presence_send_update( struct im_connection *ic )
{
struct jabber_data *jd = ic->proto_data;
struct xt_node *node, *cap;
- char *show = jd->away_state->code;
- char *status = jd->away_message;
struct groupchat *c;
int st;
node = jabber_make_packet( "presence", NULL, NULL, NULL );
xt_add_child( node, xt_new_node( "priority", set_getstr( &ic->acc->set, "priority" ), NULL ) );
- if( show && *show )
- xt_add_child( node, xt_new_node( "show", show, NULL ) );
- if( status )
- xt_add_child( node, xt_new_node( "status", status, NULL ) );
+ if( jd->away_state )
+ xt_add_child( node, xt_new_node( "show", jd->away_state->code, NULL ) );
+ if( jd->away_message )
+ xt_add_child( node, xt_new_node( "status", jd->away_message, NULL ) );
/* This makes the packet slightly bigger, but clients interested in
capabilities can now cache the discovery info. This reduces the
diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c
index 09670dfa..8930847d 100644
--- a/protocols/msn/msn.c
+++ b/protocols/msn/msn.c
@@ -116,7 +116,6 @@ static void msn_logout( struct im_connection *ic )
static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, int away )
{
struct msn_switchboard *sb;
- struct msn_data *md = ic->proto_data;
if( ( sb = msn_sb_by_handle( ic, who ) ) )
{
@@ -125,47 +124,13 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in
else
{
struct msn_message *m;
- char buf[1024];
/* Create a message. We have to arrange a usable switchboard, and send the message later. */
m = g_new0( struct msn_message, 1 );
m->who = g_strdup( who );
m->text = g_strdup( message );
- /* FIXME: *CHECK* the reliability of using spare sb's! */
- if( ( sb = msn_sb_spare( ic ) ) )
- {
- debug( "Trying to use a spare switchboard to message %s", who );
-
- sb->who = g_strdup( who );
- g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
- if( msn_sb_write( sb, buf, strlen( buf ) ) )
- {
- /* He/She should join the switchboard soon, let's queue the message. */
- sb->msgq = g_slist_append( sb->msgq, m );
- return( 1 );
- }
- }
-
- debug( "Creating a new switchboard to message %s", who );
-
- /* If we reach this line, there was no spare switchboard, so let's make one. */
- g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
- if( !msn_write( ic, buf, strlen( buf ) ) )
- {
- g_free( m->who );
- g_free( m->text );
- g_free( m );
-
- return( 0 );
- }
-
- /* And queue the message to md. We'll pick it up when the switchboard comes up. */
- md->msgq = g_slist_append( md->msgq, m );
-
- /* FIXME: If the switchboard creation fails, the message will not be sent. */
-
- return( 1 );
+ return msn_sb_write_msg( ic, m );
}
return( 0 );
@@ -177,8 +142,9 @@ static GList *msn_away_states( struct im_connection *ic )
int i;
if( l == NULL )
- for( i = 0; msn_away_state_list[i].number > -1; i ++ )
- l = g_list_append( l, (void*) msn_away_state_list[i].name );
+ for( i = 0; *msn_away_state_list[i].code; i ++ )
+ if( *msn_away_state_list[i].name )
+ l = g_list_append( l, (void*) msn_away_state_list[i].name );
return l;
}
@@ -187,17 +153,14 @@ static void msn_set_away( struct im_connection *ic, char *state, char *message )
{
char buf[1024];
struct msn_data *md = ic->proto_data;
- const struct msn_away_state *st;
- if( strcmp( state, GAIM_AWAY_CUSTOM ) == 0 )
- st = msn_away_state_by_name( "Away" );
+ if( state )
+ md->away_state = msn_away_state_by_name( state ) ? :
+ msn_away_state_list + 1;
else
- st = msn_away_state_by_name( state );
+ md->away_state = msn_away_state_list;
- if( !st ) st = msn_away_state_list;
- md->away_state = st;
-
- g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code );
+ g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, md->away_state->code );
msn_write( ic, buf, strlen( buf ) );
}
@@ -255,8 +218,6 @@ static void msn_chat_leave( struct groupchat *c )
static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )
{
struct msn_switchboard *sb;
- struct msn_data *md = ic->proto_data;
- char buf[1024];
if( ( sb = msn_sb_by_handle( ic, who ) ) )
{
@@ -267,31 +228,13 @@ static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )
{
struct msn_message *m;
- if( ( sb = msn_sb_spare( ic ) ) )
- {
- debug( "Trying to reuse an existing switchboard as a groupchat with %s", who );
- g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
- if( msn_sb_write( sb, buf, strlen( buf ) ) )
- return msn_sb_to_chat( sb );
- }
-
- /* If the stuff above failed for some reason: */
- debug( "Creating a new switchboard to groupchat with %s", who );
-
- /* Request a new switchboard. */
- g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
- if( !msn_write( ic, buf, strlen( buf ) ) )
- return( 0 );
-
/* Create a magic message. This is quite hackish, but who cares? :-P */
m = g_new0( struct msn_message, 1 );
m->who = g_strdup( who );
m->text = g_strdup( GROUPCHAT_SWITCHBOARD_MESSAGE );
- /* Queue the magic message and cross your fingers. */
- md->msgq = g_slist_append( md->msgq, m );
-
- /* FIXME: Can I try to return something here already? */
+ msn_sb_write_msg( ic, m );
+
return NULL;
}
diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h
index 16e5ab52..84914bc3 100644
--- a/protocols/msn/msn.h
+++ b/protocols/msn/msn.h
@@ -23,6 +23,9 @@
Suite 330, Boston, MA 02111-1307 USA
*/
+#ifndef _MSN_H
+#define _MSN_H
+
/* Some hackish magicstrings to make special-purpose messages/switchboards.
*/
#define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r"
@@ -93,7 +96,6 @@ struct msn_switchboard
struct msn_away_state
{
- int number;
char code[4];
char name[16];
};
@@ -175,3 +177,6 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text );
struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb );
void msn_sb_destroy( struct msn_switchboard *sb );
gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond );
+int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m );
+
+#endif //_MSN_H
diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c
index 58ad22f8..668a8b8a 100644
--- a/protocols/msn/msn_util.c
+++ b/protocols/msn/msn_util.c
@@ -170,9 +170,9 @@ char *msn_findheader( char *text, char *header, int len )
while( i < len && ( text[i] == '\r' || text[i] == '\n' ) ) i ++;
/* End of headers? */
- if( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||
- strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ||
- strncmp( text + i - 2, "\r\r", 2 ) == 0 )
+ if( ( i >= 4 && strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ) ||
+ ( i >= 2 && ( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||
+ strncmp( text + i - 2, "\r\r", 2 ) == 0 ) ) )
{
break;
}
@@ -373,6 +373,6 @@ void msn_msgq_purge( struct im_connection *ic, GSList **list )
g_slist_free( *list );
*list = NULL;
- imcb_log( ic, ret->str );
+ imcb_log( ic, "%s", ret->str );
g_string_free( ret, TRUE );
}
diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c
index fe48f96d..d05d8e0d 100644
--- a/protocols/msn/ns.c
+++ b/protocols/msn/ns.c
@@ -419,11 +419,12 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
if( !st )
{
/* FIXME: Warn/Bomb about unknown away state? */
- st = msn_away_state_list;
+ st = msn_away_state_list + 1;
}
- imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN |
- ( st->number ? OPT_AWAY : 0 ), st->name, NULL );
+ imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN |
+ ( st != msn_away_state_list ? OPT_AWAY : 0 ),
+ st->name, NULL );
}
else if( strcmp( cmd[0], "FLN" ) == 0 )
{
@@ -448,11 +449,12 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
if( !st )
{
/* FIXME: Warn/Bomb about unknown away state? */
- st = msn_away_state_list;
+ st = msn_away_state_list + 1;
}
- imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN |
- ( st->number ? OPT_AWAY : 0 ), st->name, NULL );
+ imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN |
+ ( st != msn_away_state_list ? OPT_AWAY : 0 ),
+ st->name, NULL );
}
else if( strcmp( cmd[0], "RNG" ) == 0 )
{
@@ -662,8 +664,8 @@ static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int
imcb_log( ic, "The server is going down for maintenance in %s minutes.", arg1 );
}
- if( arg1 ) g_free( arg1 );
- if( mtype ) g_free( mtype );
+ g_free( arg1 );
+ g_free( mtype );
}
else if( g_strncasecmp( ct, "text/x-msmsgsprofile", 20 ) == 0 )
{
@@ -671,25 +673,30 @@ static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int
}
else if( g_strncasecmp( ct, "text/x-msmsgsinitialemailnotification", 37 ) == 0 )
{
- char *inbox = msn_findheader( body, "Inbox-Unread:", blen );
- char *folders = msn_findheader( body, "Folders-Unread:", blen );
-
- if( inbox && folders && set_getbool( &ic->acc->set, "mail_notifications" ) )
+ if( set_getbool( &ic->acc->set, "mail_notifications" ) )
{
- imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
+ char *inbox = msn_findheader( body, "Inbox-Unread:", blen );
+ char *folders = msn_findheader( body, "Folders-Unread:", blen );
+
+ if( inbox && folders )
+ imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
+
+ g_free( inbox );
+ g_free( folders );
}
-
- g_free( inbox );
- g_free( folders );
}
else if( g_strncasecmp( ct, "text/x-msmsgsemailnotification", 30 ) == 0 )
{
- char *from = msn_findheader( body, "From-Addr:", blen );
- char *fromname = msn_findheader( body, "From:", blen );
-
- if( from && fromname && set_getbool( &ic->acc->set, "mail_notifications" ) )
+ if( set_getbool( &ic->acc->set, "mail_notifications" ) )
{
- imcb_log( ic, "Received an e-mail message from %s <%s>.", fromname, from );
+ char *from = msn_findheader( body, "From-Addr:", blen );
+ char *fromname = msn_findheader( body, "From:", blen );
+
+ if( from && fromname )
+ imcb_log( ic, "Received an e-mail message from %s <%s>.", fromname, from );
+
+ g_free( from );
+ g_free( fromname );
}
}
else if( g_strncasecmp( ct, "text/x-msmsgsactivemailnotification", 35 ) == 0 )
diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c
index 18c41ef5..e9526234 100644
--- a/protocols/msn/sb.c
+++ b/protocols/msn/sb.c
@@ -47,6 +47,48 @@ int msn_sb_write( struct msn_switchboard *sb, char *s, int len )
return( 1 );
}
+int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m )
+{
+ struct msn_data *md = ic->proto_data;
+ struct msn_switchboard *sb;
+ char buf[1024];
+
+ /* FIXME: *CHECK* the reliability of using spare sb's! */
+ if( ( sb = msn_sb_spare( ic ) ) )
+ {
+ debug( "Trying to use a spare switchboard to message %s", m->who );
+
+ sb->who = g_strdup( m->who );
+ g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, m->who );
+ if( msn_sb_write( sb, buf, strlen( buf ) ) )
+ {
+ /* He/She should join the switchboard soon, let's queue the message. */
+ sb->msgq = g_slist_append( sb->msgq, m );
+ return( 1 );
+ }
+ }
+
+ debug( "Creating a new switchboard to message %s", m->who );
+
+ /* If we reach this line, there was no spare switchboard, so let's make one. */
+ g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
+ if( !msn_write( ic, buf, strlen( buf ) ) )
+ {
+ g_free( m->who );
+ g_free( m->text );
+ g_free( m );
+
+ return( 0 );
+ }
+
+ /* And queue the message to md. We'll pick it up when the switchboard comes up. */
+ md->msgq = g_slist_append( md->msgq, m );
+
+ /* FIXME: If the switchboard creation fails, the message will not be sent. */
+
+ return( 1 );
+}
+
struct msn_switchboard *msn_sb_create( struct im_connection *ic, char *host, int port, char *key, int session )
{
struct msn_data *md = ic->proto_data;
diff --git a/protocols/msn/tables.c b/protocols/msn/tables.c
index 5ba9ea73..42b12aa9 100644
--- a/protocols/msn/tables.c
+++ b/protocols/msn/tables.c
@@ -28,48 +28,37 @@
const struct msn_away_state msn_away_state_list[] =
{
- { 0, "NLN", "Available" },
- { 1, "BSY", "Busy" },
- { 3, "IDL", "Idle" },
- { 5, "BRB", "Be Right Back" },
- { 7, "AWY", "Away" },
- { 9, "PHN", "On the Phone" },
- { 11, "LUN", "Out to Lunch" },
- { 13, "HDN", "Hidden" },
- { -1, "", "" }
+ { "NLN", "" },
+ { "AWY", "Away" },
+ { "BSY", "Busy" },
+ { "IDL", "Idle" },
+ { "BRB", "Be Right Back" },
+ { "PHN", "On the Phone" },
+ { "LUN", "Out to Lunch" },
+ { "HDN", "Hidden" },
+ { "", "" }
};
-const struct msn_away_state *msn_away_state_by_number( int number )
-{
- int i;
-
- for( i = 0; msn_away_state_list[i].number > -1; i ++ )
- if( msn_away_state_list[i].number == number )
- return( msn_away_state_list + i );
-
- return( NULL );
-}
-
const struct msn_away_state *msn_away_state_by_code( char *code )
{
int i;
- for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+ for( i = 0; *msn_away_state_list[i].code; i ++ )
if( g_strcasecmp( msn_away_state_list[i].code, code ) == 0 )
return( msn_away_state_list + i );
- return( NULL );
+ return NULL;
}
const struct msn_away_state *msn_away_state_by_name( char *name )
{
int i;
- for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+ for( i = 0; *msn_away_state_list[i].code; i ++ )
if( g_strcasecmp( msn_away_state_list[i].name, name ) == 0 )
return( msn_away_state_list + i );
- return( NULL );
+ return NULL;
}
const struct msn_status_code msn_status_code_list[] =
diff --git a/protocols/nogaim.c b/protocols/nogaim.c
index 62669aaf..75c2139b 100644
--- a/protocols/nogaim.c
+++ b/protocols/nogaim.c
@@ -1,7 +1,7 @@
/********************************************************************\
* BitlBee -- An IRC to other IM-networks gateway *
* *
- * Copyright 2002-2006 Wilmer van der Gaast and others *
+ * Copyright 2002-2010 Wilmer van der Gaast and others *
\********************************************************************/
/*
@@ -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
@@ -263,9 +267,21 @@ void imcb_connected( struct im_connection *ic )
ic->keepalive = b_timeout_add( 60000, send_keepalive, ic );
ic->flags |= OPT_LOGGED_IN;
- /* Also necessary when we're not away, at least for some of the
- protocols. */
- imc_set_away( ic, u->away );
+ /* Necessary to send initial presence status, even if we're not away. */
+ imc_away_send_update( 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 )
@@ -289,6 +305,7 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )
irc_t *irc = ic->irc;
user_t *t, *u;
account_t *a;
+ int delay;
/* Nested calls might happen sometimes, this is probably the best
place to catch them. */
@@ -304,6 +321,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 )
{
@@ -328,10 +348,9 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )
/* Uhm... This is very sick. */
}
else if( allow_reconnect && set_getbool( &irc->set, "auto_reconnect" ) &&
- set_getbool( &a->set, "auto_reconnect" ) )
+ set_getbool( &a->set, "auto_reconnect" ) &&
+ ( delay = account_reconnect_delay( a ) ) > 0 )
{
- int delay = set_getint( &irc->set, "auto_reconnect_delay" );
-
imcb_log( ic, "Reconnecting in %d seconds..", delay );
a->reconnect = b_timeout_add( delay * 1000, auto_reconnect, a );
}
@@ -428,6 +447,7 @@ struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle )
void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname )
{
user_t *u = user_findhandle( ic, handle );
+ char *set;
if( !u || !realname ) return;
@@ -440,6 +460,23 @@ void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char
if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->irc->set, "display_namechanges" ) )
imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname );
}
+
+ set = set_getstr( &ic->acc->set, "nick_source" );
+ if( strcmp( set, "handle" ) != 0 )
+ {
+ char *name = g_strdup( realname );
+
+ if( strcmp( set, "first_name" ) == 0 )
+ {
+ int i;
+ for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {}
+ name[i] = '\0';
+ }
+
+ imcb_buddy_nick_hint( ic, handle, name );
+
+ g_free( name );
+ }
}
void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group )
@@ -452,7 +489,7 @@ void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *grou
/* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM
modules to suggest a nickname for a handle. */
-void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick )
+void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick )
{
user_t *u = user_findhandle( ic, handle );
char newnick[MAX_NICK_LENGTH+1], *orig_nick;
@@ -487,33 +524,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 )
+{
+ 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 show_got_added_data*)data)->handle );
+ 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! */
@@ -524,7 +598,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 );
}
@@ -923,14 +997,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.... */
@@ -974,7 +1044,7 @@ char *set_eval_away_devoice( set_t *set, char *value )
irc->channel, pm, v, list );
}
- return( set_eval_bool( set, value ) );
+ return value;
}
@@ -1016,51 +1086,30 @@ int imc_chat_msg( struct groupchat *c, char *msg, int flags )
return 1;
}
-static char *imc_away_alias_find( GList *gcm, char *away );
+static char *imc_away_state_find( GList *gcm, char *away, char **message );
-int imc_set_away( struct im_connection *ic, char *away )
+int imc_away_send_update( struct im_connection *ic )
{
- GList *m, *ms;
- char *s;
-
- if( !away ) away = "";
- ms = m = ic->acc->prpl->away_states( ic );
-
- while( m )
- {
- if( *away )
- {
- if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
- break;
- }
- else
- {
- if( g_strcasecmp( m->data, "Available" ) == 0 )
- break;
- if( g_strcasecmp( m->data, "Online" ) == 0 )
- break;
- }
- m = m->next;
- }
+ char *away, *msg = NULL;
- if( m )
+ away = set_getstr( &ic->acc->set, "away" ) ?
+ : set_getstr( &ic->irc->set, "away" );
+ if( away && *away )
{
- ic->acc->prpl->set_away( ic, m->data, *away ? away : NULL );
+ GList *m = ic->acc->prpl->away_states( ic );
+ msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
+ away = imc_away_state_find( m, away, &msg ) ? : m->data;
}
- else
+ else if( ic->acc->flags & ACC_FLAG_STATUS_MESSAGE )
{
- s = imc_away_alias_find( ms, away );
- if( s )
- {
- ic->acc->prpl->set_away( ic, s, away );
- if( set_getbool( &ic->irc->set, "debug" ) )
- imcb_log( ic, "Setting away state to %s", s );
- }
- else
- ic->acc->prpl->set_away( ic, GAIM_AWAY_CUSTOM, away );
+ away = NULL;
+ msg = set_getstr( &ic->acc->set, "status" ) ?
+ : set_getstr( &ic->irc->set, "status" );
}
- return( 1 );
+ ic->acc->prpl->set_away( ic, away, msg );
+
+ return 1;
}
static char *imc_away_alias_list[8][5] =
@@ -1075,16 +1124,33 @@ static char *imc_away_alias_list[8][5] =
{ NULL }
};
-static char *imc_away_alias_find( GList *gcm, char *away )
+static char *imc_away_state_find( GList *gcm, char *away, char **message )
{
GList *m;
int i, j;
+ for( m = gcm; m; m = m->next )
+ if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
+ {
+ /* At least the Yahoo! module works better if message
+ contains no data unless it adds something to what
+ we have in state already. */
+ if( strlen( m->data ) == strlen( away ) )
+ *message = NULL;
+
+ return m->data;
+ }
+
for( i = 0; *imc_away_alias_list[i]; i ++ )
{
+ int keep_message;
+
for( j = 0; imc_away_alias_list[i][j]; j ++ )
if( g_strncasecmp( away, imc_away_alias_list[i][j], strlen( imc_away_alias_list[i][j] ) ) == 0 )
+ {
+ keep_message = strlen( away ) != strlen( imc_away_alias_list[i][j] );
break;
+ }
if( !imc_away_alias_list[i][j] ) /* If we reach the end, this row */
continue; /* is not what we want. Next! */
@@ -1092,17 +1158,22 @@ static char *imc_away_alias_find( GList *gcm, char *away )
/* Now find an entry in this row which exists in gcm */
for( j = 0; imc_away_alias_list[i][j]; j ++ )
{
- m = gcm;
- while( m )
- {
+ for( m = gcm; m; m = m->next )
if( g_strcasecmp( imc_away_alias_list[i][j], m->data ) == 0 )
- return( imc_away_alias_list[i][j] );
- m = m->next;
- }
+ {
+ if( !keep_message )
+ *message = NULL;
+
+ return imc_away_alias_list[i][j];
+ }
}
+
+ /* No need to look further, apparently this state doesn't
+ have any good alias for this protocol. */
+ break;
}
- return( NULL );
+ return NULL;
}
void imc_add_allow( struct im_connection *ic, char *handle )
diff --git a/protocols/nogaim.h b/protocols/nogaim.h
index ee5d7a30..a523a3a5 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"
@@ -46,7 +48,6 @@
#define BUDDY_ALIAS_MAXLEN 388 /* because MSN names can be 387 characters */
#define WEBSITE "http://www.bitlbee.org/"
-#define GAIM_AWAY_CUSTOM "Custom"
/* Sharing flags between all kinds of things. I just hope I won't hit any
limits before 32-bit machines become extinct. ;-) */
@@ -207,7 +208,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
@@ -215,13 +216,17 @@ struct prpl {
void (* chat_topic) (struct groupchat *, char *topic);
/* You can tell what away states your protocol supports, so that
- * BitlBee will try to map the IRC away reasons to them, or use
- * GAIM_AWAY_CUSTOM when calling skype_set_away(). */
+ * BitlBee will try to map the IRC away reasons to them. If your
+ * protocol doesn't have any, just return one generic "Away". */
GList *(* away_states)(struct im_connection *ic);
/* Mainly for AOL, since they think "Bung hole" == "Bu ngho le". *sigh*
* - 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);
};
/* im_api core stuff. */
@@ -237,7 +242,7 @@ G_MODULE_EXPORT void register_protocol( struct prpl * );
/* You will need this function in prpl->login() to get an im_connection from
* the account_t parameter. */
G_MODULE_EXPORT struct im_connection *imcb_new( account_t *acc );
-G_MODULE_EXPORT void imcb_free( struct im_connection *ic );
+G_MODULE_EXPORT void imc_free( struct im_connection *ic );
/* Once you're connected, you should call this function, so that the user will
* see the success. */
G_MODULE_EXPORT void imcb_connected( struct im_connection *ic );
@@ -250,13 +255,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
@@ -266,7 +278,7 @@ G_MODULE_EXPORT void imcb_add_buddy( struct im_connection *ic, const char *handl
G_MODULE_EXPORT void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group );
G_MODULE_EXPORT struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle );
G_MODULE_EXPORT void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname );
-G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick );
+G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick );
/* Buddy activity */
/* To manipulate the status of a handle.
@@ -301,7 +313,7 @@ G_MODULE_EXPORT void imcb_chat_topic( struct groupchat *c, char *who, char *topi
G_MODULE_EXPORT void imcb_chat_free( struct groupchat *c );
/* Actions, or whatever. */
-int imc_set_away( struct im_connection *ic, char *away );
+int imc_away_send_update( struct im_connection *ic );
int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags );
int imc_chat_msg( struct groupchat *c, char *msg, int flags );
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..f0e65f9a 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))) {
@@ -378,6 +379,8 @@ static void oscar_init(account_t *acc)
s = set_add( &acc->set, "web_aware", "false", set_eval_bool, acc );
s->flags |= ACC_SET_OFFLINE_ONLY;
}
+
+ acc->flags |= ACC_FLAG_AWAY_MESSAGE;
}
static void oscar_login(account_t *acc) {
@@ -519,6 +522,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 +575,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 +755,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;
@@ -1924,6 +1953,8 @@ static void oscar_get_away(struct im_connection *g, char *who) {
static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
{
+ if (state == NULL)
+ state = "";
if (!g_strcasecmp(state, _("Visible"))) {
aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
@@ -1931,15 +1962,16 @@ static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od,
} else if (!g_strcasecmp(state, _("Invisible"))) {
aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
return;
- } /* else... */
+ } else if (message == NULL) {
+ message = state;
+ }
if (od->rights.maxawaymsglen == 0)
imcb_error(ic, "oscar_set_away_aim called before locate rights received");
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 +1991,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")) {
+ if (state == NULL) {
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);
- } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
+ ic->away = g_strdup(msg);
+ } else {
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 +2049,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);
@@ -2251,20 +2281,21 @@ static void oscar_rem_deny(struct im_connection *ic, char *who) {
static GList *oscar_away_states(struct im_connection *ic)
{
struct oscar_data *od = ic->proto_data;
- GList *m = NULL;
- if (!od->icq)
- return g_list_append(m, GAIM_AWAY_CUSTOM);
-
- m = g_list_append(m, "Online");
- m = g_list_append(m, "Away");
- m = g_list_append(m, "Do Not Disturb");
- m = g_list_append(m, "Not Available");
- m = g_list_append(m, "Occupied");
- m = g_list_append(m, "Free For Chat");
- m = g_list_append(m, "Invisible");
-
- return m;
+ if (od->icq) {
+ static GList *m = NULL;
+ m = g_list_append(m, "Away");
+ m = g_list_append(m, "Do Not Disturb");
+ m = g_list_append(m, "Not Available");
+ m = g_list_append(m, "Occupied");
+ m = g_list_append(m, "Free For Chat");
+ m = g_list_append(m, "Invisible");
+ return m;
+ } else {
+ static GList *m = NULL;
+ m = g_list_append(m, "Away");
+ return m;
+ }
}
static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
@@ -2580,7 +2611,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..5b2ff44e 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);
}
@@ -837,55 +854,6 @@ static int is_same_bud(const void * a, const void * b) {
return strcmp(subject->id, object->id);
}
-static YList * bud_str2list(char *rawlist)
-{
- YList * l = NULL;
-
- char **lines;
- char **split;
- char **buddies;
- char **tmp, **bud;
-
- lines = y_strsplit(rawlist, "\n", -1);
- for (tmp = lines; *tmp; tmp++) {
- struct yahoo_buddy *newbud;
-
- split = y_strsplit(*tmp, ":", 2);
- if (!split)
- continue;
- if (!split[0] || !split[1]) {
- y_strfreev(split);
- continue;
- }
- buddies = y_strsplit(split[1], ",", -1);
-
- for (bud = buddies; bud && *bud; bud++) {
- newbud = y_new0(struct yahoo_buddy, 1);
- newbud->id = strdup(*bud);
- newbud->group = strdup(split[0]);
-
- if(y_list_find_custom(l, newbud, is_same_bud)) {
- FREE(newbud->id);
- FREE(newbud->group);
- FREE(newbud);
- continue;
- }
-
- newbud->real_name = NULL;
-
- l = y_list_append(l, newbud);
-
- NOTICE(("Added buddy %s to group %s", newbud->id, newbud->group));
- }
-
- y_strfreev(buddies);
- y_strfreev(split);
- }
- y_strfreev(lines);
-
- return l;
-}
-
static char * getcookie(char *rawcookie)
{
char * cookie=NULL;
@@ -1342,134 +1310,150 @@ static void yahoo_process_message(struct yahoo_input_data *yid, struct yahoo_pac
y_list_free(messages);
}
-
-static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+static void yahoo_process_status(struct yahoo_input_data *yid,
+ struct yahoo_packet *pkt)
{
YList *l;
struct yahoo_data *yd = yid->yd;
- struct user
- {
- char *name; /* 7 name */
- int state; /* 10 state */
- int flags; /* 13 flags, bit 0 = pager, bit 1 = chat, bit 2 = game */
- int mobile; /* 60 mobile */
- char *msg; /* 19 custom status message */
- int away; /* 47 away (or invisible)*/
- int buddy_session; /* 11 state */
- int f17; /* 17 in chat? then what about flags? */
- int idle; /* 137 seconds idle */
- int f138; /* 138 state */
- char *f184; /* 184 state */
- int f192; /* 192 state */
- int f10001; /* 10001 state */
- int f10002; /* 10002 state */
- int f198; /* 198 state */
- char *f197; /* 197 state */
- char *f205; /* 205 state */
- int f213; /* 213 state */
- } *u;
+ struct yahoo_process_status_entry *u;
YList *users = 0;
-
+
if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
- YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_DUPL, NULL);
+ YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
+ YAHOO_LOGIN_DUPL, NULL);
return;
}
+ /* Status updates may be spread accross multiple packets and not
+ even on buddy boundaries, so keeping some state is important.
+ So, continue where we left off, and only add a user entry to
+ the list once it's complete (301-315 End buddy). */
+ u = yd->half_user;
+
for (l = pkt->hash; l; l = l->next) {
struct yahoo_pair *pair = l->data;
switch (pair->key) {
- case 0: /* we won't actually do anything with this */
+ case 300: /* Begin buddy */
+ if (!strcmp(pair->value, "315") && !u) {
+ u = yd->half_user = y_new0(struct yahoo_process_status_entry, 1);
+ }
+ break;
+ case 301: /* End buddy */
+ if (!strcmp(pair->value, "315") && u) {
+ users = y_list_prepend(users, u);
+ u = yd->half_user = NULL;
+ }
+ break;
+ case 0: /* we won't actually do anything with this */
NOTICE(("key %d:%s", pair->key, pair->value));
break;
- case 1: /* we don't get the full buddy list here. */
+ case 1: /* we don't get the full buddy list here. */
if (!yd->logged_in) {
- yd->logged_in = TRUE;
- if(yd->current_status < 0)
+ yd->logged_in = 1;
+ if (yd->current_status < 0)
yd->current_status = yd->initial_status;
- YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
+ YAHOO_CALLBACK(ext_yahoo_login_response) (yd->
+ client_id, YAHOO_LOGIN_OK, NULL);
}
break;
- case 8: /* how many online buddies we have */
+ case 8: /* how many online buddies we have */
NOTICE(("key %d:%s", pair->key, pair->value));
break;
- case 7: /* the current buddy */
- u = y_new0(struct user, 1);
+ case 7: /* the current buddy */
+ if (!u) {
+ /* This will only happen in case of a single level message */
+ u = y_new0(struct yahoo_process_status_entry, 1);
+ users = y_list_prepend(users, u);
+ }
u->name = pair->value;
- users = y_list_prepend(users, u);
break;
- case 10: /* state */
- ((struct user*)users->data)->state = strtol(pair->value, NULL, 10);
+ case 10: /* state */
+ u->state = strtol(pair->value, NULL, 10);
break;
- case 19: /* custom status message */
- ((struct user*)users->data)->msg = pair->value;
+ case 19: /* custom status message */
+ u->msg = pair->value;
break;
- case 47: /* is it an away message or not */
- ((struct user*)users->data)->away = atoi(pair->value);
+ case 47: /* is it an away message or not. Not applicable for YMSG16 anymore */
+ u->away = atoi(pair->value);
break;
- case 137: /* seconds idle */
- ((struct user*)users->data)->idle = atoi(pair->value);
+ case 137: /* seconds idle */
+ u->idle = atoi(pair->value);
break;
- case 11: /* this is the buddy's session id */
- ((struct user*)users->data)->buddy_session = atoi(pair->value);
+ case 11: /* this is the buddy's session id */
+ u->buddy_session = atoi(pair->value);
break;
- case 17: /* in chat? */
- ((struct user*)users->data)->f17 = atoi(pair->value);
+ case 17: /* in chat? */
+ u->f17 = atoi(pair->value);
break;
- case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
- ((struct user*)users->data)->flags = atoi(pair->value);
+ case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
+ u->flags = atoi(pair->value);
break;
- case 60: /* SMS -> 1 MOBILE USER */
+ case 60: /* SMS -> 1 MOBILE USER */
/* sometimes going offline makes this 2, but invisible never sends it */
- ((struct user*)users->data)->mobile = atoi(pair->value);
+ u->mobile = atoi(pair->value);
break;
case 138:
- ((struct user*)users->data)->f138 = atoi(pair->value);
+ u->f138 = atoi(pair->value);
break;
case 184:
- ((struct user*)users->data)->f184 = pair->value;
+ u->f184 = pair->value;
break;
case 192:
- ((struct user*)users->data)->f192 = atoi(pair->value);
+ u->f192 = atoi(pair->value);
break;
case 10001:
- ((struct user*)users->data)->f10001 = atoi(pair->value);
+ u->f10001 = atoi(pair->value);
break;
case 10002:
- ((struct user*)users->data)->f10002 = atoi(pair->value);
+ u->f10002 = atoi(pair->value);
break;
case 198:
- ((struct user*)users->data)->f198 = atoi(pair->value);
+ u->f198 = atoi(pair->value);
break;
case 197:
- ((struct user*)users->data)->f197 = pair->value;
+ u->f197 = pair->value;
break;
case 205:
- ((struct user*)users->data)->f205 = pair->value;
+ u->f205 = pair->value;
break;
case 213:
- ((struct user*)users->data)->f213 = atoi(pair->value);
+ u->f213 = atoi(pair->value);
break;
- case 16: /* Custom error message */
- YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, pair->value, 0, E_CUSTOM);
+ case 16: /* Custom error message */
+ YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id,
+ pair->value, 0, E_CUSTOM);
break;
default:
- WARNING(("unknown status key %d:%s", pair->key, pair->value));
+ WARNING(("unknown status key %d:%s", pair->key,
+ pair->value));
break;
}
}
-
+
while (users) {
YList *t = users;
- struct user *u = users->data;
+ struct yahoo_process_status_entry *u = users->data;
if (u->name != NULL) {
- if (pkt->service == YAHOO_SERVICE_LOGOFF || u->flags == 0) {
- YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);
+ if (pkt->service ==
+ YAHOO_SERVICE_LOGOFF
+ /*|| u->flags == 0 No flags for YMSG16 */ ) {
+ YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->
+ client_id, u->name,
+ YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);
} else {
- YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, u->state, u->msg, u->away, u->idle, u->mobile);
+ /* 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,88 +1463,130 @@ static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_pack
}
}
-static void yahoo_process_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+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;
- 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);
- }
-
+ /* 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 87: /* buddies */
- if(!yd->rawbuddylist)
- yd->rawbuddylist = strdup(pair->value);
- else {
- yd->rawbuddylist = y_string_append(yd->rawbuddylist, pair->value);
- }
+ switch (pair->key) {
+ case 300:
+ case 301:
+ case 302:
+ break; /* Separators. Our logic does not need them */
+ case 303:
+ if (318 == atoi(pair->value))
+ last_packet = 1;
break;
+ case 65:
+ 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 if (yd->buddies) {
+ struct yahoo_buddy *lastbud =
+ (struct yahoo_buddy *)y_list_nth(yd->
+ buddies,
+ y_list_length(yd->buddies) - 1)->data;
+ newbud->group = strdup(lastbud->group);
+ } else
+ newbud->group = strdup("Buddies");
+
+ yd->buddies = y_list_append(yd->buddies, newbud);
- case 88: /* ignore list */
- if(!yd->ignorelist)
- yd->ignorelist = strdup("Ignore:");
- yd->ignorelist = y_string_append(yd->ignorelist, pair->value);
break;
+ }
+ }
+
+ /* we could be getting multiple packets here */
+ if (pkt->hash && !last_packet)
+ return;
+
+ YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, yd->buddies);
- case 89: /* identities */
+ /* Logged in */
+ if (!yd->logged_in) {
+ yd->logged_in = 1;
+ if (yd->current_status < 0)
+ yd->current_status = yd->initial_status;
+ YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
+ YAHOO_LOGIN_OK, NULL);
+
+ /*
+ yahoo_set_away(yd->client_id, yd->initial_status, NULL,
+ (yd->initial_status == YAHOO_STATUS_AVAILABLE) ? 0 : 1);
+
+ yahoo_get_yab(yd->client_id);
+ */
+ }
+
+}
+
+static void yahoo_process_list(struct yahoo_input_data *yid,
+ struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ YList *l;
+
+ /* 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 89: /* identities */
{
- char **identities = y_strsplit(pair->value, ",", -1);
- int i;
- for(i=0; identities[i]; i++)
- yd->identities = y_list_append(yd->identities,
+ char **identities =
+ y_strsplit(pair->value, ",", -1);
+ int i;
+ for (i = 0; identities[i]; i++)
+ yd->identities =
+ y_list_append(yd->identities,
strdup(identities[i]));
- y_strfreev(identities);
+ y_strfreev(identities);
}
- YAHOO_CALLBACK(ext_yahoo_got_identities)(yd->client_id, yd->identities);
+ YAHOO_CALLBACK(ext_yahoo_got_identities) (yd->client_id,
+ yd->identities);
break;
- case 59: /* cookies */
- if(yd->ignorelist) {
- yd->ignore = bud_str2list(yd->ignorelist);
- FREE(yd->ignorelist);
- YAHOO_CALLBACK(ext_yahoo_got_ignore)(yd->client_id, yd->ignore);
- }
- if(yd->rawbuddylist) {
- yd->buddies = bud_str2list(yd->rawbuddylist);
- FREE(yd->rawbuddylist);
- YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
- }
-
- if(pair->value[0]=='Y') {
+ case 59: /* cookies */
+ if (pair->value[0] == 'Y') {
FREE(yd->cookie_y);
FREE(yd->login_cookie);
yd->cookie_y = getcookie(pair->value);
yd->login_cookie = getlcookie(yd->cookie_y);
- } else if(pair->value[0]=='T') {
+ } else if (pair->value[0] == 'T') {
FREE(yd->cookie_t);
yd->cookie_t = getcookie(pair->value);
- } else if(pair->value[0]=='C') {
+ } else if (pair->value[0] == 'C') {
FREE(yd->cookie_c);
yd->cookie_c = getcookie(pair->value);
- }
-
- if(yd->cookie_y && yd->cookie_t && yd->cookie_c)
- YAHOO_CALLBACK(ext_yahoo_got_cookies)(yd->client_id);
+ }
break;
- case 3: /* my id */
- case 90: /* 1 */
- case 100: /* 0 */
- case 101: /* NULL */
- case 102: /* NULL */
- case 93: /* 86400/1440 */
+ case 3: /* my id */
+ case 90: /* 1 */
+ case 100: /* 0 */
+ case 101: /* NULL */
+ case 102: /* NULL */
+ case 93: /* 86400/1440 */
break;
}
}
+
+ if (yd->cookie_y && yd->cookie_t) /* We don't get cookie_c anymore */
+ YAHOO_CALLBACK(ext_yahoo_got_cookies) (yd->client_id);
}
static void yahoo_process_verify(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
@@ -2225,6 +2251,204 @@ 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;
+ struct yahoo_data *yd;
+ int st;
+
+ if (y_list_find(inputs, had->yid) == NULL)
+ return;
+
+ yid = had->yid;
+ yd = yid->yd;
+
+ 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;
+ struct yahoo_data *yd;
+ struct yahoo_packet *pack;
+ char *crumb = NULL;
+ int st;
+
+ if (y_list_find(inputs, had->yid) == NULL)
+ return;
+
+ yid = had->yid;
+ yd = yid->yd;
+
+ 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 +2477,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 +2634,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 +2643,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 +2874,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 +2896,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 +2908,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 +2923,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 +2964,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 +2995,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 +3008,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 +3793,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 +3811,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 +4014,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,46 +4029,40 @@ 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)
return;
yd = yid->yd;
+ old_status = yd->current_status;
+ yd->current_status = state;
- if (msg) {
- yd->current_status = YAHOO_STATUS_CUSTOM;
- } else {
- yd->current_status = state;
- }
+ /* 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);
- if (yd->current_status == YAHOO_STATUS_AVAILABLE)
- service = YAHOO_SERVICE_ISBACK;
- else
- service = YAHOO_SERVICE_ISAWAY;
-
- 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");
- }
-
-
-
+ 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);
+ yahoo_packet_hash(pkt, 19, msg && state == YAHOO_STATUS_CUSTOM ? msg : "");
+ 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 +4077,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 +4313,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 +4354,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 197d76a1..a47de966 100644
--- a/protocols/yahoo/yahoo.c
+++ b/protocols/yahoo/yahoo.c
@@ -129,6 +129,8 @@ static char *byahoo_strip( const char *in )
static void byahoo_init( account_t *acc )
{
set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
+
+ acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE;
}
static void byahoo_login( account_t *acc )
@@ -197,27 +199,11 @@ static void byahoo_set_away( struct im_connection *ic, char *state, char *msg )
{
struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data;
- ic->away = NULL;
-
- if( state && msg && g_strcasecmp( state, msg ) != 0 )
- {
- yd->current_status = YAHOO_STATUS_CUSTOM;
- ic->away = "";
- }
- else if( state )
+ if( state && msg == NULL )
{
- /* Set msg to NULL since (if it isn't NULL already) it's equal
- to state. msg must be empty if we want to use an existing
- away state. */
- msg = NULL;
-
- ic->away = "";
- if( g_strcasecmp( state, "Available" ) == 0 )
- {
- yd->current_status = YAHOO_STATUS_AVAILABLE;
- ic->away = NULL;
- }
- else if( g_strcasecmp( state, "Be Right Back" ) == 0 )
+ /* Use these states only if msg doesn't contain additional
+ info since away messages are only supported with CUSTOM. */
+ if( g_strcasecmp( state, "Be Right Back" ) == 0 )
yd->current_status = YAHOO_STATUS_BRB;
else if( g_strcasecmp( state, "Busy" ) == 0 )
yd->current_status = YAHOO_STATUS_BUSY;
@@ -237,35 +223,34 @@ static void byahoo_set_away( struct im_connection *ic, char *state, char *msg )
yd->current_status = YAHOO_STATUS_STEPPEDOUT;
else if( g_strcasecmp( state, "Invisible" ) == 0 )
yd->current_status = YAHOO_STATUS_INVISIBLE;
- else if( g_strcasecmp( state, GAIM_AWAY_CUSTOM ) == 0 )
- {
- yd->current_status = YAHOO_STATUS_AVAILABLE;
-
- ic->away = NULL;
- }
+ else
+ yd->current_status = YAHOO_STATUS_CUSTOM;
}
+ else if( state )
+ yd->current_status = YAHOO_STATUS_CUSTOM;
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, state ? 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, "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" );
+ }
return m;
}
@@ -346,6 +331,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 +370,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 +452,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 )
@@ -664,9 +664,6 @@ void ext_yahoo_error( int id, const char *err, int fatal, int num )
struct im_connection *ic = byahoo_get_ic_by_id( id );
imcb_error( ic, "%s", err );
-
- if( fatal )
- imc_logout( ic, TRUE );
}
/* TODO: Clear up the mess of inp and d structures */
@@ -792,9 +789,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 );
}
@@ -910,11 +920,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..f05acb3c 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
@@ -182,6 +195,8 @@ struct yahoo_data {
char *ignorelist;
void *server_settings;
+
+ struct yahoo_process_status_entry *half_user;
};
struct yab {
@@ -247,6 +262,27 @@ struct yahoo_chat_member {
char *location;
};
+struct yahoo_process_status_entry {
+ char *name; /* 7 name */
+ int state; /* 10 state */
+ int flags; /* 13 flags, bit 0 = pager, bit 1 = chat, bit 2 = game */
+ int mobile; /* 60 mobile */
+ char *msg; /* 19 custom status message */
+ int away; /* 47 away (or invisible) */
+ int buddy_session; /* 11 state */
+ int f17; /* 17 in chat? then what about flags? */
+ int idle; /* 137 seconds idle */
+ int f138; /* 138 state */
+ char *f184; /* 184 state */
+ int f192; /* 192 state */
+ int f10001; /* 10001 state */
+ int f10002; /* 10002 state */
+ int f198; /* 198 state */
+ char *f197; /* 197 state */
+ char *f205; /* 205 state */
+ int f213; /* 213 state */
+};
+
#ifdef __cplusplus
}
#endif
diff --git a/root_commands.c b/root_commands.c
index f55c4b5e..b3432b9b 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,9 +140,15 @@ 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 };
+ if( strchr( irc->umode, 'R' ) != NULL )
+ {
+ irc_usermsg( irc, "You're already logged in." );
+ return;
+ }
+
switch (status) {
case STORAGE_INVALID_PASSWORD:
irc_usermsg( irc, "Incorrect password" );
@@ -142,6 +158,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 +179,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 +255,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 +394,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 +416,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 +546,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;
+ MIN_ARGS( 2 );
- if( !cmd[2] )
- {
- irc_usermsg( irc, "Not enough parameters given (need %d)", 2 );
- return;
- }
-
- if( g_strncasecmp( cmd[2], "-del", 4 ) == 0 )
- acc_handle = g_strdup( cmd[3] );
- else
- acc_handle = g_strdup( cmd[2] );
-
- if( !acc_handle )
- {
- irc_usermsg( irc, "Not enough parameters given (need %d)", 3 );
- return;
- }
-
- if( ( tmp = strchr( acc_handle, '/' ) ) )
- {
- *tmp = 0;
- set_name = tmp + 1;
- }
-
- if( ( a = account_get( irc, acc_handle ) ) == NULL )
- {
- g_free( acc_handle );
- irc_usermsg( irc, "Invalid account" );
- return;
- }
-
- if( cmd[3] && set_name )
- {
- set_t *s = set_find( &a->set, set_name );
-
- 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 +563,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 +674,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 +699,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 +883,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,73 +1006,119 @@ static void cmd_qlist( irc_t *irc, char **cmd )
static void cmd_join_chat( irc_t *irc, char **cmd )
{
- account_t *a;
- struct im_connection *ic;
- char *chat, *channel, *nick = NULL, *password = NULL;
- struct groupchat *c;
+ 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( !( a = account_get( irc, cmd[1] ) ) )
- {
- irc_usermsg( irc, "Invalid account" );
- return;
- }
- else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) )
- {
- irc_usermsg( irc, "That account is not on-line" );
- return;
- }
- else if( a->prpl->chat_join == NULL )
- {
- irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
- return;
- }
- ic = a->ic;
+ 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;
- chat = cmd[2];
- if( cmd[3] )
+ if( g_strcasecmp( cmd[1], "add" ) == 0 )
{
- if( cmd[3][0] != '#' && cmd[3][0] != '&' )
- channel = g_strdup_printf( "&%s", cmd[3] );
- else
+ 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
+ else if( g_strcasecmp( cmd[1], "list" ) == 0 )
{
- char *s;
+ int i = 0;
- channel = g_strdup_printf( "&%s", chat );
- if( ( s = strchr( channel, '@' ) ) )
- *s = 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" );
}
- if( cmd[3] && cmd[4] )
- nick = cmd[4];
- else
- nick = irc->nick;
- if( cmd[3] && cmd[4] && cmd[5] )
- password = cmd[5];
-
- if( !nick_ok( channel + 1 ) )
+ else if( g_strcasecmp( cmd[1], "set" ) == 0 )
{
- irc_usermsg( irc, "Invalid channel name: %s", channel );
- g_free( channel );
- return;
+ MIN_ARGS( 2 );
+
+ cmd_set_real( irc, cmd + 1, cmd_chat_set_findhead, NULL );
}
- else if( g_strcasecmp( channel, irc->channel ) == 0 || irc_chat_by_channel( irc, channel ) )
+ else if( g_strcasecmp( cmd[1], "del" ) == 0 )
{
- irc_usermsg( irc, "Channel already exists: %s", channel );
- g_free( channel );
- return;
+ MIN_ARGS( 2 );
+
+ if( ( c = chat_get( irc, cmd[2] ) ) )
+ {
+ chat_del( irc, c );
+ }
+ else
+ {
+ irc_usermsg( irc, "Could not remove chat." );
+ }
}
-
- if( ( c = a->prpl->chat_join( ic, chat, nick, password ) ) )
+ else if( g_strcasecmp( cmd[1], "with" ) == 0 )
{
- g_free( c->channel );
- c->channel = channel;
+ 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, "Tried to join chat, not sure if this was successful" );
- g_free( channel );
+ irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] );
}
}
@@ -1063,5 +1142,6 @@ 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 },
{ 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/unix.c b/unix.c
index 56b0ab46..7088d0f8 100644
--- a/unix.c
+++ b/unix.c
@@ -24,11 +24,15 @@
*/
#include "bitlbee.h"
+
+#include "arc.h"
+#include "base64.h"
#include "commands.h"
-#include "crypting.h"
#include "protocols/nogaim.h"
#include "help.h"
#include "ipc.h"
+#include "md5.h"
+#include "misc.h"
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
@@ -39,12 +43,17 @@ global_t global; /* Against global namespace pollution */
static void sighandler( int signal );
+static int crypt_main( int argc, char *argv[] );
+
int main( int argc, char *argv[] )
{
int i = 0;
char *old_cwd = NULL;
struct sigaction sig, old;
+ if( argc > 1 && strcmp( argv[1], "-x" ) == 0 )
+ return crypt_main( argc, argv );
+
log_init();
global.conf_file = g_strdup( CONF_FILE_DEF );
global.conf = conf_load( argc, argv );
@@ -158,6 +167,64 @@ int main( int argc, char *argv[] )
return( 0 );
}
+static int crypt_main( int argc, char *argv[] )
+{
+ int pass_len;
+ unsigned char *pass_cr, *pass_cl;
+
+ if( argc < 4 || ( strcmp( argv[2], "hash" ) != 0 &&
+ strcmp( argv[2], "unhash" ) != 0 && argc < 5 ) )
+ {
+ printf( "Supported:\n"
+ " %s -x enc <key> <cleartext password>\n"
+ " %s -x dec <key> <encrypted password>\n"
+ " %s -x hash <cleartext password>\n"
+ " %s -x unhash <hashed password>\n"
+ " %s -x chkhash <hashed password> <cleartext password>\n",
+ argv[0], argv[0], argv[0], argv[0], argv[0] );
+ }
+ else if( strcmp( argv[2], "enc" ) == 0 )
+ {
+ pass_len = arc_encode( argv[4], strlen( argv[4] ), (unsigned char**) &pass_cr, argv[3], 12 );
+ printf( "%s\n", base64_encode( pass_cr, pass_len ) );
+ }
+ else if( strcmp( argv[2], "dec" ) == 0 )
+ {
+ pass_len = base64_decode( argv[4], (unsigned char**) &pass_cr );
+ arc_decode( pass_cr, pass_len, (char**) &pass_cl, argv[3] );
+ printf( "%s\n", pass_cl );
+ }
+ else if( strcmp( argv[2], "hash" ) == 0 )
+ {
+ md5_byte_t pass_md5[21];
+ md5_state_t md5_state;
+
+ random_bytes( pass_md5 + 16, 5 );
+ md5_init( &md5_state );
+ md5_append( &md5_state, (md5_byte_t*) argv[3], strlen( argv[3] ) );
+ md5_append( &md5_state, pass_md5 + 16, 5 ); /* Add the salt. */
+ md5_finish( &md5_state, pass_md5 );
+
+ printf( "%s\n", base64_encode( pass_md5, 21 ) );
+ }
+ else if( strcmp( argv[2], "unhash" ) == 0 )
+ {
+ printf( "Hash %s submitted to a massive Beowulf cluster of\n"
+ "overclocked 486s. Expect your answer next year somewhere around this time. :-)\n", argv[3] );
+ }
+ else if( strcmp( argv[2], "chkhash" ) == 0 )
+ {
+ char *hash = strncmp( argv[3], "md5:", 4 ) == 0 ? argv[3] + 4 : argv[3];
+ int st = md5_verify_password( argv[4], hash );
+
+ printf( "Hash %s given password.\n", st == 0 ? "matches" : "does not match" );
+
+ return st;
+ }
+
+ return 0;
+}
+
static void sighandler( int signal )
{
/* FIXME: Calling log_message() here is not a very good idea! */
@@ -213,5 +280,3 @@ double gettime()
gettimeofday( time, 0 );
return( (double) time->tv_sec + (double) time->tv_usec / 1000000 );
}
-
-