source: irc.c @ 0e8b3e8

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

Changing away_devoice will change current voice statuses in all channels.

  • Property mode set to 100644
File size: 21.9 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2004 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* The IRC-based UI (for now the only one)                              */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#include "bitlbee.h"
27#include "ipc.h"
28#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", NULL/*set_eval_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, NULL/*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                        /* This is for bug #209 (use PASS to identify to NickServ). */
647                        if( irc->password != NULL )
648                        {
649                                char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
650                               
651                                /*irc_setpass( irc, NULL );*/
652                                /*root_command( irc, send_cmd );*/
653                                g_free( send_cmd[1] );
654                        }
655                       
656                        irc_send_login( irc );
657                       
658                        irc->umode[0] = '\0';
659                        irc_umode_set( irc, "+" UMODE, TRUE );
660                       
661                        ic = irc->default_channel = irc_channel_new( irc, ROOT_CHAN );
662                        irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root );
663                        irc_channel_add_user( ic, irc->user );
664                       
665                        if( strcmp( set_getstr( &irc->b->set, "ops" ), "both" ) == 0 ||
666                            strcmp( set_getstr( &irc->b->set, "ops" ), "user" ) == 0 )
667                                irc_channel_user_set_mode( ic, irc->user, IRC_CHANNEL_USER_OP );
668                       
669                        irc->last_root_cmd = g_strdup( ROOT_CHAN );
670                       
671                        irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN,
672                                      "Welcome to the BitlBee gateway!\n\n"
673                                      "If you've never used BitlBee before, please do read the help "
674                                      "information using the \x02help\x02 command. Lots of FAQs are "
675                                      "answered there.\n"
676                                      "If you already have an account on this server, just use the "
677                                      "\x02identify\x02 command to identify yourself.", NULL );
678                       
679                        return 1;
680                }
681        }
682        else
683        {
684                /* More information needed. */
685                return 0;
686        }
687}
688
689void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv )
690{
691        /* allow_priv: Set to 0 if s contains user input, 1 if you want
692           to set a "privileged" mode (+o, +R, etc). */
693        char m[128], st = 1;
694        const char *t;
695        int i;
696        char changes[512], *p, st2 = 2;
697        char badflag = 0;
698       
699        memset( m, 0, sizeof( m ) );
700       
701        for( t = irc->umode; *t; t ++ )
702                if( *t < sizeof( m ) )
703                        m[(int)*t] = 1;
704       
705        p = changes;
706        for( t = s; *t; t ++ )
707        {
708                if( *t == '+' || *t == '-' )
709                        st = *t == '+';
710                else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) ||
711                         ( st == 1 && strchr( UMODES, *t ) ) ||
712                         ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) )
713                {
714                        if( m[(int)*t] != st)
715                        {
716                                if( st != st2 )
717                                        st2 = st, *p++ = st ? '+' : '-';
718                                *p++ = *t;
719                        }
720                        m[(int)*t] = st;
721                }
722                else
723                        badflag = 1;
724        }
725        *p = '\0';
726       
727        memset( irc->umode, 0, sizeof( irc->umode ) );
728       
729        for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
730                if( m[i] )
731                        irc->umode[strlen(irc->umode)] = i;
732       
733        if( badflag )
734                irc_send_num( irc, 501, ":Unknown MODE flag" );
735        if( *changes )
736                irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
737                           irc->user->user, irc->user->host, irc->user->nick,
738                           changes );
739}
740
741/* Returns 0 if everything seems to be okay, a number >0 when there was a
742   timeout. The number returned is the number of seconds we received no
743   pongs from the user. When not connected yet, we don't ping but drop the
744   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
745static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
746{
747        irc_t *irc = _irc;
748        int rv = 0;
749       
750        if( !( irc->status & USTATUS_LOGGED_IN ) )
751        {
752                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
753                        rv = gettime() - irc->last_pong;
754        }
755        else
756        {
757                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
758                {
759                        irc_write( irc, "PING :%s", IRC_PING_STRING );
760                        irc->pinging = 1;
761                }
762                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
763                {
764                        rv = gettime() - irc->last_pong;
765                }
766        }
767       
768        if( rv > 0 )
769        {
770                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
771                return FALSE;
772        }
773       
774        return TRUE;
775}
776
777static char *set_eval_charset( set_t *set, char *value )
778{
779        irc_t *irc = (irc_t*) set->data;
780        char *test;
781        gsize test_bytes = 0;
782        GIConv ic, oc;
783
784        if( g_strcasecmp( value, "none" ) == 0 )
785                value = g_strdup( "utf-8" );
786
787        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
788        {
789                return NULL;
790        }
791       
792        /* Do a test iconv to see if the user picked an IRC-compatible
793           charset (for example utf-16 goes *horribly* wrong). */
794        if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
795            test_bytes > 1 )
796        {
797                g_free( test );
798                g_iconv_close( oc );
799                irc_usermsg( irc, "Unsupported character set: The IRC protocol "
800                                  "only supports 8-bit character sets." );
801                return NULL;
802        }
803        g_free( test );
804       
805        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
806        {
807                g_iconv_close( oc );
808                return NULL;
809        }
810       
811        if( irc->iconv != (GIConv) -1 )
812                g_iconv_close( irc->iconv );
813        if( irc->oconv != (GIConv) -1 )
814                g_iconv_close( irc->oconv );
815       
816        irc->iconv = ic;
817        irc->oconv = oc;
818
819        return value;
820}
821
822char *set_eval_away_devoice( set_t *set, char *value )
823{
824        irc_t *irc = set->data;
825       
826        if( !is_bool( value ) )
827                return SET_INVALID;
828       
829        /* The usual problem: The setting isn't actually changed at this
830           point and we need it to be, so do it by hand. */
831        g_free( set->value );
832        set->value = g_strdup( value );
833       
834        bee_irc_channel_update( irc, NULL, NULL );
835       
836        return value;
837}
Note: See TracBrowser for help on using the repository browser.