source: irc.c @ 54a2014

Last change on this file since 54a2014 was e8c8d00, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-17T15:15:19Z

Merging mainline.

  • Property mode set to 100644
File size: 34.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 big hairy IRCd part of the project                               */
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#define BITLBEE_CORE
27#include "bitlbee.h"
28#include "sock.h"
29#include "crypting.h"
30#include "ipc.h"
31#include "dcc.h"
32
33static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond );
34
35GSList *irc_connection_list = NULL;
36
37static char *set_eval_password( set_t *set, char *value )
38{
39        irc_t *irc = set->data;
40       
41        if( irc->status & USTATUS_IDENTIFIED && value )
42        {
43                irc_setpass( irc, value );
44                return NULL;
45        }
46        else
47        {
48                return SET_INVALID;
49        }
50}
51
52static char *set_eval_charset( set_t *set, char *value )
53{
54        irc_t *irc = set->data;
55        GIConv ic, oc;
56
57        if( g_strcasecmp( value, "none" ) == 0 )
58                value = g_strdup( "utf-8" );
59
60        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
61        {
62                return NULL;
63        }
64        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
65        {
66                g_iconv_close( ic );
67                return NULL;
68        }
69       
70        if( irc->iconv != (GIConv) -1 )
71                g_iconv_close( irc->iconv );
72        if( irc->oconv != (GIConv) -1 )
73                g_iconv_close( irc->oconv );
74       
75        irc->iconv = ic;
76        irc->oconv = oc;
77
78        return value;
79}
80
81static char *set_eval_away_status( set_t *set, char *value )
82{
83        irc_t *irc = set->data;
84        account_t *a;
85       
86        g_free( set->value );
87        set->value = g_strdup( value );
88       
89        for( a = irc->accounts; a; a = a->next )
90        {
91                struct im_connection *ic = a->ic;
92               
93                if( ic && ic->flags & OPT_LOGGED_IN )
94                        imc_away_send_update( ic );
95        }
96       
97        return value;
98}
99
100irc_t *irc_new( int fd )
101{
102        irc_t *irc;
103        struct sockaddr_storage sock;
104        socklen_t socklen = sizeof( sock );
105        set_t *s;
106       
107        irc = g_new0( irc_t, 1 );
108       
109        irc->fd = fd;
110        sock_make_nonblocking( irc->fd );
111       
112        irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc );
113       
114        irc->status = USTATUS_OFFLINE;
115        irc->last_pong = gettime();
116       
117        irc->userhash = g_hash_table_new( g_str_hash, g_str_equal );
118        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
119       
120        strcpy( irc->umode, UMODE );
121        irc->mynick = g_strdup( ROOT_NICK );
122        irc->channel = g_strdup( ROOT_CHAN );
123       
124        irc->iconv = (GIConv) -1;
125        irc->oconv = (GIConv) -1;
126       
127        if( global.conf->hostname )
128        {
129                irc->myhost = g_strdup( global.conf->hostname );
130        }
131        else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 ) 
132        {
133                char buf[NI_MAXHOST+1];
134
135                if( getnameinfo( (struct sockaddr *) &sock, socklen, buf,
136                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
137                {
138                        irc->myhost = g_strdup( ipv6_unwrap( buf ) );
139                }
140        }
141       
142        if( getpeername( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 )
143        {
144                char buf[NI_MAXHOST+1];
145
146                if( getnameinfo( (struct sockaddr *)&sock, socklen, buf,
147                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
148                {
149                        irc->host = g_strdup( ipv6_unwrap( buf ) );
150                }
151        }
152       
153        if( irc->host == NULL )
154                irc->host = g_strdup( "localhost.localdomain" );
155        if( irc->myhost == NULL )
156                irc->myhost = g_strdup( "localhost.localdomain" );
157       
158        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
159                irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
160       
161        irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
162
163        irc_connection_list = g_slist_append( irc_connection_list, irc );
164       
165        s = set_add( &irc->set, "away", NULL,  set_eval_away_status, irc );
166        s->flags |= SET_NULL_OK;
167        s = set_add( &irc->set, "away_devoice", "true",  set_eval_away_devoice, irc );
168        s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
169        s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc );
170        s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc );
171        s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
172        s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
173        s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc );
174        s = set_add( &irc->set, "debug", "false", set_eval_bool, irc );
175        s = set_add( &irc->set, "default_target", "root", NULL, irc );
176        s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
177        s = set_add( &irc->set, "handle_unknown", "root", NULL, irc );
178        s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
179        s = set_add( &irc->set, "ops", "both", set_eval_ops, irc );
180        s = set_add( &irc->set, "password", NULL, set_eval_password, irc );
181        s->flags |= SET_NULL_OK;
182        s = set_add( &irc->set, "private", "true", set_eval_bool, irc );
183        s = set_add( &irc->set, "query_order", "lifo", NULL, irc );
184        s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
185        s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
186        s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
187        s = set_add( &irc->set, "status", NULL,  set_eval_away_status, irc );
188        s->flags |= SET_NULL_OK;
189        s = set_add( &irc->set, "strip_html", "true", NULL, irc );
190        s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
191        s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc );
192       
193        conf_loaddefaults( irc );
194       
195        /* Evaluator sets the iconv/oconv structures. */
196        set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
197       
198        return( irc );
199}
200
201/* immed=1 makes this function pretty much equal to irc_free(), except that
202   this one will "log". In case the connection is already broken and we
203   shouldn't try to write to it. */
204void irc_abort( irc_t *irc, int immed, char *format, ... )
205{
206        if( format != NULL )
207        {
208                va_list params;
209                char *reason;
210               
211                va_start( params, format );
212                reason = g_strdup_vprintf( format, params );
213                va_end( params );
214               
215                if( !immed )
216                        irc_write( irc, "ERROR :Closing link: %s", reason );
217               
218                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
219                                   irc->nick ? irc->nick : "(NONE)", irc->host, reason );
220               
221                g_free( reason );
222        }
223        else
224        {
225                if( !immed )
226                        irc_write( irc, "ERROR :Closing link" );
227               
228                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
229                                   irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" );
230        }
231       
232        irc->status |= USTATUS_SHUTDOWN;
233        if( irc->sendbuffer && !immed )
234        {
235                /* Set up a timeout event that should shut down the connection
236                   in a second, just in case ..._write doesn't do it first. */
237               
238                b_event_remove( irc->r_watch_source_id );
239                irc->r_watch_source_id = 0;
240               
241                b_event_remove( irc->ping_source_id );
242                irc->ping_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
243        }
244        else
245        {
246                irc_free( irc );
247        }
248}
249
250static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
251{
252        g_free( key );
253       
254        return( TRUE );
255}
256
257/* Because we have no garbage collection, this is quite annoying */
258void irc_free( irc_t * irc )
259{
260        user_t *user, *usertmp;
261       
262        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
263       
264        if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) ) 
265                if( storage_save( irc, NULL, TRUE ) != STORAGE_OK )
266                        irc_usermsg( irc, "Error while saving settings!" );
267       
268        irc_connection_list = g_slist_remove( irc_connection_list, irc );
269       
270        while( irc->accounts )
271        {
272                if( irc->accounts->ic )
273                        imc_logout( irc->accounts->ic, FALSE );
274                else if( irc->accounts->reconnect )
275                        cancel_auto_reconnect( irc->accounts );
276               
277                if( irc->accounts->ic == NULL )
278                        account_del( irc, irc->accounts );
279                else
280                        /* Nasty hack, but account_del() doesn't work in this
281                           case and we don't want infinite loops, do we? ;-) */
282                        irc->accounts = irc->accounts->next;
283        }
284       
285        while( irc->queries != NULL )
286                query_del( irc, irc->queries );
287       
288        while( irc->set )
289                set_del( &irc->set, irc->set->key );
290       
291        if (irc->users != NULL)
292        {
293                user = irc->users;
294                while( user != NULL )
295                {
296                        g_free( user->nick );
297                        g_free( user->away );
298                        g_free( user->handle );
299                        if( user->user != user->nick ) g_free( user->user );
300                        if( user->host != user->nick ) g_free( user->host );
301                        if( user->realname != user->nick ) g_free( user->realname );
302                        b_event_remove( user->sendbuf_timer );
303                                       
304                        usertmp = user;
305                        user = user->next;
306                        g_free( usertmp );
307                }
308        }
309       
310        if( irc->ping_source_id > 0 )
311                b_event_remove( irc->ping_source_id );
312        if( irc->r_watch_source_id > 0 )
313                b_event_remove( irc->r_watch_source_id );
314        if( irc->w_watch_source_id > 0 )
315                b_event_remove( irc->w_watch_source_id );
316       
317        closesocket( irc->fd );
318        irc->fd = -1;
319       
320        g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
321        g_hash_table_destroy( irc->userhash );
322       
323        g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
324        g_hash_table_destroy( irc->watches );
325       
326        if( irc->iconv != (GIConv) -1 )
327                g_iconv_close( irc->iconv );
328        if( irc->oconv != (GIConv) -1 )
329                g_iconv_close( irc->oconv );
330       
331        g_free( irc->sendbuffer );
332        g_free( irc->readbuffer );
333       
334        g_free( irc->nick );
335        g_free( irc->user );
336        g_free( irc->host );
337        g_free( irc->realname );
338        g_free( irc->password );
339       
340        g_free( irc->myhost );
341        g_free( irc->mynick );
342       
343        g_free( irc->channel );
344       
345        g_free( irc->last_target );
346       
347        g_free( irc );
348       
349        if( global.conf->runmode == RUNMODE_INETD ||
350            global.conf->runmode == RUNMODE_FORKDAEMON ||
351            ( global.conf->runmode == RUNMODE_DAEMON &&
352              global.listen_socket == -1 &&
353              irc_connection_list == NULL ) )
354                b_main_quit();
355}
356
357/* USE WITH CAUTION!
358   Sets pass without checking */
359void irc_setpass (irc_t *irc, const char *pass) 
360{
361        g_free (irc->password);
362       
363        if (pass) {
364                irc->password = g_strdup (pass);
365        } else {
366                irc->password = NULL;
367        }
368}
369
370void irc_process( irc_t *irc )
371{
372        char **lines, *temp, **cmd;
373        int i;
374
375        if( irc->readbuffer != NULL )
376        {
377                lines = irc_tokenize( irc->readbuffer );
378               
379                for( i = 0; *lines[i] != '\0'; i ++ )
380                {
381                        char *conv = NULL;
382                       
383                        /* [WvG] If the last line isn't empty, it's an incomplete line and we
384                           should wait for the rest to come in before processing it. */
385                        if( lines[i+1] == NULL )
386                        {
387                                temp = g_strdup( lines[i] );
388                                g_free( irc->readbuffer );
389                                irc->readbuffer = temp;
390                                i ++;
391                                break;
392                        }
393                       
394                        if( irc->iconv != (GIConv) -1 )
395                        {
396                                gsize bytes_read, bytes_written;
397                               
398                                conv = g_convert_with_iconv( lines[i], -1, irc->iconv,
399                                                             &bytes_read, &bytes_written, NULL );
400                               
401                                if( conv == NULL || bytes_read != strlen( lines[i] ) )
402                                {
403                                        /* GLib can do strange things if things are not in the expected charset,
404                                           so let's be a little bit paranoid here: */
405                                        if( irc->status & USTATUS_LOGGED_IN )
406                                        {
407                                                irc_usermsg( irc, "Error: Charset mismatch detected. The charset "
408                                                                  "setting is currently set to %s, so please make "
409                                                                  "sure your IRC client will send and accept text in "
410                                                                  "that charset, or tell BitlBee which charset to "
411                                                                  "expect by changing the charset setting. See "
412                                                                  "`help set charset' for more information. Your "
413                                                                  "message was ignored.",
414                                                                  set_getstr( &irc->set, "charset" ) );
415                                               
416                                                g_free( conv );
417                                                conv = NULL;
418                                        }
419                                        else
420                                        {
421                                                irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
422                                                           "Warning: invalid characters received at login time." );
423                                               
424                                                conv = g_strdup( lines[i] );
425                                                for( temp = conv; *temp; temp ++ )
426                                                        if( *temp & 0x80 )
427                                                                *temp = '?';
428                                        }
429                                }
430                                lines[i] = conv;
431                        }
432                       
433                        if( lines[i] && ( cmd = irc_parse_line( lines[i] ) ) )
434                        {
435                                irc_exec( irc, cmd );
436                                g_free( cmd );
437                        }
438                       
439                        g_free( conv );
440                       
441                        /* Shouldn't really happen, but just in case... */
442                        if( !g_slist_find( irc_connection_list, irc ) )
443                        {
444                                g_free( lines );
445                                return;
446                        }
447                }
448               
449                if( lines[i] != NULL )
450                {
451                        g_free( irc->readbuffer );
452                        irc->readbuffer = NULL;
453                }
454               
455                g_free( lines );
456        }
457}
458
459/* Splits a long string into separate lines. The array is NULL-terminated and, unless the string
460   contains an incomplete line at the end, ends with an empty string. */
461char **irc_tokenize( char *buffer )
462{
463        int i, j, n = 3;
464        char **lines;
465
466        /* Allocate n+1 elements. */
467        lines = g_new( char *, n + 1 );
468       
469        lines[0] = buffer;
470       
471        /* Split the buffer in several strings, and accept any kind of line endings,
472         * knowing that ERC on Windows may send something interesting like \r\r\n,
473         * and surely there must be clients that think just \n is enough... */
474        for( i = 0, j = 0; buffer[i] != '\0'; i ++ )
475        {
476                if( buffer[i] == '\r' || buffer[i] == '\n' )
477                {
478                        while( buffer[i] == '\r' || buffer[i] == '\n' )
479                                buffer[i++] = '\0';
480                       
481                        lines[++j] = buffer + i;
482                       
483                        if( j >= n )
484                        {
485                                n *= 2;
486                                lines = g_renew( char *, lines, n + 1 );
487                        }
488
489                        if( buffer[i] == '\0' )
490                                break;
491                }
492        }
493       
494        /* NULL terminate our list. */ 
495        lines[++j] = NULL;
496       
497        return lines;
498}
499
500/* Split an IRC-style line into little parts/arguments. */
501char **irc_parse_line( char *line )
502{
503        int i, j;
504        char **cmd;
505       
506        /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
507        if( line[0] == ':' )
508        {
509                for( i = 0; line[i] && line[i] != ' '; i ++ );
510                line = line + i;
511        }
512        for( i = 0; line[i] == ' '; i ++ );
513        line = line + i;
514       
515        /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
516        if( line[0] == '\0')
517                return NULL;
518       
519        /* Count the number of char **cmd elements we're going to need. */
520        j = 1;
521        for( i = 0; line[i] != '\0'; i ++ )
522        {
523                if( line[i] == ' ' )
524                {
525                        j ++;
526                       
527                        if( line[i+1] == ':' )
528                                break;
529                }
530        }       
531
532        /* Allocate the space we need. */
533        cmd = g_new( char *, j + 1 );
534        cmd[j] = NULL;
535       
536        /* Do the actual line splitting, format is:
537         * Input: "PRIVMSG #bitlbee :foo bar"
538         * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
539         */
540
541        cmd[0] = line;
542        for( i = 0, j = 0; line[i] != '\0'; i ++ )
543        {
544                if( line[i] == ' ' )
545                {
546                        line[i] = '\0';
547                        cmd[++j] = line + i + 1;
548                       
549                        if( line[i+1] == ':' )
550                        {
551                                cmd[j] ++;
552                                break;
553                        }
554                }
555        }
556       
557        return cmd;
558}
559
560/* Converts such an array back into a command string. Mainly used for the IPC code right now. */
561char *irc_build_line( char **cmd )
562{
563        int i, len;
564        char *s;
565       
566        if( cmd[0] == NULL )
567                return NULL;
568       
569        len = 1;
570        for( i = 0; cmd[i]; i ++ )
571                len += strlen( cmd[i] ) + 1;
572       
573        if( strchr( cmd[i-1], ' ' ) != NULL )
574                len ++;
575       
576        s = g_new0( char, len + 1 );
577        for( i = 0; cmd[i]; i ++ )
578        {
579                if( cmd[i+1] == NULL && strchr( cmd[i], ' ' ) != NULL )
580                        strcat( s, ":" );
581               
582                strcat( s, cmd[i] );
583               
584                if( cmd[i+1] )
585                        strcat( s, " " );
586        }
587        strcat( s, "\r\n" );
588       
589        return s;
590}
591
592void irc_reply( irc_t *irc, int code, char *format, ... )
593{
594        char text[IRC_MAX_LINE];
595        va_list params;
596       
597        va_start( params, format );
598        g_vsnprintf( text, IRC_MAX_LINE, format, params );
599        va_end( params );
600        irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
601       
602        return;
603}
604
605int irc_usermsg( irc_t *irc, char *format, ... )
606{
607        char text[1024];
608        va_list params;
609        char is_private = 0;
610        user_t *u;
611       
612        u = user_find( irc, irc->mynick );
613        is_private = u->is_private;
614       
615        va_start( params, format );
616        g_vsnprintf( text, sizeof( text ), format, params );
617        va_end( params );
618       
619        return( irc_msgfrom( irc, u->nick, text ) );
620}
621
622void irc_write( irc_t *irc, char *format, ... ) 
623{
624        va_list params;
625
626        va_start( params, format );
627        irc_vawrite( irc, format, params );     
628        va_end( params );
629
630        return;
631}
632
633void irc_vawrite( irc_t *irc, char *format, va_list params )
634{
635        int size;
636        char line[IRC_MAX_LINE+1];
637               
638        /* Don't try to write anything new anymore when shutting down. */
639        if( irc->status & USTATUS_SHUTDOWN )
640                return;
641       
642        memset( line, 0, sizeof( line ) );
643        g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
644        strip_newlines( line );
645       
646        if( irc->oconv != (GIConv) -1 )
647        {
648                gsize bytes_read, bytes_written;
649                char *conv;
650               
651                conv = g_convert_with_iconv( line, -1, irc->oconv,
652                                             &bytes_read, &bytes_written, NULL );
653
654                if( bytes_read == strlen( line ) )
655                        strncpy( line, conv, IRC_MAX_LINE - 2 );
656               
657                g_free( conv );
658        }
659        g_strlcat( line, "\r\n", IRC_MAX_LINE + 1 );
660       
661        if( irc->sendbuffer != NULL )
662        {
663                size = strlen( irc->sendbuffer ) + strlen( line );
664                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
665                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
666        }
667        else
668        {
669                irc->sendbuffer = g_strdup(line);
670        }
671       
672        if( irc->w_watch_source_id == 0 )
673        {
674                /* If the buffer is empty we can probably write, so call the write event handler
675                   immediately. If it returns TRUE, it should be called again, so add the event to
676                   the queue. If it's FALSE, we emptied the buffer and saved ourselves some work
677                   in the event queue. */
678                /* Really can't be done as long as the code doesn't do error checking very well:
679                if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */
680               
681                /* So just always do it via the event handler. */
682                irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc );
683        }
684       
685        return;
686}
687
688void irc_write_all( int now, char *format, ... )
689{
690        va_list params;
691        GSList *temp;   
692       
693        va_start( params, format );
694       
695        temp = irc_connection_list;
696        while( temp != NULL )
697        {
698                irc_t *irc = temp->data;
699               
700                if( now )
701                {
702                        g_free( irc->sendbuffer );
703                        irc->sendbuffer = g_strdup( "\r\n" );
704                }
705                irc_vawrite( temp->data, format, params );
706                if( now )
707                {
708                        bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
709                }
710                temp = temp->next;
711        }
712       
713        va_end( params );
714        return;
715} 
716
717void irc_names( irc_t *irc, char *channel )
718{
719        user_t *u;
720        char namelist[385] = "";
721        struct groupchat *c = NULL;
722        char *ops = set_getstr( &irc->set, "ops" );
723       
724        /* RFCs say there is no error reply allowed on NAMES, so when the
725           channel is invalid, just give an empty reply. */
726       
727        if( g_strcasecmp( channel, irc->channel ) == 0 )
728        {
729                for( u = irc->users; u; u = u->next ) if( u->online )
730                {
731                        if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
732                        {
733                                irc_reply( irc, 353, "= %s :%s", channel, namelist );
734                                *namelist = 0;
735                        }
736                       
737                        if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) )
738                                strcat( namelist, "+" );
739                        else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ||
740                                 ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) )
741                                strcat( namelist, "@" );
742                       
743                        strcat( namelist, u->nick );
744                        strcat( namelist, " " );
745                }
746        }
747        else if( ( c = irc_chat_by_channel( irc, channel ) ) )
748        {
749                GList *l;
750               
751                /* root and the user aren't in the channel userlist but should
752                   show up in /NAMES, so list them first: */
753                sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick,
754                                                 strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick );
755               
756                for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) )
757                {
758                        if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
759                        {
760                                irc_reply( irc, 353, "= %s :%s", channel, namelist );
761                                *namelist = 0;
762                        }
763                       
764                        strcat( namelist, u->nick );
765                        strcat( namelist, " " );
766                }
767        }
768       
769        if( *namelist )
770                irc_reply( irc, 353, "= %s :%s", channel, namelist );
771       
772        irc_reply( irc, 366, "%s :End of /NAMES list", channel );
773}
774
775int irc_check_login( irc_t *irc )
776{
777        if( irc->user && irc->nick )
778        {
779                if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
780                {
781                        irc_reply( irc, 464, ":This server is password-protected." );
782                        return 0;
783                }
784                else
785                {
786                        irc_login( irc );
787                        return 1;
788                }
789        }
790        else
791        {
792                /* More information needed. */
793                return 0;
794        }
795}
796
797void irc_login( irc_t *irc )
798{
799        user_t *u;
800       
801        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
802        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
803        irc_reply( irc,   3, ":%s", IRCD_INFO );
804        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
805        irc_reply( irc,   5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee "
806                             "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server",
807                             CTYPES, CMODES, MAX_NICK_LENGTH - 1 );
808        irc_motd( irc );
809        irc->umode[0] = '\0';
810        irc_umode_set( irc, "+" UMODE, 1 );
811
812        u = user_add( irc, irc->mynick );
813        u->host = g_strdup( irc->myhost );
814        u->realname = g_strdup( ROOT_FN );
815        u->online = 1;
816        u->send_handler = root_command_string;
817        u->is_private = 0; /* [SH] The channel is root's personal playground. */
818        irc_spawn( irc, u );
819       
820        u = user_add( irc, NS_NICK );
821        u->host = g_strdup( irc->myhost );
822        u->realname = g_strdup( ROOT_FN );
823        u->online = 0;
824        u->send_handler = root_command_string;
825        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
826       
827        u = user_add( irc, irc->nick );
828        u->user = g_strdup( irc->user );
829        u->host = g_strdup( irc->host );
830        u->realname = g_strdup( irc->realname );
831        u->online = 1;
832        irc_spawn( irc, u );
833       
834        irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
835                          "If you've never used BitlBee before, please do read the help "
836                          "information using the \x02help\x02 command. Lots of FAQs are "
837                          "answered there.\n"
838                          "If you already have an account on this server, just use the "
839                          "\x02identify\x02 command to identify yourself." );
840       
841        if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
842                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
843       
844        irc->status |= USTATUS_LOGGED_IN;
845       
846        /* This is for bug #209 (use PASS to identify to NickServ). */
847        if( irc->password != NULL )
848        {
849                char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
850               
851                irc_setpass( irc, NULL );
852                root_command( irc, send_cmd );
853                g_free( send_cmd[1] );
854        }
855}
856
857void irc_motd( irc_t *irc )
858{
859        int fd;
860       
861        fd = open( global.conf->motdfile, O_RDONLY );
862        if( fd == -1 )
863        {
864                irc_reply( irc, 422, ":We don't need MOTDs." );
865        }
866        else
867        {
868                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
869                char *add, max;
870                int len;
871               
872                linebuf[79] = len = 0;
873                max = sizeof( linebuf ) - 1;
874               
875                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
876                while( read( fd, linebuf + len, 1 ) == 1 )
877                {
878                        if( linebuf[len] == '\n' || len == max )
879                        {
880                                linebuf[len] = 0;
881                                irc_reply( irc, 372, ":- %s", linebuf );
882                                len = 0;
883                        }
884                        else if( linebuf[len] == '%' )
885                        {
886                                read( fd, linebuf + len, 1 );
887                                if( linebuf[len] == 'h' )
888                                        add = irc->myhost;
889                                else if( linebuf[len] == 'v' )
890                                        add = BITLBEE_VERSION;
891                                else if( linebuf[len] == 'n' )
892                                        add = irc->nick;
893                                else
894                                        add = "%";
895                               
896                                strncpy( linebuf + len, add, max - len );
897                                while( linebuf[++len] );
898                        }
899                        else if( len < max )
900                        {
901                                len ++;
902                        }
903                }
904                irc_reply( irc, 376, ":End of MOTD" );
905                close( fd );
906        }
907}
908
909void irc_topic( irc_t *irc, char *channel )
910{
911        struct groupchat *c = irc_chat_by_channel( irc, channel );
912       
913        if( c && c->topic )
914                irc_reply( irc, 332, "%s :%s", channel, c->topic );
915        else if( g_strcasecmp( channel, irc->channel ) == 0 )
916                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
917        else
918                irc_reply( irc, 331, "%s :No topic for this channel", channel );
919}
920
921void irc_umode_set( irc_t *irc, char *s, int allow_priv )
922{
923        /* allow_priv: Set to 0 if s contains user input, 1 if you want
924           to set a "privileged" mode (+o, +R, etc). */
925        char m[256], st = 1, *t;
926        int i;
927        char changes[512], *p, st2 = 2;
928        char badflag = 0;
929       
930        memset( m, 0, sizeof( m ) );
931       
932        for( t = irc->umode; *t; t ++ )
933                m[(int)*t] = 1;
934
935        p = changes;
936        for( t = s; *t; t ++ )
937        {
938                if( *t == '+' || *t == '-' )
939                        st = *t == '+';
940                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
941                {
942                        if( m[(int)*t] != st)
943                        {
944                                if( st != st2 )
945                                        st2 = st, *p++ = st ? '+' : '-';
946                                *p++ = *t;
947                        }
948                        m[(int)*t] = st;
949                }
950                else
951                        badflag = 1;
952        }
953        *p = '\0';
954       
955        memset( irc->umode, 0, sizeof( irc->umode ) );
956       
957        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
958                if( m[i] )
959                        irc->umode[strlen(irc->umode)] = i;
960       
961        if( badflag )
962                irc_reply( irc, 501, ":Unknown MODE flag" );
963        /* Deliberately no !user@host on the prefix here */
964        if( *changes )
965                irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes );
966}
967
968void irc_spawn( irc_t *irc, user_t *u )
969{
970        irc_join( irc, u, irc->channel );
971}
972
973void irc_join( irc_t *irc, user_t *u, char *channel )
974{
975        char *nick;
976       
977        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
978                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
979       
980        if( nick_cmp( u->nick, irc->nick ) == 0 )
981        {
982                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
983                irc_names( irc, channel );
984                irc_topic( irc, channel );
985        }
986       
987        nick = g_strdup( u->nick );
988        nick_lc( nick );
989        if( g_hash_table_lookup( irc->watches, nick ) )
990        {
991                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" );
992        }
993        g_free( nick );
994}
995
996void irc_part( irc_t *irc, user_t *u, char *channel )
997{
998        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
999}
1000
1001void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
1002{
1003        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
1004}
1005
1006void irc_kill( irc_t *irc, user_t *u )
1007{
1008        char *nick, *s;
1009        char reason[128];
1010       
1011        if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) )
1012        {
1013                if( u->ic->acc->server )
1014                        g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
1015                                    u->ic->acc->server );
1016                else if( ( s = strchr( u->ic->acc->user, '@' ) ) )
1017                        g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
1018                                    s + 1 );
1019                else
1020                        g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
1021                                    u->ic->acc->prpl->name, irc->myhost );
1022               
1023                /* proto_opt might contain garbage after the : */
1024                if( ( s = strchr( reason, ':' ) ) )
1025                        *s = 0;
1026        }
1027        else
1028        {
1029                strcpy( reason, "Leaving..." );
1030        }
1031       
1032        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
1033       
1034        nick = g_strdup( u->nick );
1035        nick_lc( nick );
1036        if( g_hash_table_lookup( irc->watches, nick ) )
1037        {
1038                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
1039        }
1040        g_free( nick );
1041}
1042
1043int irc_send( irc_t *irc, char *nick, char *s, int flags )
1044{
1045        struct groupchat *c = NULL;
1046        user_t *u = NULL;
1047       
1048        if( strchr( CTYPES, *nick ) )
1049        {
1050                if( !( c = irc_chat_by_channel( irc, nick ) ) )
1051                {
1052                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
1053                        return( 0 );
1054                }
1055        }
1056        else
1057        {
1058                u = user_find( irc, nick );
1059               
1060                if( !u )
1061                {
1062                        if( irc->is_private )
1063                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
1064                        else
1065                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
1066                        return( 0 );
1067                }
1068        }
1069       
1070        if( *s == 1 && s[strlen(s)-1] == 1 )
1071        {
1072                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
1073                {
1074                        if( s[7] == ' ' ) s ++;
1075                        s += 3;
1076                        *(s++) = '/';
1077                        *(s++) = 'm';
1078                        *(s++) = 'e';
1079                        *(s++) = ' ';
1080                        s -= 4;
1081                        s[strlen(s)-1] = 0;
1082                }
1083                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
1084                {
1085                        u = user_find( irc, irc->mynick );
1086                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
1087                        return( 1 );
1088                }
1089                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
1090                {
1091                        u = user_find( irc, irc->mynick );
1092                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
1093                        return( 1 );
1094                }
1095                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
1096                {
1097                        if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 )
1098                        {
1099                                time_t current_typing_notice = time( NULL );
1100                               
1101                                if( current_typing_notice - u->last_typing_notice >= 5 )
1102                                {
1103                                        u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 );
1104                                        u->last_typing_notice = current_typing_notice;
1105                                }
1106                        }
1107                        return( 1 );
1108                }
1109                else if( g_strncasecmp( s + 1, "DCC", 3 ) == 0 )
1110                {
1111                        if( u && u->ic && u->ic->acc->prpl->transfer_request )
1112                        {
1113                                file_transfer_t *ft = dcc_request( u->ic, s + 5 );
1114                                if ( ft )
1115                                        u->ic->acc->prpl->transfer_request( u->ic, ft, u->handle );
1116                        }
1117                        return( 1 );
1118                }               
1119                else
1120                {
1121                        irc_usermsg( irc, "Supported CTCPs are ACTION, VERSION, PING, TYPING, DCC" );
1122                        return( 0 );
1123                }
1124        }
1125       
1126        if( u )
1127        {
1128                /* For the next message, we probably do have to send new notices... */
1129                u->last_typing_notice = 0;
1130                u->is_private = irc->is_private;
1131               
1132                if( u->is_private )
1133                {
1134                        if( !u->online )
1135                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1136                        else if( u->away )
1137                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1138                }
1139               
1140                if( u->send_handler )
1141                {
1142                        u->send_handler( irc, u, s, flags );
1143                        return 1;
1144                }
1145        }
1146        else if( c && c->ic && c->ic->acc && c->ic->acc->prpl )
1147        {
1148                return( imc_chat_msg( c, s, 0 ) );
1149        }
1150       
1151        return( 0 );
1152}
1153
1154static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond )
1155{
1156        user_t *u = data;
1157       
1158        /* Shouldn't happen, but just to be sure. */
1159        if( u->sendbuf_len < 2 )
1160                return FALSE;
1161       
1162        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
1163        imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags );
1164       
1165        g_free( u->sendbuf );
1166        u->sendbuf = NULL;
1167        u->sendbuf_len = 0;
1168        u->sendbuf_timer = 0;
1169        u->sendbuf_flags = 0;
1170       
1171        return FALSE;
1172}
1173
1174void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
1175{
1176        if( !u || !u->ic ) return;
1177       
1178        if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 )
1179        {
1180                int delay;
1181               
1182                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1183                {
1184                        /* Flush the buffer */
1185                        b_event_remove( u->sendbuf_timer );
1186                        buddy_send_handler_delayed( u, -1, 0 );
1187                }
1188
1189                if( u->sendbuf_len == 0 )
1190                {
1191                        u->sendbuf_len = strlen( msg ) + 2;
1192                        u->sendbuf = g_new( char, u->sendbuf_len );
1193                        u->sendbuf[0] = 0;
1194                        u->sendbuf_flags = flags;
1195                }
1196                else
1197                {
1198                        u->sendbuf_len += strlen( msg ) + 1;
1199                        u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
1200                }
1201               
1202                strcat( u->sendbuf, msg );
1203                strcat( u->sendbuf, "\n" );
1204               
1205                delay = set_getint( &irc->set, "buddy_sendbuffer_delay" );
1206                if( delay <= 5 )
1207                        delay *= 1000;
1208               
1209                if( u->sendbuf_timer > 0 )
1210                        b_event_remove( u->sendbuf_timer );
1211                u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u );
1212        }
1213        else
1214        {
1215                imc_buddy_msg( u->ic, u->handle, msg, flags );
1216        }
1217}
1218
1219int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1220{
1221        char last = 0;
1222        char *s = msg, *line = msg;
1223       
1224        /* The almighty linesplitter .. woohoo!! */
1225        while( !last )
1226        {
1227                if( *s == '\r' && *(s+1) == '\n' )
1228                        *(s++) = 0;
1229                if( *s == '\n' )
1230                {
1231                        last = s[1] == 0;
1232                        *s = 0;
1233                }
1234                else
1235                {
1236                        last = s[0] == 0;
1237                }
1238                if( *s == 0 )
1239                {
1240                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1241                        {
1242                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1243                                           type, to, line + 4 );
1244                        }
1245                        else
1246                        {
1247                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1248                                           type, to, prefix ? prefix : "", line );
1249                        }
1250                        line = s + 1;
1251                }
1252                s ++;
1253        }
1254       
1255        return( 1 );
1256}
1257
1258int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1259{
1260        user_t *u = user_find( irc, nick );
1261        static char *prefix = NULL;
1262       
1263        if( !u ) return( 0 );
1264        if( prefix && *prefix ) g_free( prefix );
1265       
1266        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1267        {
1268                int len = strlen( irc->nick) + 3;
1269                prefix = g_new (char, len );
1270                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) );
1271                prefix[len-1] = 0;
1272        }
1273        else
1274        {
1275                prefix = "";
1276        }
1277       
1278        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1279}
1280
1281int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1282{
1283        user_t *u = user_find( irc, nick );
1284       
1285        if( u )
1286                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1287        else
1288                return( 0 );
1289}
1290
1291/* Returns 0 if everything seems to be okay, a number >0 when there was a
1292   timeout. The number returned is the number of seconds we received no
1293   pongs from the user. When not connected yet, we don't ping but drop the
1294   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1295static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
1296{
1297        irc_t *irc = _irc;
1298        int rv = 0;
1299       
1300        if( !( irc->status & USTATUS_LOGGED_IN ) )
1301        {
1302                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1303                        rv = gettime() - irc->last_pong;
1304        }
1305        else
1306        {
1307                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1308                {
1309                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1310                        irc->pinging = 1;
1311                }
1312                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1313                {
1314                        rv = gettime() - irc->last_pong;
1315                }
1316        }
1317       
1318        if( rv > 0 )
1319        {
1320                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
1321                return FALSE;
1322        }
1323       
1324        return TRUE;
1325}
1326
1327struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
1328{
1329        struct groupchat *c;
1330        account_t *a;
1331       
1332        /* This finds the connection which has a conversation which belongs to this channel */
1333        for( a = irc->accounts; a; a = a->next )
1334        {
1335                if( a->ic == NULL )
1336                        continue;
1337               
1338                c = a->ic->groupchats;
1339                while( c )
1340                {
1341                        if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
1342                                return c;
1343                       
1344                        c = c->next;
1345                }
1346        }
1347       
1348        return NULL;
1349}
Note: See TracBrowser for help on using the repository browser.