source: irc.c @ e8f8b18

Last change on this file since e8f8b18 was 92ad3d4, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-18T22:15:09Z

Merging changes from laptop: Now tries to empty sendbuffer before closing the connection,
and fixed some error checking flaw in bitlbee_io_current_client_write() that was also
present in the _read() version before 0.99 (and actually caused the 100% CPU bug that kept
us from releasing 1.0).

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