source: irc.c @ be68d99

Last change on this file since be68d99 was 2231302, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-11-05T22:59:49Z

Merging from Jelmer.

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