source: irc.c @ f7ca587

Last change on this file since f7ca587 was f7ca587, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-29T18:18:54Z

Restore default_target setting, kill last_root_cmd variable and just use
the last_channel variable, like for any other user.

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