source: irc.c @ 5c577bd

Last change on this file since 5c577bd was 5c577bd, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-13T22:10:29Z

IPC code (by no means final)

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