source: irc.c @ 8efa2f4

Last change on this file since 8efa2f4 was 8efa2f4, checked in by Jelmer Vernooij <jelmer@…>, at 2005-12-08T14:57:13Z

Merge some fixes from my integration branch

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