source: irc.c @ d783e48

Last change on this file since d783e48 was d783e48, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-03-31T08:53:53Z

irc_vawrite() does charset conversion now. Next step: Do it for incoming IRC
traffic, and get rid of most other iconv() calls.

  • Property mode set to 100644
File size: 28.5 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2004 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* The big hairy IRCd part of the project                               */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#define BITLBEE_CORE
27#include "bitlbee.h"
28#include "crypting.h"
29#include "ipc.h"
30
31static gboolean irc_userping( gpointer _irc );
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+1], *cs;
566               
567        if( irc->quit )
568                return;
569       
570        line[IRC_MAX_LINE] = 0;
571        g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
572       
573        strip_newlines( line );
574        if( ( cs = set_getstr( irc, "charset" ) ) )
575        {
576                char conv[IRC_MAX_LINE+1];
577               
578                conv[IRC_MAX_LINE] = 0;
579                if( do_iconv( "UTF-8", cs, line, conv, 0, IRC_MAX_LINE - 2 ) != -1 )
580                        strcpy( line, conv );
581        }
582        strcat( line, "\r\n" );
583       
584        if( irc->sendbuffer != NULL ) {
585                size = strlen( irc->sendbuffer ) + strlen( line );
586                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
587                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
588        }
589        else 
590                irc->sendbuffer = g_strdup(line);       
591       
592        if( irc->w_watch_source_id == 0 )
593        {
594                irc->w_watch_source_id = g_io_add_watch( irc->io_channel, G_IO_OUT, bitlbee_io_current_client_write, irc );
595        }
596       
597        return;
598}
599
600void irc_write_all( int now, char *format, ... )
601{
602        va_list params;
603        GSList *temp;   
604       
605        va_start( params, format );
606       
607        temp = irc_connection_list;
608        while( temp != NULL )
609        {
610                irc_t *irc = temp->data;
611               
612                if( now )
613                {
614                        g_free( irc->sendbuffer );
615                        irc->sendbuffer = g_strdup( "\r\n" );
616                }
617                irc_vawrite( temp->data, format, params );
618                if( now )
619                {
620                        bitlbee_io_current_client_write( irc->io_channel, G_IO_OUT, irc );
621                }
622                temp = temp->next;
623        }
624       
625        va_end( params );
626        return;
627} 
628
629void irc_names( irc_t *irc, char *channel )
630{
631        user_t *u = irc->users;
632        char *s;
633        int control = ( g_strcasecmp( channel, irc->channel ) == 0 );
634        struct conversation *c = NULL;
635       
636        if( !control )
637                c = conv_findchannel( channel );
638       
639        /* RFC's say there is no error reply allowed on NAMES, so when the
640           channel is invalid, just give an empty reply. */
641       
642        if( control || c ) while( u )
643        {
644                if( u->online )
645                {
646                        if( u->gc && control )
647                        {
648                                if( set_getint( irc, "away_devoice" ) && !u->away )
649                                        s = "+";
650                                else
651                                        s = "";
652                               
653                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
654                        }
655                        else if( !u->gc )
656                        {
657                                if( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "root" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
658                                        s = "@";
659                                else if( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "user" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
660                                        s = "@";
661                                else
662                                        s = "";
663                               
664                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
665                        }
666                }
667               
668                u = u->next;
669        }
670       
671        /* For non-controlchannel channels (group conversations) only root and
672           you are listed now. Time to show the channel people: */
673        if( !control && c )
674        {
675                GList *l;
676               
677                for( l = c->in_room; l; l = l->next )
678                        if( ( u = user_findhandle( c->gc, l->data ) ) )
679                                irc_reply( irc, 353, "@ %s :%s%s", channel, "", u->nick );
680        }
681       
682        irc_reply( irc, 366, "%s :End of /NAMES list", channel );
683}
684
685int irc_check_login( irc_t *irc )
686{
687        if( irc->user && irc->nick )
688        {
689                if( global.conf->authmode == AUTHMODE_CLOSED && irc->status < USTATUS_AUTHORIZED )
690                {
691                        irc_reply( irc, 464, ":This server is password-protected." );
692                        return 0;
693                }
694                else
695                {
696                        irc_login( irc );
697                        return 1;
698                }
699        }
700        else
701        {
702                /* More information needed. */
703                return 0;
704        }
705}
706
707void irc_login( irc_t *irc )
708{
709        user_t *u;
710       
711        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
712        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
713        irc_reply( irc,   3, ":%s", IRCD_INFO );
714        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
715        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 );
716        irc_motd( irc );
717        irc_umode_set( irc, "+" UMODE, 1 );
718
719        u = user_add( irc, irc->mynick );
720        u->host = g_strdup( irc->myhost );
721        u->realname = g_strdup( ROOT_FN );
722        u->online = 1;
723        u->send_handler = root_command_string;
724        u->is_private = 0; /* [SH] The channel is root's personal playground. */
725        irc_spawn( irc, u );
726       
727        u = user_add( irc, NS_NICK );
728        u->host = g_strdup( irc->myhost );
729        u->realname = g_strdup( ROOT_FN );
730        u->online = 0;
731        u->send_handler = root_command_string;
732        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
733       
734        u = user_add( irc, irc->nick );
735        u->user = g_strdup( irc->user );
736        u->host = g_strdup( irc->host );
737        u->realname = g_strdup( irc->realname );
738        u->online = 1;
739        irc_spawn( irc, u );
740       
741        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." );
742       
743        if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
744                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
745       
746        irc->status = USTATUS_LOGGED_IN;
747}
748
749void irc_motd( irc_t *irc )
750{
751        int fd;
752       
753        fd = open( global.conf->motdfile, O_RDONLY );
754        if( fd == -1 )
755        {
756                irc_reply( irc, 422, ":We don't need MOTDs." );
757        }
758        else
759        {
760                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
761                char *add, max;
762                int len;
763               
764                linebuf[79] = len = 0;
765                max = sizeof( linebuf ) - 1;
766               
767                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
768                while( read( fd, linebuf + len, 1 ) == 1 )
769                {
770                        if( linebuf[len] == '\n' || len == max )
771                        {
772                                linebuf[len] = 0;
773                                irc_reply( irc, 372, ":- %s", linebuf );
774                                len = 0;
775                        }
776                        else if( linebuf[len] == '%' )
777                        {
778                                read( fd, linebuf + len, 1 );
779                                if( linebuf[len] == 'h' )
780                                        add = irc->myhost;
781                                else if( linebuf[len] == 'v' )
782                                        add = BITLBEE_VERSION;
783                                else if( linebuf[len] == 'n' )
784                                        add = irc->nick;
785                                else
786                                        add = "%";
787                               
788                                strncpy( linebuf + len, add, max - len );
789                                while( linebuf[++len] );
790                        }
791                        else if( len < max )
792                        {
793                                len ++;
794                        }
795                }
796                irc_reply( irc, 376, ":End of MOTD" );
797                close( fd );
798        }
799}
800
801void irc_topic( irc_t *irc, char *channel )
802{
803        if( g_strcasecmp( channel, irc->channel ) == 0 )
804        {
805                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
806        }
807        else
808        {
809                struct conversation *c = conv_findchannel( channel );
810               
811                if( c )
812                        irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
813                else
814                        irc_reply( irc, 331, "%s :No topic for this channel" );
815        }
816}
817
818void irc_umode_set( irc_t *irc, char *s, int allow_priv )
819{
820        /* allow_priv: Set to 0 if s contains user input, 1 if you want
821           to set a "privileged" mode (+o, +R, etc). */
822        char m[256], st = 1, *t;
823        int i;
824       
825        memset( m, 0, sizeof( m ) );
826       
827        for( t = irc->umode; *t; t ++ )
828                m[(int)*t] = 1;
829       
830        for( t = s; *t; t ++ )
831        {
832                if( *t == '+' || *t == '-' )
833                        st = *t == '+';
834                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
835                        m[(int)*t] = st;
836        }
837       
838        memset( irc->umode, 0, sizeof( irc->umode ) );
839       
840        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
841                if( m[i] )
842                        irc->umode[strlen(irc->umode)] = i;
843       
844        irc_reply( irc, 221, "+%s", irc->umode );
845}
846
847void irc_spawn( irc_t *irc, user_t *u )
848{
849        irc_join( irc, u, irc->channel );
850}
851
852void irc_join( irc_t *irc, user_t *u, char *channel )
853{
854        char *nick;
855       
856        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
857                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
858       
859        if( nick_cmp( u->nick, irc->nick ) == 0 )
860        {
861                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
862                irc_names( irc, channel );
863                irc_topic( irc, channel );
864        }
865       
866        nick = g_strdup( u->nick );
867        nick_lc( nick );
868        if( g_hash_table_lookup( irc->watches, nick ) )
869        {
870                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged online" );
871        }
872        g_free( nick );
873}
874
875void irc_part( irc_t *irc, user_t *u, char *channel )
876{
877        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
878}
879
880void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
881{
882        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
883}
884
885void irc_kill( irc_t *irc, user_t *u )
886{
887        char *nick;
888       
889        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
890       
891        nick = g_strdup( u->nick );
892        nick_lc( nick );
893        if( g_hash_table_lookup( irc->watches, nick ) )
894        {
895                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged offline" );
896        }
897        g_free( nick );
898}
899
900int irc_send( irc_t *irc, char *nick, char *s, int flags )
901{
902        struct conversation *c = NULL;
903        user_t *u = NULL;
904       
905        if( *nick == '#' || *nick == '&' )
906        {
907                if( !( c = conv_findchannel( nick ) ) )
908                {
909                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
910                        return( 0 );
911                }
912        }
913        else
914        {
915                u = user_find( irc, nick );
916               
917                if( !u )
918                {
919                        if( irc->is_private )
920                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
921                        else
922                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
923                        return( 0 );
924                }
925        }
926       
927        if( *s == 1 && s[strlen(s)-1] == 1 )
928        {
929                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
930                {
931                        if( s[7] == ' ' ) s ++;
932                        s += 3;
933                        *(s++) = '/';
934                        *(s++) = 'm';
935                        *(s++) = 'e';
936                        *(s++) = ' ';
937                        s -= 4;
938                        s[strlen(s)-1] = 0;
939                }
940                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
941                {
942                        u = user_find( irc, irc->mynick );
943                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
944                        return( 1 );
945                }
946                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
947                {
948                        u = user_find( irc, irc->mynick );
949                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
950                        return( 1 );
951                }
952                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
953                {
954                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
955                        {
956                                time_t current_typing_notice = time( NULL );
957                               
958                                if( current_typing_notice - u->last_typing_notice >= 5 )
959                                {
960                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
961                                        u->last_typing_notice = current_typing_notice;
962                                }
963                        }
964                        return( 1 );
965                }
966                else
967                {
968                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
969                        return( 0 );
970                }
971        }
972       
973        if( u )
974        {
975                /* For the next message, we probably do have to send new notices... */
976                u->last_typing_notice = 0;
977                u->is_private = irc->is_private;
978               
979                if( u->is_private )
980                {
981                        if( !u->online )
982                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
983                        else if( u->away )
984                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
985                }
986               
987                if( u->send_handler )
988                {
989                        u->send_handler( irc, u, s, flags );
990                        return 1;
991                }
992        }
993        else if( c && c->gc && c->gc->prpl )
994        {
995                return( serv_send_chat( irc, c->gc, c->id, s ) );
996        }
997       
998        return( 0 );
999}
1000
1001gboolean buddy_send_handler_delayed( gpointer data )
1002{
1003        user_t *u = data;
1004       
1005        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
1006        serv_send_im( u->gc->irc, u, u->sendbuf, u->sendbuf_flags );
1007       
1008        g_free( u->sendbuf );
1009        u->sendbuf = NULL;
1010        u->sendbuf_len = 0;
1011        u->sendbuf_timer = 0;
1012        u->sendbuf_flags = 0;
1013       
1014        return( FALSE );
1015}
1016
1017void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
1018{
1019        if( !u || !u->gc ) return;
1020       
1021        if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 )
1022        {
1023                int delay;
1024               
1025                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1026                {
1027                        //Flush the buffer
1028                        g_source_remove( u->sendbuf_timer );
1029                        buddy_send_handler_delayed( u );
1030                }
1031
1032                if( u->sendbuf_len == 0 )
1033                {
1034                        u->sendbuf_len = strlen( msg ) + 2;
1035                        u->sendbuf = g_new (char, u->sendbuf_len );
1036                        u->sendbuf[0] = 0;
1037                        u->sendbuf_flags = flags;
1038                }
1039                else
1040                {
1041                        u->sendbuf_len += strlen( msg ) + 1;
1042                        u->sendbuf = g_renew ( char, u->sendbuf, u->sendbuf_len );
1043                }
1044               
1045                strcat( u->sendbuf, msg );
1046                strcat( u->sendbuf, "\n" );
1047               
1048                delay = set_getint( irc, "buddy_sendbuffer_delay" );
1049                if( delay <= 5 )
1050                        delay *= 1000;
1051               
1052                if( u->sendbuf_timer > 0 )
1053                        g_source_remove( u->sendbuf_timer );
1054                u->sendbuf_timer = g_timeout_add( delay, buddy_send_handler_delayed, u );
1055        }
1056        else
1057        {
1058                serv_send_im( irc, u, msg, flags );
1059        }
1060}
1061
1062int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1063{
1064        char last = 0;
1065        char *s = msg, *line = msg;
1066       
1067        /* The almighty linesplitter .. woohoo!! */
1068        while( !last )
1069        {
1070                if( *s == '\r' && *(s+1) == '\n' )
1071                        *(s++) = 0;
1072                if( *s == '\n' )
1073                {
1074                        last = s[1] == 0;
1075                        *s = 0;
1076                }
1077                else
1078                {
1079                        last = s[0] == 0;
1080                }
1081                if( *s == 0 )
1082                {
1083                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1084                        {
1085                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1086                                           type, to, line + 4 );
1087                        }
1088                        else
1089                        {
1090                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1091                                           type, to, prefix ? prefix : "", line );
1092                        }
1093                        line = s + 1;
1094                }
1095                s ++;
1096        }
1097       
1098        return( 1 );
1099}
1100
1101int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1102{
1103        user_t *u = user_find( irc, nick );
1104        static char *prefix = NULL;
1105       
1106        if( !u ) return( 0 );
1107        if( prefix && *prefix ) g_free( prefix );
1108       
1109        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1110        {
1111                int len = strlen( irc->nick) + 3;
1112                prefix = g_new (char, len );
1113                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) );
1114                prefix[len-1] = 0;
1115        }
1116        else
1117        {
1118                prefix = "";
1119        }
1120       
1121        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1122}
1123
1124int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1125{
1126        user_t *u = user_find( irc, nick );
1127       
1128        if( u )
1129                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1130        else
1131                return( 0 );
1132}
1133
1134/* Returns 0 if everything seems to be okay, a number >0 when there was a
1135   timeout. The number returned is the number of seconds we received no
1136   pongs from the user. When not connected yet, we don't ping but drop the
1137   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1138static gboolean irc_userping( gpointer _irc )
1139{
1140        irc_t *irc = _irc;
1141        int rv = 0;
1142       
1143        if( irc->status < USTATUS_LOGGED_IN )
1144        {
1145                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1146                        rv = gettime() - irc->last_pong;
1147        }
1148        else
1149        {
1150                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1151                {
1152                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1153                        irc->pinging = 1;
1154                }
1155                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1156                {
1157                        rv = gettime() - irc->last_pong;
1158                }
1159        }
1160       
1161        if( rv > 0 )
1162        {
1163                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
1164                return FALSE;
1165        }
1166       
1167        return TRUE;
1168}
Note: See TracBrowser for help on using the repository browser.