source: irc.c @ 133cdff

Last change on this file since 133cdff was af9f2ca, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-11T10:59:56Z

Added allow_takeover setting for people who don't like this new functionality.

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