source: irc_im.c @ 09dfb68

Last change on this file since 09dfb68 was b1f818b, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-11T12:21:59Z

Use bee_user structs in all nick_* functions. Prepare for a nick_get() with
more flexible nickname generation.

  • Property mode set to 100644
File size: 22.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 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_hint( bee_t *bee, bee_user_t *bu, const char *hint );
245
246static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu )
247{
248        irc_user_t *iu = (irc_user_t *) bu->ui_data;
249        irc_t *irc = (irc_t *) bee->ui_data;
250        char *s;
251       
252        if( iu->fullname != iu->nick )
253                g_free( iu->fullname );
254        iu->fullname = g_strdup( bu->fullname );
255       
256        /* Strip newlines (unlikely, but IRC-unfriendly so they must go)
257           TODO(wilmer): Do the same with away msgs again! */
258        for( s = iu->fullname; *s; s ++ )
259                if( isspace( *s ) ) *s = ' ';
260       
261        if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) )
262        {
263                char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
264                irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
265        }
266       
267        s = set_getstr( &bu->ic->acc->set, "nick_source" );
268        if( strcmp( s, "handle" ) != 0 )
269        {
270                char *name = g_strdup( bu->fullname );
271               
272                if( strcmp( s, "first_name" ) == 0 )
273                {
274                        int i;
275                        for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {}
276                        name[i] = '\0';
277                }
278               
279                bee_irc_user_nick_hint( bee, bu, name );
280               
281                g_free( name );
282        }
283       
284        return TRUE;
285}
286
287static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint )
288{
289        irc_user_t *iu = bu->ui_data;
290        char newnick[MAX_NICK_LENGTH+1], *translit;
291       
292        if( bu->flags & BEE_USER_ONLINE )
293                /* Ignore if the user is visible already. */
294                return TRUE;
295       
296        if( nick_saved( bu ) )
297                /* The user already assigned a nickname to this person. */
298                return TRUE;
299       
300        /* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT should
301           do lossy/approximate conversions, so letters with accents don't
302           just get stripped. Note that it depends on LC_CTYPE being set to
303           something other than C/POSIX. */
304        translit = g_convert( hint, -1, "ASCII//TRANSLIT//IGNORE", "UTF-8",
305                              NULL, NULL, NULL );
306       
307        strncpy( newnick, translit ? : hint, MAX_NICK_LENGTH );
308        newnick[MAX_NICK_LENGTH] = 0;
309        g_free( translit );
310       
311        /* Some processing to make sure this string is a valid IRC nickname. */
312        nick_strip( newnick );
313        if( set_getbool( &bee->set, "lcnicks" ) )
314                nick_lc( newnick );
315       
316        if( strcmp( iu->nick, newnick ) != 0 )
317        {
318                /* Only do this if newnick is different from the current one.
319                   If rejoining a channel, maybe we got this nick already
320                   (and dedupe would only add an underscore. */
321                nick_dedupe( bu, newnick );
322                irc_user_set_nick( iu, newnick );
323        }
324       
325        return TRUE;
326}
327
328static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu )
329{
330        irc_user_t *iu = (irc_user_t *) bu->ui_data;
331        irc_t *irc = (irc_t *) bee->ui_data;
332       
333        bee_irc_channel_update( irc, NULL, iu );
334       
335        return TRUE;
336}
337
338/* IRC->IM calls */
339
340static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
341
342static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg )
343{
344        const char *away;
345       
346        if( iu->bu == NULL )
347                return FALSE;
348       
349        if( ( away = irc_user_get_away( iu ) ) &&
350            time( NULL ) >= iu->away_reply_timeout )
351        {
352                irc_send_num( iu->irc, 301, "%s :%s", iu->nick, away );
353                iu->away_reply_timeout = time( NULL ) +
354                        set_getint( &iu->irc->b->set, "away_reply_timeout" );
355        }
356       
357        if( set_getbool( &iu->irc->b->set, "paste_buffer" ) )
358        {
359                int delay;
360               
361                if( iu->pastebuf == NULL )
362                        iu->pastebuf = g_string_new( msg );
363                else
364                {
365                        b_event_remove( iu->pastebuf_timer );
366                        g_string_append_printf( iu->pastebuf, "\n%s", msg );
367                }
368               
369                if( ( delay = set_getint( &iu->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
370                        delay *= 1000;
371               
372                iu->pastebuf_timer = b_timeout_add( delay, bee_irc_user_privmsg_cb, iu );
373               
374                return TRUE;
375        }
376        else
377                return bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
378}
379
380static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
381{
382        irc_user_t *iu = data;
383       
384        bee_user_msg( iu->irc->b, iu->bu, iu->pastebuf->str, 0 );
385       
386        g_string_free( iu->pastebuf, TRUE );
387        iu->pastebuf = 0;
388        iu->pastebuf_timer = 0;
389       
390        return FALSE;
391}
392
393static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp )
394{
395        if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0
396                    && g_strcasecmp( ctcp[1], "SEND" ) == 0 )
397        {
398                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
399                {
400                        file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp );
401                        if ( ft )
402                                iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle );
403                       
404                        return TRUE;
405                }
406        }
407        else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 )
408        {
409                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] )
410                {
411                        int st = ctcp[1][0];
412                        if( st >= '0' && st <= '2' )
413                        {
414                                st <<= 8;
415                                iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st );
416                        }
417                       
418                        return TRUE;
419                }
420        }
421       
422        return FALSE;
423}
424
425static const struct irc_user_funcs irc_user_im_funcs = {
426        bee_irc_user_privmsg,
427        bee_irc_user_ctcp,
428};
429
430
431/* IM->IRC: Groupchats */
432const struct irc_channel_funcs irc_channel_im_chat_funcs;
433
434static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c )
435{
436        irc_t *irc = bee->ui_data;
437        irc_channel_t *ic;
438        char *topic;
439        GSList *l;
440        int i;
441       
442        /* Try to find a channel that expects to receive a groupchat.
443           This flag is set earlier in our current call trace. */
444        for( l = irc->channels; l; l = l->next )
445        {
446                ic = l->data;
447                if( ic->flags & IRC_CHANNEL_CHAT_PICKME )
448                        break;
449        }
450       
451        /* If we found none, just generate some stupid name. */
452        if( l == NULL ) for( i = 0; i <= 999; i ++ )
453        {
454                char name[16];
455                sprintf( name, "#chat_%03d", i );
456                if( ( ic = irc_channel_new( irc, name ) ) )
457                        break;
458        }
459       
460        if( ic == NULL )
461                return FALSE;
462       
463        c->ui_data = ic;
464        ic->data = c;
465       
466        topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
467        irc_channel_set_topic( ic, topic, irc->root );
468        g_free( topic );
469       
470        return TRUE;
471}
472
473static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
474{
475        irc_channel_t *ic = c->ui_data;
476       
477        if( ic->flags & IRC_CHANNEL_JOINED )
478                irc_channel_printf( ic, "Cleaning up channel, bye!" );
479       
480        ic->data = NULL;
481        irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
482       
483        return TRUE;
484}
485
486static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text )
487{
488        irc_channel_t *ic = c->ui_data;
489       
490        irc_channel_printf( ic, "%s", text );
491       
492        return TRUE;
493}
494
495static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at )
496{
497        irc_t *irc = bee->ui_data;
498        irc_user_t *iu = bu->ui_data;
499        irc_channel_t *ic = c->ui_data;
500        char *ts = NULL;
501       
502        if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) )
503                ts = irc_format_timestamp( irc, sent_at );
504       
505        irc_send_msg( iu, "PRIVMSG", ic->name, msg, ts );
506        g_free( ts );
507       
508        return TRUE;
509}
510
511static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
512{
513        irc_t *irc = bee->ui_data;
514       
515        irc_channel_add_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data );
516       
517        return TRUE;
518}
519
520static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
521{
522        irc_t *irc = bee->ui_data;
523       
524        /* TODO: Possible bug here: If a module removes $user here instead of just
525           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
526           a broken state around here. */
527        irc_channel_del_user( c->ui_data, bu == bee->user ? irc->user : bu->ui_data, 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_t *irc = bee->ui_data;
535        irc_user_t *iu;
536       
537        if( bu == NULL )
538                iu = irc->root;
539        else if( bu == bee->user )
540                iu = irc->user;
541        else
542                iu = bu->ui_data;
543       
544        irc_channel_set_topic( c->ui_data, new, iu );
545       
546        return TRUE;
547}
548
549static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
550{
551        irc_t *irc = bee->ui_data;
552        irc_channel_t *ic = c->ui_data, *oic;
553        char stripped[MAX_NICK_LENGTH+1], *full_name;
554       
555        /* Don't rename a channel if the user's in it already. */
556        if( ic->flags & IRC_CHANNEL_JOINED )
557                return FALSE;
558       
559        strncpy( stripped, name, MAX_NICK_LENGTH );
560        stripped[MAX_NICK_LENGTH] = '\0';
561        irc_channel_name_strip( stripped );
562        if( set_getbool( &bee->set, "lcnicks" ) )
563                nick_lc( stripped );
564       
565        if( stripped[0] == '\0' )
566                return FALSE;
567       
568        full_name = g_strdup_printf( "#%s", stripped );
569        if( ( oic = irc_channel_by_name( irc, full_name ) ) )
570        {
571                char *type, *chat_type;
572               
573                type = set_getstr( &oic->set, "type" );
574                chat_type = set_getstr( &oic->set, "chat_type" );
575               
576                if( type && chat_type && oic->data == FALSE &&
577                    strcmp( type, "chat" ) == 0 &&
578                    strcmp( chat_type, "groupchat" ) == 0 )
579                {
580                        /* There's a channel with this name already, but it looks
581                           like it's not in use yet. Most likely the IRC client
582                           rejoined the channel after a reconnect. Remove it so
583                           we can reuse its name. */
584                        irc_channel_free( oic );
585                }
586                else
587                {
588                        g_free( full_name );
589                        return FALSE;
590                }
591        }
592       
593        g_free( ic->name );
594        ic->name = full_name;
595       
596        return TRUE;
597}
598
599/* IRC->IM */
600static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
601
602static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
603{
604        struct groupchat *c = ic->data;
605        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        set_del( &ic->set, "account" );
813        set_del( &ic->set, "chat_type" );
814        set_del( &ic->set, "nick" );
815        set_del( &ic->set, "room" );
816        set_del( &ic->set, "translate_to_nicks" );
817       
818        ic->flags &= ~IRC_CHANNEL_TEMP;
819       
820        return TRUE;
821}
822
823const struct irc_channel_funcs irc_channel_im_chat_funcs = {
824        bee_irc_channel_chat_privmsg,
825        bee_irc_channel_chat_join,
826        bee_irc_channel_chat_part,
827        bee_irc_channel_chat_topic,
828        bee_irc_channel_chat_invite,
829
830        bee_irc_channel_init,
831        bee_irc_channel_free,
832};
833
834
835/* IM->IRC: File transfers */
836static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
837{
838        return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
839}
840
841static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
842{
843        return dccs_recv_start( ft );
844}
845
846static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
847{
848        return dcc_close( ft );
849}
850
851static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
852{
853        dcc_file_transfer_t *df = file->priv;
854
855        if( file->bytes_transferred >= file->file_size )
856                dcc_finish( file );
857        else
858                df->proto_finished = TRUE;
859}
860
861const struct bee_ui_funcs irc_ui_funcs = {
862        bee_irc_imc_connected,
863        bee_irc_imc_disconnected,
864       
865        bee_irc_user_new,
866        bee_irc_user_free,
867        bee_irc_user_fullname,
868        bee_irc_user_nick_hint,
869        bee_irc_user_group,
870        bee_irc_user_status,
871        bee_irc_user_msg,
872        bee_irc_user_typing,
873       
874        bee_irc_chat_new,
875        bee_irc_chat_free,
876        bee_irc_chat_log,
877        bee_irc_chat_msg,
878        bee_irc_chat_add_user,
879        bee_irc_chat_remove_user,
880        bee_irc_chat_topic,
881        bee_irc_chat_name_hint,
882       
883        bee_irc_ft_in_start,
884        bee_irc_ft_out_start,
885        bee_irc_ft_close,
886        bee_irc_ft_finished,
887};
Note: See TracBrowser for help on using the repository browser.