source: irc.c @ 7deb447

Last change on this file since 7deb447 was fc630f9, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-05-23T08:31:04Z

Silenced all compiler warnings that appeared after previous commit.

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