source: irc_im.c @ 90fc864

Last change on this file since 90fc864 was 3864c08, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-10-21T03:00:54Z

Big merge from pesco, closing some OTR issues: #759, #824, #839, #830.

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