diff options
author | Jelmer Vernooij <jelmer@samba.org> | 2005-12-15 13:24:25 +0100 |
---|---|---|
committer | Jelmer Vernooij <jelmer@samba.org> | 2005-12-15 13:24:25 +0100 |
commit | bd69a219c0a618354fd80a98dd0d9a04fee755e0 (patch) | |
tree | b4477537e48c6d7b2e5a261aa860a0688d69fb66 | |
parent | 2983f5e8c2d3046bf01337e5caefa3af55ba6bff (diff) | |
parent | bf02a679c61b0f030ee8f2f01698699a7775f7d5 (diff) |
Merge Wilmer
41 files changed, 1113 insertions, 636 deletions
@@ -16,3 +16,5 @@ help.txt debian build-arch-stamp tags +decode +encode @@ -9,7 +9,7 @@ -include Makefile.settings # Program variables -objects = account.o bitlbee.o commands.o crypting.o help.o ini.o irc.o nick.o query.o set.o url.o user.o log.o +objects = account.o bitlbee.o commands.o crypting.o help.o ini.o irc.o log.o nick.o query.o set.o url.o user.o storage_text.o storage.o subdirs = protocols ifeq ($(ARCH),Windows) @@ -123,12 +123,6 @@ void account_on( irc_t *irc, account_t *a ) return; } - if (a->prpl == NULL ) - { - irc_usermsg( irc, "Support for protocol %s is not included in this BitlBee", a->prpl->name ); - return; - } - cancel_auto_reconnect( a ); u = g_new0 ( struct aim_user, 1 ); @@ -26,7 +26,6 @@ #define BITLBEE_CORE #include "bitlbee.h" #include "commands.h" -#include "crypting.h" #include "protocols/nogaim.h" #include "help.h" #include <signal.h> @@ -158,7 +157,15 @@ gboolean bitlbee_io_current_client_read( GIOChannel *source, GIOCondition condit irc_free( irc ); return FALSE; } - + + /* Very naughty, go read the RFCs! >:) */ + if( irc->readbuffer && ( strlen( irc->readbuffer ) > 1024 ) ) + { + log_message( LOGLVL_ERROR, "Maximum line length exceeded." ); + irc_free( irc ); + return FALSE; + } + return TRUE; } @@ -229,251 +236,6 @@ gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condi } } -/* DO NOT USE THIS FUNCTION IN NEW CODE. This - * function is here merely because the save/load code still uses - * ids rather then names */ -struct prpl *find_protocol_by_id(int id) -{ - switch (id) { - case 1: 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; -} - -int bitlbee_load( irc_t *irc, char* password ) -{ - char s[512]; - char *line; - char proto[20]; - char nick[MAX_NICK_LENGTH+1]; - FILE *fp; - user_t *ru = user_find( irc, ROOT_NICK ); - - if( irc->status == USTATUS_IDENTIFIED ) - return( 1 ); - - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); - fp = fopen( s, "r" ); - if( !fp ) return( 0 ); - - fscanf( fp, "%32[^\n]s", s ); - if( setpass( irc, password, s ) < 0 ) - { - fclose( fp ); - return( -1 ); - } - - /* Do this now. If the user runs with AuthMode = Registered, the - account command will not work otherwise. */ - irc->status = USTATUS_IDENTIFIED; - - while( fscanf( fp, "%511[^\n]s", s ) > 0 ) - { - fgetc( fp ); - line = deobfucrypt( irc, s ); - root_command_string( irc, ru, line, 0 ); - g_free( line ); - } - fclose( fp ); - - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); - fp = fopen( s, "r" ); - if( !fp ) return( 0 ); - while( fscanf( fp, "%s %s %s", s, proto, nick ) > 0 ) - { - struct prpl *prpl; - - prpl = find_protocol(proto); - - /* Older files saved the protocol number rather then the protocol name */ - if (!prpl && atoi(proto)) { - prpl = find_protocol_by_id(atoi(proto)); - } - - if (!prpl) - continue; - - http_decode( s ); - nick_set( irc, s, prpl, 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( 1 ); -} - -int bitlbee_save( irc_t *irc ) -{ - 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; - - /*\ - * [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 ); - if( hash == NULL ) - { - irc_usermsg( irc, "Please register yourself if you want to save your settings." ); - return( 0 ); - } - - g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks~" ); - fp = fopen( path, "w" ); - if( !fp ) return( 0 ); - 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 ), " %s %s", 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( 0 ); - } - } - if( fclose( fp ) != 0 ) - { - irc_usermsg( irc, "fclose() reported an error. Disk full?" ); - return( 0 ); - } - - 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( 0 ); - } - } - if( rename( path, new_path ) != 0 ) - { - irc_usermsg( irc, "Error while renaming new .nicks file" ); - return( 0 ); - } - - g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts~" ); - fp = fopen( path, "w" ); - if( !fp ) return( 0 ); - if( fprintf( fp, "%s", hash ) != strlen( hash ) ) - { - irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); - fclose( fp ); - return( 0 ); - } - 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( irc, s ); - if( *line ) - { - if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) - { - irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); - fclose( fp ); - return( 0 ); - } - } - 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( irc, s ); - if( *line ) - { - if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) - { - irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); - fclose( fp ); - return( 0 ); - } - } - g_free( line ); - } - } - - if( strcmp( irc->mynick, ROOT_NICK ) != 0 ) - { - g_snprintf( s, sizeof( s ), "rename %s %s", ROOT_NICK, irc->mynick ); - line = obfucrypt( irc, s ); - if( *line ) - { - if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) - { - irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); - fclose( fp ); - return( 0 ); - } - } - g_free( line ); - } - if( fclose( fp ) != 0 ) - { - irc_usermsg( irc, "fclose() reported an error. Disk full?" ); - return( 0 ); - } - - 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( 0 ); - } - } - if( rename( path, new_path ) != 0 ) - { - irc_usermsg( irc, "Error while renaming new .accounts file" ); - return( 0 ); - } - - umask( ou ); - - return( 1 ); -} - void bitlbee_shutdown( gpointer data ) { /* Try to save data for all active connections (if desired). */ @@ -29,7 +29,7 @@ #define _GNU_SOURCE /* Stupid GNU :-P */ #define PACKAGE "BitlBee" -#define BITLBEE_VERSION "1.0pre" +#define BITLBEE_VERSION "BZR" #define VERSION BITLBEE_VERSION #define MAX_STRING 128 @@ -99,6 +99,7 @@ extern char *CONF_FILE; #include "irc.h" +#include "storage.h" #include "set.h" #include "protocols/nogaim.h" #include "commands.h" @@ -114,6 +115,7 @@ typedef struct global_t { int listen_socket; help_t *help; conf_t *conf; + GList *storage; /* The first backend in the list will be used for saving */ char *helpfile; GMainLoop *loop; } global_t; @@ -126,8 +128,6 @@ gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condi int root_command_string( irc_t *irc, user_t *u, char *command, int flags ); int root_command( irc_t *irc, char *command[] ); -int bitlbee_load( irc_t *irc, char *password ); -int bitlbee_save( irc_t *irc ); void bitlbee_shutdown( gpointer data ); double gettime( void ); G_MODULE_EXPORT void http_encode( char *s ); @@ -85,54 +85,47 @@ int cmd_help( irc_t *irc, char **cmd ) int cmd_identify( irc_t *irc, char **cmd ) { - int checkie = bitlbee_load( irc, cmd[1] ); + storage_status_t status = storage_load( irc->nick, cmd[1], irc ); - if( checkie == -1 ) - { + switch (status) { + case STORAGE_INVALID_PASSWORD: irc_usermsg( irc, "Incorrect password" ); - } - else if( checkie == 0 ) - { + break; + case STORAGE_NO_SUCH_USER: irc_usermsg( irc, "The nick is (probably) not registered" ); - } - else if( checkie == 1 ) - { + break; + case STORAGE_OK: irc_usermsg( irc, "Password accepted" ); - } - else - { + break; + default: irc_usermsg( irc, "Something very weird happened" ); + break; } - + return( 0 ); } int cmd_register( irc_t *irc, char **cmd ) { - int checkie; - char path[512]; - if( global.conf->authmode == AUTHMODE_REGISTERED ) { irc_usermsg( irc, "This server does not allow registering new accounts" ); return( 0 ); } - - g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); - checkie = access( path, F_OK ); - - g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); - checkie += access( path, F_OK ); - - if( checkie == -2 ) - { - setpassnc( irc, cmd[1] ); - root_command_string( irc, user_find( irc, irc->mynick ), "save", 0 ); - irc->status = USTATUS_IDENTIFIED; - } - else - { - irc_usermsg( irc, "Nick is already registered" ); + + irc_setpass( irc, cmd[1] ); + switch( storage_save( irc, FALSE )) { + case STORAGE_ALREADY_EXISTS: + irc_usermsg( irc, "Nick is already registered" ); + break; + + case STORAGE_OK: + irc->status = USTATUS_IDENTIFIED; + break; + + default: + irc_usermsg( irc, "Error registering" ); + break; } return( 0 ); @@ -140,35 +133,24 @@ int cmd_register( irc_t *irc, char **cmd ) int cmd_drop( irc_t *irc, char **cmd ) { - char s[512]; - FILE *fp; + storage_status_t status; - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); - fp = fopen( s, "r" ); - if( !fp ) - { + status = storage_remove (irc->nick, cmd[1]); + switch (status) { + case STORAGE_NO_SUCH_USER: irc_usermsg( irc, "That account does not exist" ); return( 0 ); - } - - fscanf( fp, "%32[^\n]s", s ); - fclose( fp ); - if( setpass( irc, cmd[1], s ) < 0 ) - { - irc_usermsg( irc, "Incorrect password" ); + case STORAGE_INVALID_PASSWORD: + irc_usermsg( irc, "Password invalid" ); + return( 0 ); + case STORAGE_OK: + irc_setpass( irc, NULL ); + irc_usermsg( irc, "Account `%s' removed", irc->nick ); + return( 0 ); + default: + irc_usermsg( irc, "Error: '%d'", status ); return( 0 ); } - - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); - unlink( s ); - - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); - unlink( s ); - - setpassnc( irc, NULL ); - irc_usermsg( irc, "Files belonging to account `%s' removed", irc->nick ); - - return( 0 ); } int cmd_account( irc_t *irc, char **cmd ) @@ -622,7 +604,7 @@ int cmd_set( irc_t *irc, char **cmd ) int cmd_save( irc_t *irc, char **cmd ) { - if( bitlbee_save( irc ) ) + if( storage_save( irc, TRUE ) == STORAGE_OK ) irc_usermsg( irc, "Configuration saved" ); else irc_usermsg( irc, "Configuration could not be saved!" ); @@ -49,6 +49,7 @@ conf_t *conf_load( int argc, char *argv[] ) conf->port = 6667; conf->nofork = 0; conf->verbose = 0; + conf->primary_storage = "text"; conf->runmode = RUNMODE_INETD; conf->authmode = AUTHMODE_OPEN; conf->password = NULL; @@ -197,6 +198,16 @@ static int conf_loadini( conf_t *conf, char *file ) g_free( conf->motdfile ); conf->motdfile = g_strdup( ini->value ); } + else if( g_strcasecmp( ini->key, "account_storage" ) == 0 ) + { + g_free( conf->primary_storage ); + conf->primary_storage = g_strdup( ini->value ); + } + else if( g_strcasecmp( ini->key, "account_storage_migrate" ) == 0 ) + { + g_strfreev( conf->migrate_storage ); + conf->migrate_storage = g_strsplit( ini->value, " \t,;", -1 ); + } else if( g_strcasecmp( ini->key, "pinginterval" ) == 0 ) { if( sscanf( ini->value, "%d", &i ) != 1 ) @@ -41,6 +41,8 @@ typedef struct conf char *hostname; char *configdir; char *motdfile; + char *primary_storage; + char **migrate_storage; int ping_interval; int ping_timeout; } conf_t; @@ -56,7 +56,6 @@ Option Description Default --debug=0/1 Disable/enable debugging $debug --strip=0/1 Disable/enable binary stripping $strip ---flood=0/1 Flood protection $flood --ipv6=0/1 IPv6 socket support $ipv6 --ssl=... SSL library to use (gnutls, nss, openssl, bogus, auto) @@ -286,7 +285,11 @@ else fi if [ "$flood" = 1 ]; then - echo '#define FLOOD_SEND' >> config.h + # echo '#define FLOOD_SEND' >> config.h + echo 'Flood protection is disabled in this release because of too many bugs.' 2> /dev/stderr + rm config.h + rm Makefile.settings + exit 1 fi if [ -n "$BITLBEE_VERSION" ]; then @@ -344,33 +347,28 @@ echo echo Architecture: $arch case "$arch" in Linux ) - echo 'Linux.' ;; GNU/* ) - echo 'Debian with non-Linux kernel?' ;; *BSD ) - echo '*BSD.' echo 'EFLAGS+=-liconv' >> Makefile.settings; ;; SunOS ) - echo 'Solaris.' echo 'EFLAGS+=-lresolv -lnsl -lsocket' >> Makefile.settings echo 'STRIP=\# skip strip' >> Makefile.settings echo 'EFLAGS+=-liconv' >> Makefile.settings; ;; Darwin ) - echo 'Darwin/Mac OS X.' echo 'EFLAGS+=-liconv' >> Makefile.settings; ;; IRIX ) - echo 'IRIX.' ;; CYGWIN* ) echo 'Cygwin is not officially supported.' ;; * ) - echo 'We haven'\''t tested BitlBee on many platforms yet, yours is untested. YMMV. Please report any problems to <wilmer@gaast.net>.' + echo 'We haven'\''t tested BitlBee on many platforms yet, yours is untested. YMMV.' + echo 'Please report any problems at http://bugs.bitlbee.org/.' ;; esac @@ -393,11 +391,11 @@ if [ "$msn" = "1" ]; then echo ' Using SSL library: '$ssl; fi -if [ "$flood" = "0" ]; then - echo ' Flood protection disabled.'; -else - echo ' Flood protection enabled.'; -fi +#if [ "$flood" = "0" ]; then +# echo ' Flood protection disabled.'; +#else +# echo ' Flood protection enabled.'; +#fi if [ -n "$protocols" ]; then echo ' Building with these protocols:' $protocols; @@ -28,64 +28,20 @@ included if CRYPTING_MAIN is defined. Or just do "make decode" and the programs will be built. */ -#ifndef CRYPTING_MAIN -#define BITLBEE_CORE -#include "bitlbee.h" -#include "irc.h" #include "md5.h" #include "crypting.h" #include <string.h> #include <stdio.h> #include <stdlib.h> -#else - -typedef struct irc -{ - char *password; -} irc_t; - -#define set_add( a, b, c, d ) -#define set_find( a, b ) NULL - -#include "md5.h" -#include "crypting.h" -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#define irc_usermsg - -#endif - /*\ * [SH] Do _not_ call this if it's not entirely sure that it will not cause * harm to another users file, since this does not check the password for * correctness. \*/ -/* USE WITH CAUTION! - Sets pass without checking */ -void setpassnc (irc_t *irc, char *pass) { - if (!set_find (irc, "password")) - set_add (irc, "password", NULL, passchange); - - if (irc->password) g_free (irc->password); - - if (pass) { - irc->password = g_strdup (pass); - irc_usermsg (irc, "Password successfully changed"); - } else { - irc->password = NULL; - } -} - -char *passchange (irc_t *irc, void *set, char *value) { - setpassnc (irc, value); - return (NULL); -} - -int setpass (irc_t *irc, char *pass, char* md5sum) { +int checkpass (const char *pass, const char *md5sum) +{ md5_state_t md5state; md5_byte_t digest[16]; int i, j; @@ -102,27 +58,25 @@ int setpass (irc_t *irc, char *pass, char* md5sum) { if (digits[0] != md5sum[j]) return (-1); if (digits[1] != md5sum[j + 1]) return (-1); } - - /* If pass is correct, we end up here and we set the pass */ - setpassnc (irc, pass); - - return (0); + + return( 0 ); } -char *hashpass (irc_t *irc) { + +char *hashpass (const char *password) +{ md5_state_t md5state; md5_byte_t digest[16]; int i; char digits[3]; char *rv; - if (irc->password == NULL) return (NULL); + if (password == NULL) return (NULL); - rv = (char *)g_malloc (33); - memset (rv, 0, 33); + rv = g_new0 (char, 33); md5_init (&md5state); - md5_append (&md5state, (unsigned char *)irc->password, strlen (irc->password)); + md5_append (&md5state, (const unsigned char *)password, strlen (password)); md5_finish (&md5state, digest); for (i = 0; i < 16; i++) { @@ -134,47 +88,46 @@ char *hashpass (irc_t *irc) { return (rv); } -char *obfucrypt (irc_t *irc, char *line) { +char *obfucrypt (char *line, const char *password) +{ int i, j; char *rv; - if (irc->password == NULL) return (NULL); + if (password == NULL) return (NULL); - rv = (char *)g_malloc (strlen (line) + 1); - memset (rv, '\0', strlen (line) + 1); + rv = g_new0 (char, strlen (line) + 1); i = j = 0; while (*line) { /* Encrypt/obfuscate the line, using the password */ if (*(signed char*)line < 0) *line = - (*line); - if (((signed char*)irc->password)[i] < 0) irc->password[i] = - irc->password[i]; - rv[j] = *line + irc->password[i]; /* Overflow intended */ + rv[j] = *line + password[i]; /* Overflow intended */ line++; - if (!irc->password[++i]) i = 0; + if (!password[++i]) i = 0; j++; } return (rv); } -char *deobfucrypt (irc_t *irc, char *line) { +char *deobfucrypt (char *line, const char *password) +{ int i, j; char *rv; - if (irc->password == NULL) return (NULL); + if (password == NULL) return (NULL); - rv = (char *)g_malloc (strlen (line) + 1); - memset (rv, '\0', strlen (line) + 1); + rv = g_new0 (char, strlen (line) + 1); i = j = 0; while (*line) { /* Decrypt/deobfuscate the line, using the pass */ - rv[j] = *line - irc->password[i]; /* Overflow intended */ + rv[j] = *line - password[i]; /* Overflow intended */ line++; - if (!irc->password[++i]) i = 0; + if (!password[++i]) i = 0; j++; } @@ -188,9 +141,8 @@ char *deobfucrypt (irc_t *irc, char *line) { int main( int argc, char *argv[] ) { - irc_t *irc = g_malloc( sizeof( irc_t ) ); char *hash, *action, line[256]; - char* (*func)( irc_t *, char * ); + char* (*func)( char *, const char * ); if( argc < 2 ) { @@ -200,10 +152,7 @@ int main( int argc, char *argv[] ) return( 1 ); } - memset( irc, 0, sizeof( irc_t ) ); - irc->password = g_strdup( argv[1] ); - - hash = hashpass( irc ); + hash = hashpass( argv[1] ); action = argv[0] + strlen( argv[0] ) - strlen( "encode" ); if( strcmp( action, "encode" ) == 0 ) @@ -235,7 +184,7 @@ int main( int argc, char *argv[] ) /* Flush the newline */ fgetc( stdin ); - out = func( irc, line ); + out = func( line, argv[1] ); printf( "%s\n", out ); g_free( out ); } @@ -23,9 +23,7 @@ Suite 330, Boston, MA 02111-1307 USA */ -void setpassnc (irc_t *irc, char *pass); /* USE WITH CAUTION! */ -char *passchange (irc_t *irc, void *set, char *value); -int setpass (irc_t *irc, char *pass, char* md5sum); -char *hashpass (irc_t *irc); -char *obfucrypt (irc_t *irc, char *line); -char *deobfucrypt (irc_t *irc, char *line); +int checkpass (const char *password, const char *md5sum); +char *hashpass (const char *password); +char *obfucrypt (char *line, const char *password); +char *deobfucrypt (char *line, const char *password); diff --git a/doc/AUTHORS b/doc/AUTHORS index 82d81862..6dab1040 100644 --- a/doc/AUTHORS +++ b/doc/AUTHORS @@ -3,7 +3,7 @@ Current developers: Wilmer van der Gaast <wilmer@gaast.net> Main developer -Jelmer 'ctrlsoft' Vernooij <jelmer@nl.linux.org> +Jelmer 'ctrlsoft' Vernooij <jelmer@samba.org> Documentation, general hacking, Win32 port Maurits Dijkstra <mauritsd@xs4all.nl> diff --git a/doc/CHANGES b/doc/CHANGES index 2981a378..7b95e8cb 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -10,8 +10,26 @@ Version 1.0: - Cleaned up some unnecessary code in the Jabber module, and implemented handlers for headline messages (which allows you to use RSS-to-Jabber gateways). - -Finished ... +- Lowered the line splitting limit a bit to fix data loss issues. +- The $proto($handle) format used for messages specific to one IM-connection + now only include the ($handle) part when there's more than one $proto- + connection. +- Fix for a crash-bug on broken Jabber/SSL connections. +- Incoming typing notifications now also come in as CTCP TYPING messages, for + better consistency. Don't forget to update your scripts! +- AIM typing notifications are supported now. +- Jabber module only accepts ports 5220-5229 now, to prevent people from + abusing it as a port scanner. We aren't aware of any Jabber server that + runs on other ports than those. If you are, please warn us. +- Send flood protection can't be enabled anymore. It was disabled by default + for a good reason for some time already, but some package maintainers + turned it back on while it's way too unreliable and trigger-happy to be + used. +- Removed TODO file, the current to-do list is always in the on-line bug + tracking system. +- Fixed a potential DoS bug in input handling. + +Finished 4 Dec 2005 Version 0.99: - Fixed memory initialization bug in OSCAR module that caused crashes on @@ -38,30 +38,18 @@ A: 'root' is just the name for the most powerful user in BitlBee. Just like artistic creativity. Q: When is $random_feature going to be implemented? -A: Please do consult doc/TODO (preferably in a development snapshot, which - is more up-to-date than a TODO in a release version) before asking. - Please also check the documentation. You'd not be the first one to request - a feature which already exists! - - If your fabulous feature seems not to be requested before, just join - #bitlbee on irc.oftc.net and tell us the news. - - If your feature request is already in the TODO list, of course you can - still request it again/make us know that you'd like to see the feature as - well. But when the feature is in the "post-1.0" list, it's probably not - going to help. Most of the features in this list are low-priority because - we (the developers) don't need (or even want) them. (File transfers are a - good example here.) - Hence, they'll only be implemented when we really got too much spare - time. Obviously, if you're willing to help (i.e. submit a patch), you're - always welcome. +A: It depends on the feature. We keep a list of all wishlist "bugs" in our + Bug Tracking system at http://bugs.bitlbee.org/ Q: The messages I send and/or receive look weird. I see weird characters and annoying HTML codes. Or, BitlBee does evil things when I send messages with non-ASCII characters! A: You probably have to change some settings. To get rid of HTML in messages, - see "help set html". If you seem to have problems with your charset, see - "help set charset". + see "help set strip_html". If you seem to have problems with your charset, + see "help set charset". + + Although actually most of these problems should be gone by now. So if you + can't get things to work well, you might have found a bug. Q: Is BitlBee forked from Gaim? A: BitlBee 0.7 was, sort-of. It contained a lot of code from Gaim 0.58 @@ -107,11 +107,10 @@ not daemons). See utils/bitlbeed.c for more information about the program. -Just a little note: We run our public server im.bitlbee.org for a couple of -months now, and so far we haven't experienced this problem yet. The only -BitlBee processes killed because of CPU-time overuse were running for a long -time already, they were usually killed during the MSN login process (which -is quite CPU-time consuming). +Just a little note: Now that we reach version 1.0, this shouldn't be that +much of an issue anymore. However, on a public server, especially if you +also use it for other things, it can't hurt to protect yourself against +possible problems. USAGE @@ -145,6 +144,12 @@ WEBSITE You can find new releases of BitlBee at: http://www.bitlbee.org/ +The bug tracking system: +http://bugs.bitlbee.org/ + +Our version control system is Bazaar-NG. Our repository is at: +http://code.bitlbee.org/ + A NOTE ON ENCRYPTION ==================== @@ -186,5 +191,5 @@ also licensed under the GPL. BitlBee - An IRC to other chat networks gateway <http://www.bitlbee.org/> - Copyright (C) 2002-2004 Wilmer van der Gaast <wilmer@gaast.net> + Copyright (C) 2002-2005 Wilmer van der Gaast <wilmer@gaast.net> and others diff --git a/doc/TODO b/doc/TODO deleted file mode 100644 index 3bd241ef..00000000 --- a/doc/TODO +++ /dev/null @@ -1,40 +0,0 @@ -TODO for BitlBee: - -We're trying to keep a better TODO list now. The priorities here are somewhat -random, maybe. Or at least they change from time to time. The "After 1.0" -changes are very low-priority, the "Before 1.0" are "highest" priority, the -others are changes we'll do when we feel like doing them. They're usually not -as critical as the "Before 1.0" changes, but still a bit more important than -"After 1.0". - -KNOWN BUGS TO FIX BEFORE 1.0: -- 100% cpu usage bugs. - -> They're still there... :-( -- Check if the IRC send flood protection is reliable now. - -> Probably not, at least it still causes troubles on logging in for some - people. Maybe the thresholds aren't okay yet. Logins are just floody, - nothing we can do about that. - -Some time: (mainly features) -- Rewrite Jabber module - the current one sucks. -- Test Yahoo! groupchats a bit better, because they still seem to be a bit - flakey. - -> There are bug reports from time to time, but we can't do much about - them, possibly it's more a libyahoo2 problem. -- Groupchats - -> Make them work on other nets than MSN/Yahoo as well. -- Make usernames case-insensitive. (On case-insensitive filesystems this - change isn't necessary. This one is going to suck with backward-compati- - bilty...) - -> We'll probably combine this with the introduction of a better file - format for userdata. -- Remind the user of unanswered questions after some time. -- Maybe a way to send global messages. (for server shutdowns, for example) -- Away-auto-replies. -- Allow nick changes. - -After 1.0: (mainly toys) -- File transfers -> DCC -- SSL support? Persistent connections? Things you can do with a bouncer/proxy. -- Support for buddy groups. -- What else? diff --git a/doc/user-guide/user-guide.xml b/doc/user-guide/user-guide.xml index eff5998e..5b881fb2 100644 --- a/doc/user-guide/user-guide.xml +++ b/doc/user-guide/user-guide.xml @@ -20,24 +20,13 @@ </author> <legalnotice id="legalnotice"> - <para> - Permission is granted to copy, distribute and/or modify this - document under the terms of the <ulink type="help" - url="gnome-help:fdl"><citetitle>GNU Free Documentation - License</citetitle></ulink>, Version 1.1 or any later version - published by the Free Software Foundation with no Invariant - Sections, no Front-Cover Texts, and no Back-Cover Texts. You - may obtain a copy of the <citetitle>GNU Free Documentation - License</citetitle> from the Free Software Foundation by - visiting <ulink type="http" url="http://www.fsf.org">their - Web site</ulink> or by writing to: Free Software Foundation, - Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - USA. - </para> </legalnotice> <releaseinfo> - This is the initial release of the BitlBee User Guide. + This is the BitlBee User Guide. For now, the on-line help is + the most up-to-date documentation. Although this document shares + some parts with the on-line help system, other parts might be + very outdated. </releaseinfo> </bookinfo> @@ -31,6 +31,12 @@ static gboolean irc_userping( gpointer _irc ); GSList *irc_connection_list = NULL; +static char *passchange (irc_t *irc, void *set, char *value) +{ + irc_setpass (irc, value); + return (NULL); +} + irc_t *irc_new( int fd ) { irc_t *irc = g_new0( irc_t, 1 ); @@ -128,6 +134,7 @@ irc_t *irc_new( int fd ) 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); conf_loaddefaults( irc ); @@ -153,7 +160,7 @@ void irc_free(irc_t * irc) log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); if( irc->status >= USTATUS_IDENTIFIED && set_getint( irc, "save_on_quit" ) ) - if( !bitlbee_save( irc ) ) + if( storage_save( irc, TRUE ) != STORAGE_OK ) irc_usermsg( irc, "Error while saving settings!" ); if( irc->ping_source_id > 0 ) @@ -260,6 +267,20 @@ void irc_free(irc_t * irc) g_main_quit( global.loop ); } +/* USE WITH CAUTION! + Sets pass without checking */ +void irc_setpass (irc_t *irc, const char *pass) +{ + if (irc->password) g_free (irc->password); + + if (pass) { + irc->password = g_strdup (pass); + irc_usermsg (irc, "Password successfully changed"); + } else { + irc->password = NULL; + } +} + int irc_process( irc_t *irc ) { char **lines, *temp; @@ -1509,7 +1530,7 @@ int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char else { irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host, - type, to, prefix, line ); + type, to, prefix ? prefix : "", line ); } line = s + 1; } @@ -136,6 +136,7 @@ void irc_kill( irc_t *irc, user_t *u ); void irc_invite( irc_t *irc, char *nick, char *channel ); void irc_whois( irc_t *irc, char *nick ); int irc_away( irc_t *irc, char *away ); +void irc_setpass( irc_t *irc, const char *pass ); /* USE WITH CAUTION! */ int irc_send( irc_t *irc, char *nick, char *s, int flags ); int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg ); @@ -26,7 +26,7 @@ #define BITLBEE_CORE #include "bitlbee.h" -void nick_set( irc_t *irc, char *handle, struct prpl *proto, char *nick ) +void nick_set( irc_t *irc, const char *handle, struct prpl *proto, const char *nick ) { nick_t *m = NULL, *n = irc->nicks; @@ -55,7 +55,7 @@ void nick_set( irc_t *irc, char *handle, struct prpl *proto, char *nick ) nick_strip( n->nick ); } -char *nick_get( irc_t *irc, char *handle, struct prpl *proto, const char *realname ) +char *nick_get( irc_t *irc, const char *handle, struct prpl *proto, const char *realname ) { static char nick[MAX_NICK_LENGTH+1]; nick_t *n = irc->nicks; @@ -128,7 +128,7 @@ char *nick_get( irc_t *irc, char *handle, struct prpl *proto, const char *realna return( nick ); } -void nick_del( irc_t *irc, char *nick ) +void nick_del( irc_t *irc, const char *nick ) { nick_t *l = NULL, *n = irc->nicks; @@ -175,9 +175,9 @@ void nick_strip( char * nick ) nick[j++] = '\0'; } -int nick_ok( char *nick ) +int nick_ok( const char *nick ) { - char *s; + const char *s; /* Empty/long nicks are not allowed */ if( !*nick || strlen( nick ) > MAX_NICK_LENGTH ) @@ -236,7 +236,7 @@ int nick_uc( char *nick ) return( 1 ); } -int nick_cmp( char *a, char *b ) +int nick_cmp( const char *a, const char *b ) { char aa[1024] = "", bb[1024] = ""; @@ -252,12 +252,7 @@ int nick_cmp( char *a, char *b ) } } -char *nick_dup( char *nick ) +char *nick_dup( const char *nick ) { - char *cp; - - cp = g_new0 ( char, MAX_NICK_LENGTH + 1 ); - strncpy( cp, nick, MAX_NICK_LENGTH ); - - return( cp ); + return g_strndup( nick, MAX_NICK_LENGTH ); } @@ -31,13 +31,13 @@ typedef struct __NICK struct __NICK *next; } nick_t; -void nick_set( irc_t *irc, char *handle, struct prpl *proto, char *nick ); -char *nick_get( irc_t *irc, char *handle, struct prpl *proto, const char *realname ); -void nick_del( irc_t *irc, char *nick ); +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_strip( char *nick ); -int nick_ok( char *nick ); +int nick_ok( const char *nick ); int nick_lc( char *nick ); int nick_uc( char *nick ); -int nick_cmp( char *a, char *b ); -char *nick_dup( char *nick ); +int nick_cmp( const char *a, const char *b ); +char *nick_dup( const char *nick ); diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 413f77ef..fc419124 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -21,10 +21,6 @@ * */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #ifndef _WIN32 #include <sys/utsname.h> #endif @@ -58,6 +54,8 @@ #define DEFAULT_GROUPCHAT "conference.jabber.org" #define DEFAULT_PORT 5222 #define DEFAULT_PORT_SSL 5223 +#define JABBER_PORT_MIN 5220 +#define JABBER_PORT_MAX 5229 #define JABBER_GROUP "Friends" @@ -540,11 +538,6 @@ static void gjab_connected_ssl(gpointer data, void *source, GaimInputCondition c struct jabber_data *jd; gjconn gjc; - if (!g_slist_find(get_connections(), gc)) { - ssl_disconnect(source); - return; - } - jd = gc->proto_data; gjc = jd->gjc; @@ -553,6 +546,11 @@ static void gjab_connected_ssl(gpointer data, void *source, GaimInputCondition c return; } + if (!g_slist_find(get_connections(), gc)) { + ssl_disconnect(source); + return; + } + gjab_connected(data, gjc->fd, cond); } @@ -588,6 +586,11 @@ static void gjab_start(gjconn gjc) port = DEFAULT_PORT; else if (port == -1 && ssl) port = DEFAULT_PORT_SSL; + else 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; + } if (server == NULL) server = g_strdup(gjc->user->server); diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 793a881e..2f4d05d5 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -643,7 +643,7 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int if( who ) { - serv_got_typing( gc, who, 5 ); + serv_got_typing( gc, who, 5, 1 ); g_free( who ); } diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 474b91b2..4966a76f 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -54,6 +54,7 @@ static int remove_chat_buddy_silent( struct conversation *b, char *handle ); GSList *connections; +#ifdef WITH_PLUGINS gboolean load_plugin(char *path) { void (*init_function) (void); @@ -75,6 +76,34 @@ gboolean load_plugin(char *path) return TRUE; } +void load_plugins(void) +{ + GDir *dir; + GError *error = NULL; + + dir = g_dir_open(PLUGINDIR, 0, &error); + + if (dir) { + const gchar *entry; + char *path; + + while ((entry = g_dir_read_name(dir))) { + path = g_build_filename(PLUGINDIR, entry, NULL); + if(!path) { + log_message(LOGLVL_WARNING, "Can't build path for %s\n", entry); + continue; + } + + load_plugin(path); + + g_free(path); + } + + g_dir_close(dir); + } +} +#endif + /* nogaim.c */ GList *protocols = NULL; @@ -100,49 +129,30 @@ struct prpl *find_protocol(const char *name) /* nogaim.c */ void nogaim_init() { - GDir *dir; - GError *error = NULL; + extern void msn_init(); + extern void oscar_init(); + extern void byahoo_init(); + extern void jabber_init(); #ifdef WITH_MSN - extern void msn_init(); msn_init(); #endif #ifdef WITH_OSCAR - extern void oscar_init(); oscar_init(); #endif #ifdef WITH_YAHOO - extern void byahoo_init(); byahoo_init(); #endif #ifdef WITH_JABBER - extern void jabber_init(); jabber_init(); #endif - dir = g_dir_open(PLUGINDIR, 0, &error); - - if (dir) { - const gchar *entry; - char *path; - - while ((entry = g_dir_read_name(dir))) { - path = g_build_filename(PLUGINDIR, entry, NULL); - if(!path) { - log_message(LOGLVL_WARNING, "Can't build path for %s\n", entry); - continue; - } - - load_plugin(path); - - g_free(path); - } - - g_dir_close(dir); - } +#ifdef WITH_PLUGINS + load_plugins(); +#endif } GSList *get_connections() { return connections; } @@ -441,7 +451,14 @@ void signoff( struct gaim_connection *gc ) void do_error_dialog( struct gaim_connection *gc, char *msg, char *title ) { - serv_got_crap( gc, "Error: %s", msg ); + if( msg && title ) + serv_got_crap( gc, "Error: %s: %s", title, msg ); + else if( msg ) + serv_got_crap( gc, "Error: %s", msg ); + else if( title ) + serv_got_crap( gc, "Error: %s", title ); + else + serv_got_crap( gc, "Error" ); } void do_ask_dialog( struct gaim_connection *gc, char *msg, void *data, void *doit, void *dont ) @@ -726,7 +743,7 @@ void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 f /* If there's a newline/space in this string, split up there, looks a bit prettier. */ - if( ( nl = strrchr( msg, '\n' ) ) || ( nl = strchr( msg, ' ' ) ) ) + if( ( nl = strrchr( msg, '\n' ) ) || ( nl = strrchr( msg, ' ' ) ) ) { msg[425] = tmp; tmp = *nl; @@ -750,15 +767,25 @@ void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 f irc_msgfrom( irc, u->nick, msg ); } -void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout ) +void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout, int type ) { user_t *u; if( !set_getint( gc->irc, "typing_notice" ) ) return; - if( ( u = user_findhandle( gc, handle ) ) ) - irc_msgfrom( gc->irc, u->nick, "\1TYPING 1\1" ); + if( ( u = user_findhandle( gc, handle ) ) ) { + /* If type is: + * 0: user has stopped typing + * 1: user is actively typing + * 2: user has entered text, but is not actively typing + */ + if (type == 0 || type == 1 || type == 2) { + char buf[256]; + g_snprintf(buf, 256, "\1TYPING %d\1", type); + irc_privmsg( gc->irc, u, "PRIVMSG", gc->irc->nick, NULL, buf ); + } + } } void serv_got_chat_left( struct gaim_connection *gc, int id ) diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 3d5006d9..8ec65347 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -306,7 +306,7 @@ G_MODULE_EXPORT void show_got_added( struct gaim_connection *gc, char *id, char /* server.c */ G_MODULE_EXPORT void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, int evil, time_t signon, time_t idle, int type, guint caps ); G_MODULE_EXPORT void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 flags, time_t mtime, gint len ); -G_MODULE_EXPORT void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout ); +G_MODULE_EXPORT void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout, int type ); G_MODULE_EXPORT void serv_got_chat_invite( struct gaim_connection *gc, char *handle, char *who, char *msg, GList *data ); G_MODULE_EXPORT struct conversation *serv_got_joined_chat( struct gaim_connection *gc, int id, char *handle ); G_MODULE_EXPORT void serv_got_chat_in( struct gaim_connection *gc, int id, char *who, int whisper, char *msg, time_t mtime ); @@ -325,12 +325,6 @@ G_MODULE_EXPORT void strip_html( char *msg ); G_MODULE_EXPORT char * escape_html(const char *html); G_MODULE_EXPORT void info_string_append(GString *str, char *newline, char *name, char *value); -/* file transfers */ -G_MODULE_EXPORT void ft_progress( struct ft *, int); -G_MODULE_EXPORT void ft_incoming( struct ft_request * ); -G_MODULE_EXPORT void ft_accepted( struct ft_request *, struct ft *); -G_MODULE_EXPORT void ft_denied( struct ft_request *, const char *reason); - /* prefs.c */ G_MODULE_EXPORT void build_block_list(); G_MODULE_EXPORT void build_allow_list(); diff --git a/protocols/oscar/aim.h b/protocols/oscar/aim.h index f7bf1a8e..24cd7730 100644 --- a/protocols/oscar/aim.h +++ b/protocols/oscar/aim.h @@ -465,6 +465,7 @@ int aim_addtlvtochain_availmsg(aim_tlvlist_t **list, const guint16 type, const c int aim_addtlvtochain_raw(aim_tlvlist_t **list, const guint16 t, const guint16 l, const guint8 *v); int aim_addtlvtochain_caps(aim_tlvlist_t **list, const guint16 t, const guint32 caps); int aim_addtlvtochain_noval(aim_tlvlist_t **list, const guint16 type); +int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance); int aim_addtlvtochain_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *ui); int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl); int aim_counttlvchain(aim_tlvlist_t **list); @@ -571,6 +572,11 @@ struct aim_chat_roominfo { unsigned short instance; }; +struct aim_chat_invitation { + struct gaim_connection * gc; + char * name; + guint8 exchange; +}; #define AIM_VISIBILITYCHANGE_PERMITADD 0x05 #define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06 diff --git a/protocols/oscar/chat.c b/protocols/oscar/chat.c index 60aabc79..033c2577 100644 --- a/protocols/oscar/chat.c +++ b/protocols/oscar/chat.c @@ -183,31 +183,6 @@ int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const return 0; } -static int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) -{ - guint8 *buf; - int buflen; - aim_bstream_t bs; - - buflen = 2 + 1 + strlen(roomname) + 2; - - if (!(buf = g_malloc(buflen))) - return 0; - - aim_bstream_init(&bs, buf, buflen); - - aimbs_put16(&bs, exchange); - aimbs_put8(&bs, strlen(roomname)); - aimbs_putraw(&bs, (guint8 *)roomname, strlen(roomname)); - aimbs_put16(&bs, instance); - - aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf); - - g_free(buf); - - return 0; -} - /* * Join a room of name roomname. This is the first step to joining an * already created room. It's basically a Service Request for diff --git a/protocols/oscar/im.c b/protocols/oscar/im.c index 085687e0..c829d409 100644 --- a/protocols/oscar/im.c +++ b/protocols/oscar/im.c @@ -1368,6 +1368,30 @@ static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *r return ret; } + +static void incomingim_ch2_chat_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + + /* XXX aim_chat_roominfo_free() */ + g_free(args->info.chat.roominfo.name); + + return; +} + +static void incomingim_ch2_chat(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + + /* + * Chat room info. + */ + if (servdata) + aim_chat_readroominfo(servdata, &args->info.chat.roominfo); + + args->destructor = (void *)incomingim_ch2_chat_free; + + return; +} + static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) { @@ -1616,6 +1640,8 @@ static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *r if (args.reqclass & AIM_CAPS_ICQSERVERRELAY) incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr); + else if (args.reqclass & AIM_CAPS_CHAT) + incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, sdbsptr); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 538f47ae..5f19f12b 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -61,7 +61,7 @@ /* Don't know if support for UTF8 is really working. For now it's UTF16 here. static int gaim_caps = AIM_CAPS_UTF8; */ -static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY; +static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY | AIM_CAPS_CHAT; static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02}; struct oscar_data { @@ -155,7 +155,6 @@ static char *extract_name(const char *name) { return tmp; } -#if 0 static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int id) { GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats; struct chat_connection *c = NULL; @@ -170,7 +169,7 @@ static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int i return c; } -#endif + static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc, aim_conn_t *conn) { @@ -1073,30 +1072,38 @@ static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_ return 1; } +void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv); +void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv); + static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) { -#if 0 struct gaim_connection *gc = sess->aux_data; -#endif if (args->status != AIM_RENDEZVOUS_PROPOSE) return 1; -#if 0 + if (args->reqclass & AIM_CAPS_CHAT) { char *name = extract_name(args->info.chat.roominfo.name); int *exch = g_new0(int, 1); GList *m = NULL; + char txt[1024]; + struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1); + m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name)); *exch = args->info.chat.roominfo.exchange; m = g_list_append(m, exch); - serv_got_chat_invite(gc, - name ? name : args->info.chat.roominfo.name, - userinfo->sn, - (char *)args->msg, - m); + + g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg ); + + inv->gc = gc; + inv->exchange = *exch; + inv->name = g_strdup(name); + + do_ask_dialog( gc, txt, inv, oscar_accept_chat, oscar_reject_chat); + if (name) g_free(name); } -#endif + return 1; } @@ -2440,10 +2447,20 @@ int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...) sn = va_arg(ap, char*); type2 = va_arg(ap, int); va_end(ap); - - if(type2 == 0x0001 || type2 == 0x0002) - serv_got_typing(gc, sn, 0); - + + if(type2 == 0x0002) { + /* User is typing */ + serv_got_typing(gc, sn, 0, 1); + } + else if (type2 == 0x0001) { + /* User has typed something, but is not actively typing (stale) */ + serv_got_typing(gc, sn, 0, 2); + } + else { + /* User has stopped typing */ + serv_got_typing(gc, sn, 0, 0); + } + return 1; } @@ -2481,6 +2498,138 @@ int oscar_send_typing(struct gaim_connection *gc, char * who, int typing) return( aim_im_sendmtn(od->sess, 1, who, typing ? 0x0002 : 0x0000) ); } +int oscar_chat_send(struct gaim_connection * gc, int id, char *message) +{ + struct oscar_data * od = (struct oscar_data*)gc->proto_data; + struct chat_connection * ccon; + int ret; + guint8 len = strlen(message); + char *s; + + if(!(ccon = find_oscar_chat(gc, id))) + return -1; + + for (s = message; *s; s++) + if (*s & 128) + break; + + /* Message contains high ASCII chars, time for some translation! */ + if (*s) { + s = g_malloc(BUF_LONG); + /* Try if we can put it in an ISO8859-1 string first. + If we can't, fall back to UTF16. */ + if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) { + len = ret; + } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) { + len = ret; + } else { + /* OOF, translation failed... Oh well.. */ + g_free( s ); + s = message; + } + } else { + s = message; + } + + ret = aim_chat_send_im(od->sess, ccon->conn, AIM_CHATFLAGS_NOREFLECT, s, len); + + if (s != message) { + g_free(s); + } + + return (ret >= 0); +} + +void oscar_chat_invite(struct gaim_connection * gc, int id, char *message, char *who) +{ + struct oscar_data * od = (struct oscar_data *)gc->proto_data; + struct chat_connection *ccon = find_oscar_chat(gc, id); + + if (ccon == NULL) + return; + + aim_chat_invite(od->sess, od->conn, who, message ? message : "", + ccon->exchange, ccon->name, 0x0); +} + +void oscar_chat_kill(struct gaim_connection *gc, struct chat_connection *cc) +{ + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + + /* Notify the conversation window that we've left the chat */ + serv_got_chat_left(gc, cc->id); + + /* Destroy the chat_connection */ + od->oscar_chats = g_slist_remove(od->oscar_chats, cc); + if (cc->inpa > 0) + gaim_input_remove(cc->inpa); + aim_conn_kill(od->sess, &cc->conn); + g_free(cc->name); + g_free(cc->show); + g_free(cc); +} + +void oscar_chat_leave(struct gaim_connection * gc, int id) +{ + struct chat_connection * ccon = find_oscar_chat(gc, id); + + if(ccon == NULL) + return; + + oscar_chat_kill(gc, ccon); +} + +int oscar_chat_join(struct gaim_connection * gc, char * name) +{ + struct oscar_data * od = (struct oscar_data *)gc->proto_data; + + aim_conn_t * cur; + + if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) { + + return (aim_chatnav_createroom(od->sess, cur, name, 4) == 0); + + } else { + struct create_room * cr = g_new0(struct create_room, 1); + cr->exchange = 4; + cr->name = g_strdup(name); + od->create_rooms = g_slist_append(od->create_rooms, cr); + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV); + return 1; + } +} + +int oscar_chat_open(struct gaim_connection * gc, char *who) +{ + struct oscar_data * od = (struct oscar_data *)gc->proto_data; + int ret; + static int chat_id = 0; + char * chatname = g_new0(char, strlen(gc->username)+4); + + g_snprintf(chatname, strlen(gc->username) + 4, "%s%d", gc->username, chat_id++); + + ret = oscar_chat_join(gc, chatname); + + aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0); + + g_free(chatname); + + return ret; +} + +void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv) +{ + oscar_chat_join(inv->gc, inv->name); + g_free(inv->name); + g_free(inv); +} + +void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv) +{ + g_free(inv->name); + g_free(inv); +} + void oscar_init() { struct prpl *ret = g_new0(struct prpl, 1); @@ -2494,6 +2643,10 @@ void oscar_init() ret->get_away = oscar_get_away; ret->add_buddy = oscar_add_buddy; ret->remove_buddy = oscar_remove_buddy; + ret->chat_send = oscar_chat_send; + ret->chat_invite = oscar_chat_invite; + ret->chat_leave = oscar_chat_leave; + ret->chat_open = oscar_chat_open; ret->add_permit = oscar_add_permit; ret->add_deny = oscar_add_deny; ret->rem_permit = oscar_rem_permit; diff --git a/protocols/oscar/tlv.c b/protocols/oscar/tlv.c index 11b89758..9d827caf 100644 --- a/protocols/oscar/tlv.c +++ b/protocols/oscar/tlv.c @@ -339,6 +339,31 @@ int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvl return buflen; } +int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) +{ + guint8 *buf; + int buflen; + aim_bstream_t bs; + + buflen = 2 + 1 + strlen(roomname) + 2; + + if (!(buf = g_malloc(buflen))) + return 0; + + aim_bstream_init(&bs, buf, buflen); + + aimbs_put16(&bs, exchange); + aimbs_put8(&bs, strlen(roomname)); + aimbs_putraw(&bs, (guint8 *)roomname, strlen(roomname)); + aimbs_put16(&bs, instance); + + aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf); + + g_free(buf); + + return 0; +} + /** * aim_writetlvchain - Write a TLV chain into a data buffer. * @buf: Destination buffer diff --git a/protocols/proxy.c b/protocols/proxy.c index 6d450c92..0546f2d7 100644 --- a/protocols/proxy.c +++ b/protocols/proxy.c @@ -72,7 +72,7 @@ typedef struct _GaimIOClosure { -static struct sockaddr_in *gaim_gethostbyname(char *host, int port) +static struct sockaddr_in *gaim_gethostbyname(const char *host, int port) { static struct sockaddr_in sin; @@ -113,7 +113,7 @@ static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpoin return TRUE; } -static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) +static void gaim_io_connected(gpointer data, gint source, GaimInputCondition cond) { struct PHB *phb = data; unsigned int len; @@ -143,7 +143,7 @@ static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) } } -static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_none(const char *host, unsigned short port, struct PHB *phb) { struct sockaddr_in *sin; int fd = -1; @@ -162,7 +162,7 @@ static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb) if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { if (sockerr_again()) { - phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb); + phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb); phb->fd = fd; } else { closesocket(fd); @@ -270,7 +270,7 @@ static void http_canwrite(gpointer data, gint source, GaimInputCondition cond) phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb); } -static int proxy_connect_http(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_http(const char *host, unsigned short port, struct PHB *phb) { phb->host = g_strdup(host); phb->port = port; @@ -354,7 +354,7 @@ static void s4_canwrite(gpointer data, gint source, GaimInputCondition cond) phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb); } -static int proxy_connect_socks4(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_socks4(const char *host, unsigned short port, struct PHB *phb) { phb->host = g_strdup(host); phb->port = port; @@ -536,7 +536,7 @@ static void s5_canwrite(gpointer data, gint source, GaimInputCondition cond) phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb); } -static int proxy_connect_socks5(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_socks5(const char *host, unsigned short port, struct PHB *phb) { phb->host = g_strdup(host); phb->port = port; @@ -577,7 +577,7 @@ void gaim_input_remove(gint tag) g_source_remove(tag); } -int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data) +int proxy_connect(const char *host, int port, GaimInputFunction func, gpointer data) { struct PHB *phb; diff --git a/protocols/proxy.h b/protocols/proxy.h index 7c34fc40..47c966d2 100644 --- a/protocols/proxy.h +++ b/protocols/proxy.h @@ -55,6 +55,6 @@ typedef void (*GaimInputFunction)(gpointer, gint, GaimInputCondition); G_MODULE_EXPORT gint gaim_input_add(int, GaimInputCondition, GaimInputFunction, gpointer); G_MODULE_EXPORT void gaim_input_remove(gint); -G_MODULE_EXPORT int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data); +G_MODULE_EXPORT int proxy_connect(const char *host, int port, GaimInputFunction func, gpointer data); #endif /* _PROXY_H_ */ diff --git a/protocols/yahoo/crypt.c b/protocols/yahoo/crypt.c index 00eed70b..5122e3af 100644 --- a/protocols/yahoo/crypt.c +++ b/protocols/yahoo/crypt.c @@ -22,10 +22,6 @@ * already had. isn't that lovely. people should just use linux or * freebsd, crypt works properly on those systems. i hate solaris */ -#if HAVE_CONFIG_H -# include <config.h> -#endif - #if HAVE_STRING_H # include <string.h> #elif HAVE_STRINGS_H diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c index 62ceb0af..c691f18b 100644 --- a/protocols/yahoo/libyahoo2.c +++ b/protocols/yahoo/libyahoo2.c @@ -43,10 +43,6 @@ * */ -#if HAVE_CONFIG_H -# include <config.h> -#endif - #ifndef _WIN32 #include <unistd.h> #endif diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index e55b30af..832d1ab4 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -634,8 +634,14 @@ void ext_yahoo_got_file( int id, char *who, char *url, long expires, char *msg, void ext_yahoo_typing_notify( int id, char *who, int stat ) { struct gaim_connection *gc = byahoo_get_gc_by_id( id ); - - serv_got_typing( gc, who, 1 ); + if (stat == 1) { + /* User is typing */ + serv_got_typing( gc, who, 1, 1 ); + } + else { + /* User stopped typing */ + serv_got_typing( gc, who, 1, 0 ); + } } void ext_yahoo_system_message( int id, char *msg ) diff --git a/protocols/yahoo/yahoo_httplib.c b/protocols/yahoo/yahoo_httplib.c index 41e31a23..dbbe2a84 100644 --- a/protocols/yahoo/yahoo_httplib.c +++ b/protocols/yahoo/yahoo_httplib.c @@ -19,10 +19,6 @@ * */ -#if HAVE_CONFIG_H -# include <config.h> -#endif - #include <stdio.h> #include <stdlib.h> diff --git a/protocols/yahoo/yahoo_util.c b/protocols/yahoo/yahoo_util.c index cb155e3c..3c99cf44 100644 --- a/protocols/yahoo/yahoo_util.c +++ b/protocols/yahoo/yahoo_util.c @@ -19,10 +19,6 @@ * */ -#if HAVE_CONFIG_H -# include <config.h> -#endif - #if STDC_HEADERS # include <string.h> #else diff --git a/storage.c b/storage.c new file mode 100644 index 00000000..b766c9e3 --- /dev/null +++ b/storage.c @@ -0,0 +1,185 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Support for multiple storage backends */ + +/* + 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 "crypting.h" + +extern storage_t storage_text; + +static GList text_entry = { &storage_text, NULL, NULL }; +static GList *storage_backends = &text_entry; + +void register_storage_backend(storage_t *backend) +{ + storage_backends = g_list_append(storage_backends, backend); +} + +static storage_t *storage_init_single(const char *name) +{ + GList *gl; + storage_t *st; + + for (gl = storage_backends; gl; gl = gl->next) { + st = gl->data; + if (strcmp(st->name, name) == 0) + break; + } + + if (gl == NULL) + return NULL; + + if (st->init) + st->init(); + + return st; +} + +GList *storage_init(const char *primary, char **migrate) +{ + GList *ret = NULL; + int i; + storage_t *storage; + + storage = storage_init_single(primary); + if (storage == NULL) + return NULL; + + ret = g_list_append(ret, storage); + + for (i = 0; migrate && migrate[i]; i++) { + storage = storage_init_single(migrate[i]); + + if (storage) + ret = g_list_append(ret, storage); + } + + return ret; +} + +storage_status_t storage_check_pass (const char *nick, const char *password) +{ + GList *gl; + + /* Loop until we don't get NO_SUCH_USER */ + + for (gl = global.storage; gl; gl = gl->next) { + storage_t *st = gl->data; + storage_status_t status; + + status = st->check_pass(nick, password); + if (status != STORAGE_NO_SUCH_USER) + return status; + } + + return STORAGE_NO_SUCH_USER; +} + +storage_status_t storage_load (const char *nick, const char *password, irc_t * irc) +{ + GList *gl; + + /* Loop until we don't get NO_SUCH_USER */ + for (gl = global.storage; gl; gl = gl->next) { + storage_t *st = gl->data; + storage_status_t status; + + status = st->load(nick, password, irc); + if (status == STORAGE_OK) { + irc_setpass(irc, password); + return status; + } + + if (status != STORAGE_NO_SUCH_USER) + return status; + } + + return STORAGE_NO_SUCH_USER; +} + +storage_status_t storage_save (irc_t *irc, int overwrite) +{ + return ((storage_t *)global.storage->data)->save(irc, overwrite); +} + +storage_status_t storage_remove (const char *nick, const char *password) +{ + GList *gl; + storage_status_t ret = STORAGE_OK; + + /* Remove this account from all storage backends. If this isn't + * done, the account will still be usable, it'd just be + * loaded from a different backend. */ + for (gl = global.storage; gl; gl = gl->next) { + storage_t *st = gl->data; + storage_status_t status; + + status = st->remove(nick, password); + if (status != STORAGE_NO_SUCH_USER && + status != STORAGE_OK) + ret = status; + } + + return ret; +} + +storage_status_t storage_rename (const char *onick, const char *nnick, const char *password) +{ + storage_status_t status; + GList *gl = global.storage; + storage_t *primary_storage = gl->data; + irc_t *irc; + + /* First, try to rename in the current write backend, assuming onick + * is stored there */ + status = primary_storage->rename(onick, nnick, password); + if (status != STORAGE_NO_SUCH_USER) + return status; + + /* Try to load from a migration backend and save to the current backend. + * Explicitly remove the account from the migration backend as otherwise + * it'd still be usable under the old name */ + + irc = g_new0(irc_t, 1); + status = storage_load(onick, password, irc); + if (status != STORAGE_OK) { + irc_free(irc); + return status; + } + + g_free(irc->nick); + irc->nick = g_strdup(nnick); + + status = storage_save(irc, FALSE); + if (status != STORAGE_OK) { + irc_free(irc); + return status; + } + irc_free(irc); + + storage_remove(onick, password); + + return STORAGE_OK; +} diff --git a/storage.h b/storage.h new file mode 100644 index 00000000..301b424c --- /dev/null +++ b/storage.h @@ -0,0 +1,66 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Layer for retrieving and storing buddy information */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __STORAGE_H__ +#define __STORAGE_H__ + +typedef enum { + STORAGE_OK = 0, + STORAGE_NO_SUCH_USER, + 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 */ +} storage_status_t; + +typedef struct { + const char *name; + + /* May be set to NULL if not required */ + void (*init) (void); + + storage_status_t (*check_pass) (const char *nick, const char *password); + + storage_status_t (*load) (const char *nick, const char *password, irc_t * irc); + storage_status_t (*save) (irc_t *irc, int overwrite); + storage_status_t (*remove) (const char *nick, const char *password); + + /* May be NULL if not supported by backend */ + storage_status_t (*rename) (const char *onick, const char *nnick, const char *password); +} storage_t; + +storage_status_t storage_check_pass (const char *nick, const char *password); + +storage_status_t storage_load (const char *nick, const char *password, irc_t * irc); +storage_status_t storage_save (irc_t *irc, int overwrite); +storage_status_t storage_remove (const char *nick, const char *password); + +storage_status_t storage_rename (const char *onick, const char *nnick, const char *password); + +void register_storage_backend(storage_t *); +GList *storage_init(const char *primary, char **migrate); + +#endif /* __STORAGE_H__ */ diff --git a/storage_text.c b/storage_text.c new file mode 100644 index 00000000..ed32b73f --- /dev/null +++ b/storage_text.c @@ -0,0 +1,347 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Storage backend that uses the same file format as <=1.0 */ + +/* + 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 "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 1: 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 ) + 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 text_load ( const char *my_nick, const char* password, irc_t *irc ) +{ + char s[512]; + char *line; + int proto; + char nick[MAX_NICK_LENGTH+1]; + FILE *fp; + user_t *ru = user_find( irc, ROOT_NICK ); + + if( irc->status == USTATUS_IDENTIFIED ) + return( 1 ); + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".accounts" ); + fp = fopen( s, "r" ); + if( !fp ) return STORAGE_NO_SUCH_USER; + + fscanf( fp, "%32[^\n]s", s ); + + if (checkpass (password, s) != 0) + { + fclose( fp ); + return STORAGE_INVALID_PASSWORD; + } + + /* Do this now. If the user runs with AuthMode = Registered, the + account command will not work otherwise. */ + irc->status = USTATUS_IDENTIFIED; + + while( fscanf( fp, "%511[^\n]s", s ) > 0 ) + { + fgetc( fp ); + line = deobfucrypt( s, password ); + if (line == NULL) return STORAGE_OTHER_ERROR; + root_command_string( irc, ru, line, 0 ); + g_free( line ); + } + fclose( fp ); + + 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) + continue; + + http_decode( s ); + nick_set( irc, s, prpl, 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; +} + +static storage_status_t text_check_pass( const char *nick, const char *password ) +{ + char s[512]; + FILE *fp; + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".accounts" ); + fp = fopen( s, "r" ); + if (!fp) + return STORAGE_NO_SUCH_USER; + + fscanf( fp, "%32[^\n]s", s ); + fclose( fp ); + + if (checkpass( password, s) == -1) + return STORAGE_INVALID_PASSWORD; + + return STORAGE_OK; +} + +static storage_status_t text_remove( const char *nick, const char *password ) +{ + char s[512]; + storage_status_t status; + + status = text_check_pass( nick, password ); + if (status != STORAGE_OK) + return status; + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".accounts" ); + if (unlink( s ) == -1) + return STORAGE_OTHER_ERROR; + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".nicks" ); + if (unlink( s ) == -1) + return STORAGE_OTHER_ERROR; + + return STORAGE_OK; +} + +storage_t storage_text = { + .name = "text", + .init = text_init, + .check_pass = text_check_pass, + .remove = text_remove, + .load = text_load, + .save = text_save +}; @@ -52,11 +52,12 @@ int main( int argc, char *argv[] ) 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 ) { log_link( LOGLVL_ERROR, LOGOUTPUT_IRC ); @@ -76,6 +77,14 @@ int main( int argc, char *argv[] ) } 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 ); + return( 1 ); + } + /* Catch some signals to tell the user what's happening before quitting */ memset( &sig, 0, sizeof( sig ) ); @@ -93,10 +102,6 @@ int main( int argc, char *argv[] ) if( !getuid() || !geteuid() ) log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" ); - 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 ); if( help_init( &(global.help) ) == NULL ) log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE ); |