source: irc.c @ 55cc2be3

Last change on this file since 55cc2be3 was 55cc2be3, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-03-15T18:17:01Z

Fixed cleanup of connections in (non-forking) daemon mode. (Better handling
of auto_reconnect)

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