source: irc.c @ 5c9512f

Last change on this file since 5c9512f was 5c9512f, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-06-30T09:17:18Z

Made set.c API more generic so it's not specific to irc_t structures anymore,
but can be used for account_t structures too, for example.

  • Property mode set to 100644
File size: 30.9 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        irc_usermsg( irc, "Password successfully changed" );
39        return NULL;
40}
41
42irc_t *irc_new( int fd )
43{
44        irc_t *irc;
45        struct hostent *peer;
46        unsigned int i;
47        char buf[128];
48#ifdef IPV6
49        struct sockaddr_in6 sock[1];
50#else
51        struct sockaddr_in sock[1];
52#endif
53       
54        irc = g_new0( irc_t, 1 );
55       
56        irc->fd = fd;
57        sock_make_nonblocking( irc->fd );
58       
59        irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc );
60       
61        irc->status = USTATUS_OFFLINE;
62        irc->last_pong = gettime();
63       
64        irc->userhash = g_hash_table_new( g_str_hash, g_str_equal );
65        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
66       
67        strcpy( irc->umode, UMODE );
68        irc->mynick = g_strdup( ROOT_NICK );
69        irc->channel = g_strdup( ROOT_CHAN );
70       
71        i = sizeof( *sock );
72       
73        if( global.conf->hostname )
74                irc->myhost = g_strdup( global.conf->hostname );
75#ifdef IPV6
76        else if( getsockname( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin6_family == AF_INETx )
77        {
78                if( ( peer = gethostbyaddr( (char*) &sock->sin6_addr, sizeof( sock->sin6_addr ), AF_INETx ) ) )
79                        irc->myhost = g_strdup( peer->h_name );
80                else if( inet_ntop( AF_INETx, &sock->sin6_addr, buf, sizeof( buf ) - 1 ) != NULL )
81                        irc->myhost = g_strdup( ipv6_unwrap( buf ) );
82        }
83#else
84        else if( getsockname( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin_family == AF_INETx )
85        {
86                if( ( peer = gethostbyaddr( (char*) &sock->sin_addr, sizeof( sock->sin_addr ), AF_INETx ) ) )
87                        irc->myhost = g_strdup( peer->h_name );
88                else if( inet_ntop( AF_INETx, &sock->sin_addr, buf, sizeof( buf ) - 1 ) != NULL )
89                        irc->myhost = g_strdup( buf );
90        }
91#endif
92       
93        i = sizeof( *sock );
94#ifdef IPV6
95        if( getpeername( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin6_family == AF_INETx )
96        {
97                if( ( peer = gethostbyaddr( (char*) &sock->sin6_addr, sizeof( sock->sin6_addr ), AF_INETx ) ) )
98                        irc->host = g_strdup( peer->h_name );
99                else if( inet_ntop( AF_INETx, &sock->sin6_addr, buf, sizeof( buf ) - 1 ) != NULL )
100                        irc->host = g_strdup( ipv6_unwrap( buf ) );
101        }
102#else
103        if( getpeername( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin_family == AF_INETx )
104        {
105                if( ( peer = gethostbyaddr( (char*) &sock->sin_addr, sizeof( sock->sin_addr ), AF_INETx ) ) )
106                        irc->host = g_strdup( peer->h_name );
107                else if( inet_ntop( AF_INETx, &sock->sin_addr, buf, sizeof( buf ) - 1 ) != NULL )
108                        irc->host = g_strdup( buf );
109        }
110#endif
111       
112        /* Rare, but possible. */
113        if( !irc->host ) irc->host = g_strdup( "localhost." );
114        if( !irc->myhost ) irc->myhost = g_strdup( "localhost." );
115
116        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
117                irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
118       
119        irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
120
121        irc_connection_list = g_slist_append( irc_connection_list, irc );
122       
123        set_add( &irc->set, "away_devoice", "true",  set_eval_away_devoice, irc );
124        set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
125        set_add( &irc->set, "auto_reconnect", "false", set_eval_bool, irc );
126        set_add( &irc->set, "auto_reconnect_delay", "300", set_eval_int, irc );
127        set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
128        set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
129        set_add( &irc->set, "charset", "iso8859-1", set_eval_charset, irc );
130        set_add( &irc->set, "debug", "false", set_eval_bool, irc );
131        set_add( &irc->set, "default_target", "root", NULL, irc );
132        set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
133        set_add( &irc->set, "handle_unknown", "root", NULL, irc );
134        set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
135        set_add( &irc->set, "ops", "both", set_eval_ops, irc );
136        set_add( &irc->set, "password", NULL, passchange, irc );
137        set_add( &irc->set, "private", "true", set_eval_bool, irc );
138        set_add( &irc->set, "query_order", "lifo", NULL, irc );
139        set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
140        set_add( &irc->set, "strip_html", "true", NULL, irc );
141        set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
142        set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc );
143       
144        conf_loaddefaults( irc );
145       
146        return( irc );
147}
148
149/* immed=1 makes this function pretty much equal to irc_free(), except that
150   this one will "log". In case the connection is already broken and we
151   shouldn't try to write to it. */
152void irc_abort( irc_t *irc, int immed, char *format, ... )
153{
154        if( format != NULL )
155        {
156                va_list params;
157                char *reason;
158               
159                va_start( params, format );
160                reason = g_strdup_vprintf( format, params );
161                va_end( params );
162               
163                if( !immed )
164                        irc_write( irc, "ERROR :Closing link: %s", reason );
165               
166                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
167                                   irc->nick ? irc->nick : "(NONE)", irc->host, reason );
168               
169                g_free( reason );
170        }
171        else
172        {
173                if( !immed )
174                        irc_write( irc, "ERROR :Closing link" );
175               
176                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
177                                   irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" );
178        }
179       
180        irc->status |= USTATUS_SHUTDOWN;
181        if( irc->sendbuffer && !immed )
182        {
183                /* We won't read from this socket anymore. Instead, we'll connect a timer
184                   to it that should shut down the connection in a second, just in case
185                   bitlbee_.._write doesn't do it first. */
186               
187                b_event_remove( irc->r_watch_source_id );
188                irc->r_watch_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
189        }
190        else
191        {
192                irc_free( irc );
193        }
194}
195
196static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
197{
198        g_free( key );
199       
200        return( TRUE );
201}
202
203/* Because we have no garbage collection, this is quite annoying */
204void irc_free(irc_t * irc)
205{
206        account_t *account, *accounttmp;
207        user_t *user, *usertmp;
208        nick_t *nick, *nicktmp;
209        help_t *helpnode, *helpnodetmp;
210        set_t *setnode, *setnodetmp;
211       
212        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
213       
214        if( irc->status & USTATUS_IDENTIFIED && set_getint( &irc->set, "save_on_quit" ) ) 
215                if( storage_save( irc, TRUE ) != STORAGE_OK )
216                        irc_usermsg( irc, "Error while saving settings!" );
217       
218        closesocket( irc->fd );
219       
220        if( irc->ping_source_id > 0 )
221                b_event_remove( irc->ping_source_id );
222        b_event_remove( irc->r_watch_source_id );
223        if( irc->w_watch_source_id > 0 )
224                b_event_remove( irc->w_watch_source_id );
225       
226        irc_connection_list = g_slist_remove( irc_connection_list, irc );
227       
228        for (account = irc->accounts; account; account = account->next) {
229                if (account->gc) {
230                        account->gc->wants_to_die = TRUE;
231                        signoff(account->gc);
232                } else if (account->reconnect) {
233                        cancel_auto_reconnect(account);
234                }
235        }
236       
237        g_free(irc->sendbuffer);
238        g_free(irc->readbuffer);
239       
240        g_free(irc->nick);
241        g_free(irc->user);
242        g_free(irc->host);
243        g_free(irc->realname);
244        g_free(irc->password);
245       
246        g_free(irc->myhost);
247        g_free(irc->mynick);
248       
249        g_free(irc->channel);
250       
251        while (irc->queries != NULL)
252                query_del(irc, irc->queries);
253       
254        if (irc->accounts != NULL) {
255                account = irc->accounts;
256                while (account != NULL) {
257                        g_free(account->user);
258                        g_free(account->pass);
259                        g_free(account->server);
260                        accounttmp = account;
261                        account = account->next;
262                        g_free(accounttmp);
263                }
264        }
265       
266        if (irc->users != NULL) {
267                user = irc->users;
268                while (user != NULL) {
269                        g_free(user->nick);
270                        g_free(user->away);
271                        g_free(user->handle);
272                        if(user->user!=user->nick) g_free(user->user);
273                        if(user->host!=user->nick) g_free(user->host);
274                        if(user->realname!=user->nick) g_free(user->realname);
275                        b_event_remove(user->sendbuf_timer);
276                                       
277                        usertmp = user;
278                        user = user->next;
279                        g_free(usertmp);
280                }
281        }
282       
283        g_hash_table_foreach_remove(irc->userhash, irc_free_hashkey, NULL);
284        g_hash_table_destroy(irc->userhash);
285       
286        g_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL);
287        g_hash_table_destroy(irc->watches);
288       
289        if (irc->nicks != NULL) {
290                nick = irc->nicks;
291                while (nick != NULL) {
292                        g_free(nick->nick);
293                        g_free(nick->handle);
294                                       
295                        nicktmp = nick;
296                        nick = nick->next;
297                        g_free(nicktmp);
298                }
299        }
300        if (irc->help != NULL) {
301                helpnode = irc->help;
302                while (helpnode != NULL) {
303                        g_free(helpnode->string);
304                       
305                        helpnodetmp = helpnode;
306                        helpnode = helpnode->next;
307                        g_free(helpnodetmp);
308                }
309        }
310        if (irc->set != NULL) {
311                setnode = irc->set;
312                while (setnode != NULL) {
313                        g_free(setnode->key);
314                        g_free(setnode->def);
315                        g_free(setnode->value);
316                       
317                        setnodetmp = setnode;
318                        setnode = setnode->next;
319                        g_free(setnodetmp);
320                }
321        }
322        g_free(irc);
323       
324        if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON )
325                b_main_quit();
326}
327
328/* USE WITH CAUTION!
329   Sets pass without checking */
330void irc_setpass (irc_t *irc, const char *pass) 
331{
332        g_free (irc->password);
333       
334        if (pass) {
335                irc->password = g_strdup (pass);
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->set, "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        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        /* Don't try to write anything new anymore when shutting down. */
579        if( irc->status & USTATUS_SHUTDOWN )
580                return;
581       
582        line[IRC_MAX_LINE] = 0;
583        g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
584       
585        strip_newlines( line );
586        if( ( cs = set_getstr( &irc->set, "charset" ) ) && ( g_strcasecmp( cs, "utf-8" ) != 0 ) )
587        {
588                char conv[IRC_MAX_LINE+1];
589               
590                conv[IRC_MAX_LINE] = 0;
591                if( do_iconv( "UTF-8", cs, line, conv, 0, IRC_MAX_LINE - 2 ) != -1 )
592                        strcpy( line, conv );
593        }
594        strcat( line, "\r\n" );
595       
596        if( irc->sendbuffer != NULL )
597        {
598                size = strlen( irc->sendbuffer ) + strlen( line );
599                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
600                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
601        }
602        else
603        {
604                irc->sendbuffer = g_strdup(line);
605        }
606       
607        if( irc->w_watch_source_id == 0 )
608        {
609                /* If the buffer is empty we can probably write, so call the write event handler
610                   immediately. If it returns TRUE, it should be called again, so add the event to
611                   the queue. If it's FALSE, we emptied the buffer and saved ourselves some work
612                   in the event queue. */
613                if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) )
614                        irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc );
615        }
616       
617        return;
618}
619
620void irc_write_all( int now, char *format, ... )
621{
622        va_list params;
623        GSList *temp;   
624       
625        va_start( params, format );
626       
627        temp = irc_connection_list;
628        while( temp != NULL )
629        {
630                irc_t *irc = temp->data;
631               
632                if( now )
633                {
634                        g_free( irc->sendbuffer );
635                        irc->sendbuffer = g_strdup( "\r\n" );
636                }
637                irc_vawrite( temp->data, format, params );
638                if( now )
639                {
640                        bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
641                }
642                temp = temp->next;
643        }
644       
645        va_end( params );
646        return;
647} 
648
649void irc_names( irc_t *irc, char *channel )
650{
651        user_t *u = irc->users;
652        char *s;
653        int control = ( g_strcasecmp( channel, irc->channel ) == 0 );
654        struct conversation *c = NULL;
655       
656        if( !control )
657                c = conv_findchannel( channel );
658       
659        /* RFCs say there is no error reply allowed on NAMES, so when the
660           channel is invalid, just give an empty reply. */
661       
662        if( control || c ) while( u )
663        {
664                if( u->online )
665                {
666                        if( u->gc && control )
667                        {
668                                if( set_getint( &irc->set, "away_devoice" ) && !u->away )
669                                        s = "+";
670                                else
671                                        s = "";
672                               
673                                irc_reply( irc, 353, "= %s :%s%s", channel, s, u->nick );
674                        }
675                        else if( !u->gc )
676                        {
677                                if( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( set_getstr( &irc->set, "ops" ), "root" ) == 0 || strcmp( set_getstr( &irc->set, "ops" ), "both" ) == 0 ) )
678                                        s = "@";
679                                else if( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( set_getstr( &irc->set, "ops" ), "user" ) == 0 || strcmp( set_getstr( &irc->set, "ops" ), "both" ) == 0 ) )
680                                        s = "@";
681                                else
682                                        s = "";
683                               
684                                irc_reply( irc, 353, "= %s :%s%s", channel, s, u->nick );
685                        }
686                }
687               
688                u = u->next;
689        }
690       
691        /* For non-controlchannel channels (group conversations) only root and
692           you are listed now. Time to show the channel people: */
693        if( !control && c )
694        {
695                GList *l;
696               
697                for( l = c->in_room; l; l = l->next )
698                        if( ( u = user_findhandle( c->gc, l->data ) ) )
699                                irc_reply( irc, 353, "= %s :%s%s", channel, "", u->nick );
700        }
701       
702        irc_reply( irc, 366, "%s :End of /NAMES list", channel );
703}
704
705int irc_check_login( irc_t *irc )
706{
707        if( irc->user && irc->nick )
708        {
709                if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
710                {
711                        irc_reply( irc, 464, ":This server is password-protected." );
712                        return 0;
713                }
714                else
715                {
716                        irc_login( irc );
717                        return 1;
718                }
719        }
720        else
721        {
722                /* More information needed. */
723                return 0;
724        }
725}
726
727void irc_login( irc_t *irc )
728{
729        user_t *u;
730       
731        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
732        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
733        irc_reply( irc,   3, ":%s", IRCD_INFO );
734        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
735        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 );
736        irc_motd( irc );
737        irc->umode[0] = '\0';
738        irc_umode_set( irc, "+" UMODE, 1 );
739
740        u = user_add( irc, irc->mynick );
741        u->host = g_strdup( irc->myhost );
742        u->realname = g_strdup( ROOT_FN );
743        u->online = 1;
744        u->send_handler = root_command_string;
745        u->is_private = 0; /* [SH] The channel is root's personal playground. */
746        irc_spawn( irc, u );
747       
748        u = user_add( irc, NS_NICK );
749        u->host = g_strdup( irc->myhost );
750        u->realname = g_strdup( ROOT_FN );
751        u->online = 0;
752        u->send_handler = root_command_string;
753        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
754       
755        u = user_add( irc, irc->nick );
756        u->user = g_strdup( irc->user );
757        u->host = g_strdup( irc->host );
758        u->realname = g_strdup( irc->realname );
759        u->online = 1;
760        irc_spawn( irc, u );
761       
762        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." );
763       
764        if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
765                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
766       
767        irc->status |= USTATUS_LOGGED_IN;
768}
769
770void irc_motd( irc_t *irc )
771{
772        int fd;
773       
774        fd = open( global.conf->motdfile, O_RDONLY );
775        if( fd == -1 )
776        {
777                irc_reply( irc, 422, ":We don't need MOTDs." );
778        }
779        else
780        {
781                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
782                char *add, max;
783                int len;
784               
785                linebuf[79] = len = 0;
786                max = sizeof( linebuf ) - 1;
787               
788                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
789                while( read( fd, linebuf + len, 1 ) == 1 )
790                {
791                        if( linebuf[len] == '\n' || len == max )
792                        {
793                                linebuf[len] = 0;
794                                irc_reply( irc, 372, ":- %s", linebuf );
795                                len = 0;
796                        }
797                        else if( linebuf[len] == '%' )
798                        {
799                                read( fd, linebuf + len, 1 );
800                                if( linebuf[len] == 'h' )
801                                        add = irc->myhost;
802                                else if( linebuf[len] == 'v' )
803                                        add = BITLBEE_VERSION;
804                                else if( linebuf[len] == 'n' )
805                                        add = irc->nick;
806                                else
807                                        add = "%";
808                               
809                                strncpy( linebuf + len, add, max - len );
810                                while( linebuf[++len] );
811                        }
812                        else if( len < max )
813                        {
814                                len ++;
815                        }
816                }
817                irc_reply( irc, 376, ":End of MOTD" );
818                close( fd );
819        }
820}
821
822void irc_topic( irc_t *irc, char *channel )
823{
824        if( g_strcasecmp( channel, irc->channel ) == 0 )
825        {
826                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
827        }
828        else
829        {
830                struct conversation *c = conv_findchannel( channel );
831               
832                if( c )
833                        irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
834                else
835                        irc_reply( irc, 331, "%s :No topic for this channel", channel );
836        }
837}
838
839void irc_umode_set( irc_t *irc, char *s, int allow_priv )
840{
841        /* allow_priv: Set to 0 if s contains user input, 1 if you want
842           to set a "privileged" mode (+o, +R, etc). */
843        char m[256], st = 1, *t;
844        int i;
845        char changes[512], *p, st2 = 2;
846        char badflag = 0;
847       
848        memset( m, 0, sizeof( m ) );
849       
850        for( t = irc->umode; *t; t ++ )
851                m[(int)*t] = 1;
852
853        p = changes;
854        for( t = s; *t; t ++ )
855        {
856                if( *t == '+' || *t == '-' )
857                        st = *t == '+';
858                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
859                {
860                        if( m[(int)*t] != st)
861                        {
862                                if( st != st2 )
863                                        st2 = st, *p++ = st ? '+' : '-';
864                                *p++ = *t;
865                        }
866                        m[(int)*t] = st;
867                }
868                else
869                        badflag = 1;
870        }
871        *p = '\0';
872       
873        memset( irc->umode, 0, sizeof( irc->umode ) );
874       
875        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
876                if( m[i] )
877                        irc->umode[strlen(irc->umode)] = i;
878       
879        if( badflag )
880                irc_reply( irc, 501, ":Unknown MODE flag" );
881        /* Deliberately no !user@host on the prefix here */
882        if( *changes )
883                irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes );
884}
885
886void irc_spawn( irc_t *irc, user_t *u )
887{
888        irc_join( irc, u, irc->channel );
889}
890
891void irc_join( irc_t *irc, user_t *u, char *channel )
892{
893        char *nick;
894       
895        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
896                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
897       
898        if( nick_cmp( u->nick, irc->nick ) == 0 )
899        {
900                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
901                irc_names( irc, channel );
902                irc_topic( irc, channel );
903        }
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, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" );
910        }
911        g_free( nick );
912}
913
914void irc_part( irc_t *irc, user_t *u, char *channel )
915{
916        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
917}
918
919void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
920{
921        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
922}
923
924void irc_kill( irc_t *irc, user_t *u )
925{
926        char *nick, *s;
927        char reason[64];
928       
929        if( u->gc && u->gc->flags & OPT_LOGGING_OUT )
930        {
931                if( u->gc->user->proto_opt[0][0] )
932                        g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
933                                    u->gc->user->proto_opt[0] );
934                else if( ( s = strchr( u->gc->username, '@' ) ) )
935                        g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
936                                    s + 1 );
937                else
938                        g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
939                                    u->gc->prpl->name, irc->myhost );
940               
941                /* proto_opt might contain garbage after the : */
942                if( ( s = strchr( reason, ':' ) ) )
943                        *s = 0;
944        }
945        else
946        {
947                strcpy( reason, "Leaving..." );
948        }
949       
950        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
951       
952        nick = g_strdup( u->nick );
953        nick_lc( nick );
954        if( g_hash_table_lookup( irc->watches, nick ) )
955        {
956                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
957        }
958        g_free( nick );
959}
960
961int irc_send( irc_t *irc, char *nick, char *s, int flags )
962{
963        struct conversation *c = NULL;
964        user_t *u = NULL;
965       
966        if( *nick == '#' || *nick == '&' )
967        {
968                if( !( c = conv_findchannel( nick ) ) )
969                {
970                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
971                        return( 0 );
972                }
973        }
974        else
975        {
976                u = user_find( irc, nick );
977               
978                if( !u )
979                {
980                        if( irc->is_private )
981                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
982                        else
983                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
984                        return( 0 );
985                }
986        }
987       
988        if( *s == 1 && s[strlen(s)-1] == 1 )
989        {
990                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
991                {
992                        if( s[7] == ' ' ) s ++;
993                        s += 3;
994                        *(s++) = '/';
995                        *(s++) = 'm';
996                        *(s++) = 'e';
997                        *(s++) = ' ';
998                        s -= 4;
999                        s[strlen(s)-1] = 0;
1000                }
1001                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
1002                {
1003                        u = user_find( irc, irc->mynick );
1004                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
1005                        return( 1 );
1006                }
1007                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
1008                {
1009                        u = user_find( irc, irc->mynick );
1010                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
1011                        return( 1 );
1012                }
1013                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
1014                {
1015                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
1016                        {
1017                                time_t current_typing_notice = time( NULL );
1018                               
1019                                if( current_typing_notice - u->last_typing_notice >= 5 )
1020                                {
1021                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
1022                                        u->last_typing_notice = current_typing_notice;
1023                                }
1024                        }
1025                        return( 1 );
1026                }
1027                else
1028                {
1029                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
1030                        return( 0 );
1031                }
1032        }
1033       
1034        if( u )
1035        {
1036                /* For the next message, we probably do have to send new notices... */
1037                u->last_typing_notice = 0;
1038                u->is_private = irc->is_private;
1039               
1040                if( u->is_private )
1041                {
1042                        if( !u->online )
1043                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1044                        else if( u->away )
1045                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1046                }
1047               
1048                if( u->send_handler )
1049                {
1050                        u->send_handler( irc, u, s, flags );
1051                        return 1;
1052                }
1053        }
1054        else if( c && c->gc && c->gc->prpl )
1055        {
1056                return( bim_chat_msg( c->gc, c->id, s ) );
1057        }
1058       
1059        return( 0 );
1060}
1061
1062static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond )
1063{
1064        user_t *u = data;
1065       
1066        /* Shouldn't happen, but just to be sure. */
1067        if( u->sendbuf_len < 2 )
1068                return FALSE;
1069       
1070        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
1071        bim_buddy_msg( u->gc, u->handle, u->sendbuf, u->sendbuf_flags );
1072       
1073        g_free( u->sendbuf );
1074        u->sendbuf = NULL;
1075        u->sendbuf_len = 0;
1076        u->sendbuf_timer = 0;
1077        u->sendbuf_flags = 0;
1078       
1079        return FALSE;
1080}
1081
1082void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
1083{
1084        if( !u || !u->gc ) return;
1085       
1086        if( set_getint( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 )
1087        {
1088                int delay;
1089               
1090                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1091                {
1092                        /* Flush the buffer */
1093                        b_event_remove( u->sendbuf_timer );
1094                        buddy_send_handler_delayed( u, -1, 0 );
1095                }
1096
1097                if( u->sendbuf_len == 0 )
1098                {
1099                        u->sendbuf_len = strlen( msg ) + 2;
1100                        u->sendbuf = g_new( char, u->sendbuf_len );
1101                        u->sendbuf[0] = 0;
1102                        u->sendbuf_flags = flags;
1103                }
1104                else
1105                {
1106                        u->sendbuf_len += strlen( msg ) + 1;
1107                        u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
1108                }
1109               
1110                strcat( u->sendbuf, msg );
1111                strcat( u->sendbuf, "\n" );
1112               
1113                delay = set_getint( &irc->set, "buddy_sendbuffer_delay" );
1114                if( delay <= 5 )
1115                        delay *= 1000;
1116               
1117                if( u->sendbuf_timer > 0 )
1118                        b_event_remove( u->sendbuf_timer );
1119                u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u );
1120        }
1121        else
1122        {
1123                bim_buddy_msg( u->gc, u->handle, msg, flags );
1124        }
1125}
1126
1127int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1128{
1129        char last = 0;
1130        char *s = msg, *line = msg;
1131       
1132        /* The almighty linesplitter .. woohoo!! */
1133        while( !last )
1134        {
1135                if( *s == '\r' && *(s+1) == '\n' )
1136                        *(s++) = 0;
1137                if( *s == '\n' )
1138                {
1139                        last = s[1] == 0;
1140                        *s = 0;
1141                }
1142                else
1143                {
1144                        last = s[0] == 0;
1145                }
1146                if( *s == 0 )
1147                {
1148                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1149                        {
1150                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1151                                           type, to, line + 4 );
1152                        }
1153                        else
1154                        {
1155                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1156                                           type, to, prefix ? prefix : "", line );
1157                        }
1158                        line = s + 1;
1159                }
1160                s ++;
1161        }
1162       
1163        return( 1 );
1164}
1165
1166int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1167{
1168        user_t *u = user_find( irc, nick );
1169        static char *prefix = NULL;
1170       
1171        if( !u ) return( 0 );
1172        if( prefix && *prefix ) g_free( prefix );
1173       
1174        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1175        {
1176                int len = strlen( irc->nick) + 3;
1177                prefix = g_new (char, len );
1178                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) );
1179                prefix[len-1] = 0;
1180        }
1181        else
1182        {
1183                prefix = "";
1184        }
1185       
1186        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1187}
1188
1189int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1190{
1191        user_t *u = user_find( irc, nick );
1192       
1193        if( u )
1194                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1195        else
1196                return( 0 );
1197}
1198
1199/* Returns 0 if everything seems to be okay, a number >0 when there was a
1200   timeout. The number returned is the number of seconds we received no
1201   pongs from the user. When not connected yet, we don't ping but drop the
1202   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1203static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
1204{
1205        irc_t *irc = _irc;
1206        int rv = 0;
1207       
1208        if( !( irc->status & USTATUS_LOGGED_IN ) )
1209        {
1210                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1211                        rv = gettime() - irc->last_pong;
1212        }
1213        else
1214        {
1215                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1216                {
1217                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1218                        irc->pinging = 1;
1219                }
1220                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1221                {
1222                        rv = gettime() - irc->last_pong;
1223                }
1224        }
1225       
1226        if( rv > 0 )
1227        {
1228                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
1229                return FALSE;
1230        }
1231       
1232        return TRUE;
1233}
Note: See TracBrowser for help on using the repository browser.