source: irc.c @ bd9b00f

Last change on this file since bd9b00f was bd9b00f, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-19T17:07:47Z

Fixes for single-process daemon mode, changed value of USTATUS_SHUTDOWN. If
this still causes problems, shutting down should be an extra flag instead of
a status code.

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