source: irc_im.c @ bd599b9

Last change on this file since bd599b9 was 1aa74f55, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-23T10:34:36Z

Process incoming XMPP groupchat invites in a saner way: Create a temporary
channel the user can easily /join.

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