source: irc_im.c @ 2370ec2

Last change on this file since 2370ec2 was 6963230, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-05-01T14:57:30Z

Improved channel cleanup. Should fix a crash bug when leaving temporary
groupchat channels (i.e. channels invited to on gtalk). #780.

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