source: irc.c @ 22d41a2

Last change on this file since 22d41a2 was 22d41a2, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-11-18T19:10:20Z

Quit messages should appear again, at least on crashes. (And when running in inetd mode.)
The logging system needs some more work to complete this, maybe.

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