source: irc.c @ 27db433

Last change on this file since 27db433 was 27db433, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-15T17:36:18Z

implement background keygen via child process

  • Property mode set to 100644
File size: 32.8 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"
[522a00f]30#include <sys/types.h>
31#include <sys/wait.h>
[b7d3cc34]32
[ba9edaa]33static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond );
[b7d3cc34]34
35GSList *irc_connection_list = NULL;
36
[0383943]37static char *passchange( set_t *set, char *value )
[c2295f7]38{
[0383943]39        irc_t *irc = set->data;
40       
[88086db]41        irc_setpass( irc, value );
42        irc_usermsg( irc, "Password successfully changed" );
43        return NULL;
[c2295f7]44}
45
[b7d3cc34]46irc_t *irc_new( int fd )
47{
[e4d6271]48        irc_t *irc;
[e9b755e]49        struct sockaddr_storage sock;
[7435ccf]50        socklen_t socklen = sizeof( sock );
[e4d6271]51       
52        irc = g_new0( irc_t, 1 );
[b7d3cc34]53       
54        irc->fd = fd;
[a0d04d6]55        sock_make_nonblocking( irc->fd );
56       
[ba9edaa]57        irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc );
[b7d3cc34]58       
59        irc->status = USTATUS_OFFLINE;
60        irc->last_pong = gettime();
61       
62        irc->userhash = g_hash_table_new( g_str_hash, g_str_equal );
63        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
64       
65        strcpy( irc->umode, UMODE );
66        irc->mynick = g_strdup( ROOT_NICK );
67        irc->channel = g_strdup( ROOT_CHAN );
68       
69        if( global.conf->hostname )
70        {
71                irc->myhost = g_strdup( global.conf->hostname );
72        }
[7435ccf]73        else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 ) 
[b7d3cc34]74        {
[2231302]75                char buf[NI_MAXHOST+1];
[e9b755e]76
[2231302]77                if( getnameinfo( (struct sockaddr *) &sock, socklen, buf,
78                                 NI_MAXHOST, NULL, -1, 0 ) == 0 )
79                {
80                        irc->myhost = g_strdup( ipv6_unwrap( buf ) );
81                }
[b7d3cc34]82        }
83       
[7435ccf]84        if( getpeername( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 )
[b7d3cc34]85        {
[2231302]86                char buf[NI_MAXHOST+1];
[e9b755e]87
[2231302]88                if( getnameinfo( (struct sockaddr *)&sock, socklen, buf,
89                                 NI_MAXHOST, NULL, -1, 0 ) == 0 )
90                {
[2a6ca4f]91                        irc->host = g_strdup( ipv6_unwrap( buf ) );
[2231302]92                }
[b7d3cc34]93        }
94       
[3e1e11af]95        if( irc->host == NULL )
96                irc->host = g_strdup( "localhost.localdomain" );
97        if( irc->myhost == NULL )
98                irc->myhost = g_strdup( "localhost.localdomain" );
99       
[b7d3cc34]100        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
[ba9edaa]101                irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
[b7d3cc34]102       
103        irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
104
105        irc_connection_list = g_slist_append( irc_connection_list, irc );
[5a71d9c]106
[5c9512f]107        set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
108        set_add( &irc->set, "auto_reconnect", "false", set_eval_bool, irc );
109        set_add( &irc->set, "auto_reconnect_delay", "300", set_eval_int, irc );
110        set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
111        set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
[8d32b9e]112        set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc );
[5f4eede]113        set_add( &irc->set, "color_encrypted", "true", set_eval_bool, irc );
[5c9512f]114        set_add( &irc->set, "debug", "false", set_eval_bool, irc );
115        set_add( &irc->set, "default_target", "root", NULL, irc );
116        set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
117        set_add( &irc->set, "handle_unknown", "root", NULL, irc );
[5f4eede]118        set_add( &irc->set, "halfop_buddies", "encrypted", set_eval_halfop_buddies, irc );
[5c9512f]119        set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
[5f4eede]120        set_add( &irc->set, "op_buddies", "trusted", set_eval_op_buddies, irc );
[5a71d9c]121        set_add( &irc->set, "op_root", "true", set_eval_op_root, irc );
122        set_add( &irc->set, "op_user", "true", set_eval_op_user, irc );
[e2b15bb]123        set_add( &irc->set, "otr_policy", "opportunistic", set_eval_otr_policy, irc );
[5c9512f]124        set_add( &irc->set, "password", NULL, passchange, irc );
125        set_add( &irc->set, "private", "true", set_eval_bool, irc );
126        set_add( &irc->set, "query_order", "lifo", NULL, irc );
127        set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
[1186382]128        set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
[5c9512f]129        set_add( &irc->set, "strip_html", "true", NULL, irc );
130        set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
131        set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc );
[5a71d9c]132        set_add( &irc->set, "voice_buddies", "notaway",  set_eval_voice_buddies, irc );
[b7d3cc34]133       
134        conf_loaddefaults( irc );
[764c7d1]135
136        irc->otr_us = otrl_userstate_create();
[522a00f]137        irc->otr_keygen = 0;
[27db433]138        irc->otr_to = NULL;
139        irc->otr_from = NULL;
[522a00f]140        irc->otr_ntodo = 0;
[b7d3cc34]141       
142        return( irc );
143}
144
[f73b969]145/* immed=1 makes this function pretty much equal to irc_free(), except that
146   this one will "log". In case the connection is already broken and we
147   shouldn't try to write to it. */
[fc50d48]148void irc_abort( irc_t *irc, int immed, char *format, ... )
[c1826c6]149{
[fc50d48]150        if( format != NULL )
151        {
[f73b969]152                va_list params;
[fc50d48]153                char *reason;
154               
155                va_start( params, format );
[f73b969]156                reason = g_strdup_vprintf( format, params );
[fc50d48]157                va_end( params );
158               
159                if( !immed )
160                        irc_write( irc, "ERROR :Closing link: %s", reason );
161               
162                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
[f73b969]163                                   irc->nick ? irc->nick : "(NONE)", irc->host, reason );
[fc50d48]164               
165                g_free( reason );
166        }
167        else
168        {
169                if( !immed )
170                        irc_write( irc, "ERROR :Closing link" );
171               
172                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
[f73b969]173                                   irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" );
[fc50d48]174        }
175       
[79e826a]176        irc->status |= USTATUS_SHUTDOWN;
[fc50d48]177        if( irc->sendbuffer && !immed )
[c1826c6]178        {
[fc50d48]179                /* We won't read from this socket anymore. Instead, we'll connect a timer
180                   to it that should shut down the connection in a second, just in case
181                   bitlbee_.._write doesn't do it first. */
182               
[ba9edaa]183                b_event_remove( irc->r_watch_source_id );
184                irc->r_watch_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
[c1826c6]185        }
186        else
187        {
188                irc_free( irc );
189        }
190}
191
[36fa9bd]192static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
[b7d3cc34]193{
194        g_free( key );
195       
196        return( TRUE );
197}
198
199/* Because we have no garbage collection, this is quite annoying */
200void irc_free(irc_t * irc)
201{
[5b52a48]202        account_t *account;
[b7d3cc34]203        user_t *user, *usertmp;
204        help_t *helpnode, *helpnodetmp;
205       
206        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
207       
[d5ccd83]208        if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) ) 
[b73ac9c]209                if( storage_save( irc, TRUE ) != STORAGE_OK )
[b7d3cc34]210                        irc_usermsg( irc, "Error while saving settings!" );
211       
[bd9b00f]212        closesocket( irc->fd );
213       
[b7d3cc34]214        if( irc->ping_source_id > 0 )
[ba9edaa]215                b_event_remove( irc->ping_source_id );
216        b_event_remove( irc->r_watch_source_id );
[b7d3cc34]217        if( irc->w_watch_source_id > 0 )
[ba9edaa]218                b_event_remove( irc->w_watch_source_id );
[9c62a7c]219       
[b7d3cc34]220        irc_connection_list = g_slist_remove( irc_connection_list, irc );
221       
[55cc2be3]222        for (account = irc->accounts; account; account = account->next) {
[0da65d5]223                if (account->ic) {
[c2fb3809]224                        imc_logout(account->ic, TRUE);
[c99af3a]225                } else if (account->reconnect) {
[6adcb6c6]226                        cancel_auto_reconnect(account);
[c99af3a]227                }
[55cc2be3]228        }
[b7d3cc34]229       
230        g_free(irc->sendbuffer);
231        g_free(irc->readbuffer);
232       
233        g_free(irc->nick);
234        g_free(irc->user);
235        g_free(irc->host);
236        g_free(irc->realname);
237        g_free(irc->password);
238       
239        g_free(irc->myhost);
240        g_free(irc->mynick);
241       
242        g_free(irc->channel);
243       
244        while (irc->queries != NULL)
245                query_del(irc, irc->queries);
246       
[5b52a48]247        while (irc->accounts)
[0da65d5]248                if (irc->accounts->ic == NULL)
[f959495]249                        account_del(irc, irc->accounts);
250                else
251                        /* Nasty hack, but account_del() doesn't work in this
252                           case and we don't want infinite loops, do we? ;-) */
253                        irc->accounts = irc->accounts->next;
[5b52a48]254       
255        while (irc->set)
256                set_del(&irc->set, irc->set->key);
[b7d3cc34]257       
258        if (irc->users != NULL) {
259                user = irc->users;
260                while (user != NULL) {
261                        g_free(user->nick);
262                        g_free(user->away);
263                        g_free(user->handle);
264                        if(user->user!=user->nick) g_free(user->user);
265                        if(user->host!=user->nick) g_free(user->host);
266                        if(user->realname!=user->nick) g_free(user->realname);
[ba9edaa]267                        b_event_remove(user->sendbuf_timer);
[b7d3cc34]268                                       
269                        usertmp = user;
270                        user = user->next;
271                        g_free(usertmp);
272                }
273        }
274       
[36fa9bd]275        g_hash_table_foreach_remove(irc->userhash, irc_free_hashkey, NULL);
[b7d3cc34]276        g_hash_table_destroy(irc->userhash);
277       
[36fa9bd]278        g_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL);
[b7d3cc34]279        g_hash_table_destroy(irc->watches);
280       
281        if (irc->help != NULL) {
282                helpnode = irc->help;
283                while (helpnode != NULL) {
284                        g_free(helpnode->string);
285                       
286                        helpnodetmp = helpnode;
287                        helpnode = helpnode->next;
288                        g_free(helpnodetmp);
289                }
290        }
[764c7d1]291       
292        otrl_userstate_free(irc->otr_us);
[522a00f]293        if(irc->otr_keygen) {
294                kill(irc->otr_keygen, SIGTERM);
295                waitpid(irc->otr_keygen, NULL, 0);
296                /* TODO: remove stale keygen tempfiles */
297        }
[764c7d1]298       
[b7d3cc34]299        g_free(irc);
300       
[d25f6fc]301        if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON )
[ba9edaa]302                b_main_quit();
[b7d3cc34]303}
304
[7cad7b4]305/* USE WITH CAUTION!
306   Sets pass without checking */
307void irc_setpass (irc_t *irc, const char *pass) 
308{
[6e1fed7]309        g_free (irc->password);
[7cad7b4]310       
311        if (pass) {
312                irc->password = g_strdup (pass);
313        } else {
314                irc->password = NULL;
315        }
316}
317
[f73b969]318void irc_process( irc_t *irc )
[b7d3cc34]319{
[e27661d]320        char **lines, *temp, **cmd, *cs;
[b7d3cc34]321        int i;
322
[de3e100]323        if( irc->readbuffer != NULL )
324        {
325                lines = irc_tokenize( irc->readbuffer );
326               
327                for( i = 0; *lines[i] != '\0'; i ++ )
328                {
[7d31002]329                        char conv[IRC_MAX_LINE+1];
330                       
[e27661d]331                        /* [WvG] Because irc_tokenize splits at every newline, the lines[] list
332                            should end with an empty string. This is why this actually works.
333                            Took me a while to figure out, Maurits. :-P */
[de3e100]334                        if( lines[i+1] == NULL )
335                        {
[b7d3cc34]336                                temp = g_strdup( lines[i] );
337                                g_free( irc->readbuffer );
338                                irc->readbuffer = temp;
[de3e100]339                                i ++;
[b7d3cc34]340                                break;
[e27661d]341                        }
342                       
[94d52d64]343                        if( ( cs = set_getstr( &irc->set, "charset" ) ) )
[e27661d]344                        {
345                                conv[IRC_MAX_LINE] = 0;
[94d52d64]346                                if( do_iconv( cs, "UTF-8", lines[i], conv, 0, IRC_MAX_LINE - 2 ) == -1 )
347                                {
[fc0cf92]348                                        /* GLib can do strange things if things are not in the expected charset,
349                                           so let's be a little bit paranoid here: */
[94d52d64]350                                        if( irc->status & USTATUS_LOGGED_IN )
[fc0cf92]351                                        {
[43462708]352                                                irc_usermsg( irc, "Error: Charset mismatch detected. The charset "
[94d52d64]353                                                                  "setting is currently set to %s, so please make "
354                                                                  "sure your IRC client will send and accept text in "
355                                                                  "that charset, or tell BitlBee which charset to "
356                                                                  "expect by changing the charset setting. See "
357                                                                  "`help set charset' for more information. Your "
358                                                                  "message was ignored.", cs );
[fc0cf92]359                                                *conv = 0;
360                                        }
361                                        else
362                                        {
363                                                irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
364                                                           "Warning: invalid (non-UTF8) characters received at login time." );
365                                               
366                                                strncpy( conv, lines[i], IRC_MAX_LINE );
367                                                for( temp = conv; *temp; temp ++ )
368                                                        if( *temp & 0x80 )
369                                                                *temp = '?';
370                                        }
[94d52d64]371                                }
372                                lines[i] = conv;
[e27661d]373                        }
[de3e100]374                       
[0431ea1]375                        if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
[de3e100]376                                continue;
[f73b969]377                        irc_exec( irc, cmd );
378                       
379                        g_free( cmd );
380                       
381                        /* Shouldn't really happen, but just in case... */
382                        if( !g_slist_find( irc_connection_list, irc ) )
[de3e100]383                        {
[b7d3cc34]384                                g_free( lines );
[f73b969]385                                return;
[b7d3cc34]386                        }
387                }
[de3e100]388               
389                if( lines[i] != NULL )
390                {
391                        g_free( irc->readbuffer );
[0431ea1]392                        irc->readbuffer = NULL;
[b7d3cc34]393                }
[de3e100]394               
[b7d3cc34]395                g_free( lines );
396        }
397}
398
[e27661d]399/* Splits a long string into separate lines. The array is NULL-terminated and, unless the string
400   contains an incomplete line at the end, ends with an empty string. */
[b7d3cc34]401char **irc_tokenize( char *buffer )
402{
403        int i, j;
404        char **lines;
405
406        /* Count the number of elements we're gonna need. */
[de3e100]407        for( i = 0, j = 1; buffer[i] != '\0'; i ++ )
408        {
409                if( buffer[i] == '\n' )
410                        if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
411                                j ++;
[b7d3cc34]412        }
413       
414        /* Allocate j+1 elements. */
[de3e100]415        lines = g_new( char *, j + 1 );
[b7d3cc34]416       
417        /* NULL terminate our list. */ 
[de3e100]418        lines[j] = NULL;
[b7d3cc34]419       
[de3e100]420        lines[0] = buffer;
[b7d3cc34]421       
422        /* Split the buffer in several strings, using \r\n as our seperator, where \r is optional.
423         * Although this is not in the RFC, some braindead ircds (newnet's) use this, so some clients might too.
424         */
[de3e100]425        for( i = 0, j = 0; buffer[i] != '\0'; i ++)
426        {
427                if( buffer[i] == '\n' )
428                {
429                        buffer[i] = '\0';
430                       
431                        if( i > 0 && buffer[i-1] == '\r' )
432                                buffer[i-1] = '\0';
433                        if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
434                                lines[++j] = buffer + i + 1;
[b7d3cc34]435                }
436        }
[de3e100]437       
438        return( lines );
[b7d3cc34]439}
440
[e27661d]441/* Split an IRC-style line into little parts/arguments. */
[0431ea1]442char **irc_parse_line( char *line )
[b7d3cc34]443{
444        int i, j;
445        char **cmd;
446       
447        /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
[de3e100]448        if( line[0] == ':' )
449        {
450                for( i = 0; line[i] != ' '; i ++ );
451                line = line + i;
[b7d3cc34]452        }
[de3e100]453        for( i = 0; line[i] == ' '; i ++ );
454        line = line + i;
455       
[b7d3cc34]456        /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
[de3e100]457        if( line[0] == '\0')
458                return NULL;
459       
460        /* Count the number of char **cmd elements we're going to need. */
461        j = 1;
462        for( i = 0; line[i] != '\0'; i ++ )
463        {
464                if( line[i] == ' ' )
465                {
466                        j ++;
[b7d3cc34]467                       
[de3e100]468                        if( line[i+1] == ':' )
469                                break;
470                }
[b7d3cc34]471        }       
472
473        /* Allocate the space we need. */
[de3e100]474        cmd = g_new( char *, j + 1 );
475        cmd[j] = NULL;
[b7d3cc34]476       
477        /* Do the actual line splitting, format is:
478         * Input: "PRIVMSG #bitlbee :foo bar"
479         * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
480         */
481
[de3e100]482        cmd[0] = line;
483        for( i = 0, j = 0; line[i] != '\0'; i ++ )
[b7d3cc34]484        {
[de3e100]485                if( line[i] == ' ' )
[b7d3cc34]486                {
[de3e100]487                        line[i] = '\0';
488                        cmd[++j] = line + i + 1;
[b7d3cc34]489                       
[de3e100]490                        if( line[i+1] == ':' )
[b7d3cc34]491                        {
[de3e100]492                                cmd[j] ++;
[b7d3cc34]493                                break;
494                        }
495                }
496        }
497       
[de3e100]498        return cmd;
[b7d3cc34]499}
500
[e27661d]501/* Converts such an array back into a command string. Mainly used for the IPC code right now. */
[74c119d]502char *irc_build_line( char **cmd )
503{
504        int i, len;
505        char *s;
[b7d3cc34]506       
[74c119d]507        if( cmd[0] == NULL )
508                return NULL;
[b7d3cc34]509       
[74c119d]510        len = 1;
511        for( i = 0; cmd[i]; i ++ )
512                len += strlen( cmd[i] ) + 1;
513       
514        if( strchr( cmd[i-1], ' ' ) != NULL )
515                len ++;
516       
517        s = g_new0( char, len + 1 );
518        for( i = 0; cmd[i]; i ++ )
[b7d3cc34]519        {
[74c119d]520                if( cmd[i+1] == NULL && strchr( cmd[i], ' ' ) != NULL )
521                        strcat( s, ":" );
[b7d3cc34]522               
[74c119d]523                strcat( s, cmd[i] );
[b7d3cc34]524               
[74c119d]525                if( cmd[i+1] )
526                        strcat( s, " " );
[b7d3cc34]527        }
[74c119d]528        strcat( s, "\r\n" );
[b7d3cc34]529       
[74c119d]530        return s;
[b7d3cc34]531}
532
533void irc_reply( irc_t *irc, int code, char *format, ... )
534{
535        char text[IRC_MAX_LINE];
536        va_list params;
537       
538        va_start( params, format );
539        g_vsnprintf( text, IRC_MAX_LINE, format, params );
540        va_end( params );
541        irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
542       
543        return;
544}
545
546int irc_usermsg( irc_t *irc, char *format, ... )
547{
548        char text[1024];
549        va_list params;
550        char is_private = 0;
551        user_t *u;
552       
553        u = user_find( irc, irc->mynick );
[dd89a55]554        is_private = u->is_private;
[b7d3cc34]555       
556        va_start( params, format );
557        g_vsnprintf( text, sizeof( text ), format, params );
558        va_end( params );
559       
560        return( irc_msgfrom( irc, u->nick, text ) );
561}
562
563void irc_write( irc_t *irc, char *format, ... ) 
564{
565        va_list params;
566
567        va_start( params, format );
568        irc_vawrite( irc, format, params );     
569        va_end( params );
570
571        return;
572
573}
574
575void irc_vawrite( irc_t *irc, char *format, va_list params )
576{
577        int size;
[d783e48]578        char line[IRC_MAX_LINE+1], *cs;
579               
[0356ae3]580        /* Don't try to write anything new anymore when shutting down. */
[5898ef8]581        if( irc->status & USTATUS_SHUTDOWN )
[b7d3cc34]582                return;
[d783e48]583       
584        line[IRC_MAX_LINE] = 0;
585        g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
586       
[b7d3cc34]587        strip_newlines( line );
[5c9512f]588        if( ( cs = set_getstr( &irc->set, "charset" ) ) && ( g_strcasecmp( cs, "utf-8" ) != 0 ) )
[d783e48]589        {
590                char conv[IRC_MAX_LINE+1];
591               
592                conv[IRC_MAX_LINE] = 0;
593                if( do_iconv( "UTF-8", cs, line, conv, 0, IRC_MAX_LINE - 2 ) != -1 )
594                        strcpy( line, conv );
595        }
[b7d3cc34]596        strcat( line, "\r\n" );
[d783e48]597       
[a0d04d6]598        if( irc->sendbuffer != NULL )
599        {
[b7d3cc34]600                size = strlen( irc->sendbuffer ) + strlen( line );
601                irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 );
602                strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line );
603        }
[a0d04d6]604        else
[b7d3cc34]605        {
[a0d04d6]606                irc->sendbuffer = g_strdup(line);
[b7d3cc34]607        }
608       
[a0d04d6]609        if( irc->w_watch_source_id == 0 )
[0356ae3]610        {
611                /* If the buffer is empty we can probably write, so call the write event handler
612                   immediately. If it returns TRUE, it should be called again, so add the event to
613                   the queue. If it's FALSE, we emptied the buffer and saved ourselves some work
614                   in the event queue. */
[bbb6ffb]615                /* Really can't be done as long as the code doesn't do error checking very well:
616                if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */
617               
618                /* So just always do it via the event handler. */
619                irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc );
[0356ae3]620        }
[a0d04d6]621       
[b7d3cc34]622        return;
623}
624
[22d41a2]625void irc_write_all( int now, char *format, ... )
[b7d3cc34]626{
627        va_list params;
628        GSList *temp;   
[22d41a2]629       
[b7d3cc34]630        va_start( params, format );
[22d41a2]631       
[b7d3cc34]632        temp = irc_connection_list;
[22d41a2]633        while( temp != NULL )
634        {
635                irc_t *irc = temp->data;
636               
637                if( now )
638                {
639                        g_free( irc->sendbuffer );
640                        irc->sendbuffer = g_strdup( "\r\n" );
641                }
[b7d3cc34]642                irc_vawrite( temp->data, format, params );
[22d41a2]643                if( now )
644                {
[a0d04d6]645                        bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
[22d41a2]646                }
[b7d3cc34]647                temp = temp->next;
648        }
[22d41a2]649       
[b7d3cc34]650        va_end( params );
651        return;
652} 
653
[7f4016b]654const char *user_mode_prefix( irc_t *irc, user_t *u )
655{
656        static char op[] = "@";
657        static char halfop[] = "%";
658        static char voice[] = "+";
659        static char none[] = "";
660
661        int or = set_getbool(&irc->set, "op_root");
662        int ou = set_getbool(&irc->set, "op_user");
663        char *ob = set_getstr(&irc->set, "op_buddies");
664        char *hb = set_getstr(&irc->set, "halfop_buddies");
665        char *vb = set_getstr(&irc->set, "voice_buddies");
666
667        if( (!strcmp(u->nick, irc->mynick) && or) ||
668            (!strcmp(u->nick, irc->nick) && ou) ||
669        (!u->away && !strcmp(ob, "notaway")) ||
670        (u->encrypted && !strcmp(ob, "encrypted")) ||
671        (u->encrypted>1 && !strcmp(ob, "trusted"))
672      )
673                return op;
674        else if( (!u->away && !strcmp(hb, "notaway")) ||
675             (u->encrypted && !strcmp(hb, "encrypted")) ||
676             (u->encrypted>1 && !strcmp(hb, "trusted"))
677               )
678                return halfop;
679        else if( (!u->away && !strcmp(vb, "notaway")) ||
680             (u->encrypted && !strcmp(vb, "encrypted")) ||
681             (u->encrypted>1 && !strcmp(vb, "trusted"))
682               )
683                return voice;
684        else
685                return none;
686}
687                       
[b7d3cc34]688void irc_names( irc_t *irc, char *channel )
689{
[3f9440d]690        user_t *u;
691        char namelist[385] = "";
[0da65d5]692        struct groupchat *c = NULL;
[b7d3cc34]693       
[2f13222]694        /* RFCs say there is no error reply allowed on NAMES, so when the
[b7d3cc34]695           channel is invalid, just give an empty reply. */
696       
[3f9440d]697        if( g_strcasecmp( channel, irc->channel ) == 0 )
[b7d3cc34]698        {
[3f9440d]699                for( u = irc->users; u; u = u->next ) if( u->online )
[b7d3cc34]700                {
[3f9440d]701                        if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
[b7d3cc34]702                        {
[3f9440d]703                                irc_reply( irc, 353, "= %s :%s", channel, namelist );
704                                *namelist = 0;
[b7d3cc34]705                        }
[3f9440d]706                       
[7f4016b]707                        strcat( namelist, user_mode_prefix(irc, u) );
[3f9440d]708                        strcat( namelist, u->nick );
709                        strcat( namelist, " " );
[b7d3cc34]710                }
711        }
[0e7ab64]712        else if( ( c = irc_chat_by_channel( irc, channel ) ) )
[b7d3cc34]713        {
714                GList *l;
[3f9440d]715               
716                /* root and the user aren't in the channel userlist but should
717                   show up in /NAMES, so list them first: */
[5a71d9c]718                sprintf( namelist, "%s%s %s%s ", set_getbool(&irc->set, "op_root") ? "@" : "", irc->mynick,
719                                                 set_getbool(&irc->set, "op_user") ? "@" : "", irc->nick );
[b7d3cc34]720               
[0da65d5]721                for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) )
[3f9440d]722                {
723                        if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
724                        {
725                                irc_reply( irc, 353, "= %s :%s", channel, namelist );
726                                *namelist = 0;
727                        }
728                       
[3b8e8109]729                        strcat( namelist, user_mode_prefix(irc, u) );
[3f9440d]730                        strcat( namelist, u->nick );
731                        strcat( namelist, " " );
732                }
[b7d3cc34]733        }
734       
[3f9440d]735        if( *namelist )
736                irc_reply( irc, 353, "= %s :%s", channel, namelist );
737       
[b7d3cc34]738        irc_reply( irc, 366, "%s :End of /NAMES list", channel );
739}
740
[edf9657]741int irc_check_login( irc_t *irc )
[b7d3cc34]742{
[edf9657]743        if( irc->user && irc->nick )
744        {
[3af70b0]745                if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
[b7d3cc34]746                {
[edf9657]747                        irc_reply( irc, 464, ":This server is password-protected." );
748                        return 0;
[b7d3cc34]749                }
[edf9657]750                else
[b7d3cc34]751                {
[edf9657]752                        irc_login( irc );
753                        return 1;
[b7d3cc34]754                }
[edf9657]755        }
756        else
757        {
758                /* More information needed. */
759                return 0;
760        }
[b7d3cc34]761}
762
763void irc_login( irc_t *irc )
764{
765        user_t *u;
766       
767        irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
768        irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
769        irc_reply( irc,   3, ":%s", IRCD_INFO );
[238f828]770        irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
[2a2db6f]771        irc_reply( irc,   5, "PREFIX=(ohv)@%%+ CHANTYPES=#& CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", CMODES, MAX_NICK_LENGTH - 1 );
[b7d3cc34]772        irc_motd( irc );
[2f13222]773        irc->umode[0] = '\0';
[238f828]774        irc_umode_set( irc, "+" UMODE, 1 );
[b7d3cc34]775
776        u = user_add( irc, irc->mynick );
777        u->host = g_strdup( irc->myhost );
778        u->realname = g_strdup( ROOT_FN );
779        u->online = 1;
780        u->send_handler = root_command_string;
781        u->is_private = 0; /* [SH] The channel is root's personal playground. */
782        irc_spawn( irc, u );
783       
784        u = user_add( irc, NS_NICK );
785        u->host = g_strdup( irc->myhost );
786        u->realname = g_strdup( ROOT_FN );
787        u->online = 0;
788        u->send_handler = root_command_string;
789        u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
790       
791        u = user_add( irc, irc->nick );
792        u->user = g_strdup( irc->user );
793        u->host = g_strdup( irc->host );
794        u->realname = g_strdup( irc->realname );
795        u->online = 1;
796        irc_spawn( irc, u );
797       
[5e2615a]798        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 FAQs are answered there." );
[b7d3cc34]799       
[bd9b00f]800        if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
[2face62]801                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
802       
[79e826a]803        irc->status |= USTATUS_LOGGED_IN;
[b7d3cc34]804}
805
806void irc_motd( irc_t *irc )
807{
808        int fd;
809       
810        fd = open( global.conf->motdfile, O_RDONLY );
811        if( fd == -1 )
812        {
813                irc_reply( irc, 422, ":We don't need MOTDs." );
814        }
815        else
816        {
817                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
818                char *add, max;
819                int len;
820               
821                linebuf[79] = len = 0;
822                max = sizeof( linebuf ) - 1;
823               
824                irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
825                while( read( fd, linebuf + len, 1 ) == 1 )
826                {
827                        if( linebuf[len] == '\n' || len == max )
828                        {
829                                linebuf[len] = 0;
830                                irc_reply( irc, 372, ":- %s", linebuf );
831                                len = 0;
832                        }
833                        else if( linebuf[len] == '%' )
834                        {
835                                read( fd, linebuf + len, 1 );
836                                if( linebuf[len] == 'h' )
837                                        add = irc->myhost;
838                                else if( linebuf[len] == 'v' )
839                                        add = BITLBEE_VERSION;
840                                else if( linebuf[len] == 'n' )
841                                        add = irc->nick;
842                                else
843                                        add = "%";
844                               
845                                strncpy( linebuf + len, add, max - len );
846                                while( linebuf[++len] );
847                        }
848                        else if( len < max )
849                        {
850                                len ++;
851                        }
852                }
853                irc_reply( irc, 376, ":End of MOTD" );
[d990997]854                close( fd );
[b7d3cc34]855        }
856}
857
858void irc_topic( irc_t *irc, char *channel )
859{
[50e1776]860        struct groupchat *c = irc_chat_by_channel( irc, channel );
861       
862        if( c && c->topic )
863                irc_reply( irc, 332, "%s :%s", channel, c->topic );
864        else if( g_strcasecmp( channel, irc->channel ) == 0 )
[b7d3cc34]865                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
866        else
[50e1776]867                irc_reply( irc, 331, "%s :No topic for this channel", channel );
[b7d3cc34]868}
869
[238f828]870void irc_umode_set( irc_t *irc, char *s, int allow_priv )
[b7d3cc34]871{
[238f828]872        /* allow_priv: Set to 0 if s contains user input, 1 if you want
873           to set a "privileged" mode (+o, +R, etc). */
[b7d3cc34]874        char m[256], st = 1, *t;
875        int i;
[2f13222]876        char changes[512], *p, st2 = 2;
877        char badflag = 0;
[b7d3cc34]878       
879        memset( m, 0, sizeof( m ) );
880       
881        for( t = irc->umode; *t; t ++ )
882                m[(int)*t] = 1;
[2f13222]883
884        p = changes;
[b7d3cc34]885        for( t = s; *t; t ++ )
886        {
887                if( *t == '+' || *t == '-' )
888                        st = *t == '+';
[238f828]889                else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
[2f13222]890                {
891                        if( m[(int)*t] != st)
892                        {
893                                if( st != st2 )
894                                        st2 = st, *p++ = st ? '+' : '-';
895                                *p++ = *t;
896                        }
[b7d3cc34]897                        m[(int)*t] = st;
[2f13222]898                }
899                else
900                        badflag = 1;
[b7d3cc34]901        }
[2f13222]902        *p = '\0';
[b7d3cc34]903       
904        memset( irc->umode, 0, sizeof( irc->umode ) );
905       
906        for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
[238f828]907                if( m[i] )
[b7d3cc34]908                        irc->umode[strlen(irc->umode)] = i;
909       
[2f13222]910        if( badflag )
911                irc_reply( irc, 501, ":Unknown MODE flag" );
912        /* Deliberately no !user@host on the prefix here */
913        if( *changes )
914                irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes );
[b7d3cc34]915}
916
917void irc_spawn( irc_t *irc, user_t *u )
918{
919        irc_join( irc, u, irc->channel );
920}
921
922void irc_join( irc_t *irc, user_t *u, char *channel )
923{
924        char *nick;
925       
926        if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
927                irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
928       
929        if( nick_cmp( u->nick, irc->nick ) == 0 )
930        {
931                irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
932                irc_names( irc, channel );
933                irc_topic( irc, channel );
934        }
935       
936        nick = g_strdup( u->nick );
937        nick_lc( nick );
938        if( g_hash_table_lookup( irc->watches, nick ) )
939        {
[fc630f9]940                irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" );
[b7d3cc34]941        }
942        g_free( nick );
943}
944
945void irc_part( irc_t *irc, user_t *u, char *channel )
946{
947        irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
948}
949
950void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
951{
952        irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
953}
954
955void irc_kill( irc_t *irc, user_t *u )
956{
[fb62f81f]957        char *nick, *s;
[0a3c243]958        char reason[128];
[fb62f81f]959       
[1186382]960        if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) )
[fb62f81f]961        {
[0da65d5]962                if( u->ic->acc->server )
[fb62f81f]963                        g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
[0da65d5]964                                    u->ic->acc->server );
[c2fb3809]965                else if( ( s = strchr( u->ic->acc->user, '@' ) ) )
[fb62f81f]966                        g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
967                                    s + 1 );
968                else
969                        g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
[0da65d5]970                                    u->ic->acc->prpl->name, irc->myhost );
[fb62f81f]971               
972                /* proto_opt might contain garbage after the : */
973                if( ( s = strchr( reason, ':' ) ) )
974                        *s = 0;
975        }
976        else
977        {
978                strcpy( reason, "Leaving..." );
979        }
[b7d3cc34]980       
[fb62f81f]981        irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
[b7d3cc34]982       
983        nick = g_strdup( u->nick );
984        nick_lc( nick );
985        if( g_hash_table_lookup( irc->watches, nick ) )
986        {
[fc630f9]987                irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
[b7d3cc34]988        }
989        g_free( nick );
990}
991
992int irc_send( irc_t *irc, char *nick, char *s, int flags )
993{
[0da65d5]994        struct groupchat *c = NULL;
[b7d3cc34]995        user_t *u = NULL;
996       
[94281ef]997        if( *nick == '#' || *nick == '&' )
[b7d3cc34]998        {
[0e7ab64]999                if( !( c = irc_chat_by_channel( irc, nick ) ) )
[b7d3cc34]1000                {
1001                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
1002                        return( 0 );
1003                }
1004        }
1005        else
1006        {
1007                u = user_find( irc, nick );
1008               
1009                if( !u )
1010                {
1011                        if( irc->is_private )
1012                                irc_reply( irc, 401, "%s :Nick does not exist", nick );
1013                        else
1014                                irc_usermsg( irc, "Nick `%s' does not exist!", nick );
1015                        return( 0 );
1016                }
1017        }
1018       
1019        if( *s == 1 && s[strlen(s)-1] == 1 )
1020        {
1021                if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
1022                {
1023                        if( s[7] == ' ' ) s ++;
1024                        s += 3;
1025                        *(s++) = '/';
1026                        *(s++) = 'm';
1027                        *(s++) = 'e';
1028                        *(s++) = ' ';
1029                        s -= 4;
1030                        s[strlen(s)-1] = 0;
1031                }
1032                else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
1033                {
1034                        u = user_find( irc, irc->mynick );
1035                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
1036                        return( 1 );
1037                }
1038                else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
1039                {
1040                        u = user_find( irc, irc->mynick );
1041                        irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
1042                        return( 1 );
1043                }
1044                else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
1045                {
[0da65d5]1046                        if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 )
[b7d3cc34]1047                        {
1048                                time_t current_typing_notice = time( NULL );
1049                               
1050                                if( current_typing_notice - u->last_typing_notice >= 5 )
1051                                {
[df1fb67]1052                                        u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 );
[b7d3cc34]1053                                        u->last_typing_notice = current_typing_notice;
1054                                }
1055                        }
1056                        return( 1 );
1057                }
1058                else
1059                {
1060                        irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
1061                        return( 0 );
1062                }
1063        }
1064       
1065        if( u )
1066        {
1067                /* For the next message, we probably do have to send new notices... */
1068                u->last_typing_notice = 0;
1069                u->is_private = irc->is_private;
1070               
1071                if( u->is_private )
1072                {
1073                        if( !u->online )
1074                                irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
1075                        else if( u->away )
1076                                irc_reply( irc, 301, "%s :%s", u->nick, u->away );
1077                }
1078               
1079                if( u->send_handler )
[f73b969]1080                {
1081                        u->send_handler( irc, u, s, flags );
1082                        return 1;
1083                }
[b7d3cc34]1084        }
[0da65d5]1085        else if( c && c->ic && c->ic->acc && c->ic->acc->prpl )
[b7d3cc34]1086        {
[84b045d]1087                return( imc_chat_msg( c, s, 0 ) );
[b7d3cc34]1088        }
1089       
1090        return( 0 );
1091}
1092
[ba9edaa]1093static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond )
[b7d3cc34]1094{
1095        user_t *u = data;
1096       
[226fce1]1097        /* Shouldn't happen, but just to be sure. */
1098        if( u->sendbuf_len < 2 )
1099                return FALSE;
1100       
[b7d3cc34]1101        u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
[84b045d]1102        imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags );
[b7d3cc34]1103       
1104        g_free( u->sendbuf );
1105        u->sendbuf = NULL;
1106        u->sendbuf_len = 0;
1107        u->sendbuf_timer = 0;
1108        u->sendbuf_flags = 0;
1109       
[ba9edaa]1110        return FALSE;
[b7d3cc34]1111}
1112
[f73b969]1113void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
[b7d3cc34]1114{
[0da65d5]1115        if( !u || !u->ic ) return;
[b7d3cc34]1116       
[d5ccd83]1117        if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 )
[b7d3cc34]1118        {
[834ff44]1119                int delay;
1120               
[b7d3cc34]1121                if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
1122                {
[226fce1]1123                        /* Flush the buffer */
[ba9edaa]1124                        b_event_remove( u->sendbuf_timer );
1125                        buddy_send_handler_delayed( u, -1, 0 );
[b7d3cc34]1126                }
1127
1128                if( u->sendbuf_len == 0 )
1129                {
1130                        u->sendbuf_len = strlen( msg ) + 2;
[226fce1]1131                        u->sendbuf = g_new( char, u->sendbuf_len );
[b7d3cc34]1132                        u->sendbuf[0] = 0;
1133                        u->sendbuf_flags = flags;
1134                }
1135                else
1136                {
1137                        u->sendbuf_len += strlen( msg ) + 1;
[226fce1]1138                        u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
[b7d3cc34]1139                }
1140               
1141                strcat( u->sendbuf, msg );
1142                strcat( u->sendbuf, "\n" );
1143               
[5c9512f]1144                delay = set_getint( &irc->set, "buddy_sendbuffer_delay" );
[834ff44]1145                if( delay <= 5 )
1146                        delay *= 1000;
1147               
[b7d3cc34]1148                if( u->sendbuf_timer > 0 )
[ba9edaa]1149                        b_event_remove( u->sendbuf_timer );
1150                u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u );
[b7d3cc34]1151        }
1152        else
1153        {
[84b045d]1154                imc_buddy_msg( u->ic, u->handle, msg, flags );
[b7d3cc34]1155        }
1156}
1157
1158int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
1159{
1160        char last = 0;
1161        char *s = msg, *line = msg;
1162       
1163        /* The almighty linesplitter .. woohoo!! */
1164        while( !last )
1165        {
1166                if( *s == '\r' && *(s+1) == '\n' )
1167                        *(s++) = 0;
1168                if( *s == '\n' )
1169                {
1170                        last = s[1] == 0;
1171                        *s = 0;
1172                }
1173                else
1174                {
1175                        last = s[0] == 0;
1176                }
1177                if( *s == 0 )
1178                {
1179                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
1180                        {
1181                                irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
1182                                           type, to, line + 4 );
1183                        }
1184                        else
1185                        {
1186                                irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
[25d1be7]1187                                           type, to, prefix ? prefix : "", line );
[b7d3cc34]1188                        }
1189                        line = s + 1;
1190                }
1191                s ++;
1192        }
1193       
1194        return( 1 );
1195}
1196
1197int irc_msgfrom( irc_t *irc, char *nick, char *msg )
1198{
1199        user_t *u = user_find( irc, nick );
1200        static char *prefix = NULL;
1201       
1202        if( !u ) return( 0 );
1203        if( prefix && *prefix ) g_free( prefix );
1204       
1205        if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
1206        {
1207                int len = strlen( irc->nick) + 3;
1208                prefix = g_new (char, len );
[5c9512f]1209                g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) );
[b7d3cc34]1210                prefix[len-1] = 0;
1211        }
1212        else
1213        {
1214                prefix = "";
1215        }
1216       
1217        return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
1218}
1219
1220int irc_noticefrom( irc_t *irc, char *nick, char *msg )
1221{
1222        user_t *u = user_find( irc, nick );
1223       
1224        if( u )
1225                return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
1226        else
1227                return( 0 );
1228}
1229
1230/* Returns 0 if everything seems to be okay, a number >0 when there was a
1231   timeout. The number returned is the number of seconds we received no
1232   pongs from the user. When not connected yet, we don't ping but drop the
1233   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
[ba9edaa]1234static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
[b7d3cc34]1235{
1236        irc_t *irc = _irc;
1237        int rv = 0;
1238       
[3af70b0]1239        if( !( irc->status & USTATUS_LOGGED_IN ) )
[b7d3cc34]1240        {
1241                if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) )
1242                        rv = gettime() - irc->last_pong;
1243        }
1244        else
1245        {
1246                if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging )
1247                {
1248                        irc_write( irc, "PING :%s", IRC_PING_STRING );
1249                        irc->pinging = 1;
1250                }
1251                else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) )
1252                {
1253                        rv = gettime() - irc->last_pong;
1254                }
1255        }
1256       
1257        if( rv > 0 )
1258        {
[f73b969]1259                irc_abort( irc, 0, "Ping Timeout: %d seconds", rv );
[b7d3cc34]1260                return FALSE;
1261        }
1262       
1263        return TRUE;
1264}
[0e7ab64]1265
1266struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
1267{
1268        struct groupchat *c;
1269        account_t *a;
1270       
1271        /* This finds the connection which has a conversation which belongs to this channel */
1272        for( a = irc->accounts; a; a = a->next )
1273        {
[bdda9e9]1274                if( a->ic == NULL )
1275                        continue;
1276               
1277                c = a->ic->groupchats;
1278                while( c )
1279                {
1280                        if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
1281                                return c;
1282                       
1283                        c = c->next;
1284                }
[0e7ab64]1285        }
1286       
1287        return NULL;
1288}
Note: See TracBrowser for help on using the repository browser.