source: irc.c @ b17ce85

Last change on this file since b17ce85 was 6a9d068, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-03T00:39:39Z

Restore away_devoice.

  • Property mode set to 100644
File size: 21.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, "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                        if( strcmp( set_getstr( &irc->b->set, "ops" ), "both" ) == 0 ||
643                            strcmp( set_getstr( &irc->b->set, "ops" ), "user" ) == 0 )
644                                irc_channel_user_set_mode( ic, irc->user, IRC_CHANNEL_USER_OP );
645                       
646                        irc->last_root_cmd = g_strdup( ROOT_CHAN );
647                       
648                        irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN,
649                                      "Welcome to the BitlBee gateway!\n\n"
650                                      "If you've never used BitlBee before, please do read the help "
651                                      "information using the \x02help\x02 command. Lots of FAQs are "
652                                      "answered there.\n"
653                                      "If you already have an account on this server, just use the "
654                                      "\x02identify\x02 command to identify yourself.", NULL );
655                       
656                        return 1;
657                }
658        }
659        else
660        {
661                /* More information needed. */
662                return 0;
663        }
664}
665
666void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv )
667{
668        /* allow_priv: Set to 0 if s contains user input, 1 if you want
669           to set a "privileged" mode (+o, +R, etc). */
670        char m[128], st = 1;
671        const char *t;
672        int i;
673        char changes[512], *p, st2 = 2;
674        char badflag = 0;
675       
676        memset( m, 0, sizeof( m ) );
677       
678        for( t = irc->umode; *t; t ++ )
679                if( *t < sizeof( m ) )
680                        m[(int)*t] = 1;
681       
682        p = changes;
683        for( t = s; *t; t ++ )
684        {
685                if( *t == '+' || *t == '-' )
686                        st = *t == '+';
687                else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) ||
688                         ( st == 1 && strchr( UMODES, *t ) ) ||
689                         ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) )
690                {
691                        if( m[(int)*t] != st)
692                        {
693                                if( st != st2 )
694                                        st2 = st, *p++ = st ? '+' : '-';
695                                *p++ = *t;
696                        }
697                        m[(int)*t] = st;
698                }
699                else
700                        badflag = 1;
701        }
702        *p = '\0';
703       
704        memset( irc->umode, 0, sizeof( irc->umode ) );
705       
706        for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
707                if( m[i] )
708                        irc->umode[strlen(irc->umode)] = i;
709       
710        if( badflag )
711                irc_send_num( irc, 501, ":Unknown MODE flag" );
712        if( *changes )
713                irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
714                           irc->user->user, irc->user->host, irc->user->nick,
715                           changes );
716}
717
718
719/* Returns 0 if everything seems to be okay, a number >0 when there was a
720   timeout. The number returned is the number of seconds we received no
721   pongs from the user. When not connected yet, we don't ping but drop the
722   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
723static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
724{
725        irc_t *irc = _irc;
726        int rv = 0;
727       
728        if( !( irc->status & USTATUS_LOGGED_IN ) )
729        {
730                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
731                        rv = gettime() - irc->last_pong;
732        }
733        else
734        {
735                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
736                {
737                        irc_write( irc, "PING :%s", IRC_PING_STRING );
738                        irc->pinging = 1;
739                }
740                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
741                {
742                        rv = gettime() - irc->last_pong;
743                }
744        }
745       
746        if( rv > 0 )
747        {
748                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
749                return FALSE;
750        }
751       
752        return TRUE;
753}
754
755static char *set_eval_charset( set_t *set, char *value )
756{
757        irc_t *irc = (irc_t*) set->data;
758        char *test;
759        gsize test_bytes = 0;
760        GIConv ic, oc;
761
762        if( g_strcasecmp( value, "none" ) == 0 )
763                value = g_strdup( "utf-8" );
764
765        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
766        {
767                return NULL;
768        }
769       
770        /* Do a test iconv to see if the user picked an IRC-compatible
771           charset (for example utf-16 goes *horribly* wrong). */
772        if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
773            test_bytes > 1 )
774        {
775                g_free( test );
776                g_iconv_close( oc );
777                irc_usermsg( irc, "Unsupported character set: The IRC protocol "
778                                  "only supports 8-bit character sets." );
779                return NULL;
780        }
781        g_free( test );
782       
783        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
784        {
785                g_iconv_close( oc );
786                return NULL;
787        }
788       
789        if( irc->iconv != (GIConv) -1 )
790                g_iconv_close( irc->iconv );
791        if( irc->oconv != (GIConv) -1 )
792                g_iconv_close( irc->oconv );
793       
794        irc->iconv = ic;
795        irc->oconv = oc;
796
797        return value;
798}
Note: See TracBrowser for help on using the repository browser.