source: irc.c @ 574af7e

Last change on this file since 574af7e was 574af7e, checked in by Jelmer Vernooij <jelmer@…>, at 2006-05-25T23:20:54Z

Require GLib 2

  • Property mode set to 100644
File size: 29.4 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;
902       
903        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
904       
905        nick = g_strdup( u->nick );
906        nick_lc( nick );
907        if( g_hash_table_lookup( irc->watches, nick ) )
908        {
909                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
910        }
911        g_free( nick );
912}
913
914int irc_send( irc_t *irc, char *nick, char *s, int flags )
915{
916        struct conversation *c = NULL;
917        user_t *u = NULL;
918       
919        if( *nick == '#' || *nick == '&' )
920        {
921                if( !( c = conv_findchannel( nick ) ) )
922                {
923                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
924                        return( 0 );
925                }
926        }
927        else
928        {
929                u = user_find( irc, nick );
930               
931                if( !u )
932                {
933                        if( irc->is_private )
934                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
935                        else
936                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
937                        return( 0 );
938                }
939        }
940       
941        if( *s == 1 && s[strlen(s)-1] == 1 )
942        {
943                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
944                {
945                        if( s[7] == ' ' ) s ++;
946                        s += 3;
947                        *(s++) = '/';
948                        *(s++) = 'm';
949                        *(s++) = 'e';
950                        *(s++) = ' ';
951                        s -= 4;
952                        s[strlen(s)-1] = 0;
953                }
954                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
955                {
956                        u = user_find( irc, irc->mynick );
957                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
958                        return( 1 );
959                }
960                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
961                {
962                        u = user_find( irc, irc->mynick );
963                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
964                        return( 1 );
965                }
966                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
967                {
968                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
969                        {
970                                time_t current_typing_notice = time( NULL );
971                               
972                                if( current_typing_notice - u->last_typing_notice >= 5 )
973                                {
974                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
975                                        u->last_typing_notice = current_typing_notice;
976                                }
977                        }
978                        return( 1 );
979                }
980                else
981                {
982                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
983                        return( 0 );
984                }
985        }
986       
987        if( u )
988        {
989                /* For the next message, we probably do have to send new notices... */
990                u->last_typing_notice = 0;
991                u->is_private = irc->is_private;
992               
993                if( u->is_private )
994                {
995                        if( !u->online )
996                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
997                        else if( u->away )
998                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
999                }
1000               
1001                if( u->send_handler )
1002                {
1003                        u->send_handler( irc, u, s, flags );
1004                        return 1;
1005                }
1006        }
1007        else if( c && c->gc && c->gc->prpl )
1008        {
1009                return( bim_chat_msg( c->gc, c->id, s ) );
1010        }
1011       
1012        return( 0 );
1013}
1014
1015gboolean buddy_send_handler_delayed( gpointer data )
1016{
1017        user_t *u = data;
1018       
1019        /* Shouldn't happen, but just to be sure. */
1020        if( u->sendbuf_len < 2 )
1021                return FALSE;
1022       
1023        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
1024        bim_buddy_msg( u->gc, u->handle, u->sendbuf, u->sendbuf_flags );
1025       
1026        g_free( u->sendbuf );
1027        u->sendbuf = NULL;
1028        u->sendbuf_len = 0;
1029        u->sendbuf_timer = 0;
1030        u->sendbuf_flags = 0;
1031       
1032        return FALSE;
1033}
1034
1035void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
1036{
1037        if( !u || !u->gc ) return;
1038       
1039        if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 )
1040        {
1041                int delay;
1042               
1043                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1044                {
1045                        /* Flush the buffer */
1046                        g_source_remove( u->sendbuf_timer );
1047                        buddy_send_handler_delayed( u );
1048                }
1049
1050                if( u->sendbuf_len == 0 )
1051                {
1052                        u->sendbuf_len = strlen( msg ) + 2;
1053                        u->sendbuf = g_new( char, u->sendbuf_len );
1054                        u->sendbuf[0] = 0;
1055                        u->sendbuf_flags = flags;
1056                }
1057                else
1058                {
1059                        u->sendbuf_len += strlen( msg ) + 1;
1060                        u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
1061                }
1062               
1063                strcat( u->sendbuf, msg );
1064                strcat( u->sendbuf, "\n" );
1065               
1066                delay = set_getint( irc, "buddy_sendbuffer_delay" );
1067                if( delay <= 5 )
1068                        delay *= 1000;
1069               
1070                if( u->sendbuf_timer > 0 )
1071                        g_source_remove( u->sendbuf_timer );
1072                u->sendbuf_timer = g_timeout_add( delay, buddy_send_handler_delayed, u );
1073        }
1074        else
1075        {
1076                bim_buddy_msg( u->gc, u->handle, msg, flags );
1077        }
1078}
1079
1080int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1081{
1082        char last = 0;
1083        char *s = msg, *line = msg;
1084       
1085        /* The almighty linesplitter .. woohoo!! */
1086        while( !last )
1087        {
1088                if( *s == '\r' && *(s+1) == '\n' )
1089                        *(s++) = 0;
1090                if( *s == '\n' )
1091                {
1092                        last = s[1] == 0;
1093                        *s = 0;
1094                }
1095                else
1096                {
1097                        last = s[0] == 0;
1098                }
1099                if( *s == 0 )
1100                {
1101                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1102                        {
1103                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1104                                           type, to, line + 4 );
1105                        }
1106                        else
1107                        {
1108                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1109                                           type, to, prefix ? prefix : "", line );
1110                        }
1111                        line = s + 1;
1112                }
1113                s ++;
1114        }
1115       
1116        return( 1 );
1117}
1118
1119int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1120{
1121        user_t *u = user_find( irc, nick );
1122        static char *prefix = NULL;
1123       
1124        if( !u ) return( 0 );
1125        if( prefix && *prefix ) g_free( prefix );
1126       
1127        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1128        {
1129                int len = strlen( irc->nick) + 3;
1130                prefix = g_new (char, len );
1131                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) );
1132                prefix[len-1] = 0;
1133        }
1134        else
1135        {
1136                prefix = "";
1137        }
1138       
1139        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1140}
1141
1142int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1143{
1144        user_t *u = user_find( irc, nick );
1145       
1146        if( u )
1147                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1148        else
1149                return( 0 );
1150}
1151
1152/* Returns 0 if everything seems to be okay, a number >0 when there was a
1153   timeout. The number returned is the number of seconds we received no
1154   pongs from the user. When not connected yet, we don't ping but drop the
1155   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1156static gboolean irc_userping( gpointer _irc )
1157{
1158        irc_t *irc = _irc;
1159        int rv = 0;
1160       
1161        if( irc->status < USTATUS_LOGGED_IN )
1162        {
1163                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1164                        rv = gettime() - irc->last_pong;
1165        }
1166        else
1167        {
1168                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1169                {
1170                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1171                        irc->pinging = 1;
1172                }
1173                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1174                {
1175                        rv = gettime() - irc->last_pong;
1176                }
1177        }
1178       
1179        if( rv > 0 )
1180        {
1181                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
1182                return FALSE;
1183        }
1184       
1185        return TRUE;
1186}
Note: See TracBrowser for help on using the repository browser.