source: irc.c @ 18da20b

Last change on this file since 18da20b was eaaa986, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-08T14:48:38Z

Misc. cleanup. Also updated the Yahoo! module to deal with struct groupchat
in a GSList so that a default config fully compiles again.

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