source: irc.c @ f1c2b21

Last change on this file since f1c2b21 was f1c2b21, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-10T15:08:02Z

Cleanup. Move some code to a more appropriate location, and show the old
connection a quit message instead of just breaking the connection.

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