source: irc.c @ bce78c8

Last change on this file since bce78c8 was d33679e, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-14T12:17:12Z

Call bee_free() from irc_free() or daemon mode gets pretty sad.

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