source: irc.c @ 0fbd3a6d

Last change on this file since 0fbd3a6d was 08135df, checked in by ulim <a.sporto+bee@…>, at 2007-12-04T01:08:43Z

Merged in current devel

Wilmer van der Gaast 2007-12-02 Imported setuid() patch from Simo Leone <simo@archlinux...> with some

Wilmer van der Gaast 2007-12-02 Forgot to return something in jabber_chat_join_failed().
Wilmer van der Gaast 2007-12-02 Merging a change I should've pulled before committing three other changes.
Wilmer van der Gaast 2007-12-02 Added charset checks on incoming msgs (from the IRC side) to prevent possible
Wilmer van der Gaast 2007-12-02 Handling of presence-error packets (only useful for groupchats now), moved
Wilmer van der Gaast 2007-12-02 Defining DEBUG via CFLAGS so that it'll always be there, even when a file
Wilmer van der Gaast 2007-12-02 Removed retarded printf() (ARGH) and moved the event handling handling of
Wilmer van der Gaast 2007-11-29 printf() in daemons considered harmful.
Wilmer van der Gaast 2007-11-28 Fixed the epoll+ForkDaemon combination. The libevent event handling

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