source: irc_im.c @ b11b781

3.0.1
Last change on this file since b11b781 was 495d21b, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-11-21T15:53:08Z

Chop spaces from IM handles in all cases. Fixes #723.

  • Property mode set to 100644
File size: 24.9 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( set_getbool( &irc->b->set, "private" ) )
58                iu->last_channel = NULL;
59        else
60                iu->last_channel = irc_channel_with_user( irc, iu );
61       
62        if( ( s = strchr( bu->handle, '@' ) ) )
63        {
64                iu->host = g_strdup( s + 1 );
65                iu->user = g_strndup( bu->handle, s - bu->handle );
66        }
67        else
68        {
69                iu->user = g_strdup( bu->handle );
70                if( bu->ic->acc->server )
71                        iu->host = g_strdup( bu->ic->acc->server );
72                else
73                        iu->host = g_strdup( bu->ic->acc->prpl->name );
74        }
75       
76        while( ( s = strchr( iu->user, ' ' ) ) )
77                *s = '_';
78       
79        if( bu->flags & BEE_USER_LOCAL )
80        {
81                char *s = set_getstr( &bee->set, "handle_unknown" );
82               
83                if( strcmp( s, "add_private" ) == 0 )
84                        iu->last_channel = NULL;
85                else if( strcmp( s, "add_channel" ) == 0 )
86                        iu->last_channel = irc->default_channel;
87        }
88       
89        iu->f = &irc_user_im_funcs;
90       
91        return TRUE;
92}
93
94static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu )
95{
96        return irc_user_free( bee->ui_data, (irc_user_t *) bu->ui_data );
97}
98
99static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old )
100{
101        irc_t *irc = bee->ui_data;
102        irc_user_t *iu = bu->ui_data;
103       
104        /* Do this outside the if below since away state can change without
105           the online state changing. */
106        iu->flags &= ~IRC_USER_AWAY;
107        if( bu->flags & BEE_USER_AWAY || !( bu->flags & BEE_USER_ONLINE ) )
108                iu->flags |= IRC_USER_AWAY;
109       
110        if( ( bu->flags & BEE_USER_ONLINE ) != ( old->flags & BEE_USER_ONLINE ) )
111        {
112                if( bu->flags & BEE_USER_ONLINE )
113                {
114                        if( g_hash_table_lookup( irc->watches, iu->key ) )
115                                irc_send_num( irc, 600, "%s %s %s %d :%s", iu->nick, iu->user,
116                                              iu->host, (int) time( NULL ), "logged online" );
117                }
118                else
119                {
120                        if( g_hash_table_lookup( irc->watches, iu->key ) )
121                                irc_send_num( irc, 601, "%s %s %s %d :%s", iu->nick, iu->user,
122                                              iu->host, (int) time( NULL ), "logged offline" );
123                       
124                        /* Send a QUIT since those will also show up in any
125                           query windows the user may have, plus it's only
126                           one QUIT instead of possibly many (in case of
127                           multiple control chans). If there's a channel that
128                           shows offline people, a JOIN will follow. */
129                        if( set_getbool( &bee->set, "offline_user_quits" ) )
130                                irc_user_quit( iu, "Leaving..." );
131                }
132        }
133       
134        /* Reset this one since the info may have changed. */
135        iu->away_reply_timeout = 0;
136       
137        bee_irc_channel_update( irc, NULL, iu );
138       
139        return TRUE;
140}
141
142void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu )
143{
144        GSList *l;
145       
146        if( ic == NULL )
147        {
148                for( l = irc->channels; l; l = l->next )
149                {
150                        ic = l->data;
151                        /* TODO: Just add a type flag or so.. */
152                        if( ic->f == irc->default_channel->f &&
153                            ( ic->flags & IRC_CHANNEL_JOINED ) )
154                                bee_irc_channel_update( irc, ic, iu );
155                }
156                return;
157        }
158        if( iu == NULL )
159        {
160                for( l = irc->users; l; l = l->next )
161                {
162                        iu = l->data;
163                        if( iu->bu )
164                                bee_irc_channel_update( irc, ic, l->data );
165                }
166                return;
167        }
168       
169        if( !irc_channel_wants_user( ic, iu ) )
170        {
171                irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
172        }
173        else
174        {
175                struct irc_control_channel *icc = ic->data;
176                int mode = 0;
177               
178                if( !( iu->bu->flags & BEE_USER_ONLINE ) )
179                        mode = icc->modes[0];
180                else if( iu->bu->flags & BEE_USER_AWAY )
181                        mode = icc->modes[1];
182                else
183                        mode = icc->modes[2];
184               
185                if( !mode )
186                        irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
187                else
188                {
189                        irc_channel_add_user( ic, iu );
190                        irc_channel_user_set_mode( ic, iu, mode );
191                }
192        }
193}
194
195static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg_, time_t sent_at )
196{
197        irc_t *irc = bee->ui_data;
198        irc_user_t *iu = (irc_user_t *) bu->ui_data;
199        char *dst, *prefix = NULL;
200        char *wrapped, *ts = NULL;
201        irc_channel_t *ic = NULL;
202        char *msg = g_strdup( msg_ );
203        GSList *l;
204       
205        if( sent_at > 0 && set_getbool( &irc->b->set, "display_timestamps" ) )
206                ts = irc_format_timestamp( irc, sent_at );
207       
208        /* Too similar to irc_usermsg()... */
209        if( iu->last_channel )
210        {
211                if( iu->last_channel->flags & IRC_CHANNEL_JOINED )
212                        ic = iu->last_channel;
213                else
214                        ic = irc_channel_with_user( irc, iu );
215        }
216       
217        if( ic )
218        {
219                dst = ic->name;
220                prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ? : "" );
221        }
222        else
223        {
224                dst = irc->user->nick;
225                prefix = ts;
226                ts = NULL;
227        }
228       
229        for( l = irc_plugins; l; l = l->next )
230        {
231                irc_plugin_t *p = l->data;
232                if( p->filter_msg_in )
233                {
234                        char *s = p->filter_msg_in( iu, msg, 0 );
235                        if( s )
236                        {
237                                if( s != msg )
238                                        g_free( msg );
239                                msg = s;
240                        }
241                        else
242                        {
243                                /* Modules can swallow messages. */
244                                return TRUE;
245                        }
246                }
247        }
248       
249        if( ( g_strcasecmp( set_getstr( &bee->set, "strip_html" ), "always" ) == 0 ) ||
250            ( ( bu->ic->flags & OPT_DOES_HTML ) && set_getbool( &bee->set, "strip_html" ) ) )
251        {
252                char *s = g_strdup( msg );
253                strip_html( s );
254                g_free( msg );
255                msg = s;
256        }
257       
258        wrapped = word_wrap( msg, 425 );
259        irc_send_msg( iu, "PRIVMSG", dst, wrapped, prefix );
260       
261        g_free( wrapped );
262        g_free( prefix );
263        g_free( msg );
264        g_free( ts );
265       
266        return TRUE;
267}
268
269static gboolean bee_irc_user_typing( bee_t *bee, bee_user_t *bu, uint32_t flags )
270{
271        irc_t *irc = (irc_t *) bee->ui_data;
272       
273        if( set_getbool( &bee->set, "typing_notice" ) )
274                irc_send_msg_f( (irc_user_t *) bu->ui_data, "PRIVMSG", irc->user->nick,
275                                "\001TYPING %d\001", ( flags >> 8 ) & 3 );
276        else
277                return FALSE;
278       
279        return TRUE;
280}
281
282static gboolean bee_irc_user_nick_update( irc_user_t *iu );
283
284static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu )
285{
286        irc_user_t *iu = (irc_user_t *) bu->ui_data;
287        char *s;
288       
289        if( iu->fullname != iu->nick )
290                g_free( iu->fullname );
291        iu->fullname = g_strdup( bu->fullname );
292       
293        /* Strip newlines (unlikely, but IRC-unfriendly so they must go)
294           TODO(wilmer): Do the same with away msgs again! */
295        for( s = iu->fullname; *s; s ++ )
296                if( isspace( *s ) ) *s = ' ';
297       
298        if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) )
299        {
300                /* People don't like this /NOTICE. Meh, let's go back to the old one.
301                char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
302                irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
303                */
304                imcb_log( bu->ic, "User `%s' changed name to `%s'", iu->nick, iu->fullname );
305        }
306       
307        bee_irc_user_nick_update( iu );
308       
309        return TRUE;
310}
311
312static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint )
313{
314        bee_irc_user_nick_update( (irc_user_t*) bu->ui_data );
315       
316        return TRUE;
317}
318
319static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu )
320{
321        irc_user_t *iu = (irc_user_t *) bu->ui_data;
322        irc_t *irc = (irc_t *) bee->ui_data;
323        bee_user_flags_t online;
324       
325        /* Take the user offline temporarily so we can change the nick (if necessary). */
326        if( ( online = bu->flags & BEE_USER_ONLINE ) )
327                bu->flags &= ~BEE_USER_ONLINE;
328       
329        bee_irc_channel_update( irc, NULL, iu );
330        bee_irc_user_nick_update( iu );
331       
332        if( online )
333        {
334                bu->flags |= online;
335                bee_irc_channel_update( irc, NULL, iu );
336        }
337       
338        return TRUE;
339}
340
341static gboolean bee_irc_user_nick_update( irc_user_t *iu )
342{
343        bee_user_t *bu = iu->bu;
344        char *newnick;
345       
346        if( bu->flags & BEE_USER_ONLINE )
347                /* Ignore if the user is visible already. */
348                return TRUE;
349       
350        if( nick_saved( bu ) )
351                /* The user already assigned a nickname to this person. */
352                return TRUE;
353       
354        newnick = nick_get( bu );
355       
356        if( strcmp( iu->nick, newnick ) != 0 )
357        {
358                nick_dedupe( bu, newnick );
359                irc_user_set_nick( iu, newnick );
360        }
361       
362        return TRUE;
363}
364
365/* IRC->IM calls */
366
367static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
368
369static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg )
370{
371        const char *away;
372       
373        if( iu->bu == NULL )
374                return FALSE;
375       
376        if( ( away = irc_user_get_away( iu ) ) &&
377            time( NULL ) >= iu->away_reply_timeout )
378        {
379                irc_send_num( iu->irc, 301, "%s :%s", iu->nick, away );
380                iu->away_reply_timeout = time( NULL ) +
381                        set_getint( &iu->irc->b->set, "away_reply_timeout" );
382        }
383       
384        if( iu->pastebuf == NULL )
385                iu->pastebuf = g_string_new( msg );
386        else
387        {
388                b_event_remove( iu->pastebuf_timer );
389                g_string_append_printf( iu->pastebuf, "\n%s", msg );
390        }
391       
392        if( set_getbool( &iu->irc->b->set, "paste_buffer" ) )
393        {
394                int delay;
395               
396                if( ( delay = set_getint( &iu->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
397                        delay *= 1000;
398               
399                iu->pastebuf_timer = b_timeout_add( delay, bee_irc_user_privmsg_cb, iu );
400               
401                return TRUE;
402        }
403        else
404        {
405                bee_irc_user_privmsg_cb( iu, 0, 0 );
406               
407                return TRUE;
408        }
409}
410
411static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
412{
413        irc_user_t *iu = data;
414        char *msg = g_string_free( iu->pastebuf, FALSE );
415        GSList *l;
416       
417        for( l = irc_plugins; l; l = l->next )
418        {
419                irc_plugin_t *p = l->data;
420                if( p->filter_msg_out )
421                {
422                        char *s = p->filter_msg_out( iu, msg, 0 );
423                        if( s )
424                        {
425                                if( s != msg )
426                                        g_free( msg );
427                                msg = s;
428                        }
429                        else
430                        {
431                                /* Modules can swallow messages. */
432                                iu->pastebuf = NULL;
433                                g_free( msg );
434                                return FALSE;
435                        }
436                }
437        }
438       
439        bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
440       
441        g_free( msg );
442        iu->pastebuf = NULL;
443        iu->pastebuf_timer = 0;
444       
445        return FALSE;
446}
447
448static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp )
449{
450        if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0
451                    && g_strcasecmp( ctcp[1], "SEND" ) == 0 )
452        {
453                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
454                {
455                        file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp );
456                        if ( ft )
457                                iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle );
458                       
459                        return TRUE;
460                }
461        }
462        else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 )
463        {
464                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] )
465                {
466                        int st = ctcp[1][0];
467                        if( st >= '0' && st <= '2' )
468                        {
469                                st <<= 8;
470                                iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st );
471                        }
472                       
473                        return TRUE;
474                }
475        }
476       
477        return FALSE;
478}
479
480static const struct irc_user_funcs irc_user_im_funcs = {
481        bee_irc_user_privmsg,
482        bee_irc_user_ctcp,
483};
484
485
486/* IM->IRC: Groupchats */
487const struct irc_channel_funcs irc_channel_im_chat_funcs;
488
489static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c )
490{
491        irc_t *irc = bee->ui_data;
492        irc_channel_t *ic;
493        char *topic;
494        GSList *l;
495        int i;
496       
497        /* Try to find a channel that expects to receive a groupchat.
498           This flag is set earlier in our current call trace. */
499        for( l = irc->channels; l; l = l->next )
500        {
501                ic = l->data;
502                if( ic->flags & IRC_CHANNEL_CHAT_PICKME )
503                        break;
504        }
505       
506        /* If we found none, just generate some stupid name. */
507        if( l == NULL ) for( i = 0; i <= 999; i ++ )
508        {
509                char name[16];
510                sprintf( name, "#chat_%03d", i );
511                if( ( ic = irc_channel_new( irc, name ) ) )
512                        break;
513        }
514       
515        if( ic == NULL )
516                return FALSE;
517       
518        c->ui_data = ic;
519        ic->data = c;
520       
521        topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
522        irc_channel_set_topic( ic, topic, irc->root );
523        g_free( topic );
524       
525        return TRUE;
526}
527
528static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
529{
530        irc_channel_t *ic = c->ui_data;
531       
532        if( ic == NULL )
533                return FALSE;
534       
535        if( ic->flags & IRC_CHANNEL_JOINED )
536                irc_channel_printf( ic, "Cleaning up channel, bye!" );
537       
538        ic->data = NULL;
539        irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
540       
541        return TRUE;
542}
543
544static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text )
545{
546        irc_channel_t *ic = c->ui_data;
547       
548        if( ic == NULL )
549                return FALSE;
550       
551        irc_channel_printf( ic, "%s", text );
552       
553        return TRUE;
554}
555
556static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at )
557{
558        irc_t *irc = bee->ui_data;
559        irc_user_t *iu = bu->ui_data;
560        irc_channel_t *ic = c->ui_data;
561        char *ts = NULL;
562       
563        if( ic == NULL )
564                return FALSE;
565       
566        if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) )
567                ts = irc_format_timestamp( irc, sent_at );
568       
569        irc_send_msg( iu, "PRIVMSG", ic->name, msg, ts );
570        g_free( ts );
571       
572        return TRUE;
573}
574
575static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
576{
577        irc_t *irc = bee->ui_data;
578        irc_channel_t *ic = c->ui_data;
579       
580        if( ic == NULL )
581                return FALSE;
582       
583        irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data );
584       
585        return TRUE;
586}
587
588static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
589{
590        irc_t *irc = bee->ui_data;
591        irc_channel_t *ic = c->ui_data;
592       
593        if( ic == NULL || bu == NULL )
594                return FALSE;
595       
596        /* TODO: Possible bug here: If a module removes $user here instead of just
597           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
598           a broken state around here. */
599        irc_channel_del_user( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL );
600       
601        return TRUE;
602}
603
604static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu )
605{
606        irc_channel_t *ic = c->ui_data;
607        irc_t *irc = bee->ui_data;
608        irc_user_t *iu;
609       
610        if( ic == NULL )
611                return FALSE;
612       
613        if( bu == NULL )
614                iu = irc->root;
615        else if( bu == bee->user )
616                iu = irc->user;
617        else
618                iu = bu->ui_data;
619       
620        irc_channel_set_topic( ic, new, iu );
621       
622        return TRUE;
623}
624
625static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
626{
627        irc_t *irc = bee->ui_data;
628        irc_channel_t *ic = c->ui_data, *oic;
629        char stripped[MAX_NICK_LENGTH+1], *full_name;
630       
631        if( ic == NULL )
632                return FALSE;
633       
634        /* Don't rename a channel if the user's in it already. */
635        if( ic->flags & IRC_CHANNEL_JOINED )
636                return FALSE;
637       
638        strncpy( stripped, name, MAX_NICK_LENGTH );
639        stripped[MAX_NICK_LENGTH] = '\0';
640        irc_channel_name_strip( stripped );
641        if( set_getbool( &bee->set, "lcnicks" ) )
642                nick_lc( stripped );
643       
644        if( stripped[0] == '\0' )
645                return FALSE;
646       
647        full_name = g_strdup_printf( "#%s", stripped );
648        if( ( oic = irc_channel_by_name( irc, full_name ) ) )
649        {
650                char *type, *chat_type;
651               
652                type = set_getstr( &oic->set, "type" );
653                chat_type = set_getstr( &oic->set, "chat_type" );
654               
655                if( type && chat_type && oic->data == FALSE &&
656                    strcmp( type, "chat" ) == 0 &&
657                    strcmp( chat_type, "groupchat" ) == 0 )
658                {
659                        /* There's a channel with this name already, but it looks
660                           like it's not in use yet. Most likely the IRC client
661                           rejoined the channel after a reconnect. Remove it so
662                           we can reuse its name. */
663                        irc_channel_free( oic );
664                }
665                else
666                {
667                        g_free( full_name );
668                        return FALSE;
669                }
670        }
671       
672        g_free( ic->name );
673        ic->name = full_name;
674       
675        return TRUE;
676}
677
678static gboolean bee_irc_chat_invite( bee_t *bee, bee_user_t *bu, const char *name, const char *msg )
679{
680        char *channel, *s;
681        irc_t *irc = bee->ui_data;
682        irc_user_t *iu = bu->ui_data;
683        irc_channel_t *chan;
684       
685        if( strchr( CTYPES, name[0] ) )
686                channel = g_strdup( name );
687        else
688                channel = g_strdup_printf( "#%s", name );
689       
690        if( ( s = strchr( channel, '@' ) ) )
691                *s = '\0';
692       
693        if( strlen( channel ) > MAX_NICK_LENGTH )
694        {
695                /* If the channel name is very long (like those insane GTalk
696                   UUID names), try if we can use the inviter's nick. */
697                s = g_strdup_printf( "#%s", iu->nick );
698                if( irc_channel_by_name( irc, s ) == NULL )
699                {
700                        g_free( channel );
701                        channel = s;
702                }
703        }
704       
705        if( ( chan = irc_channel_new( irc, channel ) ) &&
706            set_setstr( &chan->set, "type", "chat" ) &&
707            set_setstr( &chan->set, "chat_type", "room" ) &&
708            set_setstr( &chan->set, "account", bu->ic->acc->tag ) &&
709            set_setstr( &chan->set, "room", (char*) name ) )
710        {
711                /* I'm assuming that if the user didn't "chat add" the room
712                   himself but got invited, it's temporary, so make this a
713                   temporary mapping that is removed as soon as we /PART. */
714                chan->flags |= IRC_CHANNEL_TEMP;
715        }
716        else
717        {
718                irc_channel_free( chan );
719                chan = NULL;
720        }
721        g_free( channel );
722       
723        irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name );
724        if( msg )
725                irc_send_msg( iu, "PRIVMSG", irc->user->nick, msg, NULL );
726        if( chan )
727        {
728                irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name );
729                irc_send_invite( iu, chan );
730        }
731       
732        return TRUE;
733}
734
735/* IRC->IM */
736static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
737
738static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
739{
740        struct groupchat *c = ic->data;
741        char *trans = NULL, *s;
742       
743        if( c == NULL )
744                return FALSE;
745       
746        if( set_getbool( &ic->set, "translate_to_nicks" ) )
747        {
748                char nick[MAX_NICK_LENGTH+1];
749                irc_user_t *iu;
750               
751                strncpy( nick, msg, MAX_NICK_LENGTH );
752                nick[MAX_NICK_LENGTH] = '\0';
753                if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) )
754                {
755                        *s = '\0';
756                        if( ( iu = irc_user_by_name( ic->irc, nick ) ) && iu->bu &&
757                            iu->bu->nick && irc_channel_has_user( ic, iu ) )
758                        {
759                                trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL );
760                                msg = trans;
761                        }
762                }
763        }
764       
765        if( set_getbool( &ic->irc->b->set, "paste_buffer" ) )
766        {
767                int delay;
768               
769                if( ic->pastebuf == NULL )
770                        ic->pastebuf = g_string_new( msg );
771                else
772                {
773                        b_event_remove( ic->pastebuf_timer );
774                        g_string_append_printf( ic->pastebuf, "\n%s", msg );
775                }
776               
777                if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
778                        delay *= 1000;
779               
780                ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic );
781               
782                g_free( trans );
783                return TRUE;
784        }
785        else
786                bee_chat_msg( ic->irc->b, c, msg, 0 );
787       
788        g_free( trans );
789        return TRUE;
790}
791
792static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
793{
794        irc_channel_t *ic = data;
795       
796        bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
797       
798        g_string_free( ic->pastebuf, TRUE );
799        ic->pastebuf = 0;
800        ic->pastebuf_timer = 0;
801       
802        return FALSE;
803}
804
805static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
806{
807        char *acc_s, *room;
808        account_t *acc;
809       
810        if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 )
811                return TRUE;
812       
813        if( ( acc_s = set_getstr( &ic->set, "account" ) ) &&
814            ( room = set_getstr( &ic->set, "room" ) ) &&
815            ( acc = account_get( ic->irc->b, acc_s ) ) &&
816            acc->ic && acc->prpl->chat_join )
817        {
818                char *nick;
819               
820                if( !( nick = set_getstr( &ic->set, "nick" ) ) )
821                        nick = ic->irc->user->nick;
822               
823                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
824                acc->prpl->chat_join( acc->ic, room, nick, NULL, &ic->set );
825                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
826               
827                return FALSE;
828        }
829        else
830        {
831                irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name );
832                return FALSE;
833        }
834}
835
836static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
837{
838        struct groupchat *c = ic->data;
839       
840        if( c && c->ic->acc->prpl->chat_leave )
841                c->ic->acc->prpl->chat_leave( c );
842       
843        return TRUE;
844}
845
846static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
847{
848        struct groupchat *c = ic->data;
849       
850        if( c == NULL )
851                return FALSE;
852       
853        if( c->ic->acc->prpl->chat_topic == NULL )
854                irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name );
855        else
856        {
857                /* TODO: Need more const goodness here, sigh */
858                char *topic = g_strdup( new );
859                c->ic->acc->prpl->chat_topic( c, topic );
860                g_free( topic );
861        }
862               
863        /* Whatever happened, the IM module should ack the topic change. */
864        return FALSE;
865}
866
867static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
868{
869        struct groupchat *c = ic->data;
870        bee_user_t *bu = iu->bu;
871       
872        if( bu == NULL )
873                return FALSE;
874       
875        if( c )
876        {
877                if( iu->bu->ic != c->ic )
878                        irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name );
879                else if( c->ic->acc->prpl->chat_invite )
880                        c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL );
881                else
882                        irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
883        }
884        else if( bu->ic->acc->prpl->chat_with &&
885                 strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 )
886        {
887                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
888                iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
889                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
890        }
891        else
892        {
893                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
894        }
895       
896        return TRUE;
897}
898
899static char *set_eval_room_account( set_t *set, char *value );
900static char *set_eval_chat_type( set_t *set, char *value );
901
902static gboolean bee_irc_channel_init( irc_channel_t *ic )
903{
904        set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
905        set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
906        set_add( &ic->set, "nick", NULL, NULL, ic );
907        set_add( &ic->set, "room", NULL, NULL, ic );
908        set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
909       
910        /* chat_type == groupchat */
911        ic->flags |= IRC_CHANNEL_TEMP;
912       
913        return TRUE;
914}
915
916static char *set_eval_room_account( set_t *set, char *value )
917{
918        struct irc_channel *ic = set->data;
919        account_t *acc, *oa;
920       
921        if( !( acc = account_get( ic->irc->b, value ) ) )
922                return SET_INVALID;
923        else if( !acc->prpl->chat_join )
924        {
925                irc_usermsg( ic->irc, "Named chatrooms not supported on that account." );
926                return SET_INVALID;
927        }
928       
929        if( set->value && ( oa = account_get( ic->irc->b, set->value ) ) &&
930            oa->prpl->chat_free_settings )
931                oa->prpl->chat_free_settings( oa, &ic->set );
932       
933        if( acc->prpl->chat_add_settings )
934                acc->prpl->chat_add_settings( acc, &ic->set );
935       
936        return g_strdup( acc->tag );
937}
938
939static char *set_eval_chat_type( set_t *set, char *value )
940{
941        struct irc_channel *ic = set->data;
942       
943        if( strcmp( value, "groupchat" ) == 0 )
944                ic->flags |= IRC_CHANNEL_TEMP;
945        else if( strcmp( value, "room" ) == 0 )
946                ic->flags &= ~IRC_CHANNEL_TEMP;
947        else
948                return NULL;
949       
950        return value;
951}
952
953static gboolean bee_irc_channel_free( irc_channel_t *ic )
954{
955        struct groupchat *c = ic->data;
956       
957        set_del( &ic->set, "account" );
958        set_del( &ic->set, "chat_type" );
959        set_del( &ic->set, "nick" );
960        set_del( &ic->set, "room" );
961        set_del( &ic->set, "translate_to_nicks" );
962       
963        ic->flags &= ~IRC_CHANNEL_TEMP;
964       
965        /* That one still points at this channel. Don't. */
966        if( c )
967                c->ui_data = NULL;
968       
969        return TRUE;
970}
971
972const struct irc_channel_funcs irc_channel_im_chat_funcs = {
973        bee_irc_channel_chat_privmsg,
974        bee_irc_channel_chat_join,
975        bee_irc_channel_chat_part,
976        bee_irc_channel_chat_topic,
977        bee_irc_channel_chat_invite,
978
979        bee_irc_channel_init,
980        bee_irc_channel_free,
981};
982
983
984/* IM->IRC: File transfers */
985static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
986{
987        return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
988}
989
990static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
991{
992        return dccs_recv_start( ft );
993}
994
995static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
996{
997        return dcc_close( ft );
998}
999
1000static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
1001{
1002        dcc_file_transfer_t *df = file->priv;
1003
1004        if( file->bytes_transferred >= file->file_size )
1005                dcc_finish( file );
1006        else
1007                df->proto_finished = TRUE;
1008}
1009
1010const struct bee_ui_funcs irc_ui_funcs = {
1011        bee_irc_imc_connected,
1012        bee_irc_imc_disconnected,
1013       
1014        bee_irc_user_new,
1015        bee_irc_user_free,
1016        bee_irc_user_fullname,
1017        bee_irc_user_nick_hint,
1018        bee_irc_user_group,
1019        bee_irc_user_status,
1020        bee_irc_user_msg,
1021        bee_irc_user_typing,
1022       
1023        bee_irc_chat_new,
1024        bee_irc_chat_free,
1025        bee_irc_chat_log,
1026        bee_irc_chat_msg,
1027        bee_irc_chat_add_user,
1028        bee_irc_chat_remove_user,
1029        bee_irc_chat_topic,
1030        bee_irc_chat_name_hint,
1031        bee_irc_chat_invite,
1032       
1033        bee_irc_ft_in_start,
1034        bee_irc_ft_out_start,
1035        bee_irc_ft_close,
1036        bee_irc_ft_finished,
1037};
Note: See TracBrowser for help on using the repository browser.