source: irc.c @ b73ac9c

Last change on this file since b73ac9c was b73ac9c, checked in by Jelmer Vernooij <jelmer@…>, at 2005-12-13T23:05:27Z

Add support for 'primary' and 'migrate' account storages.
Fix two bugs in the text storage backend that were introduced by my previous
changes.

  • Property mode set to 100644
File size: 40.0 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_motd( irc );
1079        irc_umode_set( irc, irc->myhost, "+" UMODE );
1080
1081        u = user_add( irc, irc->mynick );
1082        u->host = g_strdup( irc->myhost );
1083        u->realname = g_strdup( ROOT_FN );
1084        u->online = 1;
1085        u->send_handler = root_command_string;
1086        u->is_private = 0; /* [SH] The channel is root's personal playground. */
1087        irc_spawn( irc, u );
1088       
1089        u = user_add( irc, NS_NICK );
1090        u->host = g_strdup( irc->myhost );
1091        u->realname = g_strdup( ROOT_FN );
1092        u->online = 0;
1093        u->send_handler = root_command_string;
1094        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
1095       
1096        u = user_add( irc, irc->nick );
1097        u->user = g_strdup( irc->user );
1098        u->host = g_strdup( irc->host );
1099        u->realname = g_strdup( irc->realname );
1100        u->online = 1;
1101//      u->send_handler = msg_echo;
1102        irc_spawn( irc, u );
1103       
1104        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." );
1105       
1106        irc->status = USTATUS_LOGGED_IN;
1107}
1108
1109void irc_motd( irc_t *irc )
1110{
1111        int fd;
1112       
1113        fd = open( global.conf->motdfile, O_RDONLY );
1114        if( fd == -1 )
1115        {
1116                irc_reply( irc, 422, ":We don't need MOTDs." );
1117        }
1118        else
1119        {
1120                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
1121                char *add, max;
1122                int len;
1123               
1124                linebuf[79] = len = 0;
1125                max = sizeof( linebuf ) - 1;
1126               
1127                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
1128                while( read( fd, linebuf + len, 1 ) == 1 )
1129                {
1130                        if( linebuf[len] == '\n' || len == max )
1131                        {
1132                                linebuf[len] = 0;
1133                                irc_reply( irc, 372, ":- %s", linebuf );
1134                                len = 0;
1135                        }
1136                        else if( linebuf[len] == '%' )
1137                        {
1138                                read( fd, linebuf + len, 1 );
1139                                if( linebuf[len] == 'h' )
1140                                        add = irc->myhost;
1141                                else if( linebuf[len] == 'v' )
1142                                        add = BITLBEE_VERSION;
1143                                else if( linebuf[len] == 'n' )
1144                                        add = irc->nick;
1145                                else
1146                                        add = "%";
1147                               
1148                                strncpy( linebuf + len, add, max - len );
1149                                while( linebuf[++len] );
1150                        }
1151                        else if( len < max )
1152                        {
1153                                len ++;
1154                        }
1155                }
1156                irc_reply( irc, 376, ":End of MOTD" );
1157                closesocket( fd );
1158        }
1159}
1160
1161void irc_topic( irc_t *irc, char *channel )
1162{
1163        if( g_strcasecmp( channel, irc->channel ) == 0 )
1164        {
1165                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
1166        }
1167        else
1168        {
1169                struct conversation *c = conv_findchannel( channel );
1170               
1171                if( c )
1172                        irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
1173                else
1174                        irc_reply( irc, 331, "%s :No topic for this channel" );
1175        }
1176}
1177
1178void irc_whois( irc_t *irc, char *nick )
1179{
1180        user_t *u = user_find( irc, nick );
1181       
1182        if( u )
1183        {
1184                irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname );
1185               
1186                if( u->gc )
1187                        irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->gc->user->username,
1188                                   *u->gc->user->proto_opt[0] ? u->gc->user->proto_opt[0] : "", proto_name[u->gc->user->protocol] );
1189                else
1190                        irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO );
1191               
1192                if( !u->online )
1193                        irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1194                else if( u->away )
1195                        irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1196               
1197                irc_reply( irc, 318, "%s :End of /WHOIS list", nick );
1198        }
1199        else
1200        {
1201                irc_reply( irc, 401, "%s :Nick does not exist", nick );
1202        }
1203}
1204
1205
1206void irc_umode_set( irc_t *irc, char *who, char *s )
1207{
1208        char m[256], st = 1, *t;
1209        int i;
1210       
1211        memset( m, 0, sizeof( m ) );
1212       
1213        for( t = irc->umode; *t; t ++ )
1214                m[(int)*t] = 1;
1215       
1216        for( t = s; *t; t ++ )
1217        {
1218                if( *t == '+' || *t == '-' )
1219                        st = *t == '+';
1220                else
1221                        m[(int)*t] = st;
1222        }
1223       
1224        memset( irc->umode, 0, sizeof( irc->umode ) );
1225       
1226        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
1227                if( m[i] && strchr( UMODES, i ) )
1228                        irc->umode[strlen(irc->umode)] = i;
1229       
1230        irc_reply( irc, 221, "+%s", irc->umode );
1231}
1232
1233int irc_away( irc_t *irc, char *away )
1234{
1235        user_t *u = user_find( irc, irc->nick );
1236        GSList *c = get_connections();
1237       
1238        if( !u ) return( 0 );
1239       
1240        if( away && *away )
1241        {
1242                int i, j;
1243               
1244                /* Copy away string, but skip control chars. Mainly because
1245                   Jabber really doesn't like them. */
1246                u->away = g_malloc( strlen( away ) + 1 );
1247                for( i = j = 0; away[i]; i ++ )
1248                        if( ( u->away[j] = away[i] ) >= ' ' )
1249                                j ++;
1250                u->away[j] = 0;
1251               
1252                irc_reply( irc, 306, ":You're now away: %s", u->away );
1253                /* irc_umode_set( irc, irc->myhost, "+a" ); */
1254        }
1255        else
1256        {
1257                if( u->away ) g_free( u->away );
1258                u->away = NULL;
1259                /* irc_umode_set( irc, irc->myhost, "-a" ); */
1260                irc_reply( irc, 305, ":Welcome back" );
1261        }
1262       
1263        while( c )
1264        {
1265                if( ((struct gaim_connection *)c->data)->flags & OPT_LOGGED_IN )
1266                        proto_away( c->data, u->away );
1267               
1268                c = c->next;
1269        }
1270       
1271        return( 1 );
1272}
1273
1274void irc_spawn( irc_t *irc, user_t *u )
1275{
1276        irc_join( irc, u, irc->channel );
1277}
1278
1279void irc_join( irc_t *irc, user_t *u, char *channel )
1280{
1281        char *nick;
1282       
1283        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
1284                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
1285       
1286        if( nick_cmp( u->nick, irc->nick ) == 0 )
1287        {
1288                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
1289                irc_names( irc, channel );
1290                irc_topic( irc, channel );
1291        }
1292       
1293        nick = g_strdup( u->nick );
1294        nick_lc( nick );
1295        if( g_hash_table_lookup( irc->watches, nick ) )
1296        {
1297                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged online" );
1298        }
1299        g_free( nick );
1300}
1301
1302void irc_part( irc_t *irc, user_t *u, char *channel )
1303{
1304        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
1305}
1306
1307void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
1308{
1309        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
1310}
1311
1312void irc_kill( irc_t *irc, user_t *u )
1313{
1314        char *nick;
1315       
1316        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
1317       
1318        nick = g_strdup( u->nick );
1319        nick_lc( nick );
1320        if( g_hash_table_lookup( irc->watches, nick ) )
1321        {
1322                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged offline" );
1323        }
1324        g_free( nick );
1325}
1326
1327void irc_invite( irc_t *irc, char *nick, char *channel )
1328{
1329        struct conversation *c = conv_findchannel( channel );
1330        user_t *u = user_find( irc, nick );
1331       
1332        if( u && c && ( u->gc == c->gc ) )
1333                if( c->gc && c->gc->prpl && c->gc->prpl->chat_invite )
1334                {
1335                        c->gc->prpl->chat_invite( c->gc, c->id, "", u->handle );
1336                        irc_reply( irc, 341, "%s %s", nick, channel );
1337                        return;
1338                }
1339       
1340        irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel );
1341}
1342
1343int irc_send( irc_t *irc, char *nick, char *s, int flags )
1344{
1345        struct conversation *c = NULL;
1346        user_t *u = NULL;
1347       
1348        if( *nick == '#' || *nick == '&' )
1349        {
1350                if( !( c = conv_findchannel( nick ) ) )
1351                {
1352                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
1353                        return( 0 );
1354                }
1355        }
1356        else
1357        {
1358                u = user_find( irc, nick );
1359               
1360                if( !u )
1361                {
1362                        if( irc->is_private )
1363                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
1364                        else
1365                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
1366                        return( 0 );
1367                }
1368        }
1369       
1370        if( *s == 1 && s[strlen(s)-1] == 1 )
1371        {
1372                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
1373                {
1374                        if( s[7] == ' ' ) s ++;
1375                        s += 3;
1376                        *(s++) = '/';
1377                        *(s++) = 'm';
1378                        *(s++) = 'e';
1379                        *(s++) = ' ';
1380                        s -= 4;
1381                        s[strlen(s)-1] = 0;
1382                }
1383                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
1384                {
1385                        u = user_find( irc, irc->mynick );
1386                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
1387                        return( 1 );
1388                }
1389                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
1390                {
1391                        u = user_find( irc, irc->mynick );
1392                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
1393                        return( 1 );
1394                }
1395                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
1396                {
1397                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
1398                        {
1399                                time_t current_typing_notice = time( NULL );
1400                               
1401                                if( current_typing_notice - u->last_typing_notice >= 5 )
1402                                {
1403                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
1404                                        u->last_typing_notice = current_typing_notice;
1405                                }
1406                        }
1407                        return( 1 );
1408                }
1409                else
1410                {
1411                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
1412                        return( 0 );
1413                }
1414        }
1415       
1416        if( u )
1417        {
1418                /* For the next message, we probably do have to send new notices... */
1419                u->last_typing_notice = 0;
1420                u->is_private = irc->is_private;
1421               
1422                if( u->is_private )
1423                {
1424                        if( !u->online )
1425                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1426                        else if( u->away )
1427                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1428                }
1429               
1430                if( u->send_handler )
1431                        return( u->send_handler( irc, u, s, flags ) );
1432        }
1433        else if( c && c->gc && c->gc->prpl )
1434        {
1435                return( serv_send_chat( irc, c->gc, c->id, s ) );
1436        }
1437       
1438        return( 0 );
1439}
1440
1441gboolean buddy_send_handler_delayed( gpointer data )
1442{
1443        user_t *u = data;
1444       
1445        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
1446        serv_send_im( u->gc->irc, u, u->sendbuf, u->sendbuf_flags );
1447       
1448        g_free( u->sendbuf );
1449        u->sendbuf = NULL;
1450        u->sendbuf_len = 0;
1451        u->sendbuf_timer = 0;
1452        u->sendbuf_flags = 0;
1453       
1454        return( FALSE );
1455}
1456
1457int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
1458{
1459        if( !u || !u->gc ) return( 0 );
1460       
1461        if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 )
1462        {
1463                int delay;
1464               
1465                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1466                {
1467                        //Flush the buffer
1468                        g_source_remove( u->sendbuf_timer );
1469                        buddy_send_handler_delayed( u );
1470                }
1471
1472                if( u->sendbuf_len == 0 )
1473                {
1474                        u->sendbuf_len = strlen( msg ) + 2;
1475                        u->sendbuf = g_new (char, u->sendbuf_len );
1476                        u->sendbuf[0] = 0;
1477                        u->sendbuf_flags = flags;
1478                }
1479                else
1480                {
1481                        u->sendbuf_len += strlen( msg ) + 1;
1482                        u->sendbuf = g_renew ( char, u->sendbuf, u->sendbuf_len );
1483                }
1484               
1485                strcat( u->sendbuf, msg );
1486                strcat( u->sendbuf, "\n" );
1487               
1488                delay = set_getint( irc, "buddy_sendbuffer_delay" );
1489                if( delay <= 5 )
1490                        delay *= 1000;
1491               
1492                if( u->sendbuf_timer > 0 )
1493                        g_source_remove( u->sendbuf_timer );
1494                u->sendbuf_timer = g_timeout_add( delay, buddy_send_handler_delayed, u );
1495               
1496                return( 1 );
1497        }
1498        else
1499        {
1500                return( serv_send_im( irc, u, msg, flags ) );
1501        }
1502}
1503
1504int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1505{
1506        char last = 0;
1507        char *s = msg, *line = msg;
1508       
1509        /* The almighty linesplitter .. woohoo!! */
1510        while( !last )
1511        {
1512                if( *s == '\r' && *(s+1) == '\n' )
1513                        *(s++) = 0;
1514                if( *s == '\n' )
1515                {
1516                        last = s[1] == 0;
1517                        *s = 0;
1518                }
1519                else
1520                {
1521                        last = s[0] == 0;
1522                }
1523                if( *s == 0 )
1524                {
1525                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1526                        {
1527                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1528                                           type, to, line + 4 );
1529                        }
1530                        else
1531                        {
1532                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1533                                           type, to, prefix ? prefix : "", line );
1534                        }
1535                        line = s + 1;
1536                }
1537                s ++;
1538        }
1539       
1540        return( 1 );
1541}
1542
1543int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1544{
1545        user_t *u = user_find( irc, nick );
1546        static char *prefix = NULL;
1547       
1548        if( !u ) return( 0 );
1549        if( prefix && *prefix ) g_free( prefix );
1550       
1551        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1552        {
1553                int len = strlen( irc->nick) + 3;
1554                prefix = g_new (char, len );
1555                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) );
1556                prefix[len-1] = 0;
1557        }
1558        else
1559        {
1560                prefix = "";
1561        }
1562       
1563        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1564}
1565
1566int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1567{
1568        user_t *u = user_find( irc, nick );
1569       
1570        if( u )
1571                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1572        else
1573                return( 0 );
1574}
1575
1576/* Returns 0 if everything seems to be okay, a number >0 when there was a
1577   timeout. The number returned is the number of seconds we received no
1578   pongs from the user. When not connected yet, we don't ping but drop the
1579   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1580static gboolean irc_userping( gpointer _irc )
1581{
1582        irc_t *irc = _irc;
1583        int rv = 0;
1584       
1585        if( irc->status < USTATUS_LOGGED_IN )
1586        {
1587                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1588                        rv = gettime() - irc->last_pong;
1589        }
1590        else
1591        {
1592                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1593                {
1594                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1595                        irc->pinging = 1;
1596                }
1597                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1598                {
1599                        rv = gettime() - irc->last_pong;
1600                }
1601        }
1602       
1603        if( rv > 0 )
1604        {
1605                irc_write( irc, "ERROR :Closing Link: Ping Timeout: %d seconds", rv );
1606                irc_free( irc );
1607                return FALSE;
1608        }
1609       
1610        return TRUE;
1611}
Note: See TracBrowser for help on using the repository browser.