source: irc.c @ 8961950

Last change on this file since 8961950 was 8961950, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-16T16:25:24Z

read root's welcome message from a file (like tho MOTD)

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