source: irc_im.c @ 5c7b45c

Last change on this file since 5c7b45c was 5c7b45c, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-04T13:36:08Z

Auto joins for chatrooms.

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