source: irc_im.c @ 911d97a

Last change on this file since 911d97a was 911d97a, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-08-04T15:19:54Z

Error handling fixes.

Found one double free() bug causing troubles when a buddy_msg() handler takes
down the IM connection immediately.

  • Property mode set to 100644
File size: 26.9 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Some glue to put the IRC and the IM stuff together.                  */
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#include "bitlbee.h"
27#include "dcc.h"
28
29/* IM->IRC callbacks: Simple IM/buddy-related stuff. */
30
31static const struct irc_user_funcs irc_user_im_funcs;
32
33static void bee_irc_imc_connected( struct im_connection *ic )
34{
35        irc_t *irc = (irc_t*) ic->bee->ui_data;
36       
37        irc_channel_auto_joins( irc, ic->acc );
38}
39
40static void bee_irc_imc_disconnected( struct im_connection *ic )
41{
42        /* Maybe try to send /QUITs here instead of later on. */
43}
44
45static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu )
46{
47        irc_user_t *iu;
48        irc_t *irc = (irc_t*) bee->ui_data;
49        char nick[MAX_NICK_LENGTH+1], *s;
50       
51        memset( nick, 0, MAX_NICK_LENGTH + 1 );
52        strcpy( nick, nick_get( bu ) );
53       
54        bu->ui_data = iu = irc_user_new( irc, nick );
55        iu->bu = bu;
56       
57        if( set_getbool( &irc->b->set, "private" ) )
58                iu->last_channel = NULL;
59        else
60                iu->last_channel = irc_channel_with_user( irc, iu );
61       
62        if( ( s = strchr( bu->handle, '@' ) ) )
63        {
64                iu->host = g_strdup( s + 1 );
65                iu->user = g_strndup( bu->handle, s - bu->handle );
66        }
67        else
68        {
69                iu->user = g_strdup( bu->handle );
70                if( bu->ic->acc->server )
71                        iu->host = g_strdup( bu->ic->acc->server );
72                else
73                        iu->host = g_strdup( bu->ic->acc->prpl->name );
74        }
75       
76        while( ( s = strchr( iu->user, ' ' ) ) )
77                *s = '_';
78       
79        if( bu->flags & BEE_USER_LOCAL )
80        {
81                char *s = set_getstr( &bee->set, "handle_unknown" );
82               
83                if( strcmp( s, "add_private" ) == 0 )
84                        iu->last_channel = NULL;
85                else if( strcmp( s, "add_channel" ) == 0 )
86                        iu->last_channel = irc->default_channel;
87        }
88       
89        iu->f = &irc_user_im_funcs;
90       
91        return TRUE;
92}
93
94static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu )
95{
96        return irc_user_free( bee->ui_data, (irc_user_t *) bu->ui_data );
97}
98
99static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old )
100{
101        irc_t *irc = bee->ui_data;
102        irc_user_t *iu = bu->ui_data;
103       
104        /* Do this outside the if below since away state can change without
105           the online state changing. */
106        iu->flags &= ~IRC_USER_AWAY;
107        if( bu->flags & BEE_USER_AWAY || !( bu->flags & BEE_USER_ONLINE ) )
108                iu->flags |= IRC_USER_AWAY;
109       
110        if( ( bu->flags & BEE_USER_ONLINE ) != ( old->flags & BEE_USER_ONLINE ) )
111        {
112                if( bu->flags & BEE_USER_ONLINE )
113                {
114                        if( g_hash_table_lookup( irc->watches, iu->key ) )
115                                irc_send_num( irc, 600, "%s %s %s %d :%s", iu->nick, iu->user,
116                                              iu->host, (int) time( NULL ), "logged online" );
117                }
118                else
119                {
120                        if( g_hash_table_lookup( irc->watches, iu->key ) )
121                                irc_send_num( irc, 601, "%s %s %s %d :%s", iu->nick, iu->user,
122                                              iu->host, (int) time( NULL ), "logged offline" );
123                       
124                        /* Send a QUIT since those will also show up in any
125                           query windows the user may have, plus it's only
126                           one QUIT instead of possibly many (in case of
127                           multiple control chans). If there's a channel that
128                           shows offline people, a JOIN will follow. */
129                        if( set_getbool( &bee->set, "offline_user_quits" ) )
130                                irc_user_quit( iu, "Leaving..." );
131                }
132        }
133       
134        /* Reset this one since the info may have changed. */
135        iu->away_reply_timeout = 0;
136       
137        bee_irc_channel_update( irc, NULL, iu );
138       
139        return TRUE;
140}
141
142void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu )
143{
144        GSList *l;
145       
146        if( ic == NULL )
147        {
148                for( l = irc->channels; l; l = l->next )
149                {
150                        ic = l->data;
151                        /* TODO: Just add a type flag or so.. */
152                        if( ic->f == irc->default_channel->f &&
153                            ( ic->flags & IRC_CHANNEL_JOINED ) )
154                                bee_irc_channel_update( irc, ic, iu );
155                }
156                return;
157        }
158        if( iu == NULL )
159        {
160                for( l = irc->users; l; l = l->next )
161                {
162                        iu = l->data;
163                        if( iu->bu )
164                                bee_irc_channel_update( irc, ic, l->data );
165                }
166                return;
167        }
168       
169        if( !irc_channel_wants_user( ic, iu ) )
170        {
171                irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
172        }
173        else
174        {
175                struct irc_control_channel *icc = ic->data;
176                int mode = 0;
177               
178                if( !( iu->bu->flags & BEE_USER_ONLINE ) )
179                        mode = icc->modes[0];
180                else if( iu->bu->flags & BEE_USER_AWAY )
181                        mode = icc->modes[1];
182                else
183                        mode = icc->modes[2];
184               
185                if( !mode )
186                        irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
187                else
188                {
189                        irc_channel_add_user( ic, iu );
190                        irc_channel_user_set_mode( ic, iu, mode );
191                }
192        }
193}
194
195static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg_, time_t sent_at )
196{
197        irc_t *irc = bee->ui_data;
198        irc_user_t *iu = (irc_user_t *) bu->ui_data;
199        char *dst, *prefix = NULL;
200        char *wrapped, *ts = NULL;
201        irc_channel_t *ic = NULL;
202        char *msg = g_strdup( msg_ );
203        GSList *l;
204       
205        if( sent_at > 0 && set_getbool( &irc->b->set, "display_timestamps" ) )
206                ts = irc_format_timestamp( irc, sent_at );
207       
208        /* Too similar to irc_usermsg()... */
209        if( iu->last_channel )
210        {
211                if( iu->last_channel->flags & IRC_CHANNEL_JOINED )
212                        ic = iu->last_channel;
213                else
214                        ic = irc_channel_with_user( irc, iu );
215        }
216       
217        if( ic )
218        {
219                dst = ic->name;
220                prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ? : "" );
221        }
222        else
223        {
224                dst = irc->user->nick;
225                prefix = ts;
226                ts = NULL;
227        }
228       
229        for( l = irc_plugins; l; l = l->next )
230        {
231                irc_plugin_t *p = l->data;
232                if( p->filter_msg_in )
233                {
234                        char *s = p->filter_msg_in( iu, msg, 0 );
235                        if( s )
236                        {
237                                if( s != msg )
238                                        g_free( msg );
239                                msg = s;
240                        }
241                        else
242                        {
243                                /* Modules can swallow messages. */
244                                return TRUE;
245                        }
246                }
247        }
248       
249        if( ( g_strcasecmp( set_getstr( &bee->set, "strip_html" ), "always" ) == 0 ) ||
250            ( ( bu->ic->flags & OPT_DOES_HTML ) && set_getbool( &bee->set, "strip_html" ) ) )
251        {
252                char *s = g_strdup( msg );
253                strip_html( s );
254                g_free( msg );
255                msg = s;
256        }
257       
258        wrapped = word_wrap( msg, 425 );
259        irc_send_msg( iu, "PRIVMSG", dst, wrapped, prefix );
260       
261        g_free( wrapped );
262        g_free( prefix );
263        g_free( msg );
264        g_free( ts );
265       
266        return TRUE;
267}
268
269static gboolean bee_irc_user_typing( bee_t *bee, bee_user_t *bu, uint32_t flags )
270{
271        irc_t *irc = (irc_t *) bee->ui_data;
272       
273        if( set_getbool( &bee->set, "typing_notice" ) )
274                irc_send_msg_f( (irc_user_t *) bu->ui_data, "PRIVMSG", irc->user->nick,
275                                "\001TYPING %d\001", ( flags >> 8 ) & 3 );
276        else
277                return FALSE;
278       
279        return TRUE;
280}
281
282static gboolean bee_irc_user_action_response( bee_t *bee, bee_user_t *bu, const char *action, char * const args[], void *data )
283{
284        irc_t *irc = (irc_t *) bee->ui_data;
285        GString *msg = g_string_new( "\001" );
286       
287        g_string_append( msg, action );
288        while( *args )
289        {
290                if( strchr( *args, ' ' ) )
291                        g_string_append_printf( msg, " \"%s\"", *args );
292                else
293                        g_string_append_printf( msg, " %s", *args );
294                args ++;
295        }
296        g_string_append_c( msg, '\001' );
297       
298        irc_send_msg( (irc_user_t *) bu->ui_data, "NOTICE", irc->user->nick, msg->str, NULL );
299       
300        return TRUE;
301}
302
303static gboolean bee_irc_user_nick_update( irc_user_t *iu );
304
305static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu )
306{
307        irc_user_t *iu = (irc_user_t *) bu->ui_data;
308        char *s;
309       
310        if( iu->fullname != iu->nick )
311                g_free( iu->fullname );
312        iu->fullname = g_strdup( bu->fullname );
313       
314        /* Strip newlines (unlikely, but IRC-unfriendly so they must go)
315           TODO(wilmer): Do the same with away msgs again! */
316        for( s = iu->fullname; *s; s ++ )
317                if( isspace( *s ) ) *s = ' ';
318       
319        if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) )
320        {
321                /* People don't like this /NOTICE. Meh, let's go back to the old one.
322                char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
323                irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
324                */
325                imcb_log( bu->ic, "User `%s' changed name to `%s'", iu->nick, iu->fullname );
326        }
327       
328        bee_irc_user_nick_update( iu );
329       
330        return TRUE;
331}
332
333static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint )
334{
335        bee_irc_user_nick_update( (irc_user_t*) bu->ui_data );
336       
337        return TRUE;
338}
339
340static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu )
341{
342        irc_user_t *iu = (irc_user_t *) bu->ui_data;
343        irc_t *irc = (irc_t *) bee->ui_data;
344        bee_user_flags_t online;
345       
346        /* Take the user offline temporarily so we can change the nick (if necessary). */
347        if( ( online = bu->flags & BEE_USER_ONLINE ) )
348                bu->flags &= ~BEE_USER_ONLINE;
349       
350        bee_irc_channel_update( irc, NULL, iu );
351        bee_irc_user_nick_update( iu );
352       
353        if( online )
354        {
355                bu->flags |= online;
356                bee_irc_channel_update( irc, NULL, iu );
357        }
358       
359        return TRUE;
360}
361
362static gboolean bee_irc_user_nick_update( irc_user_t *iu )
363{
364        bee_user_t *bu = iu->bu;
365        char *newnick;
366       
367        if( bu->flags & BEE_USER_ONLINE )
368                /* Ignore if the user is visible already. */
369                return TRUE;
370       
371        if( nick_saved( bu ) )
372                /* The user already assigned a nickname to this person. */
373                return TRUE;
374       
375        newnick = nick_get( bu );
376       
377        if( strcmp( iu->nick, newnick ) != 0 )
378        {
379                nick_dedupe( bu, newnick );
380                irc_user_set_nick( iu, newnick );
381        }
382       
383        return TRUE;
384}
385
386void bee_irc_user_nick_reset( irc_user_t *iu )
387{
388        bee_user_t *bu = iu->bu;
389        bee_user_flags_t online;
390       
391        if( bu == FALSE )
392                return;
393       
394        /* In this case, pretend the user is offline. */
395        if( ( online = bu->flags & BEE_USER_ONLINE ) )
396                bu->flags &= ~BEE_USER_ONLINE;
397       
398        nick_del( bu );
399        bee_irc_user_nick_update( iu );
400       
401        bu->flags |= online;
402}
403
404/* IRC->IM calls */
405
406static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
407
408static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg )
409{
410        const char *away;
411       
412        if( iu->bu == NULL )
413                return FALSE;
414       
415        if( ( away = irc_user_get_away( iu ) ) &&
416            time( NULL ) >= iu->away_reply_timeout )
417        {
418                irc_send_num( iu->irc, 301, "%s :%s", iu->nick, away );
419                iu->away_reply_timeout = time( NULL ) +
420                        set_getint( &iu->irc->b->set, "away_reply_timeout" );
421        }
422       
423        if( iu->pastebuf == NULL )
424                iu->pastebuf = g_string_new( msg );
425        else
426        {
427                b_event_remove( iu->pastebuf_timer );
428                g_string_append_printf( iu->pastebuf, "\n%s", msg );
429        }
430       
431        if( set_getbool( &iu->irc->b->set, "paste_buffer" ) )
432        {
433                int delay;
434               
435                if( ( delay = set_getint( &iu->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
436                        delay *= 1000;
437               
438                iu->pastebuf_timer = b_timeout_add( delay, bee_irc_user_privmsg_cb, iu );
439               
440                return TRUE;
441        }
442        else
443        {
444                bee_irc_user_privmsg_cb( iu, 0, 0 );
445               
446                return TRUE;
447        }
448}
449
450static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
451{
452        irc_user_t *iu = data;
453        char *msg;
454        GSList *l;
455       
456        msg = g_string_free( iu->pastebuf, FALSE );
457        iu->pastebuf = NULL;
458        iu->pastebuf_timer = 0;
459       
460        for( l = irc_plugins; l; l = l->next )
461        {
462                irc_plugin_t *p = l->data;
463                if( p->filter_msg_out )
464                {
465                        char *s = p->filter_msg_out( iu, msg, 0 );
466                        if( s )
467                        {
468                                if( s != msg )
469                                        g_free( msg );
470                                msg = s;
471                        }
472                        else
473                        {
474                                /* Modules can swallow messages. */
475                                iu->pastebuf = NULL;
476                                g_free( msg );
477                                return FALSE;
478                        }
479                }
480        }
481       
482        bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
483       
484        g_free( msg );
485       
486        return FALSE;
487}
488
489static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp )
490{
491        if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0
492                    && g_strcasecmp( ctcp[1], "SEND" ) == 0 )
493        {
494                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
495                {
496                        file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp );
497                        if ( ft )
498                                iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle );
499                       
500                        return TRUE;
501                }
502        }
503        else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 )
504        {
505                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] )
506                {
507                        int st = ctcp[1][0];
508                        if( st >= '0' && st <= '2' )
509                        {
510                                st <<= 8;
511                                iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st );
512                        }
513                       
514                        return TRUE;
515                }
516        }
517        else if( g_strcasecmp( ctcp[0], "HELP" ) == 0 && iu->bu )
518        {
519                GString *supp = g_string_new( "Supported CTCPs:" );
520                GList *l;
521               
522                if( iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
523                        g_string_append( supp, " DCC SEND," );
524                if( iu->bu->ic && iu->bu->ic->acc->prpl->send_typing )
525                        g_string_append( supp, " TYPING," );
526                if( iu->bu->ic->acc->prpl->buddy_action_list )
527                        for( l = iu->bu->ic->acc->prpl->buddy_action_list( iu->bu ); l; l = l->next )
528                        {
529                                struct buddy_action *ba = l->data;
530                                g_string_append_printf( supp, " %s (%s),",
531                                                        ba->name, ba->description );
532                        }
533                g_string_truncate( supp, supp->len - 1 );
534                irc_send_msg_f( iu, "NOTICE", iu->irc->user->nick, "\001HELP %s\001", supp->str );
535                g_string_free( supp, TRUE );
536        }
537        else if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->buddy_action )
538        {
539                iu->bu->ic->acc->prpl->buddy_action( iu->bu, ctcp[0], ctcp + 1, NULL );
540        }
541       
542        return FALSE;
543}
544
545static const struct irc_user_funcs irc_user_im_funcs = {
546        bee_irc_user_privmsg,
547        bee_irc_user_ctcp,
548};
549
550
551/* IM->IRC: Groupchats */
552const struct irc_channel_funcs irc_channel_im_chat_funcs;
553
554static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c )
555{
556        irc_t *irc = bee->ui_data;
557        irc_channel_t *ic;
558        char *topic;
559        GSList *l;
560        int i;
561       
562        /* Try to find a channel that expects to receive a groupchat.
563           This flag is set earlier in our current call trace. */
564        for( l = irc->channels; l; l = l->next )
565        {
566                ic = l->data;
567                if( ic->flags & IRC_CHANNEL_CHAT_PICKME )
568                        break;
569        }
570       
571        /* If we found none, just generate some stupid name. */
572        if( l == NULL ) for( i = 0; i <= 999; i ++ )
573        {
574                char name[16];
575                sprintf( name, "#chat_%03d", i );
576                if( ( ic = irc_channel_new( irc, name ) ) )
577                        break;
578        }
579       
580        if( ic == NULL )
581                return FALSE;
582       
583        c->ui_data = ic;
584        ic->data = c;
585       
586        topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
587        irc_channel_set_topic( ic, topic, irc->root );
588        g_free( topic );
589       
590        return TRUE;
591}
592
593static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
594{
595        irc_channel_t *ic = c->ui_data;
596       
597        if( ic == NULL )
598                return FALSE;
599       
600        if( ic->flags & IRC_CHANNEL_JOINED )
601                irc_channel_printf( ic, "Cleaning up channel, bye!" );
602       
603        ic->data = NULL;
604        c->ui_data = NULL;
605        irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
606       
607        return TRUE;
608}
609
610static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text )
611{
612        irc_channel_t *ic = c->ui_data;
613       
614        if( ic == NULL )
615                return FALSE;
616       
617        irc_channel_printf( ic, "%s", text );
618       
619        return TRUE;
620}
621
622static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at )
623{
624        irc_t *irc = bee->ui_data;
625        irc_user_t *iu = bu->ui_data;
626        irc_channel_t *ic = c->ui_data;
627        char *ts = NULL;
628       
629        if( ic == NULL )
630                return FALSE;
631       
632        if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) )
633                ts = irc_format_timestamp( irc, sent_at );
634       
635        irc_send_msg( iu, "PRIVMSG", ic->name, msg, ts );
636        g_free( ts );
637       
638        return TRUE;
639}
640
641static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
642{
643        irc_t *irc = bee->ui_data;
644        irc_channel_t *ic = c->ui_data;
645       
646        if( ic == NULL )
647                return FALSE;
648       
649        irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data );
650       
651        return TRUE;
652}
653
654static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
655{
656        irc_t *irc = bee->ui_data;
657        irc_channel_t *ic = c->ui_data;
658       
659        if( ic == NULL || bu == NULL )
660                return FALSE;
661       
662        /* TODO: Possible bug here: If a module removes $user here instead of just
663           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
664           a broken state around here. */
665        irc_channel_del_user( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL );
666       
667        return TRUE;
668}
669
670static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu )
671{
672        irc_channel_t *ic = c->ui_data;
673        irc_t *irc = bee->ui_data;
674        irc_user_t *iu;
675       
676        if( ic == NULL )
677                return FALSE;
678       
679        if( bu == NULL )
680                iu = irc->root;
681        else if( bu == bee->user )
682                iu = irc->user;
683        else
684                iu = bu->ui_data;
685       
686        irc_channel_set_topic( ic, new, iu );
687       
688        return TRUE;
689}
690
691static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
692{
693        irc_t *irc = bee->ui_data;
694        irc_channel_t *ic = c->ui_data, *oic;
695        char stripped[MAX_NICK_LENGTH+1], *full_name;
696       
697        if( ic == NULL )
698                return FALSE;
699       
700        /* Don't rename a channel if the user's in it already. */
701        if( ic->flags & IRC_CHANNEL_JOINED )
702                return FALSE;
703       
704        strncpy( stripped, name, MAX_NICK_LENGTH );
705        stripped[MAX_NICK_LENGTH] = '\0';
706        irc_channel_name_strip( stripped );
707        if( set_getbool( &bee->set, "lcnicks" ) )
708                nick_lc( stripped );
709       
710        if( stripped[0] == '\0' )
711                return FALSE;
712       
713        full_name = g_strdup_printf( "#%s", stripped );
714        if( ( oic = irc_channel_by_name( irc, full_name ) ) )
715        {
716                char *type, *chat_type;
717               
718                type = set_getstr( &oic->set, "type" );
719                chat_type = set_getstr( &oic->set, "chat_type" );
720               
721                if( type && chat_type && oic->data == FALSE &&
722                    strcmp( type, "chat" ) == 0 &&
723                    strcmp( chat_type, "groupchat" ) == 0 )
724                {
725                        /* There's a channel with this name already, but it looks
726                           like it's not in use yet. Most likely the IRC client
727                           rejoined the channel after a reconnect. Remove it so
728                           we can reuse its name. */
729                        irc_channel_free( oic );
730                }
731                else
732                {
733                        g_free( full_name );
734                        return FALSE;
735                }
736        }
737       
738        g_free( ic->name );
739        ic->name = full_name;
740       
741        return TRUE;
742}
743
744static gboolean bee_irc_chat_invite( bee_t *bee, bee_user_t *bu, const char *name, const char *msg )
745{
746        char *channel, *s;
747        irc_t *irc = bee->ui_data;
748        irc_user_t *iu = bu->ui_data;
749        irc_channel_t *chan;
750       
751        if( strchr( CTYPES, name[0] ) )
752                channel = g_strdup( name );
753        else
754                channel = g_strdup_printf( "#%s", name );
755       
756        if( ( s = strchr( channel, '@' ) ) )
757                *s = '\0';
758       
759        if( strlen( channel ) > MAX_NICK_LENGTH )
760        {
761                /* If the channel name is very long (like those insane GTalk
762                   UUID names), try if we can use the inviter's nick. */
763                s = g_strdup_printf( "#%s", iu->nick );
764                if( irc_channel_by_name( irc, s ) == NULL )
765                {
766                        g_free( channel );
767                        channel = s;
768                }
769        }
770       
771        if( ( chan = irc_channel_new( irc, channel ) ) &&
772            set_setstr( &chan->set, "type", "chat" ) &&
773            set_setstr( &chan->set, "chat_type", "room" ) &&
774            set_setstr( &chan->set, "account", bu->ic->acc->tag ) &&
775            set_setstr( &chan->set, "room", (char*) name ) )
776        {
777                /* I'm assuming that if the user didn't "chat add" the room
778                   himself but got invited, it's temporary, so make this a
779                   temporary mapping that is removed as soon as we /PART. */
780                chan->flags |= IRC_CHANNEL_TEMP;
781        }
782        else
783        {
784                irc_channel_free( chan );
785                chan = NULL;
786        }
787        g_free( channel );
788       
789        irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name );
790        if( msg )
791                irc_send_msg( iu, "PRIVMSG", irc->user->nick, msg, NULL );
792        if( chan )
793        {
794                irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name );
795                irc_send_invite( iu, chan );
796        }
797       
798        return TRUE;
799}
800
801/* IRC->IM */
802static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
803
804static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
805{
806        struct groupchat *c = ic->data;
807        char *trans = NULL, *s;
808       
809        if( c == NULL )
810                return FALSE;
811       
812        if( set_getbool( &ic->set, "translate_to_nicks" ) )
813        {
814                char nick[MAX_NICK_LENGTH+1];
815                irc_user_t *iu;
816               
817                strncpy( nick, msg, MAX_NICK_LENGTH );
818                nick[MAX_NICK_LENGTH] = '\0';
819                if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) )
820                {
821                        *s = '\0';
822                        if( ( iu = irc_user_by_name( ic->irc, nick ) ) && iu->bu &&
823                            iu->bu->nick && irc_channel_has_user( ic, iu ) )
824                        {
825                                trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL );
826                                msg = trans;
827                        }
828                }
829        }
830       
831        if( set_getbool( &ic->irc->b->set, "paste_buffer" ) )
832        {
833                int delay;
834               
835                if( ic->pastebuf == NULL )
836                        ic->pastebuf = g_string_new( msg );
837                else
838                {
839                        b_event_remove( ic->pastebuf_timer );
840                        g_string_append_printf( ic->pastebuf, "\n%s", msg );
841                }
842               
843                if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
844                        delay *= 1000;
845               
846                ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic );
847               
848                g_free( trans );
849                return TRUE;
850        }
851        else
852                bee_chat_msg( ic->irc->b, c, msg, 0 );
853       
854        g_free( trans );
855        return TRUE;
856}
857
858static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
859{
860        irc_channel_t *ic = data;
861       
862        if( ic->data )
863                bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
864       
865        g_string_free( ic->pastebuf, TRUE );
866        ic->pastebuf = 0;
867        ic->pastebuf_timer = 0;
868       
869        return FALSE;
870}
871
872static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
873{
874        char *acc_s, *room;
875        account_t *acc;
876       
877        if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 )
878                return TRUE;
879       
880        if( ( acc_s = set_getstr( &ic->set, "account" ) ) &&
881            ( room = set_getstr( &ic->set, "room" ) ) &&
882            ( acc = account_get( ic->irc->b, acc_s ) ) &&
883            acc->ic && acc->prpl->chat_join )
884        {
885                char *nick;
886               
887                if( !( nick = set_getstr( &ic->set, "nick" ) ) )
888                        nick = ic->irc->user->nick;
889               
890                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
891                acc->prpl->chat_join( acc->ic, room, nick, NULL, &ic->set );
892                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
893               
894                return FALSE;
895        }
896        else
897        {
898                irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name );
899                return FALSE;
900        }
901}
902
903static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
904{
905        struct groupchat *c = ic->data;
906       
907        if( c && c->ic->acc->prpl->chat_leave )
908                c->ic->acc->prpl->chat_leave( c );
909       
910        /* Remove references in both directions now. We don't need each other anymore. */
911        ic->data = NULL;
912        if( c )
913                c->ui_data = NULL;
914       
915        return TRUE;
916}
917
918static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
919{
920        struct groupchat *c = ic->data;
921       
922        if( c == NULL )
923                return FALSE;
924       
925        if( c->ic->acc->prpl->chat_topic == NULL )
926                irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name );
927        else
928        {
929                /* TODO: Need more const goodness here, sigh */
930                char *topic = g_strdup( new );
931                c->ic->acc->prpl->chat_topic( c, topic );
932                g_free( topic );
933        }
934               
935        /* Whatever happened, the IM module should ack the topic change. */
936        return FALSE;
937}
938
939static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
940{
941        struct groupchat *c = ic->data;
942        bee_user_t *bu = iu->bu;
943       
944        if( bu == NULL )
945                return FALSE;
946       
947        if( c )
948        {
949                if( iu->bu->ic != c->ic )
950                        irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name );
951                else if( c->ic->acc->prpl->chat_invite )
952                        c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL );
953                else
954                        irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
955        }
956        else if( bu->ic->acc->prpl->chat_with &&
957                 strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 )
958        {
959                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
960                iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
961                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
962        }
963        else
964        {
965                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
966        }
967       
968        return TRUE;
969}
970
971static char *set_eval_room_account( set_t *set, char *value );
972static char *set_eval_chat_type( set_t *set, char *value );
973
974static gboolean bee_irc_channel_init( irc_channel_t *ic )
975{
976        set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
977        set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
978        set_add( &ic->set, "nick", NULL, NULL, ic );
979        set_add( &ic->set, "room", NULL, NULL, ic );
980        set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
981       
982        /* chat_type == groupchat */
983        ic->flags |= IRC_CHANNEL_TEMP;
984       
985        return TRUE;
986}
987
988static char *set_eval_room_account( set_t *set, char *value )
989{
990        struct irc_channel *ic = set->data;
991        account_t *acc, *oa;
992       
993        if( !( acc = account_get( ic->irc->b, value ) ) )
994                return SET_INVALID;
995        else if( !acc->prpl->chat_join )
996        {
997                irc_usermsg( ic->irc, "Named chatrooms not supported on that account." );
998                return SET_INVALID;
999        }
1000       
1001        if( set->value && ( oa = account_get( ic->irc->b, set->value ) ) &&
1002            oa->prpl->chat_free_settings )
1003                oa->prpl->chat_free_settings( oa, &ic->set );
1004       
1005        if( acc->prpl->chat_add_settings )
1006                acc->prpl->chat_add_settings( acc, &ic->set );
1007       
1008        return g_strdup( acc->tag );
1009}
1010
1011static char *set_eval_chat_type( set_t *set, char *value )
1012{
1013        struct irc_channel *ic = set->data;
1014       
1015        if( strcmp( value, "groupchat" ) == 0 )
1016                ic->flags |= IRC_CHANNEL_TEMP;
1017        else if( strcmp( value, "room" ) == 0 )
1018                ic->flags &= ~IRC_CHANNEL_TEMP;
1019        else
1020                return NULL;
1021       
1022        return value;
1023}
1024
1025static gboolean bee_irc_channel_free( irc_channel_t *ic )
1026{
1027        struct groupchat *c = ic->data;
1028       
1029        set_del( &ic->set, "account" );
1030        set_del( &ic->set, "chat_type" );
1031        set_del( &ic->set, "nick" );
1032        set_del( &ic->set, "room" );
1033        set_del( &ic->set, "translate_to_nicks" );
1034       
1035        ic->flags &= ~IRC_CHANNEL_TEMP;
1036       
1037        /* That one still points at this channel. Don't. */
1038        if( c )
1039                c->ui_data = NULL;
1040       
1041        return TRUE;
1042}
1043
1044const struct irc_channel_funcs irc_channel_im_chat_funcs = {
1045        bee_irc_channel_chat_privmsg,
1046        bee_irc_channel_chat_join,
1047        bee_irc_channel_chat_part,
1048        bee_irc_channel_chat_topic,
1049        bee_irc_channel_chat_invite,
1050
1051        bee_irc_channel_init,
1052        bee_irc_channel_free,
1053};
1054
1055
1056/* IM->IRC: File transfers */
1057static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
1058{
1059        return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
1060}
1061
1062static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
1063{
1064        return dccs_recv_start( ft );
1065}
1066
1067static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
1068{
1069        return dcc_close( ft );
1070}
1071
1072static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
1073{
1074        dcc_file_transfer_t *df = file->priv;
1075
1076        if( file->bytes_transferred >= file->file_size )
1077                dcc_finish( file );
1078        else
1079                df->proto_finished = TRUE;
1080}
1081
1082const struct bee_ui_funcs irc_ui_funcs = {
1083        bee_irc_imc_connected,
1084        bee_irc_imc_disconnected,
1085       
1086        bee_irc_user_new,
1087        bee_irc_user_free,
1088        bee_irc_user_fullname,
1089        bee_irc_user_nick_hint,
1090        bee_irc_user_group,
1091        bee_irc_user_status,
1092        bee_irc_user_msg,
1093        bee_irc_user_typing,
1094        bee_irc_user_action_response,
1095       
1096        bee_irc_chat_new,
1097        bee_irc_chat_free,
1098        bee_irc_chat_log,
1099        bee_irc_chat_msg,
1100        bee_irc_chat_add_user,
1101        bee_irc_chat_remove_user,
1102        bee_irc_chat_topic,
1103        bee_irc_chat_name_hint,
1104        bee_irc_chat_invite,
1105       
1106        bee_irc_ft_in_start,
1107        bee_irc_ft_out_start,
1108        bee_irc_ft_close,
1109        bee_irc_ft_finished,
1110};
Note: See TracBrowser for help on using the repository browser.