source: irc.c @ 24b8bbb

Last change on this file since 24b8bbb was e21c0f8, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-11T23:31:43Z

Always show root's greeting inside the control channel.

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