source: irc.c @ 88eaf4b

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

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

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