source: irc.c @ 88eaf4b

Last change on this file since 88eaf4b was 88eaf4b, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-06T01:24:13Z

buddy_sendbuffer should be renamed, since it has to work for users and
chats. For smooth transitions, add an old_key attribute to settings
which can be used as a (temporary) alias.

  • Property mode set to 100644
File size: 21.2 KB
RevLine 
[b7d3cc34]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2004 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
[3ddb7477]7/* The IRC-based UI (for now the only one)                              */
[b7d3cc34]8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#include "bitlbee.h"
[fb117aee]27#include "ipc.h"
[b7d3cc34]28
[3ddb7477]29GSList *irc_connection_list;
[b7d3cc34]30
[3923003]31static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond );
[3ddb7477]32static char *set_eval_charset( set_t *set, char *value );
[58adb7e]33
[b7d3cc34]34irc_t *irc_new( int fd )
35{
[e4d6271]36        irc_t *irc;
[e9b755e]37        struct sockaddr_storage sock;
[7435ccf]38        socklen_t socklen = sizeof( sock );
[3ddb7477]39        char *host = NULL, *myhost = NULL;
40        irc_user_t *iu;
[7125cb3]41        set_t *s;
[3ddb7477]42        bee_t *b;
[e4d6271]43       
44        irc = g_new0( irc_t, 1 );
[b7d3cc34]45       
46        irc->fd = fd;
[a0d04d6]47        sock_make_nonblocking( irc->fd );
48       
[ba9edaa]49        irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc );
[b7d3cc34]50       
51        irc->status = USTATUS_OFFLINE;
52        irc->last_pong = gettime();
53       
[3ddb7477]54        irc->nick_user_hash = g_hash_table_new( g_str_hash, g_str_equal );
[b7d3cc34]55        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
56       
[f9756bd]57        irc->iconv = (GIConv) -1;
58        irc->oconv = (GIConv) -1;
59       
[b7d3cc34]60        if( global.conf->hostname )
61        {
[3ddb7477]62                myhost = g_strdup( global.conf->hostname );
[b7d3cc34]63        }
[7435ccf]64        else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 ) 
[b7d3cc34]65        {
[2231302]66                char buf[NI_MAXHOST+1];
[e9b755e]67
[2231302]68                if( getnameinfo( (struct sockaddr *) &sock, socklen, buf,
[c84e31a]69                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
[2231302]70                {
[3ddb7477]71                        myhost = g_strdup( ipv6_unwrap( buf ) );
[2231302]72                }
[b7d3cc34]73        }
74       
[7435ccf]75        if( getpeername( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 )
[b7d3cc34]76        {
[2231302]77                char buf[NI_MAXHOST+1];
[e9b755e]78
[2231302]79                if( getnameinfo( (struct sockaddr *)&sock, socklen, buf,
[c84e31a]80                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
[2231302]81                {
[3ddb7477]82                        host = g_strdup( ipv6_unwrap( buf ) );
[2231302]83                }
[b7d3cc34]84        }
85       
[3ddb7477]86        if( host == NULL )
87                host = g_strdup( "localhost.localdomain" );
88        if( myhost == NULL )
89                myhost = g_strdup( "localhost.localdomain" );
[3e1e11af]90       
[3923003]91        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
92                irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
[b7d3cc34]93
94        irc_connection_list = g_slist_append( irc_connection_list, irc );
95       
[3ddb7477]96        b = irc->b = bee_new();
[d860a8d]97        b->ui_data = irc;
98        b->ui = &irc_ui_funcs;
[3ddb7477]99       
100        s = set_add( &b->set, "away_devoice", "true", NULL/*set_eval_away_devoice*/, irc );
101        s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc );
102        //s = set_add( &b->set, "control_channel", irc->channel, NULL/*set_eval_control_channel*/, irc );
103        s = set_add( &b->set, "default_target", "root", NULL, irc );
104        s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc );
[21c87a7]105        s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc );
[3ddb7477]106        s = set_add( &b->set, "handle_unknown", "root", NULL, irc );
107        s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc );
108        s = set_add( &b->set, "ops", "both", NULL/*set_eval_ops*/, irc );
[88eaf4b]109        s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc );
110        s->old_key = g_strdup( "buddy_sendbuffer" );
111        s = set_add( &b->set, "paste_buffer_delay", "200", set_eval_int, irc );
112        s->old_key = g_strdup( "buddy_sendbuffer_delay" );
[3ddb7477]113        s = set_add( &b->set, "private", "true", set_eval_bool, irc );
114        s = set_add( &b->set, "query_order", "lifo", NULL, irc );
115        s = set_add( &b->set, "root_nick", ROOT_NICK, NULL/*set_eval_root_nick*/, irc );
116        s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc );
[21c87a7]117        s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc );
[3ddb7477]118        s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc );
119        s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc );
120
121        irc->root = iu = irc_user_new( irc, ROOT_NICK );
122        iu->host = g_strdup( myhost );
123        iu->fullname = g_strdup( ROOT_FN );
[280c56a]124        iu->f = &irc_user_root_funcs;
[3ddb7477]125       
126        iu = irc_user_new( irc, NS_NICK );
127        iu->host = g_strdup( myhost );
128        iu->fullname = g_strdup( ROOT_FN );
[280c56a]129        iu->f = &irc_user_root_funcs;
[3ddb7477]130       
131        irc->user = g_new0( irc_user_t, 1 );
132        irc->user->host = g_strdup( host );
133       
[ebaebfe]134        conf_loaddefaults( irc );
[b7d3cc34]135       
[f9756bd]136        /* Evaluator sets the iconv/oconv structures. */
[3ddb7477]137        set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) );
[f9756bd]138       
[3ddb7477]139        irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" );
140       
[ebaebfe]141        g_free( myhost );
142        g_free( host );
143       
[3ddb7477]144        return irc;
[b7d3cc34]145}
146
[f73b969]147/* immed=1 makes this function pretty much equal to irc_free(), except that
148   this one will "log". In case the connection is already broken and we
149   shouldn't try to write to it. */
[fc50d48]150void irc_abort( irc_t *irc, int immed, char *format, ... )
[c1826c6]151{
[fc50d48]152        if( format != NULL )
153        {
[f73b969]154                va_list params;
[fc50d48]155                char *reason;
156               
157                va_start( params, format );
[f73b969]158                reason = g_strdup_vprintf( format, params );
[fc50d48]159                va_end( params );
160               
161                if( !immed )
162                        irc_write( irc, "ERROR :Closing link: %s", reason );
163               
164                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
[3ddb7477]165                                   irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, reason );
[fc50d48]166               
167                g_free( reason );
168        }
169        else
170        {
171                if( !immed )
172                        irc_write( irc, "ERROR :Closing link" );
173               
174                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
[3ddb7477]175                                   irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, "No reason given" );
[fc50d48]176        }
177       
[79e826a]178        irc->status |= USTATUS_SHUTDOWN;
[fc50d48]179        if( irc->sendbuffer && !immed )
[c1826c6]180        {
[883a398]181                /* Set up a timeout event that should shut down the connection
182                   in a second, just in case ..._write doesn't do it first. */
[fc50d48]183               
[ba9edaa]184                b_event_remove( irc->r_watch_source_id );
[883a398]185                irc->r_watch_source_id = 0;
186               
187                b_event_remove( irc->ping_source_id );
188                irc->ping_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
[c1826c6]189        }
190        else
191        {
192                irc_free( irc );
193        }
194}
195
[3ddb7477]196static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data );
[b7d3cc34]197
[fa75134]198void irc_free( irc_t * irc )
[b7d3cc34]199{
200        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
201       
[3ddb7477]202        if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) ) 
[3183c21]203                if( storage_save( irc, NULL, TRUE ) != STORAGE_OK )
[d33679e]204                        log_message( LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick );
[b7d3cc34]205       
206        irc_connection_list = g_slist_remove( irc_connection_list, irc );
207       
[fa75134]208        while( irc->queries != NULL )
209                query_del( irc, irc->queries );
[d33679e]210       
211        /* This is a little bit messy: bee_free() frees all b->users which
212           calls us back to free the corresponding irc->users. So do this
213           before we clear the remaining ones ourselves. */
214        bee_free( irc->b );
[5b52a48]215       
[3ddb7477]216        while( irc->users )
[eabc9d2]217                irc_user_free( irc, (irc_user_t *) irc->users->data );
[b7d3cc34]218       
[63a520b]219        while( irc->channels )
220                irc_channel_free( irc->channels->data );
221       
[fa75134]222        if( irc->ping_source_id > 0 )
223                b_event_remove( irc->ping_source_id );
[883a398]224        if( irc->r_watch_source_id > 0 )
225                b_event_remove( irc->r_watch_source_id );
[fa75134]226        if( irc->w_watch_source_id > 0 )
227                b_event_remove( irc->w_watch_source_id );
228       
229        closesocket( irc->fd );
230        irc->fd = -1;
231       
[3ddb7477]232        g_hash_table_foreach_remove( irc->nick_user_hash, irc_free_hashkey, NULL );
233        g_hash_table_destroy( irc->nick_user_hash );
[b7d3cc34]234       
[fa75134]235        g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
236        g_hash_table_destroy( irc->watches );
[b7d3cc34]237       
[f9756bd]238        if( irc->iconv != (GIConv) -1 )
239                g_iconv_close( irc->iconv );
240        if( irc->oconv != (GIConv) -1 )
241                g_iconv_close( irc->oconv );
242       
[fa75134]243        g_free( irc->sendbuffer );
244        g_free( irc->readbuffer );
245        g_free( irc->password );
[eaaa986]246        g_free( irc->last_root_cmd );
[fa75134]247       
248        g_free( irc );
[b7d3cc34]249       
[565a1ea]250        if( global.conf->runmode == RUNMODE_INETD ||
251            global.conf->runmode == RUNMODE_FORKDAEMON ||
252            ( global.conf->runmode == RUNMODE_DAEMON &&
253              global.listen_socket == -1 &&
254              irc_connection_list == NULL ) )
[ba9edaa]255                b_main_quit();
[b7d3cc34]256}
257
[3ddb7477]258static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
[7cad7b4]259{
[3ddb7477]260        g_free( key );
[7cad7b4]261       
[3ddb7477]262        return( TRUE );
[7cad7b4]263}
264
[1f92a58]265/* USE WITH CAUTION!
266   Sets pass without checking */
267void irc_setpass (irc_t *irc, const char *pass)
268{
269        g_free (irc->password);
270       
271        if (pass) {
272                irc->password = g_strdup (pass);
273        } else {
274                irc->password = NULL;
275        }
276}
277
[3ddb7477]278static char **irc_splitlines( char *buffer );
279
[f73b969]280void irc_process( irc_t *irc )
[b7d3cc34]281{
[f9756bd]282        char **lines, *temp, **cmd;
[b7d3cc34]283        int i;
284
[de3e100]285        if( irc->readbuffer != NULL )
286        {
[3ddb7477]287                lines = irc_splitlines( irc->readbuffer );
[de3e100]288               
289                for( i = 0; *lines[i] != '\0'; i ++ )
290                {
[f9756bd]291                        char *conv = NULL;
[7d31002]292                       
[18ff38f]293                        /* [WvG] If the last line isn't empty, it's an incomplete line and we
294                           should wait for the rest to come in before processing it. */
[de3e100]295                        if( lines[i+1] == NULL )
296                        {
[b7d3cc34]297                                temp = g_strdup( lines[i] );
298                                g_free( irc->readbuffer );
299                                irc->readbuffer = temp;
[de3e100]300                                i ++;
[b7d3cc34]301                                break;
[e27661d]302                        }
303                       
[f9756bd]304                        if( irc->iconv != (GIConv) -1 )
[e27661d]305                        {
[f9756bd]306                                gsize bytes_read, bytes_written;
307                               
308                                conv = g_convert_with_iconv( lines[i], -1, irc->iconv,
309                                                             &bytes_read, &bytes_written, NULL );
310                               
311                                if( conv == NULL || bytes_read != strlen( lines[i] ) )
[94d52d64]312                                {
[fc0cf92]313                                        /* GLib can do strange things if things are not in the expected charset,
314                                           so let's be a little bit paranoid here: */
[94d52d64]315                                        if( irc->status & USTATUS_LOGGED_IN )
[fc0cf92]316                                        {
[43462708]317                                                irc_usermsg( irc, "Error: Charset mismatch detected. The charset "
[94d52d64]318                                                                  "setting is currently set to %s, so please make "
319                                                                  "sure your IRC client will send and accept text in "
320                                                                  "that charset, or tell BitlBee which charset to "
321                                                                  "expect by changing the charset setting. See "
322                                                                  "`help set charset' for more information. Your "
[f9756bd]323                                                                  "message was ignored.",
[3ddb7477]324                                                                  set_getstr( &irc->b->set, "charset" ) );
[f9756bd]325                                               
326                                                g_free( conv );
327                                                conv = NULL;
[fc0cf92]328                                        }
329                                        else
330                                        {
[3ddb7477]331                                                irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host,
[a83442a]332                                                           "Warning: invalid characters received at login time." );
[fc0cf92]333                                               
[f9756bd]334                                                conv = g_strdup( lines[i] );
[fc0cf92]335                                                for( temp = conv; *temp; temp ++ )
336                                                        if( *temp & 0x80 )
337                                                                *temp = '?';
338                                        }
[94d52d64]339                                }
340                                lines[i] = conv;
[e27661d]341                        }
[de3e100]342                       
[e1720ce]343                        if( lines[i] && ( cmd = irc_parse_line( lines[i] ) ) )
[f9756bd]344                        {
345                                irc_exec( irc, cmd );
346                                g_free( cmd );
347                        }
[f73b969]348                       
[f9756bd]349                        g_free( conv );
[f73b969]350                       
351                        /* Shouldn't really happen, but just in case... */
352                        if( !g_slist_find( irc_connection_list, irc ) )
[de3e100]353                        {
[b7d3cc34]354                                g_free( lines );
[f73b969]355                                return;
[b7d3cc34]356                        }
357                }
[de3e100]358               
359                if( lines[i] != NULL )
360                {
361                        g_free( irc->readbuffer );
[0431ea1]362                        irc->readbuffer = NULL;
[b7d3cc34]363                }
[de3e100]364               
[b7d3cc34]365                g_free( lines );
366        }
367}
368
[3ddb7477]369/* Splits a long string into separate lines. The array is NULL-terminated
370   and, unless the string contains an incomplete line at the end, ends with
371   an empty string. Could use g_strsplit() but this one does it in-place.
372   (So yes, it's destructive.) */
373static char **irc_splitlines( char *buffer )
[b7d3cc34]374{
[18ff38f]375        int i, j, n = 3;
[b7d3cc34]376        char **lines;
377
[18ff38f]378        /* Allocate n+1 elements. */
379        lines = g_new( char *, n + 1 );
[b7d3cc34]380       
[de3e100]381        lines[0] = buffer;
[b7d3cc34]382       
[18ff38f]383        /* Split the buffer in several strings, and accept any kind of line endings,
384         * knowing that ERC on Windows may send something interesting like \r\r\n,
385         * and surely there must be clients that think just \n is enough... */
386        for( i = 0, j = 0; buffer[i] != '\0'; i ++ )
[de3e100]387        {
[18ff38f]388                if( buffer[i] == '\r' || buffer[i] == '\n' )
[de3e100]389                {
[18ff38f]390                        while( buffer[i] == '\r' || buffer[i] == '\n' )
391                                buffer[i++] = '\0';
392                       
393                        lines[++j] = buffer + i;
[de3e100]394                       
[18ff38f]395                        if( j >= n )
396                        {
397                                n *= 2;
398                                lines = g_renew( char *, lines, n + 1 );
399                        }
400
401                        if( buffer[i] == '\0' )
402                                break;
[b7d3cc34]403                }
404        }
[de3e100]405       
[18ff38f]406        /* NULL terminate our list. */ 
407        lines[++j] = NULL;
408       
409        return lines;
[b7d3cc34]410}
411
[e27661d]412/* Split an IRC-style line into little parts/arguments. */
[0431ea1]413char **irc_parse_line( char *line )
[b7d3cc34]414{
415        int i, j;
416        char **cmd;
417       
418        /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
[de3e100]419        if( line[0] == ':' )
420        {
[e1720ce]421                for( i = 0; line[i] && line[i] != ' '; i ++ );
[de3e100]422                line = line + i;
[b7d3cc34]423        }
[de3e100]424        for( i = 0; line[i] == ' '; i ++ );
425        line = line + i;
426       
[b7d3cc34]427        /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
[de3e100]428        if( line[0] == '\0')
429                return NULL;
430       
431        /* Count the number of char **cmd elements we're going to need. */
432        j = 1;
433        for( i = 0; line[i] != '\0'; i ++ )
434        {
435                if( line[i] == ' ' )
436                {
437                        j ++;
[b7d3cc34]438                       
[de3e100]439                        if( line[i+1] == ':' )
440                                break;
441                }
[b7d3cc34]442        }       
443
444        /* Allocate the space we need. */
[de3e100]445        cmd = g_new( char *, j + 1 );
446        cmd[j] = NULL;
[b7d3cc34]447       
448        /* Do the actual line splitting, format is:
449         * Input: "PRIVMSG #bitlbee :foo bar"
450         * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
451         */
452
[de3e100]453        cmd[0] = line;
454        for( i = 0, j = 0; line[i] != '\0'; i ++ )
[b7d3cc34]455        {
[de3e100]456                if( line[i] == ' ' )
[b7d3cc34]457                {
[de3e100]458                        line[i] = '\0';
459                        cmd[++j] = line + i + 1;
[b7d3cc34]460                       
[de3e100]461                        if( line[i+1] == ':' )
[b7d3cc34]462                        {
[de3e100]463                                cmd[j] ++;
[b7d3cc34]464                                break;
465                        }
466                }
467        }
468       
[de3e100]469        return cmd;
[b7d3cc34]470}
471
[e27661d]472/* Converts such an array back into a command string. Mainly used for the IPC code right now. */
[74c119d]473char *irc_build_line( char **cmd )
474{
475        int i, len;
476        char *s;
[b7d3cc34]477       
[74c119d]478        if( cmd[0] == NULL )
479                return NULL;
[b7d3cc34]480       
[74c119d]481        len = 1;
482        for( i = 0; cmd[i]; i ++ )
483                len += strlen( cmd[i] ) + 1;
484       
485        if( strchr( cmd[i-1], ' ' ) != NULL )
486                len ++;
487       
488        s = g_new0( char, len + 1 );
489        for( i = 0; cmd[i]; i ++ )
[b7d3cc34]490        {
[74c119d]491                if( cmd[i+1] == NULL && strchr( cmd[i], ' ' ) != NULL )
492                        strcat( s, ":" );
[b7d3cc34]493               
[74c119d]494                strcat( s, cmd[i] );
[b7d3cc34]495               
[74c119d]496                if( cmd[i+1] )
497                        strcat( s, " " );
[b7d3cc34]498        }
[74c119d]499        strcat( s, "\r\n" );
[b7d3cc34]500       
[74c119d]501        return s;
[b7d3cc34]502}
503
[3ddb7477]504void irc_write( irc_t *irc, char *format, ... ) 
[b7d3cc34]505{
506        va_list params;
[3ddb7477]507
[b7d3cc34]508        va_start( params, format );
[3ddb7477]509        irc_vawrite( irc, format, params );     
[b7d3cc34]510        va_end( params );
[3ddb7477]511
[b7d3cc34]512        return;
513}
514
[3ddb7477]515void irc_write_all( int now, char *format, ... )
[b7d3cc34]516{
517        va_list params;
[3ddb7477]518        GSList *temp;   
[b7d3cc34]519       
520        va_start( params, format );
521       
[3ddb7477]522        temp = irc_connection_list;
523        while( temp != NULL )
524        {
525                irc_t *irc = temp->data;
526               
527                if( now )
528                {
529                        g_free( irc->sendbuffer );
530                        irc->sendbuffer = g_strdup( "\r\n" );
531                }
532                irc_vawrite( temp->data, format, params );
533                if( now )
534                {
535                        bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
536                }
537                temp = temp->next;
538        }
539       
[b7d3cc34]540        va_end( params );
541        return;
[3ddb7477]542} 
[b7d3cc34]543
544void irc_vawrite( irc_t *irc, char *format, va_list params )
545{
546        int size;
[f9756bd]547        char line[IRC_MAX_LINE+1];
[d783e48]548               
[0356ae3]549        /* Don't try to write anything new anymore when shutting down. */
[5898ef8]550        if( irc->status & USTATUS_SHUTDOWN )
[b7d3cc34]551                return;
[d783e48]552       
[f9756bd]553        memset( line, 0, sizeof( line ) );
[d783e48]554        g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
[b7d3cc34]555        strip_newlines( line );
[f9756bd]556       
557        if( irc->oconv != (GIConv) -1 )
[d783e48]558        {
[f9756bd]559                gsize bytes_read, bytes_written;
560                char *conv;
561               
562                conv = g_convert_with_iconv( line, -1, irc->oconv,
563                                             &bytes_read, &bytes_written, NULL );
564
565                if( bytes_read == strlen( line ) )
566                        strncpy( line, conv, IRC_MAX_LINE - 2 );
[d783e48]567               
[f9756bd]568                g_free( conv );
[d783e48]569        }
[f9756bd]570        g_strlcat( line, "\r\n", IRC_MAX_LINE + 1 );
[d783e48]571       
[a0d04d6]572        if( irc->sendbuffer != NULL )
573        {
[b7d3cc34]574                size = strlen( irc->sendbuffer ) + strlen( line );
575                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
576                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
577        }
[a0d04d6]578        else
[b7d3cc34]579        {
[a0d04d6]580                irc->sendbuffer = g_strdup(line);
[b7d3cc34]581        }
582       
[a0d04d6]583        if( irc->w_watch_source_id == 0 )
[0356ae3]584        {
585                /* If the buffer is empty we can probably write, so call the write event handler
586                   immediately. If it returns TRUE, it should be called again, so add the event to
587                   the queue. If it's FALSE, we emptied the buffer and saved ourselves some work
588                   in the event queue. */
[bbb6ffb]589                /* Really can't be done as long as the code doesn't do error checking very well:
590                if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */
591               
592                /* So just always do it via the event handler. */
593                irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc );
[0356ae3]594        }
[a0d04d6]595       
[b7d3cc34]596        return;
597}
598
[edf9657]599int irc_check_login( irc_t *irc )
[b7d3cc34]600{
[3ddb7477]601        if( irc->user->user && irc->user->nick )
[edf9657]602        {
[3af70b0]603                if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
[b7d3cc34]604                {
[3ddb7477]605                        irc_send_num( irc, 464, ":This server is password-protected." );
[edf9657]606                        return 0;
[b7d3cc34]607                }
[edf9657]608                else
[b7d3cc34]609                {
[4be8239]610                        irc_channel_t *ic;
611                        irc_user_t *iu = irc->user;
612                       
613                        irc->user = irc_user_new( irc, iu->nick );
614                        irc->user->user = iu->user;
[b95932e]615                        irc->user->host = iu->host;
[4be8239]616                        irc->user->fullname = iu->fullname;
[280c56a]617                        irc->user->f = &irc_user_self_funcs;
[4be8239]618                        g_free( iu->nick );
619                        g_free( iu );
620                       
621                        if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
622                                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
623                       
624                        irc->status |= USTATUS_LOGGED_IN;
625                       
626                        /* This is for bug #209 (use PASS to identify to NickServ). */
627                        if( irc->password != NULL )
628                        {
629                                char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
630                               
631                                /*irc_setpass( irc, NULL );*/
632                                /*root_command( irc, send_cmd );*/
633                                g_free( send_cmd[1] );
634                        }
635                       
[3ddb7477]636                        irc_send_login( irc );
[4be8239]637                       
[b919363]638                        irc->umode[0] = '\0';
639                        irc_umode_set( irc, "+" UMODE, TRUE );
640                       
[fd45e1d1]641                        ic = irc->default_channel = irc_channel_new( irc, ROOT_CHAN );
[83e92bf]642                        irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root );
[4be8239]643                        irc_channel_add_user( ic, irc->user );
644                       
[6a9d068]645                        if( strcmp( set_getstr( &irc->b->set, "ops" ), "both" ) == 0 ||
646                            strcmp( set_getstr( &irc->b->set, "ops" ), "user" ) == 0 )
647                                irc_channel_user_set_mode( ic, irc->user, IRC_CHANNEL_USER_OP );
648                       
[74f1cde]649                        irc->last_root_cmd = g_strdup( ROOT_CHAN );
650                       
[e21c0f8]651                        irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN,
652                                      "Welcome to the BitlBee gateway!\n\n"
653                                      "If you've never used BitlBee before, please do read the help "
654                                      "information using the \x02help\x02 command. Lots of FAQs are "
655                                      "answered there.\n"
656                                      "If you already have an account on this server, just use the "
657                                      "\x02identify\x02 command to identify yourself.", NULL );
658                       
[edf9657]659                        return 1;
[b7d3cc34]660                }
[edf9657]661        }
662        else
663        {
664                /* More information needed. */
665                return 0;
666        }
[b7d3cc34]667}
668
[b919363]669void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv )
670{
671        /* allow_priv: Set to 0 if s contains user input, 1 if you want
672           to set a "privileged" mode (+o, +R, etc). */
673        char m[128], st = 1;
674        const char *t;
675        int i;
676        char changes[512], *p, st2 = 2;
677        char badflag = 0;
678       
679        memset( m, 0, sizeof( m ) );
680       
681        for( t = irc->umode; *t; t ++ )
682                if( *t < sizeof( m ) )
683                        m[(int)*t] = 1;
684       
685        p = changes;
686        for( t = s; *t; t ++ )
687        {
688                if( *t == '+' || *t == '-' )
689                        st = *t == '+';
690                else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) ||
691                         ( st == 1 && strchr( UMODES, *t ) ) ||
692                         ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) )
693                {
694                        if( m[(int)*t] != st)
695                        {
696                                if( st != st2 )
697                                        st2 = st, *p++ = st ? '+' : '-';
698                                *p++ = *t;
699                        }
700                        m[(int)*t] = st;
701                }
702                else
703                        badflag = 1;
704        }
705        *p = '\0';
706       
707        memset( irc->umode, 0, sizeof( irc->umode ) );
708       
709        for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
710                if( m[i] )
711                        irc->umode[strlen(irc->umode)] = i;
712       
713        if( badflag )
714                irc_send_num( irc, 501, ":Unknown MODE flag" );
715        if( *changes )
716                irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
717                           irc->user->user, irc->user->host, irc->user->nick,
718                           changes );
719}
720
[b7d3cc34]721
[3923003]722/* Returns 0 if everything seems to be okay, a number >0 when there was a
723   timeout. The number returned is the number of seconds we received no
724   pongs from the user. When not connected yet, we don't ping but drop the
725   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
726static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
727{
728        irc_t *irc = _irc;
729        int rv = 0;
730       
731        if( !( irc->status & USTATUS_LOGGED_IN ) )
732        {
733                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
734                        rv = gettime() - irc->last_pong;
735        }
736        else
737        {
738                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
739                {
740                        irc_write( irc, "PING :%s", IRC_PING_STRING );
741                        irc->pinging = 1;
742                }
743                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
744                {
745                        rv = gettime() - irc->last_pong;
746                }
747        }
748       
749        if( rv > 0 )
750        {
751                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
752                return FALSE;
753        }
754       
755        return TRUE;
756}
757
[3ddb7477]758static char *set_eval_charset( set_t *set, char *value )
[b7d3cc34]759{
[21c87a7]760        irc_t *irc = (irc_t*) set->data;
761        char *test;
762        gsize test_bytes = 0;
[3ddb7477]763        GIConv ic, oc;
[b7d3cc34]764
[3ddb7477]765        if( g_strcasecmp( value, "none" ) == 0 )
766                value = g_strdup( "utf-8" );
[b7d3cc34]767
[21c87a7]768        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
[b7d3cc34]769        {
[3ddb7477]770                return NULL;
[b7d3cc34]771        }
[0e7ab64]772       
[21c87a7]773        /* Do a test iconv to see if the user picked an IRC-compatible
774           charset (for example utf-16 goes *horribly* wrong). */
775        if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
776            test_bytes > 1 )
[0e7ab64]777        {
[21c87a7]778                g_free( test );
779                g_iconv_close( oc );
780                irc_usermsg( irc, "Unsupported character set: The IRC protocol "
781                                  "only supports 8-bit character sets." );
782                return NULL;
[0e7ab64]783        }
[21c87a7]784        g_free( test );
[0e7ab64]785       
[21c87a7]786        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
[b7d3cc34]787        {
[21c87a7]788                g_iconv_close( oc );
[3ddb7477]789                return NULL;
[b7d3cc34]790        }
791       
[3ddb7477]792        if( irc->iconv != (GIConv) -1 )
793                g_iconv_close( irc->iconv );
794        if( irc->oconv != (GIConv) -1 )
795                g_iconv_close( irc->oconv );
[b7d3cc34]796       
[3ddb7477]797        irc->iconv = ic;
798        irc->oconv = oc;
[0e7ab64]799
[3ddb7477]800        return value;
[0e7ab64]801}
Note: See TracBrowser for help on using the repository browser.