source: irc.c @ 2d99c97

Last change on this file since 2d99c97 was 9c62a7c, checked in by Jelmer Vernooij <jelmer@…>, at 2005-11-08T23:02:25Z

Fix GLib errors when sending QUIT.
Allow sending QUIT before registering.

  • Property mode set to 100644
File size: 39.2 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", "1", 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, "html", "nostrip", NULL ); */
124        set_add( irc, "lcnicks", "true", set_eval_bool );
125        set_add( irc, "ops", "both", set_eval_ops );
126        set_add( irc, "private", "true", set_eval_bool );
127        set_add( irc, "query_order", "lifo", NULL );
128        set_add( irc, "save_on_quit", "true", set_eval_bool );
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( 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                irc_vawrite( temp->data, format, params );
943                temp = temp->next;
944        }
945
946        va_end( params );
947        return;
948} 
949
950void irc_names( irc_t *irc, char *channel )
951{
952        user_t *u = irc->users;
953        char *s;
954        int control = ( g_strcasecmp( channel, irc->channel ) == 0 );
955        struct conversation *c = NULL;
956       
957        if( !control )
958                c = conv_findchannel( channel );
959       
960        /* RFC's say there is no error reply allowed on NAMES, so when the
961           channel is invalid, just give an empty reply. */
962       
963        if( control || c ) while( u )
964        {
965                if( u->online )
966                {
967                        if( u->gc && control )
968                        {
969                                if( set_getint( irc, "away_devoice" ) && !u->away )
970                                        s = "+";
971                                else
972                                        s = "";
973                               
974                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
975                        }
976                        else if( !u->gc )
977                        {
978                                if( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "root" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
979                                        s = "@";
980                                else if( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "user" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
981                                        s = "@";
982                                else
983                                        s = "";
984                               
985                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
986                        }
987                }
988               
989                u = u->next;
990        }
991       
992        /* For non-controlchannel channels (group conversations) only root and
993           you are listed now. Time to show the channel people: */
994        if( !control && c )
995        {
996                GList *l;
997               
998                for( l = c->in_room; l; l = l->next )
999                        if( ( u = user_findhandle( c->gc, l->data ) ) )
1000                                irc_reply( irc, 353, "@ %s :%s%s", channel, "", u->nick );
1001        }
1002       
1003        irc_reply( irc, 366, "%s :End of /NAMES list", channel );
1004}
1005
1006void irc_who( irc_t *irc, char *channel )
1007{
1008        user_t *u = irc->users;
1009        struct conversation *c;
1010        GList *l;
1011       
1012        if( !channel || *channel == '0' || *channel == '*' || !*channel )
1013                while( u )
1014                {
1015                        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 );
1016                        u = u->next;
1017                }
1018        else if( g_strcasecmp( channel, irc->channel ) == 0 )
1019                while( u )
1020                {
1021                        if( u->online )
1022                                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 );
1023                        u = u->next;
1024                }
1025        else if( ( c = conv_findchannel( channel ) ) )
1026                for( l = c->in_room; l; l = l->next )
1027                {
1028                        if( ( u = user_findhandle( c->gc, l->data ) ) )
1029                                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 );
1030                }
1031        else if( ( u = user_find( irc, channel ) ) )
1032                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 );
1033       
1034        irc_reply( irc, 315, "%s :End of /WHO list.", channel?channel:"**" );
1035}
1036
1037void irc_login( irc_t *irc )
1038{
1039        user_t *u;
1040       
1041        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
1042        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
1043        irc_reply( irc,   3, ":%s", IRCD_INFO );
1044        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES, CMODES );
1045        irc_motd( irc );
1046        irc_umode_set( irc, irc->myhost, "+" UMODE );
1047
1048        u = user_add( irc, irc->mynick );
1049        u->host = g_strdup( irc->myhost );
1050        u->realname = g_strdup( ROOT_FN );
1051        u->online = 1;
1052        u->send_handler = root_command_string;
1053        u->is_private = 0; /* [SH] The channel is root's personal playground. */
1054        irc_spawn( irc, u );
1055       
1056        u = user_add( irc, NS_NICK );
1057        u->host = g_strdup( irc->myhost );
1058        u->realname = g_strdup( ROOT_FN );
1059        u->online = 0;
1060        u->send_handler = root_command_string;
1061        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
1062       
1063        u = user_add( irc, irc->nick );
1064        u->user = g_strdup( irc->user );
1065        u->host = g_strdup( irc->host );
1066        u->realname = g_strdup( irc->realname );
1067        u->online = 1;
1068//      u->send_handler = msg_echo;
1069        irc_spawn( irc, u );
1070       
1071        irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\nIf you've never used BitlBee before, please do read the help information using the help command. Lots of FAQ's are answered there." );
1072       
1073        irc->status = USTATUS_LOGGED_IN;
1074}
1075
1076void irc_motd( irc_t *irc )
1077{
1078        int fd;
1079       
1080        fd = open( global.conf->motdfile, O_RDONLY );
1081        if( fd == -1 )
1082        {
1083                irc_reply( irc, 422, ":We don't need MOTDs." );
1084        }
1085        else
1086        {
1087                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
1088                char *add, max;
1089                int len;
1090               
1091                linebuf[79] = len = 0;
1092                max = sizeof( linebuf ) - 1;
1093               
1094                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
1095                while( read( fd, linebuf + len, 1 ) == 1 )
1096                {
1097                        if( linebuf[len] == '\n' || len == max )
1098                        {
1099                                linebuf[len] = 0;
1100                                irc_reply( irc, 372, ":- %s", linebuf );
1101                                len = 0;
1102                        }
1103                        else if( linebuf[len] == '%' )
1104                        {
1105                                read( fd, linebuf + len, 1 );
1106                                if( linebuf[len] == 'h' )
1107                                        add = irc->myhost;
1108                                else if( linebuf[len] == 'v' )
1109                                        add = BITLBEE_VERSION;
1110                                else if( linebuf[len] == 'n' )
1111                                        add = irc->nick;
1112                                else
1113                                        add = "%";
1114                               
1115                                strncpy( linebuf + len, add, max - len );
1116                                while( linebuf[++len] );
1117                        }
1118                        else if( len < max )
1119                        {
1120                                len ++;
1121                        }
1122                }
1123                irc_reply( irc, 376, ":End of MOTD" );
1124                closesocket( fd );
1125        }
1126}
1127
1128void irc_topic( irc_t *irc, char *channel )
1129{
1130        if( g_strcasecmp( channel, irc->channel ) == 0 )
1131        {
1132                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
1133        }
1134        else
1135        {
1136                struct conversation *c = conv_findchannel( channel );
1137               
1138                if( c )
1139                        irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
1140                else
1141                        irc_reply( irc, 331, "%s :No topic for this channel" );
1142        }
1143}
1144
1145void irc_whois( irc_t *irc, char *nick )
1146{
1147        user_t *u = user_find( irc, nick );
1148       
1149        if( u )
1150        {
1151                irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname );
1152               
1153                if( u->gc )
1154                        irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->gc->user->username,
1155                                   *u->gc->user->proto_opt[0] ? u->gc->user->proto_opt[0] : "", proto_name[u->gc->user->protocol] );
1156                else
1157                        irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO );
1158               
1159                if( !u->online )
1160                        irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1161                else if( u->away )
1162                        irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1163               
1164                irc_reply( irc, 318, "%s :End of /WHOIS list", nick );
1165        }
1166        else
1167        {
1168                irc_reply( irc, 401, "%s :Nick does not exist", nick );
1169        }
1170}
1171
1172
1173void irc_umode_set( irc_t *irc, char *who, char *s )
1174{
1175        char m[256], st = 1, *t;
1176        int i;
1177       
1178        memset( m, 0, sizeof( m ) );
1179       
1180        for( t = irc->umode; *t; t ++ )
1181                m[(int)*t] = 1;
1182       
1183        for( t = s; *t; t ++ )
1184        {
1185                if( *t == '+' || *t == '-' )
1186                        st = *t == '+';
1187                else
1188                        m[(int)*t] = st;
1189        }
1190       
1191        memset( irc->umode, 0, sizeof( irc->umode ) );
1192       
1193        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
1194                if( m[i] && strchr( UMODES, i ) )
1195                        irc->umode[strlen(irc->umode)] = i;
1196       
1197        irc_reply( irc, 221, "+%s", irc->umode );
1198}
1199
1200int irc_away( irc_t *irc, char *away )
1201{
1202        user_t *u = user_find( irc, irc->nick );
1203        GSList *c = get_connections();
1204       
1205        if( !u ) return( 0 );
1206       
1207        if( away && *away )
1208        {
1209                int i, j;
1210               
1211                /* Copy away string, but skip control chars. Mainly because
1212                   Jabber really doesn't like them. */
1213                u->away = g_malloc( strlen( away ) + 1 );
1214                for( i = j = 0; away[i]; i ++ )
1215                        if( ( u->away[j] = away[i] ) >= ' ' )
1216                                j ++;
1217                u->away[j] = 0;
1218               
1219                irc_reply( irc, 306, ":You're now away: %s", u->away );
1220                /* irc_umode_set( irc, irc->myhost, "+a" ); */
1221        }
1222        else
1223        {
1224                if( u->away ) g_free( u->away );
1225                u->away = NULL;
1226                /* irc_umode_set( irc, irc->myhost, "-a" ); */
1227                irc_reply( irc, 305, ":Welcome back" );
1228        }
1229       
1230        while( c )
1231        {
1232                if( ((struct gaim_connection *)c->data)->flags & OPT_LOGGED_IN )
1233                        proto_away( c->data, u->away );
1234               
1235                c = c->next;
1236        }
1237       
1238        return( 1 );
1239}
1240
1241void irc_spawn( irc_t *irc, user_t *u )
1242{
1243        irc_join( irc, u, irc->channel );
1244}
1245
1246void irc_join( irc_t *irc, user_t *u, char *channel )
1247{
1248        char *nick;
1249       
1250        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
1251                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
1252       
1253        if( nick_cmp( u->nick, irc->nick ) == 0 )
1254        {
1255                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
1256                irc_names( irc, channel );
1257                irc_topic( irc, channel );
1258        }
1259       
1260        nick = g_strdup( u->nick );
1261        nick_lc( nick );
1262        if( g_hash_table_lookup( irc->watches, nick ) )
1263        {
1264                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged online" );
1265        }
1266        g_free( nick );
1267}
1268
1269void irc_part( irc_t *irc, user_t *u, char *channel )
1270{
1271        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
1272}
1273
1274void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
1275{
1276        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
1277}
1278
1279void irc_kill( irc_t *irc, user_t *u )
1280{
1281        char *nick;
1282       
1283        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
1284       
1285        nick = g_strdup( u->nick );
1286        nick_lc( nick );
1287        if( g_hash_table_lookup( irc->watches, nick ) )
1288        {
1289                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged offline" );
1290        }
1291        g_free( nick );
1292}
1293
1294void irc_invite( irc_t *irc, char *nick, char *channel )
1295{
1296        struct conversation *c = conv_findchannel( channel );
1297        user_t *u = user_find( irc, nick );
1298       
1299        if( u && c && ( u->gc == c->gc ) )
1300                if( c->gc && c->gc->prpl && c->gc->prpl->chat_invite )
1301                {
1302                        c->gc->prpl->chat_invite( c->gc, c->id, "", u->handle );
1303                        irc_reply( irc, 341, "%s %s", nick, channel );
1304                        return;
1305                }
1306       
1307        irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel );
1308}
1309
1310int irc_send( irc_t *irc, char *nick, char *s, int flags )
1311{
1312        struct conversation *c = NULL;
1313        user_t *u = NULL;
1314       
1315        if( *nick == '#' )
1316        {
1317                if( !( c = conv_findchannel( nick ) ) )
1318                {
1319                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
1320                        return( 0 );
1321                }
1322        }
1323        else
1324        {
1325                u = user_find( irc, nick );
1326               
1327                if( !u )
1328                {
1329                        if( irc->is_private )
1330                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
1331                        else
1332                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
1333                        return( 0 );
1334                }
1335        }
1336       
1337        if( *s == 1 && s[strlen(s)-1] == 1 )
1338        {
1339                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
1340                {
1341                        if( s[7] == ' ' ) s ++;
1342                        s += 3;
1343                        *(s++) = '/';
1344                        *(s++) = 'm';
1345                        *(s++) = 'e';
1346                        *(s++) = ' ';
1347                        s -= 4;
1348                        s[strlen(s)-1] = 0;
1349                }
1350                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
1351                {
1352                        u = user_find( irc, irc->mynick );
1353                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
1354                        return( 1 );
1355                }
1356                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
1357                {
1358                        u = user_find( irc, irc->mynick );
1359                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
1360                        return( 1 );
1361                }
1362                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
1363                {
1364                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
1365                        {
1366                                time_t current_typing_notice = time( NULL );
1367                               
1368                                if( current_typing_notice - u->last_typing_notice >= 5 )
1369                                {
1370                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
1371                                        u->last_typing_notice = current_typing_notice;
1372                                }
1373                        }
1374                        return( 1 );
1375                }
1376                else
1377                {
1378                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
1379                        return( 0 );
1380                }
1381        }
1382       
1383        if( u )
1384        {
1385                /* For the next message, we probably do have to send new notices... */
1386                u->last_typing_notice = 0;
1387                u->is_private = irc->is_private;
1388               
1389                if( u->is_private )
1390                {
1391                        if( !u->online )
1392                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1393                        else if( u->away )
1394                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1395                }
1396               
1397                if( u->send_handler )
1398                        return( u->send_handler( irc, u, s, flags ) );
1399        }
1400        else if( c && c->gc && c->gc->prpl )
1401        {
1402                return( serv_send_chat( irc, c->gc, c->id, s ) );
1403        }
1404       
1405        return( 0 );
1406}
1407
1408gboolean buddy_send_handler_delayed( gpointer data )
1409{
1410        user_t *u = data;
1411       
1412        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
1413        serv_send_im( u->gc->irc, u, u->sendbuf, u->sendbuf_flags );
1414       
1415        g_free( u->sendbuf );
1416        u->sendbuf = NULL;
1417        u->sendbuf_len = 0;
1418        u->sendbuf_timer = 0;
1419        u->sendbuf_flags = 0;
1420       
1421        return( FALSE );
1422}
1423
1424int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
1425{
1426        if( !u || !u->gc ) return( 0 );
1427       
1428        if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 )
1429        {
1430                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1431                {
1432                        //Flush the buffer
1433                        g_source_remove( u->sendbuf_timer );
1434                        buddy_send_handler_delayed( u );
1435                }
1436
1437                if( u->sendbuf_len == 0 )
1438                {
1439                        u->sendbuf_len = strlen( msg ) + 2;
1440                        u->sendbuf = g_new (char, u->sendbuf_len );
1441                        u->sendbuf[0] = 0;
1442                        u->sendbuf_flags = flags;
1443                }
1444                else
1445                {
1446                        u->sendbuf_len += strlen( msg ) + 1;
1447                        u->sendbuf = g_renew ( char, u->sendbuf, u->sendbuf_len );
1448                }
1449               
1450                strcat( u->sendbuf, msg );
1451                strcat( u->sendbuf, "\n" );
1452               
1453                if( u->sendbuf_timer > 0 )
1454                        g_source_remove( u->sendbuf_timer );
1455                u->sendbuf_timer = g_timeout_add( set_getint( irc, "buddy_sendbuffer_delay" ) * 1000,
1456                                                  buddy_send_handler_delayed, u );
1457               
1458                return( 1 );
1459        }
1460        else
1461        {
1462                return( serv_send_im( irc, u, msg, flags ) );
1463        }
1464}
1465
1466int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1467{
1468        char last = 0;
1469        char *s = msg, *line = msg;
1470       
1471        /* The almighty linesplitter .. woohoo!! */
1472        while( !last )
1473        {
1474                if( *s == '\r' && *(s+1) == '\n' )
1475                        *(s++) = 0;
1476                if( *s == '\n' )
1477                {
1478                        last = s[1] == 0;
1479                        *s = 0;
1480                }
1481                else
1482                {
1483                        last = s[0] == 0;
1484                }
1485                if( *s == 0 )
1486                {
1487                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1488                        {
1489                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1490                                           type, to, line + 4 );
1491                        }
1492                        else
1493                        {
1494                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1495                                           type, to, prefix, line );
1496                        }
1497                        line = s + 1;
1498                }
1499                s ++;
1500        }
1501       
1502        return( 1 );
1503}
1504
1505int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1506{
1507        user_t *u = user_find( irc, nick );
1508        static char *prefix = NULL;
1509       
1510        if( !u ) return( 0 );
1511        if( prefix && *prefix ) g_free( prefix );
1512       
1513        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1514        {
1515                int len = strlen( irc->nick) + 3;
1516                prefix = g_new (char, len );
1517                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) );
1518                prefix[len-1] = 0;
1519        }
1520        else
1521        {
1522                prefix = "";
1523        }
1524       
1525        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1526}
1527
1528int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1529{
1530        user_t *u = user_find( irc, nick );
1531       
1532        if( u )
1533                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1534        else
1535                return( 0 );
1536}
1537
1538/* Returns 0 if everything seems to be okay, a number >0 when there was a
1539   timeout. The number returned is the number of seconds we received no
1540   pongs from the user. When not connected yet, we don't ping but drop the
1541   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1542static gboolean irc_userping( gpointer _irc )
1543{
1544        irc_t *irc = _irc;
1545        int rv = 0;
1546       
1547        if( irc->status < USTATUS_LOGGED_IN )
1548        {
1549                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1550                        rv = gettime() - irc->last_pong;
1551        }
1552        else
1553        {
1554                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1555                {
1556                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1557                        irc->pinging = 1;
1558                }
1559                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1560                {
1561                        rv = gettime() - irc->last_pong;
1562                }
1563        }
1564       
1565        if( rv > 0 )
1566        {
1567                irc_write( irc, "ERROR :Closing Link: Ping Timeout: %d seconds", rv );
1568                irc_free( irc );
1569                return FALSE;
1570        }
1571       
1572        return TRUE;
1573}
Note: See TracBrowser for help on using the repository browser.