source: irc.c @ 2face62

Last change on this file since 2face62 was 2face62, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-19T16:34:41Z

A bit too much for one commit, but well: Client processes didn't clean up
some master structs (bitlbee_child list) yet, and added the IPC CLIENT
command to inform the master process about host- and nickname. Can be useful
later.

  • Property mode set to 100644
File size: 27.1 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#include "ipc.h"
30
31static gboolean irc_userping( gpointer _irc );
32
33GSList *irc_connection_list = NULL;
34
35static char *passchange (irc_t *irc, void *set, char *value) 
36{
37        irc_setpass (irc, value);
38        return (NULL);
39}
40
41irc_t *irc_new( int fd )
42{
43        irc_t *irc;
44        struct hostent *peer;
45        unsigned int i;
46        char buf[128];
47#ifdef IPV6
48        struct sockaddr_in6 sock[1];
49#else
50        struct sockaddr_in sock[1];
51#endif
52       
53        irc = g_new0( irc_t, 1 );
54       
55        irc->fd = fd;
56        irc->io_channel = g_io_channel_unix_new( fd );
57#ifdef GLIB2
58        g_io_channel_set_encoding (irc->io_channel, NULL, NULL);
59        g_io_channel_set_buffered (irc->io_channel, FALSE);
60        g_io_channel_set_flags( irc->io_channel, G_IO_FLAG_NONBLOCK, NULL );
61#else
62        fcntl( irc->fd, F_SETFL, O_NONBLOCK);
63#endif
64        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 );
65       
66        irc->status = USTATUS_OFFLINE;
67        irc->last_pong = gettime();
68       
69        irc->userhash = g_hash_table_new( g_str_hash, g_str_equal );
70        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
71       
72        strcpy( irc->umode, UMODE );
73        irc->mynick = g_strdup( ROOT_NICK );
74        irc->channel = g_strdup( ROOT_CHAN );
75       
76        i = sizeof( *sock );
77       
78        if( global.conf->hostname )
79                irc->myhost = g_strdup( global.conf->hostname );
80#ifdef IPV6
81        else if( getsockname( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin6_family == AF_INETx )
82        {
83                if( ( peer = gethostbyaddr( (char*) &sock->sin6_addr, sizeof( sock->sin6_addr ), AF_INETx ) ) )
84                        irc->myhost = g_strdup( peer->h_name );
85                else if( inet_ntop( AF_INETx, &sock->sin6_addr, buf, sizeof( buf ) - 1 ) != NULL )
86                        irc->myhost = g_strdup( ipv6_unwrap( buf ) );
87        }
88#else
89        else if( getsockname( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin_family == AF_INETx )
90        {
91                if( ( peer = gethostbyaddr( (char*) &sock->sin_addr, sizeof( sock->sin_addr ), AF_INETx ) ) )
92                        irc->myhost = g_strdup( peer->h_name );
93                else if( inet_ntop( AF_INETx, &sock->sin_addr, buf, sizeof( buf ) - 1 ) != NULL )
94                        irc->myhost = g_strdup( buf );
95        }
96#endif
97       
98        i = sizeof( *sock );
99#ifdef IPV6
100        if( getpeername( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin6_family == AF_INETx )
101        {
102                if( ( peer = gethostbyaddr( (char*) &sock->sin6_addr, sizeof( sock->sin6_addr ), AF_INETx ) ) )
103                        irc->host = g_strdup( peer->h_name );
104                else if( inet_ntop( AF_INETx, &sock->sin6_addr, buf, sizeof( buf ) - 1 ) != NULL )
105                        irc->host = g_strdup( ipv6_unwrap( buf ) );
106        }
107#else
108        if( getpeername( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin_family == AF_INETx )
109        {
110                if( ( peer = gethostbyaddr( (char*) &sock->sin_addr, sizeof( sock->sin_addr ), AF_INETx ) ) )
111                        irc->host = g_strdup( peer->h_name );
112                else if( inet_ntop( AF_INETx, &sock->sin_addr, buf, sizeof( buf ) - 1 ) != NULL )
113                        irc->host = g_strdup( buf );
114        }
115#endif
116       
117        /* Rare, but possible. */
118        if( !irc->host ) irc->host = g_strdup( "localhost." );
119        if( !irc->myhost ) irc->myhost = g_strdup( "localhost." );
120
121        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
122                irc->ping_source_id = g_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
123       
124        irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
125
126        irc_connection_list = g_slist_append( irc_connection_list, irc );
127       
128        set_add( irc, "away_devoice", "true",  set_eval_away_devoice );
129        set_add( irc, "auto_connect", "true", set_eval_bool );
130        set_add( irc, "auto_reconnect", "false", set_eval_bool );
131        set_add( irc, "auto_reconnect_delay", "300", set_eval_int );
132        set_add( irc, "buddy_sendbuffer", "false", set_eval_bool );
133        set_add( irc, "buddy_sendbuffer_delay", "200", set_eval_int );
134        set_add( irc, "charset", "iso8859-1", set_eval_charset );
135        set_add( irc, "debug", "false", set_eval_bool );
136        set_add( irc, "default_target", "root", NULL );
137        set_add( irc, "display_namechanges", "false", set_eval_bool );
138        set_add( irc, "handle_unknown", "root", NULL );
139        set_add( irc, "lcnicks", "true", set_eval_bool );
140        set_add( irc, "ops", "both", set_eval_ops );
141        set_add( irc, "private", "true", set_eval_bool );
142        set_add( irc, "query_order", "lifo", NULL );
143        set_add( irc, "save_on_quit", "true", set_eval_bool );
144        set_add( irc, "strip_html", "true", NULL );
145        set_add( irc, "to_char", ": ", set_eval_to_char );
146        set_add( irc, "typing_notice", "false", set_eval_bool );
147        set_add( irc, "password", NULL, passchange);
148       
149        conf_loaddefaults( irc );
150       
151        return( irc );
152}
153
154void irc_abort( irc_t *irc )
155{
156        irc->status = USTATUS_SHUTDOWN;
157        if( irc->sendbuffer )
158        {
159                g_source_remove( irc->r_watch_source_id );
160                irc->r_watch_source_id = g_timeout_add_full( G_PRIORITY_HIGH, 1000, (GSourceFunc) irc_free, irc, NULL );
161        }
162        else
163        {
164                irc_free( irc );
165        }
166}
167
168static gboolean irc_free_userhash( gpointer key, gpointer value, gpointer data )
169{
170        g_free( key );
171       
172        return( TRUE );
173}
174
175/* Because we have no garbage collection, this is quite annoying */
176void irc_free(irc_t * irc)
177{
178        account_t *account, *accounttmp;
179        user_t *user, *usertmp;
180        nick_t *nick, *nicktmp;
181        help_t *helpnode, *helpnodetmp;
182        set_t *setnode, *setnodetmp;
183       
184        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
185       
186        if( irc->status >= USTATUS_IDENTIFIED && set_getint( irc, "save_on_quit" ) ) 
187                if( storage_save( irc, TRUE ) != STORAGE_OK )
188                        irc_usermsg( irc, "Error while saving settings!" );
189       
190        if( irc->ping_source_id > 0 )
191                g_source_remove( irc->ping_source_id );
192        g_source_remove( irc->r_watch_source_id );
193        if( irc->w_watch_source_id > 0 )
194                g_source_remove( irc->w_watch_source_id );
195       
196        g_io_channel_unref( irc->io_channel );
197        irc_connection_list = g_slist_remove( irc_connection_list, irc );
198       
199        for (account = irc->accounts; account; account = account->next)
200                if (account->gc)
201                        signoff(account->gc);
202       
203        g_free(irc->sendbuffer);
204        g_free(irc->readbuffer);
205       
206        g_free(irc->nick);
207        g_free(irc->user);
208        g_free(irc->host);
209        g_free(irc->realname);
210        g_free(irc->password);
211       
212        g_free(irc->myhost);
213        g_free(irc->mynick);
214       
215        g_free(irc->channel);
216       
217        while (irc->queries != NULL)
218                query_del(irc, irc->queries);
219       
220        if (irc->accounts != NULL) {
221                account = irc->accounts;
222                while (account != NULL) {
223                        g_free(account->user);
224                        g_free(account->pass);
225                        g_free(account->server);
226                        accounttmp = account;
227                        account = account->next;
228                        g_free(accounttmp);
229                }
230        }
231       
232        if (irc->users != NULL) {
233                user = irc->users;
234                while (user != NULL) {
235                        g_free(user->nick);
236                        g_free(user->away);
237                        g_free(user->handle);
238                        if(user->user!=user->nick) g_free(user->user);
239                        if(user->host!=user->nick) g_free(user->host);
240                        if(user->realname!=user->nick) g_free(user->realname);
241                        gaim_input_remove(user->sendbuf_timer);
242                                       
243                        usertmp = user;
244                        user = user->next;
245                        g_free(usertmp);
246                }
247        }
248       
249        g_hash_table_foreach_remove(irc->userhash, irc_free_userhash, NULL);
250        g_hash_table_destroy(irc->userhash);
251       
252        g_hash_table_foreach_remove(irc->watches, irc_free_userhash, NULL);
253        g_hash_table_destroy(irc->watches);
254       
255        if (irc->nicks != NULL) {
256                nick = irc->nicks;
257                while (nick != NULL) {
258                        g_free(nick->nick);
259                        g_free(nick->handle);
260                                       
261                        nicktmp = nick;
262                        nick = nick->next;
263                        g_free(nicktmp);
264                }
265        }
266        if (irc->help != NULL) {
267                helpnode = irc->help;
268                while (helpnode != NULL) {
269                        g_free(helpnode->string);
270                       
271                        helpnodetmp = helpnode;
272                        helpnode = helpnode->next;
273                        g_free(helpnodetmp);
274                }
275        }
276        if (irc->set != NULL) {
277                setnode = irc->set;
278                while (setnode != NULL) {
279                        g_free(setnode->key);
280                        g_free(setnode->def);
281                        g_free(setnode->value);
282                       
283                        setnodetmp = setnode;
284                        setnode = setnode->next;
285                        g_free(setnodetmp);
286                }
287        }
288        g_free(irc);
289       
290        if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON )
291                g_main_quit( global.loop );
292}
293
294/* USE WITH CAUTION!
295   Sets pass without checking */
296void irc_setpass (irc_t *irc, const char *pass) 
297{
298        if (irc->password) g_free (irc->password);
299       
300        if (pass) {
301                irc->password = g_strdup (pass);
302                irc_usermsg (irc, "Password successfully changed");
303        } else {
304                irc->password = NULL;
305        }
306}
307
308int irc_process( irc_t *irc )
309{
310        char **lines, *temp, **cmd;
311        int i;
312
313        if( irc->readbuffer != NULL )
314        {
315                lines = irc_tokenize( irc->readbuffer );
316               
317                for( i = 0; *lines[i] != '\0'; i ++ )
318                {
319                        if( lines[i+1] == NULL )
320                        {
321                                temp = g_strdup( lines[i] );
322                                g_free( irc->readbuffer );
323                                irc->readbuffer = temp;
324                                i ++;
325                                break;
326                        }                       
327                       
328                        if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
329                                continue;
330                        if( !irc_exec( irc, cmd ) )
331                        {
332                                g_free( cmd );
333                                g_free( lines );
334                                return 0;
335                        }
336                       
337                        g_free( cmd );
338                }
339               
340                if( lines[i] != NULL )
341                {
342                        g_free( irc->readbuffer );
343                        irc->readbuffer = NULL;
344                }
345               
346                g_free( lines );
347        }
348       
349        return 1;       
350}
351
352char **irc_tokenize( char *buffer )
353{
354        int i, j;
355        char **lines;
356
357        /* Count the number of elements we're gonna need. */
358        for( i = 0, j = 1; buffer[i] != '\0'; i ++ )
359        {
360                if( buffer[i] == '\n' )
361                        if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
362                                j ++;
363        }
364       
365        /* Allocate j+1 elements. */
366        lines = g_new( char *, j + 1 );
367       
368        /* NULL terminate our list. */ 
369        lines[j] = NULL;
370       
371        lines[0] = buffer;
372       
373        /* Split the buffer in several strings, using \r\n as our seperator, where \r is optional.
374         * Although this is not in the RFC, some braindead ircds (newnet's) use this, so some clients might too.
375         */
376        for( i = 0, j = 0; buffer[i] != '\0'; i ++)
377        {
378                if( buffer[i] == '\n' )
379                {
380                        buffer[i] = '\0';
381                       
382                        if( i > 0 && buffer[i-1] == '\r' )
383                                buffer[i-1] = '\0';
384                        if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
385                                lines[++j] = buffer + i + 1;
386                }
387        }
388       
389        return( lines );
390}
391
392char **irc_parse_line( char *line )
393{
394        int i, j;
395        char **cmd;
396       
397        /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
398        if( line[0] == ':' )
399        {
400                for( i = 0; line[i] != ' '; i ++ );
401                line = line + i;
402        }
403        for( i = 0; line[i] == ' '; i ++ );
404        line = line + i;
405       
406        /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
407        if( line[0] == '\0')
408                return NULL;
409       
410        /* Count the number of char **cmd elements we're going to need. */
411        j = 1;
412        for( i = 0; line[i] != '\0'; i ++ )
413        {
414                if( line[i] == ' ' )
415                {
416                        j ++;
417                       
418                        if( line[i+1] == ':' )
419                                break;
420                }
421        }       
422
423        /* Allocate the space we need. */
424        cmd = g_new( char *, j + 1 );
425        cmd[j] = NULL;
426       
427        /* Do the actual line splitting, format is:
428         * Input: "PRIVMSG #bitlbee :foo bar"
429         * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
430         */
431
432        cmd[0] = line;
433        for( i = 0, j = 0; line[i] != '\0'; i ++ )
434        {
435                if( line[i] == ' ' )
436                {
437                        line[i] = '\0';
438                        cmd[++j] = line + i + 1;
439                       
440                        if( line[i+1] == ':' )
441                        {
442                                cmd[j] ++;
443                                break;
444                        }
445                }
446        }
447       
448        return cmd;
449}
450
451char *irc_build_line( char **cmd )
452{
453        int i, len;
454        char *s;
455       
456        if( cmd[0] == NULL )
457                return NULL;
458       
459        len = 1;
460        for( i = 0; cmd[i]; i ++ )
461                len += strlen( cmd[i] ) + 1;
462       
463        if( strchr( cmd[i-1], ' ' ) != NULL )
464                len ++;
465       
466        s = g_new0( char, len + 1 );
467        for( i = 0; cmd[i]; i ++ )
468        {
469                if( cmd[i+1] == NULL && strchr( cmd[i], ' ' ) != NULL )
470                        strcat( s, ":" );
471               
472                strcat( s, cmd[i] );
473               
474                if( cmd[i+1] )
475                        strcat( s, " " );
476        }
477        strcat( s, "\r\n" );
478       
479        return s;
480}
481
482void irc_reply( irc_t *irc, int code, char *format, ... )
483{
484        char text[IRC_MAX_LINE];
485        va_list params;
486       
487        va_start( params, format );
488        g_vsnprintf( text, IRC_MAX_LINE, format, params );
489        va_end( params );
490        irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
491       
492        return;
493}
494
495int irc_usermsg( irc_t *irc, char *format, ... )
496{
497        char text[1024];
498        va_list params;
499        char is_private = 0;
500        user_t *u;
501       
502        u = user_find( irc, irc->mynick );
503        if( u ) is_private = u->is_private;
504       
505        va_start( params, format );
506        g_vsnprintf( text, sizeof( text ), format, params );
507        va_end( params );
508       
509        return( irc_msgfrom( irc, u->nick, text ) );
510}
511
512void irc_write( irc_t *irc, char *format, ... ) 
513{
514        va_list params;
515
516        va_start( params, format );
517        irc_vawrite( irc, format, params );     
518        va_end( params );
519
520        return;
521
522}
523
524void irc_vawrite( irc_t *irc, char *format, va_list params )
525{
526        int size;
527        char line[IRC_MAX_LINE];
528       
529        if( irc->quit )
530                return;
531
532        g_vsnprintf( line, IRC_MAX_LINE - 3, format, params );
533
534        strip_newlines( line );
535        strcat( line, "\r\n" );
536
537        if( irc->sendbuffer != NULL ) {
538                size = strlen( irc->sendbuffer ) + strlen( line );
539                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
540                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
541        }
542        else 
543                irc->sendbuffer = g_strdup(line);       
544       
545        if( irc->w_watch_source_id == 0 )
546        {
547                irc->w_watch_source_id = g_io_add_watch( irc->io_channel, G_IO_OUT, bitlbee_io_current_client_write, irc );
548        }
549       
550        return;
551}
552
553void irc_write_all( int now, char *format, ... )
554{
555        va_list params;
556        GSList *temp;   
557       
558        va_start( params, format );
559       
560        temp = irc_connection_list;
561        while( temp != NULL )
562        {
563                irc_t *irc = temp->data;
564               
565                if( now )
566                {
567                        g_free( irc->sendbuffer );
568                        irc->sendbuffer = g_strdup( "\r\n" );
569                }
570                irc_vawrite( temp->data, format, params );
571                if( now )
572                {
573                        bitlbee_io_current_client_write( irc->io_channel, G_IO_OUT, irc );
574                }
575                temp = temp->next;
576        }
577       
578        va_end( params );
579        return;
580} 
581
582void irc_names( irc_t *irc, char *channel )
583{
584        user_t *u = irc->users;
585        char *s;
586        int control = ( g_strcasecmp( channel, irc->channel ) == 0 );
587        struct conversation *c = NULL;
588       
589        if( !control )
590                c = conv_findchannel( channel );
591       
592        /* RFC's say there is no error reply allowed on NAMES, so when the
593           channel is invalid, just give an empty reply. */
594       
595        if( control || c ) while( u )
596        {
597                if( u->online )
598                {
599                        if( u->gc && control )
600                        {
601                                if( set_getint( irc, "away_devoice" ) && !u->away )
602                                        s = "+";
603                                else
604                                        s = "";
605                               
606                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
607                        }
608                        else if( !u->gc )
609                        {
610                                if( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "root" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
611                                        s = "@";
612                                else if( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "user" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) )
613                                        s = "@";
614                                else
615                                        s = "";
616                               
617                                irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick );
618                        }
619                }
620               
621                u = u->next;
622        }
623       
624        /* For non-controlchannel channels (group conversations) only root and
625           you are listed now. Time to show the channel people: */
626        if( !control && c )
627        {
628                GList *l;
629               
630                for( l = c->in_room; l; l = l->next )
631                        if( ( u = user_findhandle( c->gc, l->data ) ) )
632                                irc_reply( irc, 353, "@ %s :%s%s", channel, "", u->nick );
633        }
634       
635        irc_reply( irc, 366, "%s :End of /NAMES list", channel );
636}
637
638int irc_check_login( irc_t *irc )
639{
640        if( irc->user && irc->nick )
641        {
642                if( global.conf->authmode == AUTHMODE_CLOSED && irc->status < USTATUS_AUTHORIZED )
643                {
644                        irc_reply( irc, 464, ":This server is password-protected." );
645                        return 0;
646                }
647                else
648                {
649                        irc_login( irc );
650                        return 1;
651                }
652        }
653        else
654        {
655                /* More information needed. */
656                return 0;
657        }
658}
659
660void irc_login( irc_t *irc )
661{
662        user_t *u;
663       
664        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
665        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
666        irc_reply( irc,   3, ":%s", IRCD_INFO );
667        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
668        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 );
669        irc_motd( irc );
670        irc_umode_set( irc, "+" UMODE, 1 );
671
672        u = user_add( irc, irc->mynick );
673        u->host = g_strdup( irc->myhost );
674        u->realname = g_strdup( ROOT_FN );
675        u->online = 1;
676        u->send_handler = root_command_string;
677        u->is_private = 0; /* [SH] The channel is root's personal playground. */
678        irc_spawn( irc, u );
679       
680        u = user_add( irc, NS_NICK );
681        u->host = g_strdup( irc->myhost );
682        u->realname = g_strdup( ROOT_FN );
683        u->online = 0;
684        u->send_handler = root_command_string;
685        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
686       
687        u = user_add( irc, irc->nick );
688        u->user = g_strdup( irc->user );
689        u->host = g_strdup( irc->host );
690        u->realname = g_strdup( irc->realname );
691        u->online = 1;
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        if( global.conf->runmode == RUNMODE_FORKDAEMON )
697                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
698       
699        irc->status = USTATUS_LOGGED_IN;
700}
701
702void irc_motd( irc_t *irc )
703{
704        int fd;
705       
706        fd = open( global.conf->motdfile, O_RDONLY );
707        if( fd == -1 )
708        {
709                irc_reply( irc, 422, ":We don't need MOTDs." );
710        }
711        else
712        {
713                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
714                char *add, max;
715                int len;
716               
717                linebuf[79] = len = 0;
718                max = sizeof( linebuf ) - 1;
719               
720                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
721                while( read( fd, linebuf + len, 1 ) == 1 )
722                {
723                        if( linebuf[len] == '\n' || len == max )
724                        {
725                                linebuf[len] = 0;
726                                irc_reply( irc, 372, ":- %s", linebuf );
727                                len = 0;
728                        }
729                        else if( linebuf[len] == '%' )
730                        {
731                                read( fd, linebuf + len, 1 );
732                                if( linebuf[len] == 'h' )
733                                        add = irc->myhost;
734                                else if( linebuf[len] == 'v' )
735                                        add = BITLBEE_VERSION;
736                                else if( linebuf[len] == 'n' )
737                                        add = irc->nick;
738                                else
739                                        add = "%";
740                               
741                                strncpy( linebuf + len, add, max - len );
742                                while( linebuf[++len] );
743                        }
744                        else if( len < max )
745                        {
746                                len ++;
747                        }
748                }
749                irc_reply( irc, 376, ":End of MOTD" );
750                closesocket( fd );
751        }
752}
753
754void irc_topic( irc_t *irc, char *channel )
755{
756        if( g_strcasecmp( channel, irc->channel ) == 0 )
757        {
758                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
759        }
760        else
761        {
762                struct conversation *c = conv_findchannel( channel );
763               
764                if( c )
765                        irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
766                else
767                        irc_reply( irc, 331, "%s :No topic for this channel" );
768        }
769}
770
771void irc_umode_set( irc_t *irc, char *s, int allow_priv )
772{
773        /* allow_priv: Set to 0 if s contains user input, 1 if you want
774           to set a "privileged" mode (+o, +R, etc). */
775        char m[256], st = 1, *t;
776        int i;
777       
778        memset( m, 0, sizeof( m ) );
779       
780        for( t = irc->umode; *t; t ++ )
781                m[(int)*t] = 1;
782       
783        for( t = s; *t; t ++ )
784        {
785                if( *t == '+' || *t == '-' )
786                        st = *t == '+';
787                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
788                        m[(int)*t] = st;
789        }
790       
791        memset( irc->umode, 0, sizeof( irc->umode ) );
792       
793        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
794                if( m[i] )
795                        irc->umode[strlen(irc->umode)] = i;
796       
797        irc_reply( irc, 221, "+%s", irc->umode );
798}
799
800void irc_spawn( irc_t *irc, user_t *u )
801{
802        irc_join( irc, u, irc->channel );
803}
804
805void irc_join( irc_t *irc, user_t *u, char *channel )
806{
807        char *nick;
808       
809        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
810                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
811       
812        if( nick_cmp( u->nick, irc->nick ) == 0 )
813        {
814                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
815                irc_names( irc, channel );
816                irc_topic( irc, channel );
817        }
818       
819        nick = g_strdup( u->nick );
820        nick_lc( nick );
821        if( g_hash_table_lookup( irc->watches, nick ) )
822        {
823                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged online" );
824        }
825        g_free( nick );
826}
827
828void irc_part( irc_t *irc, user_t *u, char *channel )
829{
830        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
831}
832
833void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
834{
835        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
836}
837
838void irc_kill( irc_t *irc, user_t *u )
839{
840        char *nick;
841       
842        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
843       
844        nick = g_strdup( u->nick );
845        nick_lc( nick );
846        if( g_hash_table_lookup( irc->watches, nick ) )
847        {
848                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged offline" );
849        }
850        g_free( nick );
851}
852
853int irc_send( irc_t *irc, char *nick, char *s, int flags )
854{
855        struct conversation *c = NULL;
856        user_t *u = NULL;
857       
858        if( *nick == '#' || *nick == '&' )
859        {
860                if( !( c = conv_findchannel( nick ) ) )
861                {
862                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
863                        return( 0 );
864                }
865        }
866        else
867        {
868                u = user_find( irc, nick );
869               
870                if( !u )
871                {
872                        if( irc->is_private )
873                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
874                        else
875                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
876                        return( 0 );
877                }
878        }
879       
880        if( *s == 1 && s[strlen(s)-1] == 1 )
881        {
882                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
883                {
884                        if( s[7] == ' ' ) s ++;
885                        s += 3;
886                        *(s++) = '/';
887                        *(s++) = 'm';
888                        *(s++) = 'e';
889                        *(s++) = ' ';
890                        s -= 4;
891                        s[strlen(s)-1] = 0;
892                }
893                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
894                {
895                        u = user_find( irc, irc->mynick );
896                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
897                        return( 1 );
898                }
899                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
900                {
901                        u = user_find( irc, irc->mynick );
902                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
903                        return( 1 );
904                }
905                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
906                {
907                        if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 )
908                        {
909                                time_t current_typing_notice = time( NULL );
910                               
911                                if( current_typing_notice - u->last_typing_notice >= 5 )
912                                {
913                                        u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' );
914                                        u->last_typing_notice = current_typing_notice;
915                                }
916                        }
917                        return( 1 );
918                }
919                else
920                {
921                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
922                        return( 0 );
923                }
924        }
925       
926        if( u )
927        {
928                /* For the next message, we probably do have to send new notices... */
929                u->last_typing_notice = 0;
930                u->is_private = irc->is_private;
931               
932                if( u->is_private )
933                {
934                        if( !u->online )
935                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
936                        else if( u->away )
937                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
938                }
939               
940                if( u->send_handler )
941                        return( u->send_handler( irc, u, s, flags ) );
942        }
943        else if( c && c->gc && c->gc->prpl )
944        {
945                return( serv_send_chat( irc, c->gc, c->id, s ) );
946        }
947       
948        return( 0 );
949}
950
951gboolean buddy_send_handler_delayed( gpointer data )
952{
953        user_t *u = data;
954       
955        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
956        serv_send_im( u->gc->irc, u, u->sendbuf, u->sendbuf_flags );
957       
958        g_free( u->sendbuf );
959        u->sendbuf = NULL;
960        u->sendbuf_len = 0;
961        u->sendbuf_timer = 0;
962        u->sendbuf_flags = 0;
963       
964        return( FALSE );
965}
966
967int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
968{
969        if( !u || !u->gc ) return( 0 );
970       
971        if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 )
972        {
973                int delay;
974               
975                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
976                {
977                        //Flush the buffer
978                        g_source_remove( u->sendbuf_timer );
979                        buddy_send_handler_delayed( u );
980                }
981
982                if( u->sendbuf_len == 0 )
983                {
984                        u->sendbuf_len = strlen( msg ) + 2;
985                        u->sendbuf = g_new (char, u->sendbuf_len );
986                        u->sendbuf[0] = 0;
987                        u->sendbuf_flags = flags;
988                }
989                else
990                {
991                        u->sendbuf_len += strlen( msg ) + 1;
992                        u->sendbuf = g_renew ( char, u->sendbuf, u->sendbuf_len );
993                }
994               
995                strcat( u->sendbuf, msg );
996                strcat( u->sendbuf, "\n" );
997               
998                delay = set_getint( irc, "buddy_sendbuffer_delay" );
999                if( delay <= 5 )
1000                        delay *= 1000;
1001               
1002                if( u->sendbuf_timer > 0 )
1003                        g_source_remove( u->sendbuf_timer );
1004                u->sendbuf_timer = g_timeout_add( delay, buddy_send_handler_delayed, u );
1005               
1006                return( 1 );
1007        }
1008        else
1009        {
1010                return( serv_send_im( irc, u, msg, flags ) );
1011        }
1012}
1013
1014int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1015{
1016        char last = 0;
1017        char *s = msg, *line = msg;
1018       
1019        /* The almighty linesplitter .. woohoo!! */
1020        while( !last )
1021        {
1022                if( *s == '\r' && *(s+1) == '\n' )
1023                        *(s++) = 0;
1024                if( *s == '\n' )
1025                {
1026                        last = s[1] == 0;
1027                        *s = 0;
1028                }
1029                else
1030                {
1031                        last = s[0] == 0;
1032                }
1033                if( *s == 0 )
1034                {
1035                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1036                        {
1037                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1038                                           type, to, line + 4 );
1039                        }
1040                        else
1041                        {
1042                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
1043                                           type, to, prefix ? prefix : "", line );
1044                        }
1045                        line = s + 1;
1046                }
1047                s ++;
1048        }
1049       
1050        return( 1 );
1051}
1052
1053int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1054{
1055        user_t *u = user_find( irc, nick );
1056        static char *prefix = NULL;
1057       
1058        if( !u ) return( 0 );
1059        if( prefix && *prefix ) g_free( prefix );
1060       
1061        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1062        {
1063                int len = strlen( irc->nick) + 3;
1064                prefix = g_new (char, len );
1065                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) );
1066                prefix[len-1] = 0;
1067        }
1068        else
1069        {
1070                prefix = "";
1071        }
1072       
1073        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1074}
1075
1076int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1077{
1078        user_t *u = user_find( irc, nick );
1079       
1080        if( u )
1081                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1082        else
1083                return( 0 );
1084}
1085
1086/* Returns 0 if everything seems to be okay, a number >0 when there was a
1087   timeout. The number returned is the number of seconds we received no
1088   pongs from the user. When not connected yet, we don't ping but drop the
1089   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
1090static gboolean irc_userping( gpointer _irc )
1091{
1092        irc_t *irc = _irc;
1093        int rv = 0;
1094       
1095        if( irc->status < USTATUS_LOGGED_IN )
1096        {
1097                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1098                        rv = gettime() - irc->last_pong;
1099        }
1100        else
1101        {
1102                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1103                {
1104                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1105                        irc->pinging = 1;
1106                }
1107                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1108                {
1109                        rv = gettime() - irc->last_pong;
1110                }
1111        }
1112       
1113        if( rv > 0 )
1114        {
1115                irc_write( irc, "ERROR :Closing Link: Ping Timeout: %d seconds", rv );
1116                irc_free( irc );
1117                return FALSE;
1118        }
1119       
1120        return TRUE;
1121}
Note: See TracBrowser for help on using the repository browser.