source: irc.c @ 8d93b4a

3.0
Last change on this file since 8d93b4a was 8d93b4a, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-10-22T00:50:34Z

Oops. Just in time I noticed the whatsnew code doesn't kick in for pre-1.3
accounts. Fixed now.

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