source: irc.c @ 0196c47

Last change on this file since 0196c47 was 2a6ca4f, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-04T11:16:58Z

Better handling of IPv4 connections in IPv6 mode. (Wrapping/Unwrapping of ::ffff:style addresses.)

  • Property mode set to 100644
File size: 41.1 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2004 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* The big hairy IRCd part of the project                               */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#define BITLBEE_CORE
27#include "bitlbee.h"
28#include "crypting.h"
29
30static gboolean irc_userping( gpointer _irc );
31
32GSList *irc_connection_list = NULL;
33
34static char *passchange (irc_t *irc, void *set, char *value) 
35{
36        irc_setpass (irc, value);
37        return (NULL);
38}
39
40irc_t *irc_new( int fd )
41{
42        irc_t *irc;
43        struct hostent *peer;
44        unsigned int i;
45        char buf[128];
46#ifdef IPV6
47        struct sockaddr_in6 sock[1];
48#else
49        struct sockaddr_in sock[1];
50#endif
51       
52        irc = g_new0( irc_t, 1 );
53       
54        irc->fd = fd;
55        irc->io_channel = g_io_channel_unix_new( fd );
56#ifdef GLIB2
57        g_io_channel_set_encoding (irc->io_channel, NULL, NULL);
58        g_io_channel_set_buffered (irc->io_channel, FALSE);
59        g_io_channel_set_flags( irc->io_channel, G_IO_FLAG_NONBLOCK, NULL );
60#else
61        fcntl( irc->fd, F_SETFL, O_NONBLOCK);
62#endif
63        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 );
64       
65        irc->status = USTATUS_OFFLINE;
66        irc->last_pong = gettime();
67       
68        irc->userhash = g_hash_table_new( g_str_hash, g_str_equal );
69        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
70       
71        strcpy( irc->umode, UMODE );
72        irc->mynick = g_strdup( ROOT_NICK );
73        irc->channel = g_strdup( ROOT_CHAN );
74       
75        i = sizeof( *sock );
76       
77        if( global.conf->hostname )
78                irc->myhost = g_strdup( global.conf->hostname );
79#ifdef IPV6
80        else if( getsockname( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin6_family == AF_INETx )
81        {
82                if( ( peer = gethostbyaddr( (char*) &sock->sin6_addr, sizeof( sock->sin6_addr ), AF_INETx ) ) )
83                        irc->myhost = g_strdup( peer->h_name );
84                else if( inet_ntop( AF_INETx, &sock->sin6_addr, buf, sizeof( buf ) - 1 ) != NULL )
85                        irc->myhost = g_strdup( ipv6_unwrap( buf ) );
86        }
87#else
88        else if( getsockname( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin_family == AF_INETx )
89        {
90                if( ( peer = gethostbyaddr( (char*) &sock->sin_addr, sizeof( sock->sin_addr ), AF_INETx ) ) )
91                        irc->myhost = g_strdup( peer->h_name );
92                else if( inet_ntop( AF_INETx, &sock->sin_addr, buf, sizeof( buf ) - 1 ) != NULL )
93                        irc->myhost = g_strdup( buf );
94        }
95#endif
96       
97        i = sizeof( *sock );
98#ifdef IPV6
99        if( getpeername( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin6_family == AF_INETx )
100        {
101                if( ( peer = gethostbyaddr( (char*) &sock->sin6_addr, sizeof( sock->sin6_addr ), AF_INETx ) ) )
102                        irc->host = g_strdup( peer->h_name );
103                else if( inet_ntop( AF_INETx, &sock->sin6_addr, buf, sizeof( buf ) - 1 ) != NULL )
104                        irc->host = g_strdup( ipv6_unwrap( buf ) );
105        }
106#else
107        if( getpeername( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin_family == AF_INETx )
108        {
109                if( ( peer = gethostbyaddr( (char*) &sock->sin_addr, sizeof( sock->sin_addr ), AF_INETx ) ) )
110                        irc->host = g_strdup( peer->h_name );
111                else if( inet_ntop( AF_INETx, &sock->sin_addr, buf, sizeof( buf ) - 1 ) != NULL )
112                        irc->host = g_strdup( buf );
113        }
114#endif
115       
116        /* Rare, but possible. */
117        if( !irc->host ) irc->host = g_strdup( "localhost." );
118        if( !irc->myhost ) irc->myhost = g_strdup( "localhost." );
119
120        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
121                irc->ping_source_id = g_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
122       
123        irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
124
125        irc_connection_list = g_slist_append( irc_connection_list, irc );
126       
127        set_add( irc, "away_devoice", "true",  set_eval_away_devoice );
128        set_add( irc, "auto_connect", "true", set_eval_bool );
129        set_add( irc, "auto_reconnect", "false", set_eval_bool );
130        set_add( irc, "auto_reconnect_delay", "300", set_eval_int );
131        set_add( irc, "buddy_sendbuffer", "false", set_eval_bool );
132        set_add( irc, "buddy_sendbuffer_delay", "200", set_eval_int );
133        set_add( irc, "charset", "iso8859-1", set_eval_charset );
134        set_add( irc, "debug", "false", set_eval_bool );
135        set_add( irc, "default_target", "root", NULL );
136        set_add( irc, "display_namechanges", "false", set_eval_bool );
137        set_add( irc, "handle_unknown", "root", NULL );
138        set_add( irc, "lcnicks", "true", set_eval_bool );
139        set_add( irc, "ops", "both", set_eval_ops );
140        set_add( irc, "private", "true", set_eval_bool );
141        set_add( irc, "query_order", "lifo", NULL );
142        set_add( irc, "save_on_quit", "true", set_eval_bool );
143        set_add( irc, "strip_html", "true", NULL );
144        set_add( irc, "to_char", ": ", set_eval_to_char );
145        set_add( irc, "typing_notice", "false", set_eval_bool );
146        set_add( irc, "password", NULL, passchange);
147       
148        conf_loaddefaults( irc );
149       
150        return( irc );
151}
152
153static gboolean irc_free_userhash( gpointer key, gpointer value, gpointer data )
154{
155        g_free( key );
156       
157        return( TRUE );
158}
159
160/* Because we have no garbage collection, this is quite annoying */
161void irc_free(irc_t * irc)
162{
163        account_t *account, *accounttmp;
164        user_t *user, *usertmp;
165        nick_t *nick, *nicktmp;
166        help_t *helpnode, *helpnodetmp;
167        set_t *setnode, *setnodetmp;
168       
169        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
170       
171        if( irc->status >= USTATUS_IDENTIFIED && set_getint( irc, "save_on_quit" ) ) 
172                if( storage_save( irc, TRUE ) != STORAGE_OK )
173                        irc_usermsg( irc, "Error while saving settings!" );
174       
175        if( irc->ping_source_id > 0 )
176                g_source_remove( irc->ping_source_id );
177        g_source_remove( irc->r_watch_source_id );
178        if( irc->w_watch_source_id > 0 )
179                g_source_remove( irc->w_watch_source_id );
180       
181        g_io_channel_unref( irc->io_channel );
182        irc_connection_list = g_slist_remove( irc_connection_list, irc );
183       
184        for (account = irc->accounts; account; account = account->next)
185                if (account->gc)
186                        signoff(account->gc);
187       
188        g_free(irc->sendbuffer);
189        g_free(irc->readbuffer);
190       
191        g_free(irc->nick);
192        g_free(irc->user);
193        g_free(irc->host);
194        g_free(irc->realname);
195        g_free(irc->password);
196       
197        g_free(irc->myhost);
198        g_free(irc->mynick);
199       
200        g_free(irc->channel);
201       
202        while (irc->queries != NULL)
203                query_del(irc, irc->queries);
204       
205        if (irc->accounts != NULL) {
206                account = irc->accounts;
207                while (account != NULL) {
208                        g_free(account->user);
209                        g_free(account->pass);
210                        g_free(account->server);
211                        accounttmp = account;
212                        account = account->next;
213                        g_free(accounttmp);
214                }
215        }
216       
217        if (irc->users != NULL) {
218                user = irc->users;
219                while (user != NULL) {
220                        g_free(user->nick);
221                        g_free(user->away);
222                        g_free(user->handle);
223                        if(user->user!=user->nick) g_free(user->user);
224                        if(user->host!=user->nick) g_free(user->host);
225                        if(user->realname!=user->nick) g_free(user->realname);
226                        gaim_input_remove(user->sendbuf_timer);
227                                       
228                        usertmp = user;
229                        user = user->next;
230                        g_free(usertmp);
231                }
232        }
233       
234        g_hash_table_foreach_remove(irc->userhash, irc_free_userhash, NULL);
235        g_hash_table_destroy(irc->userhash);
236       
237        g_hash_table_foreach_remove(irc->watches, irc_free_userhash, NULL);
238        g_hash_table_destroy(irc->watches);
239       
240        if (irc->nicks != NULL) {
241                nick = irc->nicks;
242                while (nick != NULL) {
243                        g_free(nick->nick);
244                        g_free(nick->handle);
245                                       
246                        nicktmp = nick;
247                        nick = nick->next;
248                        g_free(nicktmp);
249                }
250        }
251        if (irc->help != NULL) {
252                helpnode = irc->help;
253                while (helpnode != NULL) {
254                        g_free(helpnode->string);
255                       
256                        helpnodetmp = helpnode;
257                        helpnode = helpnode->next;
258                        g_free(helpnodetmp);
259                }
260        }
261        if (irc->set != NULL) {
262                setnode = irc->set;
263                while (setnode != NULL) {
264                        g_free(setnode->key);
265                        g_free(setnode->def);
266                        g_free(setnode->value);
267                       
268                        setnodetmp = setnode;
269                        setnode = setnode->next;
270                        g_free(setnodetmp);
271                }
272        }
273        g_free(irc);
274       
275        if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON )
276                g_main_quit( global.loop );
277}
278
279/* USE WITH CAUTION!
280   Sets pass without checking */
281void irc_setpass (irc_t *irc, const char *pass) 
282{
283        if (irc->password) g_free (irc->password);
284       
285        if (pass) {
286                irc->password = g_strdup (pass);
287                irc_usermsg (irc, "Password successfully changed");
288        } else {
289                irc->password = NULL;
290        }
291}
292
293int irc_process( irc_t *irc )
294{
295        char **lines, *temp;   
296        int i;
297
298        if( irc->readbuffer != NULL ) {
299                lines = irc_tokenize(irc->readbuffer );
300                for( i = 0; *lines[i] != '\0'; i++ ) {
301                        if( lines[i+1] == NULL ) {
302                                temp = g_strdup( lines[i] );
303                                g_free( irc->readbuffer );
304                                irc->readbuffer = temp;
305                                i++;
306                                break;
307                        }                       
308                        if (!irc_process_line(irc, lines[i])) {
309                                g_free( lines );
310                                return 0;
311                        }
312                }
313                if(lines[i]!=NULL) {
314                        g_free(irc->readbuffer);
315                        irc->readbuffer=NULL;   
316                }
317                g_free( lines );
318        }
319        return 1;       
320}
321
322char **irc_tokenize( char *buffer )
323{
324        int i, j;
325        char **lines;
326
327        /* Count the number of elements we're gonna need. */
328        for(i=0, j=1; buffer[i]!='\0'; i++ ) {
329                if(buffer[i]=='\n' )
330                        if(buffer[i+1]!='\r' && buffer[i+1]!='\n')
331                                j++;
332        }
333       
334        /* Allocate j+1 elements. */
335        lines=g_new (char *, j+1);
336       
337        /* NULL terminate our list. */ 
338        lines[j]=NULL;
339       
340        lines[0]=buffer;
341       
342        /* Split the buffer in several strings, using \r\n as our seperator, where \r is optional.
343         * Although this is not in the RFC, some braindead ircds (newnet's) use this, so some clients might too.
344         */
345        for( i=0, j=0; buffer[i]!='\0'; i++) {
346                if(buffer[i]=='\n') {
347                        buffer[i]='\0';
348
349                        /* We dont want to read 1 byte before our buffer
350                         * and (in rare cases) generate a SIGSEGV.
351                         */
352                        if(i!=0)
353                                if(buffer[i-1]=='\r')
354                                        buffer[i-1]='\0';
355                        if(buffer[i+1]!='\r'&&buffer[i+1]!='\n')
356                                lines[++j]=buffer+i+1;
357                }
358        }
359
360        return(lines);
361}
362
363int irc_process_line( irc_t *irc, char *line )
364{
365        int i, j;
366        char **cmd;
367       
368        /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
369        if(line[0]==':') {
370                for(i=0; line[i]!=32; i++);
371                line=line+i;
372        }
373        for(i=0; line[i]==32; i++);
374        line=line+i;
375
376        /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
377        if(line[0]=='\0')
378                return 1;
379        else
380                j=1;   
381       
382        /* Count the number of char **cmd elements we're going to need. */     
383        for(i=0; line[i]!='\0'; i++) {
384                if((line[i]==32) && (line[i+1]!=32) && (line[i+1]!='\0') && (line[i+1]!=':'))           
385                        j++;
386                else if((line[i]==':') && (line[i+1]!='\0') && (line[i-1]==32)) {
387                        j++;
388                        break;
389                }
390                       
391        }       
392
393        /* Allocate the space we need. */
394        cmd=g_new(char *, j+1);
395        cmd[j]=NULL;
396       
397        /* Do the actual line splitting, format is:
398         * Input: "PRIVMSG #bitlbee :foo bar"
399         * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
400         */
401
402        cmd[0]=line;
403        for(i=0, j=0; line[i]!='\0'; i++) {
404                if((line[i]==32)) {
405                        line[i]='\0';
406                        if((line[i+1]!=32) && (line[i+1]!='\0') && (line[i+1]!=':'))           
407                                cmd[++j]=line+i+1;
408                }
409                else if((line[i]==':') && (line[i+1]!='\0') && (line[i-1]=='\0')) {
410                        cmd[++j]=line+i+1;
411                        break;
412                }
413        }
414       
415        i=irc_exec(irc, cmd);
416        g_free(cmd);
417
418        return(i);     
419}
420
421int irc_exec( irc_t *irc, char **cmd )
422{       
423        int i;
424
425        if( (global.conf)->authmode == AUTHMODE_CLOSED && irc->status < USTATUS_AUTHORIZED )
426        {
427                if( g_strcasecmp( cmd[0], "PASS" ) == 0 )
428                {
429                        if( !cmd[1] )
430                        {
431                                irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
432                        }
433                        else if( strcmp( cmd[1], (global.conf)->auth_pass ) == 0 )
434                        {
435                                irc->status = USTATUS_AUTHORIZED;
436                        }
437                        else
438                        {
439                                irc_reply( irc, 464, ":Nope, maybe you should try it again..." );
440                        }
441                }
442                else
443                {
444                        irc_reply( irc, 464, ":Uhh, fine, but I want the password first." );
445                }
446               
447                return( 1 );
448        }
449       
450        if( g_strcasecmp( cmd[0], "USER" ) == 0 )
451        {
452                if( !( cmd[1] && cmd[2] && cmd[3] && cmd[4] ) )
453                {
454                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
455                }
456                else if( irc->user )
457                {
458                        irc_reply( irc, 462, ":You can't change your nick/userinfo" );
459                }
460                else
461                {
462                        irc->user = g_strdup( cmd[1] );
463                        irc->realname = g_strdup( cmd[4] );
464                        if( irc->nick ) irc_login( irc );
465                }
466                return( 1 );
467        }
468        else if( g_strcasecmp( cmd[0], "NICK" ) == 0 )
469        {
470                if( !cmd[1] )
471                {
472                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
473                }
474                else if( irc->nick )
475                {
476                        irc_reply( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" );
477                }
478                /* This is not clean, but for now it'll have to be like this... */
479                else if( ( nick_cmp( cmd[1], irc->mynick ) == 0 ) || ( nick_cmp( cmd[1], NS_NICK ) == 0 ) )
480                {
481                        irc_reply( irc, 433, ":This nick is already in use" );
482                }
483                else if( !nick_ok( cmd[1] ) )
484                {
485                        /* [SH] Invalid characters. */
486                        irc_reply( irc, 432, ":This nick contains invalid characters" );
487                }
488                else
489                {
490                        irc->nick = g_strdup( cmd[1] );
491                        if( irc->user ) irc_login( irc );
492                }
493                return( 1 );
494        }
495        else if( g_strcasecmp( cmd[0], "QUIT" ) == 0 )
496        {
497                irc_write( irc, "ERROR :%s%s", cmd[1]?"Quit: ":"", cmd[1]?cmd[1]:"Client Quit" );
498                g_io_channel_close( irc->io_channel );
499                return( 0 );
500        }
501       
502        if( !irc->user || !irc->nick )
503        {
504                irc_reply( irc, 451, ":Register first" );
505                return( 1 );
506        }
507       
508        if( g_strcasecmp( cmd[0], "PING" ) == 0 )
509        {
510                irc_write( irc, ":%s PONG %s :%s", irc->myhost, irc->myhost, cmd[1]?cmd[1]:irc->myhost );
511        }
512        else if( g_strcasecmp( cmd[0], "OPER" ) == 0 )
513        {
514                if( !cmd[2] )
515                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
516                else if( strcmp( cmd[2], global.conf->oper_pass ) == 0 )
517                        irc_umode_set( irc, "+o", 1 );
518                // else
519                        /* FIXME/TODO: Find out which reply to send now. */
520        }
521        else if( g_strcasecmp( cmd[0], "MODE" ) == 0 )
522        {
523                if( !cmd[1] )
524                {
525                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
526                }
527                else if( *cmd[1] == '#' || *cmd[1] == '&' )
528                {
529                        if( cmd[2] )
530                        {
531                                if( *cmd[2] == '+' || *cmd[2] == '-' )
532                                        irc_reply( irc, 477, "%s :Can't change channel modes", cmd[1] );
533                                else if( *cmd[2] == 'b' )
534                                        irc_reply( irc, 368, "%s :No bans possible", cmd[1] );
535                        }
536                        else
537                                irc_reply( irc, 324, "%s +%s", cmd[1], CMODE );
538                }
539                else
540                {
541                        if( nick_cmp( cmd[1], irc->nick ) == 0 )
542                        {
543                                if( cmd[2] )
544                                        irc_umode_set( irc, cmd[2], 0 );
545                        }
546                        else
547                                irc_reply( irc, 502, ":Don't touch their modes" );
548                }
549        }
550        else if( g_strcasecmp( cmd[0], "NAMES" ) == 0 )
551        {
552                irc_names( irc, cmd[1]?cmd[1]:irc->channel );
553        }
554        else if( g_strcasecmp( cmd[0], "PART" ) == 0 )
555        {
556                struct conversation *c;
557               
558                if( !cmd[1] )
559                {
560                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
561                }
562                else if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
563                {
564                        user_t *u = user_find( irc, irc->nick );
565                       
566                        /* Not allowed to leave control channel */
567                        irc_part( irc, u, irc->channel );
568                        irc_join( irc, u, irc->channel );
569                }
570                else if( ( c = conv_findchannel( cmd[1] ) ) )
571                {
572                        user_t *u = user_find( irc, irc->nick );
573                       
574                        irc_part( irc, u, c->channel );
575                       
576                        if( c->gc && c->gc->prpl )
577                        {
578                                c->joined = 0;
579                                c->gc->prpl->chat_leave( c->gc, c->id );
580                        }
581                }
582                else
583                {
584                        irc_reply( irc, 403, "%s :No such channel", cmd[1] );
585                }
586        }
587        else if( g_strcasecmp( cmd[0], "JOIN" ) == 0 )
588        {
589                if( !cmd[1] )
590                {
591                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
592                }
593                else if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
594                        ; /* Dude, you're already there...
595                             RFC doesn't have any reply for that though? */
596                else if( cmd[1] )
597                {
598                        if( ( cmd[1][0] == '#' || cmd[1][0] == '&' ) && cmd[1][1] )
599                        {
600                                user_t *u = user_find( irc, cmd[1] + 1 );
601                               
602                                if( u && u->gc && u->gc->prpl && u->gc->prpl->chat_open )
603                                {
604                                        irc_reply( irc, 403, "%s :Initializing groupchat in a different channel", cmd[1] );
605                                       
606                                        if( !u->gc->prpl->chat_open( u->gc, u->handle ) )
607                                        {
608                                                irc_usermsg( irc, "Could not open a groupchat with %s, maybe you don't have a connection to him/her yet?", u->nick );
609                                        }
610                                }
611                                else
612                                {
613                                        irc_reply( irc, 403, "%s :Groupchats are not possible with %s", cmd[1], cmd[1]+1 );
614                                }
615                        }
616                        else
617                        {
618                                irc_reply( irc, 403, "%s :No such channel", cmd[1] );
619                        }
620                }
621        }
622        else if( g_strcasecmp( cmd[0], "INVITE" ) == 0 )
623        {
624                if( cmd[1] && cmd[2] )
625                        irc_invite( irc, cmd[1], cmd[2] );
626                else
627                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
628        }
629        else if( g_strcasecmp( cmd[0], "PRIVMSG" ) == 0 || g_strcasecmp( cmd[0], "NOTICE" ) == 0 )
630        {
631                if( !cmd[1] )
632                {
633                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
634                } 
635                else if ( !cmd[2] ) 
636                {
637                        irc_reply( irc, 412, ":No text to send" );
638                }
639                else if ( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) 
640                {
641                        irc_write( irc, ":%s!%s@%s %s %s :%s", irc->nick, irc->user, irc->host, cmd[0], cmd[1], cmd[2] ); 
642                }
643                else 
644                {
645                        if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
646                        {
647                                unsigned int i;
648                                char *t = set_getstr( irc, "default_target" );
649                               
650                                if( g_strcasecmp( t, "last" ) == 0 && irc->last_target )
651                                        cmd[1] = irc->last_target;
652                                else if( g_strcasecmp( t, "root" ) == 0 )
653                                        cmd[1] = irc->mynick;
654                               
655                                for( i = 0; i < strlen( cmd[2] ); i ++ )
656                                {
657                                        if( cmd[2][i] == ' ' ) break;
658                                        if( cmd[2][i] == ':' || cmd[2][i] == ',' )
659                                        {
660                                                cmd[1] = cmd[2];
661                                                cmd[2] += i;
662                                                *cmd[2] = 0;
663                                                while( *(++cmd[2]) == ' ' );
664                                                break;
665                                        }
666                                }
667                               
668                                irc->is_private = 0;
669                               
670                                if( cmd[1] != irc->last_target )
671                                {
672                                        if( irc->last_target )
673                                                g_free( irc->last_target );
674                                        irc->last_target = g_strdup( cmd[1] );
675                                }
676                        }
677                        else
678                        {
679                                irc->is_private = 1;
680                        }
681                        irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? IM_FLAG_AWAY : 0 );
682                }
683        }
684        else if( g_strcasecmp( cmd[0], "WHO" ) == 0 )
685        {
686                irc_who( irc, cmd[1] );
687        }
688        else if( g_strcasecmp( cmd[0], "USERHOST" ) == 0 )
689        {
690                user_t *u;
691               
692                if( !cmd[1] )
693                {
694                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
695                }
696                /* [TV] Usable USERHOST-implementation according to
697                        RFC1459. Without this, mIRC shows an error
698                        while connecting, and the used way of rejecting
699                        breaks standards.
700                */
701               
702                for( i = 1; cmd[i]; i ++ )
703                        if( ( u = user_find( irc, cmd[i] ) ) )
704                        {
705                                if( u->online && u->away )
706                                        irc_reply( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host );
707                                else
708                                        irc_reply( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host );
709                        }
710        }
711        else if( g_strcasecmp( cmd[0], "ISON" ) == 0 )
712        {
713                user_t *u;
714                char buff[IRC_MAX_LINE];
715                int lenleft;
716               
717                buff[0] = '\0';
718               
719                /* [SH] Leave room for : and \0 */
720                lenleft = IRC_MAX_LINE - 2;
721               
722                for( i = 1; cmd[i]; i ++ )
723                {
724                        if( ( u = user_find( irc, cmd[i] ) ) && u->online )
725                        {
726                                /* [SH] Make sure we don't use too much buffer space. */
727                                lenleft -= strlen( u->nick ) + 1;
728                               
729                                if( lenleft < 0 )
730                                {
731                                        break;
732                                }
733                               
734                                /* [SH] Add the nick to the buffer. Note
735                                 * that an extra space is always added. Even
736                                 * if it's the last nick in the list. Who
737                                 * cares?
738                                 */
739                               
740                                strcat( buff, u->nick );
741                                strcat( buff, " " );
742                        }
743                }
744               
745                /* [WvG] Well, maybe someone cares, so why not remove it? */
746                if( strlen( buff ) > 0 )
747                        buff[strlen(buff)-1] = '\0';
748               
749                /* [SH] By the way, that really *was* WvG talking. */
750                /* [WvG] Really? */
751                /* [SH] Yeah... But *this* is WvG talking too. ;-P */
752                /* [WvG] *sigh* */
753               
754                irc_reply( irc, 303, ":%s", buff );
755        }
756        else if( g_strcasecmp( cmd[0], "WATCH" ) == 0 )
757        {
758                /* Obviously we could also mark a user structure as being
759                   watched, but what if the WATCH command is sent right
760                   after connecting? The user won't exist yet then... */
761                for( i = 1; cmd[i]; i ++ )
762                {
763                        char *nick;
764                        user_t *u;
765                       
766                        if( !cmd[i][0] || !cmd[i][1] )
767                                break;
768                       
769                        nick = g_strdup( cmd[i] + 1 );
770                        nick_lc( nick );
771                       
772                        u = user_find( irc, nick );
773                       
774                        if( cmd[i][0] == '+' )
775                        {
776                                if( !g_hash_table_lookup( irc->watches, nick ) )
777                                        g_hash_table_insert( irc->watches, nick, nick );
778                               
779                                if( u && u->online )
780                                        irc_reply( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "is online" );
781                                else
782                                        irc_reply( irc, 605, "%s %s %s %d :%s", nick, "*", "*", time( NULL ), "is offline" );
783                        }
784                        else if( cmd[i][0] == '-' )
785                        {
786                                gpointer okey, ovalue;
787                               
788                                if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
789                                {
790                                        g_free( okey );
791                                        g_hash_table_remove( irc->watches, okey );
792                                       
793                                        irc_reply( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
794                                }
795                        }
796                }
797        }
798        else if( g_strcasecmp( cmd[0], "TOPIC" ) == 0 )
799        {
800                if( cmd[1] && cmd[2] )
801                        irc_reply( irc, 482, "%s :Cannot change topic", cmd[1] );
802                else if( cmd[1] )
803                        irc_topic( irc, cmd[1] );
804                else
805                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
806        }
807        else if( g_strcasecmp( cmd[0], "AWAY" ) == 0 )
808        {
809                irc_away( irc, cmd[1] );
810        }
811        else if( g_strcasecmp( cmd[0], "WHOIS" ) == 0 )
812        {
813                if( cmd[1] )
814                {
815                        irc_whois( irc, cmd[1] );
816                }
817                else
818                {
819                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
820                }
821        }
822        else if( g_strcasecmp( cmd[0], "WHOWAS" ) == 0 )
823        {
824                /* For some reason irssi tries a whowas when whois fails. We can
825                   ignore this, but then the user never gets a "user not found"
826                   message from irssi which is a bit annoying. So just respond
827                   with not-found and irssi users will get better error messages */
828               
829                if( cmd[1] )
830                {
831                        irc_reply( irc, 406, "%s :Nick does not exist", cmd[1] );
832                        irc_reply( irc, 369, "%s :End of WHOWAS", cmd[1] );
833                }
834                else
835                {
836                        irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
837                }
838        }
839        else if( ( g_strcasecmp( cmd[0], "NICKSERV" ) == 0 ) || ( g_strcasecmp( cmd[0], "NS" ) == 0 ) )
840        {
841                /* [SH] This aliases the NickServ command to PRIVMSG root */
842                /* [TV] This aliases the NS command to PRIVMSG root as well */
843                root_command( irc, cmd + 1 );
844        }
845        else if( g_strcasecmp( cmd[0], "MOTD" ) == 0 )
846        {
847                irc_motd( irc );
848        }
849        else if( g_strcasecmp( cmd[0], "PONG" ) == 0 )
850        {
851                /* We could check the value we get back from the user, but in
852                   fact we don't care, we're just happy he's still alive. */
853                irc->last_pong = gettime();
854                irc->pinging = 0;
855        }
856        else if( g_strcasecmp( cmd[0], "COMPLETIONS" ) == 0 )
857        {
858                user_t *u = user_find( irc, irc->mynick );
859                help_t *h;
860                set_t *s;
861                int i;
862               
863                irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "OK" );
864               
865                for( i = 0; commands[i].command; i ++ )
866                        irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", commands[i].command );
867               
868                for( h = global.help; h; h = h->next )
869                        irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->string );
870               
871                for( s = irc->set; s; s = s->next )
872                        irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS set ", s->key );
873               
874                irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "END" );
875        }
876        else if( set_getint( irc, "debug" ) )
877        {
878                irc_usermsg( irc, "\002--- Unknown command:" );
879                for( i = 0; cmd[i]; i ++ ) irc_usermsg( irc, "%s", cmd[i] );
880                irc_usermsg( irc, "\002--------------------" );
881        }
882       
883        return( 1 );
884}
885
886void irc_reply( irc_t *irc, int code, char *format, ... )
887{
888        char text[IRC_MAX_LINE];
889        va_list params;
890       
891        va_start( params, format );
892        g_vsnprintf( text, IRC_MAX_LINE, format, params );
893        va_end( params );
894        irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
895       
896        return;
897}
898
899int irc_usermsg( irc_t *irc, char *format, ... )
900{
901        char text[1024];
902        va_list params;
903        char is_private = 0;
904        user_t *u;
905       
906        u = user_find( irc, irc->mynick );
907        if( u ) is_private = u->is_private;
908       
909        va_start( params, format );
910        g_vsnprintf( text, sizeof( text ), format, params );
911        va_end( params );
912       
913        return( irc_msgfrom( irc, u->nick, text ) );
914}
915
916void irc_write( irc_t *irc, char *format, ... ) 
917{
918        va_list params;
919
920        va_start( params, format );
921        irc_vawrite( irc, format, params );     
922        va_end( params );
923
924        return;
925
926}
927
928void irc_vawrite( irc_t *irc, char *format, va_list params )
929{
930        int size;
931        char line[IRC_MAX_LINE];
932       
933        if( irc->quit )
934                return;
935
936        g_vsnprintf( line, IRC_MAX_LINE - 3, format, params );
937
938        strip_newlines( line );
939        strcat( line, "\r\n" );
940
941        if( irc->sendbuffer != NULL ) {
942                size = strlen( irc->sendbuffer ) + strlen( line );
943#ifdef FLOOD_SEND
944                if( size > FLOOD_SEND_MAXBUFFER ) {
945                        /* Die flooder, die! >:) */
946
947                        g_free(irc->sendbuffer);
948                       
949                        /* We need the \r\n at the start because else we might append our string to a half
950                         * sent line. A bit hackish, but it works.
951                         */
952                        irc->sendbuffer = g_strdup( "\r\nERROR :Sendq Exceeded\r\n" );
953                        irc->quit = 1;
954                       
955                        return;
956                }
957#endif
958                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
959                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
960        }
961        else 
962                irc->sendbuffer = g_strdup(line);       
963       
964        if( irc->w_watch_source_id == 0 )
965        {
966                irc->w_watch_source_id = g_io_add_watch( irc->io_channel, G_IO_OUT, bitlbee_io_current_client_write, irc );
967        }
968       
969        return;
970}
971
972void irc_write_all( int now, char *format, ... )
973{
974        va_list params;
975        GSList *temp;   
976       
977        va_start( params, format );
978       
979        temp = irc_connection_list;
980        while( temp != NULL )
981        {
982                irc_t *irc = temp->data;
983               
984                if( now )
985                {
986                        g_free( irc->sendbuffer );
987                        irc->sendbuffer = g_strdup( "\r\n" );
988                }
989                irc_vawrite( temp->data, format, params );
990                if( now )
991                {
992                        bitlbee_io_current_client_write( irc->io_channel, G_IO_OUT, irc );
993                }
994                temp = temp->next;
995        }
996       
997        va_end( params );
998        return;
999} 
1000
1001void irc_names( irc_t *irc, char *channel )
1002{
1003        user_t *u = irc->users;
1004        char *s;
1005        int control = ( g_strcasecmp( channel, irc->channel ) == 0 );
1006        struct conversation *c = NULL;
1007       
1008        if( !control )
1009                c = conv_findchannel( channel );
1010       
1011        /* RFC's say there is no error reply allowed on NAMES, so when the
1012           channel is invalid, just give an empty reply. */
1013       
1014        if( control || c ) while( u )
1015        {
1016                if( u->online )
1017                {
1018                        if( u->gc && control )
1019                        {
1020                                if( set_getint( irc, "away_devoice" ) && !u->away )
1021                                        s = "+";
1022                                else
1023                                        s = "";
1024                               
1025                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
1026                        }
1027                        else if( !u->gc )
1028                        {
1029                                if( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "root" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
1030                                        s = "@";
1031                                else if( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "user" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
1032                                        s = "@";
1033                                else
1034                                        s = "";
1035                               
1036                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
1037                        }
1038                }
1039               
1040                u = u->next;
1041        }
1042       
1043        /* For non-controlchannel channels (group conversations) only root and
1044           you are listed now. Time to show the channel people: */
1045        if( !control && c )
1046        {
1047                GList *l;
1048               
1049                for( l = c->in_room; l; l = l->next )
1050                        if( ( u = user_findhandle( c->gc, l->data ) ) )
1051                                irc_reply( irc, 353, "@ %s :%s%s", channel, "", u->nick );
1052        }
1053       
1054        irc_reply( irc, 366, "%s :End of /NAMES list", channel );
1055}
1056
1057void irc_who( irc_t *irc, char *channel )
1058{
1059        user_t *u = irc->users;
1060        struct conversation *c;
1061        GList *l;
1062       
1063        if( !channel || *channel == '0' || *channel == '*' || !*channel )
1064                while( u )
1065                {
1066                        irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", u->online ? irc->channel : "*", u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname );
1067                        u = u->next;
1068                }
1069        else if( g_strcasecmp( channel, irc->channel ) == 0 )
1070                while( u )
1071                {
1072                        if( u->online )
1073                                irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname );
1074                        u = u->next;
1075                }
1076        else if( ( c = conv_findchannel( channel ) ) )
1077                for( l = c->in_room; l; l = l->next )
1078                {
1079                        if( ( u = user_findhandle( c->gc, l->data ) ) )
1080                                irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname );
1081                }
1082        else if( ( u = user_find( irc, channel ) ) )
1083                irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname );
1084       
1085        irc_reply( irc, 315, "%s :End of /WHO list.", channel?channel:"**" );
1086}
1087
1088void irc_login( irc_t *irc )
1089{
1090        user_t *u;
1091       
1092        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
1093        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
1094        irc_reply( irc,   3, ":%s", IRCD_INFO );
1095        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
1096        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 );
1097        irc_motd( irc );
1098        irc_umode_set( irc, "+" UMODE, 1 );
1099
1100        u = user_add( irc, irc->mynick );
1101        u->host = g_strdup( irc->myhost );
1102        u->realname = g_strdup( ROOT_FN );
1103        u->online = 1;
1104        u->send_handler = root_command_string;
1105        u->is_private = 0; /* [SH] The channel is root's personal playground. */
1106        irc_spawn( irc, u );
1107       
1108        u = user_add( irc, NS_NICK );
1109        u->host = g_strdup( irc->myhost );
1110        u->realname = g_strdup( ROOT_FN );
1111        u->online = 0;
1112        u->send_handler = root_command_string;
1113        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
1114       
1115        u = user_add( irc, irc->nick );
1116        u->user = g_strdup( irc->user );
1117        u->host = g_strdup( irc->host );
1118        u->realname = g_strdup( irc->realname );
1119        u->online = 1;
1120//      u->send_handler = msg_echo;
1121        irc_spawn( irc, u );
1122       
1123        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." );
1124       
1125        irc->status = USTATUS_LOGGED_IN;
1126}
1127
1128void irc_motd( irc_t *irc )
1129{
1130        int fd;
1131       
1132        fd = open( global.conf->motdfile, O_RDONLY );
1133        if( fd == -1 )
1134        {
1135                irc_reply( irc, 422, ":We don't need MOTDs." );
1136        }
1137        else
1138        {
1139                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
1140                char *add, max;
1141                int len;
1142               
1143                linebuf[79] = len = 0;
1144                max = sizeof( linebuf ) - 1;
1145               
1146                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
1147                while( read( fd, linebuf + len, 1 ) == 1 )
1148                {
1149                        if( linebuf[len] == '\n' || len == max )
1150                        {
1151                                linebuf[len] = 0;
1152                                irc_reply( irc, 372, ":- %s", linebuf );
1153                                len = 0;
1154                        }
1155                        else if( linebuf[len] == '%' )
1156                        {
1157                                read( fd, linebuf + len, 1 );
1158                                if( linebuf[len] == 'h' )
1159                                        add = irc->myhost;
1160                                else if( linebuf[len] == 'v' )
1161                                        add = BITLBEE_VERSION;
1162                                else if( linebuf[len] == 'n' )
1163                                        add = irc->nick;
1164                                else
1165                                        add = "%";
1166                               
1167                                strncpy( linebuf + len, add, max - len );
1168                                while( linebuf[++len] );
1169                        }
1170                        else if( len < max )
1171                        {
1172                                len ++;
1173                        }
1174                }
1175                irc_reply( irc, 376, ":End of MOTD" );
1176                closesocket( fd );
1177        }
1178}
1179
1180void irc_topic( irc_t *irc, char *channel )
1181{
1182        if( g_strcasecmp( channel, irc->channel ) == 0 )
1183        {
1184                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
1185        }
1186        else
1187        {
1188                struct conversation *c = conv_findchannel( channel );
1189               
1190                if( c )
1191                        irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
1192                else
1193                        irc_reply( irc, 331, "%s :No topic for this channel" );
1194        }
1195}
1196
1197void irc_whois( irc_t *irc, char *nick )
1198{
1199        user_t *u = user_find( irc, nick );
1200       
1201        if( u )
1202        {
1203                irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname );
1204               
1205                if( u->gc )
1206                        irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->gc->user->username,
1207                                   *u->gc->user->proto_opt[0] ? u->gc->user->proto_opt[0] : "", u->gc->prpl->name );
1208                else
1209                        irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO );
1210               
1211                if( !u->online )
1212                        irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1213                else if( u->away )
1214                        irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1215               
1216                irc_reply( irc, 318, "%s :End of /WHOIS list", nick );
1217        }
1218        else
1219        {
1220                irc_reply( irc, 401, "%s :Nick does not exist", nick );
1221        }
1222}
1223
1224
1225void irc_umode_set( irc_t *irc, char *s, int allow_priv )
1226{
1227        /* allow_priv: Set to 0 if s contains user input, 1 if you want
1228           to set a "privileged" mode (+o, +R, etc). */
1229        char m[256], st = 1, *t;
1230        int i;
1231       
1232        memset( m, 0, sizeof( m ) );
1233       
1234        for( t = irc->umode; *t; t ++ )
1235                m[(int)*t] = 1;
1236       
1237        for( t = s; *t; t ++ )
1238        {
1239                if( *t == '+' || *t == '-' )
1240                        st = *t == '+';
1241                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
1242                        m[(int)*t] = st;
1243        }
1244       
1245        memset( irc->umode, 0, sizeof( irc->umode ) );
1246       
1247        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
1248                if( m[i] )
1249                        irc->umode[strlen(irc->umode)] = i;
1250       
1251        irc_reply( irc, 221, "+%s", irc->umode );
1252}
1253
1254int irc_away( irc_t *irc, char *away )
1255{
1256        user_t *u = user_find( irc, irc->nick );
1257        GSList *c = get_connections();
1258       
1259        if( !u ) return( 0 );
1260       
1261        if( away && *away )
1262        {
1263                int i, j;
1264               
1265                /* Copy away string, but skip control chars. Mainly because
1266                   Jabber really doesn't like them. */
1267                u->away = g_malloc( strlen( away ) + 1 );
1268                for( i = j = 0; away[i]; i ++ )
1269                        if( ( u->away[j] = away[i] ) >= ' ' )
1270                                j ++;
1271                u->away[j] = 0;
1272               
1273                irc_reply( irc, 306, ":You're now away: %s", u->away );
1274                /* irc_umode_set( irc, irc->myhost, "+a" ); */
1275        }
1276        else
1277        {
1278                if( u->away ) g_free( u->away );
1279                u->away = NULL;
1280                /* irc_umode_set( irc, irc->myhost, "-a" ); */
1281                irc_reply( irc, 305, ":Welcome back" );
1282        }
1283       
1284        while( c )
1285        {
1286                if( ((struct gaim_connection *)c->data)->flags & OPT_LOGGED_IN )
1287                        proto_away( c->data, u->away );
1288               
1289                c = c->next;
1290        }
1291       
1292        return( 1 );
1293}
1294
1295void irc_spawn( irc_t *irc, user_t *u )
1296{
1297        irc_join( irc, u, irc->channel );
1298}
1299
1300void irc_join( irc_t *irc, user_t *u, char *channel )
1301{
1302        char *nick;
1303       
1304        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
1305                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
1306       
1307        if( nick_cmp( u->nick, irc->nick ) == 0 )
1308        {
1309                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
1310                irc_names( irc, channel );
1311                irc_topic( irc, channel );
1312        }
1313       
1314        nick = g_strdup( u->nick );
1315        nick_lc( nick );
1316        if( g_hash_table_lookup( irc->watches, nick ) )
1317        {
1318                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged online" );
1319        }
1320        g_free( nick );
1321}
1322
1323void irc_part( irc_t *irc, user_t *u, char *channel )
1324{
1325        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
1326}
1327
1328void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
1329{
1330        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
1331}
1332
1333void irc_kill( irc_t *irc, user_t *u )
1334{
1335        char *nick;
1336       
1337        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
1338       
1339        nick = g_strdup( u->nick );
1340        nick_lc( nick );
1341        if( g_hash_table_lookup( irc->watches, nick ) )
1342        {
1343                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged offline" );
1344        }
1345        g_free( nick );
1346}
1347
1348void irc_invite( irc_t *irc, char *nick, char *channel )
1349{
1350        struct conversation *c = conv_findchannel( channel );
1351        user_t *u = user_find( irc, nick );
1352       
1353        if( u && c && ( u->gc == c->gc ) )
1354                if( c->gc && c->gc->prpl && c->gc->prpl->chat_invite )
1355                {
1356                        c->gc->prpl->chat_invite( c->gc, c->id, "", u->handle );
1357                        irc_reply( irc, 341, "%s %s", nick, channel );
1358                        return;
1359                }
1360       
1361        irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel );
1362}
1363
1364int irc_send( irc_t *irc, char *nick, char *s, int flags )
1365{
1366        struct conversation *c = NULL;
1367        user_t *u = NULL;
1368       
1369        if( *nick == '#' || *nick == '&' )
1370        {
1371                if( !( c = conv_findchannel( nick ) ) )
1372                {
1373                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
1374                        return( 0 );
1375                }
1376        }
1377        else
1378        {
1379                u = user_find( irc, nick );
1380               
1381                if( !u )
1382                {
1383                        if( irc->is_private )
1384                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
1385                        else
1386                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
1387                        return( 0 );
1388                }
1389        }
1390       
1391        if( *s == 1 && s[strlen(s)-1] == 1 )
1392        {
1393                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
1394                {
1395                        if( s[7] == ' ' ) s ++;
1396                        s += 3;
1397                        *(s++) = '/';
1398                        *(s++) = 'm';
1399                        *(s++) = 'e';
1400                        *(s++) = ' ';
1401                        s -= 4;
1402                        s[strlen(s)-1] = 0;
1403                }
1404                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
1405                {
1406                        u = user_find( irc, irc->mynick );
1407                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
1408                        return( 1 );
1409                }
1410                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
1411                {
1412                        u = user_find( irc, irc->mynick );
1413                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
1414                        return( 1 );
1415                }
1416                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
1417                {
1418                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
1419                        {
1420                                time_t current_typing_notice = time( NULL );
1421                               
1422                                if( current_typing_notice - u->last_typing_notice >= 5 )
1423                                {
1424                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
1425                                        u->last_typing_notice = current_typing_notice;
1426                                }
1427                        }
1428                        return( 1 );
1429                }
1430                else
1431                {
1432                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
1433                        return( 0 );
1434                }
1435        }
1436       
1437        if( u )
1438        {
1439                /* For the next message, we probably do have to send new notices... */
1440                u->last_typing_notice = 0;
1441                u->is_private = irc->is_private;
1442               
1443                if( u->is_private )
1444                {
1445                        if( !u->online )
1446                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1447                        else if( u->away )
1448                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1449                }
1450               
1451                if( u->send_handler )
1452                        return( u->send_handler( irc, u, s, flags ) );
1453        }
1454        else if( c && c->gc && c->gc->prpl )
1455        {
1456                return( serv_send_chat( irc, c->gc, c->id, s ) );
1457        }
1458       
1459        return( 0 );
1460}
1461
1462gboolean buddy_send_handler_delayed( gpointer data )
1463{
1464        user_t *u = data;
1465       
1466        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
1467        serv_send_im( u->gc->irc, u, u->sendbuf, u->sendbuf_flags );
1468       
1469        g_free( u->sendbuf );
1470        u->sendbuf = NULL;
1471        u->sendbuf_len = 0;
1472        u->sendbuf_timer = 0;
1473        u->sendbuf_flags = 0;
1474       
1475        return( FALSE );
1476}
1477
1478int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
1479{
1480        if( !u || !u->gc ) return( 0 );
1481       
1482        if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 )
1483        {
1484                int delay;
1485               
1486                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1487                {
1488                        //Flush the buffer
1489                        g_source_remove( u->sendbuf_timer );
1490                        buddy_send_handler_delayed( u );
1491                }
1492
1493                if( u->sendbuf_len == 0 )
1494                {
1495                        u->sendbuf_len = strlen( msg ) + 2;
1496                        u->sendbuf = g_new (char, u->sendbuf_len );
1497                        u->sendbuf[0] = 0;
1498                        u->sendbuf_flags = flags;
1499                }
1500                else
1501                {
1502                        u->sendbuf_len += strlen( msg ) + 1;
1503                        u->sendbuf = g_renew ( char, u->sendbuf, u->sendbuf_len );
1504                }
1505               
1506                strcat( u->sendbuf, msg );
1507                strcat( u->sendbuf, "\n" );
1508               
1509                delay = set_getint( irc, "buddy_sendbuffer_delay" );
1510                if( delay <= 5 )
1511                        delay *= 1000;
1512               
1513                if( u->sendbuf_timer > 0 )
1514                        g_source_remove( u->sendbuf_timer );
1515                u->sendbuf_timer = g_timeout_add( delay, buddy_send_handler_delayed, u );
1516               
1517                return( 1 );
1518        }
1519        else
1520        {
1521                return( serv_send_im( irc, u, msg, flags ) );
1522        }
1523}
1524
1525int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1526{
1527        char last = 0;
1528        char *s = msg, *line = msg;
1529       
1530        /* The almighty linesplitter .. woohoo!! */
1531        while( !last )
1532        {
1533                if( *s == '\r' && *(s+1) == '\n' )
1534                        *(s++) = 0;
1535                if( *s == '\n' )
1536                {
1537                        last = s[1] == 0;
1538                        *s = 0;
1539                }
1540                else
1541                {
1542                        last = s[0] == 0;
1543                }
1544                if( *s == 0 )
1545                {
1546                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1547                        {
1548                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1549                                           type, to, line + 4 );
1550                        }
1551                        else
1552                        {
1553                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1554                                           type, to, prefix ? prefix : "", line );
1555                        }
1556                        line = s + 1;
1557                }
1558                s ++;
1559        }
1560       
1561        return( 1 );
1562}
1563
1564int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1565{
1566        user_t *u = user_find( irc, nick );
1567        static char *prefix = NULL;
1568       
1569        if( !u ) return( 0 );
1570        if( prefix && *prefix ) g_free( prefix );
1571       
1572        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1573        {
1574                int len = strlen( irc->nick) + 3;
1575                prefix = g_new (char, len );
1576                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) );
1577                prefix[len-1] = 0;
1578        }
1579        else
1580        {
1581                prefix = "";
1582        }
1583       
1584        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1585}
1586
1587int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1588{
1589        user_t *u = user_find( irc, nick );
1590       
1591        if( u )
1592                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1593        else
1594                return( 0 );
1595}
1596
1597/* Returns 0 if everything seems to be okay, a number >0 when there was a
1598   timeout. The number returned is the number of seconds we received no
1599   pongs from the user. When not connected yet, we don't ping but drop the
1600   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1601static gboolean irc_userping( gpointer _irc )
1602{
1603        irc_t *irc = _irc;
1604        int rv = 0;
1605       
1606        if( irc->status < USTATUS_LOGGED_IN )
1607        {
1608                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1609                        rv = gettime() - irc->last_pong;
1610        }
1611        else
1612        {
1613                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1614                {
1615                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1616                        irc->pinging = 1;
1617                }
1618                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1619                {
1620                        rv = gettime() - irc->last_pong;
1621                }
1622        }
1623       
1624        if( rv > 0 )
1625        {
1626                irc_write( irc, "ERROR :Closing Link: Ping Timeout: %d seconds", rv );
1627                irc_free( irc );
1628                return FALSE;
1629        }
1630       
1631        return TRUE;
1632}
Note: See TracBrowser for help on using the repository browser.