source: irc.c @ e4d6271

Last change on this file since e4d6271 was e4d6271, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-12-27T14:39:32Z

IPv6 socket improvements. Daemon mode can now also listen on IPv6 sockets.
Also, when reverse lookup fails, BitlBee now correctly falls back to an
ASCII-formatted IP instead of "localhost.".

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