source: irc.c @ 7d3ef7b

Last change on this file since 7d3ef7b was 7d3ef7b, checked in by Jelmer Vernooij <jelmer@…>, at 2008-06-10T03:04:44Z

Fix undefined references.

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