source: irc.c @ 1c40aa7

Last change on this file since 1c40aa7 was 1c8e5f7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-11T15:12:27Z

Added away_reply_timeout setting so BitlBee will suppress away messages sent
in response to PRIVMSG if one was sent recently - some IRC clients including
irssi don't do this very well (when talking to >1 people who are away for
example).

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