source: irc_im.c @ e67e513

Last change on this file since e67e513 was e67e513, checked in by unknown <pesco@…>, at 2011-10-03T14:56:58Z

rename irc_usermsg to irc_rootmsg.
add new irc_usermsg, irc_usernotice.
deliver user-specific messages from libotr as notices to that user.

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