source: irc.c @ 1cc0df3

Last change on this file since 1cc0df3 was 767a148, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-21T16:06:31Z

Merging in file transfer support. Most important points from my review
are fixed now, time to let it settle in and get people to try it.

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