source: irc.c @ 0298d11

Last change on this file since 0298d11 was 0298d11, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-13T23:51:21Z

Moved all IRC commands to separate functions in irc_commands.c. At least the PASS command doesn't work yet in this form.

  • Property mode set to 100644
File size: 26.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
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" ) ) 
172                if( storage_save( irc, TRUE ) != STORAGE_OK )
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 );
180       
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       
275        if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON )
276                g_main_quit( global.loop );
277}
278
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
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
421void irc_reply( irc_t *irc, int code, char *format, ... )
422{
423        char text[IRC_MAX_LINE];
424        va_list params;
425       
426        va_start( params, format );
427        g_vsnprintf( text, IRC_MAX_LINE, format, params );
428        va_end( params );
429        irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
430       
431        return;
432}
433
434int irc_usermsg( irc_t *irc, char *format, ... )
435{
436        char text[1024];
437        va_list params;
438        char is_private = 0;
439        user_t *u;
440       
441        u = user_find( irc, irc->mynick );
442        if( u ) is_private = u->is_private;
443       
444        va_start( params, format );
445        g_vsnprintf( text, sizeof( text ), format, params );
446        va_end( params );
447       
448        return( irc_msgfrom( irc, u->nick, text ) );
449}
450
451void irc_write( irc_t *irc, char *format, ... ) 
452{
453        va_list params;
454
455        va_start( params, format );
456        irc_vawrite( irc, format, params );     
457        va_end( params );
458
459        return;
460
461}
462
463void irc_vawrite( irc_t *irc, char *format, va_list params )
464{
465        int size;
466        char line[IRC_MAX_LINE];
467       
468        if( irc->quit )
469                return;
470
471        g_vsnprintf( line, IRC_MAX_LINE - 3, format, params );
472
473        strip_newlines( line );
474        strcat( line, "\r\n" );
475
476        if( irc->sendbuffer != NULL ) {
477                size = strlen( irc->sendbuffer ) + strlen( line );
478                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
479                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
480        }
481        else 
482                irc->sendbuffer = g_strdup(line);       
483       
484        if( irc->w_watch_source_id == 0 )
485        {
486                irc->w_watch_source_id = g_io_add_watch( irc->io_channel, G_IO_OUT, bitlbee_io_current_client_write, irc );
487        }
488       
489        return;
490}
491
492void irc_write_all( int now, char *format, ... )
493{
494        va_list params;
495        GSList *temp;   
496       
497        va_start( params, format );
498       
499        temp = irc_connection_list;
500        while( temp != NULL )
501        {
502                irc_t *irc = temp->data;
503               
504                if( now )
505                {
506                        g_free( irc->sendbuffer );
507                        irc->sendbuffer = g_strdup( "\r\n" );
508                }
509                irc_vawrite( temp->data, format, params );
510                if( now )
511                {
512                        bitlbee_io_current_client_write( irc->io_channel, G_IO_OUT, irc );
513                }
514                temp = temp->next;
515        }
516       
517        va_end( params );
518        return;
519} 
520
521void irc_names( irc_t *irc, char *channel )
522{
523        user_t *u = irc->users;
524        char *s;
525        int control = ( g_strcasecmp( channel, irc->channel ) == 0 );
526        struct conversation *c = NULL;
527       
528        if( !control )
529                c = conv_findchannel( channel );
530       
531        /* RFC's say there is no error reply allowed on NAMES, so when the
532           channel is invalid, just give an empty reply. */
533       
534        if( control || c ) while( u )
535        {
536                if( u->online )
537                {
538                        if( u->gc && control )
539                        {
540                                if( set_getint( irc, "away_devoice" ) && !u->away )
541                                        s = "+";
542                                else
543                                        s = "";
544                               
545                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
546                        }
547                        else if( !u->gc )
548                        {
549                                if( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "root" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
550                                        s = "@";
551                                else if( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "user" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
552                                        s = "@";
553                                else
554                                        s = "";
555                               
556                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
557                        }
558                }
559               
560                u = u->next;
561        }
562       
563        /* For non-controlchannel channels (group conversations) only root and
564           you are listed now. Time to show the channel people: */
565        if( !control && c )
566        {
567                GList *l;
568               
569                for( l = c->in_room; l; l = l->next )
570                        if( ( u = user_findhandle( c->gc, l->data ) ) )
571                                irc_reply( irc, 353, "@ %s :%s%s", channel, "", u->nick );
572        }
573       
574        irc_reply( irc, 366, "%s :End of /NAMES list", channel );
575}
576
577void irc_login( irc_t *irc )
578{
579        user_t *u;
580       
581        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
582        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
583        irc_reply( irc,   3, ":%s", IRCD_INFO );
584        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
585        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 );
586        irc_motd( irc );
587        irc_umode_set( irc, "+" UMODE, 1 );
588
589        u = user_add( irc, irc->mynick );
590        u->host = g_strdup( irc->myhost );
591        u->realname = g_strdup( ROOT_FN );
592        u->online = 1;
593        u->send_handler = root_command_string;
594        u->is_private = 0; /* [SH] The channel is root's personal playground. */
595        irc_spawn( irc, u );
596       
597        u = user_add( irc, NS_NICK );
598        u->host = g_strdup( irc->myhost );
599        u->realname = g_strdup( ROOT_FN );
600        u->online = 0;
601        u->send_handler = root_command_string;
602        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
603       
604        u = user_add( irc, irc->nick );
605        u->user = g_strdup( irc->user );
606        u->host = g_strdup( irc->host );
607        u->realname = g_strdup( irc->realname );
608        u->online = 1;
609//      u->send_handler = msg_echo;
610        irc_spawn( irc, u );
611       
612        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." );
613       
614        irc->status = USTATUS_LOGGED_IN;
615}
616
617void irc_motd( irc_t *irc )
618{
619        int fd;
620       
621        fd = open( global.conf->motdfile, O_RDONLY );
622        if( fd == -1 )
623        {
624                irc_reply( irc, 422, ":We don't need MOTDs." );
625        }
626        else
627        {
628                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
629                char *add, max;
630                int len;
631               
632                linebuf[79] = len = 0;
633                max = sizeof( linebuf ) - 1;
634               
635                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
636                while( read( fd, linebuf + len, 1 ) == 1 )
637                {
638                        if( linebuf[len] == '\n' || len == max )
639                        {
640                                linebuf[len] = 0;
641                                irc_reply( irc, 372, ":- %s", linebuf );
642                                len = 0;
643                        }
644                        else if( linebuf[len] == '%' )
645                        {
646                                read( fd, linebuf + len, 1 );
647                                if( linebuf[len] == 'h' )
648                                        add = irc->myhost;
649                                else if( linebuf[len] == 'v' )
650                                        add = BITLBEE_VERSION;
651                                else if( linebuf[len] == 'n' )
652                                        add = irc->nick;
653                                else
654                                        add = "%";
655                               
656                                strncpy( linebuf + len, add, max - len );
657                                while( linebuf[++len] );
658                        }
659                        else if( len < max )
660                        {
661                                len ++;
662                        }
663                }
664                irc_reply( irc, 376, ":End of MOTD" );
665                closesocket( fd );
666        }
667}
668
669void irc_topic( irc_t *irc, char *channel )
670{
671        if( g_strcasecmp( channel, irc->channel ) == 0 )
672        {
673                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
674        }
675        else
676        {
677                struct conversation *c = conv_findchannel( channel );
678               
679                if( c )
680                        irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
681                else
682                        irc_reply( irc, 331, "%s :No topic for this channel" );
683        }
684}
685
686void irc_umode_set( irc_t *irc, char *s, int allow_priv )
687{
688        /* allow_priv: Set to 0 if s contains user input, 1 if you want
689           to set a "privileged" mode (+o, +R, etc). */
690        char m[256], st = 1, *t;
691        int i;
692       
693        memset( m, 0, sizeof( m ) );
694       
695        for( t = irc->umode; *t; t ++ )
696                m[(int)*t] = 1;
697       
698        for( t = s; *t; t ++ )
699        {
700                if( *t == '+' || *t == '-' )
701                        st = *t == '+';
702                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
703                        m[(int)*t] = st;
704        }
705       
706        memset( irc->umode, 0, sizeof( irc->umode ) );
707       
708        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
709                if( m[i] )
710                        irc->umode[strlen(irc->umode)] = i;
711       
712        irc_reply( irc, 221, "+%s", irc->umode );
713}
714
715void irc_spawn( irc_t *irc, user_t *u )
716{
717        irc_join( irc, u, irc->channel );
718}
719
720void irc_join( irc_t *irc, user_t *u, char *channel )
721{
722        char *nick;
723       
724        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
725                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
726       
727        if( nick_cmp( u->nick, irc->nick ) == 0 )
728        {
729                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
730                irc_names( irc, channel );
731                irc_topic( irc, channel );
732        }
733       
734        nick = g_strdup( u->nick );
735        nick_lc( nick );
736        if( g_hash_table_lookup( irc->watches, nick ) )
737        {
738                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged online" );
739        }
740        g_free( nick );
741}
742
743void irc_part( irc_t *irc, user_t *u, char *channel )
744{
745        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
746}
747
748void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
749{
750        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
751}
752
753void irc_kill( irc_t *irc, user_t *u )
754{
755        char *nick;
756       
757        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
758       
759        nick = g_strdup( u->nick );
760        nick_lc( nick );
761        if( g_hash_table_lookup( irc->watches, nick ) )
762        {
763                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged offline" );
764        }
765        g_free( nick );
766}
767
768int irc_send( irc_t *irc, char *nick, char *s, int flags )
769{
770        struct conversation *c = NULL;
771        user_t *u = NULL;
772       
773        if( *nick == '#' || *nick == '&' )
774        {
775                if( !( c = conv_findchannel( nick ) ) )
776                {
777                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
778                        return( 0 );
779                }
780        }
781        else
782        {
783                u = user_find( irc, nick );
784               
785                if( !u )
786                {
787                        if( irc->is_private )
788                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
789                        else
790                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
791                        return( 0 );
792                }
793        }
794       
795        if( *s == 1 && s[strlen(s)-1] == 1 )
796        {
797                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
798                {
799                        if( s[7] == ' ' ) s ++;
800                        s += 3;
801                        *(s++) = '/';
802                        *(s++) = 'm';
803                        *(s++) = 'e';
804                        *(s++) = ' ';
805                        s -= 4;
806                        s[strlen(s)-1] = 0;
807                }
808                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
809                {
810                        u = user_find( irc, irc->mynick );
811                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
812                        return( 1 );
813                }
814                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
815                {
816                        u = user_find( irc, irc->mynick );
817                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
818                        return( 1 );
819                }
820                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
821                {
822                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
823                        {
824                                time_t current_typing_notice = time( NULL );
825                               
826                                if( current_typing_notice - u->last_typing_notice >= 5 )
827                                {
828                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
829                                        u->last_typing_notice = current_typing_notice;
830                                }
831                        }
832                        return( 1 );
833                }
834                else
835                {
836                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
837                        return( 0 );
838                }
839        }
840       
841        if( u )
842        {
843                /* For the next message, we probably do have to send new notices... */
844                u->last_typing_notice = 0;
845                u->is_private = irc->is_private;
846               
847                if( u->is_private )
848                {
849                        if( !u->online )
850                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
851                        else if( u->away )
852                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
853                }
854               
855                if( u->send_handler )
856                        return( u->send_handler( irc, u, s, flags ) );
857        }
858        else if( c && c->gc && c->gc->prpl )
859        {
860                return( serv_send_chat( irc, c->gc, c->id, s ) );
861        }
862       
863        return( 0 );
864}
865
866gboolean buddy_send_handler_delayed( gpointer data )
867{
868        user_t *u = data;
869       
870        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
871        serv_send_im( u->gc->irc, u, u->sendbuf, u->sendbuf_flags );
872       
873        g_free( u->sendbuf );
874        u->sendbuf = NULL;
875        u->sendbuf_len = 0;
876        u->sendbuf_timer = 0;
877        u->sendbuf_flags = 0;
878       
879        return( FALSE );
880}
881
882int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
883{
884        if( !u || !u->gc ) return( 0 );
885       
886        if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 )
887        {
888                int delay;
889               
890                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
891                {
892                        //Flush the buffer
893                        g_source_remove( u->sendbuf_timer );
894                        buddy_send_handler_delayed( u );
895                }
896
897                if( u->sendbuf_len == 0 )
898                {
899                        u->sendbuf_len = strlen( msg ) + 2;
900                        u->sendbuf = g_new (char, u->sendbuf_len );
901                        u->sendbuf[0] = 0;
902                        u->sendbuf_flags = flags;
903                }
904                else
905                {
906                        u->sendbuf_len += strlen( msg ) + 1;
907                        u->sendbuf = g_renew ( char, u->sendbuf, u->sendbuf_len );
908                }
909               
910                strcat( u->sendbuf, msg );
911                strcat( u->sendbuf, "\n" );
912               
913                delay = set_getint( irc, "buddy_sendbuffer_delay" );
914                if( delay <= 5 )
915                        delay *= 1000;
916               
917                if( u->sendbuf_timer > 0 )
918                        g_source_remove( u->sendbuf_timer );
919                u->sendbuf_timer = g_timeout_add( delay, buddy_send_handler_delayed, u );
920               
921                return( 1 );
922        }
923        else
924        {
925                return( serv_send_im( irc, u, msg, flags ) );
926        }
927}
928
929int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
930{
931        char last = 0;
932        char *s = msg, *line = msg;
933       
934        /* The almighty linesplitter .. woohoo!! */
935        while( !last )
936        {
937                if( *s == '\r' && *(s+1) == '\n' )
938                        *(s++) = 0;
939                if( *s == '\n' )
940                {
941                        last = s[1] == 0;
942                        *s = 0;
943                }
944                else
945                {
946                        last = s[0] == 0;
947                }
948                if( *s == 0 )
949                {
950                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
951                        {
952                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
953                                           type, to, line + 4 );
954                        }
955                        else
956                        {
957                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
958                                           type, to, prefix ? prefix : "", line );
959                        }
960                        line = s + 1;
961                }
962                s ++;
963        }
964       
965        return( 1 );
966}
967
968int irc_msgfrom( irc_t *irc, char *nick, char *msg )
969{
970        user_t *u = user_find( irc, nick );
971        static char *prefix = NULL;
972       
973        if( !u ) return( 0 );
974        if( prefix && *prefix ) g_free( prefix );
975       
976        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
977        {
978                int len = strlen( irc->nick) + 3;
979                prefix = g_new (char, len );
980                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) );
981                prefix[len-1] = 0;
982        }
983        else
984        {
985                prefix = "";
986        }
987       
988        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
989}
990
991int irc_noticefrom( irc_t *irc, char *nick, char *msg )
992{
993        user_t *u = user_find( irc, nick );
994       
995        if( u )
996                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
997        else
998                return( 0 );
999}
1000
1001/* Returns 0 if everything seems to be okay, a number >0 when there was a
1002   timeout. The number returned is the number of seconds we received no
1003   pongs from the user. When not connected yet, we don't ping but drop the
1004   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1005static gboolean irc_userping( gpointer _irc )
1006{
1007        irc_t *irc = _irc;
1008        int rv = 0;
1009       
1010        if( irc->status < USTATUS_LOGGED_IN )
1011        {
1012                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1013                        rv = gettime() - irc->last_pong;
1014        }
1015        else
1016        {
1017                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1018                {
1019                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1020                        irc->pinging = 1;
1021                }
1022                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1023                {
1024                        rv = gettime() - irc->last_pong;
1025                }
1026        }
1027       
1028        if( rv > 0 )
1029        {
1030                irc_write( irc, "ERROR :Closing Link: Ping Timeout: %d seconds", rv );
1031                irc_free( irc );
1032                return FALSE;
1033        }
1034       
1035        return TRUE;
1036}
Note: See TracBrowser for help on using the repository browser.