source: irc.c @ fc50d48

Last change on this file since fc50d48 was fc50d48, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-20T12:21:24Z

irc_abort() does logging (including a reason) now.

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