source: irc_im.c @ a08e875

Last change on this file since a08e875 was b1af3e8, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-13T22:50:06Z

Fixed cleaning up of channels. Something broke when changing the
irc_channel_del_user() syntax.

  • Property mode set to 100644
File size: 21.8 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Some glue to put the IRC and the IM stuff together.                  */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#include "bitlbee.h"
27#include "dcc.h"
28
29/* IM->IRC callbacks: Simple IM/buddy-related stuff. */
30
31static const struct irc_user_funcs irc_user_im_funcs;
32
33static void bee_irc_imc_connected( struct im_connection *ic )
34{
35        irc_t *irc = (irc_t*) ic->bee->ui_data;
36       
37        irc_channel_auto_joins( irc, ic->acc );
38}
39
40static void bee_irc_imc_disconnected( struct im_connection *ic )
41{
42        /* Maybe try to send /QUITs here instead of later on. */
43}
44
45static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu )
46{
47        irc_user_t *iu;
48        irc_t *irc = (irc_t*) bee->ui_data;
49        char nick[MAX_NICK_LENGTH+1], *s;
50       
51        memset( nick, 0, MAX_NICK_LENGTH + 1 );
52        strcpy( nick, nick_get( bu ) );
53       
54        bu->ui_data = iu = irc_user_new( irc, nick );
55        iu->bu = bu;
56       
57        if( ( s = strchr( bu->handle, '@' ) ) )
58        {
59                iu->host = g_strdup( s + 1 );
60                iu->user = g_strndup( bu->handle, s - bu->handle );
61        }
62        else if( bu->ic->acc->server )
63        {
64                iu->host = g_strdup( bu->ic->acc->server );
65                iu->user = g_strdup( bu->handle );
66               
67                /* s/ /_/ ... important for AOL screennames */
68                for( s = iu->user; *s; s ++ )
69                        if( *s == ' ' )
70                                *s = '_';
71        }
72        else
73        {
74                iu->host = g_strdup( bu->ic->acc->prpl->name );
75                iu->user = g_strdup( bu->handle );
76        }
77       
78        if( bu->flags & BEE_USER_LOCAL )
79        {
80                char *s = set_getstr( &bee->set, "handle_unknown" );
81               
82                if( strcmp( s, "add_private" ) == 0 )
83                        iu->last_channel = NULL;
84                else if( strcmp( s, "add_channel" ) == 0 )
85                        iu->last_channel = irc->default_channel;
86        }
87       
88        iu->f = &irc_user_im_funcs;
89       
90        return TRUE;
91}
92
93static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu )
94{
95        return irc_user_free( bee->ui_data, (irc_user_t *) bu->ui_data );
96}
97
98static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old )
99{
100        irc_t *irc = bee->ui_data;
101        irc_user_t *iu = bu->ui_data;
102       
103        /* Do this outside the if below since away state can change without
104           the online state changing. */
105        iu->flags &= ~IRC_USER_AWAY;
106        if( bu->flags & BEE_USER_AWAY || !( bu->flags & BEE_USER_ONLINE ) )
107                iu->flags |= IRC_USER_AWAY;
108       
109        if( ( bu->flags & BEE_USER_ONLINE ) != ( old->flags & BEE_USER_ONLINE ) )
110        {
111                if( bu->flags & BEE_USER_ONLINE )
112                {
113                        if( g_hash_table_lookup( irc->watches, iu->key ) )
114                                irc_send_num( irc, 600, "%s %s %s %d :%s", iu->nick, iu->user,
115                                              iu->host, (int) time( NULL ), "logged online" );
116                }
117                else
118                {
119                        if( g_hash_table_lookup( irc->watches, iu->key ) )
120                                irc_send_num( irc, 601, "%s %s %s %d :%s", iu->nick, iu->user,
121                                              iu->host, (int) time( NULL ), "logged offline" );
122                       
123                        /* Send a QUIT since those will also show up in any
124                           query windows the user may have, plus it's only
125                           one QUIT instead of possibly many (in case of
126                           multiple control chans). If there's a channel that
127                           shows offline people, a JOIN will follow. */
128                        if( set_getbool( &bee->set, "offline_user_quits" ) )
129                                irc_user_quit( iu, "Leaving..." );
130                }
131        }
132       
133        /* Reset this one since the info may have changed. */
134        iu->away_reply_timeout = 0;
135       
136        bee_irc_channel_update( irc, NULL, iu );
137       
138        return TRUE;
139}
140
141void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu )
142{
143        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, IRC_CDU_PART, 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_update( irc_user_t *iu );
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        bee_irc_user_nick_update( iu );
268       
269        return TRUE;
270}
271
272static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint )
273{
274        bee_irc_user_nick_update( (irc_user_t*) bu->ui_data );
275       
276        return TRUE;
277}
278
279static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu )
280{
281        irc_user_t *iu = (irc_user_t *) bu->ui_data;
282        irc_t *irc = (irc_t *) bee->ui_data;
283       
284        bee_irc_channel_update( irc, NULL, iu );
285        bee_irc_user_nick_update( iu );
286       
287        return TRUE;
288}
289
290static gboolean bee_irc_user_nick_update( irc_user_t *iu )
291{
292        bee_user_t *bu = iu->bu;
293        char *newnick;
294       
295        if( bu->flags & BEE_USER_ONLINE )
296                /* Ignore if the user is visible already. */
297                return TRUE;
298       
299        if( nick_saved( bu ) )
300                /* The user already assigned a nickname to this person. */
301                return TRUE;
302       
303        newnick = nick_get( bu );
304       
305        if( strcmp( iu->nick, newnick ) != 0 )
306        {
307                nick_dedupe( bu, newnick );
308                irc_user_set_nick( iu, newnick );
309        }
310       
311        return TRUE;
312}
313
314/* IRC->IM calls */
315
316static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
317
318static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg )
319{
320        const char *away;
321       
322        if( iu->bu == NULL )
323                return FALSE;
324       
325        if( ( away = irc_user_get_away( iu ) ) &&
326            time( NULL ) >= iu->away_reply_timeout )
327        {
328                irc_send_num( iu->irc, 301, "%s :%s", iu->nick, away );
329                iu->away_reply_timeout = time( NULL ) +
330                        set_getint( &iu->irc->b->set, "away_reply_timeout" );
331        }
332       
333        if( set_getbool( &iu->irc->b->set, "paste_buffer" ) )
334        {
335                int delay;
336               
337                if( iu->pastebuf == NULL )
338                        iu->pastebuf = g_string_new( msg );
339                else
340                {
341                        b_event_remove( iu->pastebuf_timer );
342                        g_string_append_printf( iu->pastebuf, "\n%s", msg );
343                }
344               
345                if( ( delay = set_getint( &iu->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
346                        delay *= 1000;
347               
348                iu->pastebuf_timer = b_timeout_add( delay, bee_irc_user_privmsg_cb, iu );
349               
350                return TRUE;
351        }
352        else
353                return bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
354}
355
356static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
357{
358        irc_user_t *iu = data;
359       
360        bee_user_msg( iu->irc->b, iu->bu, iu->pastebuf->str, 0 );
361       
362        g_string_free( iu->pastebuf, TRUE );
363        iu->pastebuf = 0;
364        iu->pastebuf_timer = 0;
365       
366        return FALSE;
367}
368
369static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp )
370{
371        if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0
372                    && g_strcasecmp( ctcp[1], "SEND" ) == 0 )
373        {
374                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
375                {
376                        file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp );
377                        if ( ft )
378                                iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle );
379                       
380                        return TRUE;
381                }
382        }
383        else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 )
384        {
385                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] )
386                {
387                        int st = ctcp[1][0];
388                        if( st >= '0' && st <= '2' )
389                        {
390                                st <<= 8;
391                                iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st );
392                        }
393                       
394                        return TRUE;
395                }
396        }
397       
398        return FALSE;
399}
400
401static const struct irc_user_funcs irc_user_im_funcs = {
402        bee_irc_user_privmsg,
403        bee_irc_user_ctcp,
404};
405
406
407/* IM->IRC: Groupchats */
408const struct irc_channel_funcs irc_channel_im_chat_funcs;
409
410static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c )
411{
412        irc_t *irc = bee->ui_data;
413        irc_channel_t *ic;
414        char *topic;
415        GSList *l;
416        int i;
417       
418        /* Try to find a channel that expects to receive a groupchat.
419           This flag is set earlier in our current call trace. */
420        for( l = irc->channels; l; l = l->next )
421        {
422                ic = l->data;
423                if( ic->flags & IRC_CHANNEL_CHAT_PICKME )
424                        break;
425        }
426       
427        /* If we found none, just generate some stupid name. */
428        if( l == NULL ) for( i = 0; i <= 999; i ++ )
429        {
430                char name[16];
431                sprintf( name, "#chat_%03d", i );
432                if( ( ic = irc_channel_new( irc, name ) ) )
433                        break;
434        }
435       
436        if( ic == NULL )
437                return FALSE;
438       
439        c->ui_data = ic;
440        ic->data = c;
441       
442        topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
443        irc_channel_set_topic( ic, topic, irc->root );
444        g_free( topic );
445       
446        return TRUE;
447}
448
449static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
450{
451        irc_channel_t *ic = c->ui_data;
452       
453        if( ic == NULL )
454                return FALSE;
455       
456        if( ic->flags & IRC_CHANNEL_JOINED )
457                irc_channel_printf( ic, "Cleaning up channel, bye!" );
458       
459        ic->data = NULL;
460        irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
461       
462        return TRUE;
463}
464
465static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text )
466{
467        irc_channel_t *ic = c->ui_data;
468       
469        if( ic == NULL )
470                return FALSE;
471       
472        irc_channel_printf( ic, "%s", text );
473       
474        return TRUE;
475}
476
477static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at )
478{
479        irc_t *irc = bee->ui_data;
480        irc_user_t *iu = bu->ui_data;
481        irc_channel_t *ic = c->ui_data;
482        char *ts = NULL;
483       
484        if( ic == NULL )
485                return FALSE;
486       
487        if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) )
488                ts = irc_format_timestamp( irc, sent_at );
489       
490        irc_send_msg( iu, "PRIVMSG", ic->name, msg, ts );
491        g_free( ts );
492       
493        return TRUE;
494}
495
496static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
497{
498        irc_t *irc = bee->ui_data;
499        irc_channel_t *ic = c->ui_data;
500       
501        if( ic == NULL )
502                return FALSE;
503       
504        irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data );
505       
506        return TRUE;
507}
508
509static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
510{
511        irc_t *irc = bee->ui_data;
512        irc_channel_t *ic = c->ui_data;
513       
514        if( ic == NULL )
515                return FALSE;
516       
517        /* TODO: Possible bug here: If a module removes $user here instead of just
518           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
519           a broken state around here. */
520        irc_channel_del_user( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL );
521       
522        return TRUE;
523}
524
525static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu )
526{
527        irc_channel_t *ic = c->ui_data;
528        irc_t *irc = bee->ui_data;
529        irc_user_t *iu;
530       
531        if( ic == NULL )
532                return FALSE;
533       
534        if( bu == NULL )
535                iu = irc->root;
536        else if( bu == bee->user )
537                iu = irc->user;
538        else
539                iu = bu->ui_data;
540       
541        irc_channel_set_topic( ic, new, iu );
542       
543        return TRUE;
544}
545
546static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
547{
548        irc_t *irc = bee->ui_data;
549        irc_channel_t *ic = c->ui_data, *oic;
550        char stripped[MAX_NICK_LENGTH+1], *full_name;
551       
552        if( ic == NULL )
553                return FALSE;
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        char *trans = NULL, *s;
606       
607        if( c == NULL )
608                return FALSE;
609       
610        if( set_getbool( &ic->set, "translate_to_nicks" ) )
611        {
612                char nick[MAX_NICK_LENGTH+1];
613                irc_user_t *iu;
614               
615                strncpy( nick, msg, MAX_NICK_LENGTH );
616                nick[MAX_NICK_LENGTH] = '\0';
617                if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) )
618                {
619                        *s = '\0';
620                        if( ( iu = irc_user_by_name( ic->irc, nick ) ) &&
621                            iu->bu->nick && irc_channel_has_user( ic, iu ) )
622                        {
623                                trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL );
624                                msg = trans;
625                        }
626                }
627        }
628       
629        if( set_getbool( &ic->irc->b->set, "paste_buffer" ) )
630        {
631                int delay;
632               
633                if( ic->pastebuf == NULL )
634                        ic->pastebuf = g_string_new( msg );
635                else
636                {
637                        b_event_remove( ic->pastebuf_timer );
638                        g_string_append_printf( ic->pastebuf, "\n%s", msg );
639                }
640               
641                if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
642                        delay *= 1000;
643               
644                ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic );
645               
646                g_free( trans );
647                return TRUE;
648        }
649        else
650                bee_chat_msg( ic->irc->b, c, msg, 0 );
651       
652        g_free( trans );
653        return TRUE;
654}
655
656static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
657{
658        irc_channel_t *ic = data;
659       
660        bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
661       
662        g_string_free( ic->pastebuf, TRUE );
663        ic->pastebuf = 0;
664        ic->pastebuf_timer = 0;
665       
666        return FALSE;
667}
668
669static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
670{
671        char *acc_s, *room;
672        account_t *acc;
673       
674        if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 )
675                return TRUE;
676       
677        if( ( acc_s = set_getstr( &ic->set, "account" ) ) &&
678            ( room = set_getstr( &ic->set, "room" ) ) &&
679            ( acc = account_get( ic->irc->b, acc_s ) ) &&
680            acc->ic && acc->prpl->chat_join )
681        {
682                char *nick;
683               
684                if( !( nick = set_getstr( &ic->set, "nick" ) ) )
685                        nick = ic->irc->user->nick;
686               
687                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
688                acc->prpl->chat_join( acc->ic, room, nick, NULL );
689                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
690               
691                return FALSE;
692        }
693        else
694        {
695                irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name );
696                return FALSE;
697        }
698}
699
700static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
701{
702        struct groupchat *c = ic->data;
703       
704        if( c && c->ic->acc->prpl->chat_leave )
705                c->ic->acc->prpl->chat_leave( c );
706       
707        return TRUE;
708}
709
710static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
711{
712        struct groupchat *c = ic->data;
713       
714        if( c == NULL )
715                return FALSE;
716       
717        if( c->ic->acc->prpl->chat_topic == NULL )
718                irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name );
719        else
720        {
721                /* TODO: Need more const goodness here, sigh */
722                char *topic = g_strdup( new );
723                c->ic->acc->prpl->chat_topic( c, topic );
724                g_free( topic );
725                return TRUE;
726        }
727               
728        return FALSE;
729}
730
731static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
732{
733        struct groupchat *c = ic->data;
734        bee_user_t *bu = iu->bu;
735       
736        if( bu == NULL )
737                return FALSE;
738       
739        if( c )
740        {
741                if( iu->bu->ic != c->ic )
742                        irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name );
743                else if( c->ic->acc->prpl->chat_invite )
744                        c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL );
745                else
746                        irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
747        }
748        else if( bu->ic->acc->prpl->chat_with &&
749                 strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 )
750        {
751                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
752                iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
753                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
754        }
755        else
756        {
757                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
758        }
759       
760        return TRUE;
761}
762
763static char *set_eval_room_account( set_t *set, char *value );
764static char *set_eval_chat_type( set_t *set, char *value );
765
766static gboolean bee_irc_channel_init( irc_channel_t *ic )
767{
768        set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
769        set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
770        set_add( &ic->set, "nick", NULL, NULL, ic );
771        set_add( &ic->set, "room", NULL, NULL, ic );
772        set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
773       
774        /* chat_type == groupchat */
775        ic->flags |= IRC_CHANNEL_TEMP;
776       
777        return TRUE;
778}
779
780static char *set_eval_room_account( set_t *set, char *value )
781{
782        struct irc_channel *ic = set->data;
783        account_t *acc;
784       
785        if( !( acc = account_get( ic->irc->b, value ) ) )
786                return SET_INVALID;
787        else if( !acc->prpl->chat_join )
788        {
789                irc_usermsg( ic->irc, "Named chatrooms not supported on that account." );
790                return SET_INVALID;
791        }
792       
793        return g_strdup_printf( "%s(%s)", acc->prpl->name, acc->user );
794}
795
796static char *set_eval_chat_type( set_t *set, char *value )
797{
798        struct irc_channel *ic = set->data;
799       
800        if( strcmp( value, "groupchat" ) == 0 )
801                ic->flags |= IRC_CHANNEL_TEMP;
802        else if( strcmp( value, "room" ) == 0 )
803                ic->flags &= ~IRC_CHANNEL_TEMP;
804        else
805                return NULL;
806       
807        return value;
808}
809
810static gboolean bee_irc_channel_free( irc_channel_t *ic )
811{
812        struct groupchat *c = ic->data;
813       
814        set_del( &ic->set, "account" );
815        set_del( &ic->set, "chat_type" );
816        set_del( &ic->set, "nick" );
817        set_del( &ic->set, "room" );
818        set_del( &ic->set, "translate_to_nicks" );
819       
820        ic->flags &= ~IRC_CHANNEL_TEMP;
821       
822        /* That one still points at this channel. Don't. */
823        if( c )
824                c->ui_data = NULL;
825       
826        return TRUE;
827}
828
829const struct irc_channel_funcs irc_channel_im_chat_funcs = {
830        bee_irc_channel_chat_privmsg,
831        bee_irc_channel_chat_join,
832        bee_irc_channel_chat_part,
833        bee_irc_channel_chat_topic,
834        bee_irc_channel_chat_invite,
835
836        bee_irc_channel_init,
837        bee_irc_channel_free,
838};
839
840
841/* IM->IRC: File transfers */
842static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
843{
844        return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
845}
846
847static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
848{
849        return dccs_recv_start( ft );
850}
851
852static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
853{
854        return dcc_close( ft );
855}
856
857static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
858{
859        dcc_file_transfer_t *df = file->priv;
860
861        if( file->bytes_transferred >= file->file_size )
862                dcc_finish( file );
863        else
864                df->proto_finished = TRUE;
865}
866
867const struct bee_ui_funcs irc_ui_funcs = {
868        bee_irc_imc_connected,
869        bee_irc_imc_disconnected,
870       
871        bee_irc_user_new,
872        bee_irc_user_free,
873        bee_irc_user_fullname,
874        bee_irc_user_nick_hint,
875        bee_irc_user_group,
876        bee_irc_user_status,
877        bee_irc_user_msg,
878        bee_irc_user_typing,
879       
880        bee_irc_chat_new,
881        bee_irc_chat_free,
882        bee_irc_chat_log,
883        bee_irc_chat_msg,
884        bee_irc_chat_add_user,
885        bee_irc_chat_remove_user,
886        bee_irc_chat_topic,
887        bee_irc_chat_name_hint,
888       
889        bee_irc_ft_in_start,
890        bee_irc_ft_out_start,
891        bee_irc_ft_close,
892        bee_irc_ft_finished,
893};
Note: See TracBrowser for help on using the repository browser.