source: irc_im.c @ c8eeadd

Last change on this file since c8eeadd was 0bd948e, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-03T21:16:41Z

Show a user going offline as a QUIT, not as one or more PARTs, like in the
old-style BitlBee. This so that the IRC client will show the notification
in query windows as well. Make it a setting though, for bug #539.

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