From 3ddb7477f51d3cf1632e2a8b6f7da4c0609a52cb Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 26 Mar 2010 08:14:37 -0400 Subject: One total mess that doesn't do much yet, but reorganised some stuff and untying the IRC and the core parts a little bit. Lots of work left to do. --- Makefile | 3 +- account.c | 359 ------------------- account.h | 72 ---- bitlbee.h | 3 +- chat.c | 192 ----------- chat.h | 51 --- conf.c | 2 +- ipc.c | 12 +- irc.c | 969 +++++++--------------------------------------------- irc.h | 93 +++-- irc_commands.c | 115 ++++--- nick.c | 4 +- protocols/Makefile | 4 +- protocols/account.c | 359 +++++++++++++++++++ protocols/account.h | 72 ++++ protocols/chat.c | 192 +++++++++++ protocols/chat.h | 51 +++ protocols/user.c | 106 ++++++ protocols/user.h | 40 +++ set.c | 2 + unix.c | 4 +- user.c | 231 ------------- user.h | 62 ---- 23 files changed, 1067 insertions(+), 1931 deletions(-) delete mode 100644 account.c delete mode 100644 account.h delete mode 100644 chat.c delete mode 100644 chat.h create mode 100644 protocols/account.c create mode 100644 protocols/account.h create mode 100644 protocols/chat.c create mode 100644 protocols/chat.h create mode 100644 protocols/user.c create mode 100644 protocols/user.h delete mode 100644 user.c delete mode 100644 user.h diff --git a/Makefile b/Makefile index ef34f123..ecd133e2 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,8 @@ -include Makefile.settings # Program variables -objects = account.o bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o +#objects = bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) +objects = bitlbee.o help.o ipc.o irc.o irc_commands.o irc_send.o irc_user.o nick.o set.o headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols diff --git a/account.c b/account.c deleted file mode 100644 index a844d229..00000000 --- a/account.c +++ /dev/null @@ -1,359 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2010 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Account management functions */ - -/* - 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 "account.h" -#include "chat.h" - -account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) -{ - account_t *a; - set_t *s; - - if( irc->accounts ) - { - for( a = irc->accounts; a->next; a = a->next ); - a = a->next = g_new0( account_t, 1 ); - } - else - { - irc->accounts = a = g_new0 ( account_t, 1 ); - } - - a->prpl = prpl; - a->user = g_strdup( user ); - a->pass = g_strdup( pass ); - a->auto_connect = 1; - a->irc = irc; - - s = set_add( &a->set, "auto_connect", "true", set_eval_account, a ); - s->flags |= ACC_SET_NOSAVE; - - s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a ); - - s = set_add( &a->set, "nick_source", "handle", NULL, a ); - - s = set_add( &a->set, "password", NULL, set_eval_account, a ); - s->flags |= ACC_SET_NOSAVE | SET_NULL_OK; - - s = set_add( &a->set, "username", NULL, set_eval_account, a ); - s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; - set_setstr( &a->set, "username", user ); - - a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free ); - - /* This function adds some more settings (and might want to do more - things that have to be done now, although I can't think of anything. */ - if( prpl->init ) - prpl->init( a ); - - s = set_add( &a->set, "away", NULL, set_eval_account, a ); - s->flags |= SET_NULL_OK; - - if( a->flags & ACC_FLAG_STATUS_MESSAGE ) - { - s = set_add( &a->set, "status", NULL, set_eval_account, a ); - s->flags |= SET_NULL_OK; - } - - return a; -} - -char *set_eval_account( set_t *set, char *value ) -{ - account_t *acc = set->data; - - /* Double-check: We refuse to edit on-line accounts. */ - if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic ) - return SET_INVALID; - - if( strcmp( set->key, "server" ) == 0 ) - { - g_free( acc->server ); - if( value && *value ) - { - acc->server = g_strdup( value ); - return value; - } - else - { - acc->server = g_strdup( set->def ); - return g_strdup( set->def ); - } - } - else if( strcmp( set->key, "username" ) == 0 ) - { - g_free( acc->user ); - acc->user = g_strdup( value ); - return value; - } - else if( strcmp( set->key, "password" ) == 0 ) - { - if( value ) - { - g_free( acc->pass ); - acc->pass = g_strdup( value ); - return NULL; /* password shouldn't be visible in plaintext! */ - } - else - { - /* NULL can (should) be stored in the set_t - variable, but is otherwise not correct. */ - return SET_INVALID; - } - } - else if( strcmp( set->key, "auto_connect" ) == 0 ) - { - if( !is_bool( value ) ) - return SET_INVALID; - - acc->auto_connect = bool2int( value ); - return value; - } - else if( strcmp( set->key, "away" ) == 0 || - strcmp( set->key, "status" ) == 0 ) - { - if( acc->ic && acc->ic->flags & OPT_LOGGED_IN ) - { - /* If we're currently on-line, set the var now already - (bit of a hack) and send an update. */ - g_free( set->value ); - set->value = g_strdup( value ); - - imc_away_send_update( acc->ic ); - } - - return value; - } - - return SET_INVALID; -} - -account_t *account_get( irc_t *irc, char *id ) -{ - account_t *a, *ret = NULL; - char *handle, *s; - int nr; - - /* This checks if the id string ends with (...) */ - if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 ) - { - struct prpl *proto; - - *s = *handle = 0; - handle ++; - - if( ( proto = find_protocol( id ) ) ) - { - for( a = irc->accounts; a; a = a->next ) - if( a->prpl == proto && - a->prpl->handle_cmp( handle, a->user ) == 0 ) - ret = a; - } - - /* Restore the string. */ - handle --; - *handle = '('; - *s = ')'; - - if( ret ) - return ret; - } - - if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) - { - for( a = irc->accounts; a; a = a->next ) - if( ( nr-- ) == 0 ) - return( a ); - - return( NULL ); - } - - for( a = irc->accounts; a; a = a->next ) - { - if( g_strcasecmp( id, a->prpl->name ) == 0 ) - { - if( !ret ) - ret = a; - else - return( NULL ); /* We don't want to match more than one... */ - } - else if( strstr( a->user, id ) ) - { - if( !ret ) - ret = a; - else - return( NULL ); - } - } - - return( ret ); -} - -void account_del( irc_t *irc, account_t *acc ) -{ - account_t *a, *l = NULL; - struct chat *c, *nc; - - if( acc->ic ) - /* Caller should have checked, accounts still in use can't be deleted. */ - return; - - for( a = irc->accounts; a; a = (l=a)->next ) - if( a == acc ) - { - if( l ) - l->next = a->next; - else - irc->accounts = a->next; - - for( c = irc->chatrooms; c; c = nc ) - { - nc = c->next; - if( acc == c->acc ) - chat_del( irc, c ); - } - - while( a->set ) - set_del( &a->set, a->set->key ); - - g_hash_table_destroy( a->nicks ); - - g_free( a->user ); - g_free( a->pass ); - g_free( a->server ); - if( a->reconnect ) /* This prevents any reconnect still queued to happen */ - cancel_auto_reconnect( a ); - g_free( a ); - - break; - } -} - -void account_on( irc_t *irc, account_t *a ) -{ - if( a->ic ) - { - /* Trying to enable an already-enabled account */ - return; - } - - cancel_auto_reconnect( a ); - - a->reconnect = 0; - a->prpl->login( a ); -} - -void account_off( irc_t *irc, account_t *a ) -{ - imc_logout( a->ic, FALSE ); - a->ic = NULL; - if( a->reconnect ) - { - /* Shouldn't happen */ - cancel_auto_reconnect( a ); - } -} - -struct account_reconnect_delay -{ - int start; - char op; - int step; - int max; -}; - -int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p ) -{ - memset( p, 0, sizeof( *p ) ); - /* A whole day seems like a sane "maximum maximum". */ - p->max = 86400; - - /* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */ - while( *value && isdigit( *value ) ) - p->start = p->start * 10 + *value++ - '0'; - - /* Sure, call me evil for implementing my own fscanf here, but it's - dead simple and I immediately know where to continue parsing. */ - - if( *value == 0 ) - /* If the string ends now, the delay is constant. */ - return 1; - else if( *value != '+' && *value != '*' ) - /* Otherwise allow either a + or a * */ - return 0; - - p->op = *value++; - - /* + or * the delay by this number every time. */ - while( *value && isdigit( *value ) ) - p->step = p->step * 10 + *value++ - '0'; - - if( *value == 0 ) - /* Use the default maximum (one day). */ - return 1; - else if( *value != '<' ) - return 0; - - p->max = 0; - value ++; - while( *value && isdigit( *value ) ) - p->max = p->max * 10 + *value++ - '0'; - - return p->max > 0; -} - -char *set_eval_account_reconnect_delay( set_t *set, char *value ) -{ - struct account_reconnect_delay p; - - return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID; -} - -int account_reconnect_delay( account_t *a ) -{ - char *setting = set_getstr( &a->irc->set, "auto_reconnect_delay" ); - struct account_reconnect_delay p; - - if( account_reconnect_delay_parse( setting, &p ) ) - { - if( a->auto_reconnect_delay == 0 ) - a->auto_reconnect_delay = p.start; - else if( p.op == '+' ) - a->auto_reconnect_delay += p.step; - else if( p.op == '*' ) - a->auto_reconnect_delay *= p.step; - - if( a->auto_reconnect_delay > p.max ) - a->auto_reconnect_delay = p.max; - } - else - { - a->auto_reconnect_delay = 0; - } - - return a->auto_reconnect_delay; -} diff --git a/account.h b/account.h deleted file mode 100644 index 984dcfe6..00000000 --- a/account.h +++ /dev/null @@ -1,72 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Account management functions */ - -/* - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License with - the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; - if not, write to the Free Software Foundation, Inc., 59 Temple Place, - Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef _ACCOUNT_H -#define _ACCOUNT_H - -typedef struct account -{ - struct prpl *prpl; - char *user; - char *pass; - char *server; - - int auto_connect; - int auto_reconnect_delay; - int reconnect; - int flags; - - set_t *set; - GHashTable *nicks; - - struct irc *irc; - struct im_connection *ic; - struct account *next; -} account_t; - -account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ); -account_t *account_get( irc_t *irc, char *id ); -void account_del( irc_t *irc, account_t *acc ); -void account_on( irc_t *irc, account_t *a ); -void account_off( irc_t *irc, account_t *a ); - -char *set_eval_account( set_t *set, char *value ); -char *set_eval_account_reconnect_delay( set_t *set, char *value ); -int account_reconnect_delay( account_t *a ); - -typedef enum -{ - ACC_SET_NOSAVE = 0x01, /* Don't save this setting (i.e. stored elsewhere). */ - ACC_SET_OFFLINE_ONLY = 0x02, /* Allow changes only if the acct is offline. */ - ACC_SET_ONLINE_ONLY = 0x04, /* Allow changes only if the acct is online. */ -} account_set_flag_t; - -typedef enum -{ - ACC_FLAG_AWAY_MESSAGE = 0x01, /* Supports away messages instead of just states. */ - ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */ -} account_flag_t; - -#endif diff --git a/bitlbee.h b/bitlbee.h index 5f98deef..a40c48e6 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -123,6 +123,7 @@ #define HELP_FILE VARDIR "help.txt" #define CONF_FILE_DEF ETCDIR "bitlbee.conf" +#include "bee.h" #include "irc.h" #include "storage.h" #include "set.h" @@ -157,7 +158,7 @@ int bitlbee_inetd_init( void ); gboolean bitlbee_io_current_client_read( gpointer data, gint source, b_input_condition cond ); gboolean bitlbee_io_current_client_write( gpointer data, gint source, b_input_condition cond ); -void 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 ); void root_command( irc_t *irc, char *command[] ); gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond ); diff --git a/chat.c b/chat.c deleted file mode 100644 index 8c5ce0bc..00000000 --- a/chat.c +++ /dev/null @@ -1,192 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2008 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Keep track of chatrooms the user is interested in */ - -/* - 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 -*/ - -#include "bitlbee.h" -#include "chat.h" - -struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ) -{ - struct chat *c, *l; - set_t *s; - - if( acc->prpl->chat_join == NULL || !chat_chanok( channel ) || - chat_chancmp( channel, irc->channel ) == 0 ) - { - return NULL; - } - - for( c = irc->chatrooms; c; c = c->next ) - { - if( chat_chancmp( channel, c->channel ) == 0 ) - return NULL; - - if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) - return NULL; - - l = c; - } - - if( irc->chatrooms == NULL ) - irc->chatrooms = c = g_new0( struct chat, 1 ); - else - l->next = c = g_new0( struct chat, 1 ); - - c->acc = acc; - c->handle = g_strdup( handle ); - c->channel = g_strdup( channel ); - - s = set_add( &c->set, "auto_join", "false", set_eval_bool, c ); - /* s = set_add( &c->set, "auto_rejoin", "false", set_eval_bool, c ); */ - s = set_add( &c->set, "nick", NULL, NULL, c ); - s->flags |= SET_NULL_OK; - - return c; -} - -struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ) -{ - struct chat *c; - - for( c = irc->chatrooms; c; c = c->next ) - { - if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) - break; - } - - return c; -} - -struct chat *chat_bychannel( irc_t *irc, char *channel ) -{ - struct chat *c; - - for( c = irc->chatrooms; c; c = c->next ) - { - if( chat_chancmp( channel, c->channel ) == 0 ) - break; - } - - return c; -} - -struct chat *chat_get( irc_t *irc, char *id ) -{ - struct chat *c, *ret = NULL; - int nr; - - if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) - { - for( c = irc->chatrooms; c; c = c->next ) - if( ( nr-- ) == 0 ) - return c; - - return NULL; - } - - for( c = irc->chatrooms; c; c = c->next ) - { - if( strstr( c->handle, id ) ) - { - if( !ret ) - ret = c; - else - return NULL; - } - else if( strstr( c->channel, id ) ) - { - if( !ret ) - ret = c; - else - return NULL; - } - } - - return ret; -} - -int chat_del( irc_t *irc, struct chat *chat ) -{ - struct chat *c, *l = NULL; - - for( c = irc->chatrooms; c; c = (l=c)->next ) - if( c == chat ) - break; - - if( c == NULL ) - return 0; - else if( l == NULL ) - irc->chatrooms = c->next; - else - l->next = c->next; - - while( c->set ) - set_del( &c->set, c->set->key ); - - g_free( c->handle ); - g_free( c->channel ); - g_free( c ); - - return 1; -} - -int chat_chancmp( char *a, char *b ) -{ - if( !chat_chanok( a ) || !chat_chanok( b ) ) - return 0; - - if( a[0] == b[0] ) - return nick_cmp( a + 1, b + 1 ); - else - return -1; -} - -int chat_chanok( char *a ) -{ - if( strchr( CTYPES, a[0] ) != NULL ) - return nick_ok( a + 1 ); - else - return 0; -} - -int chat_join( irc_t *irc, struct chat *c, const char *password ) -{ - struct groupchat *gc; - char *nick = set_getstr( &c->set, "nick" ); - - if( c->acc->ic == NULL || c->acc->prpl->chat_join == NULL ) - return 0; - - if( nick == NULL ) - nick = irc->nick; - - if( ( gc = c->acc->prpl->chat_join( c->acc->ic, c->handle, nick, password ) ) ) - { - g_free( gc->channel ); - gc->channel = g_strdup( c->channel ); - return 1; - } - - return 0; -} diff --git a/chat.h b/chat.h deleted file mode 100644 index 7196aea8..00000000 --- a/chat.h +++ /dev/null @@ -1,51 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2008 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Keep track of chatrooms the user is interested in */ - -/* - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License with - the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; - if not, write to the Free Software Foundation, Inc., 59 Temple Place, - Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef _CHAT_H -#define _CHAT_H - -struct chat -{ - account_t *acc; - - char *handle; - char *channel; - set_t *set; - - struct chat *next; -}; - -struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ); -struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ); -struct chat *chat_bychannel( irc_t *irc, char *channel ); -struct chat *chat_get( irc_t *irc, char *id ); -int chat_del( irc_t *irc, struct chat *chat ); - -int chat_chancmp( char *a, char *b ); -int chat_chanok( char *a ); - -int chat_join( irc_t *irc, struct chat *c, const char *password ); - -#endif diff --git a/conf.c b/conf.c index 8687afea..d4e482cb 100644 --- a/conf.c +++ b/conf.c @@ -368,7 +368,7 @@ void conf_loaddefaults( irc_t *irc ) { if( g_strcasecmp( ini->section, "defaults" ) == 0 ) { - set_t *s = set_find( &irc->set, ini->key ); + set_t *s = set_find( &irc->b->set, ini->key ); if( s ) { diff --git a/ipc.c b/ipc.c index d6b850f1..2d833354 100644 --- a/ipc.c +++ b/ipc.c @@ -137,7 +137,7 @@ static void ipc_child_cmd_wallops( irc_t *irc, char **cmd ) return; if( strchr( irc->umode, 'w' ) ) - irc_write( irc, ":%s WALLOPS :%s", irc->myhost, cmd[1] ); + irc_write( irc, ":%s WALLOPS :%s", irc->root->host, cmd[1] ); } static void ipc_child_cmd_wall( irc_t *irc, char **cmd ) @@ -146,7 +146,7 @@ static void ipc_child_cmd_wall( irc_t *irc, char **cmd ) return; if( strchr( irc->umode, 's' ) ) - irc_write( irc, ":%s NOTICE %s :%s", irc->myhost, irc->nick, cmd[1] ); + irc_write( irc, ":%s NOTICE %s :%s", irc->root->host, irc->user->nick, cmd[1] ); } static void ipc_child_cmd_opermsg( irc_t *irc, char **cmd ) @@ -155,7 +155,7 @@ static void ipc_child_cmd_opermsg( irc_t *irc, char **cmd ) return; if( strchr( irc->umode, 'o' ) ) - irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->myhost, irc->nick, cmd[1] ); + irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->root->host, irc->user->nick, cmd[1] ); } static void ipc_child_cmd_rehash( irc_t *irc, char **cmd ) @@ -175,10 +175,10 @@ 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 ) + if( nick_cmp( cmd[1], irc->user->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_write( irc, ":%s!%s@%s KILL %s :%s", irc->root->nick, irc->root->nick, irc->root->host, irc->user->nick, cmd[2] ); irc_abort( irc, 0, "Killed by operator: %s", cmd[2] ); } @@ -187,7 +187,7 @@ 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 ); + ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); } static const command_t ipc_child_commands[] = { diff --git a/irc.c b/irc.c index edb42dde..8e44fe9e 100644 --- a/irc.c +++ b/irc.c @@ -4,7 +4,7 @@ * Copyright 2002-2004 Wilmer van der Gaast and others * \********************************************************************/ -/* The big hairy IRCd part of the project */ +/* The IRC-based UI (for now the only one) */ /* This program is free software; you can redistribute it and/or modify @@ -23,85 +23,21 @@ Suite 330, Boston, MA 02111-1307 USA */ -#define BITLBEE_CORE #include "bitlbee.h" -#include "sock.h" -#include "ipc.h" -#include "dcc.h" -static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond ); +GSList *irc_connection_list; -GSList *irc_connection_list = NULL; - -static char *set_eval_password( set_t *set, char *value ) -{ - irc_t *irc = set->data; - - if( irc->status & USTATUS_IDENTIFIED && value ) - { - irc_setpass( irc, value ); - return NULL; - } - else - { - return SET_INVALID; - } -} - -static char *set_eval_charset( set_t *set, char *value ) -{ - irc_t *irc = set->data; - GIConv ic, oc; - - if( g_strcasecmp( value, "none" ) == 0 ) - value = g_strdup( "utf-8" ); - - if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) - { - return NULL; - } - if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 ) - { - g_iconv_close( ic ); - return NULL; - } - - if( irc->iconv != (GIConv) -1 ) - g_iconv_close( irc->iconv ); - if( irc->oconv != (GIConv) -1 ) - g_iconv_close( irc->oconv ); - - irc->iconv = ic; - irc->oconv = oc; - - return value; -} - -static char *set_eval_away_status( set_t *set, char *value ) -{ - irc_t *irc = set->data; - account_t *a; - - g_free( set->value ); - set->value = g_strdup( value ); - - for( a = irc->accounts; a; a = a->next ) - { - struct im_connection *ic = a->ic; - - if( ic && ic->flags & OPT_LOGGED_IN ) - imc_away_send_update( ic ); - } - - return value; -} +static char *set_eval_charset( set_t *set, char *value ); irc_t *irc_new( int fd ) { irc_t *irc; struct sockaddr_storage sock; socklen_t socklen = sizeof( sock ); + char *host = NULL, *myhost = NULL; + irc_user_t *iu; set_t *s; + bee_t *b; irc = g_new0( irc_t, 1 ); @@ -113,19 +49,17 @@ irc_t *irc_new( int fd ) irc->status = USTATUS_OFFLINE; irc->last_pong = gettime(); - irc->userhash = g_hash_table_new( g_str_hash, g_str_equal ); + irc->nick_user_hash = g_hash_table_new( g_str_hash, g_str_equal ); irc->watches = g_hash_table_new( g_str_hash, g_str_equal ); strcpy( irc->umode, UMODE ); - irc->mynick = g_strdup( ROOT_NICK ); - irc->channel = g_strdup( ROOT_CHAN ); irc->iconv = (GIConv) -1; irc->oconv = (GIConv) -1; if( global.conf->hostname ) { - irc->myhost = g_strdup( global.conf->hostname ); + myhost = g_strdup( global.conf->hostname ); } else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 ) { @@ -134,7 +68,7 @@ irc_t *irc_new( int fd ) if( getnameinfo( (struct sockaddr *) &sock, socklen, buf, NI_MAXHOST, NULL, 0, 0 ) == 0 ) { - irc->myhost = g_strdup( ipv6_unwrap( buf ) ); + myhost = g_strdup( ipv6_unwrap( buf ) ); } } @@ -145,57 +79,58 @@ irc_t *irc_new( int fd ) if( getnameinfo( (struct sockaddr *)&sock, socklen, buf, NI_MAXHOST, NULL, 0, 0 ) == 0 ) { - irc->host = g_strdup( ipv6_unwrap( buf ) ); + host = g_strdup( ipv6_unwrap( buf ) ); } } - if( irc->host == NULL ) - irc->host = g_strdup( "localhost.localdomain" ); - if( irc->myhost == NULL ) - irc->myhost = g_strdup( "localhost.localdomain" ); + if( host == NULL ) + host = g_strdup( "localhost.localdomain" ); + if( myhost == NULL ) + myhost = g_strdup( "localhost.localdomain" ); - if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 ) - irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc ); - - irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" ); + //if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 ) + // irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc ); irc_connection_list = g_slist_append( irc_connection_list, irc ); - s = set_add( &irc->set, "away", NULL, set_eval_away_status, irc ); - s->flags |= SET_NULL_OK; - s = set_add( &irc->set, "away_devoice", "true", set_eval_away_devoice, irc ); - s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc ); - s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); - s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc ); - s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc ); - s = set_add( &irc->set, "control_channel", irc->channel, set_eval_control_channel, irc ); - s = set_add( &irc->set, "debug", "false", set_eval_bool, irc ); - s = set_add( &irc->set, "default_target", "root", NULL, irc ); - s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc ); - s = set_add( &irc->set, "handle_unknown", "root", NULL, irc ); - s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "ops", "both", set_eval_ops, irc ); - s = set_add( &irc->set, "password", NULL, set_eval_password, irc ); - s->flags |= SET_NULL_OK; - s = set_add( &irc->set, "private", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "query_order", "lifo", NULL, irc ); - s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc ); - s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc ); - s = set_add( &irc->set, "status", NULL, set_eval_away_status, irc ); - s->flags |= SET_NULL_OK; - s = set_add( &irc->set, "strip_html", "true", NULL, irc ); - s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc ); - s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc ); - - conf_loaddefaults( irc ); + b = irc->b = bee_new(); + + s = set_add( &b->set, "away_devoice", "true", NULL/*set_eval_away_devoice*/, irc ); + s = set_add( &b->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); + s = set_add( &b->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc ); + s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc ); + //s = set_add( &b->set, "control_channel", irc->channel, NULL/*set_eval_control_channel*/, irc ); + s = set_add( &b->set, "default_target", "root", NULL, irc ); + s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc ); + s = set_add( &b->set, "handle_unknown", "root", NULL, irc ); + s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc ); + s = set_add( &b->set, "ops", "both", NULL/*set_eval_ops*/, irc ); + s = set_add( &b->set, "private", "true", set_eval_bool, irc ); + s = set_add( &b->set, "query_order", "lifo", NULL, irc ); + s = set_add( &b->set, "root_nick", ROOT_NICK, NULL/*set_eval_root_nick*/, irc ); + s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc ); + s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc ); + s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc ); + + irc->root = iu = irc_user_new( irc, ROOT_NICK ); + iu->host = g_strdup( myhost ); + iu->fullname = g_strdup( ROOT_FN ); + + iu = irc_user_new( irc, NS_NICK ); + iu->host = g_strdup( myhost ); + iu->fullname = g_strdup( ROOT_FN ); + + irc->user = g_new0( irc_user_t, 1 ); + irc->user->host = g_strdup( host ); + + conf_loaddefaults( b ); /* Evaluator sets the iconv/oconv structures. */ - set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) ); + set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) ); - return( irc ); + irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" ); + + return irc; } /* immed=1 makes this function pretty much equal to irc_free(), except that @@ -216,7 +151,7 @@ void irc_abort( irc_t *irc, int immed, char *format, ... ) 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 ); + irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, reason ); g_free( reason ); } @@ -226,7 +161,7 @@ void irc_abort( irc_t *irc, int immed, char *format, ... ) 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->user->nick ? irc->user->nick : "(NONE)", irc->root->host, "No reason given" ); } irc->status |= USTATUS_SHUTDOWN; @@ -247,65 +182,27 @@ void irc_abort( irc_t *irc, int immed, char *format, ... ) } } -static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ) -{ - g_free( key ); - - return( TRUE ); -} +static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ); -/* Because we have no garbage collection, this is quite annoying */ void irc_free( irc_t * irc ) { - user_t *user, *usertmp; - log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); - if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) ) + /* + if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) ) if( storage_save( irc, NULL, TRUE ) != STORAGE_OK ) irc_usermsg( irc, "Error while saving settings!" ); + */ irc_connection_list = g_slist_remove( irc_connection_list, irc ); - while( irc->accounts ) - { - if( irc->accounts->ic ) - imc_logout( irc->accounts->ic, FALSE ); - else if( irc->accounts->reconnect ) - cancel_auto_reconnect( irc->accounts ); - - if( irc->accounts->ic == NULL ) - account_del( irc, irc->accounts ); - else - /* Nasty hack, but account_del() doesn't work in this - case and we don't want infinite loops, do we? ;-) */ - irc->accounts = irc->accounts->next; - } - + /* while( irc->queries != NULL ) query_del( irc, irc->queries ); + */ - while( irc->set ) - set_del( &irc->set, irc->set->key ); - - if (irc->users != NULL) - { - user = irc->users; - while( user != NULL ) - { - g_free( user->nick ); - g_free( user->away ); - g_free( user->handle ); - if( user->user != user->nick ) g_free( user->user ); - if( user->host != user->nick ) g_free( user->host ); - if( user->realname != user->nick ) g_free( user->realname ); - b_event_remove( user->sendbuf_timer ); - - usertmp = user; - user = user->next; - g_free( usertmp ); - } - } + while( irc->users ) + irc_user_free( irc, irc->users->data ); if( irc->ping_source_id > 0 ) b_event_remove( irc->ping_source_id ); @@ -317,8 +214,8 @@ void irc_free( irc_t * irc ) closesocket( irc->fd ); irc->fd = -1; - g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL ); - g_hash_table_destroy( irc->userhash ); + g_hash_table_foreach_remove( irc->nick_user_hash, irc_free_hashkey, NULL ); + g_hash_table_destroy( irc->nick_user_hash ); g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL ); g_hash_table_destroy( irc->watches ); @@ -331,19 +228,8 @@ void irc_free( irc_t * irc ) g_free( irc->sendbuffer ); g_free( irc->readbuffer ); - g_free( irc->nick ); - g_free( irc->user ); - g_free( irc->host ); - g_free( irc->realname ); g_free( irc->password ); - g_free( irc->myhost ); - g_free( irc->mynick ); - - g_free( irc->channel ); - - g_free( irc->last_target ); - g_free( irc ); if( global.conf->runmode == RUNMODE_INETD || @@ -354,19 +240,15 @@ void irc_free( irc_t * irc ) b_main_quit(); } -/* USE WITH CAUTION! - Sets pass without checking */ -void irc_setpass (irc_t *irc, const char *pass) +static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data ) { - g_free (irc->password); + g_free( key ); - if (pass) { - irc->password = g_strdup (pass); - } else { - irc->password = NULL; - } + return( TRUE ); } +static char **irc_splitlines( char *buffer ); + void irc_process( irc_t *irc ) { char **lines, *temp, **cmd; @@ -374,7 +256,7 @@ void irc_process( irc_t *irc ) if( irc->readbuffer != NULL ) { - lines = irc_tokenize( irc->readbuffer ); + lines = irc_splitlines( irc->readbuffer ); for( i = 0; *lines[i] != '\0'; i ++ ) { @@ -411,14 +293,14 @@ void irc_process( irc_t *irc ) "expect by changing the charset setting. See " "`help set charset' for more information. Your " "message was ignored.", - set_getstr( &irc->set, "charset" ) ); + set_getstr( &irc->b->set, "charset" ) ); g_free( conv ); conv = NULL; } else { - irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, + irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "Warning: invalid characters received at login time." ); conv = g_strdup( lines[i] ); @@ -456,9 +338,11 @@ void irc_process( irc_t *irc ) } } -/* Splits a long string into separate lines. The array is NULL-terminated and, unless the string - contains an incomplete line at the end, ends with an empty string. */ -char **irc_tokenize( char *buffer ) +/* Splits a long string into separate lines. The array is NULL-terminated + and, unless the string contains an incomplete line at the end, ends with + an empty string. Could use g_strsplit() but this one does it in-place. + (So yes, it's destructive.) */ +static char **irc_splitlines( char *buffer ) { int i, j, n = 3; char **lines; @@ -589,46 +473,45 @@ char *irc_build_line( char **cmd ) return s; } -void irc_reply( irc_t *irc, int code, char *format, ... ) +void irc_write( irc_t *irc, char *format, ... ) { - char text[IRC_MAX_LINE]; va_list params; - + va_start( params, format ); - g_vsnprintf( text, IRC_MAX_LINE, format, params ); + irc_vawrite( irc, format, params ); va_end( params ); - irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text ); - + return; } -int irc_usermsg( irc_t *irc, char *format, ... ) +void irc_write_all( int now, char *format, ... ) { - char text[1024]; va_list params; - char is_private = 0; - user_t *u; - - u = user_find( irc, irc->mynick ); - is_private = u->is_private; + GSList *temp; va_start( params, format ); - g_vsnprintf( text, sizeof( text ), format, params ); - va_end( params ); - return( irc_msgfrom( irc, u->nick, text ) ); -} - -void irc_write( irc_t *irc, char *format, ... ) -{ - va_list params; - - va_start( params, format ); - irc_vawrite( irc, format, params ); + temp = irc_connection_list; + while( temp != NULL ) + { + irc_t *irc = temp->data; + + if( now ) + { + g_free( irc->sendbuffer ); + irc->sendbuffer = g_strdup( "\r\n" ); + } + irc_vawrite( temp->data, format, params ); + if( now ) + { + bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ); + } + temp = temp->next; + } + va_end( params ); - return; -} +} void irc_vawrite( irc_t *irc, char *format, va_list params ) { @@ -685,105 +568,18 @@ void irc_vawrite( irc_t *irc, char *format, va_list params ) return; } -void irc_write_all( int now, char *format, ... ) -{ - va_list params; - GSList *temp; - - va_start( params, format ); - - temp = irc_connection_list; - while( temp != NULL ) - { - irc_t *irc = temp->data; - - if( now ) - { - g_free( irc->sendbuffer ); - irc->sendbuffer = g_strdup( "\r\n" ); - } - irc_vawrite( temp->data, format, params ); - if( now ) - { - bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ); - } - temp = temp->next; - } - - va_end( params ); - return; -} - -void irc_names( irc_t *irc, char *channel ) -{ - user_t *u; - char namelist[385] = ""; - struct groupchat *c = NULL; - char *ops = set_getstr( &irc->set, "ops" ); - - /* RFCs say there is no error reply allowed on NAMES, so when the - channel is invalid, just give an empty reply. */ - - if( g_strcasecmp( channel, irc->channel ) == 0 ) - { - for( u = irc->users; u; u = u->next ) if( u->online ) - { - if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 ) - { - irc_reply( irc, 353, "= %s :%s", channel, namelist ); - *namelist = 0; - } - - if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) ) - strcat( namelist, "+" ); - else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) || - ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ) - strcat( namelist, "@" ); - - strcat( namelist, u->nick ); - strcat( namelist, " " ); - } - } - else if( ( c = irc_chat_by_channel( irc, channel ) ) ) - { - GList *l; - - /* root and the user aren't in the channel userlist but should - show up in /NAMES, so list them first: */ - sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick, - strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick ); - - for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) ) - { - if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 ) - { - irc_reply( irc, 353, "= %s :%s", channel, namelist ); - *namelist = 0; - } - - strcat( namelist, u->nick ); - strcat( namelist, " " ); - } - } - - if( *namelist ) - irc_reply( irc, 353, "= %s :%s", channel, namelist ); - - irc_reply( irc, 366, "%s :End of /NAMES list", channel ); -} - int irc_check_login( irc_t *irc ) { - if( irc->user && irc->nick ) + if( irc->user->user && irc->user->nick ) { if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) ) { - irc_reply( irc, 464, ":This server is password-protected." ); + irc_send_num( irc, 464, ":This server is password-protected." ); return 0; } else { - irc_login( irc ); + irc_send_login( irc ); return 1; } } @@ -794,556 +590,33 @@ int irc_check_login( irc_t *irc ) } } -void irc_login( irc_t *irc ) -{ - user_t *u; - - irc_reply( irc, 1, ":Welcome to the BitlBee gateway, %s", irc->nick ); - irc_reply( irc, 2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost ); - irc_reply( irc, 3, ":%s", IRCD_INFO ); - irc_reply( irc, 4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES ); - irc_reply( irc, 5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee " - "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", - CTYPES, CMODES, MAX_NICK_LENGTH - 1 ); - irc_motd( irc ); - irc->umode[0] = '\0'; - irc_umode_set( irc, "+" UMODE, 1 ); - u = user_add( irc, irc->mynick ); - u->host = g_strdup( irc->myhost ); - u->realname = g_strdup( ROOT_FN ); - u->online = 1; - u->send_handler = root_command_string; - u->is_private = 0; /* [SH] The channel is root's personal playground. */ - irc_spawn( irc, u ); - - u = user_add( irc, NS_NICK ); - u->host = g_strdup( irc->myhost ); - u->realname = g_strdup( ROOT_FN ); - u->online = 0; - u->send_handler = root_command_string; - u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */ - - u = user_add( irc, irc->nick ); - u->user = g_strdup( irc->user ); - u->host = g_strdup( irc->host ); - u->realname = g_strdup( irc->realname ); - u->online = 1; - irc_spawn( irc, u ); - - irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n" - "If you've never used BitlBee before, please do read the help " - "information using the \x02help\x02 command. Lots of FAQs are " - "answered there.\n" - "If you already have an account on this server, just use the " - "\x02identify\x02 command to identify yourself." ); - - 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; - - /* This is for bug #209 (use PASS to identify to NickServ). */ - if( irc->password != NULL ) - { - char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL }; - - irc_setpass( irc, NULL ); - root_command( irc, send_cmd ); - g_free( send_cmd[1] ); - } -} -void irc_motd( irc_t *irc ) -{ - int fd; - - fd = open( global.conf->motdfile, O_RDONLY ); - if( fd == -1 ) - { - irc_reply( irc, 422, ":We don't need MOTDs." ); - } - else - { - char linebuf[80]; /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */ - char *add, max; - int len; - - linebuf[79] = len = 0; - max = sizeof( linebuf ) - 1; - - irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost ); - while( read( fd, linebuf + len, 1 ) == 1 ) - { - if( linebuf[len] == '\n' || len == max ) - { - linebuf[len] = 0; - irc_reply( irc, 372, ":- %s", linebuf ); - len = 0; - } - else if( linebuf[len] == '%' ) - { - read( fd, linebuf + len, 1 ); - if( linebuf[len] == 'h' ) - add = irc->myhost; - else if( linebuf[len] == 'v' ) - add = BITLBEE_VERSION; - else if( linebuf[len] == 'n' ) - add = irc->nick; - else - add = "%"; - - strncpy( linebuf + len, add, max - len ); - while( linebuf[++len] ); - } - else if( len < max ) - { - len ++; - } - } - irc_reply( irc, 376, ":End of MOTD" ); - close( fd ); - } -} - -void irc_topic( irc_t *irc, char *channel ) -{ - struct groupchat *c = irc_chat_by_channel( irc, channel ); - - if( c && c->topic ) - irc_reply( irc, 332, "%s :%s", channel, c->topic ); - else if( g_strcasecmp( channel, irc->channel ) == 0 ) - irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC ); - else - irc_reply( irc, 331, "%s :No topic for this channel", channel ); -} - -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 - to set a "privileged" mode (+o, +R, etc). */ - char m[256], st = 1, *t; - int i; - char changes[512], *p, st2 = 2; - char badflag = 0; - - memset( m, 0, sizeof( m ) ); - - for( t = irc->umode; *t; t ++ ) - m[(int)*t] = 1; - - p = changes; - for( t = s; *t; t ++ ) - { - if( *t == '+' || *t == '-' ) - st = *t == '+'; - else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) ) - { - if( m[(int)*t] != st) - { - if( st != st2 ) - st2 = st, *p++ = st ? '+' : '-'; - *p++ = *t; - } - m[(int)*t] = st; - } - else - badflag = 1; - } - *p = '\0'; - - memset( irc->umode, 0, sizeof( irc->umode ) ); - - for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ ) - if( m[i] ) - irc->umode[strlen(irc->umode)] = i; - - if( badflag ) - irc_reply( irc, 501, ":Unknown MODE flag" ); - /* Deliberately no !user@host on the prefix here */ - if( *changes ) - irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes ); -} - -void irc_spawn( irc_t *irc, user_t *u ) -{ - irc_join( irc, u, irc->channel ); -} - -void irc_join( irc_t *irc, user_t *u, char *channel ) -{ - char *nick; - - if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) ) - irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel ); - - if( nick_cmp( u->nick, irc->nick ) == 0 ) - { - irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE ); - irc_names( irc, channel ); - irc_topic( irc, channel ); - } - - nick = g_strdup( u->nick ); - nick_lc( nick ); - if( g_hash_table_lookup( irc->watches, nick ) ) - { - irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" ); - } - g_free( nick ); -} - -void irc_part( irc_t *irc, user_t *u, char *channel ) -{ - irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" ); -} - -void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker ) -{ - irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" ); -} - -void irc_kill( irc_t *irc, user_t *u ) -{ - char *nick, *s; - char reason[128]; - - if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) ) - { - if( u->ic->acc->server ) - g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost, - u->ic->acc->server ); - else if( ( s = strchr( u->ic->acc->user, '@' ) ) ) - g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost, - s + 1 ); - else - g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost, - u->ic->acc->prpl->name, irc->myhost ); - - /* proto_opt might contain garbage after the : */ - if( ( s = strchr( reason, ':' ) ) ) - *s = 0; - } - else - { - strcpy( reason, "Leaving..." ); - } - - irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason ); - - nick = g_strdup( u->nick ); - nick_lc( nick ); - if( g_hash_table_lookup( irc->watches, nick ) ) - { - irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" ); - } - g_free( nick ); -} - -int irc_send( irc_t *irc, char *nick, char *s, int flags ) -{ - struct groupchat *c = NULL; - user_t *u = NULL; - - if( strchr( CTYPES, *nick ) ) - { - if( !( c = irc_chat_by_channel( irc, nick ) ) ) - { - irc_reply( irc, 403, "%s :Channel does not exist", nick ); - return( 0 ); - } - } - else - { - u = user_find( irc, nick ); - - if( !u ) - { - if( irc->is_private ) - irc_reply( irc, 401, "%s :Nick does not exist", nick ); - else - irc_usermsg( irc, "Nick `%s' does not exist!", nick ); - return( 0 ); - } - } - - if( *s == 1 && s[strlen(s)-1] == 1 ) - { - if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 ) - { - if( s[7] == ' ' ) s ++; - s += 3; - *(s++) = '/'; - *(s++) = 'm'; - *(s++) = 'e'; - *(s++) = ' '; - s -= 4; - s[strlen(s)-1] = 0; - } - else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 ) - { - u = user_find( irc, irc->mynick ); - irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" ); - return( 1 ); - } - else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 ) - { - u = user_find( irc, irc->mynick ); - irc_privmsg( irc, u, "NOTICE", irc->nick, "", s ); - return( 1 ); - } - else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 ) - { - if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 ) - { - time_t current_typing_notice = time( NULL ); - - if( current_typing_notice - u->last_typing_notice >= 5 ) - { - u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 ); - u->last_typing_notice = current_typing_notice; - } - } - return( 1 ); - } - else if( g_strncasecmp( s + 1, "DCC", 3 ) == 0 ) - { - if( u && u->ic && u->ic->acc->prpl->transfer_request ) - { - file_transfer_t *ft = dcc_request( u->ic, s + 5 ); - if ( ft ) - u->ic->acc->prpl->transfer_request( u->ic, ft, u->handle ); - } - return( 1 ); - } - else - { - irc_usermsg( irc, "Supported CTCPs are ACTION, VERSION, PING, TYPING, DCC" ); - return( 0 ); - } - } - - if( u ) - { - /* For the next message, we probably do have to send new notices... */ - u->last_typing_notice = 0; - u->is_private = irc->is_private; - - if( u->is_private ) - { - 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 ); - } - - if( u->send_handler ) - { - u->send_handler( irc, u, s, flags ); - return 1; - } - } - else if( c && c->ic && c->ic->acc && c->ic->acc->prpl ) - { - return( imc_chat_msg( c, s, 0 ) ); - } - - return( 0 ); -} - -static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond ) -{ - user_t *u = data; - - /* Shouldn't happen, but just to be sure. */ - if( u->sendbuf_len < 2 ) - return FALSE; - - u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */ - imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags ); - - g_free( u->sendbuf ); - u->sendbuf = NULL; - u->sendbuf_len = 0; - u->sendbuf_timer = 0; - u->sendbuf_flags = 0; - - return FALSE; -} - -void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ) -{ - if( !u || !u->ic ) return; - - if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 ) - { - int delay; - - if( u->sendbuf_len > 0 && u->sendbuf_flags != flags) - { - /* Flush the buffer */ - b_event_remove( u->sendbuf_timer ); - buddy_send_handler_delayed( u, -1, 0 ); - } - - if( u->sendbuf_len == 0 ) - { - u->sendbuf_len = strlen( msg ) + 2; - u->sendbuf = g_new( char, u->sendbuf_len ); - u->sendbuf[0] = 0; - u->sendbuf_flags = flags; - } - else - { - u->sendbuf_len += strlen( msg ) + 1; - u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len ); - } - - strcat( u->sendbuf, msg ); - strcat( u->sendbuf, "\n" ); - - delay = set_getint( &irc->set, "buddy_sendbuffer_delay" ); - if( delay <= 5 ) - delay *= 1000; - - if( u->sendbuf_timer > 0 ) - b_event_remove( u->sendbuf_timer ); - u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u ); - } - else - { - imc_buddy_msg( u->ic, u->handle, msg, flags ); - } -} - -int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg ) -{ - char last = 0; - char *s = msg, *line = msg; - - /* The almighty linesplitter .. woohoo!! */ - while( !last ) - { - if( *s == '\r' && *(s+1) == '\n' ) - *(s++) = 0; - if( *s == '\n' ) - { - last = s[1] == 0; - *s = 0; - } - else - { - last = s[0] == 0; - } - if( *s == 0 ) - { - if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 ) - { - irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host, - type, to, line + 4 ); - } - else - { - irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host, - type, to, prefix ? prefix : "", line ); - } - line = s + 1; - } - s ++; - } - - return( 1 ); -} - -int irc_msgfrom( irc_t *irc, char *nick, char *msg ) +static char *set_eval_charset( set_t *set, char *value ) { - user_t *u = user_find( irc, nick ); - static char *prefix = NULL; - - if( !u ) return( 0 ); - if( prefix && *prefix ) g_free( prefix ); - - if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 ) - { - int len = strlen( irc->nick) + 3; - prefix = g_new (char, len ); - g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) ); - prefix[len-1] = 0; - } - else - { - prefix = ""; - } - - return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) ); -} + irc_t *irc = set->data; + GIConv ic, oc; -int irc_noticefrom( irc_t *irc, char *nick, char *msg ) -{ - user_t *u = user_find( irc, nick ); - - if( u ) - return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) ); - else - return( 0 ); -} + if( g_strcasecmp( value, "none" ) == 0 ) + value = g_strdup( "utf-8" ); -/* Returns 0 if everything seems to be okay, a number >0 when there was a - timeout. The number returned is the number of seconds we received no - pongs from the user. When not connected yet, we don't ping but drop the - connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */ -static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond ) -{ - irc_t *irc = _irc; - int rv = 0; - - if( !( irc->status & USTATUS_LOGGED_IN ) ) + if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) { - if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) ) - rv = gettime() - irc->last_pong; + return NULL; } - else + if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 ) { - if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging ) - { - irc_write( irc, "PING :%s", IRC_PING_STRING ); - irc->pinging = 1; - } - else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) ) - { - rv = gettime() - irc->last_pong; - } + g_iconv_close( ic ); + return NULL; } - if( rv > 0 ) - { - irc_abort( irc, 0, "Ping Timeout: %d seconds", rv ); - return FALSE; - } + if( irc->iconv != (GIConv) -1 ) + g_iconv_close( irc->iconv ); + if( irc->oconv != (GIConv) -1 ) + g_iconv_close( irc->oconv ); - return TRUE; -} + irc->iconv = ic; + irc->oconv = oc; -struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel ) -{ - struct groupchat *c; - account_t *a; - - /* This finds the connection which has a conversation which belongs to this channel */ - for( a = irc->accounts; a; a = a->next ) - { - if( a->ic == NULL ) - continue; - - c = a->ic->groupchats; - while( c ) - { - if( c->channel && g_strcasecmp( c->channel, channel ) == 0 ) - return c; - - c = c->next; - } - } - - return NULL; + return value; } diff --git a/irc.h b/irc.h index a865bde3..482ef067 100644 --- a/irc.h +++ b/irc.h @@ -4,7 +4,7 @@ * Copyright 2002-2004 Wilmer van der Gaast and others * \********************************************************************/ -/* The big hairy IRCd part of the project */ +/* The IRC-based UI (for now the only one) */ /* This program is free software; you can redistribute it and/or modify @@ -48,6 +48,8 @@ typedef enum USTATUS_SHUTDOWN = 8 } irc_status_t; +struct irc_user; + typedef struct irc { int fd; @@ -58,86 +60,83 @@ typedef struct irc char *readbuffer; GIConv iconv, oconv; - int sentbytes; - time_t oldtime; + struct irc_user *root; + struct irc_user *user; - char *nick; - char *user; - char *host; - char *realname; char *password; /* HACK: Used to save the user's password, but before logging in, this may contain a password we should send to identify after USER/NICK are received. */ char umode[8]; - char *myhost; - char *mynick; - - char *channel; - int c_id; - - char is_private; /* Not too nice... */ - char *last_target; - struct query *queries; struct account *accounts; GSList *file_transfers; struct chat *chatrooms; - struct __USER *users; - GHashTable *userhash; + GSList *users; + GHashTable *nick_user_hash; GHashTable *watches; - struct __NICK *nicks; - struct set *set; gint r_watch_source_id; gint w_watch_source_id; gint ping_source_id; + + struct bee *b; } irc_t; +typedef struct irc_user +{ + char *nick; + char *user; + char *host; + char *fullname; + + /* Nickname in lowercase for case sensitive searches */ + char *key; + + char is_private; + + char *sendbuf; + int sendbuf_len; + guint sendbuf_timer; + int sendbuf_flags; + + //struct user *b; +} irc_user_t; + #include "user.h" +/* irc.c */ extern GSList *irc_connection_list; irc_t *irc_new( int fd ); void irc_abort( irc_t *irc, int immed, char *format, ... ) G_GNUC_PRINTF( 3, 4 ); void irc_free( irc_t *irc ); -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, ... ) G_GNUC_PRINTF( 2, 3 ); void irc_write_all( int now, char *format, ... ) G_GNUC_PRINTF( 2, 3 ); -void irc_reply( irc_t *irc, int code, char *format, ... ) G_GNUC_PRINTF( 3, 4 ); -G_MODULE_EXPORT int irc_usermsg( irc_t *irc, char *format, ... ) G_GNUC_PRINTF( 2, 3 ); -char **irc_tokenize( char *buffer ); +void irc_vawrite( irc_t *irc, char *format, va_list params ); -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 ); -void irc_umode_set( irc_t *irc, char *s, int allow_priv ); -void irc_who( irc_t *irc, char *channel ); -void irc_spawn( irc_t *irc, user_t *u ); -void irc_join( irc_t *irc, user_t *u, char *channel ); -void irc_part( irc_t *irc, user_t *u, char *channel ); -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 ); -void irc_setpass( irc_t *irc, const char *pass ); /* USE WITH CAUTION! */ - -int irc_send( irc_t *irc, char *nick, char *s, int flags ); -int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg ); -int irc_msgfrom( irc_t *irc, char *nick, char *msg ); -int irc_noticefrom( irc_t *irc, char *nick, char *msg ); - -void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ); -struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel ); + +/* irc_commands.c */ +void irc_exec( irc_t *irc, char **cmd ); + +/* irc_send.c */ +void irc_send_num( irc_t *irc, int code, char *format, ... ) G_GNUC_PRINTF( 3, 4 ); +void irc_send_login( irc_t *irc ); +void irc_send_motd( irc_t *irc ); +int irc_usermsg( irc_t *irc, char *format, ... ); + +/* irc_user.c */ +irc_user_t *irc_user_new( irc_t *irc, const char *nick ); +int irc_user_free( irc_t *irc, const char *nick ); +irc_user_t *irc_user_find( irc_t *irc, const char *nick ); +int irc_user_rename( irc_t *irc, const char *old, const char *new ); #endif diff --git a/irc_commands.c b/irc_commands.c index a417e0d9..81ddd588 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2006 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* IRC commands */ @@ -38,7 +38,7 @@ static void irc_cmd_pass( irc_t *irc, char **cmd ) command last. (Possibly it won't send it at all if it turns out we don't require it, which will break this feature.) Try to identify using the given password. */ - return root_command( irc, send_cmd ); + /*return root_command( irc, send_cmd );*/ } /* Handling in pre-logged-in state, first see if this server is password-protected: */ @@ -52,48 +52,48 @@ static void irc_cmd_pass( irc_t *irc, char **cmd ) } else if( global.conf->auth_pass ) { - irc_reply( irc, 464, ":Incorrect password" ); + irc_send_num( irc, 464, ":Incorrect password" ); } else { /* Remember the password and try to identify after USER/NICK. */ - irc_setpass( irc, cmd[1] ); + /*irc_setpass( irc, cmd[1] ); */ irc_check_login( irc ); } } static void irc_cmd_user( irc_t *irc, char **cmd ) { - irc->user = g_strdup( cmd[1] ); - irc->realname = g_strdup( cmd[4] ); + irc->user->user = g_strdup( cmd[1] ); + irc->user->fullname = g_strdup( cmd[4] ); irc_check_login( irc ); } static void irc_cmd_nick( irc_t *irc, char **cmd ) { - if( irc->nick ) + if( irc->user->nick ) { - irc_reply( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" ); + irc_send_num( 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 ) ) + else if( irc_user_find( irc, cmd[1] ) ) { - irc_reply( irc, 433, ":This nick is already in use" ); + irc_send_num( 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" ); + irc_send_num( irc, 432, ":This nick contains invalid characters" ); } else { - irc->nick = g_strdup( cmd[1] ); + irc->user->nick = g_strdup( cmd[1] ); irc_check_login( irc ); } } +#if 0 static void irc_cmd_quit( irc_t *irc, char **cmd ) { if( cmd[1] && *cmd[1] ) @@ -115,11 +115,11 @@ static void irc_cmd_oper( irc_t *irc, char **cmd ) strcmp( cmd[2], global.conf->oper_pass ) == 0 ) ) { irc_umode_set( irc, "+o", 1 ); - irc_reply( irc, 381, ":Password accepted" ); + irc_send_num( irc, 381, ":Password accepted" ); } else { - irc_reply( irc, 432, ":Incorrect password" ); + irc_send_num( irc, 432, ":Incorrect password" ); } } @@ -130,12 +130,12 @@ static void irc_cmd_mode( irc_t *irc, char **cmd ) if( cmd[2] ) { if( *cmd[2] == '+' || *cmd[2] == '-' ) - irc_reply( irc, 477, "%s :Can't change channel modes", cmd[1] ); + irc_send_num( 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] ); + irc_send_num( irc, 368, "%s :No bans possible", cmd[1] ); } else - irc_reply( irc, 324, "%s +%s", cmd[1], CMODE ); + irc_send_num( irc, 324, "%s +%s", cmd[1], CMODE ); } else { @@ -144,10 +144,10 @@ static void irc_cmd_mode( irc_t *irc, char **cmd ) if( cmd[2] ) irc_umode_set( irc, cmd[2], 0 ); else - irc_reply( irc, 221, "+%s", irc->umode ); + irc_send_num( irc, 221, "+%s", irc->umode ); } else - irc_reply( irc, 502, ":Don't touch their modes" ); + irc_send_num( irc, 502, ":Don't touch their modes" ); } } @@ -182,7 +182,7 @@ static void irc_cmd_part( irc_t *irc, char **cmd ) } else { - irc_reply( irc, 403, "%s :No such channel", cmd[1] ); + irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); } } @@ -196,11 +196,11 @@ static void irc_cmd_join( irc_t *irc, char **cmd ) struct chat *c; if( strchr( CTYPES, cmd[1][0] ) == NULL || cmd[1][1] == 0 ) - irc_reply( irc, 479, "%s :Invalid channel name", cmd[1] ); + irc_send_num( irc, 479, "%s :Invalid channel name", cmd[1] ); else if( ( c = chat_bychannel( irc, cmd[1] ) ) && c->acc && c->acc->ic ) chat_join( irc, c, cmd[2] ); else - irc_reply( irc, 403, "%s :No such channel", cmd[1] ); + irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); } } @@ -214,18 +214,18 @@ static void irc_cmd_invite( irc_t *irc, char **cmd ) if( c->ic && c->ic->acc->prpl->chat_invite ) { c->ic->acc->prpl->chat_invite( c, u->handle, NULL ); - irc_reply( irc, 341, "%s %s", nick, channel ); + irc_send_num( irc, 341, "%s %s", nick, channel ); return; } - irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); + irc_send_num( 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" ); + irc_send_num( irc, 412, ":No text to send" ); } else if ( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) { @@ -282,26 +282,26 @@ static void irc_cmd_who( irc_t *irc, char **cmd ) 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 ); + irc_send_num( 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 ); + irc_send_num( 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 = irc_chat_by_channel( irc, channel ) ) ) for( l = c->in_room; l; l = l->next ) { if( ( u = user_findhandle( c->ic, 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_send_num( 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_send_num( 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:"**" ); + irc_send_num( irc, 315, "%s :End of /WHO list", channel?channel:"**" ); } static void irc_cmd_userhost( irc_t *irc, char **cmd ) @@ -319,9 +319,9 @@ static void irc_cmd_userhost( irc_t *irc, char **cmd ) 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 ); + irc_send_num( 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 ); + irc_send_num( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host ); } } @@ -376,7 +376,7 @@ static void irc_cmd_ison( irc_t *irc, char **cmd ) if( strlen( buff ) > 0 ) buff[strlen(buff)-1] = '\0'; - irc_reply( irc, 303, ":%s", buff ); + irc_send_num( irc, 303, ":%s", buff ); } static void irc_cmd_watch( irc_t *irc, char **cmd ) @@ -405,9 +405,9 @@ static void irc_cmd_watch( irc_t *irc, char **cmd ) 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, (int) time( NULL ), "is online" ); + irc_send_num( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "is online" ); else - irc_reply( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" ); + irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" ); } else if( cmd[i][0] == '-' ) { @@ -418,7 +418,7 @@ static void irc_cmd_watch( irc_t *irc, char **cmd ) g_hash_table_remove( irc->watches, okey ); g_free( okey ); - irc_reply( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" ); + irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" ); } } } @@ -462,7 +462,7 @@ static void irc_cmd_away( irc_t *irc, char **cmd ) j ++; u->away[j] = 0; - irc_reply( irc, 306, ":You're now away: %s", u->away ); + irc_send_num( irc, 306, ":You're now away: %s", u->away ); /* irc_umode_set( irc, irc->myhost, "+a" ); */ } else @@ -470,7 +470,7 @@ static void irc_cmd_away( irc_t *irc, char **cmd ) if( u->away ) g_free( u->away ); u->away = NULL; /* irc_umode_set( irc, irc->myhost, "-a" ); */ - irc_reply( irc, 305, ":Welcome back" ); + irc_send_num( irc, 305, ":Welcome back" ); } set_setstr( &irc->set, "away", u->away ); @@ -483,27 +483,27 @@ static void irc_cmd_whois( irc_t *irc, char **cmd ) if( u ) { - irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname ); + irc_send_num( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname ); if( u->ic ) - irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->ic->acc->user, + irc_send_num( irc, 312, "%s %s.%s :%s network", u->nick, u->ic->acc->user, u->ic->acc->server && *u->ic->acc->server ? u->ic->acc->server : "", u->ic->acc->prpl->name ); else - irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO ); + irc_send_num( 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" ); + irc_send_num( irc, 301, "%s :%s", u->nick, "User is offline" ); else if( u->away ) - irc_reply( irc, 301, "%s :%s", u->nick, u->away ); + irc_send_num( irc, 301, "%s :%s", u->nick, u->away ); if( u->status_msg ) - irc_reply( irc, 333, "%s :Status: %s", u->nick, u->status_msg ); + irc_send_num( irc, 333, "%s :Status: %s", u->nick, u->status_msg ); - irc_reply( irc, 318, "%s :End of /WHOIS list", nick ); + irc_send_num( irc, 318, "%s :End of /WHOIS list", nick ); } else { - irc_reply( irc, 401, "%s :Nick does not exist", nick ); + irc_send_num( irc, 401, "%s :Nick does not exist", nick ); } } @@ -514,8 +514,8 @@ static void irc_cmd_whowas( irc_t *irc, char **cmd ) 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] ); + irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] ); + irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] ); } static void irc_cmd_nickserv( irc_t *irc, char **cmd ) @@ -540,7 +540,7 @@ static void irc_cmd_pong( irc_t *irc, char **cmd ) static void irc_cmd_version( irc_t *irc, char **cmd ) { - irc_reply( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU ); + irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU ); } static void irc_cmd_completions( irc_t *irc, char **cmd ) @@ -571,13 +571,15 @@ static void irc_cmd_rehash( irc_t *irc, char **cmd ) else ipc_to_master( cmd ); - irc_reply( irc, 382, "%s :Rehashing", global.conf_file ); + irc_send_num( irc, 382, "%s :Rehashing", global.conf_file ); } +#endif static const command_t irc_commands[] = { { "pass", 1, irc_cmd_pass, 0 }, { "user", 4, irc_cmd_user, IRC_CMD_PRE_LOGIN }, { "nick", 1, irc_cmd_nick, 0 }, +#if 0 { "quit", 0, irc_cmd_quit, 0 }, { "ping", 0, irc_cmd_ping, 0 }, { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, @@ -609,6 +611,7 @@ static const command_t irc_commands[] = { { "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 }, +#endif { NULL } }; @@ -627,19 +630,19 @@ void irc_exec( irc_t *irc, char *cmd[] ) if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN ) { - irc_reply( irc, 462, ":Only allowed before logging in" ); + irc_send_num( 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" ); + irc_send_num( 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" ); + irc_send_num( 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] ); + irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] ); } else if( irc_commands[i].flags & IRC_CMD_TO_MASTER ) { @@ -656,5 +659,5 @@ void irc_exec( irc_t *irc, char *cmd[] ) } if( irc->status >= USTATUS_LOGGED_IN ) - irc_reply( irc, 421, "%s :Unknown command", cmd[0] ); + irc_send_num( irc, 421, "%s :Unknown command", cmd[0] ); } diff --git a/nick.c b/nick.c index 5d7dc8a9..6d798bc6 100644 --- a/nick.c +++ b/nick.c @@ -77,7 +77,7 @@ char *nick_get( account_t *acc, const char *handle ) *(s++) = 0; nick_strip( nick ); - if( set_getbool( &acc->irc->set, "lcnicks" ) ) + if( set_getbool( &acc->irc->b->set, "lcnicks" ) ) nick_lc( nick ); } g_free( store_handle ); @@ -95,7 +95,7 @@ void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+ /* Now, find out if the nick is already in use at the moment, and make subtle changes to make it unique. */ - while( !nick_ok( nick ) || user_find( acc->irc, nick ) ) + while( !nick_ok( nick ) || irc_user_find( acc->irc, nick ) ) { if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) ) { diff --git a/protocols/Makefile b/protocols/Makefile index 18d79e8d..f1133cc9 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -9,7 +9,9 @@ -include ../Makefile.settings # [SH] Program variables -objects = nogaim.o +#objects = account.o nogaim.o user.o +objects = bee.o + # [SH] The next two lines should contain the directory name (in $(subdirs)) # and the name of the object file, which should be linked into diff --git a/protocols/account.c b/protocols/account.c new file mode 100644 index 00000000..c549c866 --- /dev/null +++ b/protocols/account.c @@ -0,0 +1,359 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Account management functions */ + +/* + 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 "account.h" +#include "chat.h" + +account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) +{ + account_t *a; + set_t *s; + + if( irc->accounts ) + { + for( a = irc->accounts; a->next; a = a->next ); + a = a->next = g_new0( account_t, 1 ); + } + else + { + irc->accounts = a = g_new0 ( account_t, 1 ); + } + + a->prpl = prpl; + a->user = g_strdup( user ); + a->pass = g_strdup( pass ); + a->auto_connect = 1; + a->irc = irc; + + s = set_add( &a->set, "auto_connect", "true", set_eval_account, a ); + s->flags |= ACC_SET_NOSAVE; + + s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a ); + + s = set_add( &a->set, "nick_source", "handle", NULL, a ); + + s = set_add( &a->set, "password", NULL, set_eval_account, a ); + s->flags |= ACC_SET_NOSAVE | SET_NULL_OK; + + s = set_add( &a->set, "username", NULL, set_eval_account, a ); + s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; + set_setstr( &a->set, "username", user ); + + a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free ); + + /* This function adds some more settings (and might want to do more + things that have to be done now, although I can't think of anything. */ + if( prpl->init ) + prpl->init( a ); + + s = set_add( &a->set, "away", NULL, set_eval_account, a ); + s->flags |= SET_NULL_OK; + + if( a->flags & ACC_FLAG_STATUS_MESSAGE ) + { + s = set_add( &a->set, "status", NULL, set_eval_account, a ); + s->flags |= SET_NULL_OK; + } + + return a; +} + +char *set_eval_account( set_t *set, char *value ) +{ + account_t *acc = set->data; + + /* Double-check: We refuse to edit on-line accounts. */ + if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic ) + return SET_INVALID; + + if( strcmp( set->key, "server" ) == 0 ) + { + g_free( acc->server ); + if( value && *value ) + { + acc->server = g_strdup( value ); + return value; + } + else + { + acc->server = g_strdup( set->def ); + return g_strdup( set->def ); + } + } + else if( strcmp( set->key, "username" ) == 0 ) + { + g_free( acc->user ); + acc->user = g_strdup( value ); + return value; + } + else if( strcmp( set->key, "password" ) == 0 ) + { + if( value ) + { + g_free( acc->pass ); + acc->pass = g_strdup( value ); + return NULL; /* password shouldn't be visible in plaintext! */ + } + else + { + /* NULL can (should) be stored in the set_t + variable, but is otherwise not correct. */ + return SET_INVALID; + } + } + else if( strcmp( set->key, "auto_connect" ) == 0 ) + { + if( !is_bool( value ) ) + return SET_INVALID; + + acc->auto_connect = bool2int( value ); + return value; + } + else if( strcmp( set->key, "away" ) == 0 || + strcmp( set->key, "status" ) == 0 ) + { + if( acc->ic && acc->ic->flags & OPT_LOGGED_IN ) + { + /* If we're currently on-line, set the var now already + (bit of a hack) and send an update. */ + g_free( set->value ); + set->value = g_strdup( value ); + + imc_away_send_update( acc->ic ); + } + + return value; + } + + return SET_INVALID; +} + +account_t *account_get( irc_t *irc, char *id ) +{ + account_t *a, *ret = NULL; + char *handle, *s; + int nr; + + /* This checks if the id string ends with (...) */ + if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 ) + { + struct prpl *proto; + + *s = *handle = 0; + handle ++; + + if( ( proto = find_protocol( id ) ) ) + { + for( a = irc->accounts; a; a = a->next ) + if( a->prpl == proto && + a->prpl->handle_cmp( handle, a->user ) == 0 ) + ret = a; + } + + /* Restore the string. */ + handle --; + *handle = '('; + *s = ')'; + + if( ret ) + return ret; + } + + if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) + { + for( a = irc->accounts; a; a = a->next ) + if( ( nr-- ) == 0 ) + return( a ); + + return( NULL ); + } + + for( a = irc->accounts; a; a = a->next ) + { + if( g_strcasecmp( id, a->prpl->name ) == 0 ) + { + if( !ret ) + ret = a; + else + return( NULL ); /* We don't want to match more than one... */ + } + else if( strstr( a->user, id ) ) + { + if( !ret ) + ret = a; + else + return( NULL ); + } + } + + return( ret ); +} + +void account_del( irc_t *irc, account_t *acc ) +{ + account_t *a, *l = NULL; + struct chat *c, *nc; + + if( acc->ic ) + /* Caller should have checked, accounts still in use can't be deleted. */ + return; + + for( a = irc->accounts; a; a = (l=a)->next ) + if( a == acc ) + { + if( l ) + l->next = a->next; + else + irc->accounts = a->next; + + for( c = irc->chatrooms; c; c = nc ) + { + nc = c->next; + if( acc == c->acc ) + chat_del( irc, c ); + } + + while( a->set ) + set_del( &a->set, a->set->key ); + + g_hash_table_destroy( a->nicks ); + + g_free( a->user ); + g_free( a->pass ); + g_free( a->server ); + if( a->reconnect ) /* This prevents any reconnect still queued to happen */ + cancel_auto_reconnect( a ); + g_free( a ); + + break; + } +} + +void account_on( irc_t *irc, account_t *a ) +{ + if( a->ic ) + { + /* Trying to enable an already-enabled account */ + return; + } + + cancel_auto_reconnect( a ); + + a->reconnect = 0; + a->prpl->login( a ); +} + +void account_off( irc_t *irc, account_t *a ) +{ + imc_logout( a->ic, FALSE ); + a->ic = NULL; + if( a->reconnect ) + { + /* Shouldn't happen */ + cancel_auto_reconnect( a ); + } +} + +struct account_reconnect_delay +{ + int start; + char op; + int step; + int max; +}; + +int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p ) +{ + memset( p, 0, sizeof( *p ) ); + /* A whole day seems like a sane "maximum maximum". */ + p->max = 86400; + + /* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */ + while( *value && isdigit( *value ) ) + p->start = p->start * 10 + *value++ - '0'; + + /* Sure, call me evil for implementing my own fscanf here, but it's + dead simple and I immediately know where to continue parsing. */ + + if( *value == 0 ) + /* If the string ends now, the delay is constant. */ + return 1; + else if( *value != '+' && *value != '*' ) + /* Otherwise allow either a + or a * */ + return 0; + + p->op = *value++; + + /* + or * the delay by this number every time. */ + while( *value && isdigit( *value ) ) + p->step = p->step * 10 + *value++ - '0'; + + if( *value == 0 ) + /* Use the default maximum (one day). */ + return 1; + else if( *value != '<' ) + return 0; + + p->max = 0; + value ++; + while( *value && isdigit( *value ) ) + p->max = p->max * 10 + *value++ - '0'; + + return p->max > 0; +} + +char *set_eval_account_reconnect_delay( set_t *set, char *value ) +{ + struct account_reconnect_delay p; + + return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID; +} + +int account_reconnect_delay( account_t *a ) +{ + char *setting = set_getstr( &a->irc->b->set, "auto_reconnect_delay" ); + struct account_reconnect_delay p; + + if( account_reconnect_delay_parse( setting, &p ) ) + { + if( a->auto_reconnect_delay == 0 ) + a->auto_reconnect_delay = p.start; + else if( p.op == '+' ) + a->auto_reconnect_delay += p.step; + else if( p.op == '*' ) + a->auto_reconnect_delay *= p.step; + + if( a->auto_reconnect_delay > p.max ) + a->auto_reconnect_delay = p.max; + } + else + { + a->auto_reconnect_delay = 0; + } + + return a->auto_reconnect_delay; +} diff --git a/protocols/account.h b/protocols/account.h new file mode 100644 index 00000000..984dcfe6 --- /dev/null +++ b/protocols/account.h @@ -0,0 +1,72 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Account management functions */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _ACCOUNT_H +#define _ACCOUNT_H + +typedef struct account +{ + struct prpl *prpl; + char *user; + char *pass; + char *server; + + int auto_connect; + int auto_reconnect_delay; + int reconnect; + int flags; + + set_t *set; + GHashTable *nicks; + + struct irc *irc; + struct im_connection *ic; + struct account *next; +} account_t; + +account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ); +account_t *account_get( irc_t *irc, char *id ); +void account_del( irc_t *irc, account_t *acc ); +void account_on( irc_t *irc, account_t *a ); +void account_off( irc_t *irc, account_t *a ); + +char *set_eval_account( set_t *set, char *value ); +char *set_eval_account_reconnect_delay( set_t *set, char *value ); +int account_reconnect_delay( account_t *a ); + +typedef enum +{ + ACC_SET_NOSAVE = 0x01, /* Don't save this setting (i.e. stored elsewhere). */ + ACC_SET_OFFLINE_ONLY = 0x02, /* Allow changes only if the acct is offline. */ + ACC_SET_ONLINE_ONLY = 0x04, /* Allow changes only if the acct is online. */ +} account_set_flag_t; + +typedef enum +{ + ACC_FLAG_AWAY_MESSAGE = 0x01, /* Supports away messages instead of just states. */ + ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */ +} account_flag_t; + +#endif diff --git a/protocols/chat.c b/protocols/chat.c new file mode 100644 index 00000000..8c5ce0bc --- /dev/null +++ b/protocols/chat.c @@ -0,0 +1,192 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2008 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Keep track of chatrooms the user is interested in */ + +/* + 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 +*/ + +#include "bitlbee.h" +#include "chat.h" + +struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ) +{ + struct chat *c, *l; + set_t *s; + + if( acc->prpl->chat_join == NULL || !chat_chanok( channel ) || + chat_chancmp( channel, irc->channel ) == 0 ) + { + return NULL; + } + + for( c = irc->chatrooms; c; c = c->next ) + { + if( chat_chancmp( channel, c->channel ) == 0 ) + return NULL; + + if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) + return NULL; + + l = c; + } + + if( irc->chatrooms == NULL ) + irc->chatrooms = c = g_new0( struct chat, 1 ); + else + l->next = c = g_new0( struct chat, 1 ); + + c->acc = acc; + c->handle = g_strdup( handle ); + c->channel = g_strdup( channel ); + + s = set_add( &c->set, "auto_join", "false", set_eval_bool, c ); + /* s = set_add( &c->set, "auto_rejoin", "false", set_eval_bool, c ); */ + s = set_add( &c->set, "nick", NULL, NULL, c ); + s->flags |= SET_NULL_OK; + + return c; +} + +struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ) +{ + struct chat *c; + + for( c = irc->chatrooms; c; c = c->next ) + { + if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 ) + break; + } + + return c; +} + +struct chat *chat_bychannel( irc_t *irc, char *channel ) +{ + struct chat *c; + + for( c = irc->chatrooms; c; c = c->next ) + { + if( chat_chancmp( channel, c->channel ) == 0 ) + break; + } + + return c; +} + +struct chat *chat_get( irc_t *irc, char *id ) +{ + struct chat *c, *ret = NULL; + int nr; + + if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) + { + for( c = irc->chatrooms; c; c = c->next ) + if( ( nr-- ) == 0 ) + return c; + + return NULL; + } + + for( c = irc->chatrooms; c; c = c->next ) + { + if( strstr( c->handle, id ) ) + { + if( !ret ) + ret = c; + else + return NULL; + } + else if( strstr( c->channel, id ) ) + { + if( !ret ) + ret = c; + else + return NULL; + } + } + + return ret; +} + +int chat_del( irc_t *irc, struct chat *chat ) +{ + struct chat *c, *l = NULL; + + for( c = irc->chatrooms; c; c = (l=c)->next ) + if( c == chat ) + break; + + if( c == NULL ) + return 0; + else if( l == NULL ) + irc->chatrooms = c->next; + else + l->next = c->next; + + while( c->set ) + set_del( &c->set, c->set->key ); + + g_free( c->handle ); + g_free( c->channel ); + g_free( c ); + + return 1; +} + +int chat_chancmp( char *a, char *b ) +{ + if( !chat_chanok( a ) || !chat_chanok( b ) ) + return 0; + + if( a[0] == b[0] ) + return nick_cmp( a + 1, b + 1 ); + else + return -1; +} + +int chat_chanok( char *a ) +{ + if( strchr( CTYPES, a[0] ) != NULL ) + return nick_ok( a + 1 ); + else + return 0; +} + +int chat_join( irc_t *irc, struct chat *c, const char *password ) +{ + struct groupchat *gc; + char *nick = set_getstr( &c->set, "nick" ); + + if( c->acc->ic == NULL || c->acc->prpl->chat_join == NULL ) + return 0; + + if( nick == NULL ) + nick = irc->nick; + + if( ( gc = c->acc->prpl->chat_join( c->acc->ic, c->handle, nick, password ) ) ) + { + g_free( gc->channel ); + gc->channel = g_strdup( c->channel ); + return 1; + } + + return 0; +} diff --git a/protocols/chat.h b/protocols/chat.h new file mode 100644 index 00000000..7196aea8 --- /dev/null +++ b/protocols/chat.h @@ -0,0 +1,51 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2008 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Keep track of chatrooms the user is interested in */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _CHAT_H +#define _CHAT_H + +struct chat +{ + account_t *acc; + + char *handle; + char *channel; + set_t *set; + + struct chat *next; +}; + +struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel ); +struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle ); +struct chat *chat_bychannel( irc_t *irc, char *channel ); +struct chat *chat_get( irc_t *irc, char *id ); +int chat_del( irc_t *irc, struct chat *chat ); + +int chat_chancmp( char *a, char *b ); +int chat_chanok( char *a ); + +int chat_join( irc_t *irc, struct chat *c, const char *password ); + +#endif diff --git a/protocols/user.c b/protocols/user.c new file mode 100644 index 00000000..f014586b --- /dev/null +++ b/protocols/user.c @@ -0,0 +1,106 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Stuff to handle, save and search buddies */ + +/* + 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" + +user_t *user_add( irc_t *irc, char *nick ) +{ + user_t *u, *lu = NULL; + char *key; + + if( !nick_ok( nick ) ) + return( NULL ); + + if( user_find( irc, nick ) != NULL ) + return( NULL ); + + return( u ); +} + +int user_del( irc_t *irc, char *nick ) +{ + user_t *u, *t; + char *key; + gpointer okey, ovalue; + + if( !nick_ok( nick ) ) + return( 0 ); + + u = irc->users; + t = NULL; + while( u ) + { + if( nick_cmp( u->nick, nick ) == 0 ) + { + /* Get this key now already, since "nick" might be free()d + at the time we start playing with the hash... */ + key = g_strdup( nick ); + nick_lc( key ); + + if( t ) + t->next = u->next; + else + irc->users = u->next; + if( u->online ) + irc_kill( irc, u ); + g_free( u->nick ); + if( u->nick != u->user ) g_free( u->user ); + if( u->nick != u->host ) g_free( u->host ); + if( u->nick != u->realname ) g_free( u->realname ); + g_free( u->group ); + g_free( u->away ); + g_free( u->handle ); + g_free( u->sendbuf ); + if( u->sendbuf_timer ) b_event_remove( u->sendbuf_timer ); + g_free( u ); + + return( 1 ); + } + u = (t=u)->next; + } + + return( 0 ); +} + +user_t *user_findhandle( struct im_connection *ic, const char *handle ) +{ + user_t *u; + char *nick; + + /* First, let's try a hash lookup. If it works, it's probably faster. */ + if( ( nick = g_hash_table_lookup( ic->acc->nicks, handle ) ) && + ( u = user_find( ic->irc, nick ) ) && + ( ic->acc->prpl->handle_cmp( handle, u->handle ) == 0 ) ) + return u; + + /* However, it doesn't always work, so in that case we'll have to dig + through the whole userlist. :-( */ + for( u = ic->irc->users; u; u = u->next ) + if( u->ic == ic && u->handle && ic->acc->prpl->handle_cmp( u->handle, handle ) == 0 ) + return u; + + return NULL; +} diff --git a/protocols/user.h b/protocols/user.h new file mode 100644 index 00000000..26697a3a --- /dev/null +++ b/protocols/user.h @@ -0,0 +1,40 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Stuff to handle, save and search buddies */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __USER_H__ +#define __USER_H__ + +struct __USER +{ + struct im_connection *ic; + char *handle; + char *fullname; + char *group; + + char *away; + char *status_msg; +} user_t; + +#endif /* __USER_H__ */ diff --git a/set.c b/set.c index 18d5a50d..8ecc9690 100644 --- a/set.c +++ b/set.c @@ -224,6 +224,7 @@ char *set_eval_to_char( set_t *set, char *value ) return s; } +/* char *set_eval_ops( set_t *set, char *value ) { irc_t *irc = set->data; @@ -245,3 +246,4 @@ char *set_eval_ops( set_t *set, char *value ) return value; } +*/ diff --git a/unix.c b/unix.c index 7088d0f8..647418fb 100644 --- a/unix.c +++ b/unix.c @@ -61,7 +61,7 @@ int main( int argc, char *argv[] ) return( 1 ); b_main_init(); - nogaim_init(); + //nogaim_init(); srand( time( NULL ) ^ getpid() ); global.helpfile = g_strdup( HELP_FILE ); @@ -114,12 +114,14 @@ int main( int argc, char *argv[] ) } } + /* global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage ); if( global.storage == NULL ) { log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); return( 1 ); } + */ /* Catch some signals to tell the user what's happening before quitting */ memset( &sig, 0, sizeof( sig ) ); diff --git a/user.c b/user.c deleted file mode 100644 index 4d58f56b..00000000 --- a/user.c +++ /dev/null @@ -1,231 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Stuff to handle, save and search buddies */ - -/* - 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" - -user_t *user_add( irc_t *irc, char *nick ) -{ - user_t *u, *lu = NULL; - char *key; - - if( !nick_ok( nick ) ) - return( NULL ); - - if( user_find( irc, nick ) != NULL ) - return( NULL ); - - if( ( u = irc->users ) ) - { - while( u ) - { - if( nick_cmp( nick, u->nick ) < 0 ) - break; - - lu = u; - u = u->next; - } - - u = g_new0( user_t, 1 ); - if( lu ) - { - u->next = lu->next; - lu->next = u; - } - else - { - u->next = irc->users; - irc->users = u; - } - } - else - { - irc->users = u = g_new0( user_t, 1 ); - } - - u->user = u->realname = u->host = u->nick = g_strdup( nick ); - u->is_private = set_getbool( &irc->set, "private" ); - - key = g_strdup( nick ); - nick_lc( key ); - g_hash_table_insert( irc->userhash, key, u ); - - return( u ); -} - -int user_del( irc_t *irc, char *nick ) -{ - user_t *u, *t; - char *key; - gpointer okey, ovalue; - - if( !nick_ok( nick ) ) - return( 0 ); - - u = irc->users; - t = NULL; - while( u ) - { - if( nick_cmp( u->nick, nick ) == 0 ) - { - /* Get this key now already, since "nick" might be free()d - at the time we start playing with the hash... */ - key = g_strdup( nick ); - nick_lc( key ); - - if( t ) - t->next = u->next; - else - irc->users = u->next; - if( u->online ) - irc_kill( irc, u ); - g_free( u->nick ); - if( u->nick != u->user ) g_free( u->user ); - if( u->nick != u->host ) g_free( u->host ); - if( u->nick != u->realname ) g_free( u->realname ); - g_free( u->group ); - g_free( u->away ); - g_free( u->handle ); - g_free( u->sendbuf ); - if( u->sendbuf_timer ) b_event_remove( u->sendbuf_timer ); - g_free( u ); - - if( !g_hash_table_lookup_extended( irc->userhash, key, &okey, &ovalue ) || ovalue != u ) - { - g_free( key ); - return( 1 ); /* Although this is a severe error, the user is removed from the list... */ - } - g_hash_table_remove( irc->userhash, key ); - g_free( key ); - g_free( okey ); - - return( 1 ); - } - u = (t=u)->next; - } - - return( 0 ); -} - -user_t *user_find( irc_t *irc, char *nick ) -{ - char key[512] = ""; - - strncpy( key, nick, sizeof( key ) - 1 ); - if( nick_lc( key ) ) - return( g_hash_table_lookup( irc->userhash, key ) ); - else - return( NULL ); -} - -user_t *user_findhandle( struct im_connection *ic, const char *handle ) -{ - user_t *u; - char *nick; - - /* First, let's try a hash lookup. If it works, it's probably faster. */ - if( ( nick = g_hash_table_lookup( ic->acc->nicks, handle ) ) && - ( u = user_find( ic->irc, nick ) ) && - ( ic->acc->prpl->handle_cmp( handle, u->handle ) == 0 ) ) - return u; - - /* However, it doesn't always work, so in that case we'll have to dig - through the whole userlist. :-( */ - for( u = ic->irc->users; u; u = u->next ) - if( u->ic == ic && u->handle && ic->acc->prpl->handle_cmp( u->handle, handle ) == 0 ) - return u; - - return NULL; -} - -/* DO NOT PASS u->nick FOR oldnick !!! */ -void user_rename( irc_t *irc, char *oldnick, char *newnick ) -{ - user_t *u = user_find( irc, oldnick ); - gpointer okey, ovalue; - char *key; - - if( !u ) return; /* Should've been checked by the caller... */ - - g_free( u->nick ); - if( u->nick == u->user ) u->user = NULL; - if( u->nick == u->host ) u->host = NULL; - if( u->nick == u->realname ) u->realname = NULL; - u->nick = g_strdup( newnick ); - if( !u->user ) u->user = u->nick; - if( !u->host ) u->host = u->nick; - if( !u->realname ) u->realname = u->nick; - - /* Remove the old reference to this user from the hash and create a - new one with the new nick. This is indeed a bit messy. */ - key = g_strdup( oldnick ); - nick_lc( key ); - if( !g_hash_table_lookup_extended( irc->userhash, key, &okey, &ovalue ) || ovalue != u ) - { - g_free( key ); - return; /* This really shouldn't happen! */ - } - g_hash_table_remove( irc->userhash, key ); - g_free( key ); - g_free( okey ); - - key = g_strdup( newnick ); - nick_lc( key ); - g_hash_table_insert( irc->userhash, key, u ); - - /* Also, let's try to keep the linked list nicely sorted. Fear this - code. If my teacher would see this, she would cry. ;-) */ - { - user_t *u1, *lu1; - - /* Remove the user from the old position in the chain. */ - if( u == irc->users ) - { - irc->users = u->next; - } - else - { - u1 = u; - for( lu1 = irc->users; lu1->next != u1; lu1 = lu1->next ); - lu1->next = u1->next; - } - - /* Search for the new position. */ - for( lu1 = NULL, u1 = irc->users; u1; u1 = u1->next ) - { - if( nick_cmp( newnick, u1->nick ) < 0 ) - break; - - lu1 = u1; - } - - /* Insert it at this new position. */ - u->next = u1; - if( lu1 ) - lu1->next = u; - else - irc->users = u; - } -} diff --git a/user.h b/user.h deleted file mode 100644 index 8c4f9c44..00000000 --- a/user.h +++ /dev/null @@ -1,62 +0,0 @@ - /********************************************************************\ - * BitlBee -- An IRC to other IM-networks gateway * - * * - * Copyright 2002-2004 Wilmer van der Gaast and others * - \********************************************************************/ - -/* Stuff to handle, save and search buddies */ - -/* - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License with - the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; - if not, write to the Free Software Foundation, Inc., 59 Temple Place, - Suite 330, Boston, MA 02111-1307 USA -*/ -#ifndef __USER_H__ -#define __USER_H__ - -typedef struct __USER -{ - char *nick; - char *user; - char *host; - char *realname; - - char *away; - char *status_msg; /* Non-IRC extension, but nice on IM. */ - - char is_private; - char online; - - char *handle; - char *group; - struct im_connection *ic; - - char *sendbuf; - time_t last_typing_notice; - int sendbuf_len; - guint sendbuf_timer; - int sendbuf_flags; - - void (*send_handler) ( irc_t *irc, struct __USER *u, char *msg, int flags ); - - struct __USER *next; -} user_t; - -user_t *user_add( struct irc *irc, char *nick ); -int user_del( irc_t *irc, char *nick ); -G_MODULE_EXPORT user_t *user_find( irc_t *irc, char *nick ); -G_MODULE_EXPORT user_t *user_findhandle( struct im_connection *ic, const char *handle ); -void user_rename( irc_t *irc, char *oldnick, char *newnick ); - -#endif /* __USER_H__ */ -- cgit v1.2.3