source: irc.c @ 55ec2d6

Last change on this file since 55ec2d6 was 55ec2d6, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-20T12:22:30Z

Merging IPC branch, it's too different from the main code to keep it
separated (and it's pretty stable now). Have fun. :-)

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