source: irc.c @ e9b755e

Last change on this file since e9b755e was e9b755e, checked in by Jelmer Vernooij <jelmer@…>, at 2007-10-18T16:44:25Z

Use standard functions for dealing with both IPv6 and IPv4.

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