source: irc.c @ edf9657

Last change on this file since edf9657 was edf9657, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-14T18:25:00Z

Fixed the PASS-command, added error messages for invalid commands to irc_exec().

  • Property mode set to 100644
File size: 26.3 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
577int irc_check_login( irc_t *irc )
578{
579        if( irc->user && irc->nick )
580        {
581                if( global.conf->authmode == AUTHMODE_CLOSED && irc->status < USTATUS_AUTHORIZED )
582                {
583                        irc_reply( irc, 464, ":This server is password-protected." );
584                        return 0;
585                }
586                else
587                {
588                        irc_login( irc );
589                        return 1;
590                }
591        }
592        else
593        {
594                /* More information needed. */
595                return 0;
596        }
597}
598
599void irc_login( irc_t *irc )
600{
601        user_t *u;
602       
603        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
604        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
605        irc_reply( irc,   3, ":%s", IRCD_INFO );
606        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
607        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 );
608        irc_motd( irc );
609        irc_umode_set( irc, "+" UMODE, 1 );
610
611        u = user_add( irc, irc->mynick );
612        u->host = g_strdup( irc->myhost );
613        u->realname = g_strdup( ROOT_FN );
614        u->online = 1;
615        u->send_handler = root_command_string;
616        u->is_private = 0; /* [SH] The channel is root's personal playground. */
617        irc_spawn( irc, u );
618       
619        u = user_add( irc, NS_NICK );
620        u->host = g_strdup( irc->myhost );
621        u->realname = g_strdup( ROOT_FN );
622        u->online = 0;
623        u->send_handler = root_command_string;
624        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
625       
626        u = user_add( irc, irc->nick );
627        u->user = g_strdup( irc->user );
628        u->host = g_strdup( irc->host );
629        u->realname = g_strdup( irc->realname );
630        u->online = 1;
631//      u->send_handler = msg_echo;
632        irc_spawn( irc, u );
633       
634        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." );
635       
636        irc->status = USTATUS_LOGGED_IN;
637}
638
639void irc_motd( irc_t *irc )
640{
641        int fd;
642       
643        fd = open( global.conf->motdfile, O_RDONLY );
644        if( fd == -1 )
645        {
646                irc_reply( irc, 422, ":We don't need MOTDs." );
647        }
648        else
649        {
650                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
651                char *add, max;
652                int len;
653               
654                linebuf[79] = len = 0;
655                max = sizeof( linebuf ) - 1;
656               
657                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
658                while( read( fd, linebuf + len, 1 ) == 1 )
659                {
660                        if( linebuf[len] == '\n' || len == max )
661                        {
662                                linebuf[len] = 0;
663                                irc_reply( irc, 372, ":- %s", linebuf );
664                                len = 0;
665                        }
666                        else if( linebuf[len] == '%' )
667                        {
668                                read( fd, linebuf + len, 1 );
669                                if( linebuf[len] == 'h' )
670                                        add = irc->myhost;
671                                else if( linebuf[len] == 'v' )
672                                        add = BITLBEE_VERSION;
673                                else if( linebuf[len] == 'n' )
674                                        add = irc->nick;
675                                else
676                                        add = "%";
677                               
678                                strncpy( linebuf + len, add, max - len );
679                                while( linebuf[++len] );
680                        }
681                        else if( len < max )
682                        {
683                                len ++;
684                        }
685                }
686                irc_reply( irc, 376, ":End of MOTD" );
687                closesocket( fd );
688        }
689}
690
691void irc_topic( irc_t *irc, char *channel )
692{
693        if( g_strcasecmp( channel, irc->channel ) == 0 )
694        {
695                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
696        }
697        else
698        {
699                struct conversation *c = conv_findchannel( channel );
700               
701                if( c )
702                        irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
703                else
704                        irc_reply( irc, 331, "%s :No topic for this channel" );
705        }
706}
707
708void irc_umode_set( irc_t *irc, char *s, int allow_priv )
709{
710        /* allow_priv: Set to 0 if s contains user input, 1 if you want
711           to set a "privileged" mode (+o, +R, etc). */
712        char m[256], st = 1, *t;
713        int i;
714       
715        memset( m, 0, sizeof( m ) );
716       
717        for( t = irc->umode; *t; t ++ )
718                m[(int)*t] = 1;
719       
720        for( t = s; *t; t ++ )
721        {
722                if( *t == '+' || *t == '-' )
723                        st = *t == '+';
724                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
725                        m[(int)*t] = st;
726        }
727       
728        memset( irc->umode, 0, sizeof( irc->umode ) );
729       
730        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
731                if( m[i] )
732                        irc->umode[strlen(irc->umode)] = i;
733       
734        irc_reply( irc, 221, "+%s", irc->umode );
735}
736
737void irc_spawn( irc_t *irc, user_t *u )
738{
739        irc_join( irc, u, irc->channel );
740}
741
742void irc_join( irc_t *irc, user_t *u, char *channel )
743{
744        char *nick;
745       
746        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
747                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
748       
749        if( nick_cmp( u->nick, irc->nick ) == 0 )
750        {
751                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
752                irc_names( irc, channel );
753                irc_topic( irc, channel );
754        }
755       
756        nick = g_strdup( u->nick );
757        nick_lc( nick );
758        if( g_hash_table_lookup( irc->watches, nick ) )
759        {
760                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged online" );
761        }
762        g_free( nick );
763}
764
765void irc_part( irc_t *irc, user_t *u, char *channel )
766{
767        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
768}
769
770void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
771{
772        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
773}
774
775void irc_kill( irc_t *irc, user_t *u )
776{
777        char *nick;
778       
779        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
780       
781        nick = g_strdup( u->nick );
782        nick_lc( nick );
783        if( g_hash_table_lookup( irc->watches, nick ) )
784        {
785                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged offline" );
786        }
787        g_free( nick );
788}
789
790int irc_send( irc_t *irc, char *nick, char *s, int flags )
791{
792        struct conversation *c = NULL;
793        user_t *u = NULL;
794       
795        if( *nick == '#' || *nick == '&' )
796        {
797                if( !( c = conv_findchannel( nick ) ) )
798                {
799                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
800                        return( 0 );
801                }
802        }
803        else
804        {
805                u = user_find( irc, nick );
806               
807                if( !u )
808                {
809                        if( irc->is_private )
810                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
811                        else
812                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
813                        return( 0 );
814                }
815        }
816       
817        if( *s == 1 && s[strlen(s)-1] == 1 )
818        {
819                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
820                {
821                        if( s[7] == ' ' ) s ++;
822                        s += 3;
823                        *(s++) = '/';
824                        *(s++) = 'm';
825                        *(s++) = 'e';
826                        *(s++) = ' ';
827                        s -= 4;
828                        s[strlen(s)-1] = 0;
829                }
830                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
831                {
832                        u = user_find( irc, irc->mynick );
833                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
834                        return( 1 );
835                }
836                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
837                {
838                        u = user_find( irc, irc->mynick );
839                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
840                        return( 1 );
841                }
842                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
843                {
844                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
845                        {
846                                time_t current_typing_notice = time( NULL );
847                               
848                                if( current_typing_notice - u->last_typing_notice >= 5 )
849                                {
850                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
851                                        u->last_typing_notice = current_typing_notice;
852                                }
853                        }
854                        return( 1 );
855                }
856                else
857                {
858                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
859                        return( 0 );
860                }
861        }
862       
863        if( u )
864        {
865                /* For the next message, we probably do have to send new notices... */
866                u->last_typing_notice = 0;
867                u->is_private = irc->is_private;
868               
869                if( u->is_private )
870                {
871                        if( !u->online )
872                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
873                        else if( u->away )
874                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
875                }
876               
877                if( u->send_handler )
878                        return( u->send_handler( irc, u, s, flags ) );
879        }
880        else if( c && c->gc && c->gc->prpl )
881        {
882                return( serv_send_chat( irc, c->gc, c->id, s ) );
883        }
884       
885        return( 0 );
886}
887
888gboolean buddy_send_handler_delayed( gpointer data )
889{
890        user_t *u = data;
891       
892        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
893        serv_send_im( u->gc->irc, u, u->sendbuf, u->sendbuf_flags );
894       
895        g_free( u->sendbuf );
896        u->sendbuf = NULL;
897        u->sendbuf_len = 0;
898        u->sendbuf_timer = 0;
899        u->sendbuf_flags = 0;
900       
901        return( FALSE );
902}
903
904int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
905{
906        if( !u || !u->gc ) return( 0 );
907       
908        if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 )
909        {
910                int delay;
911               
912                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
913                {
914                        //Flush the buffer
915                        g_source_remove( u->sendbuf_timer );
916                        buddy_send_handler_delayed( u );
917                }
918
919                if( u->sendbuf_len == 0 )
920                {
921                        u->sendbuf_len = strlen( msg ) + 2;
922                        u->sendbuf = g_new (char, u->sendbuf_len );
923                        u->sendbuf[0] = 0;
924                        u->sendbuf_flags = flags;
925                }
926                else
927                {
928                        u->sendbuf_len += strlen( msg ) + 1;
929                        u->sendbuf = g_renew ( char, u->sendbuf, u->sendbuf_len );
930                }
931               
932                strcat( u->sendbuf, msg );
933                strcat( u->sendbuf, "\n" );
934               
935                delay = set_getint( irc, "buddy_sendbuffer_delay" );
936                if( delay <= 5 )
937                        delay *= 1000;
938               
939                if( u->sendbuf_timer > 0 )
940                        g_source_remove( u->sendbuf_timer );
941                u->sendbuf_timer = g_timeout_add( delay, buddy_send_handler_delayed, u );
942               
943                return( 1 );
944        }
945        else
946        {
947                return( serv_send_im( irc, u, msg, flags ) );
948        }
949}
950
951int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
952{
953        char last = 0;
954        char *s = msg, *line = msg;
955       
956        /* The almighty linesplitter .. woohoo!! */
957        while( !last )
958        {
959                if( *s == '\r' && *(s+1) == '\n' )
960                        *(s++) = 0;
961                if( *s == '\n' )
962                {
963                        last = s[1] == 0;
964                        *s = 0;
965                }
966                else
967                {
968                        last = s[0] == 0;
969                }
970                if( *s == 0 )
971                {
972                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
973                        {
974                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
975                                           type, to, line + 4 );
976                        }
977                        else
978                        {
979                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
980                                           type, to, prefix ? prefix : "", line );
981                        }
982                        line = s + 1;
983                }
984                s ++;
985        }
986       
987        return( 1 );
988}
989
990int irc_msgfrom( irc_t *irc, char *nick, char *msg )
991{
992        user_t *u = user_find( irc, nick );
993        static char *prefix = NULL;
994       
995        if( !u ) return( 0 );
996        if( prefix && *prefix ) g_free( prefix );
997       
998        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
999        {
1000                int len = strlen( irc->nick) + 3;
1001                prefix = g_new (char, len );
1002                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) );
1003                prefix[len-1] = 0;
1004        }
1005        else
1006        {
1007                prefix = "";
1008        }
1009       
1010        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1011}
1012
1013int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1014{
1015        user_t *u = user_find( irc, nick );
1016       
1017        if( u )
1018                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1019        else
1020                return( 0 );
1021}
1022
1023/* Returns 0 if everything seems to be okay, a number >0 when there was a
1024   timeout. The number returned is the number of seconds we received no
1025   pongs from the user. When not connected yet, we don't ping but drop the
1026   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1027static gboolean irc_userping( gpointer _irc )
1028{
1029        irc_t *irc = _irc;
1030        int rv = 0;
1031       
1032        if( irc->status < USTATUS_LOGGED_IN )
1033        {
1034                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1035                        rv = gettime() - irc->last_pong;
1036        }
1037        else
1038        {
1039                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1040                {
1041                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1042                        irc->pinging = 1;
1043                }
1044                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1045                {
1046                        rv = gettime() - irc->last_pong;
1047                }
1048        }
1049       
1050        if( rv > 0 )
1051        {
1052                irc_write( irc, "ERROR :Closing Link: Ping Timeout: %d seconds", rv );
1053                irc_free( irc );
1054                return FALSE;
1055        }
1056       
1057        return TRUE;
1058}
Note: See TracBrowser for help on using the repository browser.