source: irc.c @ 3064ea4

Last change on this file since 3064ea4 was 3064ea4, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-16T15:20:58Z

rework keygen messages and add some notices

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