source: irc_im.c @ b2b7f52

Last change on this file since b2b7f52 was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

  • Property mode set to 100644
File size: 26.8 KB
RevLine 
[81e04e1]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
[81e04e1]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;
[911d97a]444        char *msg;
[934db064]445        GSList *l;
446       
[911d97a]447        msg = g_string_free( iu->pastebuf, FALSE );
448        iu->pastebuf = NULL;
449        iu->pastebuf_timer = 0;
450       
[934db064]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        }
[619dd18]472       
[934db064]473        bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
[619dd18]474       
[934db064]475        g_free( msg );
[619dd18]476       
477        return FALSE;
[e4816ea]478}
479
480static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp )
481{
482        if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0
483                    && g_strcasecmp( ctcp[1], "SEND" ) == 0 )
484        {
485                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
486                {
487                        file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp );
488                        if ( ft )
489                                iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle );
490                       
491                        return TRUE;
492                }
493        }
494        else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 )
495        {
496                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] )
497                {
498                        int st = ctcp[1][0];
499                        if( st >= '0' && st <= '2' )
500                        {
501                                st <<= 8;
502                                iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st );
503                        }
504                       
505                        return TRUE;
506                }
507        }
[a97a336]508        else if( g_strcasecmp( ctcp[0], "HELP" ) == 0 && iu->bu )
509        {
510                GString *supp = g_string_new( "Supported CTCPs:" );
511                GList *l;
512               
513                if( iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
514                        g_string_append( supp, " DCC SEND," );
515                if( iu->bu->ic && iu->bu->ic->acc->prpl->send_typing )
516                        g_string_append( supp, " TYPING," );
517                if( iu->bu->ic->acc->prpl->buddy_action_list )
518                        for( l = iu->bu->ic->acc->prpl->buddy_action_list( iu->bu ); l; l = l->next )
519                        {
520                                struct buddy_action *ba = l->data;
521                                g_string_append_printf( supp, " %s (%s),",
522                                                        ba->name, ba->description );
523                        }
524                g_string_truncate( supp, supp->len - 1 );
525                irc_send_msg_f( iu, "NOTICE", iu->irc->user->nick, "\001HELP %s\001", supp->str );
526                g_string_free( supp, TRUE );
527        }
[d88c92a]528        else if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->buddy_action )
529        {
530                iu->bu->ic->acc->prpl->buddy_action( iu->bu, ctcp[0], ctcp + 1, NULL );
531        }
[e4816ea]532       
533        return FALSE;
534}
535
536static const struct irc_user_funcs irc_user_im_funcs = {
537        bee_irc_user_privmsg,
538        bee_irc_user_ctcp,
539};
540
[aea8b68]541
[e4816ea]542/* IM->IRC: Groupchats */
[5a75d15]543const struct irc_channel_funcs irc_channel_im_chat_funcs;
[a87754b]544
545static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c )
[aea8b68]546{
547        irc_t *irc = bee->ui_data;
548        irc_channel_t *ic;
549        char *topic;
[eb37735]550        GSList *l;
[aea8b68]551        int i;
552       
[eb37735]553        /* Try to find a channel that expects to receive a groupchat.
[52a2521]554           This flag is set earlier in our current call trace. */
[eb37735]555        for( l = irc->channels; l; l = l->next )
556        {
557                ic = l->data;
558                if( ic->flags & IRC_CHANNEL_CHAT_PICKME )
559                        break;
560        }
561       
562        /* If we found none, just generate some stupid name. */
563        if( l == NULL ) for( i = 0; i <= 999; i ++ )
[aea8b68]564        {
565                char name[16];
[5a75d15]566                sprintf( name, "#chat_%03d", i );
[aea8b68]567                if( ( ic = irc_channel_new( irc, name ) ) )
568                        break;
569        }
570       
571        if( ic == NULL )
572                return FALSE;
573       
574        c->ui_data = ic;
575        ic->data = c;
576       
577        topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
578        irc_channel_set_topic( ic, topic, irc->root );
579        g_free( topic );
580       
581        return TRUE;
582}
583
[a87754b]584static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
[aea8b68]585{
586        irc_channel_t *ic = c->ui_data;
587       
[b1af3e8]588        if( ic == NULL )
589                return FALSE;
590       
[aea8b68]591        if( ic->flags & IRC_CHANNEL_JOINED )
592                irc_channel_printf( ic, "Cleaning up channel, bye!" );
593       
[5a75d15]594        ic->data = NULL;
[6963230]595        c->ui_data = NULL;
[006a84f]596        irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
[aea8b68]597       
598        return TRUE;
599}
600
[a87754b]601static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text )
[aea8b68]602{
[27e2c66]603        irc_channel_t *ic = c->ui_data;
604       
[b1af3e8]605        if( ic == NULL )
606                return FALSE;
607       
[27e2c66]608        irc_channel_printf( ic, "%s", text );
[b17ce85]609       
610        return TRUE;
[aea8b68]611}
612
[a87754b]613static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at )
[aea8b68]614{
[27e2c66]615        irc_t *irc = bee->ui_data;
616        irc_user_t *iu = bu->ui_data;
617        irc_channel_t *ic = c->ui_data;
618        char *ts = NULL;
619       
[b1af3e8]620        if( ic == NULL )
621                return FALSE;
622       
[27e2c66]623        if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) )
624                ts = irc_format_timestamp( irc, sent_at );
625       
626        irc_send_msg( iu, "PRIVMSG", ic->name, msg, ts );
627        g_free( ts );
628       
629        return TRUE;
[aea8b68]630}
631
[a87754b]632static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
[aea8b68]633{
634        irc_t *irc = bee->ui_data;
[b1af3e8]635        irc_channel_t *ic = c->ui_data;
[aea8b68]636       
[b1af3e8]637        if( ic == NULL )
638                return FALSE;
639       
640        irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data );
[b17ce85]641       
642        return TRUE;
[aea8b68]643}
644
[a87754b]645static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
[aea8b68]646{
[b17ce85]647        irc_t *irc = bee->ui_data;
[b1af3e8]648        irc_channel_t *ic = c->ui_data;
649       
[d6657ce]650        if( ic == NULL || bu == NULL )
[b1af3e8]651                return FALSE;
[b17ce85]652       
[1c40aa7]653        /* TODO: Possible bug here: If a module removes $user here instead of just
654           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
655           a broken state around here. */
[b1af3e8]656        irc_channel_del_user( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL );
[b17ce85]657       
658        return TRUE;
[aea8b68]659}
660
[9e27f18]661static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu )
662{
[b1af3e8]663        irc_channel_t *ic = c->ui_data;
[9e27f18]664        irc_t *irc = bee->ui_data;
665        irc_user_t *iu;
666       
[b1af3e8]667        if( ic == NULL )
668                return FALSE;
669       
[9e27f18]670        if( bu == NULL )
671                iu = irc->root;
672        else if( bu == bee->user )
673                iu = irc->user;
674        else
675                iu = bu->ui_data;
676       
[b1af3e8]677        irc_channel_set_topic( ic, new, iu );
[9e27f18]678       
679        return TRUE;
680}
681
[d343eaa]682static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
683{
684        irc_t *irc = bee->ui_data;
[52a2521]685        irc_channel_t *ic = c->ui_data, *oic;
[d343eaa]686        char stripped[MAX_NICK_LENGTH+1], *full_name;
687       
[b1af3e8]688        if( ic == NULL )
689                return FALSE;
690       
[d343eaa]691        /* Don't rename a channel if the user's in it already. */
692        if( ic->flags & IRC_CHANNEL_JOINED )
693                return FALSE;
694       
695        strncpy( stripped, name, MAX_NICK_LENGTH );
696        stripped[MAX_NICK_LENGTH] = '\0';
[134a02c]697        irc_channel_name_strip( stripped );
[d343eaa]698        if( set_getbool( &bee->set, "lcnicks" ) )
699                nick_lc( stripped );
700       
[52a2521]701        if( stripped[0] == '\0' )
702                return FALSE;
[d343eaa]703       
[52a2521]704        full_name = g_strdup_printf( "#%s", stripped );
705        if( ( oic = irc_channel_by_name( irc, full_name ) ) )
[d343eaa]706        {
[52a2521]707                char *type, *chat_type;
708               
709                type = set_getstr( &oic->set, "type" );
710                chat_type = set_getstr( &oic->set, "chat_type" );
711               
712                if( type && chat_type && oic->data == FALSE &&
713                    strcmp( type, "chat" ) == 0 &&
714                    strcmp( chat_type, "groupchat" ) == 0 )
715                {
716                        /* There's a channel with this name already, but it looks
717                           like it's not in use yet. Most likely the IRC client
718                           rejoined the channel after a reconnect. Remove it so
719                           we can reuse its name. */
720                        irc_channel_free( oic );
721                }
722                else
723                {
724                        g_free( full_name );
725                        return FALSE;
726                }
[d343eaa]727        }
728       
[52a2521]729        g_free( ic->name );
730        ic->name = full_name;
731       
[d343eaa]732        return TRUE;
733}
734
[1aa74f55]735static gboolean bee_irc_chat_invite( bee_t *bee, bee_user_t *bu, const char *name, const char *msg )
736{
737        char *channel, *s;
738        irc_t *irc = bee->ui_data;
739        irc_user_t *iu = bu->ui_data;
740        irc_channel_t *chan;
741       
742        if( strchr( CTYPES, name[0] ) )
743                channel = g_strdup( name );
744        else
745                channel = g_strdup_printf( "#%s", name );
746       
747        if( ( s = strchr( channel, '@' ) ) )
748                *s = '\0';
749       
750        if( strlen( channel ) > MAX_NICK_LENGTH )
751        {
752                /* If the channel name is very long (like those insane GTalk
753                   UUID names), try if we can use the inviter's nick. */
754                s = g_strdup_printf( "#%s", iu->nick );
755                if( irc_channel_by_name( irc, s ) == NULL )
756                {
757                        g_free( channel );
758                        channel = s;
759                }
760        }
761       
762        if( ( chan = irc_channel_new( irc, channel ) ) &&
763            set_setstr( &chan->set, "type", "chat" ) &&
764            set_setstr( &chan->set, "chat_type", "room" ) &&
765            set_setstr( &chan->set, "account", bu->ic->acc->tag ) &&
766            set_setstr( &chan->set, "room", (char*) name ) )
767        {
768                /* I'm assuming that if the user didn't "chat add" the room
769                   himself but got invited, it's temporary, so make this a
770                   temporary mapping that is removed as soon as we /PART. */
771                chan->flags |= IRC_CHANNEL_TEMP;
772        }
773        else
774        {
775                irc_channel_free( chan );
776                chan = NULL;
777        }
778        g_free( channel );
779       
780        irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name );
781        if( msg )
782                irc_send_msg( iu, "PRIVMSG", irc->user->nick, msg, NULL );
783        if( chan )
784        {
785                irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name );
786                irc_send_invite( iu, chan );
787        }
788       
789        return TRUE;
790}
791
[a87754b]792/* IRC->IM */
[619dd18]793static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
794
[a87754b]795static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
796{
797        struct groupchat *c = ic->data;
[69b896b]798        char *trans = NULL, *s;
[a87754b]799       
[5a75d15]800        if( c == NULL )
801                return FALSE;
[69b896b]802       
803        if( set_getbool( &ic->set, "translate_to_nicks" ) )
804        {
805                char nick[MAX_NICK_LENGTH+1];
806                irc_user_t *iu;
807               
808                strncpy( nick, msg, MAX_NICK_LENGTH );
809                nick[MAX_NICK_LENGTH] = '\0';
810                if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) )
811                {
812                        *s = '\0';
[4c737ebd]813                        if( ( iu = irc_user_by_name( ic->irc, nick ) ) && iu->bu &&
[69b896b]814                            iu->bu->nick && irc_channel_has_user( ic, iu ) )
815                        {
816                                trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL );
817                                msg = trans;
818                        }
819                }
820        }
821       
822        if( set_getbool( &ic->irc->b->set, "paste_buffer" ) )
[619dd18]823        {
824                int delay;
825               
826                if( ic->pastebuf == NULL )
827                        ic->pastebuf = g_string_new( msg );
828                else
829                {
830                        b_event_remove( ic->pastebuf_timer );
831                        g_string_append_printf( ic->pastebuf, "\n%s", msg );
832                }
833               
834                if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
835                        delay *= 1000;
836               
837                ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic );
838               
[69b896b]839                g_free( trans );
[619dd18]840                return TRUE;
841        }
842        else
843                bee_chat_msg( ic->irc->b, c, msg, 0 );
[a87754b]844       
[69b896b]845        g_free( trans );
[a87754b]846        return TRUE;
[5a75d15]847}
848
[619dd18]849static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
850{
851        irc_channel_t *ic = data;
852       
[cc20520]853        if( ic->data )
854                bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
[619dd18]855       
856        g_string_free( ic->pastebuf, TRUE );
857        ic->pastebuf = 0;
858        ic->pastebuf_timer = 0;
859       
860        return FALSE;
861}
862
[5a75d15]863static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
864{
865        char *acc_s, *room;
866        account_t *acc;
[a87754b]867       
[5a75d15]868        if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 )
869                return TRUE;
870       
871        if( ( acc_s = set_getstr( &ic->set, "account" ) ) &&
872            ( room = set_getstr( &ic->set, "room" ) ) &&
873            ( acc = account_get( ic->irc->b, acc_s ) ) &&
874            acc->ic && acc->prpl->chat_join )
875        {
876                char *nick;
877               
878                if( !( nick = set_getstr( &ic->set, "nick" ) ) )
879                        nick = ic->irc->user->nick;
880               
881                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
[03f3828]882                acc->prpl->chat_join( acc->ic, room, nick, NULL, &ic->set );
[5a75d15]883                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
884               
885                return FALSE;
886        }
887        else
888        {
889                irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name );
890                return FALSE;
891        }
[a87754b]892}
893
[bfb99ee]894static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
895{
896        struct groupchat *c = ic->data;
897       
[5a75d15]898        if( c && c->ic->acc->prpl->chat_leave )
[bfb99ee]899                c->ic->acc->prpl->chat_leave( c );
900       
[6963230]901        /* Remove references in both directions now. We don't need each other anymore. */
[cc20520]902        ic->data = NULL;
[5f74987]903        if( c )
904                c->ui_data = NULL;
[cc20520]905       
[bfb99ee]906        return TRUE;
907}
908
[9e27f18]909static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
910{
[4469e7e]911        struct groupchat *c = ic->data;
[5a75d15]912       
913        if( c == NULL )
914                return FALSE;
[4469e7e]915       
916        if( c->ic->acc->prpl->chat_topic == NULL )
917                irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name );
918        else
919        {
[5a75d15]920                /* TODO: Need more const goodness here, sigh */
921                char *topic = g_strdup( new );
[4469e7e]922                c->ic->acc->prpl->chat_topic( c, topic );
[5a75d15]923                g_free( topic );
[4469e7e]924        }
925               
[41e0c00]926        /* Whatever happened, the IM module should ack the topic change. */
[4469e7e]927        return FALSE;
[9e27f18]928}
929
[66b9e36a]930static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
931{
932        struct groupchat *c = ic->data;
[5a75d15]933        bee_user_t *bu = iu->bu;
934       
935        if( bu == NULL )
936                return FALSE;
[66b9e36a]937       
[5a75d15]938        if( c )
939        {
940                if( iu->bu->ic != c->ic )
941                        irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name );
942                else if( c->ic->acc->prpl->chat_invite )
943                        c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL );
944                else
945                        irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
946        }
947        else if( bu->ic->acc->prpl->chat_with &&
948                 strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 )
949        {
950                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
951                iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
952                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
953        }
[66b9e36a]954        else
[5a75d15]955        {
[66b9e36a]956                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
[5a75d15]957        }
[66b9e36a]958       
959        return TRUE;
960}
961
[5a75d15]962static char *set_eval_room_account( set_t *set, char *value );
[1c40aa7]963static char *set_eval_chat_type( set_t *set, char *value );
[5a75d15]964
965static gboolean bee_irc_channel_init( irc_channel_t *ic )
966{
[57a65600]967        set_t *s;
968
[5a75d15]969        set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
[1c40aa7]970        set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
[57a65600]971
972        s = set_add( &ic->set, "nick", NULL, NULL, ic );
973        s->flags |= SET_NULL_OK;
974
[5a75d15]975        set_add( &ic->set, "room", NULL, NULL, ic );
[69b896b]976        set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
[5a75d15]977       
[1c40aa7]978        /* chat_type == groupchat */
979        ic->flags |= IRC_CHANNEL_TEMP;
980       
[5a75d15]981        return TRUE;
982}
983
984static char *set_eval_room_account( set_t *set, char *value )
985{
986        struct irc_channel *ic = set->data;
[03f3828]987        account_t *acc, *oa;
[5a75d15]988       
989        if( !( acc = account_get( ic->irc->b, value ) ) )
990                return SET_INVALID;
991        else if( !acc->prpl->chat_join )
992        {
[e67e513]993                irc_rootmsg( ic->irc, "Named chatrooms not supported on that account." );
[5a75d15]994                return SET_INVALID;
995        }
996       
[03f3828]997        if( set->value && ( oa = account_get( ic->irc->b, set->value ) ) &&
998            oa->prpl->chat_free_settings )
999                oa->prpl->chat_free_settings( oa, &ic->set );
1000       
1001        if( acc->prpl->chat_add_settings )
1002                acc->prpl->chat_add_settings( acc, &ic->set );
1003       
[e135cd09]1004        return g_strdup( acc->tag );
[5a75d15]1005}
1006
[1c40aa7]1007static char *set_eval_chat_type( set_t *set, char *value )
1008{
1009        struct irc_channel *ic = set->data;
1010       
1011        if( strcmp( value, "groupchat" ) == 0 )
1012                ic->flags |= IRC_CHANNEL_TEMP;
1013        else if( strcmp( value, "room" ) == 0 )
1014                ic->flags &= ~IRC_CHANNEL_TEMP;
1015        else
1016                return NULL;
1017       
1018        return value;
1019}
1020
[5a75d15]1021static gboolean bee_irc_channel_free( irc_channel_t *ic )
1022{
[b1af3e8]1023        struct groupchat *c = ic->data;
1024       
[5a75d15]1025        set_del( &ic->set, "account" );
1026        set_del( &ic->set, "chat_type" );
1027        set_del( &ic->set, "nick" );
1028        set_del( &ic->set, "room" );
[69b896b]1029        set_del( &ic->set, "translate_to_nicks" );
[5a75d15]1030       
[1c40aa7]1031        ic->flags &= ~IRC_CHANNEL_TEMP;
1032       
[b1af3e8]1033        /* That one still points at this channel. Don't. */
1034        if( c )
1035                c->ui_data = NULL;
1036       
[5a75d15]1037        return TRUE;
1038}
1039
1040const struct irc_channel_funcs irc_channel_im_chat_funcs = {
[a87754b]1041        bee_irc_channel_chat_privmsg,
[5a75d15]1042        bee_irc_channel_chat_join,
[bfb99ee]1043        bee_irc_channel_chat_part,
[9e27f18]1044        bee_irc_channel_chat_topic,
[66b9e36a]1045        bee_irc_channel_chat_invite,
[5a75d15]1046
1047        bee_irc_channel_init,
1048        bee_irc_channel_free,
[a87754b]1049};
1050
[aea8b68]1051
[e4816ea]1052/* IM->IRC: File transfers */
[17a6ee9]1053static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
1054{
1055        return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
1056}
1057
[a87754b]1058static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
[17a6ee9]1059{
1060        return dccs_recv_start( ft );
1061}
1062
[a87754b]1063static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
[17a6ee9]1064{
1065        return dcc_close( ft );
1066}
1067
[a87754b]1068static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
[17a6ee9]1069{
1070        dcc_file_transfer_t *df = file->priv;
1071
1072        if( file->bytes_transferred >= file->file_size )
1073                dcc_finish( file );
1074        else
1075                df->proto_finished = TRUE;
1076}
1077
[d860a8d]1078const struct bee_ui_funcs irc_ui_funcs = {
[5c7b45c]1079        bee_irc_imc_connected,
1080        bee_irc_imc_disconnected,
1081       
[81e04e1]1082        bee_irc_user_new,
[d860a8d]1083        bee_irc_user_free,
[1d39159]1084        bee_irc_user_fullname,
[6ef9065]1085        bee_irc_user_nick_hint,
[7e83e8e4]1086        bee_irc_user_group,
[d860a8d]1087        bee_irc_user_status,
[f012a9f]1088        bee_irc_user_msg,
[573dab0]1089        bee_irc_user_typing,
[d88c92a]1090        bee_irc_user_action_response,
[17a6ee9]1091       
[aea8b68]1092        bee_irc_chat_new,
1093        bee_irc_chat_free,
[27e2c66]1094        bee_irc_chat_log,
1095        bee_irc_chat_msg,
[aea8b68]1096        bee_irc_chat_add_user,
[b17ce85]1097        bee_irc_chat_remove_user,
[9e27f18]1098        bee_irc_chat_topic,
[d343eaa]1099        bee_irc_chat_name_hint,
[1aa74f55]1100        bee_irc_chat_invite,
[aea8b68]1101       
[17a6ee9]1102        bee_irc_ft_in_start,
1103        bee_irc_ft_out_start,
1104        bee_irc_ft_close,
1105        bee_irc_ft_finished,
[81e04e1]1106};
Note: See TracBrowser for help on using the repository browser.