diff options
author | Wilmer van der Gaast <wilmer@gaast.net> | 2007-10-12 01:08:58 +0100 |
---|---|---|
committer | Wilmer van der Gaast <wilmer@gaast.net> | 2007-10-12 01:08:58 +0100 |
commit | eda54e40d04c83028d84e91c895a550c1929b436 (patch) | |
tree | 878f985af2ab5d2b9c59e8c955448bc4e9ddec17 | |
parent | 82135c7178b6379f35741991f6c06bb308143194 (diff) | |
parent | d444c09e6c7ac6fc3c1686af0e63c09805d8cd00 (diff) |
Merge from devel.
-rw-r--r-- | Makefile | 2 | ||||
-rwxr-xr-x | configure | 15 | ||||
-rw-r--r-- | doc/CHANGES | 17 | ||||
-rw-r--r-- | doc/user-guide/commands.xml | 10 | ||||
-rw-r--r-- | irc.c | 5 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/arc.c (renamed from lib/rc4.c) | 107 | ||||
-rw-r--r-- | lib/arc.h (renamed from lib/rc4.h) | 16 | ||||
-rw-r--r-- | lib/base64.c | 4 | ||||
-rw-r--r-- | lib/base64.h | 2 | ||||
-rw-r--r-- | lib/misc.c | 48 | ||||
-rw-r--r-- | lib/misc.h | 2 | ||||
-rw-r--r-- | lib/ssl_bogus.c | 5 | ||||
-rw-r--r-- | protocols/jabber/sasl.c | 7 | ||||
-rw-r--r-- | protocols/nogaim.c | 64 | ||||
-rw-r--r-- | protocols/nogaim.h | 96 | ||||
-rw-r--r-- | root_commands.c | 2 | ||||
-rw-r--r-- | storage_xml.c | 23 | ||||
-rw-r--r-- | tests/Makefile | 2 | ||||
-rw-r--r-- | tests/check.c | 4 | ||||
-rw-r--r-- | tests/check_arc.c | 95 | ||||
-rw-r--r-- | tests/check_util.c | 58 | ||||
-rw-r--r-- | unix.c | 13 |
23 files changed, 466 insertions, 133 deletions
@@ -10,7 +10,7 @@ # Program variables 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 +headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha.h lib/ssl_client.h lib/url.h protocols/nogaim.h subdirs = lib protocols # Expansion of variables @@ -28,6 +28,7 @@ yahoo=1 debug=0 strip=1 gcov=0 +plugins=1 ipv6=1 events=glib @@ -68,11 +69,10 @@ Option Description Default --debug=0/1 Disable/enable debugging $debug --strip=0/1 Disable/enable binary stripping $strip --gcov=0/1 Disable/enable test coverage reporting $gcov +--plugins=0/1 Disable/enable plugins support $plugins --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 @@ -139,13 +139,14 @@ if [ "$ipv6" = "1" ]; then fi if [ "$debug" = "1" ]; then - echo 'CFLAGS=-g' >> Makefile.settings + [ -z "$CFLAGS" ] && CFLAGS=-g echo 'DEBUG=1' >> Makefile.settings echo '#define DEBUG' >> config.h else - echo 'CFLAGS=-O3' >> Makefile.settings + [ -z "$CFLAGS" ] && CFLAGS=-O3 fi +echo CFLAGS=$CFLAGS >> Makefile.settings echo CFLAGS+=-I`pwd` -I`pwd`/lib -I`pwd`/protocols -I. >> Makefile.settings echo CFLAGS+=-DHAVE_CONFIG_H >> Makefile.settings @@ -384,6 +385,12 @@ if [ "$gcov" = "1" ]; then echo "EFLAGS+=-lgcov" >> Makefile.settings fi +if [ "$plugins" = 0 ]; then + echo '#undef WITH_PLUGINS' >> config.h +else + echo '#define WITH_PLUGINS' >> config.h +fi + echo if [ -z "$BITLBEE_VERSION" -a -d .bzr ] && type bzr > /dev/null 2> /dev/null; then nick=`bzr nick` diff --git a/doc/CHANGES b/doc/CHANGES index 3f509c46..6c109f25 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -1,4 +1,4 @@ -Version 1.1dev: +Version 1.2: - First BitlBee development/testing RELEASE. This should be quite stable though (and for most people more stable than 1.0.x). It just has a couple of rough edges and needs a bit more testing. @@ -65,6 +65,21 @@ Version 1.1dev: resources available for that buddy. (Of course this only works if the buddy is in your contact list.) +Finished ??? + +Version 1.0.4: +- Removed sethostent(), which causes problems for many people, especially on + *BSD. This is basically the reason for this release. +- "allow" command actually displays the allow list, not the block list. +- Yahoo away state/msg fix. +- Don't display "Gender: Male" by default if nothing's filled in (OSCAR + "info" command) +- Fixed account cleanup (possible infinite loop) in irc_free(). +- Fixed configdir error message to not always display the compile-time + setting. + +Finished 20 Aug 2007 + Version 1.0.3: - Fixed ugliness in block/allow list commands (still not perfect though, the list is empty or not up-to-date for most protocols). diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 6646d0db..cf40782f 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -595,6 +595,16 @@ </description> </bitlbee-setting> + <bitlbee-setting name="simulate_netsplit" type="boolean" scope="global"> + <default>true</default> + + <description> + <para> + Some IRC clients parse quit messages sent by the IRC server to see if someone really left or just disappeared because of a netsplit. By default, BitlBee tries to simulate netsplit-like quit messages to keep the control channel window clean. If you don't like this (or if your IRC client doesn't support this) you can disable this setting. + </para> + </description> + </bitlbee-setting> + <bitlbee-setting name="ssl" type="boolean" scope="account"> <default>false</default> @@ -131,7 +131,7 @@ irc_t *irc_new( int fd ) 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, "charset", "utf-8", set_eval_charset, irc ); set_add( &irc->set, "debug", "false", set_eval_bool, irc ); set_add( &irc->set, "default_target", "root", NULL, irc ); set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc ); @@ -142,6 +142,7 @@ irc_t *irc_new( int fd ) 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, "simulate_netsplit", "true", set_eval_bool, irc ); set_add( &irc->set, "strip_html", "true", NULL, irc ); set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc ); set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc ); @@ -909,7 +910,7 @@ void irc_kill( irc_t *irc, user_t *u ) char *nick, *s; char reason[128]; - if( u->ic && u->ic->flags & OPT_LOGGING_OUT ) + if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) ) { if( u->ic->acc->server ) g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost, diff --git a/lib/Makefile b/lib/Makefile index a9038987..bc1966d9 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,7 +9,7 @@ -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 +objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha.o $(SSL_CLIENT) url.o CFLAGS += -Wall LFLAGS += -r @@ -1,7 +1,7 @@ /***************************************************************************\ * * * BitlBee - An IRC to IM gateway * -* Simple (but secure) RC4 implementation for safer password storage. * +* Simple (but secure) ArcFour implementation for safer password storage. * * * * Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * * * @@ -22,18 +22,21 @@ \***************************************************************************/ /* - 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. + This file implements ArcFour-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, + The reason I picked ArcFour is because it's pretty simple but effective, so it will work without adding several KBs or an extra library dependency. + + (ArcFour is an RC4-compatible cipher. See for details: + http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt) */ @@ -42,55 +45,62 @@ #include <stdlib.h> #include <string.h> #include "misc.h" -#include "rc4.h" +#include "arc.h" /* Add some seed to the password, to make sure we *never* use the same key. This defines how many bytes we use as a seed. */ -#define RC4_IV_LEN 6 +#define ARC_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 +#define ARC_CYCLES 1024 -struct rc4_state *rc4_keymaker( unsigned char *key, int kl, int cycles ) +struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ) { - struct rc4_state *st; + struct arc_state *st; int i, j, tmp; + unsigned char S2[256]; - st = g_malloc( sizeof( struct rc4_state ) ); + st = g_malloc( sizeof( struct arc_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 = 0; i < 256; i ++ ) + { + st->S[i] = i; + S2[i] = key[i%kl]; + } + for( i = j = 0; i < 256; i ++ ) { - j = ( j + st->S[i] + key[i%kl] ) & 0xff; + j = ( j + st->S[i] + S2[i] ) & 0xff; tmp = st->S[i]; st->S[i] = st->S[j]; st->S[j] = tmp; } + memset( S2, 0, 256 ); + i = j = 0; + for( i = 0; i < cycles; i ++ ) - rc4_getbyte( st ); + arc_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. + For those who don't know, ArcFour 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). + The function above initializes the 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 arc_getbyte( struct arc_state *st ) { unsigned char tmp; @@ -100,16 +110,17 @@ unsigned char rc4_getbyte( struct rc4_state *st ) tmp = st->S[st->i]; st->S[st->i] = st->S[st->j]; st->S[st->j] = tmp; + tmp = (st->S[st->i] + st->S[st->j]) & 0xff; - return st->S[(st->S[st->i] + st->S[st->j]) & 0xff]; + return st->S[tmp]; } /* 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 + by default) random bytes to the password before setting up the state structures. These 6 bytes are also saved in the results, because of - course we'll need them in rc4_decode(). + course we'll need them in arc_decode(). Because the length of the resulting string is unknown to the caller, it should pass a char**. Since the encode/decode functions allocate @@ -121,50 +132,50 @@ unsigned char rc4_getbyte( struct rc4_state *st ) 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 ) +int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password ) { - struct rc4_state *st; + struct arc_state *st; unsigned char *key; int key_len, i; - key_len = strlen( password ) + RC4_IV_LEN; + key_len = strlen( password ) + ARC_IV_LEN; if( clear_len <= 0 ) - clear_len = strlen( (char*) clear ); + clear_len = strlen( clear ); /* Prepare buffers and the key + IV */ - *crypt = g_malloc( clear_len + RC4_IV_LEN ); + *crypt = g_malloc( clear_len + ARC_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 ); + random_bytes( crypt[0], ARC_IV_LEN ); + memcpy( key + key_len - ARC_IV_LEN, crypt[0], ARC_IV_LEN ); /* Generate the initial S[] from the IVed key. */ - st = rc4_keymaker( key, key_len, RC4_CYCLES ); + st = arc_keymaker( key, key_len, ARC_CYCLES ); g_free( key ); for( i = 0; i < clear_len; i ++ ) - crypt[0][i+RC4_IV_LEN] = clear[i] ^ rc4_getbyte( st ); + crypt[0][i+ARC_IV_LEN] = clear[i] ^ arc_getbyte( st ); g_free( st ); - return clear_len + RC4_IV_LEN; + return clear_len + ARC_IV_LEN; } -int rc4_decode( unsigned char *crypt, int crypt_len, unsigned char **clear, char *password ) +int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password ) { - struct rc4_state *st; + struct arc_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; + key_len = strlen( password ) + ARC_IV_LEN; + clear_len = crypt_len - ARC_IV_LEN; if( clear_len < 0 ) { - *clear = (unsigned char*) g_strdup( "" ); + *clear = g_strdup( "" ); return 0; } @@ -172,15 +183,15 @@ int rc4_decode( unsigned char *crypt, int crypt_len, unsigned char **clear, char *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]; + for( i = 0; i < ARC_IV_LEN; i ++ ) + key[key_len-ARC_IV_LEN+i] = crypt[i]; /* Generate the initial S[] from the IVed key. */ - st = rc4_keymaker( key, key_len, RC4_CYCLES ); + st = arc_keymaker( key, key_len, ARC_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] = crypt[i+ARC_IV_LEN] ^ arc_getbyte( st ); clear[0][i] = 0; /* Nice to have for plaintexts. */ g_free( st ); @@ -1,9 +1,9 @@ /***************************************************************************\ * * * BitlBee - An IRC to IM gateway * -* Simple (but secure) RC4 implementation for safer password storage. * +* Simple (but secure) ArcFour implementation for safer password storage. * * * -* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* Copyright 2007 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 * @@ -22,15 +22,15 @@ \***************************************************************************/ -/* See rc4.c for more information. */ +/* See arc.c for more information. */ -struct rc4_state +struct arc_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 ); +struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ); +unsigned char arc_getbyte( struct arc_state *st ); +int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password ); +int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password ); diff --git a/lib/base64.c b/lib/base64.c index 69069dae..64e9692a 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -30,10 +30,10 @@ static const char real_b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv char *tobase64(const char *text) { - return base64_encode(text, strlen(text)); + return base64_encode((const unsigned char *)text, strlen(text)); } -char *base64_encode(const char *in, int len) +char *base64_encode(const unsigned char *in, int len) { char *out; diff --git a/lib/base64.h b/lib/base64.h index 570f2b14..ebd74bf1 100644 --- a/lib/base64.h +++ b/lib/base64.h @@ -26,7 +26,7 @@ #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 char *base64_encode( const unsigned 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 ); @@ -544,3 +544,51 @@ struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain ) return reply; } + +/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */ +char *word_wrap( char *msg, int line_len ) +{ + GString *ret = g_string_sized_new( strlen( msg ) + 16 ); + + while( strlen( msg ) > line_len ) + { + int i; + + /* First try to find out if there's a newline already. Don't + want to add more splits than necessary. */ + for( i = line_len; i > 0 && msg[i] != '\n'; i -- ); + if( msg[i] == '\n' ) + { + g_string_append_len( ret, msg, i + 1 ); + msg += i + 1; + continue; + } + + for( i = line_len; i > 0; i -- ) + { + if( msg[i] == '-' ) + { + g_string_append_len( ret, msg, i + 1 ); + g_string_append_c( ret, '\n' ); + msg += i + 1; + break; + } + else if( msg[i] == ' ' ) + { + g_string_append_len( ret, msg, i ); + g_string_append_c( ret, '\n' ); + msg += i + 1; + break; + } + } + if( i == 0 ) + { + g_string_append_len( ret, msg, line_len ); + g_string_append_c( ret, '\n' ); + msg += line_len; + } + } + g_string_append( ret, msg ); + + return g_string_free( ret, FALSE ); +} @@ -63,4 +63,6 @@ G_MODULE_EXPORT int bool2int( char *value ); G_MODULE_EXPORT struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain ); +G_MODULE_EXPORT char *word_wrap( char *msg, int line_len ); + #endif diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c index 00aaa7c4..5bae3496 100644 --- a/lib/ssl_bogus.c +++ b/lib/ssl_bogus.c @@ -51,6 +51,11 @@ int ssl_getfd( void *conn ) return( -1 ); } +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ + return NULL; +} + b_input_condition ssl_getdirection( void *conn ) { return GAIM_INPUT_READ; diff --git a/protocols/jabber/sasl.c b/protocols/jabber/sasl.c index 69199a8b..6eee37b3 100644 --- a/protocols/jabber/sasl.c +++ b/protocols/jabber/sasl.c @@ -88,7 +88,7 @@ xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data ) s[0] = 0; strcpy( s + 1, jd->username ); strcpy( s + 2 + strlen( jd->username ), ic->acc->pass ); - reply->text = base64_encode( s, len ); + reply->text = base64_encode( (unsigned char *)s, len ); reply->text_len = strlen( reply->text ); g_free( s ); } @@ -184,7 +184,8 @@ xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data ) struct im_connection *ic = data; struct jabber_data *jd = ic->proto_data; struct xt_node *reply = NULL; - char *nonce = NULL, *realm = NULL, *cnonce = NULL, cnonce_bin[30]; + char *nonce = NULL, *realm = NULL, *cnonce = NULL; + unsigned char cnonce_bin[30]; char *digest_uri = NULL; char *dec = NULL; char *s = NULL; @@ -215,7 +216,7 @@ xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data ) if( !realm ) realm = g_strdup( jd->server ); - random_bytes( (unsigned char *) cnonce_bin, sizeof( cnonce_bin ) ); + random_bytes( cnonce_bin, sizeof( cnonce_bin ) ); cnonce = base64_encode( cnonce_bin, sizeof( cnonce_bin ) ); digest_uri = g_strdup_printf( "%s/%s", "xmpp", jd->server ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 4b0b738b..3307b0f5 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -47,7 +47,7 @@ gboolean load_plugin(char *path) GModule *mod = g_module_open(path, G_MODULE_BIND_LAZY); if(!mod) { - log_message(LOGLVL_ERROR, "Can't find `%s', not loading", path); + log_message(LOGLVL_ERROR, "Can't find `%s', not loading (%s)\n", path, g_module_error()); return FALSE; } @@ -607,14 +607,27 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ { - irc_write( ic->irc, ":%s MODE %s %cv %s", ic->irc->myhost, - ic->irc->channel, u->away?'-':'+', u->nick ); + char *from; + + if( set_getbool( &ic->irc->set, "simulate_netsplit" ) ) + { + from = g_strdup( ic->irc->myhost ); + } + else + { + from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, + ic->irc->myhost ); + } + irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, + u->away?'-':'+', u->nick ); + g_free( from ); } } void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, u_int32_t flags, time_t sent_at ) { irc_t *irc = ic->irc; + char *wrapped; user_t *u; u = user_findhandle( ic, handle ); @@ -657,37 +670,9 @@ void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, u_int32_ ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) strip_html( msg ); - while( strlen( msg ) > 425 ) - { - char tmp, *nl; - - tmp = msg[425]; - msg[425] = 0; - - /* If there's a newline/space in this string, split up there, - looks a bit prettier. */ - if( ( nl = strrchr( msg, '\n' ) ) || ( nl = strrchr( msg, ' ' ) ) ) - { - msg[425] = tmp; - tmp = *nl; - *nl = 0; - } - - irc_msgfrom( irc, u->nick, msg ); - - /* Move on. */ - if( nl ) - { - *nl = tmp; - msg = nl + 1; - } - else - { - msg[425] = tmp; - msg += 425; - } - } - irc_msgfrom( irc, u->nick, msg ); + wrapped = word_wrap( msg, 425 ); + irc_msgfrom( irc, u->nick, wrapped ); + g_free( wrapped ); } void imcb_buddy_typing( struct im_connection *ic, char *handle, u_int32_t flags ) @@ -749,6 +734,7 @@ void imcb_chat_free( struct groupchat *c ) void imcb_chat_msg( struct groupchat *c, char *who, char *msg, u_int32_t flags, time_t sent_at ) { struct im_connection *ic = c->ic; + char *wrapped; user_t *u; /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */ @@ -761,10 +747,16 @@ void imcb_chat_msg( struct groupchat *c, char *who, char *msg, u_int32_t flags, ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) strip_html( msg ); + wrapped = word_wrap( msg, 425 ); if( c && u ) - irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, "", msg ); + { + irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, "", wrapped ); + } else - imcb_log( ic, "Message from/to conversation %s@0x%x (unknown conv/user): %s", who, (int) c, msg ); + { + imcb_log( ic, "Message from/to conversation %s@0x%x (unknown conv/user): %s", who, (int) c, wrapped ); + } + g_free( wrapped ); } struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle ) diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 6aa057ff..5bf6d922 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -97,13 +97,21 @@ struct im_connection struct groupchat { struct im_connection *ic; + /* stuff used just for chat */ + /* The in_room variable is a list of handles (not nicks!), kind of + * "nick list". This is how you can check who is in the group chat + * already, for example to avoid adding somebody two times. */ GList *in_room; GList *ignored; struct groupchat *next; char *channel; + /* The title variable contains the ID you gave when you created the + * chat using imcb_chat_new(). */ char *title; char joined; + /* This is for you, you can add your own structure here to extend this + * structure for your protocol's needs. */ void *data; }; @@ -122,26 +130,52 @@ struct buddy { struct prpl { int options; + /* You should set this to the name of your protocol. + * - The user sees this name ie. when imcb_log() is used. */ const char *name; /* Added this one to be able to add per-account settings, don't think - it should be used for anything else. */ + * it should be used for anything else. You are supposed to use the + * set_add() function to add new settings. */ void (* init) (account_t *); - /* These should all be pretty obvious. */ + /* The typical usage of the login() function: + * - Create an im_connection using imcb_new() from the account_t parameter. + * - Initialize your myproto_data struct - you should store all your protocol-specific data there. + * - Save your custom structure to im_connection->proto_data. + * - Use proxy_connect() to connect to the server. + */ void (* login) (account_t *); + /* Implementing this function is optional. */ void (* keepalive) (struct im_connection *); + /* In this function you should: + * - Tell the server about you are logging out. + * - g_free() your myproto_data struct as BitlBee does not know how to + * properly do so. + */ void (* logout) (struct im_connection *); + /* This function is called when the user wants to send a message to a handle. + * - 'to' is a handle, not a nick + * - 'flags' may be ignored + */ int (* buddy_msg) (struct im_connection *, char *to, char *message, int flags); + /* This function is called then the user uses the /away IRC command. + * - 'state' contains the away reason. + * - 'message' may be ignored if your protocol does not support it. + */ void (* set_away) (struct im_connection *, char *state, char *message); + /* Implementing this function is optional. */ void (* get_away) (struct im_connection *, char *who); + /* Implementing this function is optional. */ int (* send_typing) (struct im_connection *, char *who, int flags); - /* For now BitlBee doesn't really handle groups, just set it to NULL. */ + /* 'name' is a handle to add/remove. For now BitlBee doesn't really + * handle groups, just set it to NULL, so you can ignore that + * parameter. */ void (* add_buddy) (struct im_connection *, char *name, char *group); void (* remove_buddy) (struct im_connection *, char *name, char *group); - /* Block list stuff. */ + /* Block list stuff. Implementing these are optional. */ void (* add_permit) (struct im_connection *, char *who); void (* add_deny) (struct im_connection *, char *who); void (* rem_permit) (struct im_connection *, char *who); @@ -150,23 +184,41 @@ struct prpl { void (* set_permit_deny)(struct im_connection *); /* Request profile info. Free-formatted stuff, the IM module gives back - this info via imcb_log(). */ + this info via imcb_log(). Implementing these are optional. */ void (* get_info) (struct im_connection *, char *who); void (* set_my_name) (struct im_connection *, char *name); void (* set_name) (struct im_connection *, char *who, char *name); /* Group chat stuff. */ + /* This is called when the user uses the /invite IRC command. + * - 'who' may be ignored + * - 'message' is a handle to invite + */ void (* chat_invite) (struct groupchat *, char *who, char *message); + /* This is called when the user uses the /part IRC command in a group + * chat. You just should tell the user about it, nothing more. */ void (* chat_leave) (struct groupchat *); + /* This is called when the user sends a message to the groupchat. + * 'flags' may be ignored. */ void (* chat_msg) (struct groupchat *, char *message, int flags); + /* This is called when the user uses the /join #nick IRC command. + * - 'who' is the handle of the nick + */ struct groupchat * (* chat_with) (struct im_connection *, char *who); + /* This is used when the user uses the /join #channel IRC command. If + * your protocol does not support publicly named group chats, then do + * not implement this. */ struct groupchat * (* chat_join) (struct im_connection *, char *room, char *nick, char *password); + /* You can tell what away states your protocol supports, so that + * BitlBee will try to map the IRC away reasons to them, or use + * GAIM_AWAY_CUSTOM when calling skype_set_away(). */ GList *(* away_states)(struct im_connection *ic); - /* Mainly for AOL, since they think "Bung hole" == "Bu ngho le". *sigh* */ + /* Mainly for AOL, since they think "Bung hole" == "Bu ngho le". *sigh* + * - Most protocols will just want to set this to g_strcasecmp().*/ int (* handle_cmp) (const char *who1, const char *who2); }; @@ -174,21 +226,40 @@ struct prpl { void nogaim_init(); G_MODULE_EXPORT GSList *get_connections(); G_MODULE_EXPORT struct prpl *find_protocol( const char *name ); +/* When registering a new protocol, you should allocate space for a new prpl + * struct, initialize it (set the function pointers to point to your + * functions), finally call this function. */ G_MODULE_EXPORT void register_protocol( struct prpl * ); /* Connection management. */ +/* You will need this function in prpl->login() to get an im_connection from + * the account_t parameter. */ G_MODULE_EXPORT struct im_connection *imcb_new( account_t *acc ); G_MODULE_EXPORT void imcb_free( struct im_connection *ic ); +/* Once you're connected, you should call this function, so that the user will + * see the success. */ G_MODULE_EXPORT void imcb_connected( struct im_connection *ic ); +/* This can be used to disconnect when something went wrong (ie. read error + * from the server). You probably want to set the second parameter to TRUE. */ G_MODULE_EXPORT void imc_logout( struct im_connection *ic, int allow_reconnect ); /* Communicating with the user. */ +/* A printf()-like function to tell the user anything you want. */ G_MODULE_EXPORT void imcb_log( struct im_connection *ic, char *format, ... ) G_GNUC_PRINTF( 2, 3 ); +/* To tell the user an error, ie. before logging out when an error occurs. */ G_MODULE_EXPORT void imcb_error( struct im_connection *ic, char *format, ... ) G_GNUC_PRINTF( 2, 3 ); +/* To ask a your about something. + * - 'msg' is the question. + * - 'data' can be your custom struct - it will be passed to the callbacks. + * - 'doit' or 'dont' will be called depending of the answer of the user. + */ G_MODULE_EXPORT void imcb_ask( struct im_connection *ic, char *msg, void *data, void *doit, void *dont ); G_MODULE_EXPORT void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname ); /* Buddy management */ +/* This function should be called for each handle which are visible to the + * user, usually after a login, or if the user added a buddy and the IM + * server confirms that the add was successful. Don't forget to do this! */ G_MODULE_EXPORT void imcb_add_buddy( struct im_connection *ic, char *handle, char *group ); G_MODULE_EXPORT void imcb_remove_buddy( struct im_connection *ic, char *handle, char *group ); G_MODULE_EXPORT struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle ); @@ -196,17 +267,30 @@ G_MODULE_EXPORT void imcb_rename_buddy( struct im_connection *ic, char *handle, G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick ); /* Buddy activity */ +/* To manipulate the status of a handle. + * - flags can be |='d with OPT_* constants. You will need at least: + * OPT_LOGGED_IN and OPT_AWAY. + * - 'state' and 'message' can be NULL */ G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ); /* Not implemented yet! */ G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ); +/* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, u_int32_t flags, time_t sent_at ); G_MODULE_EXPORT void imcb_buddy_typing( struct im_connection *ic, char *handle, u_int32_t flags ); G_MODULE_EXPORT void imcb_clean_handle( struct im_connection *ic, char *handle ); /* Groupchats */ G_MODULE_EXPORT void imcb_chat_invited( struct im_connection *ic, char *handle, char *who, char *msg, GList *data ); +/* These two functions are to create a group chat. + * - imcb_chat_new(): the 'handle' parameter identifies the chat, like the + * channel name on IRC. + * - After you have a groupchat pointer, you should add the handles, finally + * the user her/himself. At that point the group chat will be visible to the + * user, too. */ G_MODULE_EXPORT struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle ); G_MODULE_EXPORT void imcb_chat_add_buddy( struct groupchat *b, char *handle ); +/* To remove a handle from a group chat. Reason can be NULL. */ G_MODULE_EXPORT void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason ); +/* To tell BitlBee 'who' said 'msg' in 'c'. 'flags' and 'sent_at' can be 0. */ G_MODULE_EXPORT void imcb_chat_msg( struct groupchat *c, char *who, char *msg, u_int32_t flags, time_t sent_at ); G_MODULE_EXPORT void imcb_chat_free( struct groupchat *c ); diff --git a/root_commands.c b/root_commands.c index 0f9f776c..e8c796d3 100644 --- a/root_commands.c +++ b/root_commands.c @@ -382,7 +382,7 @@ static void cmd_account( irc_t *irc, char **cmd ) return; } - if( cmd[3] ) + if( cmd[3] && set_name ) { set_t *s = set_find( &a->set, set_name ); diff --git a/storage_xml.c b/storage_xml.c index 00fca425..8618c5fe 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -26,7 +26,7 @@ #define BITLBEE_CORE #include "bitlbee.h" #include "base64.h" -#include "rc4.h" +#include "arc.h" #include "md5.h" typedef enum @@ -131,7 +131,8 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na else if( g_strcasecmp( element_name, "account" ) == 0 ) { char *protocol, *handle, *server, *password = NULL, *autoconnect; - char *pass_b64 = NULL, *pass_rc4 = NULL; + char *pass_b64 = NULL; + unsigned char *pass_cr = NULL; int pass_len; struct prpl *prpl = NULL; @@ -150,9 +151,8 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na 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 ) ) + else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) && + arc_decode( pass_cr, pass_len, &password, xd->given_pass ) ) { xd->current_account = account_add( irc, prpl, handle, password ); if( server ) @@ -168,7 +168,7 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na "Error while decrypting account password" ); } - g_free( pass_rc4 ); + g_free( pass_cr ); g_free( password ); } else if( g_strcasecmp( element_name, "setting" ) == 0 ) @@ -409,7 +409,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) 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 ); + pass_buf = base64_encode( 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; @@ -423,12 +423,13 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) for( acc = irc->accounts; acc; acc = acc->next ) { - char *pass_rc4, *pass_b64; + unsigned char *pass_cr; + char *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 ); + pass_len = arc_encode( acc->pass, strlen( acc->pass ), (unsigned char**) &pass_cr, irc->password ); + pass_b64 = base64_encode( pass_cr, pass_len ); + g_free( pass_cr ); if( !xml_printf( fd, 1, "<account protocol=\"%s\" handle=\"%s\" password=\"%s\" autoconnect=\"%d\"", acc->prpl->name, acc->user, pass_b64, acc->auto_connect ) ) { diff --git a/tests/Makefile b/tests/Makefile index 4d4ed8d3..5bc3fbde 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -12,7 +12,7 @@ distclean: clean main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o -test_objs = check.o check_util.o check_nick.o check_md5.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o +test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o check: $(test_objs) $(addprefix ../, $(main_objs)) ../protocols/protocols.o ../lib/lib.o @echo '*' Linking $@ diff --git a/tests/check.c b/tests/check.c index 488d9608..043889d6 100644 --- a/tests/check.c +++ b/tests/check.c @@ -47,6 +47,9 @@ Suite *nick_suite(void); /* From check_md5.c */ Suite *md5_suite(void); +/* From check_arc.c */ +Suite *arc_suite(void); + /* From check_irc.c */ Suite *irc_suite(void); @@ -101,6 +104,7 @@ int main (int argc, char **argv) sr = srunner_create(util_suite()); srunner_add_suite(sr, nick_suite()); srunner_add_suite(sr, md5_suite()); + srunner_add_suite(sr, arc_suite()); srunner_add_suite(sr, irc_suite()); srunner_add_suite(sr, help_suite()); srunner_add_suite(sr, user_suite()); diff --git a/tests/check_arc.c b/tests/check_arc.c new file mode 100644 index 00000000..989a0a66 --- /dev/null +++ b/tests/check_arc.c @@ -0,0 +1,95 @@ +#include <stdlib.h> +#include <glib.h> +#include <gmodule.h> +#include <check.h> +#include <string.h> +#include <stdio.h> +#include "arc.h" + +char *password = "TotT"; + +char *clear_tests[] = +{ + "Wie dit leest is gek :-)", + "ItllBeBitlBee", + "One more boring password", + NULL +}; + +static void check_codec(int l) +{ + int i; + + for( i = 0; clear_tests[i]; i++ ) + { + tcase_fn_start (clear_tests[i], __FILE__, __LINE__); + unsigned char *crypted; + char *decrypted; + int len; + + len = arc_encode( clear_tests[i], 0, &crypted, password ); + len = arc_decode( crypted, len, &decrypted, password ); + + fail_if( strcmp( clear_tests[i], decrypted ) != 0, + "%s didn't decrypt back properly", clear_tests[i] ); + + g_free( crypted ); + g_free( decrypted ); + } +} + +struct +{ + unsigned char crypted[24]; + int len; + char *decrypted; +} decrypt_tests[] = { + { + { + 0xc3, 0x0d, 0x43, 0xc3, 0xee, 0x80, 0xe2, 0x8c, 0x0b, 0x29, 0x32, 0x7e, + 0x38, 0x05, 0x82, 0x10, 0x21, 0x1c, 0x4a, 0x00, 0x2c + }, 21, "Debugging sucks" + }, + { + { + 0xb0, 0x00, 0x57, 0x0d, 0x0d, 0x0d, 0x70, 0xe1, 0xc0, 0x00, 0xa4, 0x25, + 0x7d, 0xbe, 0x03, 0xcc, 0x24, 0xd1, 0x0c + }, 19, "Testing rocks" + }, + { + { + 0xb6, 0x92, 0x59, 0xe4, 0xf9, 0xc1, 0x7a, 0xf6, 0xf3, 0x18, 0xea, 0x28, + 0x73, 0x6d, 0xb3, 0x0a, 0x6f, 0x0a, 0x2b, 0x43, 0x57, 0xe9, 0x3e, 0x63 + }, 24, "OSCAR is creepy..." + } +}; + +static void check_decod(int l) +{ + int i; + + for( i = 0; clear_tests[i]; i++ ) + { + tcase_fn_start (decrypt_tests[i].decrypted, __FILE__, __LINE__); + char *decrypted; + int len; + + len = arc_decode( decrypt_tests[i].crypted, decrypt_tests[i].len, + &decrypted, password ); + + fail_if( strcmp( decrypt_tests[i].decrypted, decrypted ) != 0, + "%s didn't decrypt properly", clear_tests[i] ); + + g_free( decrypted ); + } +} + +Suite *arc_suite (void) +{ + Suite *s = suite_create("ArcFour"); + TCase *tc_core = tcase_create("Core"); + suite_add_tcase (s, tc_core); + tcase_add_test (tc_core, check_codec); + tcase_add_test (tc_core, check_decod); + return s; +} diff --git a/tests/check_util.c b/tests/check_util.c index 284ddba3..b00d645b 100644 --- a/tests/check_util.c +++ b/tests/check_util.c @@ -103,6 +103,63 @@ START_TEST(test_set_url_username_pwd) fail_unless (url.port == 1080); END_TEST +struct +{ + char *orig; + int line_len; + char *wrapped; +} word_wrap_tests[] = { + { + "Line-wrapping is not as easy as it seems?", + 16, + "Line-wrapping is\nnot as easy as\nit seems?" + }, + { + "Line-wrapping is not as easy as it seems?", + 8, + "Line-\nwrapping\nis not\nas easy\nas it\nseems?" + }, + { + "Line-wrapping is\nnot as easy as it seems?", + 8, + "Line-\nwrapping\nis\nnot as\neasy as\nit\nseems?" + }, + { + "a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa", + 5, + "a aa\naaa\naaaa\naaaaa\naaaaa\na\naaaaa\naa\naaaaa\naaa", + }, + { + "aaaaaaaa aaaaaaa aaaaaa aaaaa aaaa aaa aa a", + 5, + "aaaaa\naaa\naaaaa\naa\naaaaa\na\naaaaa\naaaa\naaa\naa a", + }, + { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 5, + "aaaaa\naaaaa\naaaaa\naaaaa\naaaaa\naaaaa\naaaaa\na", + }, + { + NULL + } +}; + +START_TEST(test_word_wrap) + int i; + + for( i = 0; word_wrap_tests[i].orig && *word_wrap_tests[i].orig; i ++ ) + { + char *wrapped = word_wrap( word_wrap_tests[i].orig, word_wrap_tests[i].line_len ); + + fail_unless( strcmp( word_wrap_tests[i].wrapped, wrapped ) == 0, + "%s (line_len = %d) should wrap to `%s', not to `%s'", + word_wrap_tests[i].orig, word_wrap_tests[i].line_len, + word_wrap_tests[i].wrapped, wrapped ); + + g_free( wrapped ); + } +END_TEST + Suite *util_suite (void) { Suite *s = suite_create("Util"); @@ -115,5 +172,6 @@ Suite *util_suite (void) tcase_add_test (tc_core, test_set_url_port); tcase_add_test (tc_core, test_set_url_username); tcase_add_test (tc_core, test_set_url_username_pwd); + tcase_add_test (tc_core, test_word_wrap); return s; } @@ -46,19 +46,18 @@ int main( int argc, char *argv[], char **envp ) memset( &global, 0, sizeof( global_t ) ); - b_main_init(); log_init(); - nogaim_init(); - - 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 ); + b_main_init(); + nogaim_init(); + + srand( time( NULL ) ^ getpid() ); + global.helpfile = g_strdup( HELP_FILE ); + if( global.conf->runmode == RUNMODE_INETD ) { i = bitlbee_inetd_init(); |