source: irc.c @ 77bfd07

Last change on this file since 77bfd07 was 3e1e11af, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-11-19T23:41:42Z

Fixed NULL pointer dereference (in printf) when connected to a non-socket
(which I do quite often when testing stuff).

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