source: irc.c @ 5a5c926

Last change on this file since 5a5c926 was fb62f81f, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-06-03T19:51:16Z

Implemented netsplits for "account off".

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