source: irc.c @ 238f828

Last change on this file since 238f828 was 238f828, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-12-26T14:42:54Z

Fixed that security hole, and added mode +R (don't know if that's the right one).
Now to add the actual oper features (and IPC). :-)

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