source: irc.c @ f9756bd

Last change on this file since f9756bd was f9756bd, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-03-30T21:26:16Z

Changed charset handling: irc_t keeps two iconv structures, which are just
used for every line sent and received, so now there's no need to use
g_iconv_open() every time a message comes in/out. Also, fixed a small
memory leak that was there for a long time but somehow never caught my
attention.

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