source: irc_im.c @ bb151f7

Last change on this file since bb151f7 was bb151f7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-11-20T20:25:44Z

Added irc_channel_with_user() function to find a suitable channel to show
a user's message in, instead of just &bitlbee by default.

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