source: irc.c @ 79c6f9f

Last change on this file since 79c6f9f was c99af3a, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-04-13T07:30:11Z

Removed account_offline(), we have signoff() already.

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