source: irc_im.c @ 4255320

Last change on this file since 4255320 was 51a3d12, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-25T09:11:48Z

Allow change nicknames when moving contacts between groups. Also fixing a
bug that caused many empty channel mode changes being sent.

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