diff options
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | account.c | 82 | ||||
-rw-r--r-- | account.h | 10 | ||||
-rw-r--r-- | bitlbee.c | 4 | ||||
-rw-r--r-- | bitlbee.h | 3 | ||||
-rw-r--r-- | conf.c | 7 | ||||
-rwxr-xr-x | configure | 186 | ||||
-rw-r--r-- | doc/bitlbee.schema | 62 | ||||
-rw-r--r-- | doc/user-guide/commands.xml | 263 | ||||
-rw-r--r-- | doc/user-guide/help.xsl | 2 | ||||
-rw-r--r-- | irc.c | 122 | ||||
-rw-r--r-- | irc.h | 2 | ||||
-rw-r--r-- | irc_commands.c | 19 | ||||
-rw-r--r-- | lib/Makefile | 37 | ||||
-rw-r--r-- | lib/base64.c | 153 | ||||
-rw-r--r-- | lib/base64.h | 33 | ||||
-rw-r--r-- | lib/events.h (renamed from protocols/events.h) | 0 | ||||
-rw-r--r-- | lib/events_glib.c (renamed from protocols/events_glib.c) | 0 | ||||
-rw-r--r-- | lib/events_libevent.c (renamed from protocols/events_libevent.c) | 0 | ||||
-rw-r--r-- | lib/http_client.c (renamed from protocols/http_client.c) | 0 | ||||
-rw-r--r-- | lib/http_client.h (renamed from protocols/http_client.h) | 0 | ||||
-rw-r--r-- | lib/ini.c (renamed from ini.c) | 0 | ||||
-rw-r--r-- | lib/ini.h (renamed from ini.h) | 0 | ||||
-rw-r--r-- | lib/md5.c (renamed from protocols/md5.c) | 0 | ||||
-rw-r--r-- | lib/md5.h (renamed from protocols/md5.h) | 0 | ||||
-rw-r--r-- | lib/misc.c (renamed from util.c) | 165 | ||||
-rw-r--r-- | lib/misc.h (renamed from util.h) | 14 | ||||
-rw-r--r-- | lib/proxy.c (renamed from protocols/proxy.c) | 1 | ||||
-rw-r--r-- | lib/proxy.h (renamed from protocols/proxy.h) | 0 | ||||
-rw-r--r-- | lib/rc4.c | 189 | ||||
-rw-r--r-- | lib/rc4.h | 34 | ||||
-rw-r--r-- | lib/sha.c (renamed from protocols/sha.c) | 0 | ||||
-rw-r--r-- | lib/sha.h (renamed from protocols/sha.h) | 0 | ||||
-rw-r--r-- | lib/ssl_bogus.c (renamed from protocols/ssl_bogus.c) | 0 | ||||
-rw-r--r-- | lib/ssl_client.h (renamed from protocols/ssl_client.h) | 0 | ||||
-rw-r--r-- | lib/ssl_gnutls.c (renamed from protocols/ssl_gnutls.c) | 0 | ||||
-rw-r--r-- | lib/ssl_nss.c (renamed from protocols/ssl_nss.c) | 0 | ||||
-rw-r--r-- | lib/ssl_openssl.c (renamed from protocols/ssl_openssl.c) | 0 | ||||
-rw-r--r-- | lib/url.c (renamed from url.c) | 0 | ||||
-rw-r--r-- | lib/url.h (renamed from url.h) | 0 | ||||
-rw-r--r-- | nick.c | 111 | ||||
-rw-r--r-- | nick.h | 14 | ||||
-rw-r--r-- | protocols/Makefile | 2 | ||||
-rw-r--r-- | protocols/jabber/jabber.c | 106 | ||||
-rw-r--r-- | protocols/msn/msn.c | 98 | ||||
-rw-r--r-- | protocols/msn/ns.c | 16 | ||||
-rw-r--r-- | protocols/nogaim.c | 153 | ||||
-rw-r--r-- | protocols/nogaim.h | 29 | ||||
-rw-r--r-- | protocols/oscar/oscar.c | 53 | ||||
-rw-r--r-- | protocols/yahoo/libyahoo2.c | 30 | ||||
-rw-r--r-- | protocols/yahoo/yahoo.c | 11 | ||||
-rw-r--r-- | query.c | 4 | ||||
-rw-r--r-- | root_commands.c | 175 | ||||
-rw-r--r-- | set.c | 143 | ||||
-rw-r--r-- | set.h | 43 | ||||
-rw-r--r-- | storage.c | 15 | ||||
-rw-r--r-- | storage.h | 4 | ||||
-rw-r--r-- | storage_ldap.c | 177 | ||||
-rw-r--r-- | storage_text.c | 239 | ||||
-rw-r--r-- | storage_xml.c | 517 | ||||
-rw-r--r-- | unix.c | 14 | ||||
-rw-r--r-- | user.c | 28 |
62 files changed, 2321 insertions, 1055 deletions
@@ -9,9 +9,9 @@ -include Makefile.settings # Program variables -objects = account.o bitlbee.o conf.o crypting.o help.o ini.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_text.o unix.o url.o user.o util.o +objects = 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_OBJS) unix.o user.o headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ini.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h url.h user.h protocols/http_client.h protocols/md5.h protocols/nogaim.h protocols/proxy.h protocols/sha.h protocols/ssl_client.h -subdirs = protocols +subdirs = protocols lib # Expansion of variables subdirobjs = $(foreach dir,$(subdirs),$(dir)/$(dir).o) @@ -41,7 +41,7 @@ clean: $(subdirs) rm -f *.o $(OUTFILE) core utils/bitlbeed encode decode distclean: clean $(subdirs) - rm -f Makefile.settings config.h + rm -f Makefile.settings config.h bitlbee.pc find . -name 'DEADJOE' -o -name '*.orig' -o -name '*.rej' -o -name '*~' -exec rm -f {} \; check: @@ -30,11 +30,12 @@ account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) { account_t *a; + set_t *s; if( irc->accounts ) { for( a = irc->accounts; a->next; a = a->next ); - a = a->next = g_new0 ( account_t, 1 ); + a = a->next = g_new0( account_t, 1 ); } else { @@ -44,11 +45,70 @@ account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) a->prpl = prpl; a->user = g_strdup( user ); a->pass = g_strdup( pass ); + a->auto_connect = 1; a->irc = irc; + s = set_add( &a->set, "auto_connect", "true", set_eval_account, a ); + s->flags |= ACC_SET_NOSAVE; + + s = set_add( &a->set, "password", NULL, set_eval_account, a ); + s->flags |= ACC_SET_NOSAVE; + + s = set_add( &a->set, "username", NULL, set_eval_account, a ); + s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; + set_setstr( &a->set, "username", user ); + + a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free ); + + /* This function adds some more settings (and might want to do more + things that have to be done now, although I can't think of anything. */ + if( prpl->acc_init ) + prpl->acc_init( a ); + return( a ); } +char *set_eval_account( set_t *set, char *value ) +{ + account_t *acc = set->data; + + /* Double-check: We refuse to edit on-line accounts. */ + if( set->flags & ACC_SET_OFFLINE_ONLY && acc->gc ) + return NULL; + + if( strcmp( set->key, "username" ) == 0 ) + { + g_free( acc->user ); + acc->user = g_strdup( value ); + return 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 ) + acc->server = g_strdup( value ); + else + acc->server = NULL; + return value; + } + else if( strcmp( set->key, "auto_connect" ) == 0 ) + { + if( !is_bool( value ) ) + return NULL; + + acc->auto_connect = bool2int( value ); + return value; + } + + return NULL; +} + account_t *account_get( irc_t *irc, char *id ) { account_t *a, *ret = NULL; @@ -67,7 +127,7 @@ account_t *account_get( irc_t *irc, char *id ) { for( a = irc->accounts; a; a = a->next ) if( a->prpl == proto && - a->prpl->cmp_buddynames( handle, a->user ) == 0 ) + a->prpl->handle_cmp( handle, a->user ) == 0 ) ret = a; } @@ -128,6 +188,11 @@ void account_del( irc_t *irc, account_t *acc ) irc->accounts = a->next; } + while( a->set ) + set_del( &a->set, a->set->key ); + + g_hash_table_destroy( a->nicks ); + g_free( a->user ); g_free( a->pass ); if( a->server ) g_free( a->server ); @@ -141,8 +206,6 @@ void account_del( irc_t *irc, account_t *acc ) void account_on( irc_t *irc, account_t *a ) { - struct aim_user *u; - if( a->gc ) { /* Trying to enable an already-enabled account */ @@ -151,17 +214,8 @@ void account_on( irc_t *irc, account_t *a ) cancel_auto_reconnect( a ); - u = g_new0 ( struct aim_user, 1 ); - u->irc = irc; - u->prpl = a->prpl; - strncpy( u->username, a->user, sizeof( u->username ) - 1 ); - strncpy( u->password, a->pass, sizeof( u->password ) - 1 ); - if( a->server) strncpy( u->proto_opt[0], a->server, sizeof( u->proto_opt[0] ) - 1 ); - - a->gc = (struct gaim_connection *) u; /* Bit hackish :-/ */ a->reconnect = 0; - - a->prpl->login( u ); + a->prpl->login( a ); } void account_off( irc_t *irc, account_t *a ) @@ -33,8 +33,12 @@ typedef struct account char *pass; char *server; + int auto_connect; int reconnect; + set_t *set; + GHashTable *nicks; + struct irc *irc; struct gaim_connection *gc; struct account *next; @@ -46,4 +50,10 @@ void account_del( irc_t *irc, account_t *acc ); 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 ); + +#define ACC_SET_NOSAVE 1 +#define ACC_SET_OFFLINE_ONLY 2 +#define ACC_SET_ONLINE_ONLY 4 + #endif @@ -309,6 +309,10 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition { irc_t *irc; + /* Since we're fork()ing here, let's make sure we won't + get the same random numbers as the parent/siblings. */ + srand( time( NULL ) ^ getpid() ); + /* Close the listening socket, we're a client. */ close( global.listen_socket ); b_event_remove( global.listen_watch_source_id ); @@ -123,13 +123,14 @@ extern char *CONF_FILE; #include "nogaim.h" #include "commands.h" #include "account.h" +#include "nick.h" #include "conf.h" #include "log.h" #include "ini.h" #include "help.h" #include "query.h" #include "sock.h" -#include "util.h" +#include "misc.h" #include "proxy.h" typedef struct global { @@ -33,7 +33,7 @@ #include "url.h" #include "ipc.h" -#include "protocols/proxy.h" +#include "proxy.h" char *CONF_FILE; @@ -54,7 +54,8 @@ conf_t *conf_load( int argc, char *argv[] ) conf->port = 6667; conf->nofork = 0; conf->verbose = 0; - conf->primary_storage = "text"; + conf->primary_storage = "xml"; + conf->migrate_storage = g_strsplit( "text", ",", -1 ); conf->runmode = RUNMODE_INETD; conf->authmode = AUTHMODE_OPEN; conf->auth_pass = NULL; @@ -321,7 +322,7 @@ void conf_loaddefaults( irc_t *irc ) { if( g_strcasecmp( ini->section, "defaults" ) == 0 ) { - set_t *s = set_find( irc, ini->key ); + set_t *s = set_find( &irc->set, ini->key ); if( s ) { @@ -30,6 +30,7 @@ strip=1 ipv6=1 events=glib +ldap=auto ssl=auto arch=`uname -s` @@ -66,6 +67,8 @@ Option Description Default --ipv6=0/1 IPv6 socket support $ipv6 +--ldap=0/1/auto LDAP support $ldap + --events=... Event handler (glib, libevent) $events --ssl=... SSL library to use (gnutls, nss, openssl, bogus, auto) $ssl @@ -140,21 +143,23 @@ else echo 'CFLAGS=-O3' >> Makefile.settings fi -echo CFLAGS+=-I`pwd` -I`pwd`/protocols -I. >> Makefile.settings +echo CFLAGS+=-I`pwd` -I`pwd`/lib -I`pwd`/protocols -I. >> Makefile.settings echo CFLAGS+=-DHAVE_CONFIG_H >> Makefile.settings if [ -n "$CC" ]; then - echo "CC=$CC" >> Makefile.settings; + CC=$CC elif type gcc > /dev/null 2> /dev/null; then - echo "CC=gcc" >> Makefile.settings; + CC=gcc elif type cc > /dev/null 2> /dev/null; then - echo "CC=cc" >> Makefile.settings; + CC=cc else echo 'Cannot find a C compiler, aborting.' exit 1; fi +echo "CC=$CC" >> Makefile.settings; + if [ -n "$LD" ]; then echo "LD=$LD" >> Makefile.settings; elif type ld > /dev/null 2> /dev/null; then @@ -231,66 +236,106 @@ EOF fi; } -if [ "$msn" = 1 -o "$jabber" = 1 ]; then - if [ "$ssl" = "auto" ]; then - detect_gnutls - if [ "$ret" = "0" ]; then - detect_nss - fi; - elif [ "$ssl" = "gnutls" ]; then - detect_gnutls; - elif [ "$ssl" = "nss" ]; then - detect_nss; - elif [ "$ssl" = "openssl" ]; then - echo - echo 'No detection code exists for OpenSSL. Make sure that you have a complete' - echo 'install of OpenSSL (including devel/header files) before reporting' - echo 'compilation problems.' - echo - echo 'Also, keep in mind that the OpenSSL is, according to some people, not' - echo 'completely GPL-compatible. Using GnuTLS or NSS is recommended and better' - echo 'supported by us. However, on many BSD machines, OpenSSL can be considered' - echo 'part of the operating system, which makes it GPL-compatible.' - echo - echo 'For more info, see: http://www.openssl.org/support/faq.html#LEGAL2' - echo ' http://www.gnome.org/~markmc/openssl-and-the-gpl.html' - echo - echo 'Please note that distributing a BitlBee binary which links to OpenSSL is' - echo 'probably illegal. If you want to create and distribute a binary BitlBee' - echo 'package, you really should use GnuTLS or NSS instead.' - echo - echo 'Also, the OpenSSL license requires us to say this:' - echo ' * "This product includes software developed by the OpenSSL Project' - echo ' * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"' - - echo 'EFLAGS+=-lssl -lcrypto' >> Makefile.settings - - ret=1; - elif [ "$ssl" = "bogus" ]; then - echo - echo 'Using bogus SSL code. This will not make the MSN module work, but it will' - echo 'allow you to use the Jabber module - although without working SSL support.' - - ret=1; +detect_ldap() +{ + TMPFILE=`mktemp` + if $CC -o $TMPFILE -shared -lldap 2>/dev/null >/dev/null; then + cat<<EOF>>Makefile.settings +EFLAGS+=-lldap +CFLAGS+= +EOF + ldap=1 + rm -f $TMPFILE + ret=1 else - echo - echo 'ERROR: Unknown SSL library specified.' - exit 1; + ldap=0 + ret=0 fi - +} + +if [ "$ssl" = "auto" ]; then + detect_gnutls if [ "$ret" = "0" ]; then + detect_nss + fi +elif [ "$ssl" = "gnutls" ]; then + detect_gnutls +elif [ "$ssl" = "nss" ]; then + detect_nss +elif [ "$ssl" = "openssl" ]; then + echo + echo 'No detection code exists for OpenSSL. Make sure that you have a complete' + echo 'install of OpenSSL (including devel/header files) before reporting' + echo 'compilation problems.' + echo + echo 'Also, keep in mind that the OpenSSL is, according to some people, not' + echo 'completely GPL-compatible. Using GnuTLS or NSS is recommended and better' + echo 'supported by us. However, on many BSD machines, OpenSSL can be considered' + echo 'part of the operating system, which makes it GPL-compatible.' + echo + echo 'For more info, see: http://www.openssl.org/support/faq.html#LEGAL2' + echo ' http://www.gnome.org/~markmc/openssl-and-the-gpl.html' + echo + echo 'Please note that distributing a BitlBee binary which links to OpenSSL is' + echo 'probably illegal. If you want to create and distribute a binary BitlBee' + echo 'package, you really should use GnuTLS or NSS instead.' + echo + echo 'Also, the OpenSSL license requires us to say this:' + echo ' * "This product includes software developed by the OpenSSL Project' + echo ' * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"' + + echo 'EFLAGS+=-lssl -lcrypto' >> Makefile.settings + + ret=1 +elif [ "$ssl" = "bogus" ]; then + echo + echo 'Using bogus SSL code. This means some features have to be disabled.' + + ## Yes, you, at the console! How can you authenticate if you don't have any SSL!? + if [ "$msn" = "1" ]; then echo - echo 'ERROR: Could not find a suitable SSL library (GnuTLS, libnss or OpenSSL).' - echo ' This is necessary for MSN and full Jabber support. To continue,' - echo ' install a suitable SSL library or disable MSN support (--msn=0).' - echo ' If you want Jabber without SSL support you can try --ssl=bogus.' - - exit 1; - fi; + echo 'Real SSL support is necessary for MSN authentication, will build without' + echo 'MSN protocol support.' + msn=0 + fi + + ret=1 +else + echo + echo 'ERROR: Unknown SSL library specified.' + exit 1 +fi + +if [ "$ret" = "0" ]; then + echo + echo 'ERROR: Could not find a suitable SSL library (GnuTLS, libnss or OpenSSL).' + echo ' Please note that this script doesn'\''t have detection code for OpenSSL,' + echo ' so if you want to use that, you have to select it by hand. If you don'\''t' + echo ' need SSL support, you can select the "bogus" SSL library. (--ssl=bogus)' - echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings + exit 1 +fi; + +echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings + +STORAGES="text xml" + +if [ "$ldap" = "auto" ]; then + detect_ldap +fi + +if [ "$ldap" = 0 ]; then + echo "#undef WITH_LDAP" >> config.h +elif [ "$ldap" = 1 ]; then + echo "#define WITH_LDAP 1" >> config.h + STORAGES="$STORAGES ldap" fi +for i in $STORAGES; do + STORAGE_OBJS="$STORAGE_OBJS storage_$i.o" +done +echo "STORAGE_OBJS="$STORAGE_OBJS >> Makefile.settings + if [ "$strip" = 0 ]; then echo "STRIP=\# skip strip" >> Makefile.settings; else @@ -303,8 +348,6 @@ else echo "STRIP=$STRIP" >> Makefile.settings; elif type strip > /dev/null 2> /dev/null; then echo "STRIP=strip" >> Makefile.settings; - elif /bin/test -x /usr/ccs/bin/strip; then - echo "STRIP=/usr/ccs/bin/strip" >> Makefile.settings; else echo echo 'No strip utility found, cannot remove unnecessary parts from executable.' @@ -383,7 +426,7 @@ fi if [ "$protocols" = "PROTOCOLS = " ]; then echo "WARNING: You haven't selected any communication protocol to compile!" - echo " Bitlbee will run, but you will be unable to connect to IM servers!" + echo " BitlBee will run, but you will be unable to connect to IM servers!" fi echo "PROTOCOLS = $protocols" >> Makefile.settings @@ -418,28 +461,23 @@ echo echo 'Configuration done:' if [ "$debug" = "1" ]; then - echo ' Debugging enabled.'; + echo ' Debugging enabled.' else - echo ' Debugging disabled.'; + echo ' Debugging disabled.' fi if [ "$strip" = "1" ]; then - echo ' Binary stripping enabled.'; + echo ' Binary stripping enabled.' else - echo ' Binary stripping disabled.'; + echo ' Binary stripping disabled.' fi -echo ' Using event handler: '$events; -echo ' Using SSL library: '$ssl; - -#if [ "$flood" = "0" ]; then -# echo ' Flood protection disabled.'; -#else -# echo ' Flood protection enabled.'; -#fi +echo ' Using event handler: '$events +echo ' Using SSL library: '$ssl +echo ' Building with these storage backends: '$STORAGES if [ -n "$protocols" ]; then - echo ' Building with these protocols:' $protocols; + echo ' Building with these protocols:' $protocols else - echo ' Building without IM-protocol support. We wish you a lot of fun...'; + echo ' Building without IM-protocol support. We wish you a lot of fun...' fi diff --git a/doc/bitlbee.schema b/doc/bitlbee.schema new file mode 100644 index 00000000..3322e057 --- /dev/null +++ b/doc/bitlbee.schema @@ -0,0 +1,62 @@ +## LDAP Schema file for BitlBee +## Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org> +## +## We need the following object classes and related attributes: +## +## bitlBeeBuddy: +## - nick +## - handle + +## each bitlBeeNick has zero or more bitlBeeAccount subentries +## and bitlBeeAccount entries contain zero or more bitlBeeBuddy entries + +## The admin needs to setup the LDAP server to: +## - allow anonymous users to auth against bitlBeeNick objects on the +## password field +## - allow anonymous users to create new objects that start with nick= +## - allow read/write for a user that is authenticated only to his/her own +## object and subentries + +## - userid +## - userPassword +## - setting (multiple values) +## depends: top, account + +attributetype ( 1.3.6.1.4.1.25873.2.1.1 NAME 'bitlBeeAutoConnect' + DESC 'Autoconnect setting' + EQUALITY booleanMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.25873.2.1.2 NAME 'bitlBeeAccountNo' + DESC 'Account number' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +objectclass ( 1.3.6.1.4.1.25873.2.2.3 NAME 'bitlBeeAccount' SUP account STRUCTURAL + DESC 'BitlBee User Account ' + MUST ( userid, userPassword ) + MAY ( ) ) + +## bitlBeeAccount: +## - accountNo 1.3.6.1.4.1.1466.115.121.1.27 +## - protocol (msn, oscar, jabber, yahoo, ...) +## - username +## - password +## - server name +## - autoconnect (true/false) 1.3.6.1.4.1.1466.115.121.1.7 +## depends: top + +objectclass ( 1.3.6.1.4.1.25873.2.2.1 NAME 'bitlBeeIMAccount' SUP account STRUCTURAL + DESC 'BitlBee IM Account ' + MUST ( bitlBeeAccountNo, userid, userPassword ) + MAY ( host, bitlBeeAutoconnect ) ) + +objectclass ( 1.3.6.1.4.1.25873.2.2.2 NAME 'bitlBeeSetting' SUP top STRUCTURAL + DESC 'BitlBee Configuration Setting' + MUST ( bitlBeeSettingName ) + MAY ( bitlBeeSettingValue ) ) + +objectclass ( 1.3.6.1.4.1.25873.2.2.3 NAME 'bitlBeeBuddy' SUP top STRUCTURAL + DESC 'BitlBee Nick Mapping' + MUST ( bitlBeeBuddyHandle ) + MAY ( ircNick ) ) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 44a9882f..d28a8531 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -10,7 +10,7 @@ <description> <para> - Available actions: add, del, list, on, off. See <emphasis>help account <action></emphasis> for more information. + Available actions: add, del, list, on, off and set. See <emphasis>help account <action></emphasis> for more information. </para> </description> @@ -25,28 +25,28 @@ </description> <bitlbee-command name="jabber"> - <syntax>account add jabber <handle> <password> [<servertag>]</syntax> + <syntax>account add jabber <handle@server.tld> <password> [<servertag>]</syntax> <description> <para> - Note that the servertag argument is optional. You only have to use it if the part after the @ in your handle isn't the hostname of your Jabber server, or if you want to use SSL/connect to a non-standard port number. The format is simple: [<servername>[:<portnumber>][:ssl]]. For example, this is how you can connect to Google Talk: + Note that the servertag argument is optional. You only have to use it if the part after the @ in your handle isn't the hostname of your Jabber server, or if you want to use SSL/connect to a non-standard port number. The format is simple: [<servername>[:<portnumber>][:ssl]]. </para> </description> - <ircexample> - <ircline nick="wilmer">account add jabber example@gmail.com hobbelmeeuw talk.google.com:5223:ssl</ircline> - <ircline nick="root">Account successfully added</ircline> - </ircexample> - <description> <para> - Note that Google talk is SSL-only, but officially reachable over both port 5222 and 5223. However, for some people only port 5222 works, for some people only 5223. This is something you'll have to try out. + Google Talk uses the Jabber protocol. Please note that Google talk is SSL-only, but officially reachable over both port 5222 and 5223. Usually BitlBee users have to connect via port 5223, for example like this: </para> </description> + + <ircexample> + <ircline nick="wilmer">account add jabber example@gmail.com hobbelmeeuw talk.google.com:5223:ssl</ircline> + <ircline nick="root">Account successfully added</ircline> + </ircexample> </bitlbee-command> <bitlbee-command name="msn"> - <syntax>account add msn <handle> <password></syntax> + <syntax>account add msn <handle@server.tld> <password></syntax> <description> <para> @@ -102,7 +102,7 @@ <description> <para> - This command will try to log into the specified account. If no account is specified, BitlBee will log into all the accounts. (Including accounts awaiting a reconnection) + This command will try to log into the specified account. If no account is specified, BitlBee will log into all the accounts that have the auto_connect flag set. </para> <para> @@ -117,7 +117,7 @@ <description> <para> - This command disconnects the connection for the specified account. If no account is specified, BitlBee will deactivate all active accounts. (Including accounts awaiting a reconnection) + This command disconnects the connection for the specified account. If no account is specified, BitlBee will deactivate all active accounts and cancel all pending reconnects. </para> <para> @@ -135,6 +135,26 @@ </para> </description> </bitlbee-command> + + <bitlbee-command name="set"> + <syntax>account set <account id></syntax> + <syntax>account set <account id>/<setting></syntax> + <syntax>account set <account id>/<setting> <value></syntax> + + <description> + <para> + This account can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing <emphasis>account set <account id></emphasis>. + </para> + + <para> + For more infomation about a setting, see <emphasis>help set <setting></emphasis>. + </para> + + <para> + The account ID can be a number (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection. + </para> + </description> + </bitlbee-command> </bitlbee-command> <bitlbee-command name="add"> @@ -275,120 +295,145 @@ </description> </bitlbee-command> - <bitlbee-setting name="charset" type="string"> - <default>iso8859-1</default> - <possible-values>you can get a list of all possible values by doing 'iconv -l' in a shell</possible-values> + <bitlbee-setting name="auto_connect" type="boolean"> + <default>True</default> <description> <para> - The charset setting enables you to use different character sets in BitlBee. These get converted to UTF-8 before sending and from UTF-8 when receiving. + With this option enabled, when you identify BitlBee will automatically connect to your accounts, with this disabled it will not do this. </para> - + <para> - If you don't know what's the best value for this, at least iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://czyborra.com/charsets/iso8859.html + This setting can also be changed for specific accounts using the <emphasis>account set</emphasis> command. (However, these values will be ignored if the global <emphasis>auto_connect</emphasis> setting is disabled!) </para> </description> - </bitlbee-setting> - <bitlbee-setting name="private" type="boolean"> - <default>True</default> + <bitlbee-setting name="auto_reconnect" type="boolean"> + <default>False</default> <description> - <para> - If value is true, messages from users will appear in separate query windows. If false, messages from users will appear in the control channel. + If an IM-connections breaks, you're supposed to bring it back up yourself. Having BitlBee do this automatically might not always be a good idea, for several reasons. If you want the connections to be restored automatically, you can enable this setting. </para> <para> - This setting is remembered (during one session) per-user, this setting only changes the default state. This option takes effect as soon as you reconnect. + See also the <emphasis>auto_reconnect_delay</emphasis> setting. </para> </description> + </bitlbee-setting> - <bitlbee-setting name="save_on_quit" type="boolean"> - <default>True</default> + <bitlbee-setting name="auto_reconnect_delay" type="integer"> + <default>300</default> <description> <para> - If enabled causes BitlBee to save all current settings and account details when user disconnects. This is enabled by default, and these days there's not really a reason to have it disabled anymore. + 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. + </para> + + <para> + See also the <emphasis>auto_reconnect</emphasis> setting. </para> </description> </bitlbee-setting> - <bitlbee-setting name="strip_html" type="boolean"> + <bitlbee-setting name="away_devoice" type="boolean"> <default>True</default> <description> <para> - Determines what BitlBee should do with HTML in messages. Normally this is turned on and HTML will be stripped from messages, if BitlBee thinks there is HTML. - </para> - <para> - If BitlBee fails to detect this sometimes (most likely in AIM messages over an ICQ connection), you can set this setting to <emphasis>always</emphasis>, but this might sometimes accidentally strip non-HTML things too. + With this option enabled, the root user devoices people when they go away (just away, not offline) and gives the voice back when they come back. You might dislike the voice-floods you'll get if your contact list is huge, so this option can be disabled. </para> </description> </bitlbee-setting> - <bitlbee-setting name="debug" type="boolean"> + <bitlbee-setting name="buddy_sendbuffer" type="boolean"> <default>False</default> <description> <para> - Some debugging messages can be sent to the control channel if you wish. They're probably not really useful for you, unless you're doing some development on BitlBee. + By default, when you send a message to someone, BitlBee forwards this message to the user immediately. When you paste a large number of lines, the lines will be sent in separate messages, which might not be very nice to read. If you enable this setting, BitlBee will buffer your messages and wait for more data. + </para> + + <para> + Using the <emphasis>buddy_sendbuffer_delay</emphasis> setting you can specify the number of seconds BitlBee should wait for more data before the complete message is sent. + </para> + + <para> + Please note that if you remove a buddy from your list (or if the connection to that user drops) and there's still data in the buffer, this data will be lost. BitlBee will not try to send the message to the user in those cases. </para> </description> </bitlbee-setting> - <bitlbee-setting name="to_char" type="string"> - <default>": "</default> + <bitlbee-setting name="buddy_sendbuffer_delay" type="integer"> + <default>200</default> <description> <para> - It's customary that messages meant for one specific person on an IRC channel are prepended by his/her alias followed by a colon ':'. BitlBee does this by default. If you prefer a different character, you can set it using <emphasis>set to_char</emphasis>. + Tell BitlBee after how many (mili)seconds a buffered message should be sent. Values greater than 5 will be interpreted as miliseconds, 5 and lower as seconds. </para> <para> - Please note that this setting is only used for incoming messages. For outgoing messages you can use ':' (colon) or ',' to separate the destination nick from the message, and this is not configurable. + See also the <emphasis>buddy_sendbuffer</emphasis> setting. </para> </description> </bitlbee-setting> - <bitlbee-setting name="typing_notice" type="boolean"> - <default>False</default> + <bitlbee-setting name="charset" type="string"> + <default>iso8859-1</default> + <possible-values>you can get a list of all possible values by doing 'iconv -l' in a shell</possible-values> <description> <para> - Sends you a /notice when a user starts typing a message (if the protocol supports it, MSN for example). This is a bug, not a feature. (But please don't report it.. ;-) You don't want to use it. Really. In fact the typing-notification is just one of the least useful 'innovations' ever. It's just there because some guy will probably ask me about it anyway. ;-) + The charset setting enables you to use different character sets in BitlBee. These get converted to UTF-8 before sending and from UTF-8 when receiving. + </para> + + <para> + If you don't know what's the best value for this, at least iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://czyborra.com/charsets/iso8859.html </para> </description> + </bitlbee-setting> - <bitlbee-setting name="ops" type="string"> - <default>both</default> - <possible-values>both, root, user, none</possible-values> + <bitlbee-setting name="debug" type="boolean"> + <default>False</default> <description> <para> - Some people prefer themself and root to have operator status in &bitlbee, other people don't. You can change these states using this setting. + Some debugging messages can be sent to the control channel if you wish. They're probably not really useful for you, unless you're doing some development on BitlBee. </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="default_target" type="string"> + <default>root</default> + <possible-values>root, last</possible-values> + <description> <para> - The value "both" means both user and root get ops. "root" means, well, just root. "user" means just the user. "none" means nobody will get operator status. + With this value set to <emphasis>root</emphasis>, lines written in the control channel without any nickname in front of them will be interpreted as commands. If you want BitlBee to send those lines to the last person you addressed in the control channel, set this to <emphasis>last</emphasis>. </para> </description> </bitlbee-setting> - <bitlbee-setting name="away_devoice" type="boolean"> - <default>True</default> - + <bitlbee-setting name="display_name" type="string"> <description> <para> - With this option enabled, the root user devoices people when they go away (just away, not offline) and gives the voice back when they come back. You might dislike the voice-floods you'll get if your contact list is huge, so this option can be disabled. + Currently only available for MSN connections. This setting allows you to read and change your "friendly name" for this connection. Since this is a server-side setting, it can't be changed when the account is off-line. </para> </description> </bitlbee-setting> + <bitlbee-setting name="display_namechanges" type="boolean"> + <default>False</default> + + <para> + With this option enabled, root will inform you when someone in your buddy list changes his/her "friendly name". + </para> + </bitlbee-setting> + <bitlbee-setting name="handle_unknown" type="string"> <default>root</default> <possible-values>root, add, add_private, add_channel, ignore</possible-values> @@ -416,134 +461,158 @@ </bitlbee-setting> - <bitlbee-setting name="auto_connect" type="boolean"> + <bitlbee-setting name="lcnicks" type="boolean"> <default>True</default> <description> <para> - With this option enabled, when you identify BitlBee will automatically connect to your accounts, with this disabled it will not do this. + Hereby you can change whether you want all lower case nick names or leave the case as it intended by your peer. </para> </description> + </bitlbee-setting> - <bitlbee-setting name="auto_reconnect" type="boolean"> - <default>False</default> + <bitlbee-setting name="ops" type="string"> + <default>both</default> + <possible-values>both, root, user, none</possible-values> <description> <para> - If an IM-connections breaks, you're supposed to bring it back up yourself. Having BitlBee do this automatically might not always be a good idea, for several reasons. If you want the connections to be restored automatically, you can enable this setting. + Some people prefer themself and root to have operator status in &bitlbee, other people don't. You can change these states using this setting. </para> <para> - See also the <emphasis>auto_reconnect_delay</emphasis> setting. + The value "both" means both user and root get ops. "root" means, well, just root. "user" means just the user. "none" means nobody will get operator status. </para> </description> - </bitlbee-setting> - <bitlbee-setting name="auto_reconnect_delay" type="integer"> - <default>300</default> - + <bitlbee-setting name="password" type="string"> <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. + Use this global setting to change your "NickServ" password. </para> - + <para> - See also the <emphasis>auto_reconnect</emphasis> setting. + This setting is also available for all IM accounts to change the password BitlBee uses to connect to the service. + </para> + + <para> + Note that BitlBee will always say this setting is empty. This doesn't mean there is no password, it just means that, for security reasons, BitlBee stores passwords somewhere else so they can't just be retrieved in plain text. </para> </description> </bitlbee-setting> - - <bitlbee-setting name="buddy_sendbuffer" type="boolean"> - <default>False</default> - + + <bitlbee-setting name="port" type="integer"> <description> - <para> - By default, when you send a message to someone, BitlBee forwards this message to the user immediately. When you paste a large number of lines, the lines will be sent in separate messages, which might not be very nice to read. If you enable this setting, BitlBee will buffer your messages and wait for more data. + Currently only available for Jabber connections. Specifies the port number to connect to. Usually this should be set to 5222, or 5223 for SSL-connections. </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="private" type="boolean"> + <default>True</default> + <description> <para> - Using the <emphasis>buddy_sendbuffer_delay</emphasis> setting you can specify the number of seconds BitlBee should wait for more data before the complete message is sent. + If value is true, messages from users will appear in separate query windows. If false, messages from users will appear in the control channel. </para> <para> - Please note that if you remove a buddy from your list (or if the connection to that user drops) and there's still data in the buffer, this data will be lost. BitlBee will not try to send the message to the user in those cases. + This setting is remembered (during one session) per-user, this setting only changes the default state. This option takes effect as soon as you reconnect. </para> </description> - </bitlbee-setting> - <bitlbee-setting name="buddy_sendbuffer_delay" type="integer"> - <default>200</default> + <bitlbee-setting name="query_order" type="string"> + <default>lifo</default> + <possible-values>lifo, fifo</possible-values> <description> - <para> - Tell BitlBee after how many (mili)seconds a buffered message should be sent. Values greater than 5 will be interpreted as miliseconds, 5 and lower as seconds. + This changes the order in which the questions from root (usually authorization requests from buddies) should be answered. When set to <emphasis>lifo</emphasis>, BitlBee immediately displays all new questions and they should be answered in reverse order. When this is set to <emphasis>fifo</emphasis>, BitlBee displays the first question which comes in and caches all the others until you answer the first one. </para> <para> - See also the <emphasis>buddy_sendbuffer</emphasis> setting. + Although the <emphasis>fifo</emphasis> setting might sound more logical (and used to be the default behaviour in older BitlBee versions), it turned out not to be very convenient for many users when they missed the first question (and never received the next ones). </para> </description> + </bitlbee-setting> + <bitlbee-setting name="resource" type="string"> + <default>BitlBee</default> + + <description> + <para> + Can be set for Jabber connections. You can use this to connect to your Jabber account from multiple clients at once, with every client using a different resource string. + </para> + </description> </bitlbee-setting> - <bitlbee-setting name="default_target" type="string"> - <default>root</default> - <possible-values>root, last</possible-values> + <bitlbee-setting name="save_on_quit" type="boolean"> + <default>True</default> <description> <para> - With this value set to <emphasis>root</emphasis>, lines written in the control channel without any nickname in front of them will be interpreted as commands. If you want BitlBee to send those lines to the last person you addressed in the control channel, set this to <emphasis>last</emphasis>. + If enabled causes BitlBee to save all current settings and account details when user disconnects. This is enabled by default, and these days there's not really a reason to have it disabled anymore. </para> </description> + </bitlbee-setting> + <bitlbee-setting name="server" type="string"> + <description> + <para> + Can be set for Jabber- and OSCAR-connections. For OSCAR, this must be set to <emphasis>login.icq.com</emphasis> if it's an ICQ connection, or <emphasis>login.oscar.aol.com</emphasis> if it's an AIM connection. For Jabber, you have to set this if the servername isn't equal to the part after the @ in the Jabber handle. + </para> + </description> </bitlbee-setting> - <bitlbee-setting name="display_namechanges" type="boolean"> + <bitlbee-setting name="ssl" type="boolean"> <default>False</default> - <para> - With this option enabled, root will inform you when someone in your buddy list changes his/her "friendly name". - </para> + <description> + <para> + Currently only available for Jabber connections. Set this to true if the server accepts SSL connections. + </para> + </description> </bitlbee-setting> - <bitlbee-setting name="password" type="string"> + <bitlbee-setting name="strip_html" type="boolean"> + <default>True</default> + <description> <para> - Use this setting to change your "NickServ" password. + Determines what BitlBee should do with HTML in messages. Normally this is turned on and HTML will be stripped from messages, if BitlBee thinks there is HTML. + </para> + <para> + If BitlBee fails to detect this sometimes (most likely in AIM messages over an ICQ connection), you can set this setting to <emphasis>always</emphasis>, but this might sometimes accidentally strip non-HTML things too. </para> </description> </bitlbee-setting> - <bitlbee-setting name="query_order" type="string"> - <default>lifo</default> - <possible-values>lifo, fifo</possible-values> + <bitlbee-setting name="to_char" type="string"> + <default>": "</default> <description> <para> - This changes the order in which the questions from root (usually authorization requests from buddies) should be answered. When set to <emphasis>lifo</emphasis>, BitlBee immediately displays all new questions and they should be answered in reverse order. When this is set to <emphasis>fifo</emphasis>, BitlBee displays the first question which comes in and caches all the others until you answer the first one. + It's customary that messages meant for one specific person on an IRC channel are prepended by his/her alias followed by a colon ':'. BitlBee does this by default. If you prefer a different character, you can set it using <emphasis>set to_char</emphasis>. </para> <para> - Although the <emphasis>fifo</emphasis> setting might sound more logical (and used to be the default behaviour in older BitlBee versions), it turned out not to be very convenient for many users when they missed the first question (and never received the next ones). + Please note that this setting is only used for incoming messages. For outgoing messages you can use ':' (colon) or ',' to separate the destination nick from the message, and this is not configurable. </para> </description> </bitlbee-setting> - <bitlbee-setting name="lcnicks" type="boolean"> - <default>True</default> + <bitlbee-setting name="typing_notice" type="boolean"> + <default>False</default> <description> <para> - Hereby you can change whether you want all lower case nick names or leave the case as it intended by your peer. + Sends you a /notice when a user starts typing a message (if the protocol supports it, MSN for example). This is a bug, not a feature. (But please don't report it.. ;-) You don't want to use it. Really. In fact the typing-notification is just one of the least useful 'innovations' ever. It's just there because some guy will probably ask me about it anyway. ;-) </para> </description> - </bitlbee-setting> <bitlbee-command name="rename"> diff --git a/doc/user-guide/help.xsl b/doc/user-guide/help.xsl index 0eb1a88b..394530a7 100644 --- a/doc/user-guide/help.xsl +++ b/doc/user-guide/help.xsl @@ -6,7 +6,7 @@ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.1"> - <xsl:output method="text" encoding="iso-8859-1" standalone="yes"/> + <xsl:output method="text" encoding="utf-8" standalone="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="text()"> @@ -32,10 +32,11 @@ static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond ); GSList *irc_connection_list = NULL; -static char *passchange (irc_t *irc, void *set, char *value) +static char *passchange( irc_t *irc, void *set, char *value ) { - irc_setpass (irc, value); - return (NULL); + irc_setpass( irc, value ); + irc_usermsg( irc, "Password successfully changed" ); + return NULL; } irc_t *irc_new( int fd ) @@ -122,26 +123,26 @@ irc_t *irc_new( int fd ) irc_connection_list = g_slist_append( irc_connection_list, irc ); - set_add( irc, "away_devoice", "true", set_eval_away_devoice ); - set_add( irc, "auto_connect", "true", set_eval_bool ); - set_add( irc, "auto_reconnect", "false", set_eval_bool ); - set_add( irc, "auto_reconnect_delay", "300", set_eval_int ); - set_add( irc, "buddy_sendbuffer", "false", set_eval_bool ); - set_add( irc, "buddy_sendbuffer_delay", "200", set_eval_int ); - set_add( irc, "charset", "iso8859-1", set_eval_charset ); - set_add( irc, "debug", "false", set_eval_bool ); - set_add( irc, "default_target", "root", NULL ); - set_add( irc, "display_namechanges", "false", set_eval_bool ); - set_add( irc, "handle_unknown", "root", NULL ); - set_add( irc, "lcnicks", "true", set_eval_bool ); - set_add( irc, "ops", "both", set_eval_ops ); - set_add( irc, "private", "true", set_eval_bool ); - set_add( irc, "query_order", "lifo", NULL ); - set_add( irc, "save_on_quit", "true", set_eval_bool ); - set_add( irc, "strip_html", "true", NULL ); - set_add( irc, "to_char", ": ", set_eval_to_char ); - set_add( irc, "typing_notice", "false", set_eval_bool ); - set_add( irc, "password", NULL, passchange); + 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", "iso8859-1", 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, "save_on_quit", "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 ); conf_loaddefaults( irc ); @@ -205,15 +206,13 @@ static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ) /* Because we have no garbage collection, this is quite annoying */ void irc_free(irc_t * irc) { - account_t *account, *accounttmp; + account_t *account; user_t *user, *usertmp; - nick_t *nick, *nicktmp; help_t *helpnode, *helpnodetmp; - set_t *setnode, *setnodetmp; log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); - if( irc->status & USTATUS_IDENTIFIED && set_getint( irc, "save_on_quit" ) ) + if( irc->status & USTATUS_IDENTIFIED && set_getint( &irc->set, "save_on_quit" ) ) if( storage_save( irc, TRUE ) != STORAGE_OK ) irc_usermsg( irc, "Error while saving settings!" ); @@ -253,17 +252,11 @@ void irc_free(irc_t * irc) while (irc->queries != NULL) query_del(irc, irc->queries); - if (irc->accounts != NULL) { - account = irc->accounts; - while (account != NULL) { - g_free(account->user); - g_free(account->pass); - g_free(account->server); - accounttmp = account; - account = account->next; - g_free(accounttmp); - } - } + while (irc->accounts) + account_del(irc, irc->accounts); + + while (irc->set) + set_del(&irc->set, irc->set->key); if (irc->users != NULL) { user = irc->users; @@ -288,17 +281,6 @@ void irc_free(irc_t * irc) g_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL); g_hash_table_destroy(irc->watches); - if (irc->nicks != NULL) { - nick = irc->nicks; - while (nick != NULL) { - g_free(nick->nick); - g_free(nick->handle); - - nicktmp = nick; - nick = nick->next; - g_free(nicktmp); - } - } if (irc->help != NULL) { helpnode = irc->help; while (helpnode != NULL) { @@ -309,18 +291,6 @@ void irc_free(irc_t * irc) g_free(helpnodetmp); } } - if (irc->set != NULL) { - setnode = irc->set; - while (setnode != NULL) { - g_free(setnode->key); - g_free(setnode->def); - g_free(setnode->value); - - setnodetmp = setnode; - setnode = setnode->next; - g_free(setnodetmp); - } - } g_free(irc); if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON ) @@ -331,11 +301,10 @@ void irc_free(irc_t * irc) Sets pass without checking */ void irc_setpass (irc_t *irc, const char *pass) { - if (irc->password) g_free (irc->password); + g_free (irc->password); if (pass) { irc->password = g_strdup (pass); - irc_usermsg (irc, "Password successfully changed"); } else { irc->password = NULL; } @@ -366,7 +335,7 @@ void irc_process( irc_t *irc ) break; } - if( ( cs = set_getstr( irc, "charset" ) ) && ( g_strcasecmp( cs, "utf-8" ) != 0 ) ) + if( ( cs = set_getstr( &irc->set, "charset" ) ) && ( g_strcasecmp( cs, "utf-8" ) != 0 ) ) { conv[IRC_MAX_LINE] = 0; if( do_iconv( cs, "UTF-8", lines[i], conv, 0, IRC_MAX_LINE - 2 ) != -1 ) @@ -586,7 +555,7 @@ void irc_vawrite( irc_t *irc, char *format, va_list params ) g_vsnprintf( line, IRC_MAX_LINE - 2, format, params ); strip_newlines( line ); - if( ( cs = set_getstr( irc, "charset" ) ) && ( g_strcasecmp( cs, "utf-8" ) != 0 ) ) + if( ( cs = set_getstr( &irc->set, "charset" ) ) && ( g_strcasecmp( cs, "utf-8" ) != 0 ) ) { char conv[IRC_MAX_LINE+1]; @@ -669,7 +638,7 @@ void irc_names( irc_t *irc, char *channel ) *namelist = 0; } - if( u->gc && !u->away && set_getint( irc, "away_devoice" ) ) + if( u->gc && !u->away && set_getbool( &irc->set, "away_devoice" ) ) strcat( namelist, "+" ); else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) || ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ) @@ -682,6 +651,7 @@ void irc_names( irc_t *irc, char *channel ) else if( ( c = conv_findchannel( channel ) ) ) { GList *l; + char *ops = set_getstr( &irc->set, "ops" ); /* root and the user aren't in the channel userlist but should show up in /NAMES, so list them first: */ @@ -929,19 +899,19 @@ void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker ) void irc_kill( irc_t *irc, user_t *u ) { char *nick, *s; - char reason[64]; + char reason[128]; if( u->gc && u->gc->flags & OPT_LOGGING_OUT ) { - if( u->gc->user->proto_opt[0][0] ) + if( u->gc->acc->server ) g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost, - u->gc->user->proto_opt[0] ); + u->gc->acc->server ); else if( ( s = strchr( u->gc->username, '@' ) ) ) g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost, s + 1 ); else g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost, - u->gc->prpl->name, irc->myhost ); + u->gc->acc->prpl->name, irc->myhost ); /* proto_opt might contain garbage after the : */ if( ( s = strchr( reason, ':' ) ) ) @@ -1017,13 +987,13 @@ int irc_send( irc_t *irc, char *nick, char *s, int flags ) } else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 ) { - if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 ) + if( u && u->gc && u->gc->acc->prpl->send_typing && strlen( s ) >= 10 ) { time_t current_typing_notice = time( NULL ); if( current_typing_notice - u->last_typing_notice >= 5 ) { - u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' ); + u->gc->acc->prpl->send_typing( u->gc, u->handle, s[8] == '1' ); u->last_typing_notice = current_typing_notice; } } @@ -1056,7 +1026,7 @@ int irc_send( irc_t *irc, char *nick, char *s, int flags ) return 1; } } - else if( c && c->gc && c->gc->prpl ) + else if( c && c->gc && c->gc->acc && c->gc->acc->prpl ) { return( bim_chat_msg( c->gc, c->id, s ) ); } @@ -1088,7 +1058,7 @@ void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ) { if( !u || !u->gc ) return; - if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 ) + if( set_getint( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 ) { int delay; @@ -1115,7 +1085,7 @@ void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ) strcat( u->sendbuf, msg ); strcat( u->sendbuf, "\n" ); - delay = set_getint( irc, "buddy_sendbuffer_delay" ); + delay = set_getint( &irc->set, "buddy_sendbuffer_delay" ); if( delay <= 5 ) delay *= 1000; @@ -1180,7 +1150,7 @@ int irc_msgfrom( irc_t *irc, char *nick, char *msg ) { int len = strlen( irc->nick) + 3; prefix = g_new (char, len ); - g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) ); + g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) ); prefix[len-1] = 0; } else @@ -97,7 +97,7 @@ typedef struct irc } irc_t; #include "user.h" -#include "nick.h" +// #include "nick.h" extern GSList *irc_connection_list; diff --git a/irc_commands.c b/irc_commands.c index 3a7ace1c..47d9e8cb 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -149,10 +149,10 @@ static void irc_cmd_part( irc_t *irc, char **cmd ) irc_part( irc, u, c->channel ); - if( c->gc && c->gc->prpl ) + if( c->gc ) { c->joined = 0; - c->gc->prpl->chat_leave( c->gc, c->id ); + c->gc->acc->prpl->chat_leave( c->gc, c->id ); } } else @@ -172,11 +172,11 @@ static void irc_cmd_join( irc_t *irc, char **cmd ) { user_t *u = user_find( irc, cmd[1] + 1 ); - if( u && u->gc && u->gc->prpl && u->gc->prpl->chat_open ) + if( u && u->gc && u->gc->acc->prpl->chat_open ) { irc_reply( irc, 403, "%s :Initializing groupchat in a different channel", cmd[1] ); - if( !u->gc->prpl->chat_open( u->gc, u->handle ) ) + if( !u->gc->acc->prpl->chat_open( u->gc, u->handle ) ) { irc_usermsg( irc, "Could not open a groupchat with %s.", u->nick ); } @@ -204,9 +204,9 @@ static void irc_cmd_invite( irc_t *irc, char **cmd ) user_t *u = user_find( irc, nick ); if( u && c && ( u->gc == c->gc ) ) - if( c->gc && c->gc->prpl && c->gc->prpl->chat_invite ) + if( c->gc && c->gc->acc->prpl->chat_invite ) { - c->gc->prpl->chat_invite( c->gc, c->id, "", u->handle ); + c->gc->acc->prpl->chat_invite( c->gc, c->id, "", u->handle ); irc_reply( irc, 341, "%s %s", nick, channel ); return; } @@ -229,7 +229,7 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd ) if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) { unsigned int i; - char *t = set_getstr( irc, "default_target" ); + char *t = set_getstr( &irc->set, "default_target" ); if( g_strcasecmp( t, "last" ) == 0 && irc->last_target ) cmd[1] = irc->last_target; @@ -476,8 +476,9 @@ static void irc_cmd_whois( irc_t *irc, char **cmd ) irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname ); if( u->gc ) - irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->gc->user->username, - *u->gc->user->proto_opt[0] ? u->gc->user->proto_opt[0] : "", u->gc->prpl->name ); + irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->gc->acc->user, + u->gc->acc->server && *u->gc->acc->server ? u->gc->acc->server : "", + u->gc->acc->prpl->name ); else irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO ); diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..6408c5ba --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,37 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2006 Lintux ## +########################### + +### DEFINITIONS + +-include ../Makefile.settings + +# [SH] Program variables +objects = base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o rc4.o sha.o $(SSL_CLIENT) url.o + +CFLAGS += -Wall +LFLAGS += -r + +# [SH] Phony targets +all: lib.o + +.PHONY: all clean distclean + +clean: $(subdirs) + rm -f *.o $(OUTFILE) core + +distclean: clean $(subdirs) + +### MAIN PROGRAM + +lib.o: $(objects) $(subdirs) + @echo '*' Linking lib.o + @$(LD) $(LFLAGS) $(objects) -o lib.o + +$(objects): ../Makefile.settings Makefile + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/lib/base64.c b/lib/base64.c new file mode 100644 index 00000000..69069dae --- /dev/null +++ b/lib/base64.c @@ -0,0 +1,153 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Base64 handling functions. encode_real() is mostly based on the y64 en- * +* coder from libyahoo2. Moving it to a new file because it's getting big. * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include <glib.h> +#include <string.h> +#include "base64.h" + +static const char real_b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +char *tobase64(const char *text) +{ + return base64_encode(text, strlen(text)); +} + +char *base64_encode(const char *in, int len) +{ + char *out; + + out = g_malloc((len + 2) /* the == padding */ + / 3 /* every 3-byte block */ + * 4 /* becomes a 4-byte one */ + + 1); /* and of course, ASCIIZ! */ + + base64_encode_real((unsigned char*) in, len, (unsigned char*) out, real_b64); + + return out; +} + +int base64_encode_real(const unsigned char *in, int inlen, unsigned char *out, const char *b64digits) +{ + int outlen = 0; + + for (; inlen >= 3; inlen -= 3) + { + out[outlen++] = b64digits[in[0] >> 2]; + out[outlen++] = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; + out[outlen++] = b64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)]; + out[outlen++] = b64digits[in[2] & 0x3f]; + in += 3; + } + if (inlen > 0) + { + out[outlen++] = b64digits[in[0] >> 2]; + if (inlen > 1) + { + out[outlen++] = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; + out[outlen++] = b64digits[((in[1]<<2) & 0x3c)]; + } + else + { + out[outlen++] = b64digits[((in[0]<<4) & 0x30)]; + out[outlen++] = b64digits[64]; + } + out[outlen++] = b64digits[64]; + } + out[outlen] = 0; + + return outlen; +} + +/* Just a simple wrapper, but usually not very convenient because of zero + termination. */ +char *frombase64(const char *in) +{ + unsigned char *out; + + base64_decode(in, &out); + + return (char*) out; +} + +/* FIXME: Lookup table stuff is not threadsafe! (But for now BitlBee is not threaded.) */ +int base64_decode(const char *in, unsigned char **out) +{ + static char b64rev[256] = { 0 }; + int len, i; + + /* Create a reverse-lookup for the Base64 sequence. */ + if( b64rev[0] == 0 ) + { + memset( b64rev, 0xff, 256 ); + for( i = 0; i <= 64; i ++ ) + b64rev[(int)real_b64[i]] = i; + } + + len = strlen( in ); + *out = g_malloc( ( len + 6 ) / 4 * 3 ); + len = base64_decode_real( (unsigned char*) in, *out, b64rev ); + *out = g_realloc( *out, len + 1 ); + out[0][len] = 0; /* Zero termination can't hurt. */ + + return len; +} + +int base64_decode_real(const unsigned char *in, unsigned char *out, char *b64rev) +{ + int i, outlen = 0; + + for( i = 0; in[i]; i += 4 ) + { + int sx; + + sx = b64rev[(int)in[i+0]]; + if( sx >= 64 ) + break; + out[outlen] = ( sx << 2 ) & 0xfc; + + sx = b64rev[(int)in[i+1]]; + if( sx >= 64 ) + break; + out[outlen] |= ( sx >> 4 ) & 0x03; + outlen ++; + out[outlen] = ( sx << 4 ) & 0xf0; + + sx = b64rev[(int)in[i+2]]; + if( sx >= 64 ) + break; + out[outlen] |= ( sx >> 2 ) & 0x0f; + outlen ++; + out[outlen] = ( sx << 6 ) & 0xc0; + + sx = b64rev[(int)in[i+3]]; + if( sx >= 64 ) + break; + out[outlen] |= sx; + outlen ++; + } + + /* If sx > 64 the base64 string was damaged. Should we ignore this? */ + + return outlen; +} diff --git a/lib/base64.h b/lib/base64.h new file mode 100644 index 00000000..570f2b14 --- /dev/null +++ b/lib/base64.h @@ -0,0 +1,33 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Base64 handling functions. encode_real() is mostly based on the y64 en- * +* coder from libyahoo2. Moving it to a new file because it's getting big. * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include <glib.h> +#include <gmodule.h> + +G_MODULE_EXPORT char *tobase64( const char *text ); +G_MODULE_EXPORT char *base64_encode( const char *in, int len ); +G_MODULE_EXPORT int base64_encode_real( const unsigned char *in, int inlen, unsigned char *out, const char *b64digits ); +G_MODULE_EXPORT char *frombase64( const char *in ); +G_MODULE_EXPORT int base64_decode( const char *in, unsigned char **out ); +G_MODULE_EXPORT int base64_decode_real( const unsigned char *in, unsigned char *out, char *b64reverse ); diff --git a/protocols/events.h b/lib/events.h index 781fca6a..781fca6a 100644 --- a/protocols/events.h +++ b/lib/events.h diff --git a/protocols/events_glib.c b/lib/events_glib.c index 620720cd..620720cd 100644 --- a/protocols/events_glib.c +++ b/lib/events_glib.c diff --git a/protocols/events_libevent.c b/lib/events_libevent.c index 1119c2ab..1119c2ab 100644 --- a/protocols/events_libevent.c +++ b/lib/events_libevent.c diff --git a/protocols/http_client.c b/lib/http_client.c index b00fcf98..b00fcf98 100644 --- a/protocols/http_client.c +++ b/lib/http_client.c diff --git a/protocols/http_client.h b/lib/http_client.h index 50ee80cf..50ee80cf 100644 --- a/protocols/http_client.h +++ b/lib/http_client.h diff --git a/protocols/md5.c b/lib/md5.c index e6273585..e6273585 100644 --- a/protocols/md5.c +++ b/lib/md5.c diff --git a/protocols/md5.h b/lib/md5.h index f24f2ff1..f24f2ff1 100644 --- a/protocols/md5.h +++ b/lib/md5.h @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2006 Wilmer van der Gaast and others * \********************************************************************/ /* @@ -10,7 +10,7 @@ * * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> * (and possibly other members of the Gaim team) - * Copyright 2002-2005 Wilmer van der Gaast <wilmer@gaast.net> + * Copyright 2002-2006 Wilmer van der Gaast <wilmer@gaast.net> */ /* @@ -83,63 +83,6 @@ char *add_cr(char *text) return ret; } -static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789+/"; - -/* XXX Find bug */ -char *tobase64(const char *text) -{ - char *out = NULL; - const char *c; - unsigned int tmp = 0; - int len = 0, n = 0; - - c = text; - - while (*c) { - tmp = tmp << 8; - tmp += *c; - n++; - - if (n == 3) { - out = g_realloc(out, len + 4); - out[len] = alphabet[(tmp >> 18) & 0x3f]; - out[len + 1] = alphabet[(tmp >> 12) & 0x3f]; - out[len + 2] = alphabet[(tmp >> 6) & 0x3f]; - out[len + 3] = alphabet[tmp & 0x3f]; - len += 4; - tmp = 0; - n = 0; - } - c++; - } - switch (n) { - - case 2: - tmp <<= 8; - out = g_realloc(out, len + 5); - out[len] = alphabet[(tmp >> 18) & 0x3f]; - out[len + 1] = alphabet[(tmp >> 12) & 0x3f]; - out[len + 2] = alphabet[(tmp >> 6) & 0x3f]; - out[len + 3] = '='; - out[len + 4] = 0; - break; - case 1: - tmp <<= 16; - out = g_realloc(out, len + 5); - out[len] = alphabet[(tmp >> 18) & 0x3f]; - out[len + 1] = alphabet[(tmp >> 12) & 0x3f]; - out[len + 2] = '='; - out[len + 3] = '='; - out[len + 4] = 0; - break; - case 0: - out = g_realloc(out, len + 1); - out[len] = 0; - break; - } - return out; -} - char *normalize(const char *s) { static char buf[BUF_LEN]; @@ -478,17 +421,101 @@ signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t si return( outbuf - dst ); } -char *set_eval_charset( irc_t *irc, set_t *set, char *value ) +/* A pretty reliable random number generator. Tries to use the /dev/random + devices first, and falls back to the random number generator from libc + when it fails. Opens randomizer devices with O_NONBLOCK to make sure a + lack of entropy won't halt BitlBee. */ +void random_bytes( unsigned char *buf, int count ) { - GIConv cd; - - if ( g_strncasecmp( value, "none", 4 ) == 0 ) - return( value ); + static int use_dev = -1; + + /* Actually this probing code isn't really necessary, is it? */ + if( use_dev == -1 ) + { + if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 ) + use_dev = 1; + else + { + use_dev = 0; + srand( ( getpid() << 16 ) ^ time( NULL ) ); + } + } + + if( use_dev ) + { + int fd; + + /* At least on Linux, /dev/random can block if there's not + enough entropy. We really don't want that, so if it can't + give anything, use /dev/urandom instead. */ + if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 ) + if( read( fd, buf, count ) == count ) + { + close( fd ); + return; + } + close( fd ); + + /* urandom isn't supposed to block at all, but just to be + sure. If it blocks, we'll disable use_dev and use the libc + randomizer instead. */ + if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 ) + if( read( fd, buf, count ) == count ) + { + close( fd ); + return; + } + close( fd ); + + /* If /dev/random blocks once, we'll still try to use it + again next time. If /dev/urandom also fails for some + reason, stick with libc during this session. */ + + use_dev = 0; + srand( ( getpid() << 16 ) ^ time( NULL ) ); + } + + if( !use_dev ) + { + int i; + + /* Possibly the LSB of rand() isn't very random on some + platforms. Seems okay on at least Linux and OSX though. */ + for( i = 0; i < count; i ++ ) + buf[i] = rand() & 0xff; + } +} - cd = g_iconv_open( "UTF-8", value ); - if( cd == (GIConv) -1 ) - return( NULL ); +int is_bool( char *value ) +{ + if( *value == 0 ) + return 0; + + if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) + return 1; + if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) + return 1; + + while( *value ) + if( !isdigit( *value ) ) + return 0; + else + value ++; + + return 1; +} - g_iconv_close( cd ); - return( value ); +int bool2int( char *value ) +{ + int i; + + if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) + return 1; + if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) + return 0; + + if( sscanf( value, "%d", &i ) == 1 ) + return i; + + return 0; } @@ -23,13 +23,15 @@ Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _UTIL_H -#define _UTIL_H +#ifndef _MISC_H +#define _MISC_H + +#include <gmodule.h> +#include <time.h> G_MODULE_EXPORT void strip_linefeed( gchar *text ); G_MODULE_EXPORT char *add_cr( char *text ); G_MODULE_EXPORT char *strip_newlines(char *source); -G_MODULE_EXPORT char *tobase64( const char *text ); G_MODULE_EXPORT char *normalize( const char *s ); G_MODULE_EXPORT void info_string_append( GString *str, char *newline, char *name, char *value ); @@ -45,6 +47,10 @@ G_MODULE_EXPORT char *ipv6_wrap( char *src ); G_MODULE_EXPORT char *ipv6_unwrap( char *src ); G_MODULE_EXPORT signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf ); -char *set_eval_charset( irc_t *irc, set_t *set, char *value ); + +G_MODULE_EXPORT void random_bytes( unsigned char *buf, int count ); + +G_MODULE_EXPORT int is_bool( char *value ); +G_MODULE_EXPORT int bool2int( char *value ); #endif diff --git a/protocols/proxy.c b/lib/proxy.c index 70a2158d..7911b06f 100644 --- a/protocols/proxy.c +++ b/lib/proxy.c @@ -40,6 +40,7 @@ #include <errno.h> #include "nogaim.h" #include "proxy.h" +#include "base64.h" char proxyhost[128] = ""; int proxyport = 0; diff --git a/protocols/proxy.h b/lib/proxy.h index 680790a5..680790a5 100644 --- a/protocols/proxy.h +++ b/lib/proxy.h diff --git a/lib/rc4.c b/lib/rc4.c new file mode 100644 index 00000000..5e334507 --- /dev/null +++ b/lib/rc4.c @@ -0,0 +1,189 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple (but secure) RC4 implementation for safer password storage. * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +/* + This file implements RC4-encryption, which will mainly be used to save IM + passwords safely in the new XML-format. Possibly other uses will come up + later. It's supposed to be quite reliable (thanks to the use of a 6-byte + IV/seed), certainly compared to the old format. The only realistic way to + crack BitlBee passwords now is to use a sniffer to get your hands on the + user's password. + + If you see that something's wrong in this implementation (I asked a + couple of people to look at it already, but who knows), please tell me. + + The reason I chose for RC4 is because it's pretty simple but effective, + so it will work without adding several KBs or an extra library dependency. +*/ + + +#include <glib.h> +#include <gmodule.h> +#include <stdlib.h> +#include <string.h> +#include "misc.h" +#include "rc4.h" + +/* Add some seed to the password, to make sure we *never* use the same key. + This defines how many byes we use as a seed. */ +#define RC4_IV_LEN 6 + +/* To defend against a "Fluhrer, Mantin and Shamir attack", it is recommended + to shuffle S[] just a bit more before you start to use it. This defines how + many bytes we'll request before we'll really use them for encryption. */ +#define RC4_CYCLES 1024 + +struct rc4_state *rc4_keymaker( unsigned char *key, int kl, int cycles ) +{ + struct rc4_state *st; + int i, j, tmp; + + st = g_malloc( sizeof( struct rc4_state ) ); + st->i = st->j = 0; + for( i = 0; i < 256; i ++ ) + st->S[i] = i; + + if( kl <= 0 ) + kl = strlen( (char*) key ); + + for( i = j = 0; i < 256; i ++ ) + { + j = ( j + st->S[i] + key[i%kl] ) & 0xff; + tmp = st->S[i]; + st->S[i] = st->S[j]; + st->S[j] = tmp; + } + + for( i = 0; i < cycles; i ++ ) + rc4_getbyte( st ); + + return st; +} + +/* + For those who don't know, RC4 is basically an algorithm that generates a + stream of bytes after you give it a key. Just get a byte from it and xor + it with your cleartext. To decrypt, just give it the same key again and + start xorring. + + The function above initializes the RC4 byte generator, the next function + can be used to get bytes from the generator (and shuffle things a bit). +*/ + +unsigned char rc4_getbyte( struct rc4_state *st ) +{ + unsigned char tmp; + + /* Unfortunately the st-> stuff doesn't really improve readability here... */ + st->i ++; + st->j += st->S[st->i]; + tmp = st->S[st->i]; + st->S[st->i] = st->S[st->j]; + st->S[st->j] = tmp; + + return st->S[(st->S[st->i] + st->S[st->j]) & 0xff]; +} + +/* + The following two functions can be used for reliable encryption and + decryption. Known plaintext attacks are prevented by adding some (6, + by default) random bytes to the password before setting up the RC4 + structures. These 6 bytes are also saved in the results, because of + course we'll need them in rc4_decode(). + + Because the length of the resulting string is unknown to the caller, + it should pass a char**. Since the encode/decode functions allocate + memory for the string, make sure the char** points at a NULL-pointer + (or at least to something you already free()d), or you'll leak + memory. And of course, don't forget to free() the result when you + don't need it anymore. + + Both functions return the number of bytes in the result string. +*/ + +int rc4_encode( unsigned char *clear, int clear_len, unsigned char **crypt, char *password ) +{ + struct rc4_state *st; + unsigned char *key; + int key_len, i; + + key_len = strlen( password ) + RC4_IV_LEN; + if( clear_len <= 0 ) + clear_len = strlen( (char*) clear ); + + /* Prepare buffers and the key + IV */ + *crypt = g_malloc( clear_len + RC4_IV_LEN ); + key = g_malloc( key_len ); + strcpy( (char*) key, password ); + + /* Add the salt. Save it for later (when decrypting) and, of course, + add it to the encryption key. */ + random_bytes( crypt[0], RC4_IV_LEN ); + memcpy( key + key_len - RC4_IV_LEN, crypt[0], RC4_IV_LEN ); + + /* Generate the initial S[] from the IVed key. */ + st = rc4_keymaker( key, key_len, RC4_CYCLES ); + g_free( key ); + + for( i = 0; i < clear_len; i ++ ) + crypt[0][i+RC4_IV_LEN] = clear[i] ^ rc4_getbyte( st ); + + g_free( st ); + + return clear_len + RC4_IV_LEN; +} + +int rc4_decode( unsigned char *crypt, int crypt_len, unsigned char **clear, char *password ) +{ + struct rc4_state *st; + unsigned char *key; + int key_len, clear_len, i; + + key_len = strlen( password ) + RC4_IV_LEN; + clear_len = crypt_len - RC4_IV_LEN; + + if( clear_len < 0 ) + { + *clear = (unsigned char*) g_strdup( "" ); + return 0; + } + + /* Prepare buffers and the key + IV */ + *clear = g_malloc( clear_len + 1 ); + key = g_malloc( key_len ); + strcpy( (char*) key, password ); + for( i = 0; i < RC4_IV_LEN; i ++ ) + key[key_len-RC4_IV_LEN+i] = crypt[i]; + + /* Generate the initial S[] from the IVed key. */ + st = rc4_keymaker( key, key_len, RC4_CYCLES ); + g_free( key ); + + for( i = 0; i < clear_len; i ++ ) + clear[0][i] = crypt[i+RC4_IV_LEN] ^ rc4_getbyte( st ); + clear[0][i] = 0; /* Nice to have for plaintexts. */ + + g_free( st ); + + return clear_len; +} diff --git a/lib/rc4.h b/lib/rc4.h new file mode 100644 index 00000000..6c9ea6b9 --- /dev/null +++ b/lib/rc4.h @@ -0,0 +1,34 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple (but secure) RC4 implementation for safer password storage. * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + + +struct rc4_state +{ + unsigned char S[256]; + unsigned char i, j; +}; + +struct rc4_state *rc4_keymaker( unsigned char *key, int kl, int cycles ); +unsigned char rc4_getbyte( struct rc4_state *st ); +int rc4_encode( unsigned char *clear, int clear_len, unsigned char **crypt, char *password ); +int rc4_decode( unsigned char *crypt, int crypt_len, unsigned char **clear, char *password ); diff --git a/protocols/sha.c b/lib/sha.c index 895505a1..895505a1 100644 --- a/protocols/sha.c +++ b/lib/sha.c diff --git a/protocols/sha.h b/lib/sha.h index e8152b1b..e8152b1b 100644 --- a/protocols/sha.h +++ b/lib/sha.h diff --git a/protocols/ssl_bogus.c b/lib/ssl_bogus.c index 00aaa7c4..00aaa7c4 100644 --- a/protocols/ssl_bogus.c +++ b/lib/ssl_bogus.c diff --git a/protocols/ssl_client.h b/lib/ssl_client.h index 1a9c79e9..1a9c79e9 100644 --- a/protocols/ssl_client.h +++ b/lib/ssl_client.h diff --git a/protocols/ssl_gnutls.c b/lib/ssl_gnutls.c index 3ebe1756..3ebe1756 100644 --- a/protocols/ssl_gnutls.c +++ b/lib/ssl_gnutls.c diff --git a/protocols/ssl_nss.c b/lib/ssl_nss.c index 218b3a80..218b3a80 100644 --- a/protocols/ssl_nss.c +++ b/lib/ssl_nss.c diff --git a/protocols/ssl_openssl.c b/lib/ssl_openssl.c index b6f6c520..b6f6c520 100644 --- a/protocols/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2006 Wilmer van der Gaast and others * \********************************************************************/ /* Some stuff to fetch, save and handle nicknames for your buddies */ @@ -26,50 +26,48 @@ #define BITLBEE_CORE #include "bitlbee.h" -void nick_set( irc_t *irc, const char *handle, struct prpl *proto, const char *nick ) +/* Store handles in lower case and strip spaces, because AIM is braindead. */ +static char *clean_handle( const char *orig ) { - nick_t *m = NULL, *n = irc->nicks; + char *new = g_malloc( strlen( orig ) + 1 ); + int i = 0; - while( n ) - { - if( ( g_strcasecmp( n->handle, handle ) == 0 ) && n->proto == proto ) - { - g_free( n->nick ); - n->nick = nick_dup( nick ); - nick_strip( n->nick ); - - return; - } - n = ( m = n )->next; // :-P + do { + if (*orig != ' ') + new[i++] = tolower( *orig ); } + while (*(orig++)); - if( m ) - n = m->next = g_new0( nick_t, 1 ); - else - n = irc->nicks = g_new0( nick_t, 1 ); + return new; +} + +void nick_set( account_t *acc, const char *handle, const char *nick ) +{ + char *store_handle, *store_nick = g_malloc( MAX_NICK_LENGTH + 1 ); - n->handle = g_strdup( handle ); - n->proto = proto; - n->nick = nick_dup( nick ); + store_handle = clean_handle( handle ); + strncpy( store_nick, nick, MAX_NICK_LENGTH ); + nick_strip( store_nick ); - nick_strip( n->nick ); + g_hash_table_replace( acc->nicks, store_handle, store_nick ); } -char *nick_get( irc_t *irc, const char *handle, struct prpl *proto, const char *realname ) +char *nick_get( account_t *acc, const char *handle, const char *realname ) { static char nick[MAX_NICK_LENGTH+1]; - nick_t *n = irc->nicks; + char *store_handle, *found_nick; int inf_protection = 256; memset( nick, 0, MAX_NICK_LENGTH + 1 ); - while( n && !*nick ) - if( ( n->proto == proto ) && ( g_strcasecmp( n->handle, handle ) == 0 ) ) - strcpy( nick, n->nick ); - else - n = n->next; - - if( !n ) + store_handle = clean_handle( handle ); + /* Find out if we stored a nick for this person already. If not, try + to generate a sane nick automatically. */ + if( ( found_nick = g_hash_table_lookup( acc->nicks, store_handle ) ) ) + { + strncpy( nick, found_nick, MAX_NICK_LENGTH ); + } + else { char *s; @@ -85,11 +83,14 @@ char *nick_get( irc_t *irc, const char *handle, struct prpl *proto, const char * g_snprintf( nick, MAX_NICK_LENGTH, "%s", realname ); nick_strip( nick ); - if (set_getint(irc, "lcnicks")) + if( set_getbool( &acc->irc->set, "lcnicks" ) ) nick_lc( nick ); } + g_free( store_handle ); - while( !nick_ok( nick ) || user_find( irc, nick ) ) + /* Now, find out if the nick is already in use at the moment, and make + subtle changes to make it unique. */ + while( !nick_ok( nick ) || user_find( acc->irc, nick ) ) { if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) ) { @@ -105,19 +106,19 @@ char *nick_get( irc_t *irc, const char *handle, struct prpl *proto, const char * { int i; - irc_usermsg( irc, "WARNING: Almost had an infinite loop in nick_get()! " - "This used to be a fatal BitlBee bug, but we tried to fix it. " - "This message should *never* appear anymore. " - "If it does, please *do* send us a bug report! " - "Please send all the following lines in your report:" ); + irc_usermsg( acc->irc, "WARNING: Almost had an infinite loop in nick_get()! " + "This used to be a fatal BitlBee bug, but we tried to fix it. " + "This message should *never* appear anymore. " + "If it does, please *do* send us a bug report! " + "Please send all the following lines in your report:" ); - irc_usermsg( irc, "Trying to get a sane nick for handle %s", handle ); + irc_usermsg( acc->irc, "Trying to get a sane nick for handle %s", handle ); for( i = 0; i < MAX_NICK_LENGTH; i ++ ) - irc_usermsg( irc, "Char %d: %c/%d", i, nick[i], nick[i] ); + irc_usermsg( acc->irc, "Char %d: %c/%d", i, nick[i], nick[i] ); - irc_usermsg( irc, "FAILED. Returning an insane nick now. Things might break. " - "Good luck, and please don't forget to paste the lines up here " - "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" ); + irc_usermsg( acc->irc, "FAILED. Returning an insane nick now. Things might break. " + "Good luck, and please don't forget to paste the lines up here " + "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" ); g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() ); @@ -125,30 +126,12 @@ char *nick_get( irc_t *irc, const char *handle, struct prpl *proto, const char * } } - return( nick ); + return nick; } -void nick_del( irc_t *irc, const char *nick ) +void nick_del( account_t *acc, const char *handle ) { - nick_t *l = NULL, *n = irc->nicks; - - while( n ) - { - if( g_strcasecmp( n->nick, nick ) == 0 ) - { - if( l ) - l->next = n->next; - else - irc->nicks = n->next; - - g_free( n->handle ); - g_free( n->nick ); - g_free( n ); - - break; - } - n = (l=n)->next; - } + g_hash_table_remove( acc->nicks, handle ); } @@ -23,17 +23,9 @@ Suite 330, Boston, MA 02111-1307 USA */ -typedef struct __NICK -{ - char *handle; - struct prpl *proto; - char *nick; - struct __NICK *next; -} nick_t; - -void nick_set( irc_t *irc, const char *handle, struct prpl *proto, const char *nick ); -char *nick_get( irc_t *irc, const char *handle, struct prpl *proto, const char *realname ); -void nick_del( irc_t *irc, const char *nick ); +void nick_set( account_t *acc, const char *handle, const char *nick ); +char *nick_get( account_t *acc, const char *handle, const char *realname ); +void nick_del( account_t *acc, const char *handle ); void nick_strip( char *nick ); int nick_ok( const char *nick ); diff --git a/protocols/Makefile b/protocols/Makefile index b74212f4..cc45fb09 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -9,7 +9,7 @@ -include ../Makefile.settings # [SH] Program variables -objects = $(EVENT_HANDLER) http_client.o md5.o nogaim.o proxy.o sha.o $(SSL_CLIENT) +objects = nogaim.o # [SH] The next two lines should contain the directory name (in $(subdirs)) # and the name of the object file, which should be linked into diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 029473fd..e765a475 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -560,37 +560,19 @@ static gboolean gjab_connected_ssl(gpointer data, void *source, b_input_conditio static void gjab_start(gjconn gjc) { - struct aim_user *user; + account_t *acc; int port = -1, ssl = 0; - char *server = NULL, *s; + char *server = NULL; if (!gjc || gjc->state != JCONN_STATE_OFF) return; - user = GJ_GC(gjc)->user; - if (*user->proto_opt[0]) { - /* If there's a dot, assume there's a hostname in the beginning */ - if (strchr(user->proto_opt[0], '.')) { - server = g_strdup(user->proto_opt[0]); - if ((s = strchr(server, ':'))) - *s = 0; - } - - /* After the hostname, there can be a port number */ - s = strchr(user->proto_opt[0], ':'); - if (s && isdigit(s[1])) - sscanf(s + 1, "%d", &port); - - /* And if there's the string ssl, the user wants an SSL-connection */ - if (strstr(user->proto_opt[0], ":ssl") || g_strcasecmp(user->proto_opt[0], "ssl") == 0) - ssl = 1; - } + acc = GJ_GC(gjc)->acc; + server = acc->server; + port = set_getint(&acc->set, "port"); + ssl = set_getbool(&acc->set, "ssl"); - if (port == -1 && !ssl) - port = DEFAULT_PORT; - else if (port == -1 && ssl) - port = DEFAULT_PORT_SSL; - else if (port < JABBER_PORT_MIN || port > JABBER_PORT_MAX) { + if (port < JABBER_PORT_MIN || port > JABBER_PORT_MAX) { serv_got_crap(GJ_GC(gjc), "For security reasons, the Jabber port number must be in the %d-%d range.", JABBER_PORT_MIN, JABBER_PORT_MAX); STATE_EVT(JCONN_STATE_OFF) return; @@ -613,9 +595,7 @@ static void gjab_start(gjconn gjc) gjc->fd = proxy_connect(server, port, gjab_connected, GJ_GC(gjc)); } - g_free(server); - - if (!user->gc || (gjc->fd < 0)) { + if (!acc->gc || (gjc->fd < 0)) { STATE_EVT(JCONN_STATE_OFF) return; } @@ -1515,18 +1495,75 @@ static void jabber_handlestate(gjconn gjc, int state) return; } -static void jabber_login(struct aim_user *user) +static void jabber_acc_init(account_t *acc) { - struct gaim_connection *gc = new_gaim_conn(user); - struct jabber_data *jd = gc->proto_data = g_new0(struct jabber_data, 1); - char *loginname = create_valid_jid(user->username, DEFAULT_SERVER, "BitlBee"); + set_t *s; + + s = set_add( &acc->set, "port", "5222", set_eval_int, acc ); + s->flags |= ACC_SET_OFFLINE_ONLY; + + s = set_add( &acc->set, "resource", "BitlBee", NULL, acc ); + s->flags |= ACC_SET_OFFLINE_ONLY; + + s = set_add( &acc->set, "server", NULL, set_eval_account, acc ); + s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; + + s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc ); + s->flags |= ACC_SET_OFFLINE_ONLY; +} +static void jabber_login(account_t *acc) +{ + struct gaim_connection *gc; + struct jabber_data *jd; + char *resource, *loginname; + + /* Time to move some data/things from the old syntax to the new one: */ + if (acc->server) { + char *s, *tmp_server; + int port; + + if (g_strcasecmp(acc->server, "ssl") == 0) { + set_setstr(&acc->set, "server", ""); + set_setint(&acc->set, "port", DEFAULT_PORT_SSL); + set_setstr(&acc->set, "ssl", "true"); + + g_free(acc->server); + acc->server = NULL; + } else if ((s = strchr(acc->server, ':'))) { + if (strstr(acc->server, ":ssl")) { + set_setint(&acc->set, "port", DEFAULT_PORT_SSL); + set_setstr(&acc->set, "ssl", "true"); + } + if (isdigit(s[1])) { + if (sscanf(s + 1, "%d", &port) == 1) + set_setint(&acc->set, "port", port); + } + tmp_server = g_strndup(acc->server, s - acc->server); + set_setstr(&acc->set, "server", tmp_server); + g_free(tmp_server); + } + } + + gc = new_gaim_conn(acc); + jd = gc->proto_data = g_new0(struct jabber_data, 1); + + if( strchr( acc->user, '@' ) == NULL ) + { + hide_login_progress( gc, "Invalid account name" ); + signoff( gc ); + return; + } + + resource = set_getstr(&acc->set, "resource"); + loginname = create_valid_jid(acc->user, DEFAULT_SERVER, resource); + jd->hash = g_hash_table_new(g_str_hash, g_str_equal); jd->chats = NULL; /* we have no chats yet */ set_login_progress(gc, 1, _("Connecting")); - if (!(jd->gjc = gjab_new(loginname, user->password, gc))) { + if (!(jd->gjc = gjab_new(loginname, acc->pass, gc))) { g_free(loginname); hide_login_progress(gc, _("Unable to connect")); signoff(gc); @@ -2336,6 +2373,7 @@ void jabber_init() ret->name = "jabber"; ret->away_states = jabber_away_states; + ret->acc_init = jabber_acc_init; ret->login = jabber_login; ret->close = jabber_close; ret->send_im = jabber_send_im; @@ -2348,7 +2386,7 @@ void jabber_init() ret->keepalive = jabber_keepalive; ret->alias_buddy = jabber_roster_update; ret->group_buddy = jabber_group_change; - ret->cmp_buddynames = g_strcasecmp; + ret->handle_cmp = g_strcasecmp; register_protocol (ret); } diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index a8d85a66..01b011c7 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -26,9 +26,19 @@ #include "nogaim.h" #include "msn.h" -static void msn_login( struct aim_user *acct ) +static char *msn_set_display_name( set_t *set, char *value ); + +static void msn_acc_init( account_t *acc ) { - struct gaim_connection *gc = new_gaim_conn( acct ); + set_t *s; + + s = set_add( &acc->set, "display_name", NULL, msn_set_display_name, acc ); + s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY; +} + +static void msn_login( account_t *acc ) +{ + struct gaim_connection *gc = new_gaim_conn( acc ); struct msn_data *md = g_new0( struct msn_data, 1 ); set_login_progress( gc, 1, "Connecting" ); @@ -36,7 +46,7 @@ static void msn_login( struct aim_user *acct ) gc->proto_data = md; md->fd = -1; - if( strchr( acct->username, '@' ) == NULL ) + if( strchr( acc->user, '@' ) == NULL ) { hide_login_progress( gc, "Invalid account name" ); signoff( gc ); @@ -211,36 +221,7 @@ static void msn_set_away( struct gaim_connection *gc, char *state, char *message static void msn_set_info( struct gaim_connection *gc, char *info ) { - int i; - char buf[1024], *fn, *s; - struct msn_data *md = gc->proto_data; - - if( strlen( info ) > 129 ) - { - do_error_dialog( gc, "Maximum name length exceeded", "MSN" ); - return; - } - - /* Of course we could use http_encode() here, but when we encode - every character, the server is less likely to complain about the - chosen name. However, the MSN server doesn't seem to like escaped - non-ASCII chars, so we keep those unescaped. */ - s = fn = g_new0( char, strlen( info ) * 3 + 1 ); - for( i = 0; info[i]; i ++ ) - if( info[i] & 128 ) - { - *s = info[i]; - s ++; - } - else - { - g_snprintf( s, 4, "%%%02X", info[i] ); - s += 3; - } - - g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, gc->username, fn ); - msn_write( gc, buf, strlen( buf ) ); - g_free( fn ); + msn_set_display_name( set_find( &gc->acc->set, "display_name" ), info ); } static void msn_get_info(struct gaim_connection *gc, char *who) @@ -379,11 +360,60 @@ static int msn_send_typing( struct gaim_connection *gc, char *who, int typing ) return( 1 ); } +static char *msn_set_display_name( set_t *set, char *value ) +{ + account_t *acc = set->data; + struct gaim_connection *gc = acc->gc; + struct msn_data *md; + char buf[1024], *fn, *s; + int i; + + /* Double-check. */ + if( gc == NULL ) + return NULL; + + md = gc->proto_data; + + if( strlen( value ) > 129 ) + { + serv_got_crap( gc, "Maximum name length exceeded" ); + return NULL; + } + + /* Of course we could use http_encode() here, but when we encode + every character, the server is less likely to complain about the + chosen name. However, the MSN server doesn't seem to like escaped + non-ASCII chars, so we keep those unescaped. */ + s = fn = g_new0( char, strlen( value ) * 3 + 1 ); + for( i = 0; value[i]; i ++ ) + if( value[i] & 128 ) + { + *s = value[i]; + s ++; + } + else + { + g_snprintf( s, 4, "%%%02X", value[i] ); + s += 3; + } + + g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, gc->username, fn ); + msn_write( gc, buf, strlen( buf ) ); + g_free( fn ); + + /* Returning NULL would be better, because the server still has to + confirm the name change. However, it looks a bit confusing to the + user. */ + return value; +} + void msn_init() { struct prpl *ret = g_new0(struct prpl, 1); + ret->name = "msn"; ret->login = msn_login; + ret->acc_init = msn_acc_init; ret->close = msn_close; ret->send_im = msn_send_im; ret->away_states = msn_away_states; @@ -403,7 +433,7 @@ void msn_init() ret->add_deny = msn_add_deny; ret->rem_deny = msn_rem_deny; ret->send_typing = msn_send_typing; - ret->cmp_buddynames = g_strcasecmp; + ret->handle_cmp = g_strcasecmp; register_protocol(ret); } diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index e4c2b68c..9774f3e2 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -222,11 +222,19 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( num_parts == 7 && strcmp( cmd[2], "OK" ) == 0 ) { + set_t *s; + http_decode( cmd[4] ); strncpy( gc->displayname, cmd[4], sizeof( gc->displayname ) ); gc->displayname[sizeof(gc->displayname)-1] = 0; + if( ( s = set_find( &gc->acc->set, "display_name" ) ) ) + { + g_free( s->value ); + s->value = g_strdup( cmd[4] ); + } + set_login_progress( gc, 1, "Authenticated, getting buddy list" ); g_snprintf( buf, sizeof( buf ), "SYN %d 0\r\n", ++md->trId ); @@ -516,9 +524,17 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) if( g_strcasecmp( cmd[3], gc->username ) == 0 ) { + set_t *s; + http_decode( cmd[4] ); strncpy( gc->displayname, cmd[4], sizeof( gc->displayname ) ); gc->displayname[sizeof(gc->displayname)-1] = 0; + + if( ( s = set_find( &gc->acc->set, "display_name" ) ) ) + { + g_free( s->value ); + s->value = g_strdup( cmd[4] ); + } } else { diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 78b51b53..54965b84 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -144,33 +144,21 @@ GSList *get_connections() { return connections; } /* multi.c */ -struct gaim_connection *new_gaim_conn( struct aim_user *user ) +struct gaim_connection *new_gaim_conn( account_t *acc ) { struct gaim_connection *gc; - account_t *a; gc = g_new0( struct gaim_connection, 1 ); - gc->prpl = user->prpl; - g_snprintf( gc->username, sizeof( gc->username ), "%s", user->username ); - g_snprintf( gc->password, sizeof( gc->password ), "%s", user->password ); - /* [MD] BUGFIX: don't set gc->irc to the global IRC, but use the one from the struct aim_user. - * This fixes daemon mode breakage where IRC doesn't point to the currently active connection. - */ - gc->irc = user->irc; - - connections = g_slist_append( connections, gc ); + /* Maybe we should get rid of this memory waste later. ;-) */ + g_snprintf( gc->username, sizeof( gc->username ), "%s", acc->user ); + g_snprintf( gc->password, sizeof( gc->password ), "%s", acc->pass ); - user->gc = gc; - gc->user = user; + gc->irc = acc->irc; + gc->acc = acc; + acc->gc = gc; - // Find the account_t so we can set its gc pointer - for( a = gc->irc->accounts; a; a = a->next ) - if( ( struct aim_user * ) a->gc == user ) - { - a->gc = gc; - break; - } + connections = g_slist_append( connections, gc ); return( gc ); } @@ -188,7 +176,6 @@ void destroy_gaim_conn( struct gaim_connection *gc ) } connections = g_slist_remove( connections, gc ); - g_free( gc->user ); g_free( gc ); } @@ -219,20 +206,20 @@ void serv_got_crap( struct gaim_connection *gc, char *format, ... ) text = g_strdup_vprintf( format, params ); va_end( params ); - if( ( g_strcasecmp( set_getstr( gc->irc, "strip_html" ), "always" ) == 0 ) || - ( ( gc->flags & OPT_CONN_HTML ) && set_getint( gc->irc, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &gc->irc->set, "strip_html" ), "always" ) == 0 ) || + ( ( gc->flags & OPT_CONN_HTML ) && set_getint( &gc->irc->set, "strip_html" ) ) ) strip_html( text ); /* Try to find a different connection on the same protocol. */ for( a = gc->irc->accounts; a; a = a->next ) - if( a->prpl == gc->prpl && a->gc != gc ) + if( a->prpl == gc->acc->prpl && a->gc != gc ) break; /* If we found one, include the screenname in the message. */ if( a ) - irc_usermsg( gc->irc, "%s(%s) - %s", gc->prpl->name, gc->username, text ); + irc_usermsg( gc->irc, "%s(%s) - %s", gc->acc->prpl->name, gc->username, text ); else - irc_usermsg( gc->irc, "%s - %s", gc->prpl->name, text ); + irc_usermsg( gc->irc, "%s - %s", gc->acc->prpl->name, text ); g_free( text ); } @@ -241,8 +228,8 @@ static gboolean send_keepalive( gpointer d, gint fd, b_input_condition cond ) { struct gaim_connection *gc = d; - if( gc->prpl && gc->prpl->keepalive ) - gc->prpl->keepalive( gc ); + if( gc->acc->prpl->keepalive ) + gc->acc->prpl->keepalive( gc ); return TRUE; } @@ -296,8 +283,9 @@ void signoff( struct gaim_connection *gc ) b_event_remove( gc->keepalive ); gc->flags |= OPT_LOGGING_OUT; + gc->keepalive = 0; - gc->prpl->close( gc ); + gc->acc->prpl->close( gc ); b_event_remove( gc->inpa ); while( u ) @@ -322,9 +310,9 @@ void signoff( struct gaim_connection *gc ) { /* Uhm... This is very sick. */ } - else if( !gc->wants_to_die && set_getint( irc, "auto_reconnect" ) ) + else if( !gc->wants_to_die && set_getint( &irc->set, "auto_reconnect" ) ) { - int delay = set_getint( irc, "auto_reconnect_delay" ); + int delay = set_getint( &irc->set, "auto_reconnect_delay" ); serv_got_crap( gc, "Reconnecting in %d seconds..", delay ); a->reconnect = b_timeout_add( delay * 1000, auto_reconnect, a ); @@ -363,12 +351,12 @@ void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *rea char *s; irc_t *irc = gc->irc; - if( set_getint( irc, "debug" ) && 0 ) /* This message is too useless */ + if( set_getint( &irc->set, "debug" ) && 0 ) /* This message is too useless */ serv_got_crap( gc, "Receiving user add from handle: %s", handle ); if( user_findhandle( gc, handle ) ) { - if( set_getint( irc, "debug" ) ) + if( set_getint( &irc->set, "debug" ) ) serv_got_crap( gc, "User already exists, ignoring add request: %s", handle ); return; @@ -377,7 +365,7 @@ void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *rea } memset( nick, 0, MAX_NICK_LENGTH + 1 ); - strcpy( nick, nick_get( gc->irc, handle, gc->prpl, realname ) ); + strcpy( nick, nick_get( gc->acc, handle, realname ) ); u = user_add( gc->irc, nick ); @@ -389,15 +377,15 @@ void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *rea u->host = g_strdup( s + 1 ); u->user = g_strndup( handle, s - handle ); } - else if( gc->user->proto_opt[0] && *gc->user->proto_opt[0] ) + else if( gc->acc->server ) { char *colon; - if( ( colon = strchr( gc->user->proto_opt[0], ':' ) ) ) - u->host = g_strndup( gc->user->proto_opt[0], - colon - gc->user->proto_opt[0] ); + if( ( colon = strchr( gc->acc->server, ':' ) ) ) + u->host = g_strndup( gc->acc->server, + colon - gc->acc->server ); else - u->host = g_strdup( gc->user->proto_opt[0] ); + u->host = g_strdup( gc->acc->server ); u->user = g_strdup( handle ); @@ -408,7 +396,7 @@ void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *rea } else { - u->host = g_strdup( gc->user->prpl->name ); + u->host = g_strdup( gc->acc->prpl->name ); u->user = g_strdup( handle ); } @@ -456,7 +444,7 @@ void serv_buddy_rename( struct gaim_connection *gc, char *handle, char *realname u->realname = g_strdup( realname ); - if( ( gc->flags & OPT_LOGGED_IN ) && set_getint( gc->irc, "display_namechanges" ) ) + if( ( gc->flags & OPT_LOGGED_IN ) && set_getint( &gc->irc->set, "display_namechanges" ) ) serv_got_crap( gc, "User `%s' changed name to `%s'", u->nick, u->realname ); } } @@ -478,7 +466,7 @@ void show_got_added_no( gpointer w, struct show_got_added_data *data ) void show_got_added_yes( gpointer w, struct show_got_added_data *data ) { - data->gc->prpl->add_buddy( data->gc, data->handle ); + data->gc->acc->prpl->add_buddy( data->gc, data->handle ); add_buddy( data->gc, NULL, data->handle, data->handle ); return show_got_added_no( w, data ); @@ -512,14 +500,14 @@ void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, in if( !u ) { - if( g_strcasecmp( set_getstr( gc->irc, "handle_unknown" ), "add" ) == 0 ) + if( g_strcasecmp( set_getstr( &gc->irc->set, "handle_unknown" ), "add" ) == 0 ) { add_buddy( gc, NULL, handle, NULL ); u = user_findhandle( gc, handle ); } else { - if( set_getint( gc->irc, "debug" ) || g_strcasecmp( set_getstr( gc->irc, "handle_unknown" ), "ignore" ) != 0 ) + if( set_getint( &gc->irc->set, "debug" ) || g_strcasecmp( set_getstr( &gc->irc->set, "handle_unknown" ), "ignore" ) != 0 ) { serv_got_crap( gc, "serv_got_update() for handle %s:", handle ); serv_got_crap( gc, "loggedin = %d, type = %d", loggedin, type ); @@ -557,11 +545,11 @@ void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, in remove_chat_buddy_silent( c, handle ); } - if( ( type & UC_UNAVAILABLE ) && ( !strcmp(gc->prpl->name, "oscar") || !strcmp(gc->prpl->name, "icq")) ) + if( ( type & UC_UNAVAILABLE ) && ( strcmp( gc->acc->prpl->name, "oscar" ) == 0 || strcmp( gc->acc->prpl->name, "icq" ) == 0 ) ) { u->away = g_strdup( "Away" ); } - else if( ( type & UC_UNAVAILABLE ) && ( !strcmp(gc->prpl->name, "jabber") ) ) + else if( ( type & UC_UNAVAILABLE ) && ( strcmp( gc->acc->prpl->name, "jabber" ) == 0 ) ) { if( type & UC_DND ) u->away = g_strdup( "Do Not Disturb" ); @@ -570,15 +558,15 @@ void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, in else // if( type & UC_AWAY ) u->away = g_strdup( "Away" ); } - else if( ( type & UC_UNAVAILABLE ) && gc->prpl->get_status_string ) + else if( ( type & UC_UNAVAILABLE ) && gc->acc->prpl->get_status_string ) { - u->away = g_strdup( gc->prpl->get_status_string( gc, type ) ); + u->away = g_strdup( gc->acc->prpl->get_status_string( gc, type ) ); } else u->away = NULL; /* LISPy... */ - if( ( set_getint( gc->irc, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ + if( ( set_getint( &gc->irc->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ ( u->online ) && /* Don't touch offline people */ ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ @@ -597,18 +585,18 @@ void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 f if( !u ) { - char *h = set_getstr( irc, "handle_unknown" ); + char *h = set_getstr( &irc->set, "handle_unknown" ); if( g_strcasecmp( h, "ignore" ) == 0 ) { - if( set_getint( irc, "debug" ) ) + if( set_getint( &irc->set, "debug" ) ) serv_got_crap( gc, "Ignoring message from unknown handle %s", handle ); return; } else if( g_strncasecmp( h, "add", 3 ) == 0 ) { - int private = set_getint( irc, "private" ); + int private = set_getint( &irc->set, "private" ); if( h[3] ) { @@ -629,8 +617,8 @@ void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 f } } - if( ( g_strcasecmp( set_getstr( gc->irc, "strip_html" ), "always" ) == 0 ) || - ( ( gc->flags & OPT_CONN_HTML ) && set_getint( gc->irc, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &gc->irc->set, "strip_html" ), "always" ) == 0 ) || + ( ( gc->flags & OPT_CONN_HTML ) && set_getint( &gc->irc->set, "strip_html" ) ) ) strip_html( msg ); while( strlen( msg ) > 425 ) @@ -670,7 +658,7 @@ void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout, int { user_t *u; - if( !set_getint( gc->irc, "typing_notice" ) ) + if( !set_getint( &gc->irc->set, "typing_notice" ) ) return; if( ( u = user_findhandle( gc, handle ) ) ) { @@ -692,7 +680,7 @@ void serv_got_chat_left( struct gaim_connection *gc, int id ) struct conversation *c, *l = NULL; GList *ir; - if( set_getint( gc->irc, "debug" ) ) + if( set_getint( &gc->irc->set, "debug" ) ) serv_got_crap( gc, "You were removed from conversation %d", (int) id ); for( c = gc->conversations; c && c->id != id; c = (l=c)->next ); @@ -731,14 +719,14 @@ void serv_got_chat_in( struct gaim_connection *gc, int id, char *who, int whispe user_t *u; /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */ - if( g_strcasecmp( who, gc->user->username ) == 0 ) + if( g_strcasecmp( who, gc->username ) == 0 ) return; u = user_findhandle( gc, who ); for( c = gc->conversations; c && c->id != id; c = c->next ); - if( ( g_strcasecmp( set_getstr( gc->irc, "strip_html" ), "always" ) == 0 ) || - ( ( gc->flags & OPT_CONN_HTML ) && set_getint( gc->irc, "strip_html" ) ) ) + if( ( g_strcasecmp( set_getstr( &gc->irc->set, "strip_html" ), "always" ) == 0 ) || + ( ( gc->flags & OPT_CONN_HTML ) && set_getint( &gc->irc->set, "strip_html" ) ) ) strip_html( msg ); if( c && u ) @@ -771,7 +759,7 @@ struct conversation *serv_got_joined_chat( struct gaim_connection *gc, int id, c c->channel = g_strdup( s ); g_free( s ); - if( set_getint( gc->irc, "debug" ) ) + if( set_getint( &gc->irc->set, "debug" ) ) serv_got_crap( gc, "Creating new conversation: (id=%d,handle=%s)", id, handle ); return( c ); @@ -785,11 +773,11 @@ void add_chat_buddy( struct conversation *b, char *handle ) user_t *u = user_findhandle( b->gc, handle ); int me = 0; - if( set_getint( b->gc->irc, "debug" ) ) + if( set_getint( &b->gc->irc->set, "debug" ) ) serv_got_crap( b->gc, "User %s added to conversation %d", handle, b->id ); /* It might be yourself! */ - if( b->gc->prpl->cmp_buddynames( handle, b->gc->user->username ) == 0 ) + if( b->gc->acc->prpl->handle_cmp( handle, b->gc->username ) == 0 ) { u = user_find( b->gc->irc, b->gc->irc->nick ); if( !b->joined ) @@ -819,11 +807,11 @@ void remove_chat_buddy( struct conversation *b, char *handle, char *reason ) user_t *u; int me = 0; - if( set_getint( b->gc->irc, "debug" ) ) + if( set_getint( &b->gc->irc->set, "debug" ) ) serv_got_crap( b->gc, "User %s removed from conversation %d (%s)", handle, b->id, reason ? reason : "" ); /* It might be yourself! */ - if( g_strcasecmp( handle, b->gc->user->username ) == 0 ) + if( g_strcasecmp( handle, b->gc->username ) == 0 ) { u = user_find( b->gc->irc, b->gc->irc->nick ); b->joined = 0; @@ -881,8 +869,9 @@ struct conversation *conv_findchannel( char *channel ) return( NULL ); } -char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value ) +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 ) ) @@ -896,7 +885,7 @@ char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value ) /* Horror.... */ - if( st != set_getint( irc, "away_devoice" ) ) + if( st != set_getint( &irc->set, "away_devoice" ) ) { char list[80] = ""; user_t *u = irc->users; @@ -936,7 +925,7 @@ char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value ) irc->channel, pm, v, list ); } - return( set_eval_bool( irc, set, value ) ); + return( set_eval_bool( set, value ) ); } @@ -956,7 +945,7 @@ int bim_buddy_msg( struct gaim_connection *gc, char *handle, char *msg, int flag msg = buf; } - st = gc->prpl->send_im( gc, handle, msg, strlen( msg ), flags ); + st = gc->acc->prpl->send_im( gc, handle, msg, strlen( msg ), flags ); g_free( buf ); return st; @@ -973,7 +962,7 @@ int bim_chat_msg( struct gaim_connection *gc, int id, char *msg ) msg = buf; } - st = gc->prpl->chat_send( gc, id, msg ); + st = gc->acc->prpl->chat_send( gc, id, msg ); g_free( buf ); return st; @@ -987,7 +976,7 @@ int bim_set_away( struct gaim_connection *gc, char *away ) char *s; if( !away ) away = ""; - ms = m = gc->prpl->away_states( gc ); + ms = m = gc->acc->prpl->away_states( gc ); while( m ) { @@ -1008,19 +997,19 @@ int bim_set_away( struct gaim_connection *gc, char *away ) if( m ) { - gc->prpl->set_away( gc, m->data, *away ? away : NULL ); + gc->acc->prpl->set_away( gc, m->data, *away ? away : NULL ); } else { s = bim_away_alias_find( ms, away ); if( s ) { - gc->prpl->set_away( gc, s, away ); - if( set_getint( gc->irc, "debug" ) ) + gc->acc->prpl->set_away( gc, s, away ); + if( set_getint( &gc->irc->set, "debug" ) ) serv_got_crap( gc, "Setting away state to %s", s ); } else - gc->prpl->set_away( gc, GAIM_AWAY_CUSTOM, away ); + gc->acc->prpl->set_away( gc, GAIM_AWAY_CUSTOM, away ); } g_list_free( ms ); @@ -1072,46 +1061,46 @@ static char *bim_away_alias_find( GList *gcm, char *away ) void bim_add_allow( struct gaim_connection *gc, char *handle ) { - if( g_slist_find_custom( gc->permit, handle, (GCompareFunc) gc->prpl->cmp_buddynames ) == NULL ) + if( g_slist_find_custom( gc->permit, handle, (GCompareFunc) gc->acc->prpl->handle_cmp ) == NULL ) { gc->permit = g_slist_prepend( gc->permit, g_strdup( handle ) ); } - gc->prpl->add_permit( gc, handle ); + gc->acc->prpl->add_permit( gc, handle ); } void bim_rem_allow( struct gaim_connection *gc, char *handle ) { GSList *l; - if( ( l = g_slist_find_custom( gc->permit, handle, (GCompareFunc) gc->prpl->cmp_buddynames ) ) ) + if( ( l = g_slist_find_custom( gc->permit, handle, (GCompareFunc) gc->acc->prpl->handle_cmp ) ) ) { g_free( l->data ); gc->permit = g_slist_delete_link( gc->permit, l ); } - gc->prpl->rem_permit( gc, handle ); + gc->acc->prpl->rem_permit( gc, handle ); } void bim_add_block( struct gaim_connection *gc, char *handle ) { - if( g_slist_find_custom( gc->deny, handle, (GCompareFunc) gc->prpl->cmp_buddynames ) == NULL ) + if( g_slist_find_custom( gc->deny, handle, (GCompareFunc) gc->acc->prpl->handle_cmp ) == NULL ) { gc->deny = g_slist_prepend( gc->deny, g_strdup( handle ) ); } - gc->prpl->add_deny( gc, handle ); + gc->acc->prpl->add_deny( gc, handle ); } void bim_rem_block( struct gaim_connection *gc, char *handle ) { GSList *l; - if( ( l = g_slist_find_custom( gc->deny, handle, (GCompareFunc) gc->prpl->cmp_buddynames ) ) ) + if( ( l = g_slist_find_custom( gc->deny, handle, (GCompareFunc) gc->acc->prpl->handle_cmp ) ) ) { g_free( l->data ); gc->deny = g_slist_delete_link( gc->deny, l ); } - gc->prpl->rem_deny( gc, handle ); + gc->acc->prpl->rem_deny( gc, handle ); } diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 2080465c..bae4489f 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -38,6 +38,7 @@ #define _NOGAIM_H #include "bitlbee.h" +#include "account.h" #include "proxy.h" #include "md5.h" #include "sha.h" @@ -62,7 +63,7 @@ /* ok. now the fun begins. first we create a connection structure */ struct gaim_connection { - struct prpl *prpl; + account_t *acc; guint32 flags; /* each connection then can have its own protocol-specific data */ @@ -78,8 +79,6 @@ struct gaim_connection GSList *deny; int permdeny; - struct aim_user *user; - char username[64]; char displayname[128]; char password[32]; @@ -125,26 +124,12 @@ struct buddy { struct gaim_connection *gc; /* the connection it belongs to */ }; -struct aim_user { - char username[64]; - char alias[SELF_ALIAS_LEN]; - char password[32]; - char user_info[2048]; - int options; - struct prpl *prpl; - /* prpls can use this to save information about the user, - * like which server to connect to, etc */ - char proto_opt[7][256]; - - struct gaim_connection *gc; - irc_t *irc; -}; - struct prpl { int options; const char *name; - void (* login) (struct aim_user *); + void (* acc_init) (account_t *); + void (* login) (account_t *); void (* keepalive) (struct gaim_connection *); void (* close) (struct gaim_connection *); @@ -179,7 +164,7 @@ struct prpl { GList *(* away_states)(struct gaim_connection *gc); /* Mainly for AOL, since they think "Bung hole" == "Bu ngho le". *sigh* */ - int (* cmp_buddynames) (const char *who1, const char *who2); + int (* handle_cmp) (const char *who1, const char *who2); }; #define UC_UNAVAILABLE 1 @@ -205,13 +190,13 @@ void bim_add_block( struct gaim_connection *gc, char *handle ); void bim_rem_block( struct gaim_connection *gc, char *handle ); void nogaim_init(); -char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value ); +char *set_eval_away_devoice( set_t *set, char *value ); gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond ); void cancel_auto_reconnect( struct account *a ); /* multi.c */ -G_MODULE_EXPORT struct gaim_connection *new_gaim_conn( struct aim_user *user ); +G_MODULE_EXPORT struct gaim_connection *new_gaim_conn( account_t *acc ); G_MODULE_EXPORT void destroy_gaim_conn( struct gaim_connection *gc ); G_MODULE_EXPORT void set_login_progress( struct gaim_connection *gc, int step, char *msg ); G_MODULE_EXPORT void hide_login_progress( struct gaim_connection *gc, char *msg ); diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 7c76533a..f65332dc 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -355,18 +355,26 @@ static gboolean oscar_login_connect(gpointer data, gint source, b_input_conditio return FALSE; } -static void oscar_login(struct aim_user *user) { +static void oscar_acc_init(account_t *acc) +{ + set_t *s; + + s = set_add( &acc->set, "server", NULL, set_eval_account, acc ); + s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; +} + +static void oscar_login(account_t *acc) { aim_session_t *sess; aim_conn_t *conn; char buf[256]; - struct gaim_connection *gc = new_gaim_conn(user); + struct gaim_connection *gc = new_gaim_conn(acc); struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1); - if (isdigit(*user->username)) { + if (isdigit(acc->user[0])) { odata->icq = TRUE; /* This is odd but it's necessary for a proper do_import and do_export. We don't do those anymore, but let's stick with it, just in case - it accidentally fixes something else too... */ + it accidentally fixes something else too... </bitlbee> */ gc->password[8] = 0; } else { gc->flags |= OPT_CONN_HTML; @@ -389,9 +397,15 @@ static void oscar_login(struct aim_user *user) { return; } - if (g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.icq.com") != 0 && - g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.oscar.aol.com") != 0) { - serv_got_crap(gc, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails.",user->proto_opt[USEROPT_AUTH]); + if (acc->server == NULL) { + hide_login_progress(gc, "No servername specified"); + signoff(gc); + return; + } + + if (g_strcasecmp(acc->server, "login.icq.com") != 0 && + g_strcasecmp(acc->server, "login.oscar.aol.com") != 0) { + serv_got_crap(gc, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails.",acc->server); } g_snprintf(buf, sizeof(buf), _("Signon: %s"), gc->username); @@ -401,11 +415,7 @@ static void oscar_login(struct aim_user *user) { aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0); conn->status |= AIM_CONN_STATUS_INPROGRESS; - conn->fd = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ? - user->proto_opt[USEROPT_AUTH] : AIM_DEFAULT_LOGIN_SERVER, - user->proto_opt[USEROPT_AUTHPORT][0] ? - atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT, - oscar_login_connect, gc); + conn->fd = proxy_connect(acc->server, AIM_LOGIN_PORT, oscar_login_connect, gc); if (conn->fd < 0) { hide_login_progress(gc, _("Couldn't connect to host")); signoff(gc); @@ -484,14 +494,11 @@ static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; struct aim_authresp_info *info; int i; char *host; int port; - struct aim_user *user; aim_conn_t *bosconn; struct gaim_connection *gc = sess->aux_data; struct oscar_data *od = gc->proto_data; - user = gc->user; - port = user->proto_opt[USEROPT_AUTHPORT][0] ? - atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT, + port = AIM_LOGIN_PORT; va_start(ap, fr); info = va_arg(ap, struct aim_authresp_info *); @@ -870,19 +877,16 @@ static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; struct aim_redirect_data *redir; struct gaim_connection *gc = sess->aux_data; - struct aim_user *user = gc->user; aim_conn_t *tstconn; int i; char *host; int port; - port = user->proto_opt[USEROPT_AUTHPORT][0] ? - atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT, - va_start(ap, fr); redir = va_arg(ap, struct aim_redirect_data *); va_end(ap); + port = AIM_LOGIN_PORT; for (i = 0; i < (int)strlen(redir->ip); i++) { if (redir->ip[i] == ':') { port = atoi(&(redir->ip[i+1])); @@ -1722,8 +1726,11 @@ static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...) odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen; + /* FIXME: It seems we're not really using this, and it broke now that + struct aim_user is dead. aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps); - + */ + return 1; } @@ -2655,6 +2662,7 @@ void oscar_init() ret->name = "oscar"; ret->away_states = oscar_away_states; ret->login = oscar_login; + ret->acc_init = oscar_acc_init; ret->close = oscar_close; ret->send_im = oscar_send_im; ret->get_info = oscar_get_info; @@ -2672,9 +2680,10 @@ void oscar_init() ret->rem_deny = oscar_rem_deny; ret->set_permit_deny = oscar_set_permit_deny; ret->keepalive = oscar_keepalive; - ret->cmp_buddynames = aim_sncmp; ret->get_status_string = oscar_get_status_string; ret->send_typing = oscar_send_typing; + + ret->handle_cmp = aim_sncmp; register_protocol(ret); } diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c index c691f18b..69b63baa 100644 --- a/protocols/yahoo/libyahoo2.c +++ b/protocols/yahoo/libyahoo2.c @@ -89,6 +89,8 @@ char *strchr (), *strrchr (); #define vsnprintf _vsnprintf #endif +#include "base64.h" + #ifdef USE_STRUCT_CALLBACKS struct yahoo_callbacks *yc=NULL; @@ -694,34 +696,10 @@ static void yahoo_packet_dump(unsigned char *data, int len) } } -static char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789._"; -static void to_y64(unsigned char *out, const unsigned char *in, int inlen) /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ +static void to_y64(unsigned char *out, const unsigned char *in, int inlen) { - for (; inlen >= 3; inlen -= 3) - { - *out++ = base64digits[in[0] >> 2]; - *out++ = base64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; - *out++ = base64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)]; - *out++ = base64digits[in[2] & 0x3f]; - in += 3; - } - if (inlen > 0) - { - unsigned char fragment; - - *out++ = base64digits[in[0] >> 2]; - fragment = (in[0] << 4) & 0x30; - if (inlen > 1) - fragment |= in[1] >> 4; - *out++ = base64digits[fragment]; - *out++ = (inlen < 2) ? '-' - : base64digits[(in[1] << 2) & 0x3c]; - *out++ = '-'; - } - *out = '\0'; + base64_encode_real(in, inlen, out, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"); } static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, int length) diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 79c0febb..23c6f813 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -120,16 +120,16 @@ static char *byahoo_strip( char *in ) return( g_strndup( in, len ) ); } -static void byahoo_login( struct aim_user *user ) +static void byahoo_login( account_t *acc ) { - struct gaim_connection *gc = new_gaim_conn( user ); + struct gaim_connection *gc = new_gaim_conn( acc ); struct byahoo_data *yd = gc->proto_data = g_new0( struct byahoo_data, 1 ); yd->logged_in = FALSE; yd->current_status = YAHOO_STATUS_AVAILABLE; set_login_progress( gc, 1, "Connecting" ); - yd->y2_id = yahoo_init( user->username, user->password ); + yd->y2_id = yahoo_init( acc->user, acc->pass ); yahoo_login( yd->y2_id, yd->current_status ); } @@ -408,7 +408,8 @@ void byahoo_init( ) ret->chat_invite = byahoo_chat_invite; ret->chat_leave = byahoo_chat_leave; ret->chat_open = byahoo_chat_open; - ret->cmp_buddynames = g_strcasecmp; + + ret->handle_cmp = g_strcasecmp; register_protocol(ret); } @@ -424,7 +425,7 @@ static struct gaim_connection *byahoo_get_gc_by_id( int id ) gc = l->data; yd = gc->proto_data; - if( !strcmp(gc->prpl->name, "yahoo") && yd->y2_id == id ) + if( strcmp( gc->acc->prpl->name, "yahoo" ) == 0 && yd->y2_id == id ) return( gc ); } @@ -62,7 +62,7 @@ query_t *query_add( irc_t *irc, struct gaim_connection *gc, char *question, void irc->queries = q; } - if( g_strcasecmp( set_getstr( irc, "query_order" ), "lifo" ) == 0 || irc->queries == q ) + if( g_strcasecmp( set_getstr( &irc->set, "query_order" ), "lifo" ) == 0 || irc->queries == q ) query_display( irc, q ); return( q ); @@ -171,7 +171,7 @@ static query_t *query_default( irc_t *irc ) { query_t *q; - if( g_strcasecmp( set_getstr( irc, "query_order" ), "fifo" ) == 0 ) + if( g_strcasecmp( set_getstr( &irc->set, "query_order" ), "fifo" ) == 0 ) q = irc->queries; else for( q = irc->queries; q && q->next; q = q->next ); diff --git a/root_commands.c b/root_commands.c index 3d3584b3..389266eb 100644 --- a/root_commands.c +++ b/root_commands.c @@ -126,9 +126,12 @@ static void cmd_help( irc_t *irc, char **cmd ) } } +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 ); + char *account_on[] = { "account", "on", NULL }; switch (status) { case STORAGE_INVALID_PASSWORD: @@ -138,11 +141,14 @@ static void cmd_identify( irc_t *irc, char **cmd ) irc_usermsg( irc, "The nick is (probably) not registered" ); break; case STORAGE_OK: - irc_usermsg( irc, "Password accepted" ); + irc_usermsg( irc, "Password accepted, settings and accounts loaded" ); irc_umode_set( irc, "+R", 1 ); + if( set_getint( &irc->set, "auto_connect" ) ) + cmd_account( irc, account_on ); break; + case STORAGE_OTHER_ERROR: default: - irc_usermsg( irc, "Something very weird happened" ); + irc_usermsg( irc, "Unknown error while loading configuration" ); break; } } @@ -225,9 +231,8 @@ static void cmd_account( irc_t *irc, char **cmd ) } a = account_add( irc, prpl, cmd[3], cmd[4] ); - if( cmd[5] ) - a->server = g_strdup( cmd[5] ); + set_setstr( &a->set, "server", cmd[5] ); irc_usermsg( irc, "Account successfully added" ); } @@ -305,7 +310,7 @@ static void cmd_account( irc_t *irc, char **cmd ) irc_usermsg( irc, "Trying to get all accounts connected..." ); for( a = irc->accounts; a; a = a->next ) - if( !a->gc ) + if( !a->gc && a->auto_connect ) account_on( irc, a ); } else @@ -351,6 +356,76 @@ static void cmd_account( irc_t *irc, char **cmd ) return; } } + else if( g_strcasecmp( cmd[1], "set" ) == 0 ) + { + char *acc_handle, *set_name = NULL, *tmp; + + if( !cmd[2] ) + { + irc_usermsg( irc, "Not enough parameters given (need %d)", 2 ); + return; + } + + acc_handle = g_strdup( cmd[2] ); + if( ( tmp = strchr( acc_handle, '/' ) ) ) + { + *tmp = 0; + set_name = tmp + 1; + } + a = account_get( irc, acc_handle ); + + if( a == NULL ) + { + g_free( acc_handle ); + irc_usermsg( irc, "Invalid account" ); + return; + } + + if( cmd[3] ) + { + set_t *s = set_find( &a->set, set_name ); + + if( a->gc && 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->gc && 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; + } + + set_setstr( &a->set, set_name, cmd[3] ); + + if( ( strcmp( cmd[3], "=" ) ) == 0 && cmd[4] ) + irc_usermsg( irc, "Warning: Correct syntax: \002account set <variable> <value>\002 (without =)" ); + } + 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 ); + } else { irc_usermsg( irc, "Unknown command: account %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[1] ); @@ -393,14 +468,14 @@ static void cmd_add( irc_t *irc, char **cmd ) } else { - nick_set( irc, cmd[2], a->gc->prpl, cmd[3] ); + nick_set( a, cmd[2], cmd[3] ); } } /* By making this optional, you can talk to people without having to add them to your *real* (server-side) contact list. */ if( add_for_real ) - a->gc->prpl->add_buddy( a->gc, cmd[2] ); + a->gc->acc->prpl->add_buddy( a->gc, cmd[2] ); add_buddy( a->gc, NULL, cmd[2], cmd[2] ); @@ -434,13 +509,13 @@ static void cmd_info( irc_t *irc, char **cmd ) return; } - if( !gc->prpl->get_info ) + if( !gc->acc->prpl->get_info ) { irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] ); } else { - gc->prpl->get_info( gc, cmd[2] ); + gc->acc->prpl->get_info( gc, cmd[2] ); } } @@ -475,7 +550,7 @@ static void cmd_rename( irc_t *irc, char **cmd ) } else if( u->send_handler == buddy_send_handler ) { - nick_set( irc, u->handle, u->gc->prpl, cmd[2] ); + nick_set( u->gc->acc, u->handle, cmd[2] ); } irc_usermsg( irc, "Nick successfully changed" ); @@ -494,9 +569,9 @@ static void cmd_remove( irc_t *irc, char **cmd ) } s = g_strdup( u->handle ); - u->gc->prpl->remove_buddy( u->gc, u->handle, NULL ); + u->gc->acc->prpl->remove_buddy( u->gc, u->handle, NULL ); user_del( irc, cmd[1] ); - nick_del( irc, cmd[1] ); + nick_del( u->gc->acc, u->handle ); irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] ); g_free( s ); @@ -551,7 +626,7 @@ static void cmd_block( irc_t *irc, char **cmd ) return; } - if( !gc->prpl->add_deny || !gc->prpl->rem_permit ) + if( !gc->acc->prpl->add_deny || !gc->acc->prpl->rem_permit ) { irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] ); } @@ -610,7 +685,7 @@ static void cmd_allow( irc_t *irc, char **cmd ) return; } - if( !gc->prpl->rem_deny || !gc->prpl->add_permit ) + if( !gc->acc->prpl->rem_deny || !gc->acc->prpl->add_permit ) { irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] ); } @@ -665,16 +740,18 @@ static void cmd_set( irc_t *irc, char **cmd ) { if( cmd[1] && cmd[2] ) { - set_setstr( irc, cmd[1], cmd[2] ); + set_setstr( &irc->set, cmd[1], cmd[2] ); if( ( strcmp( cmd[2], "=" ) ) == 0 && cmd[3] ) irc_usermsg( irc, "Warning: Correct syntax: \002set <variable> <value>\002 (without =)" ); } if( cmd[1] ) /* else 'forgotten' on purpose.. Must show new value after changing */ { - char *s = set_getstr( irc, cmd[1] ); + char *s = set_getstr( &irc->set, cmd[1] ); if( s ) irc_usermsg( irc, "%s = `%s'", cmd[1], s ); + else + irc_usermsg( irc, "%s is empty", cmd[1] ); } else { @@ -683,6 +760,8 @@ static void cmd_set( irc_t *irc, char **cmd ) { 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; } } @@ -726,7 +805,7 @@ static void cmd_blist( irc_t *irc, char **cmd ) { if( online == 1 ) { - g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->gc->user->prpl->name ); + g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->gc->acc->prpl->name ); irc_usermsg( irc, format, u->nick, s, "Online" ); } @@ -737,7 +816,7 @@ static void cmd_blist( irc_t *irc, char **cmd ) { if( away == 1 ) { - g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->gc->user->prpl->name ); + g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->gc->acc->prpl->name ); irc_usermsg( irc, format, u->nick, s, u->away ); } n_away ++; @@ -747,7 +826,7 @@ static void cmd_blist( irc_t *irc, char **cmd ) { if( offline == 1 ) { - g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->gc->user->prpl->name ); + g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->gc->acc->prpl->name ); irc_usermsg( irc, format, u->nick, s, "Offline" ); } n_offline ++; @@ -772,7 +851,7 @@ static void cmd_nick( irc_t *irc, char **cmd ) { irc_usermsg( irc, "Your name is `%s'" , a->gc->displayname ? a->gc->displayname : "NULL" ); } - else if ( !a->gc->prpl->set_info ) + else if ( !a->prpl->set_info ) { irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] ); } @@ -780,7 +859,7 @@ static void cmd_nick( irc_t *irc, char **cmd ) { irc_usermsg( irc, "Setting your name to `%s'", cmd[2] ); - a->gc->prpl->set_info( a->gc, cmd[2] ); + a->prpl->set_info( a->gc, cmd[2] ); } } @@ -799,62 +878,11 @@ static void cmd_qlist( irc_t *irc, char **cmd ) for( num = 0; q; q = q->next, num ++ ) if( q->gc ) /* Not necessary yet, but it might come later */ - irc_usermsg( irc, "%d, %s(%s): %s", num, q->gc->prpl->name, q->gc->username, q->question ); + irc_usermsg( irc, "%d, %s(%s): %s", num, q->gc->acc->prpl->name, q->gc->username, q->question ); else irc_usermsg( irc, "%d, BitlBee: %s", num, q->question ); } -static void cmd_import_buddies( irc_t *irc, char **cmd ) -{ - struct gaim_connection *gc; - account_t *a; - nick_t *n; - - if( !( a = account_get( irc, cmd[1] ) ) ) - { - irc_usermsg( irc, "Invalid account" ); - return; - } - else if( !( ( gc = a->gc ) && ( a->gc->flags & OPT_LOGGED_IN ) ) ) - { - irc_usermsg( irc, "That account is not on-line" ); - return; - } - - if( cmd[2] ) - { - if( g_strcasecmp( cmd[2], "clear" ) == 0 ) - { - user_t *u; - - for( u = irc->users; u; u = u->next ) - if( u->gc == gc ) - { - u->gc->prpl->remove_buddy( u->gc, u->handle, NULL ); - user_del( irc, u->nick ); - } - - irc_usermsg( irc, "Old buddy list cleared." ); - } - else - { - irc_usermsg( irc, "Invalid argument: %s", cmd[2] ); - return; - } - } - - for( n = gc->irc->nicks; n; n = n->next ) - { - if( n->proto == gc->prpl && !user_findhandle( gc, n->handle ) ) - { - gc->prpl->add_buddy( gc, n->handle ); - add_buddy( gc, NULL, n->handle, NULL ); - } - } - - irc_usermsg( irc, "Sent all add requests. Please wait for a while, the server needs some time to handle all the adds." ); -} - const command_t commands[] = { { "help", 0, cmd_help, 0 }, { "identify", 1, cmd_identify, 0 }, @@ -873,7 +901,6 @@ const command_t commands[] = { { "no", 0, cmd_yesno, 0 }, { "blist", 0, cmd_blist, 0 }, { "nick", 1, cmd_nick, 0 }, - { "import_buddies", 1, cmd_import_buddies, 0 }, { "qlist", 0, cmd_qlist, 0 }, { NULL } }; @@ -25,23 +25,24 @@ #define BITLBEE_CORE #include "bitlbee.h" -set_t *set_add( irc_t *irc, char *key, char *def, void *eval ) +set_t *set_add( set_t **head, char *key, char *def, void *eval, void *data ) { - set_t *s = set_find( irc, key ); + set_t *s = set_find( head, key ); + /* Possibly the setting already exists. If it doesn't exist yet, + we create it. If it does, we'll just change the default. */ if( !s ) { - if( ( s = irc->set ) ) + if( ( s = *head ) ) { while( s->next ) s = s->next; - s->next = g_new ( set_t, 1 ); + s->next = g_new0( set_t, 1 ); s = s->next; } else { - s = irc->set = g_new( set_t, 1 ); + s = *head = g_new0( set_t, 1 ); } - memset( s, 0, sizeof( set_t ) ); s->key = g_strdup( key ); } @@ -52,19 +53,15 @@ set_t *set_add( irc_t *irc, char *key, char *def, void *eval ) } if( def ) s->def = g_strdup( def ); - if( s->eval ) - { - g_free( s->eval ); - s->eval = NULL; - } - if( eval ) s->eval = eval; + s->eval = eval; + s->data = data; - return( s ); + return s; } -set_t *set_find( irc_t *irc, char *key ) +set_t *set_find( set_t **head, char *key ) { - set_t *s = irc->set; + set_t *s = *head; while( s ) { @@ -73,46 +70,56 @@ set_t *set_find( irc_t *irc, char *key ) s = s->next; } - return( s ); + return s; } -char *set_getstr( irc_t *irc, char *key ) +char *set_getstr( set_t **head, char *key ) { - set_t *s = set_find( irc, key ); + set_t *s = set_find( head, key ); if( !s || ( !s->value && !s->def ) ) - return( NULL ); + return NULL; - return( s->value?s->value:s->def ); + return s->value ? s->value : s->def; } -int set_getint( irc_t *irc, char *key ) +int set_getint( set_t **head, char *key ) { - char *s = set_getstr( irc, key ); + char *s = set_getstr( head, key ); int i = 0; if( !s ) - return( 0 ); + return 0; if( ( g_strcasecmp( s, "true" ) == 0 ) || ( g_strcasecmp( s, "yes" ) == 0 ) || ( g_strcasecmp( s, "on" ) == 0 ) ) - return( 1 ); + return 1; if( sscanf( s, "%d", &i ) != 1 ) - return( 0 ); + return 0; + + return i; +} + +int set_getbool( set_t **head, char *key ) +{ + char *s = set_getstr( head, key ); - return( i ); + if( !s ) + return 0; + + return bool2int( s ); } -int set_setstr( irc_t *irc, char *key, char *value ) +int set_setstr( set_t **head, char *key, char *value ) { - set_t *s = set_find( irc, key ); + set_t *s = set_find( head, key ); char *nv = value; if( !s ) - s = set_add( irc, key, NULL, NULL ); + s = set_add( head, key, NULL, NULL, NULL ); - if( s->eval && !( nv = s->eval( irc, s, value ) ) ) - return( 0 ); + if( s->eval && !( nv = s->eval( s, value ) ) ) + return 0; if( s->value ) { @@ -120,26 +127,28 @@ int set_setstr( irc_t *irc, char *key, char *value ) s->value = NULL; } + /* If there's a default setting and it's equal to what we're trying to + set, stick with s->value = NULL. Otherwise, remember the setting. */ if( !s->def || ( strcmp( nv, s->def ) != 0 ) ) s->value = g_strdup( nv ); if( nv != value ) g_free( nv ); - return( 1 ); + return 1; } -int set_setint( irc_t *irc, char *key, int value ) +int set_setint( set_t **head, char *key, int value ) { char s[24]; /* Not quite 128-bit clean eh? ;-) */ - sprintf( s, "%d", value ); - return( set_setstr( irc, key, s ) ); + g_snprintf( s, sizeof( s ), "%d", value ); + return set_setstr( head, key, s ); } -void set_del( irc_t *irc, char *key ) +void set_del( set_t **head, char *key ) { - set_t *s = irc->set, *t = NULL; + set_t *s = *head, *t = NULL; while( s ) { @@ -152,7 +161,7 @@ void set_del( irc_t *irc, char *key ) if( t ) t->next = s->next; else - irc->set = s->next; + *head = s->next; g_free( s->key ); if( s->value ) g_free( s->value ); @@ -161,27 +170,23 @@ void set_del( irc_t *irc, char *key ) } } -char *set_eval_int( irc_t *irc, set_t *set, char *value ) +char *set_eval_int( set_t *set, char *value ) { - char *s = value; + char *s; - for( ; *s; s ++ ) - if( *s < '0' || *s > '9' ) - return( NULL ); + for( s = value; *s; s ++ ) + if( !isdigit( *s ) ) + return NULL; - return( value ); + return value; } -char *set_eval_bool( irc_t *irc, set_t *set, char *value ) +char *set_eval_bool( set_t *set, char *value ) { - if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) - return( value ); - if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) - return( value ); - return( set_eval_int( irc, set, value ) ); + return is_bool( value ) ? value : NULL; } -char *set_eval_to_char( irc_t *irc, set_t *set, char *value ) +char *set_eval_to_char( set_t *set, char *value ) { char *s = g_new( char, 3 ); @@ -190,36 +195,42 @@ char *set_eval_to_char( irc_t *irc, set_t *set, char *value ) else sprintf( s, "%c ", *value ); - return( s ); + return s; } -char *set_eval_ops( irc_t *irc, set_t *set, char *value ) +char *set_eval_ops( set_t *set, char *value ) { + irc_t *irc = set->data; + if( g_strcasecmp( value, "user" ) == 0 ) - { irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, irc->channel, "+o-o", irc->nick, irc->mynick ); - return( value ); - } else if( g_strcasecmp( value, "root" ) == 0 ) - { irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, irc->channel, "-o+o", irc->nick, irc->mynick ); - return( value ); - } else if( g_strcasecmp( value, "both" ) == 0 ) - { irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, irc->channel, "+oo", irc->nick, irc->mynick ); - return( value ); - } else if( g_strcasecmp( value, "none" ) == 0 ) - { irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, irc->channel, "-oo", irc->nick, irc->mynick ); - return( value ); - } + else + return NULL; - return( NULL ); + return value; } +char *set_eval_charset( set_t *set, char *value ) +{ + GIConv cd; + + if ( g_strncasecmp( value, "none", 4 ) == 0 ) + return value; + + cd = g_iconv_open( "UTF-8", value ); + if( cd == (GIConv) -1 ) + return NULL; + + g_iconv_close( cd ); + return value; +} @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2006 Wilmer van der Gaast and others * \********************************************************************/ /* Some stuff to register, handle and save user preferences */ @@ -25,28 +25,33 @@ typedef struct set { + void *data; + char *key; char *value; char *def; /* Default */ - /* Eval: Returns NULL if the value is incorrect. Can return a - corrected value. set_setstr() should be able to free() the - returned string! */ - char *(*eval) ( irc_t *irc, struct set *set, char *value ); + int flags; + + /* Eval: Returns NULL 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! */ + char *(*eval) ( struct set *set, char *value ); struct set *next; } set_t; -set_t *set_add( irc_t *irc, char *key, char *def, void *eval ); -G_MODULE_EXPORT set_t *set_find( irc_t *irc, char *key ); -G_MODULE_EXPORT char *set_getstr( irc_t *irc, char *key ); -G_MODULE_EXPORT int set_getint( irc_t *irc, char *key ); -int set_setstr( irc_t *irc, char *key, char *value ); -int set_setint( irc_t *irc, char *key, int value ); -void set_del( irc_t *irc, char *key ); - -char *set_eval_int( irc_t *irc, set_t *set, char *value ); -char *set_eval_bool( irc_t *irc, set_t *set, char *value ); -char *set_eval_to_char( irc_t *irc, set_t *set, char *value ); -char *set_eval_ops( irc_t *irc, set_t *set, char *value ); - - +set_t *set_add( set_t **head, char *key, char *def, void *eval, void *data ); +set_t *set_find( set_t **head, char *key ); +G_MODULE_EXPORT char *set_getstr( set_t **head, char *key ); +G_MODULE_EXPORT int set_getint( set_t **head, char *key ); +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 ); + +char *set_eval_int( set_t *set, char *value ); +char *set_eval_bool( set_t *set, char *value ); + +char *set_eval_to_char( set_t *set, char *value ); +char *set_eval_ops( set_t *set, char *value ); +char *set_eval_charset( set_t *set, char *value ); @@ -6,6 +6,8 @@ /* Support for multiple storage backends */ +/* Copyright (C) 2005 Jelmer Vernooij <jelmer@samba.org> */ + /* 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 @@ -28,9 +30,9 @@ #include "crypting.h" extern storage_t storage_text; +extern storage_t storage_xml; -static GList text_entry = { &storage_text, NULL, NULL }; -static GList *storage_backends = &text_entry; +static GList *storage_backends = NULL; void register_storage_backend(storage_t *backend) { @@ -40,7 +42,7 @@ void register_storage_backend(storage_t *backend) static storage_t *storage_init_single(const char *name) { GList *gl; - storage_t *st; + storage_t *st = NULL; for (gl = storage_backends; gl; gl = gl->next) { st = gl->data; @@ -62,9 +64,12 @@ GList *storage_init(const char *primary, char **migrate) GList *ret = NULL; int i; storage_t *storage; - + + register_storage_backend(&storage_text); + register_storage_backend(&storage_xml); + storage = storage_init_single(primary); - if (storage == NULL) + if (storage == NULL && storage->save == NULL) return NULL; ret = g_list_append(ret, storage); @@ -32,8 +32,8 @@ typedef enum { STORAGE_INVALID_PASSWORD, STORAGE_ALREADY_EXISTS, STORAGE_OTHER_ERROR /* Error that isn't caused by user input, such as - a database that is unreachable. log() will be - used for the exact error message */ + a database that is unreachable. log() will be + used for the exact error message */ } storage_status_t; typedef struct { diff --git a/storage_ldap.c b/storage_ldap.c new file mode 100644 index 00000000..4bc99de5 --- /dev/null +++ b/storage_ldap.c @@ -0,0 +1,177 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Storage backend that uses a LDAP database */ + +/* Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org> */ + +/* + 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 +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include <ldap.h> + +#define BB_LDAP_HOST "localhost" +#define BB_LDAP_BASE "" + +static char *nick_dn(const char *nick) +{ + return g_strdup_printf("bitlBeeNick=%s%s%s", nick, BB_LDAP_BASE?",":"", BB_LDAP_BASE?BB_LDAP_BASE:""); +} + +static storage_status_t nick_connect(const char *nick, const char *password, LDAP **ld) +{ + char *mydn; + int ret; + storage_status_t status; + *ld = ldap_init(BB_LDAP_HOST, LDAP_PORT); + + if (!ld) { + log_message( LOGLVL_WARNING, "Unable to connect to LDAP server at %s", BB_LDAP_HOST ); + return STORAGE_OTHER_ERROR; + } + + mydn = nick_dn(nick); + + ret = ldap_simple_bind_s(*ld, mydn, password); + + switch (ret) { + case LDAP_SUCCESS: status = STORAGE_OK; break; + case LDAP_INVALID_CREDENTIALS: status = STORAGE_INVALID_PASSWORD; break; + default: + log_message( LOGLVL_WARNING, "Unable to authenticate %s: %s", mydn, ldap_err2string(ret) ); + status = STORAGE_OTHER_ERROR; + break; + } + + g_free(mydn); + + return status; +} + +static storage_status_t sldap_load ( const char *my_nick, const char* password, irc_t *irc ) +{ + LDAPMessage *res, *msg; + LDAP *ld; + int ret, i; + storage_status_t status; + char *mydn; + + status = nick_connect(my_nick, password, &ld); + if (status != STORAGE_OK) + return status; + + mydn = nick_dn(my_nick); + + ret = ldap_search_s(ld, mydn, LDAP_SCOPE_BASE, "(objectClass=*)", NULL, 0, &res); + + if (ret != LDAP_SUCCESS) { + log_message( LOGLVL_WARNING, "Unable to search for %s: %s", mydn, ldap_err2string(ret) ); + ldap_unbind_s(ld); + return STORAGE_OTHER_ERROR; + } + + g_free(mydn); + + for (msg = ldap_first_entry(ld, res); msg; msg = ldap_next_entry(ld, msg)) { + } + + /* FIXME: Store in irc_t */ + + ldap_unbind_s(ld); + + return STORAGE_OK; +} + +static storage_status_t sldap_check_pass( const char *nick, const char *password ) +{ + LDAP *ld; + storage_status_t status; + + status = nick_connect(nick, password, &ld); + + ldap_unbind_s(ld); + + return status; +} + +static storage_status_t sldap_remove( const char *nick, const char *password ) +{ + storage_status_t status; + LDAP *ld; + char *mydn; + int ret; + + status = nick_connect(nick, password, &ld); + + if (status != STORAGE_OK) + return status; + + mydn = nick_dn(nick); + + ret = ldap_delete(ld, mydn); + + if (ret != LDAP_SUCCESS) { + log_message( LOGLVL_WARNING, "Error removing %s: %s", mydn, ldap_err2string(ret) ); + ldap_unbind_s(ld); + return STORAGE_OTHER_ERROR; + } + + ldap_unbind_s(ld); + + g_free(mydn); + return STORAGE_OK; +} + +static storage_status_t sldap_save( irc_t *irc, int overwrite ) +{ + LDAP *ld; + char *mydn; + storage_status_t status; + LDAPMessage *msg; + + status = nick_connect(irc->nick, irc->password, &ld); + if (status != STORAGE_OK) + return status; + + mydn = nick_dn(irc->nick); + + /* FIXME: Make this a bit more atomic? What if we crash after + * removing the old account but before adding the new one ? */ + if (overwrite) + sldap_remove(irc->nick, irc->password); + + g_free(mydn); + + ldap_unbind_s(ld); + + return STORAGE_OK; +} + + + +storage_t storage_ldap = { + .name = "ldap", + .check_pass = sldap_check_pass, + .remove = sldap_remove, + .load = sldap_load, + .save = sldap_save +}; diff --git a/storage_text.c b/storage_text.c index 506c9f03..acc9eefe 100644 --- a/storage_text.c +++ b/storage_text.c @@ -27,32 +27,6 @@ #include "bitlbee.h" #include "crypting.h" -/* DO NOT USE THIS FUNCTION IN NEW CODE. This - * function is here merely because the save/load code still uses - * ids rather than names */ -static struct prpl *find_protocol_by_id(int id) -{ - switch (id) { - case 0: case 1: case 3: return find_protocol("oscar"); - case 4: return find_protocol("msn"); - case 2: return find_protocol("yahoo"); - case 8: return find_protocol("jabber"); - default: break; - } - return NULL; -} - -static int find_protocol_id(const char *name) -{ - if (!strcmp(name, "oscar")) return 1; - if (!strcmp(name, "msn")) return 4; - if (!strcmp(name, "yahoo")) return 2; - if (!strcmp(name, "jabber")) return 8; - - return -1; -} - - static void text_init (void) { if( access( global.conf->configdir, F_OK ) != 0 ) @@ -69,6 +43,7 @@ static storage_status_t text_load ( const char *my_nick, const char* password, i char nick[MAX_NICK_LENGTH+1]; FILE *fp; user_t *ru = user_find( irc, ROOT_NICK ); + account_t *acc, *acc_lookup[9]; if( irc->status & USTATUS_IDENTIFIED ) return( 1 ); @@ -79,7 +54,7 @@ static storage_status_t text_load ( const char *my_nick, const char* password, i fscanf( fp, "%32[^\n]s", s ); - if (checkpass (password, s) != 0) + if( checkpass( password, s ) != 0 ) { fclose( fp ); return STORAGE_INVALID_PASSWORD; @@ -99,202 +74,37 @@ static storage_status_t text_load ( const char *my_nick, const char* password, i } fclose( fp ); + /* Build a list with the first listed account of every protocol + number. So if the user had nicks defined for a second account on + the same IM network, those nicks will be added to the wrong + account, and the user should rename those buddies again. But at + least from now on things will be saved properly. */ + memset( acc_lookup, 0, sizeof( acc_lookup ) ); + for( acc = irc->accounts; acc; acc = acc->next ) + { + if( acc_lookup[0] == NULL && strcmp( acc->prpl->name, "oscar" ) == 0 ) + acc_lookup[0] = acc_lookup[1] = acc_lookup[3] = acc; + else if( acc_lookup[2] == NULL && strcmp( acc->prpl->name, "yahoo" ) == 0 ) + acc_lookup[2] = acc; + else if( acc_lookup[4] == NULL && strcmp( acc->prpl->name, "msn" ) == 0 ) + acc_lookup[4] = acc; + else if( acc_lookup[8] == NULL && strcmp( acc->prpl->name, "jabber" ) == 0 ) + acc_lookup[8] = acc; + } + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".nicks" ); fp = fopen( s, "r" ); if( !fp ) return STORAGE_NO_SUCH_USER; while( fscanf( fp, "%s %d %s", s, &proto, nick ) > 0 ) { - struct prpl *prpl; - - prpl = find_protocol_by_id(proto); - - if (!prpl) + if( ( acc = acc_lookup[proto] ) == NULL ) continue; - + http_decode( s ); - nick_set( irc, s, prpl, nick ); + nick_set( acc, s, nick ); } fclose( fp ); - if( set_getint( irc, "auto_connect" ) ) - { - strcpy( s, "account on" ); /* Can't do this directly because r_c_s alters the string */ - root_command_string( irc, ru, s, 0 ); - } - - return STORAGE_OK; -} - -static storage_status_t text_save( irc_t *irc, int overwrite ) -{ - char s[512]; - char path[512], new_path[512]; - char *line; - nick_t *n; - set_t *set; - mode_t ou = umask( 0077 ); - account_t *a; - FILE *fp; - char *hash; - - if (!overwrite) { - g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); - if (access( path, F_OK ) != -1) - return STORAGE_ALREADY_EXISTS; - - g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); - if (access( path, F_OK ) != -1) - return STORAGE_ALREADY_EXISTS; - } - - /*\ - * [SH] Nothing should be saved if no password is set, because the - * password is not set if it was wrong, or if one is not identified - * yet. This means that a malicious user could easily overwrite - * files owned by someone else: - * a Bad Thing, methinks - \*/ - - /* [WVG] No? Really? */ - - /*\ - * [SH] Okay, okay, it wasn't really Wilmer who said that, it was - * me. I just thought it was funny. - \*/ - - hash = hashpass( irc->password ); - if( hash == NULL ) - { - irc_usermsg( irc, "Please register yourself if you want to save your settings." ); - return STORAGE_OTHER_ERROR; - } - - g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks~" ); - fp = fopen( path, "w" ); - if( !fp ) return STORAGE_OTHER_ERROR; - for( n = irc->nicks; n; n = n->next ) - { - strcpy( s, n->handle ); - s[169] = 0; /* Prevent any overflow (169 ~ 512 / 3) */ - http_encode( s ); - g_snprintf( s + strlen( s ), 510 - strlen( s ), " %d %s", find_protocol_id(n->proto->name), n->nick ); - if( fprintf( fp, "%s\n", s ) != strlen( s ) + 1 ) - { - irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); - fclose( fp ); - return STORAGE_OTHER_ERROR; - } - } - if( fclose( fp ) != 0 ) - { - irc_usermsg( irc, "fclose() reported an error. Disk full?" ); - return STORAGE_OTHER_ERROR; - } - - g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); - if( unlink( new_path ) != 0 ) - { - if( errno != ENOENT ) - { - irc_usermsg( irc, "Error while removing old .nicks file" ); - return STORAGE_OTHER_ERROR; - } - } - if( rename( path, new_path ) != 0 ) - { - irc_usermsg( irc, "Error while renaming new .nicks file" ); - return STORAGE_OTHER_ERROR; - } - - g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts~" ); - fp = fopen( path, "w" ); - if( !fp ) return STORAGE_OTHER_ERROR; - if( fprintf( fp, "%s", hash ) != strlen( hash ) ) - { - irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); - fclose( fp ); - return STORAGE_OTHER_ERROR; - } - g_free( hash ); - - for( a = irc->accounts; a; a = a->next ) - { - if( !strcmp(a->prpl->name, "oscar") ) - g_snprintf( s, sizeof( s ), "account add oscar \"%s\" \"%s\" %s", a->user, a->pass, a->server ); - else - g_snprintf( s, sizeof( s ), "account add %s \"%s\" \"%s\" \"%s\"", - a->prpl->name, a->user, a->pass, a->server ? a->server : "" ); - - line = obfucrypt( s, irc->password ); - if( *line ) - { - if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) - { - irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); - fclose( fp ); - return STORAGE_OTHER_ERROR; - } - } - g_free( line ); - } - - for( set = irc->set; set; set = set->next ) - { - if( set->value && set->def ) - { - g_snprintf( s, sizeof( s ), "set %s \"%s\"", set->key, set->value ); - line = obfucrypt( s, irc->password ); - if( *line ) - { - if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) - { - irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); - fclose( fp ); - return STORAGE_OTHER_ERROR; - } - } - g_free( line ); - } - } - - if( strcmp( irc->mynick, ROOT_NICK ) != 0 ) - { - g_snprintf( s, sizeof( s ), "rename %s %s", ROOT_NICK, irc->mynick ); - line = obfucrypt( s, irc->password ); - if( *line ) - { - if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) - { - irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); - fclose( fp ); - return STORAGE_OTHER_ERROR; - } - } - g_free( line ); - } - if( fclose( fp ) != 0 ) - { - irc_usermsg( irc, "fclose() reported an error. Disk full?" ); - return STORAGE_OTHER_ERROR; - } - - g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); - if( unlink( new_path ) != 0 ) - { - if( errno != ENOENT ) - { - irc_usermsg( irc, "Error while removing old .accounts file" ); - return STORAGE_OTHER_ERROR; - } - } - if( rename( path, new_path ) != 0 ) - { - irc_usermsg( irc, "Error while renaming new .accounts file" ); - return STORAGE_OTHER_ERROR; - } - - umask( ou ); - return STORAGE_OK; } @@ -342,6 +152,5 @@ storage_t storage_text = { .init = text_init, .check_pass = text_check_pass, .remove = text_remove, - .load = text_load, - .save = text_save + .load = text_load }; diff --git a/storage_xml.c b/storage_xml.c new file mode 100644 index 00000000..2585b475 --- /dev/null +++ b/storage_xml.c @@ -0,0 +1,517 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2006 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Storage backend that uses an XMLish format for all data. */ + +/* + 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 +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include "base64.h" +#include "rc4.h" +#include "md5.h" + +typedef enum +{ + XML_PASS_CHECK_ONLY = -1, + XML_PASS_UNKNOWN = 0, + XML_PASS_WRONG, + XML_PASS_OK +} xml_pass_st; + +/* To make it easier later when extending the format: */ +#define XML_FORMAT_VERSION 1 + +struct xml_parsedata +{ + irc_t *irc; + char *current_setting; + account_t *current_account; + char *given_nick; + char *given_pass; + xml_pass_st pass_st; +}; + +static char *xml_attr( const gchar **attr_names, const gchar **attr_values, const gchar *key ) +{ + int i; + + for( i = 0; attr_names[i]; i ++ ) + if( g_strcasecmp( attr_names[i], key ) == 0 ) + return (char*) attr_values[i]; + + return NULL; +} + +static void xml_destroy_xd( gpointer data ) +{ + struct xml_parsedata *xd = data; + + g_free( xd->given_nick ); + g_free( xd->given_pass ); + g_free( xd ); +} + +static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error ) +{ + struct xml_parsedata *xd = data; + irc_t *irc = xd->irc; + + if( g_strcasecmp( element_name, "user" ) == 0 ) + { + char *nick = xml_attr( attr_names, attr_values, "nick" ); + char *pass = xml_attr( attr_names, attr_values, "password" ); + md5_byte_t *pass_dec = NULL; + + if( !nick || !pass ) + { + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Missing attributes for %s element", element_name ); + } + else if( base64_decode( pass, &pass_dec ) != 21 ) + { + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Error while decoding password attribute" ); + } + else + { + md5_byte_t pass_md5[16]; + md5_state_t md5_state; + int i; + + md5_init( &md5_state ); + md5_append( &md5_state, (md5_byte_t*) xd->given_pass, strlen( xd->given_pass ) ); + md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */ + md5_finish( &md5_state, pass_md5 ); + + for( i = 0; i < 16; i ++ ) + { + if( pass_dec[i] != pass_md5[i] ) + { + xd->pass_st = XML_PASS_WRONG; + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Password mismatch" ); + break; + } + } + + /* If we reached the end of the loop, it was a match! */ + if( i == 16 ) + { + if( xd->pass_st != XML_PASS_CHECK_ONLY ) + xd->pass_st = XML_PASS_OK; + } + } + + g_free( pass_dec ); + } + else if( xd->pass_st < XML_PASS_OK ) + { + /* Let's not parse anything else if we only have to check + the password. */ + } + else if( g_strcasecmp( element_name, "account" ) == 0 ) + { + char *protocol, *handle, *server, *password = NULL, *autoconnect; + char *pass_b64 = NULL, *pass_rc4 = NULL; + int pass_len; + struct prpl *prpl = NULL; + + handle = xml_attr( attr_names, attr_values, "handle" ); + pass_b64 = xml_attr( attr_names, attr_values, "password" ); + server = xml_attr( attr_names, attr_values, "server" ); + autoconnect = xml_attr( attr_names, attr_values, "autoconnect" ); + + protocol = xml_attr( attr_names, attr_values, "protocol" ); + if( protocol ) + prpl = find_protocol( protocol ); + + if( !handle || !pass_b64 || !protocol ) + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Missing attributes for %s element", element_name ); + else if( !prpl ) + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Unknown protocol: %s", protocol ); + else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_rc4 ) ) && + rc4_decode( (unsigned char*) pass_rc4, pass_len, + (unsigned char**) &password, xd->given_pass ) ) + { + xd->current_account = account_add( irc, prpl, handle, password ); + if( server ) + set_setstr( &xd->current_account->set, "server", server ); + if( autoconnect ) + set_setstr( &xd->current_account->set, "auto_connect", autoconnect ); + } + else + { + /* Actually the _decode functions don't even return error codes, + but maybe they will later... */ + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Error while decrypting account password" ); + } + + g_free( pass_rc4 ); + g_free( password ); + } + else if( g_strcasecmp( element_name, "setting" ) == 0 ) + { + char *setting; + + if( xd->current_setting ) + { + g_free( xd->current_setting ); + xd->current_setting = NULL; + } + + if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) ) + 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 ); + } + else if( g_strcasecmp( element_name, "buddy" ) == 0 ) + { + char *handle, *nick; + + handle = xml_attr( attr_names, attr_values, "handle" ); + nick = xml_attr( attr_names, attr_values, "nick" ); + + if( xd->current_account && handle && nick ) + { + nick_set( xd->current_account, handle, nick ); + } + 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, + "Unkown element: %s", element_name ); + } +} + +static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error ) +{ + struct xml_parsedata *xd = data; + + if( g_strcasecmp( element_name, "setting" ) == 0 && xd->current_setting ) + { + g_free( xd->current_setting ); + xd->current_setting = NULL; + } + else if( g_strcasecmp( element_name, "account" ) == 0 ) + { + xd->current_account = NULL; + } +} + +static void xml_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error ) +{ + struct xml_parsedata *xd = data; + irc_t *irc = xd->irc; + + if( xd->pass_st < XML_PASS_OK ) + { + /* Let's not parse anything else if we only have to check + the password, or if we didn't get the chance to check it + yet. */ + } + 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 ); + g_free( xd->current_setting ); + xd->current_setting = NULL; + } +} + +GMarkupParser xml_parser = +{ + xml_start_element, + xml_end_element, + xml_text, + NULL, + NULL +}; + +static void xml_init( void ) +{ + if( access( global.conf->configdir, F_OK ) != 0 ) + log_message( LOGLVL_WARNING, "The configuration directory %s does not exist. Configuration won't be saved.", CONFIG ); + else if( access( global.conf->configdir, R_OK ) != 0 || access( global.conf->configdir, W_OK ) != 0 ) + 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 ) +{ + GMarkupParseContext *ctx; + struct xml_parsedata *xd; + char *fn, buf[512]; + 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 ); + xd->given_pass = g_strdup( password ); + xd->pass_st = action; + nick_lc( xd->given_nick ); + + fn = g_strdup_printf( "%s%s%s", global.conf->configdir, xd->given_nick, ".xml" ); + if( ( fd = open( fn, O_RDONLY ) ) < 0 ) + { + xml_destroy_xd( xd ); + g_free( fn ); + return STORAGE_NO_SUCH_USER; + } + g_free( fn ); + + ctx = g_markup_parse_context_new( &xml_parser, 0, xd, xml_destroy_xd ); + + while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 ) + { + if( !g_markup_parse_context_parse( ctx, buf, st, &gerr ) || gerr ) + { + xml_pass_st pass_st = xd->pass_st; + + g_markup_parse_context_free( ctx ); + close( fd ); + + if( pass_st == XML_PASS_WRONG ) + { + g_clear_error( &gerr ); + return STORAGE_INVALID_PASSWORD; + } + else + { + if( gerr && irc ) + irc_usermsg( irc, "Error from XML-parser: %s", gerr->message ); + + g_clear_error( &gerr ); + return STORAGE_OTHER_ERROR; + } + } + } + /* Just to be sure... */ + g_clear_error( &gerr ); + + g_markup_parse_context_free( ctx ); + close( fd ); + + 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 ) +{ + return xml_load_real( my_nick, password, irc, 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 ); +} + +static int xml_printf( int fd, int indent, char *fmt, ... ) +{ + va_list params; + char *out; + char tabs[9] = "\t\t\t\t\t\t\t\t"; + int len; + + /* Maybe not very clean, but who needs more than 8 levels of indentation anyway? */ + if( write( fd, tabs, indent <= 8 ? indent : 8 ) != indent ) + return 0; + + va_start( params, fmt ); + out = g_markup_vprintf_escaped( fmt, params ); + va_end( params ); + + len = strlen( out ); + len -= write( fd, out, len ); + g_free( out ); + + return len == 0; +} + +static gboolean xml_save_nick( gpointer key, gpointer value, gpointer data ); + +static storage_status_t xml_save( irc_t *irc, int overwrite ) +{ + char path[512], *path2, *pass_buf = NULL; + set_t *set; + account_t *acc; + int fd; + 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" ); + g_free( path2 ); + + if( !overwrite && access( path, F_OK ) != -1 ) + return STORAGE_ALREADY_EXISTS; + + strcat( path, "~" ); + if( ( fd = open( path, O_WRONLY | O_CREAT, 0600 ) ) < 0 ) + { + irc_usermsg( irc, "Error while opening configuration file." ); + return STORAGE_OTHER_ERROR; + } + + /* Generate a salted md5sum of the password. Use 5 bytes for the salt + (to prevent dictionary lookups of passwords) to end up with a 21- + byte password hash, more convenient for base64 encoding. */ + random_bytes( pass_md5 + 16, 5 ); + md5_init( &md5_state ); + md5_append( &md5_state, (md5_byte_t*) irc->password, strlen( irc->password ) ); + md5_append( &md5_state, pass_md5 + 16, 5 ); /* Add the salt. */ + md5_finish( &md5_state, pass_md5 ); + /* Save the hash in base64-encoded form. */ + pass_buf = base64_encode( (char*) pass_md5, 21 ); + + if( !xml_printf( fd, 0, "<user nick=\"%s\" password=\"%s\" version=\"%d\">\n", irc->nick, pass_buf, XML_FORMAT_VERSION ) ) + goto write_error; + + g_free( pass_buf ); + + for( set = irc->set; set; set = set->next ) + if( set->value && set->def ) + if( !xml_printf( fd, 1, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) ) + goto write_error; + + for( acc = irc->accounts; acc; acc = acc->next ) + { + char *pass_rc4, *pass_b64; + int pass_len; + + pass_len = rc4_encode( (unsigned char*) acc->pass, strlen( acc->pass ), (unsigned char**) &pass_rc4, irc->password ); + pass_b64 = base64_encode( pass_rc4, pass_len ); + g_free( pass_rc4 ); + + if( !xml_printf( fd, 1, "<account protocol=\"%s\" handle=\"%s\" password=\"%s\" autoconnect=\"%d\"", acc->prpl->name, acc->user, pass_b64, acc->auto_connect ) ) + { + g_free( pass_b64 ); + goto write_error; + } + g_free( pass_b64 ); + + if( acc->server && acc->server[0] && !xml_printf( fd, 0, " server=\"%s\"", acc->server ) ) + goto write_error; + if( !xml_printf( fd, 0, ">\n" ) ) + goto write_error; + + for( set = acc->set; set; set = set->next ) + if( set->value && set->def && !( set->flags & ACC_SET_NOSAVE ) ) + if( !xml_printf( fd, 2, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) ) + goto write_error; + + /* This probably looks pretty strange. g_hash_table_foreach + is quite a PITA already (but it can't get much better in + C without using #define, I'm afraid), and since it + doesn't seem to be possible to abort the foreach on write + errors, so instead let's use the _find function and + return TRUE on write errors. Which means, if we found + something, there was an error. :-) */ + if( g_hash_table_find( acc->nicks, xml_save_nick, (gpointer) fd ) ) + goto write_error; + + if( !xml_printf( fd, 1, "</account>\n" ) ) + goto write_error; + } + + if( !xml_printf( fd, 0, "</user>\n" ) ) + goto write_error; + + close( fd ); + + path2 = g_strndup( path, strlen( path ) - 1 ); + if( rename( path, path2 ) != 0 ) + { + irc_usermsg( irc, "Error while renaming temporary configuration file." ); + + g_free( path2 ); + unlink( path ); + + return STORAGE_OTHER_ERROR; + } + + g_free( path2 ); + + return STORAGE_OK; + +write_error: + g_free( pass_buf ); + + irc_usermsg( irc, "Write error. Disk full?" ); + close( fd ); + + return STORAGE_OTHER_ERROR; +} + +static gboolean xml_save_nick( gpointer key, gpointer value, gpointer data ) +{ + return !xml_printf( (int) data, 2, "<buddy handle=\"%s\" nick=\"%s\" />\n", key, value ); +} + +static storage_status_t xml_remove( const char *nick, const char *password ) +{ + char s[512]; + storage_status_t status; + + status = xml_check_pass( nick, password ); + if( status != STORAGE_OK ) + return status; + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".xml" ); + if( unlink( s ) == -1 ) + return STORAGE_OTHER_ERROR; + + return STORAGE_OK; +} + +storage_t storage_xml = { + .name = "xml", + .init = xml_init, + .check_pass = xml_check_pass, + .remove = xml_remove, + .load = xml_load, + .save = xml_save +}; @@ -47,20 +47,18 @@ int main( int argc, char *argv[], char **envp ) memset( &global, 0, sizeof( global_t ) ); b_main_init(); - log_init(); - nogaim_init(); - - CONF_FILE = g_strdup( CONF_FILE_DEF ); + srand( time( NULL ) ^ getpid() ); + + CONF_FILE = g_strdup( CONF_FILE_DEF ); global.helpfile = g_strdup( HELP_FILE ); - + global.conf = conf_load( argc, argv ); if( global.conf == NULL ) return( 1 ); - - + if( global.conf->runmode == RUNMODE_INETD ) { i = bitlbee_inetd_init(); @@ -88,7 +86,7 @@ int main( int argc, char *argv[], char **envp ) } if( i != 0 ) return( i ); - + global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage ); if ( global.storage == NULL) { log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); @@ -66,7 +66,7 @@ user_t *user_add( irc_t *irc, char *nick ) } u->user = u->realname = u->host = u->nick = g_strdup( nick ); - u->is_private = set_getint( irc, "private" ); + u->is_private = set_getint( &irc->set, "private" ); key = g_strdup( nick ); nick_lc( key ); @@ -142,16 +142,22 @@ user_t *user_find( irc_t *irc, char *nick ) user_t *user_findhandle( struct gaim_connection *gc, char *handle ) { - user_t *u = gc->irc->users; - - while( u ) - { - if( u->gc == gc && u->handle && gc->prpl->cmp_buddynames ( u->handle, handle ) == 0 ) - break; - u = u->next; - } - - return( u ); + user_t *u; + char *nick; + + /* First, let's try a hash lookup. If it works, it's probably faster. */ + if( ( nick = g_hash_table_lookup( gc->acc->nicks, handle ) ) && + ( u = user_find( gc->irc, nick ) ) && + ( gc->acc->prpl->handle_cmp( handle, u->handle ) == 0 ) ) + return u; + + /* However, it doesn't always work, so in that case we'll have to dig + through the whole userlist. :-( */ + for( u = gc->irc->users; u; u = u->next ) + if( u->gc == gc && u->handle && gc->acc->prpl->handle_cmp( u->handle, handle ) == 0 ) + return u; + + return NULL; } void user_rename( irc_t *irc, char *oldnick, char *newnick ) |