source: irc_im.c @ 5a61bf59

Last change on this file since 5a61bf59 was 94d5da9c, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-18T22:12:19Z

One of the last few things I wanted to get done in this branch: combining
show_offline and away_devoice and possibly other ideas into one setting
called show_users. Documentation will come soon. :-P

  • 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 match = 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( icc->type == IRC_CC_TYPE_DEFAULT )
173                match = TRUE;
174        else if( icc->type == IRC_CC_TYPE_GROUP )
175                match = iu->bu->group == icc->group;
176        else if( icc->type == IRC_CC_TYPE_ACCOUNT )
177                match = iu->bu->ic->acc == icc->account;
178        else if( icc->type == IRC_CC_TYPE_PROTOCOL )
179                match = iu->bu->ic->acc->prpl == icc->protocol;
180       
181        if( !match )
182        {
183                irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
184        }
185        else
186        {
187                int mode = 0;
188               
189                if( !( iu->bu->flags & BEE_USER_ONLINE ) )
190                        mode = icc->modes[0];
191                else if( iu->bu->flags & BEE_USER_AWAY )
192                        mode = icc->modes[1];
193                else
194                        mode = icc->modes[2];
195               
196                if( !mode )
197                        irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
198                else
199                {
200                        irc_channel_add_user( ic, iu );
201                        irc_channel_user_set_mode( ic, iu, mode );
202                }
203        }
204}
205
206static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at )
207{
208        irc_t *irc = bee->ui_data;
209        irc_user_t *iu = (irc_user_t *) bu->ui_data;
210        char *dst, *prefix = NULL;
211        char *wrapped, *ts = NULL;
212       
213        if( sent_at > 0 && set_getbool( &irc->b->set, "display_timestamps" ) )
214                ts = irc_format_timestamp( irc, sent_at );
215       
216        if( iu->last_channel )
217        {
218                dst = iu->last_channel->name;
219                prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ? : "" );
220        }
221        else
222        {
223                dst = irc->user->nick;
224                prefix = ts;
225                ts = NULL;
226        }
227       
228        wrapped = word_wrap( msg, 425 );
229        irc_send_msg( iu, "PRIVMSG", dst, wrapped, prefix );
230       
231        g_free( wrapped );
232        g_free( prefix );
233        g_free( ts );
234       
235        return TRUE;
236}
237
238static gboolean bee_irc_user_typing( bee_t *bee, bee_user_t *bu, uint32_t flags )
239{
240        irc_t *irc = (irc_t *) bee->ui_data;
241       
242        if( set_getbool( &bee->set, "typing_notice" ) )
243                irc_send_msg_f( (irc_user_t *) bu->ui_data, "PRIVMSG", irc->user->nick,
244                                "\001TYPING %d\001", ( flags >> 8 ) & 3 );
245        else
246                return FALSE;
247       
248        return TRUE;
249}
250
251static gboolean bee_irc_user_nick_update( irc_user_t *iu );
252
253static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu )
254{
255        irc_user_t *iu = (irc_user_t *) bu->ui_data;
256        irc_t *irc = (irc_t *) bee->ui_data;
257        char *s;
258       
259        if( iu->fullname != iu->nick )
260                g_free( iu->fullname );
261        iu->fullname = g_strdup( bu->fullname );
262       
263        /* Strip newlines (unlikely, but IRC-unfriendly so they must go)
264           TODO(wilmer): Do the same with away msgs again! */
265        for( s = iu->fullname; *s; s ++ )
266                if( isspace( *s ) ) *s = ' ';
267       
268        if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) )
269        {
270                char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
271                irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
272        }
273       
274        bee_irc_user_nick_update( iu );
275       
276        return TRUE;
277}
278
279static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint )
280{
281        bee_irc_user_nick_update( (irc_user_t*) bu->ui_data );
282       
283        return TRUE;
284}
285
286static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu )
287{
288        irc_user_t *iu = (irc_user_t *) bu->ui_data;
289        irc_t *irc = (irc_t *) bee->ui_data;
290       
291        bee_irc_channel_update( irc, NULL, iu );
292        bee_irc_user_nick_update( iu );
293       
294        return TRUE;
295}
296
297static gboolean bee_irc_user_nick_update( irc_user_t *iu )
298{
299        bee_user_t *bu = iu->bu;
300        char *newnick;
301       
302        if( bu->flags & BEE_USER_ONLINE )
303                /* Ignore if the user is visible already. */
304                return TRUE;
305       
306        if( nick_saved( bu ) )
307                /* The user already assigned a nickname to this person. */
308                return TRUE;
309       
310        newnick = nick_get( bu );
311       
312        if( strcmp( iu->nick, newnick ) != 0 )
313        {
314                nick_dedupe( bu, newnick );
315                irc_user_set_nick( iu, newnick );
316        }
317       
318        return TRUE;
319}
320
321/* IRC->IM calls */
322
323static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
324
325static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg )
326{
327        const char *away;
328       
329        if( iu->bu == NULL )
330                return FALSE;
331       
332        if( ( away = irc_user_get_away( iu ) ) &&
333            time( NULL ) >= iu->away_reply_timeout )
334        {
335                irc_send_num( iu->irc, 301, "%s :%s", iu->nick, away );
336                iu->away_reply_timeout = time( NULL ) +
337                        set_getint( &iu->irc->b->set, "away_reply_timeout" );
338        }
339       
340        if( set_getbool( &iu->irc->b->set, "paste_buffer" ) )
341        {
342                int delay;
343               
344                if( iu->pastebuf == NULL )
345                        iu->pastebuf = g_string_new( msg );
346                else
347                {
348                        b_event_remove( iu->pastebuf_timer );
349                        g_string_append_printf( iu->pastebuf, "\n%s", msg );
350                }
351               
352                if( ( delay = set_getint( &iu->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
353                        delay *= 1000;
354               
355                iu->pastebuf_timer = b_timeout_add( delay, bee_irc_user_privmsg_cb, iu );
356               
357                return TRUE;
358        }
359        else
360                return bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
361}
362
363static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
364{
365        irc_user_t *iu = data;
366       
367        bee_user_msg( iu->irc->b, iu->bu, iu->pastebuf->str, 0 );
368       
369        g_string_free( iu->pastebuf, TRUE );
370        iu->pastebuf = 0;
371        iu->pastebuf_timer = 0;
372       
373        return FALSE;
374}
375
376static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp )
377{
378        if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0
379                    && g_strcasecmp( ctcp[1], "SEND" ) == 0 )
380        {
381                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
382                {
383                        file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp );
384                        if ( ft )
385                                iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle );
386                       
387                        return TRUE;
388                }
389        }
390        else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 )
391        {
392                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] )
393                {
394                        int st = ctcp[1][0];
395                        if( st >= '0' && st <= '2' )
396                        {
397                                st <<= 8;
398                                iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st );
399                        }
400                       
401                        return TRUE;
402                }
403        }
404       
405        return FALSE;
406}
407
408static const struct irc_user_funcs irc_user_im_funcs = {
409        bee_irc_user_privmsg,
410        bee_irc_user_ctcp,
411};
412
413
414/* IM->IRC: Groupchats */
415const struct irc_channel_funcs irc_channel_im_chat_funcs;
416
417static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c )
418{
419        irc_t *irc = bee->ui_data;
420        irc_channel_t *ic;
421        char *topic;
422        GSList *l;
423        int i;
424       
425        /* Try to find a channel that expects to receive a groupchat.
426           This flag is set earlier in our current call trace. */
427        for( l = irc->channels; l; l = l->next )
428        {
429                ic = l->data;
430                if( ic->flags & IRC_CHANNEL_CHAT_PICKME )
431                        break;
432        }
433       
434        /* If we found none, just generate some stupid name. */
435        if( l == NULL ) for( i = 0; i <= 999; i ++ )
436        {
437                char name[16];
438                sprintf( name, "#chat_%03d", i );
439                if( ( ic = irc_channel_new( irc, name ) ) )
440                        break;
441        }
442       
443        if( ic == NULL )
444                return FALSE;
445       
446        c->ui_data = ic;
447        ic->data = c;
448       
449        topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
450        irc_channel_set_topic( ic, topic, irc->root );
451        g_free( topic );
452       
453        return TRUE;
454}
455
456static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
457{
458        irc_channel_t *ic = c->ui_data;
459       
460        if( ic == NULL )
461                return FALSE;
462       
463        if( ic->flags & IRC_CHANNEL_JOINED )
464                irc_channel_printf( ic, "Cleaning up channel, bye!" );
465       
466        ic->data = NULL;
467        irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
468       
469        return TRUE;
470}
471
472static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text )
473{
474        irc_channel_t *ic = c->ui_data;
475       
476        if( ic == NULL )
477                return FALSE;
478       
479        irc_channel_printf( ic, "%s", text );
480       
481        return TRUE;
482}
483
484static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at )
485{
486        irc_t *irc = bee->ui_data;
487        irc_user_t *iu = bu->ui_data;
488        irc_channel_t *ic = c->ui_data;
489        char *ts = NULL;
490       
491        if( ic == NULL )
492                return FALSE;
493       
494        if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) )
495                ts = irc_format_timestamp( irc, sent_at );
496       
497        irc_send_msg( iu, "PRIVMSG", ic->name, msg, ts );
498        g_free( ts );
499       
500        return TRUE;
501}
502
503static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
504{
505        irc_t *irc = bee->ui_data;
506        irc_channel_t *ic = c->ui_data;
507       
508        if( ic == NULL )
509                return FALSE;
510       
511        irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data );
512       
513        return TRUE;
514}
515
516static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
517{
518        irc_t *irc = bee->ui_data;
519        irc_channel_t *ic = c->ui_data;
520       
521        if( ic == NULL )
522                return FALSE;
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( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, 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_channel_t *ic = c->ui_data;
535        irc_t *irc = bee->ui_data;
536        irc_user_t *iu;
537       
538        if( ic == NULL )
539                return FALSE;
540       
541        if( bu == NULL )
542                iu = irc->root;
543        else if( bu == bee->user )
544                iu = irc->user;
545        else
546                iu = bu->ui_data;
547       
548        irc_channel_set_topic( ic, new, iu );
549       
550        return TRUE;
551}
552
553static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
554{
555        irc_t *irc = bee->ui_data;
556        irc_channel_t *ic = c->ui_data, *oic;
557        char stripped[MAX_NICK_LENGTH+1], *full_name;
558       
559        if( ic == NULL )
560                return FALSE;
561       
562        /* Don't rename a channel if the user's in it already. */
563        if( ic->flags & IRC_CHANNEL_JOINED )
564                return FALSE;
565       
566        strncpy( stripped, name, MAX_NICK_LENGTH );
567        stripped[MAX_NICK_LENGTH] = '\0';
568        irc_channel_name_strip( stripped );
569        if( set_getbool( &bee->set, "lcnicks" ) )
570                nick_lc( stripped );
571       
572        if( stripped[0] == '\0' )
573                return FALSE;
574       
575        full_name = g_strdup_printf( "#%s", stripped );
576        if( ( oic = irc_channel_by_name( irc, full_name ) ) )
577        {
578                char *type, *chat_type;
579               
580                type = set_getstr( &oic->set, "type" );
581                chat_type = set_getstr( &oic->set, "chat_type" );
582               
583                if( type && chat_type && oic->data == FALSE &&
584                    strcmp( type, "chat" ) == 0 &&
585                    strcmp( chat_type, "groupchat" ) == 0 )
586                {
587                        /* There's a channel with this name already, but it looks
588                           like it's not in use yet. Most likely the IRC client
589                           rejoined the channel after a reconnect. Remove it so
590                           we can reuse its name. */
591                        irc_channel_free( oic );
592                }
593                else
594                {
595                        g_free( full_name );
596                        return FALSE;
597                }
598        }
599       
600        g_free( ic->name );
601        ic->name = full_name;
602       
603        return TRUE;
604}
605
606/* IRC->IM */
607static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
608
609static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
610{
611        struct groupchat *c = ic->data;
612        char *trans = NULL, *s;
613       
614        if( c == NULL )
615                return FALSE;
616       
617        if( set_getbool( &ic->set, "translate_to_nicks" ) )
618        {
619                char nick[MAX_NICK_LENGTH+1];
620                irc_user_t *iu;
621               
622                strncpy( nick, msg, MAX_NICK_LENGTH );
623                nick[MAX_NICK_LENGTH] = '\0';
624                if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) )
625                {
626                        *s = '\0';
627                        if( ( iu = irc_user_by_name( ic->irc, nick ) ) &&
628                            iu->bu->nick && irc_channel_has_user( ic, iu ) )
629                        {
630                                trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL );
631                                msg = trans;
632                        }
633                }
634        }
635       
636        if( set_getbool( &ic->irc->b->set, "paste_buffer" ) )
637        {
638                int delay;
639               
640                if( ic->pastebuf == NULL )
641                        ic->pastebuf = g_string_new( msg );
642                else
643                {
644                        b_event_remove( ic->pastebuf_timer );
645                        g_string_append_printf( ic->pastebuf, "\n%s", msg );
646                }
647               
648                if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
649                        delay *= 1000;
650               
651                ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic );
652               
653                g_free( trans );
654                return TRUE;
655        }
656        else
657                bee_chat_msg( ic->irc->b, c, msg, 0 );
658       
659        g_free( trans );
660        return TRUE;
661}
662
663static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
664{
665        irc_channel_t *ic = data;
666       
667        bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
668       
669        g_string_free( ic->pastebuf, TRUE );
670        ic->pastebuf = 0;
671        ic->pastebuf_timer = 0;
672       
673        return FALSE;
674}
675
676static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
677{
678        char *acc_s, *room;
679        account_t *acc;
680       
681        if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 )
682                return TRUE;
683       
684        if( ( acc_s = set_getstr( &ic->set, "account" ) ) &&
685            ( room = set_getstr( &ic->set, "room" ) ) &&
686            ( acc = account_get( ic->irc->b, acc_s ) ) &&
687            acc->ic && acc->prpl->chat_join )
688        {
689                char *nick;
690               
691                if( !( nick = set_getstr( &ic->set, "nick" ) ) )
692                        nick = ic->irc->user->nick;
693               
694                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
695                acc->prpl->chat_join( acc->ic, room, nick, NULL );
696                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
697               
698                return FALSE;
699        }
700        else
701        {
702                irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name );
703                return FALSE;
704        }
705}
706
707static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
708{
709        struct groupchat *c = ic->data;
710       
711        if( c && c->ic->acc->prpl->chat_leave )
712                c->ic->acc->prpl->chat_leave( c );
713       
714        return TRUE;
715}
716
717static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
718{
719        struct groupchat *c = ic->data;
720       
721        if( c == NULL )
722                return FALSE;
723       
724        if( c->ic->acc->prpl->chat_topic == NULL )
725                irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name );
726        else
727        {
728                /* TODO: Need more const goodness here, sigh */
729                char *topic = g_strdup( new );
730                c->ic->acc->prpl->chat_topic( c, topic );
731                g_free( topic );
732                return TRUE;
733        }
734               
735        return FALSE;
736}
737
738static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
739{
740        struct groupchat *c = ic->data;
741        bee_user_t *bu = iu->bu;
742       
743        if( bu == NULL )
744                return FALSE;
745       
746        if( c )
747        {
748                if( iu->bu->ic != c->ic )
749                        irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name );
750                else if( c->ic->acc->prpl->chat_invite )
751                        c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL );
752                else
753                        irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
754        }
755        else if( bu->ic->acc->prpl->chat_with &&
756                 strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 )
757        {
758                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
759                iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
760                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
761        }
762        else
763        {
764                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
765        }
766       
767        return TRUE;
768}
769
770static char *set_eval_room_account( set_t *set, char *value );
771static char *set_eval_chat_type( set_t *set, char *value );
772
773static gboolean bee_irc_channel_init( irc_channel_t *ic )
774{
775        set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
776        set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
777        set_add( &ic->set, "nick", NULL, NULL, ic );
778        set_add( &ic->set, "room", NULL, NULL, ic );
779        set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
780       
781        /* chat_type == groupchat */
782        ic->flags |= IRC_CHANNEL_TEMP;
783       
784        return TRUE;
785}
786
787static char *set_eval_room_account( set_t *set, char *value )
788{
789        struct irc_channel *ic = set->data;
790        account_t *acc;
791       
792        if( !( acc = account_get( ic->irc->b, value ) ) )
793                return SET_INVALID;
794        else if( !acc->prpl->chat_join )
795        {
796                irc_usermsg( ic->irc, "Named chatrooms not supported on that account." );
797                return SET_INVALID;
798        }
799       
800        return g_strdup_printf( "%s(%s)", acc->prpl->name, acc->user );
801}
802
803static char *set_eval_chat_type( set_t *set, char *value )
804{
805        struct irc_channel *ic = set->data;
806       
807        if( strcmp( value, "groupchat" ) == 0 )
808                ic->flags |= IRC_CHANNEL_TEMP;
809        else if( strcmp( value, "room" ) == 0 )
810                ic->flags &= ~IRC_CHANNEL_TEMP;
811        else
812                return NULL;
813       
814        return value;
815}
816
817static gboolean bee_irc_channel_free( irc_channel_t *ic )
818{
819        struct groupchat *c = ic->data;
820       
821        set_del( &ic->set, "account" );
822        set_del( &ic->set, "chat_type" );
823        set_del( &ic->set, "nick" );
824        set_del( &ic->set, "room" );
825        set_del( &ic->set, "translate_to_nicks" );
826       
827        ic->flags &= ~IRC_CHANNEL_TEMP;
828       
829        /* That one still points at this channel. Don't. */
830        if( c )
831                c->ui_data = NULL;
832       
833        return TRUE;
834}
835
836const struct irc_channel_funcs irc_channel_im_chat_funcs = {
837        bee_irc_channel_chat_privmsg,
838        bee_irc_channel_chat_join,
839        bee_irc_channel_chat_part,
840        bee_irc_channel_chat_topic,
841        bee_irc_channel_chat_invite,
842
843        bee_irc_channel_init,
844        bee_irc_channel_free,
845};
846
847
848/* IM->IRC: File transfers */
849static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
850{
851        return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
852}
853
854static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
855{
856        return dccs_recv_start( ft );
857}
858
859static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
860{
861        return dcc_close( ft );
862}
863
864static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
865{
866        dcc_file_transfer_t *df = file->priv;
867
868        if( file->bytes_transferred >= file->file_size )
869                dcc_finish( file );
870        else
871                df->proto_finished = TRUE;
872}
873
874const struct bee_ui_funcs irc_ui_funcs = {
875        bee_irc_imc_connected,
876        bee_irc_imc_disconnected,
877       
878        bee_irc_user_new,
879        bee_irc_user_free,
880        bee_irc_user_fullname,
881        bee_irc_user_nick_hint,
882        bee_irc_user_group,
883        bee_irc_user_status,
884        bee_irc_user_msg,
885        bee_irc_user_typing,
886       
887        bee_irc_chat_new,
888        bee_irc_chat_free,
889        bee_irc_chat_log,
890        bee_irc_chat_msg,
891        bee_irc_chat_add_user,
892        bee_irc_chat_remove_user,
893        bee_irc_chat_topic,
894        bee_irc_chat_name_hint,
895       
896        bee_irc_ft_in_start,
897        bee_irc_ft_out_start,
898        bee_irc_ft_close,
899        bee_irc_ft_finished,
900};
Note: See TracBrowser for help on using the repository browser.