source: irc.c @ 764c7d1

Last change on this file since 764c7d1 was 764c7d1, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-03T21:30:03Z

OTR support, first checkin

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