source: irc.c @ de3e100

Last change on this file since de3e100 was de3e100, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-14T20:31:59Z

Separated the IRC line splitting/parsing code (to use it for IPC too), and improved the splitting to deal with empty arguments.

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