source: irc.c @ 823de9d

Last change on this file since 823de9d was 823de9d, checked in by Sven Moritz Hallberg <pesco@…>, at 2009-03-12T19:10:06Z

commit updates by ashish shukla <wahjava@…>

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