Changeset 2945c6f for irc.c


Ignore:
Timestamp:
2010-07-24T21:16:18Z (14 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
f1f7b5e
Parents:
ef14a83 (diff), 593971d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge ui-fix (which includes killerbee (i.e. file transfers and libpurple
support)). ui-fix rewrites the complete IRC core, fixing many things that
were broken/hacky/limited so far.

The list is too long to include here, but http://wiki.bitlbee.org/UiFix
has a summary, as does doc/CHANGES and of course the full revision history.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • irc.c

    ref14a83 r2945c6f  
    55  \********************************************************************/
    66
    7 /* The big hairy IRCd part of the project                               */
     7/* The IRC-based UI (for now the only one)                              */
    88
    99/*
     
    2424*/
    2525
    26 #define BITLBEE_CORE
    2726#include "bitlbee.h"
    28 #include "sock.h"
    29 #include "crypting.h"
    3027#include "ipc.h"
    31 
    32 static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond );
    33 
    34 GSList *irc_connection_list = NULL;
    35 
    36 static char *set_eval_password( set_t *set, char *value )
    37 {
    38         irc_t *irc = set->data;
    39        
    40         if( irc->status & USTATUS_IDENTIFIED && value )
    41         {
    42                 irc_setpass( irc, value );
    43                 return NULL;
    44         }
    45         else
    46         {
    47                 return SET_INVALID;
    48         }
    49 }
    50 
    51 static char *set_eval_charset( set_t *set, char *value )
    52 {
    53         irc_t *irc = set->data;
    54         char *test;
    55         gsize test_bytes = 0;
    56         GIConv ic, oc;
    57 
    58         if( g_strcasecmp( value, "none" ) == 0 )
    59                 value = g_strdup( "utf-8" );
    60 
    61         if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
    62         {
    63                 return NULL;
    64         }
    65        
    66         /* Do a test iconv to see if the user picked an IRC-compatible
    67            charset (for example utf-16 goes *horribly* wrong). */
    68         if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
    69             test_bytes > 1 )
    70         {
    71                 g_free( test );
    72                 g_iconv_close( oc );
    73                 irc_usermsg( irc, "Unsupported character set: The IRC protocol "
    74                                   "only supports 8-bit character sets." );
    75                 return NULL;
    76         }
    77         g_free( test );
    78        
    79         if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
    80         {
    81                 g_iconv_close( oc );
    82                 return NULL;
    83         }
    84        
    85         if( irc->iconv != (GIConv) -1 )
    86                 g_iconv_close( irc->iconv );
    87         if( irc->oconv != (GIConv) -1 )
    88                 g_iconv_close( irc->oconv );
    89        
    90         irc->iconv = ic;
    91         irc->oconv = oc;
    92 
    93         return value;
    94 }
    95 
    96 static char *set_eval_away_status( set_t *set, char *value )
    97 {
    98         irc_t *irc = set->data;
    99         account_t *a;
    100        
    101         g_free( set->value );
    102         set->value = g_strdup( value );
    103        
    104         for( a = irc->accounts; a; a = a->next )
    105         {
    106                 struct im_connection *ic = a->ic;
    107                
    108                 if( ic && ic->flags & OPT_LOGGED_IN )
    109                         imc_away_send_update( ic );
    110         }
    111        
    112         return value;
    113 }
     28#include "dcc.h"
     29
     30GSList *irc_connection_list;
     31
     32static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond );
     33static char *set_eval_charset( set_t *set, char *value );
     34static char *set_eval_password( set_t *set, char *value );
     35static char *set_eval_bw_compat( set_t *set, char *value );
    11436
    11537irc_t *irc_new( int fd )
     
    11840        struct sockaddr_storage sock;
    11941        socklen_t socklen = sizeof( sock );
     42        char *host = NULL, *myhost = NULL;
     43        irc_user_t *iu;
    12044        set_t *s;
     45        bee_t *b;
    12146       
    12247        irc = g_new0( irc_t, 1 );
     
    12550        sock_make_nonblocking( irc->fd );
    12651       
    127         irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc );
     52        irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc );
    12853       
    12954        irc->status = USTATUS_OFFLINE;
    13055        irc->last_pong = gettime();
    13156       
    132         irc->userhash = g_hash_table_new( g_str_hash, g_str_equal );
     57        irc->nick_user_hash = g_hash_table_new( g_str_hash, g_str_equal );
    13358        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
    134        
    135         strcpy( irc->umode, UMODE );
    136         irc->mynick = g_strdup( ROOT_NICK );
    137         irc->channel = g_strdup( ROOT_CHAN );
    13859       
    13960        irc->iconv = (GIConv) -1;
     
    14263        if( global.conf->hostname )
    14364        {
    144                 irc->myhost = g_strdup( global.conf->hostname );
     65                myhost = g_strdup( global.conf->hostname );
    14566        }
    14667        else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 )
     
    15172                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
    15273                {
    153                         irc->myhost = g_strdup( ipv6_unwrap( buf ) );
     74                        myhost = g_strdup( ipv6_unwrap( buf ) );
    15475                }
    15576        }
     
    16283                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
    16384                {
    164                         irc->host = g_strdup( ipv6_unwrap( buf ) );
    165                 }
    166         }
    167        
    168         if( irc->host == NULL )
    169                 irc->host = g_strdup( "localhost.localdomain" );
    170         if( irc->myhost == NULL )
    171                 irc->myhost = g_strdup( "localhost.localdomain" );
     85                        host = g_strdup( ipv6_unwrap( buf ) );
     86                }
     87        }
     88       
     89        if( host == NULL )
     90                host = g_strdup( "localhost.localdomain" );
     91        if( myhost == NULL )
     92                myhost = g_strdup( "localhost.localdomain" );
    17293       
    17394        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
    17495                irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
    175        
    176         irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
    17796
    17897        irc_connection_list = g_slist_append( irc_connection_list, irc );
    17998       
    180         s = set_add( &irc->set, "away", NULL,  set_eval_away_status, irc );
     99        b = irc->b = bee_new();
     100        b->ui_data = irc;
     101        b->ui = &irc_ui_funcs;
     102       
     103        s = set_add( &b->set, "allow_takeover", "true", set_eval_bool, irc );
     104        s = set_add( &b->set, "away_devoice", "true", set_eval_bw_compat, irc );
     105        s = set_add( &b->set, "away_reply_timeout", "3600", set_eval_int, irc );
     106        s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc );
     107        s = set_add( &b->set, "default_target", "root", NULL, irc );
     108        s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc );
     109        s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc );
     110        s = set_add( &b->set, "handle_unknown", "add_channel", NULL, irc );
     111        s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc );
     112        s = set_add( &b->set, "nick_format", "%-@nick", NULL, irc );
     113        s = set_add( &b->set, "offline_user_quits", "true", set_eval_bool, irc );
     114        s = set_add( &b->set, "ops", "both", set_eval_irc_channel_ops, irc );
     115        s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc );
     116        s->old_key = g_strdup( "buddy_sendbuffer" );
     117        s = set_add( &b->set, "paste_buffer_delay", "200", set_eval_int, irc );
     118        s->old_key = g_strdup( "buddy_sendbuffer_delay" );
     119        s = set_add( &b->set, "password", NULL, set_eval_password, irc );
    181120        s->flags |= SET_NULL_OK;
    182         s = set_add( &irc->set, "away_devoice", "true",  set_eval_away_devoice, irc );
    183         s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
    184         s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc );
    185         s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc );
    186         s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
    187         s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
    188         s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc );
    189         s = set_add( &irc->set, "control_channel", irc->channel, set_eval_control_channel, irc );
    190         s = set_add( &irc->set, "debug", "false", set_eval_bool, irc );
    191         s = set_add( &irc->set, "default_target", "root", NULL, irc );
    192         s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
    193         s = set_add( &irc->set, "display_timestamps", "true", set_eval_bool, irc );
    194         s = set_add( &irc->set, "handle_unknown", "root", NULL, irc );
    195         s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
    196         s = set_add( &irc->set, "ops", "both", set_eval_ops, irc );
    197         s = set_add( &irc->set, "password", NULL, set_eval_password, irc );
    198         s->flags |= SET_NULL_OK;
    199         s = set_add( &irc->set, "private", "true", set_eval_bool, irc );
    200         s = set_add( &irc->set, "query_order", "lifo", NULL, irc );
    201         s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
    202         s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
    203         s = set_add( &irc->set, "show_offline", "false", set_eval_bool, irc );
    204         s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
    205         s = set_add( &irc->set, "status", NULL,  set_eval_away_status, irc );
    206         s->flags |= SET_NULL_OK;
    207         s = set_add( &irc->set, "strip_html", "true", NULL, irc );
    208         s = set_add( &irc->set, "timezone", "local", set_eval_timezone, irc );
    209         s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
    210         s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc );
     121        s = set_add( &b->set, "private", "true", set_eval_bool, irc );
     122        s = set_add( &b->set, "query_order", "lifo", NULL, irc );
     123        s = set_add( &b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc );
     124        s = set_add( &b->set, "show_offline", "false", set_eval_bw_compat, irc );
     125        s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc );
     126        s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc );
     127        s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc );
     128        s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc );
     129
     130        irc->root = iu = irc_user_new( irc, ROOT_NICK );
     131        iu->host = g_strdup( myhost );
     132        iu->fullname = g_strdup( ROOT_FN );
     133        iu->f = &irc_user_root_funcs;
     134       
     135        iu = irc_user_new( irc, NS_NICK );
     136        iu->host = g_strdup( myhost );
     137        iu->fullname = g_strdup( ROOT_FN );
     138        iu->f = &irc_user_root_funcs;
     139       
     140        irc->user = g_new0( irc_user_t, 1 );
     141        irc->user->host = g_strdup( host );
    211142       
    212143        conf_loaddefaults( irc );
    213144       
    214145        /* Evaluator sets the iconv/oconv structures. */
    215         set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
    216        
    217         return( irc );
     146        set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) );
     147       
     148        irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" );
     149       
     150        g_free( myhost );
     151        g_free( host );
     152       
     153        nogaim_init();
     154       
     155        return irc;
    218156}
    219157
     
    236174               
    237175                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
    238                                    irc->nick ? irc->nick : "(NONE)", irc->host, reason );
     176                                   irc->user->nick ? irc->user->nick : "(NONE)", irc->user->host, reason );
    239177               
    240178                g_free( reason );
     
    246184               
    247185                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
    248                                    irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" );
     186                                   irc->user->nick ? irc->user->nick : "(NONE)", irc->user->host, "No reason given" );
    249187        }
    250188       
     
    267205}
    268206
    269 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
    270 {
    271         g_free( key );
    272        
    273         return( TRUE );
    274 }
    275 
    276 /* Because we have no garbage collection, this is quite annoying */
     207static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data );
     208
    277209void irc_free( irc_t * irc )
    278210{
    279         user_t *user, *usertmp;
    280        
    281211        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
    282212       
    283         if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) )
     213        if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) )
    284214                if( storage_save( irc, NULL, TRUE ) != STORAGE_OK )
    285                         irc_usermsg( irc, "Error while saving settings!" );
     215                        log_message( LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick );
    286216       
    287217        irc_connection_list = g_slist_remove( irc_connection_list, irc );
    288        
    289         while( irc->accounts )
    290         {
    291                 if( irc->accounts->ic )
    292                         imc_logout( irc->accounts->ic, FALSE );
    293                 else if( irc->accounts->reconnect )
    294                         cancel_auto_reconnect( irc->accounts );
    295                
    296                 if( irc->accounts->ic == NULL )
    297                         account_del( irc, irc->accounts );
    298                 else
    299                         /* Nasty hack, but account_del() doesn't work in this
    300                            case and we don't want infinite loops, do we? ;-) */
    301                         irc->accounts = irc->accounts->next;
    302         }
    303218       
    304219        while( irc->queries != NULL )
    305220                query_del( irc, irc->queries );
    306221       
    307         while( irc->set )
    308                 set_del( &irc->set, irc->set->key );
    309        
    310         if (irc->users != NULL)
    311         {
    312                 user = irc->users;
    313                 while( user != NULL )
    314                 {
    315                         g_free( user->nick );
    316                         g_free( user->away );
    317                         g_free( user->handle );
    318                         if( user->user != user->nick ) g_free( user->user );
    319                         if( user->host != user->nick ) g_free( user->host );
    320                         if( user->realname != user->nick ) g_free( user->realname );
    321                         b_event_remove( user->sendbuf_timer );
    322                                        
    323                         usertmp = user;
    324                         user = user->next;
    325                         g_free( usertmp );
    326                 }
    327         }
     222        /* This is a little bit messy: bee_free() frees all b->users which
     223           calls us back to free the corresponding irc->users. So do this
     224           before we clear the remaining ones ourselves. */
     225        bee_free( irc->b );
     226       
     227        while( irc->users )
     228                irc_user_free( irc, (irc_user_t *) irc->users->data );
     229       
     230        while( irc->channels )
     231                irc_channel_free( irc->channels->data );
    328232       
    329233        if( irc->ping_source_id > 0 )
     
    337241        irc->fd = -1;
    338242       
    339         g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
    340         g_hash_table_destroy( irc->userhash );
     243        g_hash_table_foreach_remove( irc->nick_user_hash, irc_free_hashkey, NULL );
     244        g_hash_table_destroy( irc->nick_user_hash );
    341245       
    342246        g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
     
    350254        g_free( irc->sendbuffer );
    351255        g_free( irc->readbuffer );
    352        
    353         g_free( irc->nick );
    354         g_free( irc->user );
    355         g_free( irc->host );
    356         g_free( irc->realname );
    357256        g_free( irc->password );
    358        
    359         g_free( irc->myhost );
    360         g_free( irc->mynick );
    361        
    362         g_free( irc->channel );
    363        
    364         g_free( irc->last_target );
     257        g_free( irc->last_root_cmd );
    365258       
    366259        g_free( irc );
     
    374267}
    375268
     269static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
     270{
     271        g_free( key );
     272       
     273        return( TRUE );
     274}
     275
    376276/* USE WITH CAUTION!
    377277   Sets pass without checking */
    378 void irc_setpass (irc_t *irc, const char *pass) 
     278void irc_setpass (irc_t *irc, const char *pass)
    379279{
    380280        g_free (irc->password);
     
    387287}
    388288
     289static char *set_eval_password( set_t *set, char *value )
     290{
     291        irc_t *irc = set->data;
     292       
     293        if( irc->status & USTATUS_IDENTIFIED && value )
     294        {
     295                irc_setpass( irc, value );
     296                return NULL;
     297        }
     298        else
     299        {
     300                return SET_INVALID;
     301        }
     302}
     303
     304static char **irc_splitlines( char *buffer );
     305
    389306void irc_process( irc_t *irc )
    390307{
     
    394311        if( irc->readbuffer != NULL )
    395312        {
    396                 lines = irc_tokenize( irc->readbuffer );
     313                lines = irc_splitlines( irc->readbuffer );
    397314               
    398315                for( i = 0; *lines[i] != '\0'; i ++ )
     
    431348                                                                  "`help set charset' for more information. Your "
    432349                                                                  "message was ignored.",
    433                                                                   set_getstr( &irc->set, "charset" ) );
     350                                                                  set_getstr( &irc->b->set, "charset" ) );
    434351                                               
    435352                                                g_free( conv );
     
    438355                                        else
    439356                                        {
    440                                                 irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
     357                                                irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host,
    441358                                                           "Warning: invalid characters received at login time." );
    442359                                               
     
    476393}
    477394
    478 /* Splits a long string into separate lines. The array is NULL-terminated and, unless the string
    479    contains an incomplete line at the end, ends with an empty string. */
    480 char **irc_tokenize( char *buffer )
     395/* Splits a long string into separate lines. The array is NULL-terminated
     396   and, unless the string contains an incomplete line at the end, ends with
     397   an empty string. Could use g_strsplit() but this one does it in-place.
     398   (So yes, it's destructive.) */
     399static char **irc_splitlines( char *buffer )
    481400{
    482401        int i, j, n = 3;
     
    609528}
    610529
    611 void irc_reply( irc_t *irc, int code, char *format, ... )
    612 {
    613         char text[IRC_MAX_LINE];
    614         va_list params;
    615        
    616         va_start( params, format );
    617         g_vsnprintf( text, IRC_MAX_LINE, format, params );
    618         va_end( params );
    619         irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
    620        
    621         return;
    622 }
    623 
    624 int irc_usermsg( irc_t *irc, char *format, ... )
    625 {
    626         char text[1024];
    627         va_list params;
    628         char is_private = 0;
    629         user_t *u;
    630        
    631         u = user_find( irc, irc->mynick );
    632         is_private = u->is_private;
    633        
    634         va_start( params, format );
    635         g_vsnprintf( text, sizeof( text ), format, params );
    636         va_end( params );
    637        
    638         return( irc_msgfrom( irc, u->nick, text ) );
    639 }
    640 
    641530void irc_write( irc_t *irc, char *format, ... )
    642531{
     
    649538        return;
    650539}
     540
     541void irc_write_all( int now, char *format, ... )
     542{
     543        va_list params;
     544        GSList *temp;   
     545       
     546        va_start( params, format );
     547       
     548        temp = irc_connection_list;
     549        while( temp != NULL )
     550        {
     551                irc_t *irc = temp->data;
     552               
     553                if( now )
     554                {
     555                        g_free( irc->sendbuffer );
     556                        irc->sendbuffer = g_strdup( "\r\n" );
     557                }
     558                irc_vawrite( temp->data, format, params );
     559                if( now )
     560                {
     561                        bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE );
     562                }
     563                temp = temp->next;
     564        }
     565       
     566        va_end( params );
     567        return;
     568}
    651569
    652570void irc_vawrite( irc_t *irc, char *format, va_list params )
     
    696614                   in the event queue. */
    697615                /* Really can't be done as long as the code doesn't do error checking very well:
    698                 if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */
     616                if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */
    699617               
    700618                /* So just always do it via the event handler. */
    701                 irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc );
     619                irc->w_watch_source_id = b_input_add( irc->fd, B_EV_IO_WRITE, bitlbee_io_current_client_write, irc );
    702620        }
    703621       
     
    705623}
    706624
    707 void irc_write_all( int now, char *format, ... )
    708 {
    709         va_list params;
    710         GSList *temp;   
    711        
    712         va_start( params, format );
    713        
    714         temp = irc_connection_list;
    715         while( temp != NULL )
    716         {
    717                 irc_t *irc = temp->data;
    718                
    719                 if( now )
    720                 {
    721                         g_free( irc->sendbuffer );
    722                         irc->sendbuffer = g_strdup( "\r\n" );
    723                 }
    724                 irc_vawrite( temp->data, format, params );
    725                 if( now )
    726                 {
    727                         bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
    728                 }
    729                 temp = temp->next;
    730         }
    731        
    732         va_end( params );
    733         return;
    734 }
    735 
    736 void irc_names( irc_t *irc, char *channel )
    737 {
    738         user_t *u;
    739         char namelist[385] = "";
    740         struct groupchat *c = NULL;
    741         char *ops = set_getstr( &irc->set, "ops" );
    742        
    743         /* RFCs say there is no error reply allowed on NAMES, so when the
    744            channel is invalid, just give an empty reply. */
    745        
    746         if( g_strcasecmp( channel, irc->channel ) == 0 )
    747         {
    748                 for( u = irc->users; u; u = u->next ) if( u->online )
    749                 {
    750                         if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
     625/* Flush sendbuffer if you can. If it fails, fail silently and let some
     626   I/O event handler clean up. */
     627void irc_flush( irc_t *irc )
     628{
     629        ssize_t n;
     630        size_t len;
     631       
     632        if( irc->sendbuffer == NULL )
     633                return;
     634       
     635        len = strlen( irc->sendbuffer );
     636        if( ( n = send( irc->fd, irc->sendbuffer, len, 0 ) ) == len )
     637        {
     638                g_free( irc->sendbuffer );
     639                irc->sendbuffer = NULL;
     640               
     641                b_event_remove( irc->w_watch_source_id );
     642                irc->w_watch_source_id = 0;
     643        }
     644        else if( n > 0 )
     645        {
     646                char *s = g_strdup( irc->sendbuffer + n );
     647                g_free( irc->sendbuffer );
     648                irc->sendbuffer = s;
     649        }
     650        /* Otherwise something went wrong and we don't currently care
     651           what the error was. We may or may not succeed later, we
     652           were just trying to flush the buffer immediately. */
     653}
     654
     655/* Meant for takeover functionality. Transfer an IRC connection to a different
     656   socket. */
     657void irc_switch_fd( irc_t *irc, int fd )
     658{
     659        irc_write( irc, "ERROR :Transferring session to a new connection" );
     660        irc_flush( irc ); /* Write it now or forget about it forever. */
     661       
     662        if( irc->sendbuffer )
     663        {
     664                b_event_remove( irc->w_watch_source_id );
     665                irc->w_watch_source_id = 0;
     666                g_free( irc->sendbuffer );
     667                irc->sendbuffer = NULL;
     668        }
     669       
     670        b_event_remove( irc->r_watch_source_id );
     671        closesocket( irc->fd );
     672        irc->fd = fd;
     673        irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc );
     674}
     675
     676void irc_sync( irc_t *irc )
     677{
     678        GSList *l;
     679       
     680        irc_write( irc, ":%s!%s@%s MODE %s :+%s", irc->user->nick,
     681                   irc->user->user, irc->user->host, irc->user->nick,
     682                   irc->umode );
     683       
     684        for( l = irc->channels; l; l = l->next )
     685        {
     686                irc_channel_t *ic = l->data;
     687                if( ic->flags & IRC_CHANNEL_JOINED )
     688                        irc_send_join( ic, irc->user );
     689        }
     690}
     691
     692void irc_desync( irc_t *irc )
     693{
     694        GSList *l;
     695       
     696        for( l = irc->channels; l; l = l->next )
     697                irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK,
     698                                      "Switching to old session" );
     699       
     700        irc_write( irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick,
     701                   irc->user->user, irc->user->host, irc->user->nick,
     702                   irc->umode );
     703}
     704
     705int irc_check_login( irc_t *irc )
     706{
     707        if( irc->user->user && irc->user->nick )
     708        {
     709                if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
     710                {
     711                        irc_send_num( irc, 464, ":This server is password-protected." );
     712                        return 0;
     713                }
     714                else
     715                {
     716                        irc_channel_t *ic;
     717                        irc_user_t *iu = irc->user;
     718                       
     719                        irc->user = irc_user_new( irc, iu->nick );
     720                        irc->user->user = iu->user;
     721                        irc->user->host = iu->host;
     722                        irc->user->fullname = iu->fullname;
     723                        irc->user->f = &irc_user_self_funcs;
     724                        g_free( iu->nick );
     725                        g_free( iu );
     726                       
     727                        if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
     728                                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
     729                       
     730                        irc->status |= USTATUS_LOGGED_IN;
     731                       
     732                        irc_send_login( irc );
     733                       
     734                        irc->umode[0] = '\0';
     735                        irc_umode_set( irc, "+" UMODE, TRUE );
     736                       
     737                        ic = irc->default_channel = irc_channel_new( irc, ROOT_CHAN );
     738                        irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root );
     739                        set_setstr( &ic->set, "auto_join", "true" );
     740                        irc_channel_auto_joins( irc, NULL );
     741                       
     742                        irc->last_root_cmd = g_strdup( ROOT_CHAN );
     743                       
     744                        irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN,
     745                                      "Welcome to the BitlBee gateway!\n\n"
     746                                      "If you've never used BitlBee before, please do read the help "
     747                                      "information using the \x02help\x02 command. Lots of FAQs are "
     748                                      "answered there.\n"
     749                                      "If you already have an account on this server, just use the "
     750                                      "\x02identify\x02 command to identify yourself.", NULL );
     751                       
     752                        /* This is for bug #209 (use PASS to identify to NickServ). */
     753                        if( irc->password != NULL )
    751754                        {
    752                                 irc_reply( irc, 353, "= %s :%s", channel, namelist );
    753                                 *namelist = 0;
     755                                char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
     756                               
     757                                irc_setpass( irc, NULL );
     758                                root_command( irc, send_cmd );
     759                                g_free( send_cmd[1] );
    754760                        }
    755761                       
    756                         if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) )
    757                                 strcat( namelist, "+" );
    758                         else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ||
    759                                  ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) )
    760                                 strcat( namelist, "@" );
    761                        
    762                         strcat( namelist, u->nick );
    763                         strcat( namelist, " " );
    764                 }
    765         }
    766         else if( ( c = irc_chat_by_channel( irc, channel ) ) )
    767         {
    768                 GList *l;
    769                
    770                 /* root and the user aren't in the channel userlist but should
    771                    show up in /NAMES, so list them first: */
    772                 sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick,
    773                                                  strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick );
    774                
    775                 for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) )
    776                 {
    777                         if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
    778                         {
    779                                 irc_reply( irc, 353, "= %s :%s", channel, namelist );
    780                                 *namelist = 0;
    781                         }
    782                        
    783                         strcat( namelist, u->nick );
    784                         strcat( namelist, " " );
    785                 }
    786         }
    787        
    788         if( *namelist )
    789                 irc_reply( irc, 353, "= %s :%s", channel, namelist );
    790        
    791         irc_reply( irc, 366, "%s :End of /NAMES list", channel );
    792 }
    793 
    794 int irc_check_login( irc_t *irc )
    795 {
    796         if( irc->user && irc->nick )
    797         {
    798                 if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
    799                 {
    800                         irc_reply( irc, 464, ":This server is password-protected." );
    801                         return 0;
    802                 }
    803                 else
    804                 {
    805                         irc_login( irc );
    806762                        return 1;
    807763                }
     
    814770}
    815771
    816 void irc_login( irc_t *irc )
    817 {
    818         user_t *u;
    819        
    820         irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
    821         irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
    822         irc_reply( irc,   3, ":%s", IRCD_INFO );
    823         irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
    824         irc_reply( irc,   5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee "
    825                              "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server",
    826                              CTYPES, CMODES, MAX_NICK_LENGTH - 1 );
    827         irc_motd( irc );
    828         irc->umode[0] = '\0';
    829         irc_umode_set( irc, "+" UMODE, 1 );
    830 
    831         u = user_add( irc, irc->mynick );
    832         u->host = g_strdup( irc->myhost );
    833         u->realname = g_strdup( ROOT_FN );
    834         u->online = 1;
    835         u->send_handler = root_command_string;
    836         u->is_private = 0; /* [SH] The channel is root's personal playground. */
    837         irc_spawn( irc, u );
    838        
    839         u = user_add( irc, NS_NICK );
    840         u->host = g_strdup( irc->myhost );
    841         u->realname = g_strdup( ROOT_FN );
    842         u->online = 0;
    843         u->send_handler = root_command_string;
    844         u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
    845        
    846         u = user_add( irc, irc->nick );
    847         u->user = g_strdup( irc->user );
    848         u->host = g_strdup( irc->host );
    849         u->realname = g_strdup( irc->realname );
    850         u->online = 1;
    851         irc_spawn( irc, u );
    852        
    853         irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
    854                           "If you've never used BitlBee before, please do read the help "
    855                           "information using the \x02help\x02 command. Lots of FAQs are "
    856                           "answered there.\n"
    857                           "If you already have an account on this server, just use the "
    858                           "\x02identify\x02 command to identify yourself." );
    859        
    860         if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
    861                 ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
    862        
    863         irc->status |= USTATUS_LOGGED_IN;
    864        
    865         /* This is for bug #209 (use PASS to identify to NickServ). */
    866         if( irc->password != NULL )
    867         {
    868                 char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
    869                
    870                 irc_setpass( irc, NULL );
    871                 root_command( irc, send_cmd );
    872                 g_free( send_cmd[1] );
    873         }
    874 }
    875 
    876 void irc_motd( irc_t *irc )
    877 {
    878         int fd;
    879        
    880         fd = open( global.conf->motdfile, O_RDONLY );
    881         if( fd == -1 )
    882         {
    883                 irc_reply( irc, 422, ":We don't need MOTDs." );
    884         }
    885         else
    886         {
    887                 char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
    888                 char *add, max;
    889                 int len;
    890                
    891                 linebuf[79] = len = 0;
    892                 max = sizeof( linebuf ) - 1;
    893                
    894                 irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
    895                 while( read( fd, linebuf + len, 1 ) == 1 )
    896                 {
    897                         if( linebuf[len] == '\n' || len == max )
    898                         {
    899                                 linebuf[len] = 0;
    900                                 irc_reply( irc, 372, ":- %s", linebuf );
    901                                 len = 0;
    902                         }
    903                         else if( linebuf[len] == '%' )
    904                         {
    905                                 read( fd, linebuf + len, 1 );
    906                                 if( linebuf[len] == 'h' )
    907                                         add = irc->myhost;
    908                                 else if( linebuf[len] == 'v' )
    909                                         add = BITLBEE_VERSION;
    910                                 else if( linebuf[len] == 'n' )
    911                                         add = irc->nick;
    912                                 else
    913                                         add = "%";
    914                                
    915                                 strncpy( linebuf + len, add, max - len );
    916                                 while( linebuf[++len] );
    917                         }
    918                         else if( len < max )
    919                         {
    920                                 len ++;
    921                         }
    922                 }
    923                 irc_reply( irc, 376, ":End of MOTD" );
    924                 close( fd );
    925         }
    926 }
    927 
    928 void irc_topic( irc_t *irc, char *channel )
    929 {
    930         struct groupchat *c = irc_chat_by_channel( irc, channel );
    931        
    932         if( c && c->topic )
    933                 irc_reply( irc, 332, "%s :%s", channel, c->topic );
    934         else if( g_strcasecmp( channel, irc->channel ) == 0 )
    935                 irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
    936         else
    937                 irc_reply( irc, 331, "%s :No topic for this channel", channel );
    938 }
    939 
    940 void irc_umode_set( irc_t *irc, char *s, int allow_priv )
     772void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv )
    941773{
    942774        /* allow_priv: Set to 0 if s contains user input, 1 if you want
    943775           to set a "privileged" mode (+o, +R, etc). */
    944         char m[256], st = 1, *t;
     776        char m[128], st = 1;
     777        const char *t;
    945778        int i;
    946779        char changes[512], *p, st2 = 2;
     
    950783       
    951784        for( t = irc->umode; *t; t ++ )
    952                 m[(int)*t] = 1;
    953 
     785                if( *t < sizeof( m ) )
     786                        m[(int)*t] = 1;
     787       
    954788        p = changes;
    955789        for( t = s; *t; t ++ )
     
    957791                if( *t == '+' || *t == '-' )
    958792                        st = *t == '+';
    959                 else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
     793                else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) ||
     794                         ( st == 1 && strchr( UMODES, *t ) ) ||
     795                         ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) )
    960796                {
    961797                        if( m[(int)*t] != st)
     
    974810        memset( irc->umode, 0, sizeof( irc->umode ) );
    975811       
    976         for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
     812        for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
    977813                if( m[i] )
    978814                        irc->umode[strlen(irc->umode)] = i;
    979815       
    980816        if( badflag )
    981                 irc_reply( irc, 501, ":Unknown MODE flag" );
    982         /* Deliberately no !user@host on the prefix here */
     817                irc_send_num( irc, 501, ":Unknown MODE flag" );
    983818        if( *changes )
    984                 irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes );
    985 }
    986 
    987 void irc_spawn( irc_t *irc, user_t *u )
    988 {
    989         irc_join( irc, u, irc->channel );
    990 }
    991 
    992 void irc_join( irc_t *irc, user_t *u, char *channel )
    993 {
    994         char *nick;
    995        
    996         if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
    997                 irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
    998        
    999         if( nick_cmp( u->nick, irc->nick ) == 0 )
    1000         {
    1001                 irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
    1002                 irc_names( irc, channel );
    1003                 irc_topic( irc, channel );
    1004         }
    1005        
    1006         nick = g_strdup( u->nick );
    1007         nick_lc( nick );
    1008         if( g_hash_table_lookup( irc->watches, nick ) )
    1009         {
    1010                 irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" );
    1011         }
    1012         g_free( nick );
    1013 }
    1014 
    1015 void irc_part( irc_t *irc, user_t *u, char *channel )
    1016 {
    1017         irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
    1018 }
    1019 
    1020 void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
    1021 {
    1022         irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
    1023 }
    1024 
    1025 void irc_kill( irc_t *irc, user_t *u )
    1026 {
    1027         char *nick, *s;
    1028         char reason[128];
    1029        
    1030         if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) )
    1031         {
    1032                 if( u->ic->acc->server )
    1033                         g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
    1034                                     u->ic->acc->server );
    1035                 else if( ( s = strchr( u->ic->acc->user, '@' ) ) )
    1036                         g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
    1037                                     s + 1 );
    1038                 else
    1039                         g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
    1040                                     u->ic->acc->prpl->name, irc->myhost );
    1041                
    1042                 /* proto_opt might contain garbage after the : */
    1043                 if( ( s = strchr( reason, ':' ) ) )
    1044                         *s = 0;
    1045         }
    1046         else
    1047         {
    1048                 strcpy( reason, "Leaving..." );
    1049         }
    1050        
    1051         irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
    1052        
    1053         nick = g_strdup( u->nick );
    1054         nick_lc( nick );
    1055         if( g_hash_table_lookup( irc->watches, nick ) )
    1056         {
    1057                 irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
    1058         }
    1059         g_free( nick );
    1060 }
    1061 
    1062 int irc_send( irc_t *irc, char *nick, char *s, int flags )
    1063 {
    1064         struct groupchat *c = NULL;
    1065         user_t *u = NULL;
    1066        
    1067         if( strchr( CTYPES, *nick ) )
    1068         {
    1069                 if( !( c = irc_chat_by_channel( irc, nick ) ) )
    1070                 {
    1071                         irc_reply( irc, 403, "%s :Channel does not exist", nick );
    1072                         return( 0 );
    1073                 }
    1074         }
    1075         else
    1076         {
    1077                 u = user_find( irc, nick );
    1078                
    1079                 if( !u )
    1080                 {
    1081                         if( irc->is_private )
    1082                                 irc_reply( irc, 401, "%s :Nick does not exist", nick );
    1083                         else
    1084                                 irc_usermsg( irc, "Nick `%s' does not exist!", nick );
    1085                         return( 0 );
    1086                 }
    1087         }
    1088        
    1089         if( *s == 1 && s[strlen(s)-1] == 1 )
    1090         {
    1091                 if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
    1092                 {
    1093                         if( s[7] == ' ' ) s ++;
    1094                         s += 3;
    1095                         *(s++) = '/';
    1096                         *(s++) = 'm';
    1097                         *(s++) = 'e';
    1098                         *(s++) = ' ';
    1099                         s -= 4;
    1100                         s[strlen(s)-1] = 0;
    1101                 }
    1102                 else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
    1103                 {
    1104                         u = user_find( irc, irc->mynick );
    1105                         irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
    1106                         return( 1 );
    1107                 }
    1108                 else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
    1109                 {
    1110                         u = user_find( irc, irc->mynick );
    1111                         irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
    1112                         return( 1 );
    1113                 }
    1114                 else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
    1115                 {
    1116                         if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 )
    1117                         {
    1118                                 time_t current_typing_notice = time( NULL );
    1119                                
    1120                                 if( current_typing_notice - u->last_typing_notice >= 5 )
    1121                                 {
    1122                                         u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 );
    1123                                         u->last_typing_notice = current_typing_notice;
    1124                                 }
    1125                         }
    1126                         return( 1 );
    1127                 }
    1128                 else
    1129                 {
    1130                         irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
    1131                         return( 0 );
    1132                 }
    1133         }
    1134        
    1135         if( u )
    1136         {
    1137                 /* For the next message, we probably do have to send new notices... */
    1138                 u->last_typing_notice = 0;
    1139                 u->is_private = irc->is_private;
    1140                
    1141                 if( u->is_private )
    1142                 {
    1143                         if( !u->online )
    1144                                 irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
    1145                         else if( u->away )
    1146                                 irc_reply( irc, 301, "%s :%s", u->nick, u->away );
    1147                 }
    1148                
    1149                 if( u->send_handler )
    1150                 {
    1151                         u->send_handler( irc, u, s, flags );
    1152                         return 1;
    1153                 }
    1154         }
    1155         else if( c && c->ic && c->ic->acc && c->ic->acc->prpl )
    1156         {
    1157                 return( imc_chat_msg( c, s, 0 ) );
    1158         }
    1159        
    1160         return( 0 );
    1161 }
    1162 
    1163 static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond )
    1164 {
    1165         user_t *u = data;
    1166        
    1167         /* Shouldn't happen, but just to be sure. */
    1168         if( u->sendbuf_len < 2 )
    1169                 return FALSE;
    1170        
    1171         u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
    1172         imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags );
    1173        
    1174         g_free( u->sendbuf );
    1175         u->sendbuf = NULL;
    1176         u->sendbuf_len = 0;
    1177         u->sendbuf_timer = 0;
    1178         u->sendbuf_flags = 0;
    1179        
    1180         return FALSE;
    1181 }
    1182 
    1183 void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
    1184 {
    1185         if( !u || !u->ic ) return;
    1186        
    1187         if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 )
    1188         {
    1189                 int delay;
    1190                
    1191                 if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
    1192                 {
    1193                         /* Flush the buffer */
    1194                         b_event_remove( u->sendbuf_timer );
    1195                         buddy_send_handler_delayed( u, -1, 0 );
    1196                 }
    1197 
    1198                 if( u->sendbuf_len == 0 )
    1199                 {
    1200                         u->sendbuf_len = strlen( msg ) + 2;
    1201                         u->sendbuf = g_new( char, u->sendbuf_len );
    1202                         u->sendbuf[0] = 0;
    1203                         u->sendbuf_flags = flags;
    1204                 }
    1205                 else
    1206                 {
    1207                         u->sendbuf_len += strlen( msg ) + 1;
    1208                         u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
    1209                 }
    1210                
    1211                 strcat( u->sendbuf, msg );
    1212                 strcat( u->sendbuf, "\n" );
    1213                
    1214                 delay = set_getint( &irc->set, "buddy_sendbuffer_delay" );
    1215                 if( delay <= 5 )
    1216                         delay *= 1000;
    1217                
    1218                 if( u->sendbuf_timer > 0 )
    1219                         b_event_remove( u->sendbuf_timer );
    1220                 u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u );
    1221         }
    1222         else
    1223         {
    1224                 imc_buddy_msg( u->ic, u->handle, msg, flags );
    1225         }
    1226 }
    1227 
    1228 int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
    1229 {
    1230         char last = 0;
    1231         char *s = msg, *line = msg;
    1232        
    1233         /* The almighty linesplitter .. woohoo!! */
    1234         while( !last )
    1235         {
    1236                 if( *s == '\r' && *(s+1) == '\n' )
    1237                         *(s++) = 0;
    1238                 if( *s == '\n' )
    1239                 {
    1240                         last = s[1] == 0;
    1241                         *s = 0;
    1242                 }
    1243                 else
    1244                 {
    1245                         last = s[0] == 0;
    1246                 }
    1247                 if( *s == 0 )
    1248                 {
    1249                         if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
    1250                         {
    1251                                 irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
    1252                                            type, to, line + 4 );
    1253                         }
    1254                         else
    1255                         {
    1256                                 irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
    1257                                            type, to, prefix ? prefix : "", line );
    1258                         }
    1259                         line = s + 1;
    1260                 }
    1261                 s ++;
    1262         }
    1263        
    1264         return( 1 );
    1265 }
    1266 
    1267 int irc_msgfrom( irc_t *irc, char *nick, char *msg )
    1268 {
    1269         user_t *u = user_find( irc, nick );
    1270         static char *prefix = NULL;
    1271        
    1272         if( !u ) return( 0 );
    1273         if( prefix && *prefix ) g_free( prefix );
    1274        
    1275         if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
    1276         {
    1277                 int len = strlen( irc->nick) + 3;
    1278                 prefix = g_new (char, len );
    1279                 g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) );
    1280                 prefix[len-1] = 0;
    1281         }
    1282         else
    1283         {
    1284                 prefix = "";
    1285         }
    1286        
    1287         return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
    1288 }
    1289 
    1290 int irc_noticefrom( irc_t *irc, char *nick, char *msg )
    1291 {
    1292         user_t *u = user_find( irc, nick );
    1293        
    1294         if( u )
    1295                 return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
    1296         else
    1297                 return( 0 );
     819                irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
     820                           irc->user->user, irc->user->host, irc->user->nick,
     821                           changes );
    1298822}
    1299823
     
    1334858}
    1335859
    1336 struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
    1337 {
    1338         struct groupchat *c;
    1339         account_t *a;
    1340        
    1341         /* This finds the connection which has a conversation which belongs to this channel */
    1342         for( a = irc->accounts; a; a = a->next )
    1343         {
    1344                 if( a->ic == NULL )
    1345                         continue;
    1346                
    1347                 c = a->ic->groupchats;
    1348                 while( c )
    1349                 {
    1350                         if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
    1351                                 return c;
    1352                        
    1353                         c = c->next;
    1354                 }
    1355         }
    1356        
    1357         return NULL;
    1358 }
     860static char *set_eval_charset( set_t *set, char *value )
     861{
     862        irc_t *irc = (irc_t*) set->data;
     863        char *test;
     864        gsize test_bytes = 0;
     865        GIConv ic, oc;
     866
     867        if( g_strcasecmp( value, "none" ) == 0 )
     868                value = g_strdup( "utf-8" );
     869
     870        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
     871        {
     872                return NULL;
     873        }
     874       
     875        /* Do a test iconv to see if the user picked an IRC-compatible
     876           charset (for example utf-16 goes *horribly* wrong). */
     877        if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
     878            test_bytes > 1 )
     879        {
     880                g_free( test );
     881                g_iconv_close( oc );
     882                irc_usermsg( irc, "Unsupported character set: The IRC protocol "
     883                                  "only supports 8-bit character sets." );
     884                return NULL;
     885        }
     886        g_free( test );
     887       
     888        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
     889        {
     890                g_iconv_close( oc );
     891                return NULL;
     892        }
     893       
     894        if( irc->iconv != (GIConv) -1 )
     895                g_iconv_close( irc->iconv );
     896        if( irc->oconv != (GIConv) -1 )
     897                g_iconv_close( irc->oconv );
     898       
     899        irc->iconv = ic;
     900        irc->oconv = oc;
     901
     902        return value;
     903}
     904
     905/* Mostly meant for upgrades. If one of these is set to the non-default,
     906   set show_users of all channels to something with the same effect. */
     907static char *set_eval_bw_compat( set_t *set, char *value )
     908{
     909        irc_t *irc = set->data;
     910        char *val;
     911        GSList *l;
     912       
     913        irc_usermsg( irc, "Setting `%s' is obsolete, use the `show_users' "
     914                     "channel setting instead.", set->key );
     915       
     916        if( strcmp( set->key, "away_devoice" ) == 0 && !bool2int( value ) )
     917                val = "online,away";
     918        else if( strcmp( set->key, "show_offline" ) == 0 && bool2int( value ) )
     919                val = "online@,away+,offline";
     920        else
     921                return SET_INVALID;
     922       
     923        for( l = irc->channels; l; l = l->next )
     924        {
     925                irc_channel_t *ic = l->data;
     926                /* No need to check channel type, if the setting doesn't exist it
     927                   will just be ignored. */
     928                set_setstr( &ic->set, "show_users", val );
     929        }
     930       
     931        return SET_INVALID;
     932}
Note: See TracChangeset for help on using the changeset viewer.