source: irc.c @ 9d6b229

Last change on this file since 9d6b229 was 9d6b229, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-14T20:34:10Z

Imported changes from main tree.

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