source: irc.c @ 674a01d

Last change on this file since 674a01d was 674a01d, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-09-06T00:20:01Z

Move whatsnew stuff into help.txt. Stop putting too much string content
into the executable.

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