source: irc.c @ 3079db8

3.0.6-1
Last change on this file since 3079db8 was b958cb5, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-02-26T09:20:30Z

Changing ping behaviour. Pinging seems to be misbehaving for some people
for reasons not entirely clear to me. Instead of suppressing a PING to the
client if we're still waiting for a response to a previous one, just keep
sending them. One PONG will be enough to stay connected but that's okay.

  • Property mode set to 100644
File size: 25.5 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 | SET_PASSWORD;
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_rootmsg( 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_rootmsg( 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], st2 = 2;
807        char badflag = 0;
808       
809        memset( m, 0, sizeof( m ) );
810       
811        /* Keep track of which modes are enabled in this array. */
812        for( t = irc->umode; *t; t ++ )
813                if( *t < sizeof( m ) )
814                        m[(int)*t] = 1;
815       
816        i = 0;
817        for( t = s; *t && i < sizeof( changes ) - 3; t ++ )
818        {
819                if( *t == '+' || *t == '-' )
820                        st = *t == '+';
821                else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) ||
822                         ( st == 1 && strchr( UMODES, *t ) ) ||
823                         ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) )
824                {
825                        if( m[(int)*t] != st)
826                        {
827                                /* If we're actually making a change, remember this
828                                   for the response. */
829                                if( st != st2 )
830                                        st2 = st, changes[i++] = st ? '+' : '-';
831                                changes[i++] = *t;
832                        }
833                        m[(int)*t] = st;
834                }
835                else
836                        badflag = 1;
837        }
838        changes[i] = '\0';
839       
840        /* Convert the m array back into an umode string. */
841        memset( irc->umode, 0, sizeof( irc->umode ) );
842        for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
843                if( m[i] )
844                        irc->umode[strlen(irc->umode)] = i;
845       
846        if( badflag )
847                irc_send_num( irc, 501, ":Unknown MODE flag" );
848        if( *changes )
849                irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
850                           irc->user->user, irc->user->host, irc->user->nick,
851                           changes );
852}
853
854/* Returns 0 if everything seems to be okay, a number >0 when there was a
855   timeout. The number returned is the number of seconds we received no
856   pongs from the user. When not connected yet, we don't ping but drop the
857   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
858static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
859{
860        double now = gettime();
861        irc_t *irc = _irc;
862        int fail = 0;
863       
864        if( !( irc->status & USTATUS_LOGGED_IN ) )
865        {
866                if( now > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
867                        fail = now - irc->last_pong;
868        }
869        else
870        {
871                if( now > ( irc->last_pong + global.conf->ping_timeout ) )
872                {
873                        fail = now - irc->last_pong;
874                }
875                else
876                {
877                        irc_write( irc, "PING :%s", IRC_PING_STRING );
878                }
879        }
880       
881        if( fail > 0 )
882        {
883                irc_abort( irc, 0, "Ping Timeout: %d seconds", fail );
884                return FALSE;
885        }
886       
887        return TRUE;
888}
889
890static char *set_eval_charset( set_t *set, char *value )
891{
892        irc_t *irc = (irc_t*) set->data;
893        char *test;
894        gsize test_bytes = 0;
895        GIConv ic, oc;
896
897        if( g_strcasecmp( value, "none" ) == 0 )
898                value = g_strdup( "utf-8" );
899
900        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
901        {
902                return NULL;
903        }
904       
905        /* Do a test iconv to see if the user picked an IRC-compatible
906           charset (for example utf-16 goes *horribly* wrong). */
907        if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
908            test_bytes > 1 )
909        {
910                g_free( test );
911                g_iconv_close( oc );
912                irc_rootmsg( irc, "Unsupported character set: The IRC protocol "
913                                  "only supports 8-bit character sets." );
914                return NULL;
915        }
916        g_free( test );
917       
918        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
919        {
920                g_iconv_close( oc );
921                return NULL;
922        }
923       
924        if( irc->iconv != (GIConv) -1 )
925                g_iconv_close( irc->iconv );
926        if( irc->oconv != (GIConv) -1 )
927                g_iconv_close( irc->oconv );
928       
929        irc->iconv = ic;
930        irc->oconv = oc;
931
932        return value;
933}
934
935/* Mostly meant for upgrades. If one of these is set to the non-default,
936   set show_users of all channels to something with the same effect. */
937static char *set_eval_bw_compat( set_t *set, char *value )
938{
939        irc_t *irc = set->data;
940        char *val;
941        GSList *l;
942       
943        irc_rootmsg( irc, "Setting `%s' is obsolete, use the `show_users' "
944                     "channel setting instead.", set->key );
945       
946        if( strcmp( set->key, "away_devoice" ) == 0 && !bool2int( value ) )
947                val = "online,away";
948        else if( strcmp( set->key, "show_offline" ) == 0 && bool2int( value ) )
949                val = "online@,away+,offline";
950        else
951                val = "online+,away";
952       
953        for( l = irc->channels; l; l = l->next )
954        {
955                irc_channel_t *ic = l->data;
956                /* No need to check channel type, if the setting doesn't exist it
957                   will just be ignored. */
958                set_setstr( &ic->set, "show_users", val );
959        }
960       
961        return SET_INVALID;
962}
963
964void register_irc_plugin( const struct irc_plugin *p )
965{
966        irc_plugins = g_slist_prepend( irc_plugins, (gpointer) p );
967}
Note: See TracBrowser for help on using the repository browser.