source: irc_im.c @ fb11647a

Last change on this file since fb11647a was fb11647a, checked in by unknown <pesco@…>, at 2011-10-01T14:55:24Z

factor making a message appear from a specific nick out of bee_irc_user_msg

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