source: irc.c @ 4358b10

Last change on this file since 4358b10 was 4358b10, checked in by ulim <a.sporto+bee@…>, at 2008-05-04T13:32:15Z

ulibc support, fixes "Invalid SOCKS5 Connect message" problem

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