source: irc.c @ 38ee021

Last change on this file since 38ee021 was 3923003, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-28T02:49:19Z

Restored server-initiated PINGs.

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