source: irc.c @ 673a54c

Last change on this file since 673a54c was 673a54c, checked in by Sven Moritz Hallberg <pesco@…>, at 2009-03-12T19:33:28Z

pretty blind try at merging in the latest trunk

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