source: irc.c @ 7df5a08

Last change on this file since 7df5a08 was 50e1776, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-11-19T22:23:58Z

Merging /TOPIC code from Miklos Vajna. Untested, because I still have to
implement the Jabber hooks.

  • Property mode set to 100644
File size: 30.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                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 = irc_chat_by_channel( irc, 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        struct groupchat *c = irc_chat_by_channel( irc, channel );
791       
792        if( c && c->topic )
793                irc_reply( irc, 332, "%s :%s", channel, c->topic );
794        else if( g_strcasecmp( channel, irc->channel ) == 0 )
795                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
796        else
797                irc_reply( irc, 331, "%s :No topic for this channel", channel );
798}
799
800void irc_umode_set( irc_t *irc, char *s, int allow_priv )
801{
802        /* allow_priv: Set to 0 if s contains user input, 1 if you want
803           to set a "privileged" mode (+o, +R, etc). */
804        char m[256], st = 1, *t;
805        int i;
806        char changes[512], *p, st2 = 2;
807        char badflag = 0;
808       
809        memset( m, 0, sizeof( m ) );
810       
811        for( t = irc->umode; *t; t ++ )
812                m[(int)*t] = 1;
813
814        p = changes;
815        for( t = s; *t; t ++ )
816        {
817                if( *t == '+' || *t == '-' )
818                        st = *t == '+';
819                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
820                {
821                        if( m[(int)*t] != st)
822                        {
823                                if( st != st2 )
824                                        st2 = st, *p++ = st ? '+' : '-';
825                                *p++ = *t;
826                        }
827                        m[(int)*t] = st;
828                }
829                else
830                        badflag = 1;
831        }
832        *p = '\0';
833       
834        memset( irc->umode, 0, sizeof( irc->umode ) );
835       
836        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
837                if( m[i] )
838                        irc->umode[strlen(irc->umode)] = i;
839       
840        if( badflag )
841                irc_reply( irc, 501, ":Unknown MODE flag" );
842        /* Deliberately no !user@host on the prefix here */
843        if( *changes )
844                irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes );
845}
846
847void irc_spawn( irc_t *irc, user_t *u )
848{
849        irc_join( irc, u, irc->channel );
850}
851
852void irc_join( irc_t *irc, user_t *u, char *channel )
853{
854        char *nick;
855       
856        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
857                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
858       
859        if( nick_cmp( u->nick, irc->nick ) == 0 )
860        {
861                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
862                irc_names( irc, channel );
863                irc_topic( irc, channel );
864        }
865       
866        nick = g_strdup( u->nick );
867        nick_lc( nick );
868        if( g_hash_table_lookup( irc->watches, nick ) )
869        {
870                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" );
871        }
872        g_free( nick );
873}
874
875void irc_part( irc_t *irc, user_t *u, char *channel )
876{
877        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
878}
879
880void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
881{
882        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
883}
884
885void irc_kill( irc_t *irc, user_t *u )
886{
887        char *nick, *s;
888        char reason[128];
889       
890        if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) )
891        {
892                if( u->ic->acc->server )
893                        g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
894                                    u->ic->acc->server );
895                else if( ( s = strchr( u->ic->acc->user, '@' ) ) )
896                        g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
897                                    s + 1 );
898                else
899                        g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
900                                    u->ic->acc->prpl->name, irc->myhost );
901               
902                /* proto_opt might contain garbage after the : */
903                if( ( s = strchr( reason, ':' ) ) )
904                        *s = 0;
905        }
906        else
907        {
908                strcpy( reason, "Leaving..." );
909        }
910       
911        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
912       
913        nick = g_strdup( u->nick );
914        nick_lc( nick );
915        if( g_hash_table_lookup( irc->watches, nick ) )
916        {
917                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
918        }
919        g_free( nick );
920}
921
922int irc_send( irc_t *irc, char *nick, char *s, int flags )
923{
924        struct groupchat *c = NULL;
925        user_t *u = NULL;
926       
927        if( *nick == '#' || *nick == '&' )
928        {
929                if( !( c = irc_chat_by_channel( irc, nick ) ) )
930                {
931                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
932                        return( 0 );
933                }
934        }
935        else
936        {
937                u = user_find( irc, nick );
938               
939                if( !u )
940                {
941                        if( irc->is_private )
942                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
943                        else
944                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
945                        return( 0 );
946                }
947        }
948       
949        if( *s == 1 && s[strlen(s)-1] == 1 )
950        {
951                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
952                {
953                        if( s[7] == ' ' ) s ++;
954                        s += 3;
955                        *(s++) = '/';
956                        *(s++) = 'm';
957                        *(s++) = 'e';
958                        *(s++) = ' ';
959                        s -= 4;
960                        s[strlen(s)-1] = 0;
961                }
962                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
963                {
964                        u = user_find( irc, irc->mynick );
965                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
966                        return( 1 );
967                }
968                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
969                {
970                        u = user_find( irc, irc->mynick );
971                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
972                        return( 1 );
973                }
974                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
975                {
976                        if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 )
977                        {
978                                time_t current_typing_notice = time( NULL );
979                               
980                                if( current_typing_notice - u->last_typing_notice >= 5 )
981                                {
982                                        u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 );
983                                        u->last_typing_notice = current_typing_notice;
984                                }
985                        }
986                        return( 1 );
987                }
988                else
989                {
990                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
991                        return( 0 );
992                }
993        }
994       
995        if( u )
996        {
997                /* For the next message, we probably do have to send new notices... */
998                u->last_typing_notice = 0;
999                u->is_private = irc->is_private;
1000               
1001                if( u->is_private )
1002                {
1003                        if( !u->online )
1004                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1005                        else if( u->away )
1006                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1007                }
1008               
1009                if( u->send_handler )
1010                {
1011                        u->send_handler( irc, u, s, flags );
1012                        return 1;
1013                }
1014        }
1015        else if( c && c->ic && c->ic->acc && c->ic->acc->prpl )
1016        {
1017                return( imc_chat_msg( c, s, 0 ) );
1018        }
1019       
1020        return( 0 );
1021}
1022
1023static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond )
1024{
1025        user_t *u = data;
1026       
1027        /* Shouldn't happen, but just to be sure. */
1028        if( u->sendbuf_len < 2 )
1029                return FALSE;
1030       
1031        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
1032        imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags );
1033       
1034        g_free( u->sendbuf );
1035        u->sendbuf = NULL;
1036        u->sendbuf_len = 0;
1037        u->sendbuf_timer = 0;
1038        u->sendbuf_flags = 0;
1039       
1040        return FALSE;
1041}
1042
1043void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
1044{
1045        if( !u || !u->ic ) return;
1046       
1047        if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 )
1048        {
1049                int delay;
1050               
1051                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1052                {
1053                        /* Flush the buffer */
1054                        b_event_remove( u->sendbuf_timer );
1055                        buddy_send_handler_delayed( u, -1, 0 );
1056                }
1057
1058                if( u->sendbuf_len == 0 )
1059                {
1060                        u->sendbuf_len = strlen( msg ) + 2;
1061                        u->sendbuf = g_new( char, u->sendbuf_len );
1062                        u->sendbuf[0] = 0;
1063                        u->sendbuf_flags = flags;
1064                }
1065                else
1066                {
1067                        u->sendbuf_len += strlen( msg ) + 1;
1068                        u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
1069                }
1070               
1071                strcat( u->sendbuf, msg );
1072                strcat( u->sendbuf, "\n" );
1073               
1074                delay = set_getint( &irc->set, "buddy_sendbuffer_delay" );
1075                if( delay <= 5 )
1076                        delay *= 1000;
1077               
1078                if( u->sendbuf_timer > 0 )
1079                        b_event_remove( u->sendbuf_timer );
1080                u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u );
1081        }
1082        else
1083        {
1084                imc_buddy_msg( u->ic, u->handle, msg, flags );
1085        }
1086}
1087
1088int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1089{
1090        char last = 0;
1091        char *s = msg, *line = msg;
1092       
1093        /* The almighty linesplitter .. woohoo!! */
1094        while( !last )
1095        {
1096                if( *s == '\r' && *(s+1) == '\n' )
1097                        *(s++) = 0;
1098                if( *s == '\n' )
1099                {
1100                        last = s[1] == 0;
1101                        *s = 0;
1102                }
1103                else
1104                {
1105                        last = s[0] == 0;
1106                }
1107                if( *s == 0 )
1108                {
1109                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1110                        {
1111                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1112                                           type, to, line + 4 );
1113                        }
1114                        else
1115                        {
1116                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1117                                           type, to, prefix ? prefix : "", line );
1118                        }
1119                        line = s + 1;
1120                }
1121                s ++;
1122        }
1123       
1124        return( 1 );
1125}
1126
1127int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1128{
1129        user_t *u = user_find( irc, nick );
1130        static char *prefix = NULL;
1131       
1132        if( !u ) return( 0 );
1133        if( prefix && *prefix ) g_free( prefix );
1134       
1135        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1136        {
1137                int len = strlen( irc->nick) + 3;
1138                prefix = g_new (char, len );
1139                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) );
1140                prefix[len-1] = 0;
1141        }
1142        else
1143        {
1144                prefix = "";
1145        }
1146       
1147        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1148}
1149
1150int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1151{
1152        user_t *u = user_find( irc, nick );
1153       
1154        if( u )
1155                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1156        else
1157                return( 0 );
1158}
1159
1160/* Returns 0 if everything seems to be okay, a number >0 when there was a
1161   timeout. The number returned is the number of seconds we received no
1162   pongs from the user. When not connected yet, we don't ping but drop the
1163   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1164static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
1165{
1166        irc_t *irc = _irc;
1167        int rv = 0;
1168       
1169        if( !( irc->status & USTATUS_LOGGED_IN ) )
1170        {
1171                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1172                        rv = gettime() - irc->last_pong;
1173        }
1174        else
1175        {
1176                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1177                {
1178                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1179                        irc->pinging = 1;
1180                }
1181                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1182                {
1183                        rv = gettime() - irc->last_pong;
1184                }
1185        }
1186       
1187        if( rv > 0 )
1188        {
1189                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
1190                return FALSE;
1191        }
1192       
1193        return TRUE;
1194}
1195
1196struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
1197{
1198        struct groupchat *c;
1199        account_t *a;
1200       
1201        /* This finds the connection which has a conversation which belongs to this channel */
1202        for( a = irc->accounts; a; a = a->next )
1203        {
1204                if( a->ic == NULL )
1205                        continue;
1206               
1207                c = a->ic->groupchats;
1208                while( c )
1209                {
1210                        if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
1211                                return c;
1212                       
1213                        c = c->next;
1214                }
1215        }
1216       
1217        return NULL;
1218}
Note: See TracBrowser for help on using the repository browser.