source: irc.c @ 12c6634

Last change on this file since 12c6634 was 12c6634, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-12-17T10:06:52Z

BitlBee now sends a (hopefully correct) 005 reply on connect.

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