source: irc.c @ f73b969

Last change on this file since f73b969 was f73b969, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-20T15:15:49Z

Renamed commands.c, got rid of return values in all command functions.

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