source: irc.c @ 4c266f2

Last change on this file since 4c266f2 was 4c266f2, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-18T22:17:59Z

Merged (and adapted) changes from main tree.

  • Property mode set to 100644
File size: 27.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, **cmd;
310        int i;
311
312        if( irc->readbuffer != NULL )
313        {
314                lines = irc_tokenize( irc->readbuffer );
315               
316                for( i = 0; *lines[i] != '\0'; i ++ )
317                {
318                        if( lines[i+1] == NULL )
319                        {
320                                temp = g_strdup( lines[i] );
321                                g_free( irc->readbuffer );
322                                irc->readbuffer = temp;
323                                i ++;
324                                break;
325                        }                       
326                       
327                        if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
328                                continue;
329                        if( !irc_exec( irc, cmd ) )
330                        {
331                                g_free( cmd );
332                                g_free( lines );
333                                return 0;
334                        }
335                       
336                        g_free( cmd );
337                }
338               
339                if( lines[i] != NULL )
340                {
341                        g_free( irc->readbuffer );
342                        irc->readbuffer = NULL;
343                }
344               
345                g_free( lines );
346        }
347       
348        return 1;       
349}
350
351char **irc_tokenize( char *buffer )
352{
353        int i, j;
354        char **lines;
355
356        /* Count the number of elements we're gonna need. */
357        for( i = 0, j = 1; buffer[i] != '\0'; i ++ )
358        {
359                if( buffer[i] == '\n' )
360                        if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
361                                j ++;
362        }
363       
364        /* Allocate j+1 elements. */
365        lines = g_new( char *, j + 1 );
366       
367        /* NULL terminate our list. */ 
368        lines[j] = NULL;
369       
370        lines[0] = buffer;
371       
372        /* Split the buffer in several strings, using \r\n as our seperator, where \r is optional.
373         * Although this is not in the RFC, some braindead ircds (newnet's) use this, so some clients might too.
374         */
375        for( i = 0, j = 0; buffer[i] != '\0'; i ++)
376        {
377                if( buffer[i] == '\n' )
378                {
379                        buffer[i] = '\0';
380                       
381                        if( i > 0 && buffer[i-1] == '\r' )
382                                buffer[i-1] = '\0';
383                        if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
384                                lines[++j] = buffer + i + 1;
385                }
386        }
387       
388        return( lines );
389}
390
391char **irc_parse_line( char *line )
392{
393        int i, j;
394        char **cmd;
395       
396        /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
397        if( line[0] == ':' )
398        {
399                for( i = 0; line[i] != ' '; i ++ );
400                line = line + i;
401        }
402        for( i = 0; line[i] == ' '; i ++ );
403        line = line + i;
404       
405        /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
406        if( line[0] == '\0')
407                return NULL;
408       
409        /* Count the number of char **cmd elements we're going to need. */
410        j = 1;
411        for( i = 0; line[i] != '\0'; i ++ )
412        {
413                if( line[i] == ' ' )
414                {
415                        j ++;
416                       
417                        if( line[i+1] == ':' )
418                                break;
419                }
420        }       
421
422        /* Allocate the space we need. */
423        cmd = g_new( char *, j + 1 );
424        cmd[j] = NULL;
425       
426        /* Do the actual line splitting, format is:
427         * Input: "PRIVMSG #bitlbee :foo bar"
428         * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
429         */
430
431        cmd[0] = line;
432        for( i = 0, j = 0; line[i] != '\0'; i ++ )
433        {
434                if( line[i] == ' ' )
435                {
436                        line[i] = '\0';
437                        cmd[++j] = line + i + 1;
438                       
439                        if( line[i+1] == ':' )
440                        {
441                                cmd[j] ++;
442                                break;
443                        }
444                }
445        }
446       
447        return cmd;
448}
449
450char *irc_build_line( char **cmd )
451{
452        int i, len;
453        char *s;
454       
455        if( cmd[0] == NULL )
456                return NULL;
457       
458        len = 1;
459        for( i = 0; cmd[i]; i ++ )
460                len += strlen( cmd[i] ) + 1;
461       
462        if( strchr( cmd[i-1], ' ' ) != NULL )
463                len ++;
464       
465        s = g_new0( char, len + 1 );
466        for( i = 0; cmd[i]; i ++ )
467        {
468                if( cmd[i+1] == NULL && strchr( cmd[i], ' ' ) != NULL )
469                        strcat( s, ":" );
470               
471                strcat( s, cmd[i] );
472               
473                if( cmd[i+1] )
474                        strcat( s, " " );
475        }
476        strcat( s, "\r\n" );
477       
478        return s;
479}
480
481void irc_reply( irc_t *irc, int code, char *format, ... )
482{
483        char text[IRC_MAX_LINE];
484        va_list params;
485       
486        va_start( params, format );
487        g_vsnprintf( text, IRC_MAX_LINE, format, params );
488        va_end( params );
489        irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
490       
491        return;
492}
493
494int irc_usermsg( irc_t *irc, char *format, ... )
495{
496        char text[1024];
497        va_list params;
498        char is_private = 0;
499        user_t *u;
500       
501        u = user_find( irc, irc->mynick );
502        if( u ) is_private = u->is_private;
503       
504        va_start( params, format );
505        g_vsnprintf( text, sizeof( text ), format, params );
506        va_end( params );
507       
508        return( irc_msgfrom( irc, u->nick, text ) );
509}
510
511void irc_write( irc_t *irc, char *format, ... ) 
512{
513        va_list params;
514
515        va_start( params, format );
516        irc_vawrite( irc, format, params );     
517        va_end( params );
518
519        return;
520
521}
522
523void irc_vawrite( irc_t *irc, char *format, va_list params )
524{
525        int size;
526        char line[IRC_MAX_LINE];
527       
528        if( irc->quit )
529                return;
530
531        g_vsnprintf( line, IRC_MAX_LINE - 3, format, params );
532
533        strip_newlines( line );
534        strcat( line, "\r\n" );
535
536        if( irc->sendbuffer != NULL ) {
537                size = strlen( irc->sendbuffer ) + strlen( line );
538                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
539                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
540        }
541        else 
542                irc->sendbuffer = g_strdup(line);       
543       
544        if( irc->w_watch_source_id == 0 )
545        {
546                irc->w_watch_source_id = g_io_add_watch( irc->io_channel, G_IO_OUT, bitlbee_io_current_client_write, irc );
547        }
548       
549        return;
550}
551
552void irc_write_all( int now, char *format, ... )
553{
554        va_list params;
555        GSList *temp;   
556       
557        va_start( params, format );
558       
559        temp = irc_connection_list;
560        while( temp != NULL )
561        {
562                irc_t *irc = temp->data;
563               
564                if( now )
565                {
566                        g_free( irc->sendbuffer );
567                        irc->sendbuffer = g_strdup( "\r\n" );
568                }
569                irc_vawrite( temp->data, format, params );
570                if( now )
571                {
572                        bitlbee_io_current_client_write( irc->io_channel, G_IO_OUT, irc );
573                }
574                temp = temp->next;
575        }
576       
577        va_end( params );
578        return;
579} 
580
581void irc_names( irc_t *irc, char *channel )
582{
583        user_t *u = irc->users;
584        char *s;
585        int control = ( g_strcasecmp( channel, irc->channel ) == 0 );
586        struct conversation *c = NULL;
587       
588        if( !control )
589                c = conv_findchannel( channel );
590       
591        /* RFC's say there is no error reply allowed on NAMES, so when the
592           channel is invalid, just give an empty reply. */
593       
594        if( control || c ) while( u )
595        {
596                if( u->online )
597                {
598                        if( u->gc && control )
599                        {
600                                if( set_getint( irc, "away_devoice" ) && !u->away )
601                                        s = "+";
602                                else
603                                        s = "";
604                               
605                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
606                        }
607                        else if( !u->gc )
608                        {
609                                if( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "root" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
610                                        s = "@";
611                                else if( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "user" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
612                                        s = "@";
613                                else
614                                        s = "";
615                               
616                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
617                        }
618                }
619               
620                u = u->next;
621        }
622       
623        /* For non-controlchannel channels (group conversations) only root and
624           you are listed now. Time to show the channel people: */
625        if( !control && c )
626        {
627                GList *l;
628               
629                for( l = c->in_room; l; l = l->next )
630                        if( ( u = user_findhandle( c->gc, l->data ) ) )
631                                irc_reply( irc, 353, "@ %s :%s%s", channel, "", u->nick );
632        }
633       
634        irc_reply( irc, 366, "%s :End of /NAMES list", channel );
635}
636
637int irc_check_login( irc_t *irc )
638{
639        if( irc->user && irc->nick )
640        {
641                if( global.conf->authmode == AUTHMODE_CLOSED && irc->status < USTATUS_AUTHORIZED )
642                {
643                        irc_reply( irc, 464, ":This server is password-protected." );
644                        return 0;
645                }
646                else
647                {
648                        irc_login( irc );
649                        return 1;
650                }
651        }
652        else
653        {
654                /* More information needed. */
655                return 0;
656        }
657}
658
659void irc_login( irc_t *irc )
660{
661        user_t *u;
662       
663        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
664        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
665        irc_reply( irc,   3, ":%s", IRCD_INFO );
666        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
667        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 );
668        irc_motd( irc );
669        irc_umode_set( irc, "+" UMODE, 1 );
670
671        u = user_add( irc, irc->mynick );
672        u->host = g_strdup( irc->myhost );
673        u->realname = g_strdup( ROOT_FN );
674        u->online = 1;
675        u->send_handler = root_command_string;
676        u->is_private = 0; /* [SH] The channel is root's personal playground. */
677        irc_spawn( irc, u );
678       
679        u = user_add( irc, NS_NICK );
680        u->host = g_strdup( irc->myhost );
681        u->realname = g_strdup( ROOT_FN );
682        u->online = 0;
683        u->send_handler = root_command_string;
684        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
685       
686        u = user_add( irc, irc->nick );
687        u->user = g_strdup( irc->user );
688        u->host = g_strdup( irc->host );
689        u->realname = g_strdup( irc->realname );
690        u->online = 1;
691//      u->send_handler = msg_echo;
692        irc_spawn( irc, u );
693       
694        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." );
695       
696        irc->status = USTATUS_LOGGED_IN;
697}
698
699void irc_motd( irc_t *irc )
700{
701        int fd;
702       
703        fd = open( global.conf->motdfile, O_RDONLY );
704        if( fd == -1 )
705        {
706                irc_reply( irc, 422, ":We don't need MOTDs." );
707        }
708        else
709        {
710                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
711                char *add, max;
712                int len;
713               
714                linebuf[79] = len = 0;
715                max = sizeof( linebuf ) - 1;
716               
717                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
718                while( read( fd, linebuf + len, 1 ) == 1 )
719                {
720                        if( linebuf[len] == '\n' || len == max )
721                        {
722                                linebuf[len] = 0;
723                                irc_reply( irc, 372, ":- %s", linebuf );
724                                len = 0;
725                        }
726                        else if( linebuf[len] == '%' )
727                        {
728                                read( fd, linebuf + len, 1 );
729                                if( linebuf[len] == 'h' )
730                                        add = irc->myhost;
731                                else if( linebuf[len] == 'v' )
732                                        add = BITLBEE_VERSION;
733                                else if( linebuf[len] == 'n' )
734                                        add = irc->nick;
735                                else
736                                        add = "%";
737                               
738                                strncpy( linebuf + len, add, max - len );
739                                while( linebuf[++len] );
740                        }
741                        else if( len < max )
742                        {
743                                len ++;
744                        }
745                }
746                irc_reply( irc, 376, ":End of MOTD" );
747                closesocket( fd );
748        }
749}
750
751void irc_topic( irc_t *irc, char *channel )
752{
753        if( g_strcasecmp( channel, irc->channel ) == 0 )
754        {
755                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
756        }
757        else
758        {
759                struct conversation *c = conv_findchannel( channel );
760               
761                if( c )
762                        irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
763                else
764                        irc_reply( irc, 331, "%s :No topic for this channel" );
765        }
766}
767
768void irc_umode_set( irc_t *irc, char *s, int allow_priv )
769{
770        /* allow_priv: Set to 0 if s contains user input, 1 if you want
771           to set a "privileged" mode (+o, +R, etc). */
772        char m[256], st = 1, *t;
773        int i;
774       
775        memset( m, 0, sizeof( m ) );
776       
777        for( t = irc->umode; *t; t ++ )
778                m[(int)*t] = 1;
779       
780        for( t = s; *t; t ++ )
781        {
782                if( *t == '+' || *t == '-' )
783                        st = *t == '+';
784                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
785                        m[(int)*t] = st;
786        }
787       
788        memset( irc->umode, 0, sizeof( irc->umode ) );
789       
790        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
791                if( m[i] )
792                        irc->umode[strlen(irc->umode)] = i;
793       
794        irc_reply( irc, 221, "+%s", irc->umode );
795}
796
797void irc_spawn( irc_t *irc, user_t *u )
798{
799        irc_join( irc, u, irc->channel );
800}
801
802void irc_join( irc_t *irc, user_t *u, char *channel )
803{
804        char *nick;
805       
806        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
807                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
808       
809        if( nick_cmp( u->nick, irc->nick ) == 0 )
810        {
811                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
812                irc_names( irc, channel );
813                irc_topic( irc, channel );
814        }
815       
816        nick = g_strdup( u->nick );
817        nick_lc( nick );
818        if( g_hash_table_lookup( irc->watches, nick ) )
819        {
820                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged online" );
821        }
822        g_free( nick );
823}
824
825void irc_part( irc_t *irc, user_t *u, char *channel )
826{
827        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
828}
829
830void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
831{
832        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
833}
834
835void irc_kill( irc_t *irc, user_t *u )
836{
837        char *nick;
838       
839        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
840       
841        nick = g_strdup( u->nick );
842        nick_lc( nick );
843        if( g_hash_table_lookup( irc->watches, nick ) )
844        {
845                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged offline" );
846        }
847        g_free( nick );
848}
849
850int irc_send( irc_t *irc, char *nick, char *s, int flags )
851{
852        struct conversation *c = NULL;
853        user_t *u = NULL;
854       
855        if( *nick == '#' || *nick == '&' )
856        {
857                if( !( c = conv_findchannel( nick ) ) )
858                {
859                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
860                        return( 0 );
861                }
862        }
863        else
864        {
865                u = user_find( irc, nick );
866               
867                if( !u )
868                {
869                        if( irc->is_private )
870                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
871                        else
872                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
873                        return( 0 );
874                }
875        }
876       
877        if( *s == 1 && s[strlen(s)-1] == 1 )
878        {
879                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
880                {
881                        if( s[7] == ' ' ) s ++;
882                        s += 3;
883                        *(s++) = '/';
884                        *(s++) = 'm';
885                        *(s++) = 'e';
886                        *(s++) = ' ';
887                        s -= 4;
888                        s[strlen(s)-1] = 0;
889                }
890                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
891                {
892                        u = user_find( irc, irc->mynick );
893                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
894                        return( 1 );
895                }
896                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
897                {
898                        u = user_find( irc, irc->mynick );
899                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
900                        return( 1 );
901                }
902                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
903                {
904                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
905                        {
906                                time_t current_typing_notice = time( NULL );
907                               
908                                if( current_typing_notice - u->last_typing_notice >= 5 )
909                                {
910                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
911                                        u->last_typing_notice = current_typing_notice;
912                                }
913                        }
914                        return( 1 );
915                }
916                else
917                {
918                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
919                        return( 0 );
920                }
921        }
922       
923        if( u )
924        {
925                /* For the next message, we probably do have to send new notices... */
926                u->last_typing_notice = 0;
927                u->is_private = irc->is_private;
928               
929                if( u->is_private )
930                {
931                        if( !u->online )
932                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
933                        else if( u->away )
934                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
935                }
936               
937                if( u->send_handler )
938                        return( u->send_handler( irc, u, s, flags ) );
939        }
940        else if( c && c->gc && c->gc->prpl )
941        {
942                return( serv_send_chat( irc, c->gc, c->id, s ) );
943        }
944       
945        return( 0 );
946}
947
948gboolean buddy_send_handler_delayed( gpointer data )
949{
950        user_t *u = data;
951       
952        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
953        serv_send_im( u->gc->irc, u, u->sendbuf, u->sendbuf_flags );
954       
955        g_free( u->sendbuf );
956        u->sendbuf = NULL;
957        u->sendbuf_len = 0;
958        u->sendbuf_timer = 0;
959        u->sendbuf_flags = 0;
960       
961        return( FALSE );
962}
963
964int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
965{
966        if( !u || !u->gc ) return( 0 );
967       
968        if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 )
969        {
970                int delay;
971               
972                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
973                {
974                        //Flush the buffer
975                        g_source_remove( u->sendbuf_timer );
976                        buddy_send_handler_delayed( u );
977                }
978
979                if( u->sendbuf_len == 0 )
980                {
981                        u->sendbuf_len = strlen( msg ) + 2;
982                        u->sendbuf = g_new (char, u->sendbuf_len );
983                        u->sendbuf[0] = 0;
984                        u->sendbuf_flags = flags;
985                }
986                else
987                {
988                        u->sendbuf_len += strlen( msg ) + 1;
989                        u->sendbuf = g_renew ( char, u->sendbuf, u->sendbuf_len );
990                }
991               
992                strcat( u->sendbuf, msg );
993                strcat( u->sendbuf, "\n" );
994               
995                delay = set_getint( irc, "buddy_sendbuffer_delay" );
996                if( delay <= 5 )
997                        delay *= 1000;
998               
999                if( u->sendbuf_timer > 0 )
1000                        g_source_remove( u->sendbuf_timer );
1001                u->sendbuf_timer = g_timeout_add( delay, buddy_send_handler_delayed, u );
1002               
1003                return( 1 );
1004        }
1005        else
1006        {
1007                return( serv_send_im( irc, u, msg, flags ) );
1008        }
1009}
1010
1011int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1012{
1013        char last = 0;
1014        char *s = msg, *line = msg;
1015       
1016        /* The almighty linesplitter .. woohoo!! */
1017        while( !last )
1018        {
1019                if( *s == '\r' && *(s+1) == '\n' )
1020                        *(s++) = 0;
1021                if( *s == '\n' )
1022                {
1023                        last = s[1] == 0;
1024                        *s = 0;
1025                }
1026                else
1027                {
1028                        last = s[0] == 0;
1029                }
1030                if( *s == 0 )
1031                {
1032                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1033                        {
1034                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1035                                           type, to, line + 4 );
1036                        }
1037                        else
1038                        {
1039                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1040                                           type, to, prefix ? prefix : "", line );
1041                        }
1042                        line = s + 1;
1043                }
1044                s ++;
1045        }
1046       
1047        return( 1 );
1048}
1049
1050int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1051{
1052        user_t *u = user_find( irc, nick );
1053        static char *prefix = NULL;
1054       
1055        if( !u ) return( 0 );
1056        if( prefix && *prefix ) g_free( prefix );
1057       
1058        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1059        {
1060                int len = strlen( irc->nick) + 3;
1061                prefix = g_new (char, len );
1062                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) );
1063                prefix[len-1] = 0;
1064        }
1065        else
1066        {
1067                prefix = "";
1068        }
1069       
1070        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1071}
1072
1073int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1074{
1075        user_t *u = user_find( irc, nick );
1076       
1077        if( u )
1078                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1079        else
1080                return( 0 );
1081}
1082
1083/* Returns 0 if everything seems to be okay, a number >0 when there was a
1084   timeout. The number returned is the number of seconds we received no
1085   pongs from the user. When not connected yet, we don't ping but drop the
1086   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1087static gboolean irc_userping( gpointer _irc )
1088{
1089        irc_t *irc = _irc;
1090        int rv = 0;
1091       
1092        if( irc->status < USTATUS_LOGGED_IN )
1093        {
1094                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1095                        rv = gettime() - irc->last_pong;
1096        }
1097        else
1098        {
1099                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1100                {
1101                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1102                        irc->pinging = 1;
1103                }
1104                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1105                {
1106                        rv = gettime() - irc->last_pong;
1107                }
1108        }
1109       
1110        if( rv > 0 )
1111        {
1112                irc_write( irc, "ERROR :Closing Link: Ping Timeout: %d seconds", rv );
1113                irc_free( irc );
1114                return FALSE;
1115        }
1116       
1117        return TRUE;
1118}
Note: See TracBrowser for help on using the repository browser.