diff options
author | Jelmer Vernooij <jelmer@samba.org> | 2006-03-01 23:48:37 +0100 |
---|---|---|
committer | Jelmer Vernooij <jelmer@samba.org> | 2006-03-01 23:48:37 +0100 |
commit | a4dc9f77de03eb46ecabed02dbd1b678319cf11d (patch) | |
tree | 71a8dfa927ea2ac4bfc30a36b4d9324a51247aeb | |
parent | 8e419cb4f86679636b2d96618e1bec4853636c11 (diff) | |
parent | 9a1555dc8521f0973347911bcb26d1038259f967 (diff) |
[merge] Wilmer
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | bitlbee.c | 182 | ||||
-rw-r--r-- | bitlbee.h | 8 | ||||
-rw-r--r-- | commands.h | 32 | ||||
-rw-r--r-- | conf.c | 39 | ||||
-rw-r--r-- | conf.h | 1 | ||||
-rwxr-xr-x | configure | 9 | ||||
-rw-r--r-- | doc/README | 2 | ||||
-rw-r--r-- | ipc.c | 576 | ||||
-rw-r--r-- | ipc.h | 61 | ||||
-rw-r--r-- | irc.c | 825 | ||||
-rw-r--r-- | irc.h | 19 | ||||
-rw-r--r-- | irc_commands.c | 622 | ||||
-rw-r--r-- | log.c | 112 | ||||
-rw-r--r-- | protocols/jabber/jabber.c | 2 | ||||
-rw-r--r-- | protocols/msn/msn.c | 4 | ||||
-rw-r--r-- | protocols/msn/sb.c | 5 | ||||
-rw-r--r-- | protocols/oscar/oscar.c | 1 | ||||
-rw-r--r-- | protocols/oscar/rxqueue.c | 9 | ||||
-rw-r--r-- | protocols/oscar/service.c | 2 | ||||
-rw-r--r-- | protocols/ssl_openssl.c | 86 | ||||
-rw-r--r-- | protocols/yahoo/Makefile | 2 | ||||
-rw-r--r-- | protocols/yahoo/yahoo.c | 17 | ||||
-rw-r--r-- | protocols/yahoo/yahoo_list.c | 236 | ||||
-rw-r--r-- | protocols/yahoo/yahoo_list.h | 68 | ||||
-rw-r--r-- | root_commands.c (renamed from commands.c) | 240 | ||||
-rw-r--r-- | unix.c | 42 | ||||
-rw-r--r-- | url.c | 6 | ||||
-rw-r--r-- | user.h | 2 | ||||
-rwxr-xr-x | utils/bitlbee-ctl.pl | 59 |
30 files changed, 1959 insertions, 1312 deletions
@@ -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 storage.o storage_text.o url.o user.o util.o +objects = account.o bitlbee.o crypting.o help.o ini.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o storage_text.o url.o user.o util.o subdirs = protocols ifeq ($(ARCH),Windows) @@ -28,46 +28,12 @@ #include "commands.h" #include "protocols/nogaim.h" #include "help.h" +#include "ipc.h" #include <signal.h> #include <stdio.h> #include <errno.h> -gboolean bitlbee_io_new_client( GIOChannel *source, GIOCondition condition, gpointer data ) -{ - size_t size = sizeof( struct sockaddr_in ); - struct sockaddr_in conn_info; - int new_socket = accept( global.listen_socket, (struct sockaddr *) &conn_info, &size ); - pid_t client_pid = 0; - - if( global.conf->runmode == RUNMODE_FORKDAEMON ) - client_pid = fork(); - - if( client_pid == 0 ) - { - log_message( LOGLVL_INFO, "Creating new connection with fd %d.", new_socket ); - irc_new( new_socket ); - - if( global.conf->runmode == RUNMODE_FORKDAEMON ) - { - /* Close the listening socket, we're a client. */ - close( global.listen_socket ); - g_source_remove( global.listen_watch_source_id ); - } - } - else - { - /* We don't need this one, only the client does. */ - close( new_socket ); - - /* Or maybe we didn't even get a child process... */ - if( client_pid == -1 ) - log_message( LOGLVL_ERROR, "Failed to fork() subprocess for client: %s", strerror( errno ) ); - } - - return TRUE; -} - - +gboolean bitlbee_io_new_client( GIOChannel *source, GIOCondition condition, gpointer data ); int bitlbee_daemon_init() { @@ -78,6 +44,7 @@ int bitlbee_daemon_init() #endif int i; GIOChannel *ch; + FILE *fp; log_link( LOGLVL_ERROR, LOGOUTPUT_SYSLOG ); log_link( LOGLVL_WARNING, LOGOUTPUT_SYSLOG ); @@ -89,6 +56,10 @@ int bitlbee_daemon_init() return( -1 ); } + /* TIME_WAIT (?) sucks.. */ + i = 1; + setsockopt( global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) ); + #ifdef IPV6 listen_addr.sin6_family = AF_INETx; listen_addr.sin6_port = htons( global.conf->port ); @@ -133,13 +104,34 @@ int bitlbee_daemon_init() } else if( i != 0 ) exit( 0 ); - close( 0 ); - close( 1 ); - close( 2 ); + chdir( "/" ); + + /* Sometimes std* are already closed (for example when we're in a RESTARTed + BitlBee process. So let's only close TTY-fds. */ + if( isatty( 0 ) ) close( 0 ); + if( isatty( 0 ) ) close( 1 ); + if( isatty( 0 ) ) close( 2 ); } #endif + if( global.conf->runmode == RUNMODE_FORKDAEMON ) + ipc_master_load_state(); + + if( global.conf->runmode == RUNMODE_DAEMON || + global.conf->runmode == RUNMODE_FORKDAEMON ) + ipc_master_listen_socket(); + + if( ( fp = fopen( global.conf->pidfile, "w" ) ) ) + { + fprintf( fp, "%d\n", (int) getpid() ); + fclose( fp ); + } + else + { + log_message( LOGLVL_WARNING, "Warning: Couldn't write PID to `%s'", global.conf->pidfile ); + } + return( 0 ); } @@ -159,14 +151,14 @@ gboolean bitlbee_io_current_client_read( GIOChannel *source, GIOCondition condit if( condition & G_IO_ERR || condition & G_IO_HUP ) { - irc_free( irc ); + irc_abort( irc, 1, "Read error" ); return FALSE; } st = read( irc->fd, line, sizeof( line ) - 1 ); if( st == 0 ) { - irc_free( irc ); + irc_abort( irc, 1, "Connection reset by peer" ); return FALSE; } else if( st < 0 ) @@ -177,7 +169,7 @@ gboolean bitlbee_io_current_client_read( GIOChannel *source, GIOCondition condit } else { - irc_free( irc ); + irc_abort( irc, 1, "Read error: %s", strerror( errno ) ); return FALSE; } } @@ -193,18 +185,19 @@ gboolean bitlbee_io_current_client_read( GIOChannel *source, GIOCondition condit strcpy( ( irc->readbuffer + strlen( irc->readbuffer ) ), line ); } - if( !irc_process( irc ) ) + irc_process( irc ); + + /* Normally, irc_process() shouldn't call irc_free() but irc_abort(). Just in case: */ + if( !g_slist_find( irc_connection_list, irc ) ) { - log_message( LOGLVL_INFO, "Destroying connection with fd %d.", irc->fd ); - irc_free( irc ); + log_message( LOGLVL_WARNING, "Abnormal termination of connection with fd %d.", irc->fd ); 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 ); + irc_abort( irc, 0, "Maximum line length exceeded" ); return FALSE; } @@ -223,25 +216,25 @@ gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condi size = strlen( irc->sendbuffer ); st = write( irc->fd, irc->sendbuffer, size ); - if( st <= 0 ) + if( st == 0 || ( st < 0 && !sockerr_again() ) ) { - if( sockerr_again() ) - { - return TRUE; - } - else - { - irc_free( irc ); - return FALSE; - } + irc_abort( irc, 1, "Write error: %s", strerror( errno ) ); + return FALSE; + } + else if( st < 0 ) /* && sockerr_again() */ + { + return TRUE; } if( st == size ) { g_free( irc->sendbuffer ); irc->sendbuffer = NULL; - irc->w_watch_source_id = 0; + + if( irc->status == USTATUS_SHUTDOWN ) + irc_free( irc ); + return( FALSE ); } else @@ -254,6 +247,79 @@ gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condi } } +gboolean bitlbee_io_new_client( GIOChannel *source, GIOCondition condition, gpointer data ) +{ + size_t size = sizeof( struct sockaddr_in ); + struct sockaddr_in conn_info; + int new_socket = accept( global.listen_socket, (struct sockaddr *) &conn_info, &size ); + pid_t client_pid = 0; + + if( new_socket == -1 ) + { + log_message( LOGLVL_WARNING, "Could not accept new connection: %s", strerror( errno ) ); + return TRUE; + } + + if( global.conf->runmode == RUNMODE_FORKDAEMON ) + { + int fds[2]; + + if( socketpair( AF_UNIX, SOCK_STREAM, 0, fds ) == -1 ) + { + log_message( LOGLVL_WARNING, "Could not create IPC socket for client: %s", strerror( errno ) ); + fds[0] = fds[1] = -1; + } + + sock_make_nonblocking( fds[0] ); + sock_make_nonblocking( fds[1] ); + + client_pid = fork(); + + if( client_pid > 0 && fds[0] != -1 ) + { + struct bitlbee_child *child; + + child = g_new0( struct bitlbee_child, 1 ); + child->pid = client_pid; + child->ipc_fd = fds[0]; + child->ipc_inpa = gaim_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child ); + child_list = g_slist_append( child_list, child ); + + log_message( LOGLVL_INFO, "Creating new subprocess with pid %d.", client_pid ); + + /* Close some things we don't need in the parent process. */ + close( new_socket ); + close( fds[1] ); + } + else if( client_pid == 0 ) + { + irc_t *irc; + + /* Close the listening socket, we're a client. */ + close( global.listen_socket ); + g_source_remove( global.listen_watch_source_id ); + + /* Make the connection. */ + irc = irc_new( new_socket ); + + /* We can store the IPC fd there now. */ + global.listen_socket = fds[1]; + global.listen_watch_source_id = gaim_input_add( fds[1], GAIM_INPUT_READ, ipc_child_read, irc ); + + close( fds[0] ); + + ipc_master_free_all(); + } + } + else + { + log_message( LOGLVL_INFO, "Creating new connection with fd %d.", new_socket ); + irc_new( new_socket ); + } + + return TRUE; +} + void bitlbee_shutdown( gpointer data ) { /* Try to save data for all active connections (if desired). */ @@ -108,7 +108,8 @@ extern char *CONF_FILE; #include "query.h" #include "sock.h" -typedef struct global_t { +typedef struct global { + /* In forked mode, child processes store the fd of the IPC socket here. */ int listen_socket; gint listen_watch_source_id; help_t *help; @@ -116,6 +117,7 @@ typedef struct global_t { GList *storage; /* The first backend in the list will be used for saving */ char *helpfile; GMainLoop *loop; + int restart; } global_t; int bitlbee_daemon_init( void ); @@ -124,8 +126,8 @@ int bitlbee_inetd_init( void ); gboolean bitlbee_io_current_client_read( GIOChannel *source, GIOCondition condition, gpointer data ); gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condition, gpointer data ); -int root_command_string( irc_t *irc, user_t *u, char *command, int flags ); -int root_command( irc_t *irc, char *command[] ); +void root_command_string( irc_t *irc, user_t *u, char *command, int flags ); +void root_command( irc_t *irc, char *command[] ); void bitlbee_shutdown( gpointer data ); double gettime( void ); G_MODULE_EXPORT void http_encode( char *s ); @@ -28,33 +28,21 @@ #include "bitlbee.h" -typedef struct command_t +typedef struct command { char *command; int required_parameters; - int (*execute)(irc_t *, char **args); + void (*execute)(irc_t *, char **args); + int flags; } command_t; -int cmd_account( irc_t *irc, char **cmd ); -int cmd_help( irc_t *irc, char **args); -int cmd_info( irc_t *irc, char **args); -int cmd_add( irc_t *irc, char **args) ; -int cmd_rename( irc_t *irc, char **args ); -int cmd_remove( irc_t *irc, char **args ); -int cmd_block( irc_t *irc, char **args ); -int cmd_allow( irc_t *irc, char **args ); -int cmd_save( irc_t *irc, char **args ); -int cmd_set( irc_t *irc, char **args ); -int cmd_yesno( irc_t *irc, char **args ); -int cmd_identify( irc_t *irc, char **args ); -int cmd_register( irc_t *irc, char **args ); -int cmd_drop( irc_t *irc, char **args ); -int cmd_blist( irc_t *irc, char **cmd ); -int cmd_nick( irc_t *irc, char **cmd ); -int cmd_qlist( irc_t *irc, char **cmd ); -int cmd_import_buddies( irc_t *irc, char **cmd ); -int cmd_dump( irc_t *irc, char **cmd ); - extern const command_t commands[]; +#define IRC_CMD_PRE_LOGIN 1 +#define IRC_CMD_LOGGED_IN 2 +#define IRC_CMD_OPER_ONLY 4 +#define IRC_CMD_TO_MASTER 8 + +#define IPC_CMD_TO_CHILDREN 1 + #endif @@ -31,6 +31,7 @@ #include "conf.h" #include "ini.h" #include "url.h" +#include "ipc.h" #include "protocols/proxy.h" @@ -60,9 +61,11 @@ conf_t *conf_load( int argc, char *argv[] ) conf->oper_pass = NULL; conf->configdir = g_strdup( CONFIG ); conf->plugindir = g_strdup( PLUGINDIR ); + conf->pidfile = g_strdup( "/var/run/bitlbee.pid" ); conf->motdfile = g_strdup( ETCDIR "/motd.txt" ); conf->ping_interval = 180; conf->ping_timeout = 300; + proxytype = 0; i = conf_loadini( conf, CONF_FILE ); if( i == 0 ) @@ -75,7 +78,8 @@ conf_t *conf_load( int argc, char *argv[] ) fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", CONF_FILE ); } - while( ( opt = getopt( argc, argv, "i:p:nvIDFc:d:h" ) ) >= 0 ) + while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hR:" ) ) >= 0 ) + /* ^^^^ Just to make sure we skip this step from the REHASH handler. */ { if( opt == 'i' ) { @@ -90,16 +94,21 @@ conf_t *conf_load( int argc, char *argv[] ) } conf->port = i; } + else if( opt == 'p' ) + { + g_free( conf->pidfile ); + conf->pidfile = g_strdup( optarg ); + } else if( opt == 'n' ) - conf->nofork=1; + conf->nofork = 1; else if( opt == 'v' ) - conf->verbose=1; + conf->verbose = 1; else if( opt == 'I' ) - conf->runmode=RUNMODE_INETD; + conf->runmode = RUNMODE_INETD; else if( opt == 'D' ) - conf->runmode=RUNMODE_DAEMON; + conf->runmode = RUNMODE_DAEMON; else if( opt == 'F' ) - conf->runmode=RUNMODE_FORKDAEMON; + conf->runmode = RUNMODE_FORKDAEMON; else if( opt == 'c' ) { if( strcmp( CONF_FILE, optarg ) != 0 ) @@ -107,6 +116,10 @@ conf_t *conf_load( int argc, char *argv[] ) g_free( CONF_FILE ); 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 ) ); } } @@ -125,6 +138,7 @@ conf_t *conf_load( int argc, char *argv[] ) " -I Classic/InetD mode. (Default)\n" " -D Daemon mode. (Still EXPERIMENTAL!)\n" " -F Forking daemon. (one process per client)\n" + " -P Specify PID-file (not for inetd mode)\n" " -i Specify the interface (by IP address) to listen on.\n" " (Default: 0.0.0.0 (any interface))\n" " -p Port number to listen on. (Default: 6667)\n" @@ -135,6 +149,14 @@ conf_t *conf_load( int argc, char *argv[] ) " -h Show this help page.\n" ); return( NULL ); } + else if( opt == 'R' ) + { + /* We can't load the statefile yet (and should make very sure we do this + only once), so set the filename here and load the state information + when initializing ForkDaemon. (This option only makes sense in that + mode anyway!) */ + ipc_master_set_statefile( optarg ); + } } if( conf->configdir[strlen(conf->configdir)-1] != '/' ) @@ -169,6 +191,11 @@ static int conf_loadini( conf_t *conf, char *file ) else conf->runmode = RUNMODE_INETD; } + else if( g_strcasecmp( ini->key, "pidfile" ) == 0 ) + { + g_free( conf->pidfile ); + conf->pidfile = g_strdup( ini->value ); + } else if( g_strcasecmp( ini->key, "daemoninterface" ) == 0 ) { conf->iface = g_strdup( ini->value ); @@ -42,6 +42,7 @@ typedef struct conf char *hostname; char *configdir; char *plugindir; + char *pidfile; char *motdfile; char *primary_storage; char **migrate_storage; @@ -13,6 +13,8 @@ etcdir='$prefix/etc/bitlbee/' mandir='$prefix/share/man/' datadir='$prefix/share/bitlbee/' config='/var/lib/bitlbee/' +pidfile='/var/run/bitlbee.pid' +ipcsocket='/var/run/bitlbee' plugindir='$prefix/lib/bitlbee' msn=1 @@ -45,7 +47,9 @@ Option Description Default --mandir=... $mandir --datadir=... $datadir --plugindir=... $plugindir +--pidfile=... $pidfile --config=... $config +--ipcsocket=... $ipcsocket --msn=0/1 Disable/enable MSN part $msn --jabber=0/1 Disable/enable Jabber part $jabber @@ -73,6 +77,8 @@ mandir=`eval echo "$mandir/" | sed 's/\/\{1,\}/\//g'` datadir=`eval echo "$datadir/" | sed 's/\/\{1,\}/\//g'` config=`eval echo "$config/" | sed 's/\/\{1,\}/\//g'` plugindir=`eval echo "$plugindir/" | sed 's/\/\{1,\}/\//g'` +pidfile=`eval echo "$pidfile" | sed 's/\/\{1,\}/\//g'` +ipcsocket=`eval echo "$ipcsocket" | sed 's/\/\{1,\}/\//g'` cat<<EOF>Makefile.settings ## BitlBee settings, generated by configure @@ -83,6 +89,7 @@ MANDIR=$mandir DATADIR=$datadir PLUGINDIR=$plugindir CONFIG=$config +IPCSOCKET=$ipcsocket ARCH=$arch CPU=$cpu @@ -103,6 +110,8 @@ cat<<EOF>config.h #define ETCDIR "$etcdir" #define VARDIR "$datadir" #define PLUGINDIR "$plugindir" +#define PIDFILE "$pidfile" +#define IPCSOCKET "$ipcsocket" #define ARCH "$arch" #define CPU "$cpu" EOF @@ -49,7 +49,7 @@ BitlBee's only real dependency is GLib. This is available on virtually every platform. Any recent version of GLib (including 1.x versions) will work. These days, MSN Messenger clients have to connect to the MS Passport servers -through HTTPS. BitlBee can use serveral SSL libraries for this: GnuTLS, NSS +through HTTPS. BitlBee can use several SSL libraries for this: GnuTLS, NSS (which comes with Mozilla) and OpenSSL. OpenSSL is not GPL-compatible in some situations, so using GnuTLS or NSS is preferred. However, especially on *BSD, OpenSSL can be considered part of the operating system, which eliminates the @@ -0,0 +1,576 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2006 Wilmer van der Gaast and others * + \********************************************************************/ + +/* IPC - communication between BitlBee processes */ + +/* + 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 "ipc.h" +#include "commands.h" +#ifndef _WIN32 +#include <sys/un.h> +#endif + +GSList *child_list = NULL; +static char *statefile = NULL; + +static void ipc_master_cmd_client( irc_t *data, char **cmd ) +{ + /* Normally data points at an irc_t block, but for the IPC master + this is different. We think this scary cast is better than + creating a new command_t structure, just to make the compiler + happy. */ + struct bitlbee_child *child = (void*) data; + + if( child && cmd[1] ) + { + child->host = g_strdup( cmd[1] ); + child->nick = g_strdup( cmd[2] ); + child->realname = g_strdup( cmd[3] ); + } + + if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 ) + ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n", + child ? child->pid : -1, cmd[2], cmd[1], cmd[3] ); +} + +static void ipc_master_cmd_die( irc_t *data, char **cmd ) +{ + if( global.conf->runmode == RUNMODE_FORKDAEMON ) + ipc_to_children_str( "DIE\r\n" ); + + bitlbee_shutdown( NULL ); +} + +void ipc_master_cmd_rehash( irc_t *data, char **cmd ) +{ + runmode_t oldmode; + + oldmode = global.conf->runmode; + + g_free( global.conf ); + global.conf = conf_load( 0, NULL ); + + if( global.conf->runmode != oldmode ) + { + log_message( LOGLVL_WARNING, "Can't change RunMode setting at runtime, restoring original setting" ); + global.conf->runmode = oldmode; + } + + if( global.conf->runmode == RUNMODE_FORKDAEMON ) + ipc_to_children( cmd ); +} + +void ipc_master_cmd_restart( irc_t *data, char **cmd ) +{ + struct bitlbee_child *child = (void*) data; + + if( global.conf->runmode != RUNMODE_FORKDAEMON ) + { + /* Tell child that this is unsupported. */ + return; + } + + global.restart = -1; + bitlbee_shutdown( NULL ); +} + +static const command_t ipc_master_commands[] = { + { "client", 3, ipc_master_cmd_client, 0 }, + { "hello", 0, ipc_master_cmd_client, 0 }, + { "die", 0, ipc_master_cmd_die, 0 }, + { "wallops", 1, NULL, IPC_CMD_TO_CHILDREN }, + { "lilo", 1, NULL, IPC_CMD_TO_CHILDREN }, + { "opermsg", 1, NULL, IPC_CMD_TO_CHILDREN }, + { "rehash", 0, ipc_master_cmd_rehash, 0 }, + { "kill", 2, NULL, IPC_CMD_TO_CHILDREN }, + { "restart", 0, ipc_master_cmd_restart, 0 }, + { NULL } +}; + + +static void ipc_child_cmd_die( irc_t *irc, char **cmd ) +{ + irc_abort( irc, 0, "Shutdown requested by operator" ); +} + +static void ipc_child_cmd_wallops( irc_t *irc, char **cmd ) +{ + if( irc->status < USTATUS_LOGGED_IN ) + return; + + if( strchr( irc->umode, 'w' ) ) + irc_write( irc, ":%s WALLOPS :%s", irc->myhost, cmd[1] ); +} + +static void ipc_child_cmd_lilo( irc_t *irc, char **cmd ) +{ + if( irc->status < USTATUS_LOGGED_IN ) + return; + + if( strchr( irc->umode, 's' ) ) + irc_write( irc, ":%s NOTICE %s :%s", irc->myhost, irc->nick, cmd[1] ); +} + +static void ipc_child_cmd_opermsg( irc_t *irc, char **cmd ) +{ + if( irc->status < USTATUS_LOGGED_IN ) + return; + + if( strchr( irc->umode, 'o' ) ) + irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->myhost, irc->nick, cmd[1] ); +} + +static void ipc_child_cmd_rehash( irc_t *irc, char **cmd ) +{ + runmode_t oldmode; + + oldmode = global.conf->runmode; + + g_free( global.conf ); + global.conf = conf_load( 0, NULL ); + + global.conf->runmode = oldmode; +} + +static void ipc_child_cmd_kill( irc_t *irc, char **cmd ) +{ + if( irc->status < USTATUS_LOGGED_IN ) + return; + + if( nick_cmp( cmd[1], irc->nick ) != 0 ) + return; /* It's not for us. */ + + irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->mynick, irc->mynick, irc->myhost, irc->nick, cmd[2] ); + irc_abort( irc, 0, "Killed by operator: %s", cmd[2] ); +} + +static void ipc_child_cmd_hello( irc_t *irc, char **cmd ) +{ + if( irc->status < USTATUS_LOGGED_IN ) + ipc_to_master_str( "HELLO\r\n" ); + else + ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->host, irc->nick, irc->realname ); +} + +static const command_t ipc_child_commands[] = { + { "die", 0, ipc_child_cmd_die, 0 }, + { "wallops", 1, ipc_child_cmd_wallops, 0 }, + { "lilo", 1, ipc_child_cmd_lilo, 0 }, + { "opermsg", 1, ipc_child_cmd_opermsg, 0 }, + { "rehash", 0, ipc_child_cmd_rehash, 0 }, + { "kill", 2, ipc_child_cmd_kill, 0 }, + { "hello", 0, ipc_child_cmd_hello, 0 }, + { NULL } +}; + + +static void ipc_command_exec( void *data, char **cmd, const command_t *commands ) +{ + int i, j; + + if( !cmd[0] ) + return; + + for( i = 0; commands[i].command; i ++ ) + if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 ) + { + /* There is no typo in this line: */ + for( j = 1; cmd[j]; j ++ ); j --; + + if( j < commands[i].required_parameters ) + break; + + if( commands[i].flags & IPC_CMD_TO_CHILDREN ) + ipc_to_children( cmd ); + else + commands[i].execute( data, cmd ); + + break; + } +} + +static char *ipc_readline( int fd ) +{ + char *buf, *eol; + int size; + + buf = g_new0( char, 513 ); + + /* Because this is internal communication, it should be pretty safe + to just peek at the message, find its length (by searching for the + end-of-line) and then just read that message. With internal + sockets and limites message length, messages should always be + complete. Saves us quite a lot of code and buffering. */ + size = recv( fd, buf, 512, MSG_PEEK ); + if( size == 0 || ( size < 0 && !sockerr_again() ) ) + return NULL; + else if( size < 0 ) /* && sockerr_again() */ + return( g_strdup( "" ) ); + else + buf[size] = 0; + + eol = strstr( buf, "\r\n" ); + if( eol == NULL ) + return NULL; + else + size = eol - buf + 2; + + g_free( buf ); + buf = g_new0( char, size + 1 ); + + if( recv( fd, buf, size, 0 ) != size ) + return NULL; + else + buf[size-2] = 0; + + return buf; +} + +void ipc_master_read( gpointer data, gint source, GaimInputCondition cond ) +{ + char *buf, **cmd; + + if( ( buf = ipc_readline( source ) ) ) + { + cmd = irc_parse_line( buf ); + if( cmd ) + ipc_command_exec( data, cmd, ipc_master_commands ); + } + else + { + GSList *l; + struct bitlbee_child *c; + + for( l = child_list; l; l = l->next ) + { + c = l->data; + if( c->ipc_fd == source ) + { + ipc_master_free_one( c ); + child_list = g_slist_remove( child_list, c ); + break; + } + } + } +} + +void ipc_child_read( gpointer data, gint source, GaimInputCondition cond ) +{ + char *buf, **cmd; + + if( ( buf = ipc_readline( source ) ) ) + { + cmd = irc_parse_line( buf ); + if( cmd ) + ipc_command_exec( data, cmd, ipc_child_commands ); + } + else + { + gaim_input_remove( global.listen_watch_source_id ); + close( global.listen_socket ); + + global.listen_socket = -1; + } +} + +void ipc_to_master( char **cmd ) +{ + if( global.conf->runmode == RUNMODE_FORKDAEMON ) + { + char *s = irc_build_line( cmd ); + ipc_to_master_str( "%s", s ); + g_free( s ); + } + else if( global.conf->runmode == RUNMODE_DAEMON ) + { + ipc_command_exec( NULL, cmd, ipc_master_commands ); + } +} + +void ipc_to_master_str( char *format, ... ) +{ + char *msg_buf; + va_list params; + + va_start( params, format ); + msg_buf = g_strdup_vprintf( format, params ); + va_end( params ); + + if( strlen( msg_buf ) > 512 ) + { + /* Don't send it, it's too long... */ + } + else if( global.conf->runmode == RUNMODE_FORKDAEMON ) + { + write( global.listen_socket, msg_buf, strlen( msg_buf ) ); + } + else if( global.conf->runmode == RUNMODE_DAEMON ) + { + char **cmd, *s; + + if( ( s = strchr( msg_buf, '\r' ) ) ) + *s = 0; + + cmd = irc_parse_line( msg_buf ); + ipc_command_exec( NULL, cmd, ipc_master_commands ); + g_free( cmd ); + } + + g_free( msg_buf ); +} + +void ipc_to_children( char **cmd ) +{ + if( global.conf->runmode == RUNMODE_FORKDAEMON ) + { + char *msg_buf = irc_build_line( cmd ); + ipc_to_children_str( "%s", msg_buf ); + g_free( msg_buf ); + } + else if( global.conf->runmode == RUNMODE_DAEMON ) + { + GSList *l; + + for( l = irc_connection_list; l; l = l->next ) + ipc_command_exec( l->data, cmd, ipc_child_commands ); + } +} + +void ipc_to_children_str( char *format, ... ) +{ + char *msg_buf; + va_list params; + + va_start( params, format ); + msg_buf = g_strdup_vprintf( format, params ); + va_end( params ); + + if( strlen( msg_buf ) > 512 ) + { + /* Don't send it, it's too long... */ + } + else if( global.conf->runmode == RUNMODE_FORKDAEMON ) + { + int msg_len = strlen( msg_buf ); + GSList *l; + + for( l = child_list; l; l = l->next ) + { + struct bitlbee_child *c = l->data; + write( c->ipc_fd, msg_buf, msg_len ); + } + } + else if( global.conf->runmode == RUNMODE_DAEMON ) + { + char **cmd, *s; + + if( ( s = strchr( msg_buf, '\r' ) ) ) + *s = 0; + + cmd = irc_parse_line( msg_buf ); + ipc_to_children( cmd ); + g_free( cmd ); + } + + g_free( msg_buf ); +} + +void ipc_master_free_one( struct bitlbee_child *c ) +{ + gaim_input_remove( c->ipc_inpa ); + closesocket( c->ipc_fd ); + + g_free( c->host ); + g_free( c->nick ); + g_free( c->realname ); + g_free( c ); +} + +void ipc_master_free_all() +{ + GSList *l; + + for( l = child_list; l; l = l->next ) + ipc_master_free_one( l->data ); + + g_slist_free( child_list ); + child_list = NULL; +} + +char *ipc_master_save_state() +{ + char *fn = g_strdup( "/tmp/bee-restart.XXXXXX" ); + int fd = mkstemp( fn ); + GSList *l; + FILE *fp; + int i; + + if( fd == -1 ) + { + log_message( LOGLVL_ERROR, "Could not create temporary file: %s", strerror( errno ) ); + g_free( fn ); + return NULL; + } + + /* This is more convenient now. */ + fp = fdopen( fd, "w" ); + + for( l = child_list, i = 0; l; l = l->next ) + i ++; + + /* Number of client processes. */ + fprintf( fp, "%d\n", i ); + + for( l = child_list; l; l = l->next ) + fprintf( fp, "%d %d\n", ((struct bitlbee_child*)l->data)->pid, + ((struct bitlbee_child*)l->data)->ipc_fd ); + + if( fclose( fp ) == 0 ) + { + return fn; + } + else + { + unlink( fn ); + g_free( fn ); + return NULL; + } +} + +void ipc_master_set_statefile( char *fn ) +{ + statefile = g_strdup( fn ); +} + + +static gboolean new_ipc_client (GIOChannel *gio, GIOCondition cond, gpointer data) +{ + struct bitlbee_child *child = g_new0( struct bitlbee_child, 1 ); + int serversock; + + serversock = g_io_channel_unix_get_fd(gio); + + child->ipc_fd = accept(serversock, NULL, 0); + + if (child->ipc_fd == -1) { + log_message( LOGLVL_WARNING, "Unable to accept connection on UNIX domain socket: %s", strerror(errno) ); + return TRUE; + } + + child->ipc_inpa = gaim_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child ); + + child_list = g_slist_append( child_list, child ); + + return TRUE; +} + +#ifndef _WIN32 +int ipc_master_listen_socket() +{ + struct sockaddr_un un_addr; + int serversock; + GIOChannel *gio; + + /* Clean up old socket files that were hanging around.. */ + if (unlink(IPCSOCKET) == -1 && errno != ENOENT) { + log_message( LOGLVL_ERROR, "Could not remove old IPC socket at %s: %s", IPCSOCKET, strerror(errno) ); + return 0; + } + + un_addr.sun_family = AF_UNIX; + strcpy(un_addr.sun_path, IPCSOCKET); + + serversock = socket(AF_UNIX, SOCK_STREAM, PF_UNIX); + + if (serversock == -1) { + log_message( LOGLVL_WARNING, "Unable to create UNIX socket: %s", strerror(errno) ); + return 0; + } + + if (bind(serversock, &un_addr, sizeof(un_addr)) == -1) { + log_message( LOGLVL_WARNING, "Unable to bind UNIX socket to %s: %s", IPCSOCKET, strerror(errno) ); + return 0; + } + + if (listen(serversock, 5) == -1) { + log_message( LOGLVL_WARNING, "Unable to listen on UNIX socket: %s", strerror(errno) ); + return 0; + } + + gio = g_io_channel_unix_new(serversock); + + if (gio == NULL) { + log_message( LOGLVL_WARNING, "Unable to create IO channel for unix socket" ); + return 0; + } + + g_io_add_watch(gio, G_IO_IN, new_ipc_client, NULL); + return 1; +} +#else + /* FIXME: Open named pipe \\.\BITLBEE */ +#endif + +int ipc_master_load_state() +{ + struct bitlbee_child *child; + FILE *fp; + int i, n; + + if( statefile == NULL ) + return 0; + fp = fopen( statefile, "r" ); + unlink( statefile ); /* Why do it later? :-) */ + if( fp == NULL ) + return 0; + + if( fscanf( fp, "%d", &n ) != 1 ) + { + log_message( LOGLVL_WARNING, "Could not import state information for child processes." ); + fclose( fp ); + return 0; + } + + log_message( LOGLVL_INFO, "Importing information for %d child processes.", n ); + for( i = 0; i < n; i ++ ) + { + child = g_new0( struct bitlbee_child, 1 ); + + if( fscanf( fp, "%d %d", &child->pid, &child->ipc_fd ) != 2 ) + { + log_message( LOGLVL_WARNING, "Unexpected end of file: Only processed %d clients.", i ); + g_free( child ); + fclose( fp ); + return 0; + } + child->ipc_inpa = gaim_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child ); + + child_list = g_slist_append( child_list, child ); + } + + ipc_to_children_str( "HELLO\r\n" ); + ipc_to_children_str( "OPERMSG :New BitlBee master process started (version " BITLBEE_VERSION ")\r\n" ); + + return 1; +} @@ -0,0 +1,61 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* IPC - communication between BitlBee processes */ + +/* + 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" + + +struct bitlbee_child +{ + pid_t pid; + int ipc_fd; + gint ipc_inpa; + + char *host; + char *nick; + char *realname; +}; + + +void ipc_master_read( gpointer data, gint source, GaimInputCondition cond ); +void ipc_child_read( gpointer data, gint source, GaimInputCondition cond ); + +void ipc_master_free_one( struct bitlbee_child *child ); +void ipc_master_free_all(); + +void ipc_to_master( char **cmd ); +void ipc_to_master_str( char *format, ... ); +void ipc_to_children( char **cmd ); +void ipc_to_children_str( char *format, ... ); + +/* We need this function in inetd mode, so let's just make it non-static. */ +void ipc_master_cmd_rehash( irc_t *data, char **cmd ); + +char *ipc_master_save_state(); +void ipc_master_set_statefile( char *fn ); +int ipc_master_load_state(); +int ipc_master_listen_socket(); + +extern GSList *child_list; @@ -26,6 +26,7 @@ #define BITLBEE_CORE #include "bitlbee.h" #include "crypting.h" +#include "ipc.h" static gboolean irc_userping( gpointer _irc ); @@ -150,6 +151,53 @@ irc_t *irc_new( int fd ) return( irc ); } +/* immed=1 makes this function pretty much equal to irc_free(), except that + this one will "log". In case the connection is already broken and we + shouldn't try to write to it. */ +void irc_abort( irc_t *irc, int immed, char *format, ... ) +{ + if( format != NULL ) + { + va_list params; + char *reason; + + va_start( params, format ); + reason = g_strdup_vprintf( format, params ); + va_end( params ); + + if( !immed ) + irc_write( irc, "ERROR :Closing link: %s", reason ); + + ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", + irc->nick ? irc->nick : "(NONE)", irc->host, reason ); + + g_free( reason ); + } + else + { + if( !immed ) + irc_write( irc, "ERROR :Closing link" ); + + ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n", + irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" ); + } + + irc->status = USTATUS_SHUTDOWN; + if( irc->sendbuffer && !immed ) + { + /* We won't read from this socket anymore. Instead, we'll connect a timer + to it that should shut down the connection in a second, just in case + bitlbee_.._write doesn't do it first. */ + + g_source_remove( irc->r_watch_source_id ); + irc->r_watch_source_id = g_timeout_add_full( G_PRIORITY_HIGH, 1000, (GSourceFunc) irc_free, irc, NULL ); + } + else + { + irc_free( irc ); + } +} + static gboolean irc_free_userhash( gpointer key, gpointer value, gpointer data ) { g_free( key ); @@ -172,6 +220,8 @@ void irc_free(irc_t * irc) if( storage_save( irc, TRUE ) != STORAGE_OK ) irc_usermsg( irc, "Error while saving settings!" ); + closesocket( irc->fd ); + if( irc->ping_source_id > 0 ) g_source_remove( irc->ping_source_id ); g_source_remove( irc->r_watch_source_id ); @@ -290,33 +340,48 @@ void irc_setpass (irc_t *irc, const char *pass) } } -int irc_process( irc_t *irc ) +void irc_process( irc_t *irc ) { - char **lines, *temp; + char **lines, *temp, **cmd; int i; - if( irc->readbuffer != NULL ) { - lines = irc_tokenize(irc->readbuffer ); - for( i = 0; *lines[i] != '\0'; i++ ) { - if( lines[i+1] == NULL ) { + if( irc->readbuffer != NULL ) + { + lines = irc_tokenize( irc->readbuffer ); + + for( i = 0; *lines[i] != '\0'; i ++ ) + { + if( lines[i+1] == NULL ) + { temp = g_strdup( lines[i] ); g_free( irc->readbuffer ); irc->readbuffer = temp; - i++; + i ++; break; } - if (!irc_process_line(irc, lines[i])) { + + if( ( cmd = irc_parse_line( lines[i] ) ) == NULL ) + continue; + irc_exec( irc, cmd ); + + g_free( cmd ); + + /* Shouldn't really happen, but just in case... */ + if( !g_slist_find( irc_connection_list, irc ) ) + { g_free( lines ); - return 0; + return; } } - if(lines[i]!=NULL) { - g_free(irc->readbuffer); - irc->readbuffer=NULL; + + if( lines[i] != NULL ) + { + g_free( irc->readbuffer ); + irc->readbuffer = NULL; } + g_free( lines ); } - return 1; } char **irc_tokenize( char *buffer ) @@ -325,562 +390,128 @@ char **irc_tokenize( char *buffer ) char **lines; /* Count the number of elements we're gonna need. */ - for(i=0, j=1; buffer[i]!='\0'; i++ ) { - if(buffer[i]=='\n' ) - if(buffer[i+1]!='\r' && buffer[i+1]!='\n') - j++; + for( i = 0, j = 1; buffer[i] != '\0'; i ++ ) + { + if( buffer[i] == '\n' ) + if( buffer[i+1] != '\r' && buffer[i+1] != '\n' ) + j ++; } /* Allocate j+1 elements. */ - lines=g_new (char *, j+1); + lines = g_new( char *, j + 1 ); /* NULL terminate our list. */ - lines[j]=NULL; + lines[j] = NULL; - lines[0]=buffer; + lines[0] = buffer; /* Split the buffer in several strings, using \r\n as our seperator, where \r is optional. * Although this is not in the RFC, some braindead ircds (newnet's) use this, so some clients might too. */ - for( i=0, j=0; buffer[i]!='\0'; i++) { - if(buffer[i]=='\n') { - buffer[i]='\0'; - - /* We dont want to read 1 byte before our buffer - * and (in rare cases) generate a SIGSEGV. - */ - if(i!=0) - if(buffer[i-1]=='\r') - buffer[i-1]='\0'; - if(buffer[i+1]!='\r'&&buffer[i+1]!='\n') - lines[++j]=buffer+i+1; + for( i = 0, j = 0; buffer[i] != '\0'; i ++) + { + if( buffer[i] == '\n' ) + { + buffer[i] = '\0'; + + if( i > 0 && buffer[i-1] == '\r' ) + buffer[i-1] = '\0'; + if( buffer[i+1] != '\r' && buffer[i+1] != '\n' ) + lines[++j] = buffer + i + 1; } } - - return(lines); + + return( lines ); } -int irc_process_line( irc_t *irc, char *line ) +char **irc_parse_line( char *line ) { int i, j; char **cmd; /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */ - if(line[0]==':') { - for(i=0; line[i]!=32; i++); - line=line+i; + if( line[0] == ':' ) + { + for( i = 0; line[i] != ' '; i ++ ); + line = line + i; } - for(i=0; line[i]==32; i++); - line=line+i; - + for( i = 0; line[i] == ' '; i ++ ); + line = line + i; + /* If we're already at the end of the line, return. If not, we're going to need at least one element. */ - if(line[0]=='\0') - return 1; - else - j=1; - - /* Count the number of char **cmd elements we're going to need. */ - for(i=0; line[i]!='\0'; i++) { - if((line[i]==32) && (line[i+1]!=32) && (line[i+1]!='\0') && (line[i+1]!=':')) - j++; - else if((line[i]==':') && (line[i+1]!='\0') && (line[i-1]==32)) { - j++; - break; - } + if( line[0] == '\0') + return NULL; + + /* Count the number of char **cmd elements we're going to need. */ + j = 1; + for( i = 0; line[i] != '\0'; i ++ ) + { + if( line[i] == ' ' ) + { + j ++; + if( line[i+1] == ':' ) + break; + } } /* Allocate the space we need. */ - cmd=g_new(char *, j+1); - cmd[j]=NULL; + cmd = g_new( char *, j + 1 ); + cmd[j] = NULL; /* Do the actual line splitting, format is: * Input: "PRIVMSG #bitlbee :foo bar" * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL */ - cmd[0]=line; - for(i=0, j=0; line[i]!='\0'; i++) { - if((line[i]==32)) { - line[i]='\0'; - if((line[i+1]!=32) && (line[i+1]!='\0') && (line[i+1]!=':')) - cmd[++j]=line+i+1; - } - else if((line[i]==':') && (line[i+1]!='\0') && (line[i-1]=='\0')) { - cmd[++j]=line+i+1; - break; - } - } - - i=irc_exec(irc, cmd); - g_free(cmd); - - return(i); -} - -int irc_exec( irc_t *irc, char **cmd ) -{ - int i; - - if( (global.conf)->authmode == AUTHMODE_CLOSED && irc->status < USTATUS_AUTHORIZED ) + cmd[0] = line; + for( i = 0, j = 0; line[i] != '\0'; i ++ ) { - if( g_strcasecmp( cmd[0], "PASS" ) == 0 ) + if( line[i] == ' ' ) { - if( !cmd[1] ) - { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - else if( strcmp( cmd[1], (global.conf)->auth_pass ) == 0 ) - { - irc->status = USTATUS_AUTHORIZED; - } - else + line[i] = '\0'; + cmd[++j] = line + i + 1; + + if( line[i+1] == ':' ) { - irc_reply( irc, 464, ":Nope, maybe you should try it again..." ); + cmd[j] ++; + break; } } - else - { - irc_reply( irc, 464, ":Uhh, fine, but I want the password first." ); - } - - return( 1 ); } - if( g_strcasecmp( cmd[0], "USER" ) == 0 ) - { - if( !( cmd[1] && cmd[2] && cmd[3] && cmd[4] ) ) - { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - else if( irc->user ) - { - irc_reply( irc, 462, ":You can't change your nick/userinfo" ); - } - else - { - irc->user = g_strdup( cmd[1] ); - irc->realname = g_strdup( cmd[4] ); - if( irc->nick ) irc_login( irc ); - } - return( 1 ); - } - else if( g_strcasecmp( cmd[0], "NICK" ) == 0 ) - { - if( !cmd[1] ) - { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - else if( irc->nick ) - { - irc_reply( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" ); - } - /* This is not clean, but for now it'll have to be like this... */ - else if( ( nick_cmp( cmd[1], irc->mynick ) == 0 ) || ( nick_cmp( cmd[1], NS_NICK ) == 0 ) ) - { - irc_reply( irc, 433, ":This nick is already in use" ); - } - else if( !nick_ok( cmd[1] ) ) - { - /* [SH] Invalid characters. */ - irc_reply( irc, 432, ":This nick contains invalid characters" ); - } - else - { - irc->nick = g_strdup( cmd[1] ); - if( irc->user ) irc_login( irc ); - } - return( 1 ); - } - else if( g_strcasecmp( cmd[0], "QUIT" ) == 0 ) - { - irc_write( irc, "ERROR :%s%s", cmd[1]?"Quit: ":"", cmd[1]?cmd[1]:"Client Quit" ); - g_io_channel_close( irc->io_channel ); - return( 0 ); - } + return cmd; +} + +char *irc_build_line( char **cmd ) +{ + int i, len; + char *s; - if( !irc->user || !irc->nick ) - { - irc_reply( irc, 451, ":Register first" ); - return( 1 ); - } + if( cmd[0] == NULL ) + return NULL; - if( g_strcasecmp( cmd[0], "PING" ) == 0 ) - { - irc_write( irc, ":%s PONG %s :%s", irc->myhost, irc->myhost, cmd[1]?cmd[1]:irc->myhost ); - } - else if( g_strcasecmp( cmd[0], "OPER" ) == 0 ) - { - if( !cmd[2] ) - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - else if( strcmp( cmd[2], global.conf->oper_pass ) == 0 ) - irc_umode_set( irc, "+o", 1 ); - // else - /* FIXME/TODO: Find out which reply to send now. */ - } - else if( g_strcasecmp( cmd[0], "MODE" ) == 0 ) - { - if( !cmd[1] ) - { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - else if( *cmd[1] == '#' || *cmd[1] == '&' ) - { - if( cmd[2] ) - { - if( *cmd[2] == '+' || *cmd[2] == '-' ) - irc_reply( irc, 477, "%s :Can't change channel modes", cmd[1] ); - else if( *cmd[2] == 'b' ) - irc_reply( irc, 368, "%s :No bans possible", cmd[1] ); - } - else - irc_reply( irc, 324, "%s +%s", cmd[1], CMODE ); - } - else - { - if( nick_cmp( cmd[1], irc->nick ) == 0 ) - { - if( cmd[2] ) - irc_umode_set( irc, cmd[2], 0 ); - } - else - irc_reply( irc, 502, ":Don't touch their modes" ); - } - } - else if( g_strcasecmp( cmd[0], "NAMES" ) == 0 ) - { - irc_names( irc, cmd[1]?cmd[1]:irc->channel ); - } - else if( g_strcasecmp( cmd[0], "PART" ) == 0 ) - { - struct conversation *c; - - if( !cmd[1] ) - { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - else if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) - { - user_t *u = user_find( irc, irc->nick ); - - /* Not allowed to leave control channel */ - irc_part( irc, u, irc->channel ); - irc_join( irc, u, irc->channel ); - } - else if( ( c = conv_findchannel( cmd[1] ) ) ) - { - user_t *u = user_find( irc, irc->nick ); - - irc_part( irc, u, c->channel ); - - if( c->gc && c->gc->prpl ) - { - c->joined = 0; - c->gc->prpl->chat_leave( c->gc, c->id ); - } - } - else - { - irc_reply( irc, 403, "%s :No such channel", cmd[1] ); - } - } - else if( g_strcasecmp( cmd[0], "JOIN" ) == 0 ) - { - if( !cmd[1] ) - { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - else if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) - ; /* Dude, you're already there... - RFC doesn't have any reply for that though? */ - else if( cmd[1] ) - { - if( ( cmd[1][0] == '#' || cmd[1][0] == '&' ) && cmd[1][1] ) - { - user_t *u = user_find( irc, cmd[1] + 1 ); - - if( u && u->gc && u->gc->prpl && u->gc->prpl->chat_open ) - { - irc_reply( irc, 403, "%s :Initializing groupchat in a different channel", cmd[1] ); - - if( !u->gc->prpl->chat_open( u->gc, u->handle ) ) - { - irc_usermsg( irc, "Could not open a groupchat with %s, maybe you don't have a connection to him/her yet?", u->nick ); - } - } - else - { - irc_reply( irc, 403, "%s :Groupchats are not possible with %s", cmd[1], cmd[1]+1 ); - } - } - else - { - irc_reply( irc, 403, "%s :No such channel", cmd[1] ); - } - } - } - else if( g_strcasecmp( cmd[0], "INVITE" ) == 0 ) - { - if( cmd[1] && cmd[2] ) - irc_invite( irc, cmd[1], cmd[2] ); - else - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - else if( g_strcasecmp( cmd[0], "PRIVMSG" ) == 0 || g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) - { - if( !cmd[1] ) - { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - else if ( !cmd[2] ) - { - irc_reply( irc, 412, ":No text to send" ); - } - else if ( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) - { - irc_write( irc, ":%s!%s@%s %s %s :%s", irc->nick, irc->user, irc->host, cmd[0], cmd[1], cmd[2] ); - } - else - { - if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) - { - unsigned int i; - char *t = set_getstr( irc, "default_target" ); - - if( g_strcasecmp( t, "last" ) == 0 && irc->last_target ) - cmd[1] = irc->last_target; - else if( g_strcasecmp( t, "root" ) == 0 ) - cmd[1] = irc->mynick; - - for( i = 0; i < strlen( cmd[2] ); i ++ ) - { - if( cmd[2][i] == ' ' ) break; - if( cmd[2][i] == ':' || cmd[2][i] == ',' ) - { - cmd[1] = cmd[2]; - cmd[2] += i; - *cmd[2] = 0; - while( *(++cmd[2]) == ' ' ); - break; - } - } - - irc->is_private = 0; - - if( cmd[1] != irc->last_target ) - { - if( irc->last_target ) - g_free( irc->last_target ); - irc->last_target = g_strdup( cmd[1] ); - } - } - else - { - irc->is_private = 1; - } - irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? IM_FLAG_AWAY : 0 ); - } - } - else if( g_strcasecmp( cmd[0], "WHO" ) == 0 ) - { - irc_who( irc, cmd[1] ); - } - else if( g_strcasecmp( cmd[0], "USERHOST" ) == 0 ) - { - user_t *u; - - if( !cmd[1] ) - { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - /* [TV] Usable USERHOST-implementation according to - RFC1459. Without this, mIRC shows an error - while connecting, and the used way of rejecting - breaks standards. - */ - - for( i = 1; cmd[i]; i ++ ) - if( ( u = user_find( irc, cmd[i] ) ) ) - { - if( u->online && u->away ) - irc_reply( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host ); - else - irc_reply( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host ); - } - } - else if( g_strcasecmp( cmd[0], "ISON" ) == 0 ) - { - user_t *u; - char buff[IRC_MAX_LINE]; - int lenleft; - - buff[0] = '\0'; - - /* [SH] Leave room for : and \0 */ - lenleft = IRC_MAX_LINE - 2; - - for( i = 1; cmd[i]; i ++ ) - { - if( ( u = user_find( irc, cmd[i] ) ) && u->online ) - { - /* [SH] Make sure we don't use too much buffer space. */ - lenleft -= strlen( u->nick ) + 1; - - if( lenleft < 0 ) - { - break; - } - - /* [SH] Add the nick to the buffer. Note - * that an extra space is always added. Even - * if it's the last nick in the list. Who - * cares? - */ - - strcat( buff, u->nick ); - strcat( buff, " " ); - } - } - - /* [WvG] Well, maybe someone cares, so why not remove it? */ - if( strlen( buff ) > 0 ) - buff[strlen(buff)-1] = '\0'; - - /* [SH] By the way, that really *was* WvG talking. */ - /* [WvG] Really? */ - /* [SH] Yeah... But *this* is WvG talking too. ;-P */ - /* [WvG] *sigh* */ - - irc_reply( irc, 303, ":%s", buff ); - } - else if( g_strcasecmp( cmd[0], "WATCH" ) == 0 ) - { - /* Obviously we could also mark a user structure as being - watched, but what if the WATCH command is sent right - after connecting? The user won't exist yet then... */ - for( i = 1; cmd[i]; i ++ ) - { - char *nick; - user_t *u; - - if( !cmd[i][0] || !cmd[i][1] ) - break; - - nick = g_strdup( cmd[i] + 1 ); - nick_lc( nick ); - - u = user_find( irc, nick ); - - if( cmd[i][0] == '+' ) - { - if( !g_hash_table_lookup( irc->watches, nick ) ) - g_hash_table_insert( irc->watches, nick, nick ); - - if( u && u->online ) - irc_reply( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "is online" ); - else - irc_reply( irc, 605, "%s %s %s %d :%s", nick, "*", "*", time( NULL ), "is offline" ); - } - else if( cmd[i][0] == '-' ) - { - gpointer okey, ovalue; - - if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) ) - { - g_free( okey ); - g_hash_table_remove( irc->watches, okey ); - - irc_reply( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" ); - } - } - } - } - else if( g_strcasecmp( cmd[0], "TOPIC" ) == 0 ) - { - if( cmd[1] && cmd[2] ) - irc_reply( irc, 482, "%s :Cannot change topic", cmd[1] ); - else if( cmd[1] ) - irc_topic( irc, cmd[1] ); - else - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - else if( g_strcasecmp( cmd[0], "AWAY" ) == 0 ) - { - irc_away( irc, cmd[1] ); - } - else if( g_strcasecmp( cmd[0], "WHOIS" ) == 0 ) - { - if( cmd[1] ) - { - irc_whois( irc, cmd[1] ); - } - else - { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - } - else if( g_strcasecmp( cmd[0], "WHOWAS" ) == 0 ) - { - /* For some reason irssi tries a whowas when whois fails. We can - ignore this, but then the user never gets a "user not found" - message from irssi which is a bit annoying. So just respond - with not-found and irssi users will get better error messages */ - - if( cmd[1] ) - { - irc_reply( irc, 406, "%s :Nick does not exist", cmd[1] ); - irc_reply( irc, 369, "%s :End of WHOWAS", cmd[1] ); - } - else - { - irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); - } - } - else if( ( g_strcasecmp( cmd[0], "NICKSERV" ) == 0 ) || ( g_strcasecmp( cmd[0], "NS" ) == 0 ) ) - { - /* [SH] This aliases the NickServ command to PRIVMSG root */ - /* [TV] This aliases the NS command to PRIVMSG root as well */ - root_command( irc, cmd + 1 ); - } - else if( g_strcasecmp( cmd[0], "MOTD" ) == 0 ) - { - irc_motd( irc ); - } - else if( g_strcasecmp( cmd[0], "PONG" ) == 0 ) - { - /* We could check the value we get back from the user, but in - fact we don't care, we're just happy he's still alive. */ - irc->last_pong = gettime(); - irc->pinging = 0; - } - else if( g_strcasecmp( cmd[0], "COMPLETIONS" ) == 0 ) + len = 1; + for( i = 0; cmd[i]; i ++ ) + len += strlen( cmd[i] ) + 1; + + if( strchr( cmd[i-1], ' ' ) != NULL ) + len ++; + + s = g_new0( char, len + 1 ); + for( i = 0; cmd[i]; i ++ ) { - user_t *u = user_find( irc, irc->mynick ); - help_t *h; - set_t *s; - int i; - - irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "OK" ); - - for( i = 0; commands[i].command; i ++ ) - 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 ); + if( cmd[i+1] == NULL && strchr( cmd[i], ' ' ) != NULL ) + strcat( s, ":" ); - for( s = irc->set; s; s = s->next ) - irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS set ", s->key ); + strcat( s, cmd[i] ); - irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "END" ); - } - else if( set_getint( irc, "debug" ) ) - { - irc_usermsg( irc, "\002--- Unknown command:" ); - for( i = 0; cmd[i]; i ++ ) irc_usermsg( irc, "%s", cmd[i] ); - irc_usermsg( irc, "\002--------------------" ); + if( cmd[i+1] ) + strcat( s, " " ); } + strcat( s, "\r\n" ); - return( 1 ); + return s; } void irc_reply( irc_t *irc, int code, char *format, ... ) @@ -1039,35 +670,26 @@ void irc_names( irc_t *irc, char *channel ) irc_reply( irc, 366, "%s :End of /NAMES list", channel ); } -void irc_who( irc_t *irc, char *channel ) +int irc_check_login( irc_t *irc ) { - user_t *u = irc->users; - struct conversation *c; - GList *l; - - if( !channel || *channel == '0' || *channel == '*' || !*channel ) - while( u ) - { - irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", u->online ? irc->channel : "*", u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); - u = u->next; - } - else if( g_strcasecmp( channel, irc->channel ) == 0 ) - while( u ) + if( irc->user && irc->nick ) + { + if( global.conf->authmode == AUTHMODE_CLOSED && irc->status < USTATUS_AUTHORIZED ) { - if( u->online ) - irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); - u = u->next; + irc_reply( irc, 464, ":This server is password-protected." ); + return 0; } - else if( ( c = conv_findchannel( channel ) ) ) - for( l = c->in_room; l; l = l->next ) + else { - if( ( u = user_findhandle( c->gc, l->data ) ) ) - irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); + irc_login( irc ); + return 1; } - else if( ( u = user_find( irc, channel ) ) ) - irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); - - irc_reply( irc, 315, "%s :End of /WHO list.", channel?channel:"**" ); + } + else + { + /* More information needed. */ + return 0; + } } void irc_login( irc_t *irc ) @@ -1102,11 +724,13 @@ void irc_login( irc_t *irc ) u->host = g_strdup( irc->host ); u->realname = g_strdup( irc->realname ); u->online = 1; -// u->send_handler = msg_echo; irc_spawn( irc, u ); irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\nIf you've never used BitlBee before, please do read the help information using the \x02help\x02 command. Lots of FAQ's are answered there." ); + if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) + ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname ); + irc->status = USTATUS_LOGGED_IN; } @@ -1158,7 +782,7 @@ void irc_motd( irc_t *irc ) } } irc_reply( irc, 376, ":End of MOTD" ); - closesocket( fd ); + close( fd ); } } @@ -1179,34 +803,6 @@ void irc_topic( irc_t *irc, char *channel ) } } -void irc_whois( irc_t *irc, char *nick ) -{ - user_t *u = user_find( irc, nick ); - - if( u ) - { - irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname ); - - if( u->gc ) - irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->gc->user->username, - *u->gc->user->proto_opt[0] ? u->gc->user->proto_opt[0] : "", u->gc->prpl->name ); - else - irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO ); - - if( !u->online ) - irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" ); - else if( u->away ) - irc_reply( irc, 301, "%s :%s", u->nick, u->away ); - - irc_reply( irc, 318, "%s :End of /WHOIS list", nick ); - } - else - { - irc_reply( irc, 401, "%s :Nick does not exist", nick ); - } -} - - void irc_umode_set( irc_t *irc, char *s, int allow_priv ) { /* allow_priv: Set to 0 if s contains user input, 1 if you want @@ -1236,47 +832,6 @@ void irc_umode_set( irc_t *irc, char *s, int allow_priv ) irc_reply( irc, 221, "+%s", irc->umode ); } -int irc_away( irc_t *irc, char *away ) -{ - user_t *u = user_find( irc, irc->nick ); - GSList *c = get_connections(); - - if( !u ) return( 0 ); - - if( away && *away ) - { - int i, j; - - /* Copy away string, but skip control chars. Mainly because - Jabber really doesn't like them. */ - u->away = g_malloc( strlen( away ) + 1 ); - for( i = j = 0; away[i]; i ++ ) - if( ( u->away[j] = away[i] ) >= ' ' ) - j ++; - u->away[j] = 0; - - irc_reply( irc, 306, ":You're now away: %s", u->away ); - /* irc_umode_set( irc, irc->myhost, "+a" ); */ - } - else - { - if( u->away ) g_free( u->away ); - u->away = NULL; - /* irc_umode_set( irc, irc->myhost, "-a" ); */ - irc_reply( irc, 305, ":Welcome back" ); - } - - while( c ) - { - if( ((struct gaim_connection *)c->data)->flags & OPT_LOGGED_IN ) - proto_away( c->data, u->away ); - - c = c->next; - } - - return( 1 ); -} - void irc_spawn( irc_t *irc, user_t *u ) { irc_join( irc, u, irc->channel ); @@ -1330,22 +885,6 @@ void irc_kill( irc_t *irc, user_t *u ) g_free( nick ); } -void irc_invite( irc_t *irc, char *nick, char *channel ) -{ - struct conversation *c = conv_findchannel( channel ); - user_t *u = user_find( irc, nick ); - - if( u && c && ( u->gc == c->gc ) ) - if( c->gc && c->gc->prpl && c->gc->prpl->chat_invite ) - { - c->gc->prpl->chat_invite( c->gc, c->id, "", u->handle ); - irc_reply( irc, 341, "%s %s", nick, channel ); - return; - } - - irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); -} - int irc_send( irc_t *irc, char *nick, char *s, int flags ) { struct conversation *c = NULL; @@ -1434,7 +973,10 @@ int irc_send( irc_t *irc, char *nick, char *s, int flags ) } if( u->send_handler ) - return( u->send_handler( irc, u, s, flags ) ); + { + u->send_handler( irc, u, s, flags ); + return 1; + } } else if( c && c->gc && c->gc->prpl ) { @@ -1460,9 +1002,9 @@ gboolean buddy_send_handler_delayed( gpointer data ) return( FALSE ); } -int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ) +void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ) { - if( !u || !u->gc ) return( 0 ); + if( !u || !u->gc ) return; if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 ) { @@ -1498,12 +1040,10 @@ int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ) if( u->sendbuf_timer > 0 ) g_source_remove( u->sendbuf_timer ); u->sendbuf_timer = g_timeout_add( delay, buddy_send_handler_delayed, u ); - - return( 1 ); } else { - return( serv_send_im( irc, u, msg, flags ) ); + serv_send_im( irc, u, msg, flags ); } } @@ -1608,8 +1148,7 @@ static gboolean irc_userping( gpointer _irc ) if( rv > 0 ) { - irc_write( irc, "ERROR :Closing Link: Ping Timeout: %d seconds", rv ); - irc_free( irc ); + irc_abort( irc, 0, "Ping Timeout: %d seconds", rv ); return FALSE; } @@ -32,7 +32,7 @@ #define IRC_LOGIN_TIMEOUT 60 #define IRC_PING_STRING "PinglBee" -#define UMODES "ias" +#define UMODES "iasw" #define UMODES_PRIV "Ro" #define CMODES "nt" #define CMODE "t" @@ -40,10 +40,11 @@ typedef enum { - USTATUS_OFFLINE, + USTATUS_OFFLINE = 0, USTATUS_AUTHORIZED, USTATUS_LOGGED_IN, - USTATUS_IDENTIFIED + USTATUS_IDENTIFIED, + USTATUS_SHUTDOWN = -1 } irc_status_t; typedef struct channel @@ -103,11 +104,13 @@ typedef struct irc extern GSList *irc_connection_list; irc_t *irc_new( int fd ); +void irc_abort( irc_t *irc, int immed, char *format, ... ); void irc_free( irc_t *irc ); -int irc_exec( irc_t *irc, char **cmd ); -int irc_process( irc_t *irc ); -int irc_process_line( irc_t *irc, char *line ); +void irc_exec( irc_t *irc, char **cmd ); +void irc_process( irc_t *irc ); +char **irc_parse_line( char *line ); +char *irc_build_line( char **cmd ); void irc_vawrite( irc_t *irc, char *format, va_list params ); void irc_write( irc_t *irc, char *format, ... ); @@ -117,6 +120,7 @@ G_MODULE_EXPORT int irc_usermsg( irc_t *irc, char *format, ... ); char **irc_tokenize( char *buffer ); void irc_login( irc_t *irc ); +int irc_check_login( irc_t *irc ); void irc_motd( irc_t *irc ); void irc_names( irc_t *irc, char *channel ); void irc_topic( irc_t *irc, char *channel ); @@ -129,7 +133,6 @@ void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker ); 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 ); @@ -137,6 +140,6 @@ int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char int irc_msgfrom( irc_t *irc, char *nick, char *msg ); int irc_noticefrom( irc_t *irc, char *nick, char *msg ); -int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ); +void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ); #endif diff --git a/irc_commands.c b/irc_commands.c new file mode 100644 index 00000000..f2c7a645 --- /dev/null +++ b/irc_commands.c @@ -0,0 +1,622 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2006 Wilmer van der Gaast and others * + \********************************************************************/ + +/* IRC commands */ + +/* + 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 "ipc.h" + +static void irc_cmd_pass( irc_t *irc, char **cmd ) +{ + if( global.conf->auth_pass && strcmp( cmd[1], global.conf->auth_pass ) == 0 ) + { + irc->status = USTATUS_AUTHORIZED; + irc_check_login( irc ); + } + else + { + irc_reply( irc, 464, ":Incorrect password" ); + } +} + +static void irc_cmd_user( irc_t *irc, char **cmd ) +{ + irc->user = g_strdup( cmd[1] ); + irc->realname = g_strdup( cmd[4] ); + + irc_check_login( irc ); +} + +static void irc_cmd_nick( irc_t *irc, char **cmd ) +{ + if( irc->nick ) + { + irc_reply( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" ); + } + /* This is not clean, but for now it'll have to be like this... */ + else if( ( nick_cmp( cmd[1], irc->mynick ) == 0 ) || ( nick_cmp( cmd[1], NS_NICK ) == 0 ) ) + { + irc_reply( irc, 433, ":This nick is already in use" ); + } + else if( !nick_ok( cmd[1] ) ) + { + /* [SH] Invalid characters. */ + irc_reply( irc, 432, ":This nick contains invalid characters" ); + } + else + { + irc->nick = g_strdup( cmd[1] ); + + irc_check_login( irc ); + } +} + +static void irc_cmd_quit( irc_t *irc, char **cmd ) +{ + if( cmd[1] && *cmd[1] ) + irc_abort( irc, 0, "Quit: %s", cmd[1] ); + else + irc_abort( irc, 0, "Leaving..." ); +} + +static void irc_cmd_ping( irc_t *irc, char **cmd ) +{ + irc_write( irc, ":%s PONG %s :%s", irc->myhost, irc->myhost, cmd[1]?cmd[1]:irc->myhost ); +} + +static void irc_cmd_oper( irc_t *irc, char **cmd ) +{ + if( global.conf->oper_pass && strcmp( cmd[2], global.conf->oper_pass ) == 0 ) + { + irc_umode_set( irc, "+o", 1 ); + irc_reply( irc, 381, ":Password accepted" ); + } + else + { + irc_reply( irc, 432, ":Incorrect password" ); + } +} + +static void irc_cmd_mode( irc_t *irc, char **cmd ) +{ + if( *cmd[1] == '#' || *cmd[1] == '&' ) + { + if( cmd[2] ) + { + if( *cmd[2] == '+' || *cmd[2] == '-' ) + irc_reply( irc, 477, "%s :Can't change channel modes", cmd[1] ); + else if( *cmd[2] == 'b' ) + irc_reply( irc, 368, "%s :No bans possible", cmd[1] ); + } + else + irc_reply( irc, 324, "%s +%s", cmd[1], CMODE ); + } + else + { + if( nick_cmp( cmd[1], irc->nick ) == 0 ) + { + if( cmd[2] ) + irc_umode_set( irc, cmd[2], 0 ); + } + else + irc_reply( irc, 502, ":Don't touch their modes" ); + } +} + +static void irc_cmd_names( irc_t *irc, char **cmd ) +{ + irc_names( irc, cmd[1]?cmd[1]:irc->channel ); +} + +static void irc_cmd_part( irc_t *irc, char **cmd ) +{ + struct conversation *c; + + if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) + { + user_t *u = user_find( irc, irc->nick ); + + /* Not allowed to leave control channel */ + irc_part( irc, u, irc->channel ); + irc_join( irc, u, irc->channel ); + } + else if( ( c = conv_findchannel( cmd[1] ) ) ) + { + user_t *u = user_find( irc, irc->nick ); + + irc_part( irc, u, c->channel ); + + if( c->gc && c->gc->prpl ) + { + c->joined = 0; + c->gc->prpl->chat_leave( c->gc, c->id ); + } + } + else + { + irc_reply( irc, 403, "%s :No such channel", cmd[1] ); + } +} + +static void irc_cmd_join( irc_t *irc, char **cmd ) +{ + if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) + ; /* Dude, you're already there... + RFC doesn't have any reply for that though? */ + else if( cmd[1] ) + { + if( ( cmd[1][0] == '#' || cmd[1][0] == '&' ) && cmd[1][1] ) + { + user_t *u = user_find( irc, cmd[1] + 1 ); + + if( u && u->gc && u->gc->prpl && u->gc->prpl->chat_open ) + { + irc_reply( irc, 403, "%s :Initializing groupchat in a different channel", cmd[1] ); + + if( !u->gc->prpl->chat_open( u->gc, u->handle ) ) + { + irc_usermsg( irc, "Could not open a groupchat with %s, maybe you don't have a connection to him/her yet?", u->nick ); + } + } + else if( u ) + { + irc_reply( irc, 403, "%s :Groupchats are not possible with %s", cmd[1], cmd[1]+1 ); + } + else + { + irc_reply( irc, 403, "%s :No such nick", cmd[1] ); + } + } + else + { + irc_reply( irc, 403, "%s :No such channel", cmd[1] ); + } + } +} + +static void irc_cmd_invite( irc_t *irc, char **cmd ) +{ + char *nick = cmd[1], *channel = cmd[2]; + struct conversation *c = conv_findchannel( channel ); + user_t *u = user_find( irc, nick ); + + if( u && c && ( u->gc == c->gc ) ) + if( c->gc && c->gc->prpl && c->gc->prpl->chat_invite ) + { + c->gc->prpl->chat_invite( c->gc, c->id, "", u->handle ); + irc_reply( irc, 341, "%s %s", nick, channel ); + return; + } + + irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); +} + +static void irc_cmd_privmsg( irc_t *irc, char **cmd ) +{ + if ( !cmd[2] ) + { + irc_reply( irc, 412, ":No text to send" ); + } + else if ( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) + { + irc_write( irc, ":%s!%s@%s %s %s :%s", irc->nick, irc->user, irc->host, cmd[0], cmd[1], cmd[2] ); + } + else + { + if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) + { + unsigned int i; + char *t = set_getstr( irc, "default_target" ); + + if( g_strcasecmp( t, "last" ) == 0 && irc->last_target ) + cmd[1] = irc->last_target; + else if( g_strcasecmp( t, "root" ) == 0 ) + cmd[1] = irc->mynick; + + for( i = 0; i < strlen( cmd[2] ); i ++ ) + { + if( cmd[2][i] == ' ' ) break; + if( cmd[2][i] == ':' || cmd[2][i] == ',' ) + { + cmd[1] = cmd[2]; + cmd[2] += i; + *cmd[2] = 0; + while( *(++cmd[2]) == ' ' ); + break; + } + } + + irc->is_private = 0; + + if( cmd[1] != irc->last_target ) + { + if( irc->last_target ) + g_free( irc->last_target ); + irc->last_target = g_strdup( cmd[1] ); + } + } + else + { + irc->is_private = 1; + } + irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? IM_FLAG_AWAY : 0 ); + } +} + +static void irc_cmd_who( irc_t *irc, char **cmd ) +{ + char *channel = cmd[1]; + user_t *u = irc->users; + struct conversation *c; + GList *l; + + if( !channel || *channel == '0' || *channel == '*' || !*channel ) + while( u ) + { + irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", u->online ? irc->channel : "*", u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); + u = u->next; + } + else if( g_strcasecmp( channel, irc->channel ) == 0 ) + while( u ) + { + if( u->online ) + irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); + u = u->next; + } + else if( ( c = conv_findchannel( channel ) ) ) + for( l = c->in_room; l; l = l->next ) + { + if( ( u = user_findhandle( c->gc, l->data ) ) ) + irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); + } + else if( ( u = user_find( irc, channel ) ) ) + irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); + + irc_reply( irc, 315, "%s :End of /WHO list", channel?channel:"**" ); +} + +static void irc_cmd_userhost( irc_t *irc, char **cmd ) +{ + user_t *u; + int i; + + /* [TV] Usable USERHOST-implementation according to + RFC1459. Without this, mIRC shows an error + while connecting, and the used way of rejecting + breaks standards. + */ + + for( i = 1; cmd[i]; i ++ ) + if( ( u = user_find( irc, cmd[i] ) ) ) + { + if( u->online && u->away ) + irc_reply( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host ); + else + irc_reply( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host ); + } +} + +static void irc_cmd_ison( irc_t *irc, char **cmd ) +{ + user_t *u; + char buff[IRC_MAX_LINE]; + int lenleft, i; + + buff[0] = '\0'; + + /* [SH] Leave room for : and \0 */ + lenleft = IRC_MAX_LINE - 2; + + for( i = 1; cmd[i]; i ++ ) + { + if( ( u = user_find( irc, cmd[i] ) ) && u->online ) + { + /* [SH] Make sure we don't use too much buffer space. */ + lenleft -= strlen( u->nick ) + 1; + + if( lenleft < 0 ) + { + break; + } + + /* [SH] Add the nick to the buffer. Note + * that an extra space is always added. Even + * if it's the last nick in the list. Who + * cares? + */ + + strcat( buff, u->nick ); + strcat( buff, " " ); + } + } + + /* [WvG] Well, maybe someone cares, so why not remove it? */ + if( strlen( buff ) > 0 ) + buff[strlen(buff)-1] = '\0'; + + irc_reply( irc, 303, ":%s", buff ); +} + +static void irc_cmd_watch( irc_t *irc, char **cmd ) +{ + int i; + + /* Obviously we could also mark a user structure as being + watched, but what if the WATCH command is sent right + after connecting? The user won't exist yet then... */ + for( i = 1; cmd[i]; i ++ ) + { + char *nick; + user_t *u; + + if( !cmd[i][0] || !cmd[i][1] ) + break; + + nick = g_strdup( cmd[i] + 1 ); + nick_lc( nick ); + + u = user_find( irc, nick ); + + if( cmd[i][0] == '+' ) + { + if( !g_hash_table_lookup( irc->watches, nick ) ) + g_hash_table_insert( irc->watches, nick, nick ); + + if( u && u->online ) + irc_reply( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "is online" ); + else + irc_reply( irc, 605, "%s %s %s %d :%s", nick, "*", "*", time( NULL ), "is offline" ); + } + else if( cmd[i][0] == '-' ) + { + gpointer okey, ovalue; + + if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) ) + { + g_free( okey ); + g_hash_table_remove( irc->watches, okey ); + + irc_reply( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" ); + } + } + } +} + +static void irc_cmd_topic( irc_t *irc, char **cmd ) +{ + if( cmd[2] ) + irc_reply( irc, 482, "%s :Cannot change topic", cmd[1] ); + else + irc_topic( irc, cmd[1] ); +} + +static void irc_cmd_away( irc_t *irc, char **cmd ) +{ + user_t *u = user_find( irc, irc->nick ); + char *away = cmd[1]; + account_t *a; + + if( !u ) return; + + if( away && *away ) + { + int i, j; + + /* Copy away string, but skip control chars. Mainly because + Jabber really doesn't like them. */ + u->away = g_malloc( strlen( away ) + 1 ); + for( i = j = 0; away[i]; i ++ ) + if( ( u->away[j] = away[i] ) >= ' ' ) + j ++; + u->away[j] = 0; + + irc_reply( irc, 306, ":You're now away: %s", u->away ); + /* irc_umode_set( irc, irc->myhost, "+a" ); */ + } + else + { + if( u->away ) g_free( u->away ); + u->away = NULL; + /* irc_umode_set( irc, irc->myhost, "-a" ); */ + irc_reply( irc, 305, ":Welcome back" ); + } + + for( a = irc->accounts; a; a = a->next ) + { + struct gaim_connection *gc = a->gc; + + if( gc && gc->flags & OPT_LOGGED_IN ) + proto_away( gc, u->away ); + } +} + +static void irc_cmd_whois( irc_t *irc, char **cmd ) +{ + char *nick = cmd[1]; + user_t *u = user_find( irc, nick ); + + if( u ) + { + irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname ); + + if( u->gc ) + irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->gc->user->username, + *u->gc->user->proto_opt[0] ? u->gc->user->proto_opt[0] : "", u->gc->prpl->name ); + else + irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO ); + + if( !u->online ) + irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" ); + else if( u->away ) + irc_reply( irc, 301, "%s :%s", u->nick, u->away ); + + irc_reply( irc, 318, "%s :End of /WHOIS list", nick ); + } + else + { + irc_reply( irc, 401, "%s :Nick does not exist", nick ); + } +} + +static void irc_cmd_whowas( irc_t *irc, char **cmd ) +{ + /* For some reason irssi tries a whowas when whois fails. We can + ignore this, but then the user never gets a "user not found" + message from irssi which is a bit annoying. So just respond + with not-found and irssi users will get better error messages */ + + irc_reply( irc, 406, "%s :Nick does not exist", cmd[1] ); + irc_reply( irc, 369, "%s :End of WHOWAS", cmd[1] ); +} + +static void irc_cmd_nickserv( irc_t *irc, char **cmd ) +{ + /* [SH] This aliases the NickServ command to PRIVMSG root */ + /* [TV] This aliases the NS command to PRIVMSG root as well */ + root_command( irc, cmd + 1 ); +} + +static void irc_cmd_motd( irc_t *irc, char **cmd ) +{ + irc_motd( irc ); +} + +static void irc_cmd_pong( irc_t *irc, char **cmd ) +{ + /* We could check the value we get back from the user, but in + fact we don't care, we're just happy he's still alive. */ + irc->last_pong = gettime(); + irc->pinging = 0; +} + +static void irc_cmd_completions( irc_t *irc, char **cmd ) +{ + user_t *u = user_find( irc, irc->mynick ); + help_t *h; + set_t *s; + int i; + + irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "OK" ); + + for( i = 0; commands[i].command; i ++ ) + 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 ); + + for( s = irc->set; s; s = s->next ) + irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS set ", s->key ); + + irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "END" ); +} + +static void irc_cmd_rehash( irc_t *irc, char **cmd ) +{ + if( global.conf->runmode == RUNMODE_INETD ) + ipc_master_cmd_rehash( NULL, NULL ); + else + ipc_to_master( cmd ); + + irc_reply( irc, 382, "%s :Rehashing", CONF_FILE ); +} + +static const command_t irc_commands[] = { + { "pass", 1, irc_cmd_pass, IRC_CMD_PRE_LOGIN }, + { "user", 4, irc_cmd_user, IRC_CMD_PRE_LOGIN }, + { "nick", 1, irc_cmd_nick, 0 }, + { "quit", 0, irc_cmd_quit, 0 }, + { "ping", 0, irc_cmd_ping, 0 }, + { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, + { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, + { "names", 0, irc_cmd_names, IRC_CMD_LOGGED_IN }, + { "part", 1, irc_cmd_part, IRC_CMD_LOGGED_IN }, + { "join", 1, irc_cmd_join, IRC_CMD_LOGGED_IN }, + { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, + { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, + { "notice", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, + { "who", 0, irc_cmd_who, IRC_CMD_LOGGED_IN }, + { "userhost", 1, irc_cmd_userhost, IRC_CMD_LOGGED_IN }, + { "ison", 1, irc_cmd_ison, IRC_CMD_LOGGED_IN }, + { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN }, + { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, + { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, + { "whois", 1, irc_cmd_whois, IRC_CMD_LOGGED_IN }, + { "whowas", 1, irc_cmd_whowas, IRC_CMD_LOGGED_IN }, + { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, + { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, + { "motd", 0, irc_cmd_motd, IRC_CMD_LOGGED_IN }, + { "pong", 0, irc_cmd_pong, IRC_CMD_LOGGED_IN }, + { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN }, + { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, + { "wallops", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, + { "lilo", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, + { "rehash", 0, irc_cmd_rehash, IRC_CMD_OPER_ONLY }, + { "restart", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, + { "kill", 2, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, + { NULL } +}; + +void irc_exec( irc_t *irc, char *cmd[] ) +{ + int i, n_arg; + + if( !cmd[0] ) + return; + + for( i = 0; irc_commands[i].command; i++ ) + if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 ) + { + /* There should be no typo in the next line: */ + for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --; + + if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status >= USTATUS_LOGGED_IN ) + { + irc_reply( irc, 462, ":Only allowed before logging in" ); + } + else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && irc->status < USTATUS_LOGGED_IN ) + { + irc_reply( irc, 451, ":Register first" ); + } + else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) ) + { + irc_reply( irc, 481, ":Permission denied - You're not an IRC operator" ); + } + else if( n_arg < irc_commands[i].required_parameters ) + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + else if( irc_commands[i].flags & IRC_CMD_TO_MASTER ) + { + /* IPC doesn't make sense in inetd mode, + but the function will catch that. */ + ipc_to_master( cmd ); + } + else + { + irc_commands[i].execute( irc, cmd ); + } + + break; + } +} @@ -37,11 +37,11 @@ static void log_console(int level, char *logmessage); void log_init(void) { openlog("bitlbee", LOG_PID, LOG_DAEMON); - logoutput.informational=&log_null; - logoutput.warning=&log_null; - logoutput.error=&log_null; + logoutput.informational = &log_null; + logoutput.warning = &log_null; + logoutput.error = &log_null; #ifdef DEBUG - logoutput.debug=&log_null; + logoutput.debug = &log_null; #endif return; @@ -50,46 +50,46 @@ void log_init(void) { void log_link(int level, int output) { /* I know it's ugly, but it works and I didn't feel like messing with pointer to function pointers */ - if(level==LOGLVL_INFO) { - if(output==LOGOUTPUT_NULL) - logoutput.informational=&log_null; - else if(output==LOGOUTPUT_IRC) - logoutput.informational=&log_irc; - else if(output==LOGOUTPUT_SYSLOG) - logoutput.informational=&log_syslog; - else if(output==LOGOUTPUT_CONSOLE) - logoutput.informational=&log_console; + if(level == LOGLVL_INFO) { + if(output == LOGOUTPUT_NULL) + logoutput.informational = &log_null; + else if(output == LOGOUTPUT_IRC) + logoutput.informational = &log_irc; + else if(output == LOGOUTPUT_SYSLOG) + logoutput.informational = &log_syslog; + else if(output == LOGOUTPUT_CONSOLE) + logoutput.informational = &log_console; } - else if(level==LOGLVL_WARNING) { - if(output==LOGOUTPUT_NULL) - logoutput.warning=&log_null; - else if(output==LOGOUTPUT_IRC) - logoutput.warning=&log_irc; - else if(output==LOGOUTPUT_SYSLOG) - logoutput.warning=&log_syslog; - else if(output==LOGOUTPUT_CONSOLE) - logoutput.warning=&log_console; + else if(level == LOGLVL_WARNING) { + if(output == LOGOUTPUT_NULL) + logoutput.warning = &log_null; + else if(output == LOGOUTPUT_IRC) + logoutput.warning = &log_irc; + else if(output == LOGOUTPUT_SYSLOG) + logoutput.warning = &log_syslog; + else if(output == LOGOUTPUT_CONSOLE) + logoutput.warning = &log_console; } - else if(level==LOGLVL_ERROR) { - if(output==LOGOUTPUT_NULL) - logoutput.error=&log_null; - else if(output==LOGOUTPUT_IRC) - logoutput.error=&log_irc; - else if(output==LOGOUTPUT_SYSLOG) - logoutput.error=&log_syslog; - else if(output==LOGOUTPUT_CONSOLE) - logoutput.error=&log_console; + else if(level == LOGLVL_ERROR) { + if(output == LOGOUTPUT_NULL) + logoutput.error = &log_null; + else if(output == LOGOUTPUT_IRC) + logoutput.error = &log_irc; + else if(output == LOGOUTPUT_SYSLOG) + logoutput.error = &log_syslog; + else if(output == LOGOUTPUT_CONSOLE) + logoutput.error = &log_console; } #ifdef DEBUG - else if(level==LOGLVL_DEBUG) { - if(output==LOGOUTPUT_NULL) - logoutput.debug=&log_null; - else if(output==LOGOUTPUT_IRC) - logoutput.debug=&log_irc; - else if(output==LOGOUTPUT_SYSLOG) - logoutput.debug=&log_syslog; - else if(output==LOGOUTPUT_CONSOLE) - logoutput.debug=&log_console; + else if(level == LOGLVL_DEBUG) { + if(output == LOGOUTPUT_NULL) + logoutput.debug = &log_null; + else if(output == LOGOUTPUT_IRC) + logoutput.debug = &log_irc; + else if(output == LOGOUTPUT_SYSLOG) + logoutput.debug = &log_syslog; + else if(output == LOGOUTPUT_CONSOLE) + logoutput.debug = &log_console; } #endif return; @@ -105,14 +105,14 @@ void log_message(int level, char *message, ... ) { msgstring = g_strdup_vprintf(message, ap); va_end(ap); - if(level==LOGLVL_INFO) + if(level == LOGLVL_INFO) (*(logoutput.informational))(level, msgstring); - if(level==LOGLVL_WARNING) + if(level == LOGLVL_WARNING) (*(logoutput.warning))(level, msgstring); - if(level==LOGLVL_ERROR) + if(level == LOGLVL_ERROR) (*(logoutput.error))(level, msgstring); #ifdef DEBUG - if(level==LOGLVL_DEBUG) + if(level == LOGLVL_DEBUG) (*(logoutput.debug))(level, msgstring); #endif @@ -132,14 +132,14 @@ static void log_null(int level, char *message) { } static void log_irc(int level, char *message) { - if(level==LOGLVL_ERROR) + if(level == LOGLVL_ERROR) irc_write_all(1, "ERROR :Error: %s", message); - if(level==LOGLVL_WARNING) + if(level == LOGLVL_WARNING) irc_write_all(0, "ERROR :Warning: %s", message); - if(level==LOGLVL_INFO) + if(level == LOGLVL_INFO) irc_write_all(0, "ERROR :Informational: %s", message); #ifdef DEBUG - if(level==LOGLVL_DEBUG) + if(level == LOGLVL_DEBUG) irc_write_all(0, "ERROR :Debug: %s", message); #endif @@ -147,28 +147,28 @@ static void log_irc(int level, char *message) { } static void log_syslog(int level, char *message) { - if(level==LOGLVL_ERROR) + if(level == LOGLVL_ERROR) syslog(LOG_ERR, "%s", message); - if(level==LOGLVL_WARNING) + if(level == LOGLVL_WARNING) syslog(LOG_WARNING, "%s", message); - if(level==LOGLVL_INFO) + if(level == LOGLVL_INFO) syslog(LOG_INFO, "%s", message); #ifdef DEBUG - if(level==LOGLVL_DEBUG) + if(level == LOGLVL_DEBUG) syslog(LOG_DEBUG, "%s", message); #endif return; } static void log_console(int level, char *message) { - if(level==LOGLVL_ERROR) + if(level == LOGLVL_ERROR) fprintf(stderr, "Error: %s\n", message); - if(level==LOGLVL_WARNING) + if(level == LOGLVL_WARNING) fprintf(stderr, "Warning: %s\n", message); - if(level==LOGLVL_INFO) + if(level == LOGLVL_INFO) fprintf(stdout, "Informational: %s\n", message); #ifdef DEBUG - if(level==LOGLVL_DEBUG) + if(level == LOGLVL_DEBUG) fprintf(stdout, "Debug: %s\n", message); #endif return; diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index d4b5bde5..ba652b8a 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -412,7 +412,7 @@ static void gjab_recv(gjconn gjc) XML_Parse(gjc->parser, buf, len, 0); if (jd->die) signoff(GJ_GC(gjc)); - } else if (len < 0 || errno != EAGAIN) { + } else if (len == 0 || (len < 0 && (!sockerr_again() || gjc->ssl))) { STATE_EVT(JCONN_STATE_OFF) } } diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index b03c898e..3c7064f8 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -83,13 +83,13 @@ static void msn_close( struct gaim_connection *gc ) for( l = md->msgq; l; l = l->next ) { m = l->data; + + serv_got_crap( gc, "Warning: Closing down MSN connection with unsent message to %s, you'll have to resend it.", m->who ); g_free( m->who ); g_free( m->text ); g_free( m ); } g_slist_free( md->msgq ); - - serv_got_crap( gc, "Warning: Closing down MSN connection with unsent message(s), you'll have to resend them." ); } for( l = gc->permit; l; l = l->next ) diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 9e7ef841..deaceba1 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -212,13 +212,16 @@ void msn_sb_destroy( struct msn_switchboard *sb ) for( l = sb->msgq; l; l = l->next ) { m = l->data; + g_free( m->who ); g_free( m->text ); g_free( m ); } g_slist_free( sb->msgq ); - serv_got_crap( gc, "Warning: Closing down MSN switchboard connection with unsent message(s), you'll have to resend them." ); + serv_got_crap( gc, "Warning: Closing down MSN switchboard connection with " + "unsent message to %s, you'll have to resend it.", + m->who ? m->who : "(unknown)" ); } if( sb->chat ) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index b4bfb220..4e552bce 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -607,6 +607,7 @@ static void damn_you(gpointer data, gint source, GaimInputCondition c) g_free(pos); return; } + /* [WvG] Wheeeee! Who needs error checking anyway? ;-) */ read(pos->fd, m, 16); m[16] = '\0'; gaim_input_remove(pos->inpa); diff --git a/protocols/oscar/rxqueue.c b/protocols/oscar/rxqueue.c index d8adaa73..6e8dd29c 100644 --- a/protocols/oscar/rxqueue.c +++ b/protocols/oscar/rxqueue.c @@ -352,8 +352,15 @@ int aim_get_command(aim_session_t *sess, aim_conn_t *conn) if (conn->fd == -1) return -1; /* its a aim_conn_close()'d connection */ - if (conn->fd < 3) /* can happen when people abuse the interface */ + /* KIDS, THIS IS WHAT HAPPENS IF YOU USE CODE WRITTEN FOR GUIS IN A DAEMON! + + And wouldn't it make sense to return something that prevents this function + from being called again IMMEDIATELY (and making the program suck up all + CPU time)?... + + if (conn->fd < 3) return 0; + */ if (conn->status & AIM_CONN_STATUS_INPROGRESS) return aim_conn_completeconnect(sess, conn); diff --git a/protocols/oscar/service.c b/protocols/oscar/service.c index 875a2eb0..573e1983 100644 --- a/protocols/oscar/service.c +++ b/protocols/oscar/service.c @@ -736,8 +736,6 @@ int aim_setextstatus(aim_session_t *sess, aim_conn_t *conn, guint32 status) tlvlen = aim_addtlvtochain32(&tl, 0x0006, data); - printf("%d\n", tlvlen); - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 8))) return -ENOMEM; diff --git a/protocols/ssl_openssl.c b/protocols/ssl_openssl.c index ae55f3f9..e62f95b9 100644 --- a/protocols/ssl_openssl.c +++ b/protocols/ssl_openssl.c @@ -4,7 +4,7 @@ * Copyright 2002-2004 Wilmer van der Gaast and others * \********************************************************************/ -/* SSL module - GnuTLS version */ +/* SSL module - OpenTLS version */ /* This program is free software; you can redistribute it and/or modify @@ -40,13 +40,11 @@ static gboolean initialized = FALSE; struct scd { - ssl_input_function func; + SslInputFunction func; gpointer data; int fd; gboolean established; - int inpa; - int lasterr; /* Necessary for SSL_get_error */ SSL *ssl; SSL_CTX *ssl_ctx; }; @@ -55,7 +53,7 @@ static void ssl_connected( gpointer data, gint source, GaimInputCondition cond ) -void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); SSL_METHOD *meth; @@ -94,45 +92,19 @@ void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data return( conn ); } -static void ssl_handshake( gpointer data, gint source, GaimInputCondition cond ); - static void ssl_connected( gpointer data, gint source, GaimInputCondition cond ) { struct scd *conn = data; if( source == -1 ) - return ssl_handshake( data, -1, cond ); + goto ssl_connected_failure; - /* Make it non-blocking at least during the handshake... */ - sock_make_nonblocking( conn->fd ); SSL_set_fd( conn->ssl, conn->fd ); - return ssl_handshake( data, source, cond ); -} - -static void ssl_handshake( gpointer data, gint source, GaimInputCondition cond ) -{ - struct scd *conn = data; - int st; - - if( conn->inpa != -1 ) - { - gaim_input_remove( conn->inpa ); - conn->inpa = -1; - } - - if( ( st = SSL_connect( conn->ssl ) ) < 0 ) - { - conn->lasterr = SSL_get_error( conn->ssl, st ); - if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE ) - goto ssl_connected_failure; - - conn->inpa = gaim_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data ); - return; - } + if( SSL_connect( conn->ssl ) < 0 ) + goto ssl_connected_failure; conn->established = TRUE; - sock_make_blocking( conn->fd ); /* For now... */ conn->func( conn->data, conn, cond ); return; @@ -154,57 +126,24 @@ ssl_connected_failure: int ssl_read( void *conn, char *buf, int len ) { - int st; - if( !((struct scd*)conn)->established ) - { - ssl_errno = SSL_NOHANDSHAKE; - return -1; - } - - st = SSL_read( ((struct scd*)conn)->ssl, buf, len ); + return( 0 ); - ssl_errno = SSL_OK; - if( st <= 0 ) - { - ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st ); - if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ) - ssl_errno = SSL_AGAIN; - } - - return st; + return( SSL_read( ((struct scd*)conn)->ssl, buf, len ) ); } int ssl_write( void *conn, const char *buf, int len ) { - int st; - if( !((struct scd*)conn)->established ) - { - ssl_errno = SSL_NOHANDSHAKE; - return -1; - } - - st = SSL_write( ((struct scd*)conn)->ssl, buf, len ); + return( 0 ); - ssl_errno = SSL_OK; - if( st <= 0 ) - { - ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st ); - if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ) - ssl_errno = SSL_AGAIN; - } - - return st; + return( SSL_write( ((struct scd*)conn)->ssl, buf, len ) ); } void ssl_disconnect( void *conn_ ) { struct scd *conn = conn_; - if( conn->inpa != -1 ) - gaim_input_remove( conn->inpa ); - if( conn->established ) SSL_shutdown( conn->ssl ); @@ -219,8 +158,3 @@ int ssl_getfd( void *conn ) { return( ((struct scd*)conn)->fd ); } - -GaimInputCondition ssl_getdirection( void *conn ) -{ - return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? GAIM_INPUT_WRITE : GAIM_INPUT_READ ); -} diff --git a/protocols/yahoo/Makefile b/protocols/yahoo/Makefile index e594cc1f..b4014f8a 100644 --- a/protocols/yahoo/Makefile +++ b/protocols/yahoo/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = yahoo.o crypt.o libyahoo2.o yahoo_fn.o yahoo_httplib.o yahoo_list.o yahoo_util.o +objects = yahoo.o crypt.o libyahoo2.o yahoo_fn.o yahoo_httplib.o yahoo_util.o CFLAGS += -Wall -DSTDC_HEADERS -DHAVE_STRING_H -DHAVE_STRCHR -DHAVE_MEMCPY -DHAVE_GLIB LFLAGS += -r diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 832d1ab4..4f257d99 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -188,18 +188,18 @@ static int byahoo_send_typing( struct gaim_connection *gc, char *who, int typing static void byahoo_set_away( struct gaim_connection *gc, char *state, char *msg ) { struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data; - + gc->away = NULL; - - if (msg) + + if( msg ) { yd->current_status = YAHOO_STATUS_CUSTOM; gc->away = ""; } - else if (state) + if( state ) { gc->away = ""; - if( g_strcasecmp(state, "Available" ) == 0 ) + if( g_strcasecmp( state, "Available" ) == 0 ) { yd->current_status = YAHOO_STATUS_AVAILABLE; gc->away = NULL; @@ -234,12 +234,15 @@ static void byahoo_set_away( struct gaim_connection *gc, char *state, char *msg gc->away = NULL; } } - else if ( gc->is_idle ) + else if( gc->is_idle ) yd->current_status = YAHOO_STATUS_IDLE; else yd->current_status = YAHOO_STATUS_AVAILABLE; - yahoo_set_away( yd->y2_id, yd->current_status, msg, gc->away != NULL ); + if( yd->current_status == YAHOO_STATUS_INVISIBLE ) + yahoo_set_away( yd->y2_id, yd->current_status, NULL, gc->away != NULL ); + else + yahoo_set_away( yd->y2_id, yd->current_status, msg, gc->away != NULL ); } static GList *byahoo_away_states( struct gaim_connection *gc ) diff --git a/protocols/yahoo/yahoo_list.c b/protocols/yahoo/yahoo_list.c deleted file mode 100644 index cda631c6..00000000 --- a/protocols/yahoo/yahoo_list.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * yahoo_list.c: linked list routines - * - * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> - * Other code copyright Meredydd Luff <meredydd AT everybuddy.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Some of this code was borrowed from elist.c in the eb-lite sources - * - */ - -#include <stdlib.h> - -#include "yahoo_list.h" - -YList *y_list_append(YList * list, void *data) -{ - YList *n; - YList *new_list = malloc(sizeof(YList)); - YList *attach_to = NULL; - - new_list->next = NULL; - new_list->data = data; - - for (n = list; n != NULL; n = n->next) { - attach_to = n; - } - - if (attach_to == NULL) { - new_list->prev = NULL; - return new_list; - } else { - new_list->prev = attach_to; - attach_to->next = new_list; - return list; - } -} - -YList *y_list_prepend(YList * list, void *data) -{ - YList *n = malloc(sizeof(YList)); - - n->next = list; - n->prev = NULL; - n->data = data; - if (list) - list->prev = n; - - return n; -} - -YList *y_list_concat(YList * list, YList * add) -{ - YList *l; - - if(!list) - return add; - - if(!add) - return list; - - for (l = list; l->next; l = l->next) - ; - - l->next = add; - add->prev = l; - - return list; -} - -YList *y_list_remove(YList * list, void *data) -{ - YList *n; - - for (n = list; n != NULL; n = n->next) { - if (n->data == data) { - list=y_list_remove_link(list, n); - y_list_free_1(n); - break; - } - } - - return list; -} - -/* Warning */ -/* link MUST be part of list */ -/* caller must free link using y_list_free_1 */ -YList *y_list_remove_link(YList * list, const YList * link) -{ - if (!link) - return list; - - if (link->next) - link->next->prev = link->prev; - if (link->prev) - link->prev->next = link->next; - - if (link == list) - list = link->next; - - return list; -} - -int y_list_length(const YList * list) -{ - int retval = 0; - const YList *n = list; - - for (n = list; n != NULL; n = n->next) { - retval++; - } - - return retval; -} - -/* well, you could just check for list == NULL, but that would be - * implementation dependent - */ -int y_list_empty(const YList * list) -{ - if(!list) - return 1; - else - return 0; -} - -int y_list_singleton(const YList * list) -{ - if(!list || list->next) - return 0; - return 1; -} - -YList *y_list_copy(YList * list) -{ - YList *n; - YList *copy = NULL; - - for (n = list; n != NULL; n = n->next) { - copy = y_list_append(copy, n->data); - } - - return copy; -} - -void y_list_free_1(YList * list) -{ - free(list); -} - -void y_list_free(YList * list) -{ - YList *n = list; - - while (n != NULL) { - YList *next = n->next; - free(n); - n = next; - } -} - -YList *y_list_find(YList * list, const void *data) -{ - YList *l; - for (l = list; l && l->data != data; l = l->next) - ; - - return l; -} - -void y_list_foreach(YList * list, YListFunc fn, void * user_data) -{ - for (; list; list = list->next) - fn(list->data, user_data); -} - -YList *y_list_find_custom(YList * list, const void *data, YListCompFunc comp) -{ - YList *l; - for (l = list; l; l = l->next) - if (comp(l->data, data) == 0) - return l; - - return NULL; -} - -YList *y_list_nth(YList * list, int n) -{ - int i=n; - for ( ; list && i; list = list->next, i--) - ; - - return list; -} - -YList *y_list_insert_sorted(YList * list, void *data, YListCompFunc comp) -{ - YList *l, *n, *prev = NULL; - if (!list) - return y_list_append(list, data); - - n = malloc(sizeof(YList)); - n->data = data; - for (l = list; l && comp(l->data, n->data) <= 0; l = l->next) - prev = l; - - if (l) { - n->prev = l->prev; - l->prev = n; - } else - n->prev = prev; - - n->next = l; - - if(n->prev) { - n->prev->next = n; - return list; - } else { - return n; - } - -} diff --git a/protocols/yahoo/yahoo_list.h b/protocols/yahoo/yahoo_list.h index a7a69635..0d335acd 100644 --- a/protocols/yahoo/yahoo_list.h +++ b/protocols/yahoo/yahoo_list.h @@ -20,55 +20,29 @@ * */ -/* - * This is a replacement for the GList. It only provides functions that - * we use in Ayttm. Thanks to Meredyyd from everybuddy dev for doing - * most of it. - */ - #ifndef __YLIST_H__ #define __YLIST_H__ -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _YList { - struct _YList *next; - struct _YList *prev; - void *data; -} YList; - -typedef int (*YListCompFunc) (const void *, const void *); -typedef void (*YListFunc) (void *, void *); - -YList *y_list_append(YList * list, void *data); -YList *y_list_prepend(YList * list, void *data); -YList *y_list_remove_link(YList * list, const YList * link); -YList *y_list_remove(YList * list, void *data); - -YList *y_list_insert_sorted(YList * list, void * data, YListCompFunc comp); +/* GLib has linked list already, so I don't see why libyahoo2 has to copy this... */ + +typedef GList YList; + +#define y_list_append g_list_append +#define y_list_concat g_list_concat +#define y_list_copy g_list_copy +#define y_list_empty g_list_empty +#define y_list_find g_list_find +#define y_list_find_custom g_list_find_custom +#define y_list_foreach g_list_foreach +#define y_list_free g_list_free +#define y_list_free_1 g_list_free_1 +#define y_list_insert_sorted g_list_insert_sorted +#define y_list_length g_list_length +#define y_list_next g_list_next +#define y_list_nth g_list_nth +#define y_list_prepend g_list_prepend +#define y_list_remove g_list_remove +#define y_list_remove_link g_list_remove_link +#define y_list_singleton g_list_singleton -YList *y_list_copy(YList * list); - -YList *y_list_concat(YList * list, YList * add); - -YList *y_list_find(YList * list, const void *data); -YList *y_list_find_custom(YList * list, const void *data, YListCompFunc comp); - -YList *y_list_nth(YList * list, int n); - -void y_list_foreach(YList * list, YListFunc fn, void *user_data); - -void y_list_free_1(YList * list); -void y_list_free(YList * list); -int y_list_length(const YList * list); -int y_list_empty(const YList * list); -int y_list_singleton(const YList * list); - -#define y_list_next(list) list->next - -#ifdef __cplusplus -} -#endif #endif diff --git a/commands.c b/root_commands.c index 82143168..f69442d3 100644 --- a/commands.c +++ b/root_commands.c @@ -31,30 +31,7 @@ #include <string.h> -const command_t commands[] = { - { "help", 0, cmd_help }, - { "identify", 1, cmd_identify }, - { "register", 1, cmd_register }, - { "drop", 1, cmd_drop }, - { "account", 1, cmd_account }, - { "add", 2, cmd_add }, - { "info", 1, cmd_info }, - { "rename", 2, cmd_rename }, - { "remove", 1, cmd_remove }, - { "block", 1, cmd_block }, - { "allow", 1, cmd_allow }, - { "save", 0, cmd_save }, - { "set", 0, cmd_set }, - { "yes", 0, cmd_yesno }, - { "no", 0, cmd_yesno }, - { "blist", 0, cmd_blist }, - { "nick", 1, cmd_nick }, - { "import_buddies", 1, cmd_import_buddies }, - { "qlist", 0, cmd_qlist }, - { NULL } -}; - -int root_command_string( irc_t *irc, user_t *u, char *command, int flags ) +void root_command_string( irc_t *irc, user_t *u, char *command, int flags ) { char *cmd[IRC_MAX_ARGS]; char *s; @@ -86,15 +63,15 @@ int root_command_string( irc_t *irc, user_t *u, char *command, int flags ) } cmd[k] = NULL; - return( root_command( irc, cmd ) ); + root_command( irc, cmd ); } -int root_command( irc_t *irc, char *cmd[] ) +void root_command( irc_t *irc, char *cmd[] ) { int i; if( !cmd[0] ) - return( 0 ); + return; for( i = 0; commands[i].command; i++ ) if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 ) @@ -102,18 +79,16 @@ int root_command( irc_t *irc, char *cmd[] ) if( !cmd[commands[i].required_parameters] ) { irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters ); - return( 0 ); + return; } commands[i].execute( irc, cmd ); - return( 1 ); + return; } irc_usermsg( irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[0] ); - - return( 1 ); } -int cmd_help( irc_t *irc, char **cmd ) +static void cmd_help( irc_t *irc, char **cmd ) { char param[80]; int i; @@ -133,16 +108,14 @@ int cmd_help( irc_t *irc, char **cmd ) { irc_usermsg( irc, "%s", s ); g_free( s ); - return( 1 ); } else { irc_usermsg( irc, "Error opening helpfile." ); - return( 0 ); } } -int cmd_identify( irc_t *irc, char **cmd ) +static void cmd_identify( irc_t *irc, char **cmd ) { storage_status_t status = storage_load( irc->nick, cmd[1], irc ); @@ -161,16 +134,14 @@ int cmd_identify( irc_t *irc, char **cmd ) irc_usermsg( irc, "Something very weird happened" ); break; } - - return( 0 ); } -int cmd_register( irc_t *irc, char **cmd ) +static void cmd_register( irc_t *irc, char **cmd ) { if( global.conf->authmode == AUTHMODE_REGISTERED ) { irc_usermsg( irc, "This server does not allow registering new accounts" ); - return( 0 ); + return; } irc_setpass( irc, cmd[1] ); @@ -188,11 +159,9 @@ int cmd_register( irc_t *irc, char **cmd ) irc_usermsg( irc, "Error registering" ); break; } - - return( 0 ); } -int cmd_drop( irc_t *irc, char **cmd ) +static void cmd_drop( irc_t *irc, char **cmd ) { storage_status_t status; @@ -200,30 +169,30 @@ int cmd_drop( irc_t *irc, char **cmd ) switch (status) { case STORAGE_NO_SUCH_USER: irc_usermsg( irc, "That account does not exist" ); - return( 0 ); + break; case STORAGE_INVALID_PASSWORD: irc_usermsg( irc, "Password invalid" ); - return( 0 ); + break; case STORAGE_OK: irc_setpass( irc, NULL ); irc->status = USTATUS_LOGGED_IN; irc_umode_set( irc, "-R", 1 ); irc_usermsg( irc, "Account `%s' removed", irc->nick ); - return( 0 ); + break; default: irc_usermsg( irc, "Error: '%d'", status ); - return( 0 ); + break; } } -int cmd_account( irc_t *irc, char **cmd ) +static void cmd_account( irc_t *irc, char **cmd ) { account_t *a; if( global.conf->authmode == AUTHMODE_REGISTERED && irc->status < USTATUS_IDENTIFIED ) { irc_usermsg( irc, "This server only accepts registered users" ); - return( 0 ); + return; } if( g_strcasecmp( cmd[1], "add" ) == 0 ) @@ -233,7 +202,7 @@ int cmd_account( irc_t *irc, char **cmd ) if( cmd[2] == NULL || cmd[3] == NULL || cmd[4] == NULL ) { irc_usermsg( irc, "Not enough parameters" ); - return( 0 ); + return; } prpl = find_protocol(cmd[2]); @@ -241,7 +210,7 @@ int cmd_account( irc_t *irc, char **cmd ) if( prpl == NULL ) { irc_usermsg( irc, "Unknown protocol" ); - return( 0 ); + return; } a = account_add( irc, prpl, cmd[3], cmd[4] ); @@ -303,7 +272,7 @@ int cmd_account( irc_t *irc, char **cmd ) if( a->gc ) { irc_usermsg( irc, "Account already online" ); - return( 0 ); + return; } else { @@ -313,7 +282,7 @@ int cmd_account( irc_t *irc, char **cmd ) else { irc_usermsg( irc, "Invalid account" ); - return( 0 ); + return; } } else @@ -359,36 +328,34 @@ int cmd_account( irc_t *irc, char **cmd ) else { irc_usermsg( irc, "Account already offline" ); - return( 0 ); + return; } } else { irc_usermsg( irc, "Invalid account" ); - return( 0 ); + return; } } else { irc_usermsg( irc, "Unknown command: account %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[1] ); } - - return( 1 ); } -int cmd_add( irc_t *irc, char **cmd ) +static void cmd_add( irc_t *irc, char **cmd ) { account_t *a; if( !( a = account_get( irc, cmd[1] ) ) ) { irc_usermsg( irc, "Invalid account" ); - return( 1 ); + return; } else if( !( a->gc && ( a->gc->flags & OPT_LOGGED_IN ) ) ) { irc_usermsg( irc, "That account is not on-line" ); - return( 1 ); + return; } if( cmd[3] ) @@ -396,12 +363,12 @@ int cmd_add( irc_t *irc, char **cmd ) if( !nick_ok( cmd[3] ) ) { irc_usermsg( irc, "The requested nick `%s' is invalid", cmd[3] ); - return( 0 ); + return; } else if( user_find( irc, cmd[3] ) ) { irc_usermsg( irc, "The requested nick `%s' already exists", cmd[3] ); - return( 0 ); + return; } else { @@ -412,11 +379,9 @@ int cmd_add( irc_t *irc, char **cmd ) add_buddy( a->gc, NULL, cmd[2], cmd[2] ); irc_usermsg( irc, "User `%s' added to your contact list as `%s'", cmd[2], user_findhandle( a->gc, cmd[2] )->nick ); - - return( 0 ); } -int cmd_info( irc_t *irc, char **cmd ) +static void cmd_info( irc_t *irc, char **cmd ) { struct gaim_connection *gc; account_t *a; @@ -427,7 +392,7 @@ int cmd_info( irc_t *irc, char **cmd ) if( !u || !u->gc ) { irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); - return( 1 ); + return; } gc = u->gc; cmd[2] = u->handle; @@ -435,66 +400,63 @@ int cmd_info( irc_t *irc, char **cmd ) else if( !( a = account_get( irc, cmd[1] ) ) ) { irc_usermsg( irc, "Invalid account" ); - return( 1 ); + return; } else if( !( ( gc = a->gc ) && ( a->gc->flags & OPT_LOGGED_IN ) ) ) { irc_usermsg( irc, "That account is not on-line" ); - return( 1 ); + return; } if( !gc->prpl->get_info ) { irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] ); - return( 1 ); } - gc->prpl->get_info( gc, cmd[2] ); - - return( 0 ); + else + { + gc->prpl->get_info( gc, cmd[2] ); + } } -int cmd_rename( irc_t *irc, char **cmd ) +static void cmd_rename( irc_t *irc, char **cmd ) { user_t *u; if( g_strcasecmp( cmd[1], irc->nick ) == 0 ) { irc_usermsg( irc, "Nick `%s' can't be changed", cmd[1] ); - return( 1 ); } - if( user_find( irc, cmd[2] ) && ( nick_cmp( cmd[1], cmd[2] ) != 0 ) ) + else if( user_find( irc, cmd[2] ) && ( nick_cmp( cmd[1], cmd[2] ) != 0 ) ) { irc_usermsg( irc, "Nick `%s' already exists", cmd[2] ); - return( 1 ); } - if( !nick_ok( cmd[2] ) ) + else if( !nick_ok( cmd[2] ) ) { irc_usermsg( irc, "Nick `%s' is invalid", cmd[2] ); - return( 1 ); } - if( !( u = user_find( irc, cmd[1] ) ) ) + else if( !( u = user_find( irc, cmd[1] ) ) ) { irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); - return( 1 ); - } - user_rename( irc, cmd[1], cmd[2] ); - irc_write( irc, ":%s!%s@%s NICK %s", cmd[1], u->user, u->host, cmd[2] ); - if( g_strcasecmp( cmd[1], irc->mynick ) == 0 ) - { - g_free( irc->mynick ); - irc->mynick = g_strdup( cmd[2] ); } - else if( u->send_handler == buddy_send_handler ) + else { - nick_set( irc, u->handle, u->gc->prpl, cmd[2] ); + user_rename( irc, cmd[1], cmd[2] ); + irc_write( irc, ":%s!%s@%s NICK %s", cmd[1], u->user, u->host, cmd[2] ); + if( g_strcasecmp( cmd[1], irc->mynick ) == 0 ) + { + g_free( irc->mynick ); + irc->mynick = g_strdup( cmd[2] ); + } + else if( u->send_handler == buddy_send_handler ) + { + nick_set( irc, u->handle, u->gc->prpl, cmd[2] ); + } + + irc_usermsg( irc, "Nick successfully changed" ); } - - irc_usermsg( irc, "Nick successfully changed" ); - - return( 0 ); } -int cmd_remove( irc_t *irc, char **cmd ) +static void cmd_remove( irc_t *irc, char **cmd ) { user_t *u; char *s; @@ -502,7 +464,7 @@ int cmd_remove( irc_t *irc, char **cmd ) if( !( u = user_find( irc, cmd[1] ) ) || !u->gc ) { irc_usermsg( irc, "Buddy `%s' not found", cmd[1] ); - return( 1 ); + return; } s = g_strdup( u->handle ); @@ -513,10 +475,10 @@ int cmd_remove( irc_t *irc, char **cmd ) irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] ); g_free( s ); - return( 0 ); + return; } -int cmd_block( irc_t *irc, char **cmd ) +static void cmd_block( irc_t *irc, char **cmd ) { struct gaim_connection *gc; account_t *a; @@ -527,7 +489,7 @@ int cmd_block( irc_t *irc, char **cmd ) if( !u || !u->gc ) { irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); - return( 1 ); + return; } gc = u->gc; cmd[2] = u->handle; @@ -535,12 +497,12 @@ int cmd_block( irc_t *irc, char **cmd ) else if( !( a = account_get( irc, cmd[1] ) ) ) { irc_usermsg( irc, "Invalid account" ); - return( 1 ); + return; } else if( !( ( gc = a->gc ) && ( a->gc->flags & OPT_LOGGED_IN ) ) ) { irc_usermsg( irc, "That account is not on-line" ); - return( 1 ); + return; } if( !gc->prpl->add_deny || !gc->prpl->rem_permit ) @@ -553,11 +515,9 @@ int cmd_block( irc_t *irc, char **cmd ) gc->prpl->add_deny( gc, cmd[2] ); irc_usermsg( irc, "Buddy `%s' moved from your permit- to your deny-list", cmd[2] ); } - - return( 0 ); } -int cmd_allow( irc_t *irc, char **cmd ) +static void cmd_allow( irc_t *irc, char **cmd ) { struct gaim_connection *gc; account_t *a; @@ -568,7 +528,7 @@ int cmd_allow( irc_t *irc, char **cmd ) if( !u || !u->gc ) { irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); - return( 1 ); + return; } gc = u->gc; cmd[2] = u->handle; @@ -576,12 +536,12 @@ int cmd_allow( irc_t *irc, char **cmd ) else if( !( a = account_get( irc, cmd[1] ) ) ) { irc_usermsg( irc, "Invalid account" ); - return( 1 ); + return; } else if( !( ( gc = a->gc ) && ( a->gc->flags & OPT_LOGGED_IN ) ) ) { irc_usermsg( irc, "That account is not on-line" ); - return( 1 ); + return; } if( !gc->prpl->rem_deny || !gc->prpl->add_permit ) @@ -595,11 +555,9 @@ int cmd_allow( irc_t *irc, char **cmd ) irc_usermsg( irc, "Buddy `%s' moved from your deny- to your permit-list", cmd[2] ); } - - return( 0 ); } -int cmd_yesno( irc_t *irc, char **cmd ) +static void cmd_yesno( irc_t *irc, char **cmd ) { query_t *q = NULL; int numq = 0; @@ -607,7 +565,7 @@ int cmd_yesno( irc_t *irc, char **cmd ) if( irc->queries == NULL ) { irc_usermsg( irc, "Did I ask you something?" ); - return( 0 ); + return; } /* If there's an argument, the user seems to want to answer another question than the @@ -617,7 +575,7 @@ int cmd_yesno( irc_t *irc, char **cmd ) if( sscanf( cmd[1], "%d", &numq ) != 1 ) { irc_usermsg( irc, "Invalid query number" ); - return( 0 ); + return; } for( q = irc->queries; q; q = q->next, numq -- ) @@ -627,7 +585,7 @@ int cmd_yesno( irc_t *irc, char **cmd ) if( !q ) { irc_usermsg( irc, "Uhm, I never asked you something like that..." ); - return( 0 ); + return; } } @@ -635,15 +593,16 @@ int cmd_yesno( irc_t *irc, char **cmd ) query_answer( irc, q, 1 ); else if( g_strcasecmp( cmd[0], "no" ) == 0 ) query_answer( irc, q, 0 ); - - return( 1 ); } -int cmd_set( irc_t *irc, char **cmd ) +static void cmd_set( irc_t *irc, char **cmd ) { if( cmd[1] && cmd[2] ) { set_setstr( irc, cmd[1], cmd[2] ); + + if( ( strcmp( cmd[2], "=" ) ) == 0 && cmd[3] ) + irc_usermsg( irc, "Warning: Correct syntax: \002set <variable> <value>\002 (without =)" ); } if( cmd[1] ) /* else 'forgotten' on purpose.. Must show new value after changing */ { @@ -661,21 +620,17 @@ int cmd_set( irc_t *irc, char **cmd ) s = s->next; } } - - return( 0 ); } -int cmd_save( irc_t *irc, char **cmd ) +static void cmd_save( irc_t *irc, char **cmd ) { if( storage_save( irc, TRUE ) == STORAGE_OK ) irc_usermsg( irc, "Configuration saved" ); else irc_usermsg( irc, "Configuration could not be saved!" ); - - return( 0 ); } -int cmd_blist( irc_t *irc, char **cmd ) +static void cmd_blist( irc_t *irc, char **cmd ) { int online = 0, away = 0, offline = 0; user_t *u; @@ -717,11 +672,9 @@ int cmd_blist( irc_t *irc, char **cmd ) } irc_usermsg( irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online, n_away, n_offline ); - - return( 0 ); } -int cmd_nick( irc_t *irc, char **cmd ) +static void cmd_nick( irc_t *irc, char **cmd ) { account_t *a; @@ -753,11 +706,9 @@ int cmd_nick( irc_t *irc, char **cmd ) else a->gc->prpl->set_info( a->gc, cmd[2] ); } - - return( 1 ); } -int cmd_qlist( irc_t *irc, char **cmd ) +static void cmd_qlist( irc_t *irc, char **cmd ) { query_t *q = irc->queries; int num; @@ -765,7 +716,7 @@ int cmd_qlist( irc_t *irc, char **cmd ) if( !q ) { irc_usermsg( irc, "There are no pending questions." ); - return( 0 ); + return; } irc_usermsg( irc, "Pending queries:" ); @@ -775,11 +726,9 @@ int cmd_qlist( irc_t *irc, char **cmd ) irc_usermsg( irc, "%d, %s(%s): %s", num, q->gc->prpl->name, q->gc->username, q->question ); else irc_usermsg( irc, "%d, BitlBee: %s", num, q->question ); - - return( 0 ); } -int cmd_import_buddies( irc_t *irc, char **cmd ) +static void cmd_import_buddies( irc_t *irc, char **cmd ) { struct gaim_connection *gc; account_t *a; @@ -788,12 +737,12 @@ int cmd_import_buddies( irc_t *irc, char **cmd ) if( !( a = account_get( irc, cmd[1] ) ) ) { irc_usermsg( irc, "Invalid account" ); - return( 0 ); + return; } else if( !( ( gc = a->gc ) && ( a->gc->flags & OPT_LOGGED_IN ) ) ) { irc_usermsg( irc, "That account is not on-line" ); - return( 0 ); + return; } if( cmd[2] ) @@ -814,7 +763,7 @@ int cmd_import_buddies( irc_t *irc, char **cmd ) else { irc_usermsg( irc, "Invalid argument: %s", cmd[2] ); - return( 0 ); + return; } } @@ -828,6 +777,27 @@ int cmd_import_buddies( irc_t *irc, char **cmd ) } irc_usermsg( irc, "Sent all add requests. Please wait for a while, the server needs some time to handle all the adds." ); - - return( 0 ); } + +const command_t commands[] = { + { "help", 0, cmd_help, 0 }, + { "identify", 1, cmd_identify, 0 }, + { "register", 1, cmd_register, 0 }, + { "drop", 1, cmd_drop, 0 }, + { "account", 1, cmd_account, 0 }, + { "add", 2, cmd_add, 0 }, + { "info", 1, cmd_info, 0 }, + { "rename", 2, cmd_rename, 0 }, + { "remove", 1, cmd_remove, 0 }, + { "block", 1, cmd_block, 0 }, + { "allow", 1, cmd_allow, 0 }, + { "save", 0, cmd_save, 0 }, + { "set", 0, cmd_set, 0 }, + { "yes", 0, cmd_yesno, 0 }, + { "no", 0, cmd_yesno, 0 }, + { "blist", 0, cmd_blist, 0 }, + { "nick", 1, cmd_nick, 0 }, + { "import_buddies", 1, cmd_import_buddies, 0 }, + { "qlist", 0, cmd_qlist, 0 }, + { NULL } +}; @@ -28,6 +28,7 @@ #include "crypting.h" #include "protocols/nogaim.h" #include "help.h" +#include "ipc.h" #include <signal.h> #include <unistd.h> #include <sys/time.h> @@ -37,9 +38,10 @@ global_t global; /* Against global namespace pollution */ static void sighandler( int signal ); -int main( int argc, char *argv[] ) +int main( int argc, char *argv[], char **envp ) { int i = 0; + char *old_cwd = NULL; struct sigaction sig, old; memset( &global, 0, sizeof( global_t ) ); @@ -78,6 +80,15 @@ int main( int argc, char *argv[] ) } else if( global.conf->runmode == RUNMODE_FORKDAEMON ) { + /* In case the operator requests a restart, we need this. */ + old_cwd = g_malloc( 256 ); + if( getcwd( old_cwd, 255 ) == NULL ) + { + log_message( LOGLVL_WARNING, "Could not save current directory: %s", strerror( errno ) ); + g_free( old_cwd ); + old_cwd = NULL; + } + i = bitlbee_daemon_init(); log_message( LOGLVL_INFO, "Bitlbee %s starting in forking daemon mode.", BITLBEE_VERSION ); } @@ -113,6 +124,35 @@ int main( int argc, char *argv[] ) g_main_run( global.loop ); + if( global.restart ) + { + char *fn = ipc_master_save_state(); + char **args; + int n, i; + + chdir( old_cwd ); + + n = 0; + args = g_new0( char *, argc + 3 ); + args[n++] = argv[0]; + if( fn ) + { + args[n++] = "-R"; + args[n++] = fn; + } + for( i = 1; argv[i] && i < argc; i ++ ) + { + if( strcmp( argv[i], "-R" ) == 0 ) + i += 2; + + args[n++] = argv[i]; + } + + close( global.listen_socket ); + + execve( args[0], args, envp ); + } + return( 0 ); } @@ -39,10 +39,10 @@ int url_set( url_t *url, char *set_url ) } else { - if( g_strncasecmp( set_url, "https", i - set_url ) == 0 ) - url->proto = PROTO_HTTPS; - else if( g_strncasecmp( set_url, "http", i - set_url ) == 0 ) + if( g_strncasecmp( set_url, "http", i - set_url ) == 0 ) url->proto = PROTO_HTTP; + else if( g_strncasecmp( set_url, "https", i - set_url ) == 0 ) + url->proto = PROTO_HTTPS; else if( g_strncasecmp( set_url, "socks4", i - set_url ) == 0 ) url->proto = PROTO_SOCKS4; else if( g_strncasecmp( set_url, "socks5", i - set_url ) == 0 ) @@ -44,7 +44,7 @@ typedef struct __USER guint sendbuf_timer; int sendbuf_flags; - int (*send_handler) ( irc_t *irc, struct __USER *u, char *msg, int flags ); + void (*send_handler) ( irc_t *irc, struct __USER *u, char *msg, int flags ); struct __USER *next; } user_t; diff --git a/utils/bitlbee-ctl.pl b/utils/bitlbee-ctl.pl new file mode 100755 index 00000000..32f0a81e --- /dev/null +++ b/utils/bitlbee-ctl.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl +# Simple front-end to BitlBee's administration commands +# Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org> + +use IO::Socket; +use Getopt::Long; +use strict; +use warnings; + +my $opt_help; +my $opt_socketfile = "/var/run/bitlbee"; + +sub ShowHelp +{ + print +"bitlbee-ctl.pl [options] command ... + +Available options: + + --ipc-socket=SOCKET Override path to IPC socket [$opt_socketfile] + --help Show this help message + +Available commands: + + die + +"; + exit (0); +} + +GetOptions ( + 'help|h|?' => \&ShowHelp, + 'ipc-socket=s' => \$opt_socketfile + ) or exit(1); + +my $client = IO::Socket::UNIX->new(Peer => $opt_socketfile, + Type => SOCK_STREAM, + Timeout => 10); + +if (not $client) { + print "Error connecting to $opt_socketfile: $@\n"; + exit(1); +} + +my $cmd = shift @ARGV; + +if (not defined($cmd)) { + print "Usage: bitlbee-ctl.pl [options] command ...\n"; + exit(1); +} + +if ($cmd eq "die") { + $client->send("DIE\r\n"); +} else { + print "No such command: $cmd\n"; + exit(1); +} + +$client->close(); |