source: irc.c @ 1ea13be

Last change on this file since 1ea13be was 74c119d, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-15T15:42:20Z

Better DIE implementation, added SO_REUSEADDR to listening socket.

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