source: irc_im.c @ a429907

Last change on this file since a429907 was a429907, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-12-05T12:28:07Z

rename -del

  • Property mode set to 100644
File size: 25.3 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_nick_update( irc_user_t *iu );
283
284static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu )
285{
286        irc_user_t *iu = (irc_user_t *) bu->ui_data;
287        char *s;
288       
289        if( iu->fullname != iu->nick )
290                g_free( iu->fullname );
291        iu->fullname = g_strdup( bu->fullname );
292       
293        /* Strip newlines (unlikely, but IRC-unfriendly so they must go)
294           TODO(wilmer): Do the same with away msgs again! */
295        for( s = iu->fullname; *s; s ++ )
296                if( isspace( *s ) ) *s = ' ';
297       
298        if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) )
299        {
300                /* People don't like this /NOTICE. Meh, let's go back to the old one.
301                char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
302                irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
303                */
304                imcb_log( bu->ic, "User `%s' changed name to `%s'", iu->nick, iu->fullname );
305        }
306       
307        bee_irc_user_nick_update( iu );
308       
309        return TRUE;
310}
311
312static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint )
313{
314        bee_irc_user_nick_update( (irc_user_t*) bu->ui_data );
315       
316        return TRUE;
317}
318
319static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu )
320{
321        irc_user_t *iu = (irc_user_t *) bu->ui_data;
322        irc_t *irc = (irc_t *) bee->ui_data;
323        bee_user_flags_t online;
324       
325        /* Take the user offline temporarily so we can change the nick (if necessary). */
326        if( ( online = bu->flags & BEE_USER_ONLINE ) )
327                bu->flags &= ~BEE_USER_ONLINE;
328       
329        bee_irc_channel_update( irc, NULL, iu );
330        bee_irc_user_nick_update( iu );
331       
332        if( online )
333        {
334                bu->flags |= online;
335                bee_irc_channel_update( irc, NULL, iu );
336        }
337       
338        return TRUE;
339}
340
341static gboolean bee_irc_user_nick_update( irc_user_t *iu )
342{
343        bee_user_t *bu = iu->bu;
344        char *newnick;
345       
346        if( bu->flags & BEE_USER_ONLINE )
347                /* Ignore if the user is visible already. */
348                return TRUE;
349       
350        if( nick_saved( bu ) )
351                /* The user already assigned a nickname to this person. */
352                return TRUE;
353       
354        newnick = nick_get( bu );
355       
356        if( strcmp( iu->nick, newnick ) != 0 )
357        {
358                nick_dedupe( bu, newnick );
359                irc_user_set_nick( iu, newnick );
360        }
361       
362        return TRUE;
363}
364
365void bee_irc_user_nick_reset( irc_user_t *iu )
366{
367        bee_user_t *bu = iu->bu;
368        bee_user_flags_t online;
369       
370        if( bu == FALSE )
371                return;
372       
373        /* In this case, pretend the user is offline. */
374        if( ( online = bu->flags & BEE_USER_ONLINE ) )
375                bu->flags &= ~BEE_USER_ONLINE;
376       
377        nick_del( bu );
378        bee_irc_user_nick_update( iu );
379       
380        bu->flags |= online;
381}
382
383/* IRC->IM calls */
384
385static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
386
387static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg )
388{
389        const char *away;
390       
391        if( iu->bu == NULL )
392                return FALSE;
393       
394        if( ( away = irc_user_get_away( iu ) ) &&
395            time( NULL ) >= iu->away_reply_timeout )
396        {
397                irc_send_num( iu->irc, 301, "%s :%s", iu->nick, away );
398                iu->away_reply_timeout = time( NULL ) +
399                        set_getint( &iu->irc->b->set, "away_reply_timeout" );
400        }
401       
402        if( iu->pastebuf == NULL )
403                iu->pastebuf = g_string_new( msg );
404        else
405        {
406                b_event_remove( iu->pastebuf_timer );
407                g_string_append_printf( iu->pastebuf, "\n%s", msg );
408        }
409       
410        if( set_getbool( &iu->irc->b->set, "paste_buffer" ) )
411        {
412                int delay;
413               
414                if( ( delay = set_getint( &iu->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
415                        delay *= 1000;
416               
417                iu->pastebuf_timer = b_timeout_add( delay, bee_irc_user_privmsg_cb, iu );
418               
419                return TRUE;
420        }
421        else
422        {
423                bee_irc_user_privmsg_cb( iu, 0, 0 );
424               
425                return TRUE;
426        }
427}
428
429static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
430{
431        irc_user_t *iu = data;
432        char *msg = g_string_free( iu->pastebuf, FALSE );
433        GSList *l;
434       
435        for( l = irc_plugins; l; l = l->next )
436        {
437                irc_plugin_t *p = l->data;
438                if( p->filter_msg_out )
439                {
440                        char *s = p->filter_msg_out( iu, msg, 0 );
441                        if( s )
442                        {
443                                if( s != msg )
444                                        g_free( msg );
445                                msg = s;
446                        }
447                        else
448                        {
449                                /* Modules can swallow messages. */
450                                iu->pastebuf = NULL;
451                                g_free( msg );
452                                return FALSE;
453                        }
454                }
455        }
456       
457        bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
458       
459        g_free( msg );
460        iu->pastebuf = NULL;
461        iu->pastebuf_timer = 0;
462       
463        return FALSE;
464}
465
466static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp )
467{
468        if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0
469                    && g_strcasecmp( ctcp[1], "SEND" ) == 0 )
470        {
471                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
472                {
473                        file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp );
474                        if ( ft )
475                                iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle );
476                       
477                        return TRUE;
478                }
479        }
480        else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 )
481        {
482                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] )
483                {
484                        int st = ctcp[1][0];
485                        if( st >= '0' && st <= '2' )
486                        {
487                                st <<= 8;
488                                iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st );
489                        }
490                       
491                        return TRUE;
492                }
493        }
494       
495        return FALSE;
496}
497
498static const struct irc_user_funcs irc_user_im_funcs = {
499        bee_irc_user_privmsg,
500        bee_irc_user_ctcp,
501};
502
503
504/* IM->IRC: Groupchats */
505const struct irc_channel_funcs irc_channel_im_chat_funcs;
506
507static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c )
508{
509        irc_t *irc = bee->ui_data;
510        irc_channel_t *ic;
511        char *topic;
512        GSList *l;
513        int i;
514       
515        /* Try to find a channel that expects to receive a groupchat.
516           This flag is set earlier in our current call trace. */
517        for( l = irc->channels; l; l = l->next )
518        {
519                ic = l->data;
520                if( ic->flags & IRC_CHANNEL_CHAT_PICKME )
521                        break;
522        }
523       
524        /* If we found none, just generate some stupid name. */
525        if( l == NULL ) for( i = 0; i <= 999; i ++ )
526        {
527                char name[16];
528                sprintf( name, "#chat_%03d", i );
529                if( ( ic = irc_channel_new( irc, name ) ) )
530                        break;
531        }
532       
533        if( ic == NULL )
534                return FALSE;
535       
536        c->ui_data = ic;
537        ic->data = c;
538       
539        topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
540        irc_channel_set_topic( ic, topic, irc->root );
541        g_free( topic );
542       
543        return TRUE;
544}
545
546static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
547{
548        irc_channel_t *ic = c->ui_data;
549       
550        if( ic == NULL )
551                return FALSE;
552       
553        if( ic->flags & IRC_CHANNEL_JOINED )
554                irc_channel_printf( ic, "Cleaning up channel, bye!" );
555       
556        ic->data = NULL;
557        irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
558       
559        return TRUE;
560}
561
562static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text )
563{
564        irc_channel_t *ic = c->ui_data;
565       
566        if( ic == NULL )
567                return FALSE;
568       
569        irc_channel_printf( ic, "%s", text );
570       
571        return TRUE;
572}
573
574static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at )
575{
576        irc_t *irc = bee->ui_data;
577        irc_user_t *iu = bu->ui_data;
578        irc_channel_t *ic = c->ui_data;
579        char *ts = NULL;
580       
581        if( ic == NULL )
582                return FALSE;
583       
584        if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) )
585                ts = irc_format_timestamp( irc, sent_at );
586       
587        irc_send_msg( iu, "PRIVMSG", ic->name, msg, ts );
588        g_free( ts );
589       
590        return TRUE;
591}
592
593static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
594{
595        irc_t *irc = bee->ui_data;
596        irc_channel_t *ic = c->ui_data;
597       
598        if( ic == NULL )
599                return FALSE;
600       
601        irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data );
602       
603        return TRUE;
604}
605
606static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
607{
608        irc_t *irc = bee->ui_data;
609        irc_channel_t *ic = c->ui_data;
610       
611        if( ic == NULL || bu == NULL )
612                return FALSE;
613       
614        /* TODO: Possible bug here: If a module removes $user here instead of just
615           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
616           a broken state around here. */
617        irc_channel_del_user( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL );
618       
619        return TRUE;
620}
621
622static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu )
623{
624        irc_channel_t *ic = c->ui_data;
625        irc_t *irc = bee->ui_data;
626        irc_user_t *iu;
627       
628        if( ic == NULL )
629                return FALSE;
630       
631        if( bu == NULL )
632                iu = irc->root;
633        else if( bu == bee->user )
634                iu = irc->user;
635        else
636                iu = bu->ui_data;
637       
638        irc_channel_set_topic( ic, new, iu );
639       
640        return TRUE;
641}
642
643static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
644{
645        irc_t *irc = bee->ui_data;
646        irc_channel_t *ic = c->ui_data, *oic;
647        char stripped[MAX_NICK_LENGTH+1], *full_name;
648       
649        if( ic == NULL )
650                return FALSE;
651       
652        /* Don't rename a channel if the user's in it already. */
653        if( ic->flags & IRC_CHANNEL_JOINED )
654                return FALSE;
655       
656        strncpy( stripped, name, MAX_NICK_LENGTH );
657        stripped[MAX_NICK_LENGTH] = '\0';
658        irc_channel_name_strip( stripped );
659        if( set_getbool( &bee->set, "lcnicks" ) )
660                nick_lc( stripped );
661       
662        if( stripped[0] == '\0' )
663                return FALSE;
664       
665        full_name = g_strdup_printf( "#%s", stripped );
666        if( ( oic = irc_channel_by_name( irc, full_name ) ) )
667        {
668                char *type, *chat_type;
669               
670                type = set_getstr( &oic->set, "type" );
671                chat_type = set_getstr( &oic->set, "chat_type" );
672               
673                if( type && chat_type && oic->data == FALSE &&
674                    strcmp( type, "chat" ) == 0 &&
675                    strcmp( chat_type, "groupchat" ) == 0 )
676                {
677                        /* There's a channel with this name already, but it looks
678                           like it's not in use yet. Most likely the IRC client
679                           rejoined the channel after a reconnect. Remove it so
680                           we can reuse its name. */
681                        irc_channel_free( oic );
682                }
683                else
684                {
685                        g_free( full_name );
686                        return FALSE;
687                }
688        }
689       
690        g_free( ic->name );
691        ic->name = full_name;
692       
693        return TRUE;
694}
695
696static gboolean bee_irc_chat_invite( bee_t *bee, bee_user_t *bu, const char *name, const char *msg )
697{
698        char *channel, *s;
699        irc_t *irc = bee->ui_data;
700        irc_user_t *iu = bu->ui_data;
701        irc_channel_t *chan;
702       
703        if( strchr( CTYPES, name[0] ) )
704                channel = g_strdup( name );
705        else
706                channel = g_strdup_printf( "#%s", name );
707       
708        if( ( s = strchr( channel, '@' ) ) )
709                *s = '\0';
710       
711        if( strlen( channel ) > MAX_NICK_LENGTH )
712        {
713                /* If the channel name is very long (like those insane GTalk
714                   UUID names), try if we can use the inviter's nick. */
715                s = g_strdup_printf( "#%s", iu->nick );
716                if( irc_channel_by_name( irc, s ) == NULL )
717                {
718                        g_free( channel );
719                        channel = s;
720                }
721        }
722       
723        if( ( chan = irc_channel_new( irc, channel ) ) &&
724            set_setstr( &chan->set, "type", "chat" ) &&
725            set_setstr( &chan->set, "chat_type", "room" ) &&
726            set_setstr( &chan->set, "account", bu->ic->acc->tag ) &&
727            set_setstr( &chan->set, "room", (char*) name ) )
728        {
729                /* I'm assuming that if the user didn't "chat add" the room
730                   himself but got invited, it's temporary, so make this a
731                   temporary mapping that is removed as soon as we /PART. */
732                chan->flags |= IRC_CHANNEL_TEMP;
733        }
734        else
735        {
736                irc_channel_free( chan );
737                chan = NULL;
738        }
739        g_free( channel );
740       
741        irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name );
742        if( msg )
743                irc_send_msg( iu, "PRIVMSG", irc->user->nick, msg, NULL );
744        if( chan )
745        {
746                irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name );
747                irc_send_invite( iu, chan );
748        }
749       
750        return TRUE;
751}
752
753/* IRC->IM */
754static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
755
756static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
757{
758        struct groupchat *c = ic->data;
759        char *trans = NULL, *s;
760       
761        if( c == NULL )
762                return FALSE;
763       
764        if( set_getbool( &ic->set, "translate_to_nicks" ) )
765        {
766                char nick[MAX_NICK_LENGTH+1];
767                irc_user_t *iu;
768               
769                strncpy( nick, msg, MAX_NICK_LENGTH );
770                nick[MAX_NICK_LENGTH] = '\0';
771                if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) )
772                {
773                        *s = '\0';
774                        if( ( iu = irc_user_by_name( ic->irc, nick ) ) && iu->bu &&
775                            iu->bu->nick && irc_channel_has_user( ic, iu ) )
776                        {
777                                trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL );
778                                msg = trans;
779                        }
780                }
781        }
782       
783        if( set_getbool( &ic->irc->b->set, "paste_buffer" ) )
784        {
785                int delay;
786               
787                if( ic->pastebuf == NULL )
788                        ic->pastebuf = g_string_new( msg );
789                else
790                {
791                        b_event_remove( ic->pastebuf_timer );
792                        g_string_append_printf( ic->pastebuf, "\n%s", msg );
793                }
794               
795                if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
796                        delay *= 1000;
797               
798                ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic );
799               
800                g_free( trans );
801                return TRUE;
802        }
803        else
804                bee_chat_msg( ic->irc->b, c, msg, 0 );
805       
806        g_free( trans );
807        return TRUE;
808}
809
810static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
811{
812        irc_channel_t *ic = data;
813       
814        if( ic->data )
815                bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
816       
817        g_string_free( ic->pastebuf, TRUE );
818        ic->pastebuf = 0;
819        ic->pastebuf_timer = 0;
820       
821        return FALSE;
822}
823
824static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
825{
826        char *acc_s, *room;
827        account_t *acc;
828       
829        if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 )
830                return TRUE;
831       
832        if( ( acc_s = set_getstr( &ic->set, "account" ) ) &&
833            ( room = set_getstr( &ic->set, "room" ) ) &&
834            ( acc = account_get( ic->irc->b, acc_s ) ) &&
835            acc->ic && acc->prpl->chat_join )
836        {
837                char *nick;
838               
839                if( !( nick = set_getstr( &ic->set, "nick" ) ) )
840                        nick = ic->irc->user->nick;
841               
842                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
843                acc->prpl->chat_join( acc->ic, room, nick, NULL, &ic->set );
844                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
845               
846                return FALSE;
847        }
848        else
849        {
850                irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name );
851                return FALSE;
852        }
853}
854
855static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
856{
857        struct groupchat *c = ic->data;
858       
859        if( c && c->ic->acc->prpl->chat_leave )
860                c->ic->acc->prpl->chat_leave( c );
861       
862        ic->data = NULL;
863       
864        return TRUE;
865}
866
867static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
868{
869        struct groupchat *c = ic->data;
870       
871        if( c == NULL )
872                return FALSE;
873       
874        if( c->ic->acc->prpl->chat_topic == NULL )
875                irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name );
876        else
877        {
878                /* TODO: Need more const goodness here, sigh */
879                char *topic = g_strdup( new );
880                c->ic->acc->prpl->chat_topic( c, topic );
881                g_free( topic );
882        }
883               
884        /* Whatever happened, the IM module should ack the topic change. */
885        return FALSE;
886}
887
888static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
889{
890        struct groupchat *c = ic->data;
891        bee_user_t *bu = iu->bu;
892       
893        if( bu == NULL )
894                return FALSE;
895       
896        if( c )
897        {
898                if( iu->bu->ic != c->ic )
899                        irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name );
900                else if( c->ic->acc->prpl->chat_invite )
901                        c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL );
902                else
903                        irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
904        }
905        else if( bu->ic->acc->prpl->chat_with &&
906                 strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 )
907        {
908                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
909                iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
910                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
911        }
912        else
913        {
914                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
915        }
916       
917        return TRUE;
918}
919
920static char *set_eval_room_account( set_t *set, char *value );
921static char *set_eval_chat_type( set_t *set, char *value );
922
923static gboolean bee_irc_channel_init( irc_channel_t *ic )
924{
925        set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
926        set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
927        set_add( &ic->set, "nick", NULL, NULL, ic );
928        set_add( &ic->set, "room", NULL, NULL, ic );
929        set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
930       
931        /* chat_type == groupchat */
932        ic->flags |= IRC_CHANNEL_TEMP;
933       
934        return TRUE;
935}
936
937static char *set_eval_room_account( set_t *set, char *value )
938{
939        struct irc_channel *ic = set->data;
940        account_t *acc, *oa;
941       
942        if( !( acc = account_get( ic->irc->b, value ) ) )
943                return SET_INVALID;
944        else if( !acc->prpl->chat_join )
945        {
946                irc_usermsg( ic->irc, "Named chatrooms not supported on that account." );
947                return SET_INVALID;
948        }
949       
950        if( set->value && ( oa = account_get( ic->irc->b, set->value ) ) &&
951            oa->prpl->chat_free_settings )
952                oa->prpl->chat_free_settings( oa, &ic->set );
953       
954        if( acc->prpl->chat_add_settings )
955                acc->prpl->chat_add_settings( acc, &ic->set );
956       
957        return g_strdup( acc->tag );
958}
959
960static char *set_eval_chat_type( set_t *set, char *value )
961{
962        struct irc_channel *ic = set->data;
963       
964        if( strcmp( value, "groupchat" ) == 0 )
965                ic->flags |= IRC_CHANNEL_TEMP;
966        else if( strcmp( value, "room" ) == 0 )
967                ic->flags &= ~IRC_CHANNEL_TEMP;
968        else
969                return NULL;
970       
971        return value;
972}
973
974static gboolean bee_irc_channel_free( irc_channel_t *ic )
975{
976        struct groupchat *c = ic->data;
977       
978        set_del( &ic->set, "account" );
979        set_del( &ic->set, "chat_type" );
980        set_del( &ic->set, "nick" );
981        set_del( &ic->set, "room" );
982        set_del( &ic->set, "translate_to_nicks" );
983       
984        ic->flags &= ~IRC_CHANNEL_TEMP;
985       
986        /* That one still points at this channel. Don't. */
987        if( c )
988                c->ui_data = NULL;
989       
990        return TRUE;
991}
992
993const struct irc_channel_funcs irc_channel_im_chat_funcs = {
994        bee_irc_channel_chat_privmsg,
995        bee_irc_channel_chat_join,
996        bee_irc_channel_chat_part,
997        bee_irc_channel_chat_topic,
998        bee_irc_channel_chat_invite,
999
1000        bee_irc_channel_init,
1001        bee_irc_channel_free,
1002};
1003
1004
1005/* IM->IRC: File transfers */
1006static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
1007{
1008        return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
1009}
1010
1011static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
1012{
1013        return dccs_recv_start( ft );
1014}
1015
1016static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
1017{
1018        return dcc_close( ft );
1019}
1020
1021static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
1022{
1023        dcc_file_transfer_t *df = file->priv;
1024
1025        if( file->bytes_transferred >= file->file_size )
1026                dcc_finish( file );
1027        else
1028                df->proto_finished = TRUE;
1029}
1030
1031const struct bee_ui_funcs irc_ui_funcs = {
1032        bee_irc_imc_connected,
1033        bee_irc_imc_disconnected,
1034       
1035        bee_irc_user_new,
1036        bee_irc_user_free,
1037        bee_irc_user_fullname,
1038        bee_irc_user_nick_hint,
1039        bee_irc_user_group,
1040        bee_irc_user_status,
1041        bee_irc_user_msg,
1042        bee_irc_user_typing,
1043       
1044        bee_irc_chat_new,
1045        bee_irc_chat_free,
1046        bee_irc_chat_log,
1047        bee_irc_chat_msg,
1048        bee_irc_chat_add_user,
1049        bee_irc_chat_remove_user,
1050        bee_irc_chat_topic,
1051        bee_irc_chat_name_hint,
1052        bee_irc_chat_invite,
1053       
1054        bee_irc_ft_in_start,
1055        bee_irc_ft_out_start,
1056        bee_irc_ft_close,
1057        bee_irc_ft_finished,
1058};
Note: See TracBrowser for help on using the repository browser.