source: irc_im.c @ 27b407f

Last change on this file since 27b407f was 8e9e2b7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-10-03T02:45:26Z

Merging mainline, which includes a huge msnp13 merge.

Not 100% sure about the OpenSSL merge, should double check that but I'm
currently offline.

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