source: irc.c @ 0a6e5d1

Last change on this file since 0a6e5d1 was 0a6e5d1, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-07T15:51:31Z

Restore "set root_nick" behaviour. All disabled set evaluators are back
now. Getting *very* close to "feature parity" now!

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