source: irc.c @ a08e875

Last change on this file since a08e875 was 09dfb68, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-12T20:27:11Z

Added %nick and %group. They don't work yet since nick_gen() is only called
once.

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