diff options
author | Sven Moritz Hallberg <sm@khjk.org> | 2008-02-16 18:15:31 +0100 |
---|---|---|
committer | Sven Moritz Hallberg <sm@khjk.org> | 2008-02-16 18:15:31 +0100 |
commit | 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206 (patch) | |
tree | a54ec7b6b4622aa35ef7bd1efc58ab8ec45749ae | |
parent | 896195002cc903ec4b1ef7e1468f73c1dc08df9e (diff) | |
parent | ca605509d0b49e6012d10ae5d1553ced007e6ce7 (diff) |
merge in upstream changes
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | bitlbee.c | 6 | ||||
-rw-r--r-- | bitlbee.h | 9 | ||||
-rw-r--r-- | conf.c | 45 | ||||
-rw-r--r-- | crypting.c | 5 | ||||
-rw-r--r-- | debian/watch | 2 | ||||
-rw-r--r-- | help.c | 66 | ||||
-rw-r--r-- | help.h | 5 | ||||
-rw-r--r-- | irc.c | 16 | ||||
-rw-r--r-- | irc.h | 1 | ||||
-rw-r--r-- | irc_commands.c | 4 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | protocols/Makefile | 2 | ||||
-rw-r--r-- | protocols/jabber/Makefile | 2 | ||||
-rw-r--r-- | protocols/jabber/conference.c | 11 | ||||
-rw-r--r-- | protocols/jabber/iq.c | 3 | ||||
-rw-r--r-- | protocols/jabber/jabber.h | 20 | ||||
-rw-r--r-- | protocols/jabber/jabber_util.c | 16 | ||||
-rw-r--r-- | protocols/jabber/presence.c | 34 | ||||
-rw-r--r-- | protocols/jabber/sasl.c | 27 | ||||
-rw-r--r-- | protocols/msn/Makefile | 2 | ||||
-rw-r--r-- | protocols/msn/ns.c | 2 | ||||
-rw-r--r-- | protocols/msn/sb.c | 2 | ||||
-rw-r--r-- | protocols/oscar/Makefile | 2 | ||||
-rw-r--r-- | protocols/oscar/oscar.c | 10 | ||||
-rw-r--r-- | protocols/yahoo/Makefile | 2 | ||||
-rw-r--r-- | root_commands.c | 11 | ||||
-rw-r--r-- | storage_text.c | 8 | ||||
-rw-r--r-- | storage_xml.c | 4 | ||||
-rw-r--r-- | tests/Makefile | 4 | ||||
-rw-r--r-- | tests/check.c | 4 | ||||
-rw-r--r-- | tests/check_arc.c | 5 | ||||
-rw-r--r-- | tests/check_jabber_sasl.c | 117 | ||||
-rw-r--r-- | unix.c | 7 |
34 files changed, 332 insertions, 125 deletions
@@ -49,7 +49,6 @@ distclean: clean $(subdirs) check: all $(MAKE) -C tests -lcov: gcov: check gcov *.c @@ -47,7 +47,11 @@ int bitlbee_daemon_init() memset( &hints, 0, sizeof( hints ) ); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; + hints.ai_flags = AI_PASSIVE +#ifdef AI_ADDRCONFIG + | AI_ADDRCONFIG +#endif + ; i = getaddrinfo( global.conf->iface, global.conf->port, &hints, &addrinfo_bind ); if( i ) @@ -98,6 +98,12 @@ #define F_OK 0 #endif +#ifndef G_GNUC_MALLOC +/* Doesn't exist in GLib <=2.4 while everything else in BitlBee should + work with it, so let's fake this one. */ +#define G_GNUC_MALLOC +#endif + #define _( x ) x #define ROOT_NICK "root" @@ -115,8 +121,6 @@ #define HELP_FILE VARDIR "help.txt" #define CONF_FILE_DEF ETCDIR "bitlbee.conf" -extern char *CONF_FILE; - #include "irc.h" #include "storage.h" #include "set.h" @@ -139,6 +143,7 @@ typedef struct global { int listen_socket; gint listen_watch_source_id; help_t *help; + char *conf_file; conf_t *conf; GList *storage; /* The first backend in the list will be used for saving */ char *helpfile; @@ -35,14 +35,12 @@ #include "proxy.h" -char *CONF_FILE; - static int conf_loadini( conf_t *conf, char *file ); conf_t *conf_load( int argc, char *argv[] ) { conf_t *conf; - int opt, i; + int opt, i, config_missing = 0; conf = g_new0( conf_t, 1 ); @@ -66,15 +64,17 @@ conf_t *conf_load( int argc, char *argv[] ) conf->user = NULL; proxytype = 0; - i = conf_loadini( conf, CONF_FILE ); + i = conf_loadini( conf, global.conf_file ); if( i == 0 ) { - fprintf( stderr, "Error: Syntax error in configuration file `%s'.\n", CONF_FILE ); - return( NULL ); + fprintf( stderr, "Error: Syntax error in configuration file `%s'.\n", global.conf_file ); + return NULL; } else if( i == -1 ) { - fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", CONF_FILE ); + config_missing ++; + /* Whine after parsing the options if there was no -c pointing + at a *valid* configuration file. */ } while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hR:u:" ) ) >= 0 ) @@ -106,16 +106,16 @@ conf_t *conf_load( int argc, char *argv[] ) conf->runmode = RUNMODE_FORKDAEMON; else if( opt == 'c' ) { - if( strcmp( CONF_FILE, optarg ) != 0 ) + if( strcmp( global.conf_file, optarg ) != 0 ) { - g_free( CONF_FILE ); - CONF_FILE = g_strdup( optarg ); + g_free( global.conf_file ); + global.conf_file = g_strdup( optarg ); g_free( conf ); /* Re-evaluate arguments. Don't use this option twice, you'll end up in an infinite loop! Hope this trick works with all libcs BTW.. */ optind = 1; - return( conf_load( argc, argv ) ); + return conf_load( argc, argv ); } } else if( opt == 'd' ) @@ -143,7 +143,7 @@ conf_t *conf_load( int argc, char *argv[] ) " -c Load alternative configuration file\n" " -d Specify alternative user configuration directory\n" " -h Show this help page.\n" ); - return( NULL ); + return NULL; } else if( opt == 'R' ) { @@ -169,7 +169,10 @@ conf_t *conf_load( int argc, char *argv[] ) conf->configdir = s; } - return( conf ); + if( config_missing ) + fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", global.conf_file ); + + return conf; } static int conf_loadini( conf_t *conf, char *file ) @@ -178,7 +181,7 @@ static int conf_loadini( conf_t *conf, char *file ) int i; ini = ini_open( file ); - if( ini == NULL ) return( -1 ); + if( ini == NULL ) return -1; while( ini_read( ini ) ) { if( g_strcasecmp( ini->section, "settings" ) == 0 ) @@ -261,7 +264,7 @@ static int conf_loadini( conf_t *conf, char *file ) if( sscanf( ini->value, "%d", &i ) != 1 ) { fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value ); - return( 0 ); + return 0; } conf->ping_interval = i; } @@ -270,7 +273,7 @@ static int conf_loadini( conf_t *conf, char *file ) if( sscanf( ini->value, "%d", &i ) != 1 ) { fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value ); - return( 0 ); + return 0; } conf->ping_timeout = i; } @@ -282,7 +285,7 @@ static int conf_loadini( conf_t *conf, char *file ) { fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value ); g_free( url ); - return( 0 ); + return 0; } strncpy( proxyhost, url->host, sizeof( proxyhost ) ); @@ -306,7 +309,7 @@ static int conf_loadini( conf_t *conf, char *file ) else { fprintf( stderr, "Error: Unknown setting `%s` in configuration file.\n", ini->key ); - return( 0 ); + return 0; /* For now just ignore unknown keys... */ } } @@ -314,19 +317,19 @@ static int conf_loadini( conf_t *conf, char *file ) { fprintf( stderr, "Error: Unknown section [%s] in configuration file. " "BitlBee configuration must be put in a [settings] section!\n", ini->section ); - return( 0 ); + return 0; } } ini_close( ini ); - return( 1 ); + return 1; } void conf_loaddefaults( irc_t *irc ) { ini_t *ini; - ini = ini_open( CONF_FILE ); + ini = ini_open( global.conf_file ); if( ini == NULL ) return; while( ini_read( ini ) ) { @@ -28,10 +28,7 @@ included if CRYPTING_MAIN is defined. Or just do "make decode" and the programs will be built. */ -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <glib.h> +#include <bitlbee.h> #include "md5.h" #include "crypting.h" diff --git a/debian/watch b/debian/watch new file mode 100644 index 00000000..66ab4504 --- /dev/null +++ b/debian/watch @@ -0,0 +1,2 @@ +version=2 +http://get.bitlbee.org/src/bitlbee-(.*).tar.gz @@ -70,21 +70,20 @@ help_t *help_init( help_t **help, const char *helpfile ) if( !( t = strstr( s, "\n%\n" ) ) || s[0] != '?' ) { /* FIXME: Clean up */ -// help_close( *help ); - *help = NULL; + help_free( help ); g_free( s ); - return( NULL ); + return NULL; } i = strchr( s, '\n' ) - s; - if( h->string ) + if( h->title ) { h = h->next = g_new0( help_t, 1 ); } - h->string = g_new ( char, i ); + h->title = g_new ( char, i ); - strncpy( h->string, s + 1, i - 1 ); - h->string[i-1] = 0; + strncpy( h->title, s + 1, i - 1 ); + h->title[i-1] = 0; h->fd = (*help)->fd; h->offset.file_offset = lseek( h->fd, 0, SEEK_CUR ) - buflen + i + 1; h->length = t - s - i - 1; @@ -102,7 +101,31 @@ help_t *help_init( help_t **help, const char *helpfile ) return( *help ); } -char *help_get( help_t **help, char *string ) +void help_free( help_t **help ) +{ + help_t *h, *oh; + int last_fd = -1; /* Weak de-dupe */ + + if( help == NULL || *help == NULL ) + return; + + h = *help; + while( h ) + { + if( h->fd != last_fd ) + { + close( h->fd ); + last_fd = h->fd; + } + g_free( h->title ); + h = (oh=h)->next; + g_free( oh ); + } + + *help = NULL; +} + +char *help_get( help_t **help, char *title ) { time_t mtime; struct stat stat[1]; @@ -110,28 +133,29 @@ char *help_get( help_t **help, char *string ) for( h = *help; h; h = h->next ) { - if( h->string != NULL && - g_strcasecmp( h->string, string ) == 0 ) + if( h->title != NULL && g_strcasecmp( h->title, title ) == 0 ) break; } if( h && h->length > 0 ) { char *s = g_new( char, h->length + 1 ); - if( fstat( h->fd, stat ) != 0 ) - { - g_free( h ); - *help = NULL; - return NULL; - } - mtime = stat->st_mtime; - - if( mtime > h->mtime ) - return NULL; - s[h->length] = 0; if( h->fd >= 0 ) { + if( fstat( h->fd, stat ) != 0 ) + { + g_free( s ); + return NULL; + } + mtime = stat->st_mtime; + + if( mtime > h->mtime ) + { + g_free( s ); + return NULL; + } + lseek( h->fd, h->offset.file_offset, SEEK_SET ); read( h->fd, s, h->length ); } @@ -36,13 +36,14 @@ typedef struct help { int fd; time_t mtime; - char *string; + char *title; help_off_t offset; int length; struct help *next; } help_t; G_GNUC_MALLOC help_t *help_init( help_t **help, const char *helpfile ); -char *help_get( help_t **help, char *string ); +void help_free( help_t **help ); +char *help_get( help_t **help, char *title ); #endif @@ -76,7 +76,7 @@ irc_t *irc_new( int fd ) char buf[NI_MAXHOST+1]; if( getnameinfo( (struct sockaddr *) &sock, socklen, buf, - NI_MAXHOST, NULL, -1, 0 ) == 0 ) + NI_MAXHOST, NULL, 0, 0 ) == 0 ) { irc->myhost = g_strdup( ipv6_unwrap( buf ) ); } @@ -87,7 +87,7 @@ irc_t *irc_new( int fd ) char buf[NI_MAXHOST+1]; if( getnameinfo( (struct sockaddr *)&sock, socklen, buf, - NI_MAXHOST, NULL, -1, 0 ) == 0 ) + NI_MAXHOST, NULL, 0, 0 ) == 0 ) { irc->host = g_strdup( ipv6_unwrap( buf ) ); } @@ -198,7 +198,6 @@ void irc_free(irc_t * irc) { account_t *account; user_t *user, *usertmp; - help_t *helpnode, *helpnodetmp; log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); @@ -275,17 +274,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->help != NULL) { - helpnode = irc->help; - while (helpnode != NULL) { - g_free(helpnode->string); - - helpnodetmp = helpnode; - helpnode = helpnode->next; - g_free(helpnodetmp); - } - } - otr_free(irc->otr); g_free(irc); @@ -90,7 +90,6 @@ typedef struct irc GHashTable *userhash; GHashTable *watches; struct __NICK *nicks; - struct help *help; struct set *set; gint r_watch_source_id; diff --git a/irc_commands.c b/irc_commands.c index 65f0d6c6..68db4617 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -555,7 +555,7 @@ static void irc_cmd_completions( irc_t *irc, char **cmd ) irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", commands[i].command ); for( h = global.help; h; h = h->next ) - irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->string ); + irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->title ); for( s = irc->set; s; s = s->next ) irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS set ", s->key ); @@ -570,7 +570,7 @@ static void irc_cmd_rehash( irc_t *irc, char **cmd ) else ipc_to_master( cmd ); - irc_reply( irc, 382, "%s :Rehashing", CONF_FILE ); + irc_reply( irc, 382, "%s :Rehashing", global.conf_file ); } static const command_t irc_commands[] = { diff --git a/lib/Makefile b/lib/Makefile index a79f7c4c..975deceb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -17,7 +17,7 @@ LFLAGS += -r # [SH] Phony targets all: lib.o check: all -lcov: +lcov: check gcov: gcov *.c diff --git a/protocols/Makefile b/protocols/Makefile index f7d76e0f..18d79e8d 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -26,7 +26,7 @@ LFLAGS += -r # [SH] Phony targets all: protocols.o check: all -lcov: +lcov: check gcov: gcov *.c diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index e042f812..3ce78127 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -17,7 +17,7 @@ LFLAGS += -r # [SH] Phony targets all: jabber_mod.o check: all -lcov: +lcov: check gcov: gcov *.c diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index 515194fc..79fdd053 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -36,6 +36,8 @@ struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char * node = xt_new_node( "x", NULL, NULL ); xt_add_attr( node, "xmlns", XMLNS_MUC ); node = jabber_make_packet( "presence", NULL, roomjid, node ); + if( password ) + xt_add_child( node, xt_new_node( "password", password, NULL ) ); jabber_cache_add( ic, node, jabber_chat_join_failed ); if( !jabber_write_packet( ic, node ) ) @@ -122,6 +124,8 @@ int jabber_chat_msg( struct groupchat *c, char *message, int flags ) struct jabber_chat *jc = c->data; struct xt_node *node; + jc->flags |= JCFLAG_MESSAGE_SENT; + node = xt_new_node( "body", message, NULL ); node = jabber_make_packet( "message", "groupchat", jc->name, node ); @@ -294,10 +298,11 @@ void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud { struct xt_node *subject = xt_find_node( node->children, "subject" ); struct xt_node *body = xt_find_node( node->children, "body" ); - struct groupchat *chat = NULL; + struct groupchat *chat = bud ? jabber_chat_by_jid( ic, bud->bare_jid ) : NULL; + struct jabber_chat *jc = chat ? chat->data : NULL; char *s; - if( bud == NULL ) + if( bud == NULL || ( jc && ~jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me ) ) { char *nick; @@ -345,7 +350,7 @@ void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud return; } - else if( ( chat = jabber_chat_by_jid( ic, bud->bare_jid ) ) == NULL ) + else if( chat == NULL ) { /* How could this happen?? We could do kill( self, 11 ) now or just wait for the OS to do it. :-) */ diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index e1bab29e..c88bc0b0 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -91,7 +91,8 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) } else if( strcmp( s, XMLNS_DISCOVER ) == 0 ) { - const char *features[] = { XMLNS_VERSION, + const char *features[] = { XMLNS_DISCOVER, + XMLNS_VERSION, XMLNS_TIME, XMLNS_CHATSTATES, XMLNS_MUC, diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index cf0f8e6a..1ff0e8dd 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -48,16 +48,22 @@ typedef enum typedef enum { - JBFLAG_PROBED_XEP85 = 1, /* Set this when we sent our probe packet to make + JBFLAG_PROBED_XEP85 = 1, /* Set this when we sent our probe packet to make sure it gets sent only once. */ - JBFLAG_DOES_XEP85 = 2, /* Set this when the resource seems to support + JBFLAG_DOES_XEP85 = 2, /* Set this when the resource seems to support XEP85 (typing notification shite). */ - JBFLAG_IS_CHATROOM = 4, /* It's convenient to use this JID thingy for + JBFLAG_IS_CHATROOM = 4, /* It's convenient to use this JID thingy for groupchat state info too. */ - JBFLAG_IS_ANONYMOUS = 8, /* For anonymous chatrooms, when we don't have + JBFLAG_IS_ANONYMOUS = 8, /* For anonymous chatrooms, when we don't have have a real JID. */ } jabber_buddy_flags_t; +typedef enum +{ + JCFLAG_MESSAGE_SENT = 1, /* Set this after sending the first message, so + we can detect echoes/backlogs. */ +} jabber_chat_flags_t; + struct jabber_data { struct im_connection *ic; @@ -94,6 +100,7 @@ typedef xt_status (*jabber_cache_event) ( struct im_connection *ic, struct xt_no struct jabber_cache_entry { + time_t saved_at; struct xt_node *node; jabber_cache_event func; }; @@ -140,6 +147,10 @@ struct jabber_chat #define JABBER_PACKET_ID "BeeP" #define JABBER_CACHED_ID "BeeC" +/* The number of seconds to keep cached packets before garbage collecting + them. This gc is done on every keepalive (every minute). */ +#define JABBER_CACHE_MAX_AGE 600 + /* RFC 392[01] stuff */ #define XMLNS_TLS "urn:ietf:params:xml:ns:xmpp-tls" #define XMLNS_SASL "urn:ietf:params:xml:ns:xmpp-sasl" @@ -160,6 +171,7 @@ struct jabber_chat #define XMLNS_DISCOVER "http://jabber.org/protocol/disco#info" /* 0030 */ #define XMLNS_MUC "http://jabber.org/protocol/muc" /* XEP-0045 */ #define XMLNS_MUC_USER "http://jabber.org/protocol/muc#user"/* XEP-0045 */ +#define XMLNS_CAPS "http://jabber.org/protocol/caps" /* XEP-0115 */ /* iq.c */ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ); diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index 794a1040..6e872040 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -141,6 +141,7 @@ void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_ca entry->node = node; entry->func = func; + entry->saved_at = time( NULL ); g_hash_table_insert( jd->node_cache, xt_find_attr( node, "id" ), entry ); } @@ -162,22 +163,17 @@ gboolean jabber_cache_clean_entry( gpointer key, gpointer entry, gpointer nullpo void jabber_cache_clean( struct im_connection *ic ) { struct jabber_data *jd = ic->proto_data; + time_t threshold = time( NULL ) - JABBER_CACHE_MAX_AGE; - g_hash_table_foreach_remove( jd->node_cache, jabber_cache_clean_entry, NULL ); + g_hash_table_foreach_remove( jd->node_cache, jabber_cache_clean_entry, &threshold ); } -gboolean jabber_cache_clean_entry( gpointer key, gpointer entry_, gpointer nullpointer ) +gboolean jabber_cache_clean_entry( gpointer key, gpointer entry_, gpointer threshold_ ) { struct jabber_cache_entry *entry = entry_; - struct xt_node *node = entry->node; + time_t *threshold = threshold_; - if( node->flags & XT_SEEN ) - return TRUE; - else - { - node->flags |= XT_SEEN; - return FALSE; - } + return entry->saved_at < *threshold; } xt_status jabber_cache_handle_packet( struct im_connection *ic, struct xt_node *node ) diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index 2c49b800..6fc360b7 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -28,7 +28,7 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data ) struct im_connection *ic = data; char *from = xt_find_attr( node, "from" ); char *type = xt_find_attr( node, "type" ); /* NULL should mean the person is online. */ - struct xt_node *c; + struct xt_node *c, *cap; struct jabber_buddy *bud, *send_presence = NULL; int is_chat = 0; char *s; @@ -76,6 +76,26 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data ) else bud->priority = 0; + if( bud && ( cap = xt_find_node( node->children, "c" ) ) && + ( s = xt_find_attr( cap, "xmlns" ) ) && strcmp( s, XMLNS_CAPS ) == 0 ) + { + /* This <presence> stanza includes an XEP-0115 + capabilities part. Not too interesting, but we can + see if it has an ext= attribute. */ + s = xt_find_attr( cap, "ext" ); + if( s && ( strstr( s, "cstates" ) || strstr( s, "chatstate" ) ) ) + bud->flags |= JBFLAG_DOES_XEP85; + + /* This field can contain more information like xhtml + support, but we don't support that ourselves. + Officially the ext= tag was deprecated, but enough + clients do send it. + + (I'm aware that this is not the right way to use + this field.) See for an explanation of ext=: + http://www.xmpp.org/extensions/attic/xep-0115-1.3.html*/ + } + if( is_chat ) jabber_chat_pkt_presence( ic, bud, node ); else @@ -185,7 +205,7 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data ) int presence_send_update( struct im_connection *ic ) { struct jabber_data *jd = ic->proto_data; - struct xt_node *node; + struct xt_node *node, *cap; char *show = jd->away_state->code; char *status = jd->away_message; struct groupchat *c; @@ -198,6 +218,16 @@ int presence_send_update( struct im_connection *ic ) if( status ) xt_add_child( node, xt_new_node( "status", status, NULL ) ); + /* This makes the packet slightly bigger, but clients interested in + capabilities can now cache the discovery info. This reduces the + usual post-login iq-flood. See XEP-0115. At least libpurple and + Trillian seem to do this right. */ + cap = xt_new_node( "c", NULL, NULL ); + xt_add_attr( cap, "xmlns", XMLNS_CAPS ); + xt_add_attr( cap, "node", "http://bitlbee.org/xmpp/caps" ); + xt_add_attr( cap, "ver", BITLBEE_VERSION ); /* The XEP wants this hashed, but nobody's doing that. */ + xt_add_child( node, cap ); + st = jabber_write_packet( ic, node ); /* Have to send this update to all groupchats too, the server won't diff --git a/protocols/jabber/sasl.c b/protocols/jabber/sasl.c index 87059051..53248ef3 100644 --- a/protocols/jabber/sasl.c +++ b/protocols/jabber/sasl.c @@ -21,6 +21,8 @@ * * \***************************************************************************/ +#include <ctype.h> + #include "jabber.h" #include "base64.h" @@ -106,12 +108,17 @@ xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data ) return XT_HANDLED; } -static char *sasl_get_part( char *data, char *field ) +/* Non-static function, but not mentioned in jabber.h because it's for internal + use, just that the unittest should be able to reach it... */ +char *sasl_get_part( char *data, char *field ) { int i, len; len = strlen( field ); + while( isspace( *data ) || *data == ',' ) + data ++; + if( g_strncasecmp( data, field, len ) == 0 && data[len] == '=' ) { i = strlen( field ) + 1; @@ -128,13 +135,19 @@ static char *sasl_get_part( char *data, char *field ) i ++; } - /* If we got a comma, we got a new field. Check it. */ - if( data[i] == ',' && - g_strncasecmp( data + i + 1, field, len ) == 0 && - data[i+len+1] == '=' ) + /* If we got a comma, we got a new field. Check it, + find the next key after it. */ + if( data[i] == ',' ) { - i += len + 2; - break; + while( isspace( data[i] ) || data[i] == ',' ) + i ++; + + if( g_strncasecmp( data + i, field, len ) == 0 && + data[i+len] == '=' ) + { + i += len + 1; + break; + } } } } diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 3440658d..6a588613 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -17,7 +17,7 @@ LFLAGS += -r # [SH] Phony targets all: msn_mod.o check: all -lcov: +lcov: check gcov: gcov *.c diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 3735aad6..0bb84a74 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -583,7 +583,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else { - debug( "Received unknown command from main server: %s", cmd[0] ); + /* debug( "Received unknown command from main server: %s", cmd[0] ); */ } return( 1 ); diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index cdf2e8ad..18c41ef5 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -593,7 +593,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else { - debug( "Received unknown command from switchboard server: %s", cmd[0] ); + /* debug( "Received unknown command from switchboard server: %s", cmd[0] ); */ } return( 1 ); diff --git a/protocols/oscar/Makefile b/protocols/oscar/Makefile index 95e85ec2..2792f22a 100644 --- a/protocols/oscar/Makefile +++ b/protocols/oscar/Makefile @@ -17,7 +17,7 @@ LFLAGS += -r # [SH] Phony targets all: oscar_mod.o check: all -lcov: +lcov: check gcov: gcov *.c diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 9167f6a3..120ebc3e 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -1065,12 +1065,14 @@ static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_ } else if (args->mpmsg.numparts == 0) { g_snprintf(tmp, BUF_LONG, "%s", args->msg); } else { - int i; + aim_mpmsg_section_t *part; *tmp = 0; - for (i = 0; i < args->mpmsg.numparts; i ++) { - g_strlcat(tmp, (char*) args->mpmsg.parts[i].data, BUF_LONG); - g_strlcat(tmp, "\n", BUF_LONG); + for (part = args->mpmsg.parts; part; part = part->next) { + if (part->data) { + g_strlcat(tmp, (char*) part->data, BUF_LONG); + g_strlcat(tmp, "\n", BUF_LONG); + } } } diff --git a/protocols/yahoo/Makefile b/protocols/yahoo/Makefile index 2cfd147b..b4fe56e2 100644 --- a/protocols/yahoo/Makefile +++ b/protocols/yahoo/Makefile @@ -17,7 +17,7 @@ LFLAGS += -r # [SH] Phony targets all: yahoo_mod.o check: all -lcov: +lcov: check gcov: gcov *.c diff --git a/root_commands.c b/root_commands.c index d47a8b1d..5b64052f 100644 --- a/root_commands.c +++ b/root_commands.c @@ -453,7 +453,7 @@ static void cmd_add( irc_t *irc, char **cmd ) if( g_strcasecmp( cmd[1], "-tmp" ) == 0 ) { add_on_server = 0; - cmd ++; /* So evil... :-D */ + cmd ++; } if( !( a = account_get( irc, cmd[1] ) ) ) @@ -485,12 +485,13 @@ static void cmd_add( irc_t *irc, char **cmd ) } } - /* By making this optional, you can talk to people without having to - add them to your *real* (server-side) contact list. */ if( add_on_server ) a->ic->acc->prpl->add_buddy( a->ic, cmd[2], NULL ); - - /* add_buddy( a->ic, NULL, cmd[2], cmd[2] ); */ + else + /* Yeah, officially this is a call-*back*... So if we just + called add_buddy, we'll wait for the IM server to respond + before we do this. */ + imcb_add_buddy( a->ic, cmd[2], NULL ); irc_usermsg( irc, "Adding `%s' to your contact list", cmd[2] ); } diff --git a/storage_text.c b/storage_text.c index 7c29d95a..5ee6438d 100644 --- a/storage_text.c +++ b/storage_text.c @@ -29,10 +29,10 @@ static void text_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.", global.conf->configdir ); - 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 ); + /* Don't complain about the configuration directory anymore, leave it + up to the XML storage module, which uses the same directory for it + anyway. Nobody should be using just the text plugin anymore since + it's read only! */ } static storage_status_t text_load ( const char *my_nick, const char* password, irc_t *irc ) diff --git a/storage_xml.c b/storage_xml.c index 4c372cde..19070a74 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -262,9 +262,9 @@ GMarkupParser xml_parser = 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.", global.conf->configdir ); + log_message( LOGLVL_WARNING, "The configuration directory `%s' does not exist. Configuration won't be saved.", global.conf->configdir ); 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 ); + 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 ) diff --git a/tests/Makefile b/tests/Makefile index 5bc3fbde..ae76fef5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -10,9 +10,9 @@ clean: distclean: clean -main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o +main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o -test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o +test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o check_jabber_sasl.o check: $(test_objs) $(addprefix ../, $(main_objs)) ../protocols/protocols.o ../lib/lib.o @echo '*' Linking $@ diff --git a/tests/check.c b/tests/check.c index 043889d6..b3ffb957 100644 --- a/tests/check.c +++ b/tests/check.c @@ -65,6 +65,9 @@ Suite *crypting_suite(void); /* From check_set.c */ Suite *set_suite(void); +/* From check_jabber_sasl.c */ +Suite *jabber_sasl_suite(void); + int main (int argc, char **argv) { int nf; @@ -110,6 +113,7 @@ int main (int argc, char **argv) srunner_add_suite(sr, user_suite()); srunner_add_suite(sr, crypting_suite()); srunner_add_suite(sr, set_suite()); + srunner_add_suite(sr, jabber_sasl_suite()); if (no_fork) srunner_set_fork_status(sr, CK_NOFORK); srunner_run_all (sr, verbose?CK_VERBOSE:CK_NORMAL); diff --git a/tests/check_arc.c b/tests/check_arc.c index 989a0a66..a430f899 100644 --- a/tests/check_arc.c +++ b/tests/check_arc.c @@ -61,14 +61,15 @@ struct 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..." - } + }, + { "", 0, NULL } }; static void check_decod(int l) { int i; - for( i = 0; clear_tests[i]; i++ ) + for( i = 0; decrypt_tests[i].len; i++ ) { tcase_fn_start (decrypt_tests[i].decrypted, __FILE__, __LINE__); char *decrypted; diff --git a/tests/check_jabber_sasl.c b/tests/check_jabber_sasl.c new file mode 100644 index 00000000..96c05837 --- /dev/null +++ b/tests/check_jabber_sasl.c @@ -0,0 +1,117 @@ +#include <stdlib.h> +#include <glib.h> +#include <gmodule.h> +#include <check.h> +#include <string.h> +#include <stdio.h> +#include "arc.h" + +char *sasl_get_part( char *data, char *field ); + +#define challenge1 "nonce=\"1669585310\",qop=\"auth\",charset=utf-8,algorithm=md5-sess," \ + "something=\"Not \\\"standardized\\\"\"" +#define challenge2 "realm=\"quadpoint.org\", nonce=\"NPotlQpQf9RNYodOwierkQ==\", " \ + "qop=\"auth, auth-int\", charset=utf-8, algorithm=md5-sess" +#define challenge3 ", realm=\"localhost\", nonce=\"LlBV2txnO8RbB5hgs3KgiQ==\", " \ + "qop=\"auth, auth-int, \", ,\n, charset=utf-8, algorithm=md5-sess," + +struct +{ + const char *challenge; + char *key; + char *value; +} get_part_tests[] = { + { + challenge1, + "nonce", + "1669585310" + }, + { + challenge1, + "charset", + "utf-8" + }, + { + challenge1, + "harset", + NULL + }, + { + challenge1, + "something", + "Not \"standardized\"" + }, + { + challenge1, + "something_else", + NULL + }, + { + challenge2, + "realm", + "quadpoint.org", + }, + { + challenge2, + "real", + NULL + }, + { + challenge2, + "qop", + "auth, auth-int" + }, + { + challenge3, + "realm", + "localhost" + }, + { + challenge3, + "qop", + "auth, auth-int, " + }, + { + challenge3, + "charset", + "utf-8" + }, + { NULL, NULL, NULL } +}; + +static void check_get_part(int l) +{ + int i; + + for( i = 0; get_part_tests[i].key; i++ ) + { + tcase_fn_start( get_part_tests[i].key, __FILE__, i ); + char *res; + int len; + + res = sasl_get_part( get_part_tests[i].challenge, + get_part_tests[i].key ); + + if( get_part_tests[i].value == NULL ) + fail_if( res != NULL, "Found key %s in %s while it shouldn't be there!", + get_part_tests[i].key, get_part_tests[i].challenge ); + else if( res ) + fail_unless( strcmp( res, get_part_tests[i].value ) == 0, + "Incorrect value for key %s in %s: %s", + get_part_tests[i].key, get_part_tests[i].challenge, res ); + else + fail( "Could not find key %s in %s", + get_part_tests[i].key, get_part_tests[i].challenge ); + + g_free( res ); + } +} + +Suite *jabber_sasl_suite (void) +{ + Suite *s = suite_create("jabber/sasl"); + TCase *tc_core = tcase_create("Core"); + suite_add_tcase (s, tc_core); + tcase_add_test (tc_core, check_get_part); + return s; +} @@ -47,7 +47,7 @@ int main( int argc, char *argv[], char **envp ) struct sigaction sig, old; log_init(); - CONF_FILE = g_strdup( CONF_FILE_DEF ); + global.conf_file = g_strdup( CONF_FILE_DEF ); global.conf = conf_load( argc, argv ); if( global.conf == NULL ) return( 1 ); @@ -125,11 +125,14 @@ int main( int argc, char *argv[], char **envp ) if( !getuid() || !geteuid() ) log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" ); - if( help_init( &(global.help), global.helpfile ) == NULL ) + if( help_init( &global.help, global.helpfile ) == NULL ) log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE ); b_main_run(); + /* Mainly good for restarting, to make sure we close the help.txt fd. */ + help_free( &global.help ); + if( global.restart ) { char *fn = ipc_master_save_state(); |