source: irc.c @ 36577aa

Last change on this file since 36577aa was eaaa986, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-08T14:48:38Z

Misc. cleanup. Also updated the Yahoo! module to deal with struct groupchat
in a GSList so that a default config fully compiles again.

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