source: irc.c @ d25f6fc

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

Added OperPassword and RunMode = ForkDaemon settings. Oper stuff is
*INSECURE* because users can just do /mode +o to become operator.

  • Property mode set to 100644
File size: 40.5 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 )
508                        irc_umode_set( irc, irc->nick, "+o" );
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] )
535                                        irc_umode_set( irc, irc->nick, cmd[2] );
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 );
1086        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES, 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 );
1089        irc_umode_set( irc, irc->myhost, "+" UMODE );
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
1216void irc_umode_set( irc_t *irc, char *who, char *s )
1217{
1218        char m[256], st = 1, *t;
1219        int i;
1220       
1221        memset( m, 0, sizeof( m ) );
1222       
1223        for( t = irc->umode; *t; t ++ )
1224                m[(int)*t] = 1;
1225       
1226        for( t = s; *t; t ++ )
1227        {
1228                if( *t == '+' || *t == '-' )
1229                        st = *t == '+';
1230                else
1231                        m[(int)*t] = st;
1232        }
1233       
1234        memset( irc->umode, 0, sizeof( irc->umode ) );
1235       
1236        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
1237                if( m[i] && strchr( UMODES, i ) )
1238                        irc->umode[strlen(irc->umode)] = i;
1239       
1240        irc_reply( irc, 221, "+%s", irc->umode );
1241}
1242
1243int irc_away( irc_t *irc, char *away )
1244{
1245        user_t *u = user_find( irc, irc->nick );
1246        GSList *c = get_connections();
1247       
1248        if( !u ) return( 0 );
1249       
1250        if( away && *away )
1251        {
1252                int i, j;
1253               
1254                /* Copy away string, but skip control chars. Mainly because
1255                   Jabber really doesn't like them. */
1256                u->away = g_malloc( strlen( away ) + 1 );
1257                for( i = j = 0; away[i]; i ++ )
1258                        if( ( u->away[j] = away[i] ) >= ' ' )
1259                                j ++;
1260                u->away[j] = 0;
1261               
1262                irc_reply( irc, 306, ":You're now away: %s", u->away );
1263                /* irc_umode_set( irc, irc->myhost, "+a" ); */
1264        }
1265        else
1266        {
1267                if( u->away ) g_free( u->away );
1268                u->away = NULL;
1269                /* irc_umode_set( irc, irc->myhost, "-a" ); */
1270                irc_reply( irc, 305, ":Welcome back" );
1271        }
1272       
1273        while( c )
1274        {
1275                if( ((struct gaim_connection *)c->data)->flags & OPT_LOGGED_IN )
1276                        proto_away( c->data, u->away );
1277               
1278                c = c->next;
1279        }
1280       
1281        return( 1 );
1282}
1283
1284void irc_spawn( irc_t *irc, user_t *u )
1285{
1286        irc_join( irc, u, irc->channel );
1287}
1288
1289void irc_join( irc_t *irc, user_t *u, char *channel )
1290{
1291        char *nick;
1292       
1293        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
1294                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
1295       
1296        if( nick_cmp( u->nick, irc->nick ) == 0 )
1297        {
1298                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
1299                irc_names( irc, channel );
1300                irc_topic( irc, channel );
1301        }
1302       
1303        nick = g_strdup( u->nick );
1304        nick_lc( nick );
1305        if( g_hash_table_lookup( irc->watches, nick ) )
1306        {
1307                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged online" );
1308        }
1309        g_free( nick );
1310}
1311
1312void irc_part( irc_t *irc, user_t *u, char *channel )
1313{
1314        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
1315}
1316
1317void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
1318{
1319        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
1320}
1321
1322void irc_kill( irc_t *irc, user_t *u )
1323{
1324        char *nick;
1325       
1326        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
1327       
1328        nick = g_strdup( u->nick );
1329        nick_lc( nick );
1330        if( g_hash_table_lookup( irc->watches, nick ) )
1331        {
1332                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged offline" );
1333        }
1334        g_free( nick );
1335}
1336
1337void irc_invite( irc_t *irc, char *nick, char *channel )
1338{
1339        struct conversation *c = conv_findchannel( channel );
1340        user_t *u = user_find( irc, nick );
1341       
1342        if( u && c && ( u->gc == c->gc ) )
1343                if( c->gc && c->gc->prpl && c->gc->prpl->chat_invite )
1344                {
1345                        c->gc->prpl->chat_invite( c->gc, c->id, "", u->handle );
1346                        irc_reply( irc, 341, "%s %s", nick, channel );
1347                        return;
1348                }
1349       
1350        irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel );
1351}
1352
1353int irc_send( irc_t *irc, char *nick, char *s, int flags )
1354{
1355        struct conversation *c = NULL;
1356        user_t *u = NULL;
1357       
[94281ef]1358        if( *nick == '#' || *nick == '&' )
[b7d3cc34]1359        {
1360                if( !( c = conv_findchannel( nick ) ) )
1361                {
1362                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
1363                        return( 0 );
1364                }
1365        }
1366        else
1367        {
1368                u = user_find( irc, nick );
1369               
1370                if( !u )
1371                {
1372                        if( irc->is_private )
1373                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
1374                        else
1375                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
1376                        return( 0 );
1377                }
1378        }
1379       
1380        if( *s == 1 && s[strlen(s)-1] == 1 )
1381        {
1382                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
1383                {
1384                        if( s[7] == ' ' ) s ++;
1385                        s += 3;
1386                        *(s++) = '/';
1387                        *(s++) = 'm';
1388                        *(s++) = 'e';
1389                        *(s++) = ' ';
1390                        s -= 4;
1391                        s[strlen(s)-1] = 0;
1392                }
1393                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
1394                {
1395                        u = user_find( irc, irc->mynick );
1396                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
1397                        return( 1 );
1398                }
1399                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
1400                {
1401                        u = user_find( irc, irc->mynick );
1402                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
1403                        return( 1 );
1404                }
1405                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
1406                {
1407                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
1408                        {
1409                                time_t current_typing_notice = time( NULL );
1410                               
1411                                if( current_typing_notice - u->last_typing_notice >= 5 )
1412                                {
1413                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
1414                                        u->last_typing_notice = current_typing_notice;
1415                                }
1416                        }
1417                        return( 1 );
1418                }
1419                else
1420                {
1421                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
1422                        return( 0 );
1423                }
1424        }
1425       
1426        if( u )
1427        {
1428                /* For the next message, we probably do have to send new notices... */
1429                u->last_typing_notice = 0;
1430                u->is_private = irc->is_private;
1431               
1432                if( u->is_private )
1433                {
1434                        if( !u->online )
1435                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1436                        else if( u->away )
1437                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1438                }
1439               
1440                if( u->send_handler )
1441                        return( u->send_handler( irc, u, s, flags ) );
1442        }
1443        else if( c && c->gc && c->gc->prpl )
1444        {
1445                return( serv_send_chat( irc, c->gc, c->id, s ) );
1446        }
1447       
1448        return( 0 );
1449}
1450
1451gboolean buddy_send_handler_delayed( gpointer data )
1452{
1453        user_t *u = data;
1454       
1455        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
1456        serv_send_im( u->gc->irc, u, u->sendbuf, u->sendbuf_flags );
1457       
1458        g_free( u->sendbuf );
1459        u->sendbuf = NULL;
1460        u->sendbuf_len = 0;
1461        u->sendbuf_timer = 0;
1462        u->sendbuf_flags = 0;
1463       
1464        return( FALSE );
1465}
1466
1467int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
1468{
1469        if( !u || !u->gc ) return( 0 );
1470       
1471        if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 )
1472        {
[834ff44]1473                int delay;
1474               
[b7d3cc34]1475                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1476                {
1477                        //Flush the buffer
1478                        g_source_remove( u->sendbuf_timer );
1479                        buddy_send_handler_delayed( u );
1480                }
1481
1482                if( u->sendbuf_len == 0 )
1483                {
1484                        u->sendbuf_len = strlen( msg ) + 2;
1485                        u->sendbuf = g_new (char, u->sendbuf_len );
1486                        u->sendbuf[0] = 0;
1487                        u->sendbuf_flags = flags;
1488                }
1489                else
1490                {
1491                        u->sendbuf_len += strlen( msg ) + 1;
1492                        u->sendbuf = g_renew ( char, u->sendbuf, u->sendbuf_len );
1493                }
1494               
1495                strcat( u->sendbuf, msg );
1496                strcat( u->sendbuf, "\n" );
1497               
[834ff44]1498                delay = set_getint( irc, "buddy_sendbuffer_delay" );
1499                if( delay <= 5 )
1500                        delay *= 1000;
1501               
[b7d3cc34]1502                if( u->sendbuf_timer > 0 )
1503                        g_source_remove( u->sendbuf_timer );
[834ff44]1504                u->sendbuf_timer = g_timeout_add( delay, buddy_send_handler_delayed, u );
[b7d3cc34]1505               
1506                return( 1 );
1507        }
1508        else
1509        {
1510                return( serv_send_im( irc, u, msg, flags ) );
1511        }
1512}
1513
1514int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1515{
1516        char last = 0;
1517        char *s = msg, *line = msg;
1518       
1519        /* The almighty linesplitter .. woohoo!! */
1520        while( !last )
1521        {
1522                if( *s == '\r' && *(s+1) == '\n' )
1523                        *(s++) = 0;
1524                if( *s == '\n' )
1525                {
1526                        last = s[1] == 0;
1527                        *s = 0;
1528                }
1529                else
1530                {
1531                        last = s[0] == 0;
1532                }
1533                if( *s == 0 )
1534                {
1535                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1536                        {
1537                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1538                                           type, to, line + 4 );
1539                        }
1540                        else
1541                        {
1542                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
[25d1be7]1543                                           type, to, prefix ? prefix : "", line );
[b7d3cc34]1544                        }
1545                        line = s + 1;
1546                }
1547                s ++;
1548        }
1549       
1550        return( 1 );
1551}
1552
1553int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1554{
1555        user_t *u = user_find( irc, nick );
1556        static char *prefix = NULL;
1557       
1558        if( !u ) return( 0 );
1559        if( prefix && *prefix ) g_free( prefix );
1560       
1561        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1562        {
1563                int len = strlen( irc->nick) + 3;
1564                prefix = g_new (char, len );
1565                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) );
1566                prefix[len-1] = 0;
1567        }
1568        else
1569        {
1570                prefix = "";
1571        }
1572       
1573        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1574}
1575
1576int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1577{
1578        user_t *u = user_find( irc, nick );
1579       
1580        if( u )
1581                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1582        else
1583                return( 0 );
1584}
1585
1586/* Returns 0 if everything seems to be okay, a number >0 when there was a
1587   timeout. The number returned is the number of seconds we received no
1588   pongs from the user. When not connected yet, we don't ping but drop the
1589   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1590static gboolean irc_userping( gpointer _irc )
1591{
1592        irc_t *irc = _irc;
1593        int rv = 0;
1594       
1595        if( irc->status < USTATUS_LOGGED_IN )
1596        {
1597                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1598                        rv = gettime() - irc->last_pong;
1599        }
1600        else
1601        {
1602                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1603                {
1604                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1605                        irc->pinging = 1;
1606                }
1607                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1608                {
1609                        rv = gettime() - irc->last_pong;
1610                }
1611        }
1612       
1613        if( rv > 0 )
1614        {
1615                irc_write( irc, "ERROR :Closing Link: Ping Timeout: %d seconds", rv );
1616                irc_free( irc );
1617                return FALSE;
1618        }
1619       
1620        return TRUE;
1621}
Note: See TracBrowser for help on using the repository browser.