source: irc.c @ 7d31002

Last change on this file since 7d31002 was 7d31002, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-03-31T21:09:36Z

Tested and fixed one issue, and got rid of one more superfluous iconv call.

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