source: irc.c @ 19ac9c5

Last change on this file since 19ac9c5 was ba9edaa, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-05-10T17:34:46Z

Moved everything to the BitlBee event handling API.

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