source: irc.c @ 180ab31

Last change on this file since 180ab31 was 180ab31, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-21T19:34:17Z

Added some neat whatsnew code that keeps track of the newest version of
BitlBee used by a user, and if it looks like s/he hasn't used this one
before, show a list of new features that may be interesting.

Since I don't think im.bitlbee.org users will read any changelogs ever,
this is probably not a bad idea. If you hate it, the following command
should get rid of it forever: set last_version 9999999

  • Property mode set to 100644
File size: 24.7 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, "last_version", NULL, NULL, irc );
112        s->flags |= SET_HIDDEN;
113        s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc );
114        s = set_add( &b->set, "nick_format", "%-@nick", NULL, irc );
115        s = set_add( &b->set, "offline_user_quits", "true", set_eval_bool, irc );
116        s = set_add( &b->set, "ops", "both", set_eval_irc_channel_ops, irc );
117        s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc );
118        s->old_key = g_strdup( "buddy_sendbuffer" );
119        s = set_add( &b->set, "paste_buffer_delay", "200", set_eval_int, irc );
120        s->old_key = g_strdup( "buddy_sendbuffer_delay" );
121        s = set_add( &b->set, "password", NULL, set_eval_password, irc );
122        s->flags |= SET_NULL_OK;
123        s = set_add( &b->set, "private", "true", set_eval_bool, irc );
124        s = set_add( &b->set, "query_order", "lifo", NULL, irc );
125        s = set_add( &b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc );
126        s->flags |= SET_HIDDEN;
127        s = set_add( &b->set, "show_offline", "false", set_eval_bw_compat, irc );
128        s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc );
129        s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc );
130        s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc );
131        s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc );
132
133        irc->root = iu = irc_user_new( irc, ROOT_NICK );
134        iu->host = g_strdup( myhost );
135        iu->fullname = g_strdup( ROOT_FN );
136        iu->f = &irc_user_root_funcs;
137       
138        iu = irc_user_new( irc, NS_NICK );
139        iu->host = g_strdup( myhost );
140        iu->fullname = g_strdup( ROOT_FN );
141        iu->f = &irc_user_root_funcs;
142       
143        irc->user = g_new0( irc_user_t, 1 );
144        irc->user->host = g_strdup( host );
145       
146        conf_loaddefaults( irc );
147       
148        /* Evaluator sets the iconv/oconv structures. */
149        set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) );
150       
151        irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" );
152        if( isatty( irc->fd ) )
153                irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host,
154                           "If you read this, you most likely accidentally "
155                           "started BitlBee in inetd mode on the command line. "
156                           "You probably want to run it in (Fork)Daemon mode. "
157                           "See doc/README for more information." );
158       
159        g_free( myhost );
160        g_free( host );
161       
162        nogaim_init();
163       
164        return irc;
165}
166
167/* immed=1 makes this function pretty much equal to irc_free(), except that
168   this one will "log". In case the connection is already broken and we
169   shouldn't try to write to it. */
170void irc_abort( irc_t *irc, int immed, char *format, ... )
171{
172        char *reason = NULL;
173       
174        if( format != NULL )
175        {
176                va_list params;
177               
178                va_start( params, format );
179                reason = g_strdup_vprintf( format, params );
180                va_end( params );
181        }
182       
183        irc_write( irc, "ERROR :Closing link: %s", reason ? : "" );
184       
185        ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
186                           irc->user->nick ? irc->user->nick : "(NONE)",
187                           irc->user->host, reason ? : "" );
188       
189        g_free( reason );
190       
191        irc_flush( irc );
192        if( immed )
193        {
194                irc_free( irc );
195        }
196        else
197        {
198                b_event_remove( irc->ping_source_id );
199                irc->ping_source_id = b_timeout_add( 1, (b_event_handler) irc_free, irc );
200        }
201}
202
203static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data );
204
205void irc_free( irc_t * irc )
206{
207        irc->status |= USTATUS_SHUTDOWN;
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       
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->root->last_channel = irc->default_channel;
740                       
741                        irc_usermsg( irc,
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." );
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
769/* TODO: This is a mess, but this function is a bit too complicated to be
770   converted to something more generic. */
771void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv )
772{
773        /* allow_priv: Set to 0 if s contains user input, 1 if you want
774           to set a "privileged" mode (+o, +R, etc). */
775        char m[128], st = 1;
776        const char *t;
777        int i;
778        char changes[512], *p, st2 = 2;
779        char badflag = 0;
780       
781        memset( m, 0, sizeof( m ) );
782       
783        for( t = irc->umode; *t; t ++ )
784                if( *t < sizeof( m ) )
785                        m[(int)*t] = 1;
786       
787        p = changes;
788        for( t = s; *t; t ++ )
789        {
790                if( *t == '+' || *t == '-' )
791                        st = *t == '+';
792                else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) ||
793                         ( st == 1 && strchr( UMODES, *t ) ) ||
794                         ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) )
795                {
796                        if( m[(int)*t] != st)
797                        {
798                                if( st != st2 )
799                                        st2 = st, *p++ = st ? '+' : '-';
800                                *p++ = *t;
801                        }
802                        m[(int)*t] = st;
803                }
804                else
805                        badflag = 1;
806        }
807        *p = '\0';
808       
809        memset( irc->umode, 0, sizeof( irc->umode ) );
810       
811        for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
812                if( m[i] )
813                        irc->umode[strlen(irc->umode)] = i;
814       
815        if( badflag )
816                irc_send_num( irc, 501, ":Unknown MODE flag" );
817        if( *changes )
818                irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
819                           irc->user->user, irc->user->host, irc->user->nick,
820                           changes );
821}
822
823/* Returns 0 if everything seems to be okay, a number >0 when there was a
824   timeout. The number returned is the number of seconds we received no
825   pongs from the user. When not connected yet, we don't ping but drop the
826   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
827static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
828{
829        irc_t *irc = _irc;
830        int rv = 0;
831       
832        if( !( irc->status & USTATUS_LOGGED_IN ) )
833        {
834                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
835                        rv = gettime() - irc->last_pong;
836        }
837        else
838        {
839                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
840                {
841                        irc_write( irc, "PING :%s", IRC_PING_STRING );
842                        irc->pinging = 1;
843                }
844                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
845                {
846                        rv = gettime() - irc->last_pong;
847                }
848        }
849       
850        if( rv > 0 )
851        {
852                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
853                return FALSE;
854        }
855       
856        return TRUE;
857}
858
859static char *set_eval_charset( set_t *set, char *value )
860{
861        irc_t *irc = (irc_t*) set->data;
862        char *test;
863        gsize test_bytes = 0;
864        GIConv ic, oc;
865
866        if( g_strcasecmp( value, "none" ) == 0 )
867                value = g_strdup( "utf-8" );
868
869        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
870        {
871                return NULL;
872        }
873       
874        /* Do a test iconv to see if the user picked an IRC-compatible
875           charset (for example utf-16 goes *horribly* wrong). */
876        if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
877            test_bytes > 1 )
878        {
879                g_free( test );
880                g_iconv_close( oc );
881                irc_usermsg( irc, "Unsupported character set: The IRC protocol "
882                                  "only supports 8-bit character sets." );
883                return NULL;
884        }
885        g_free( test );
886       
887        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
888        {
889                g_iconv_close( oc );
890                return NULL;
891        }
892       
893        if( irc->iconv != (GIConv) -1 )
894                g_iconv_close( irc->iconv );
895        if( irc->oconv != (GIConv) -1 )
896                g_iconv_close( irc->oconv );
897       
898        irc->iconv = ic;
899        irc->oconv = oc;
900
901        return value;
902}
903
904/* Mostly meant for upgrades. If one of these is set to the non-default,
905   set show_users of all channels to something with the same effect. */
906static char *set_eval_bw_compat( set_t *set, char *value )
907{
908        irc_t *irc = set->data;
909        char *val;
910        GSList *l;
911       
912        irc_usermsg( irc, "Setting `%s' is obsolete, use the `show_users' "
913                     "channel setting instead.", set->key );
914       
915        if( strcmp( set->key, "away_devoice" ) == 0 && !bool2int( value ) )
916                val = "online,away";
917        else if( strcmp( set->key, "show_offline" ) == 0 && bool2int( value ) )
918                val = "online@,away+,offline";
919        else
920                return SET_INVALID;
921       
922        for( l = irc->channels; l; l = l->next )
923        {
924                irc_channel_t *ic = l->data;
925                /* No need to check channel type, if the setting doesn't exist it
926                   will just be ignored. */
927                set_setstr( &ic->set, "show_users", val );
928        }
929       
930        return SET_INVALID;
931}
Note: See TracBrowser for help on using the repository browser.