source: irc_im.c @ 52bba15

Last change on this file since 52bba15 was 6e9ae72, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-12-17T13:50:01Z

Mainline merge.

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