source: irc_im.c @ e4752a6

Last change on this file since e4752a6 was c92ee728, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-07-01T22:43:02Z

Use account tag instead of protocol/network name for generating hostmasks.

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