source: irc_im.c @ ea2828f

Last change on this file since ea2828f was 172aa37f, checked in by dequis <dx@…>, at 2014-02-28T00:53:43Z

Word wrapping for group chat mesages (Ticket #1131)

  • Property mode set to 100644
File size: 27.2 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;
[172aa37f]628        char *wrapped, *ts = NULL;
[27e2c66]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       
[172aa37f]636        wrapped = word_wrap( msg, 425 );
637        irc_send_msg( iu, "PRIVMSG", ic->name, wrapped, ts );
[27e2c66]638        g_free( ts );
639       
640        return TRUE;
[aea8b68]641}
642
[a87754b]643static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
[aea8b68]644{
645        irc_t *irc = bee->ui_data;
[b1af3e8]646        irc_channel_t *ic = c->ui_data;
[aea8b68]647       
[b1af3e8]648        if( ic == NULL )
649                return FALSE;
650       
651        irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data );
[b17ce85]652       
653        return TRUE;
[aea8b68]654}
655
[a87754b]656static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
[aea8b68]657{
[b17ce85]658        irc_t *irc = bee->ui_data;
[b1af3e8]659        irc_channel_t *ic = c->ui_data;
660       
[d6657ce]661        if( ic == NULL || bu == NULL )
[b1af3e8]662                return FALSE;
[b17ce85]663       
[1c40aa7]664        /* TODO: Possible bug here: If a module removes $user here instead of just
665           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
666           a broken state around here. */
[b1af3e8]667        irc_channel_del_user( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL );
[b17ce85]668       
669        return TRUE;
[aea8b68]670}
671
[9e27f18]672static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu )
673{
[b1af3e8]674        irc_channel_t *ic = c->ui_data;
[9e27f18]675        irc_t *irc = bee->ui_data;
676        irc_user_t *iu;
677       
[b1af3e8]678        if( ic == NULL )
679                return FALSE;
680       
[9e27f18]681        if( bu == NULL )
682                iu = irc->root;
683        else if( bu == bee->user )
684                iu = irc->user;
685        else
686                iu = bu->ui_data;
687       
[b1af3e8]688        irc_channel_set_topic( ic, new, iu );
[9e27f18]689       
690        return TRUE;
691}
692
[d343eaa]693static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
694{
695        irc_t *irc = bee->ui_data;
[52a2521]696        irc_channel_t *ic = c->ui_data, *oic;
[d343eaa]697        char stripped[MAX_NICK_LENGTH+1], *full_name;
698       
[b1af3e8]699        if( ic == NULL )
700                return FALSE;
701       
[d343eaa]702        /* Don't rename a channel if the user's in it already. */
703        if( ic->flags & IRC_CHANNEL_JOINED )
704                return FALSE;
705       
706        strncpy( stripped, name, MAX_NICK_LENGTH );
707        stripped[MAX_NICK_LENGTH] = '\0';
[134a02c]708        irc_channel_name_strip( stripped );
[d343eaa]709        if( set_getbool( &bee->set, "lcnicks" ) )
[e277e80]710                nick_lc( irc, stripped );
[d343eaa]711       
[52a2521]712        if( stripped[0] == '\0' )
713                return FALSE;
[d343eaa]714       
[52a2521]715        full_name = g_strdup_printf( "#%s", stripped );
716        if( ( oic = irc_channel_by_name( irc, full_name ) ) )
[d343eaa]717        {
[52a2521]718                char *type, *chat_type;
719               
720                type = set_getstr( &oic->set, "type" );
721                chat_type = set_getstr( &oic->set, "chat_type" );
722               
723                if( type && chat_type && oic->data == FALSE &&
724                    strcmp( type, "chat" ) == 0 &&
725                    strcmp( chat_type, "groupchat" ) == 0 )
726                {
727                        /* There's a channel with this name already, but it looks
728                           like it's not in use yet. Most likely the IRC client
729                           rejoined the channel after a reconnect. Remove it so
730                           we can reuse its name. */
731                        irc_channel_free( oic );
732                }
733                else
734                {
735                        g_free( full_name );
736                        return FALSE;
737                }
[d343eaa]738        }
739       
[52a2521]740        g_free( ic->name );
741        ic->name = full_name;
742       
[d343eaa]743        return TRUE;
744}
745
[1aa74f55]746static gboolean bee_irc_chat_invite( bee_t *bee, bee_user_t *bu, const char *name, const char *msg )
747{
748        char *channel, *s;
749        irc_t *irc = bee->ui_data;
750        irc_user_t *iu = bu->ui_data;
751        irc_channel_t *chan;
752       
753        if( strchr( CTYPES, name[0] ) )
754                channel = g_strdup( name );
755        else
756                channel = g_strdup_printf( "#%s", name );
757       
758        if( ( s = strchr( channel, '@' ) ) )
759                *s = '\0';
760       
761        if( strlen( channel ) > MAX_NICK_LENGTH )
762        {
763                /* If the channel name is very long (like those insane GTalk
764                   UUID names), try if we can use the inviter's nick. */
765                s = g_strdup_printf( "#%s", iu->nick );
766                if( irc_channel_by_name( irc, s ) == NULL )
767                {
768                        g_free( channel );
769                        channel = s;
770                }
771        }
772       
773        if( ( chan = irc_channel_new( irc, channel ) ) &&
774            set_setstr( &chan->set, "type", "chat" ) &&
775            set_setstr( &chan->set, "chat_type", "room" ) &&
776            set_setstr( &chan->set, "account", bu->ic->acc->tag ) &&
777            set_setstr( &chan->set, "room", (char*) name ) )
778        {
779                /* I'm assuming that if the user didn't "chat add" the room
780                   himself but got invited, it's temporary, so make this a
781                   temporary mapping that is removed as soon as we /PART. */
782                chan->flags |= IRC_CHANNEL_TEMP;
783        }
784        else
785        {
786                irc_channel_free( chan );
787                chan = NULL;
788        }
789        g_free( channel );
790       
791        irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name );
792        if( msg )
793                irc_send_msg( iu, "PRIVMSG", irc->user->nick, msg, NULL );
794        if( chan )
795        {
796                irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name );
797                irc_send_invite( iu, chan );
798        }
799       
800        return TRUE;
801}
802
[a87754b]803/* IRC->IM */
[619dd18]804static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
805
[a87754b]806static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
807{
808        struct groupchat *c = ic->data;
[69b896b]809        char *trans = NULL, *s;
[a87754b]810       
[5a75d15]811        if( c == NULL )
812                return FALSE;
[69b896b]813       
814        if( set_getbool( &ic->set, "translate_to_nicks" ) )
815        {
816                char nick[MAX_NICK_LENGTH+1];
817                irc_user_t *iu;
818               
819                strncpy( nick, msg, MAX_NICK_LENGTH );
820                nick[MAX_NICK_LENGTH] = '\0';
821                if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) )
822                {
823                        *s = '\0';
[4c737ebd]824                        if( ( iu = irc_user_by_name( ic->irc, nick ) ) && iu->bu &&
[69b896b]825                            iu->bu->nick && irc_channel_has_user( ic, iu ) )
826                        {
827                                trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL );
828                                msg = trans;
829                        }
830                }
831        }
832       
833        if( set_getbool( &ic->irc->b->set, "paste_buffer" ) )
[619dd18]834        {
835                int delay;
836               
837                if( ic->pastebuf == NULL )
838                        ic->pastebuf = g_string_new( msg );
839                else
840                {
841                        b_event_remove( ic->pastebuf_timer );
842                        g_string_append_printf( ic->pastebuf, "\n%s", msg );
843                }
844               
845                if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
846                        delay *= 1000;
847               
848                ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic );
849               
[69b896b]850                g_free( trans );
[619dd18]851                return TRUE;
852        }
853        else
854                bee_chat_msg( ic->irc->b, c, msg, 0 );
[a87754b]855       
[69b896b]856        g_free( trans );
[a87754b]857        return TRUE;
[5a75d15]858}
859
[619dd18]860static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
861{
862        irc_channel_t *ic = data;
863       
[cc20520]864        if( ic->data )
865                bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
[619dd18]866       
867        g_string_free( ic->pastebuf, TRUE );
868        ic->pastebuf = 0;
869        ic->pastebuf_timer = 0;
870       
871        return FALSE;
872}
873
[5a75d15]874static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
875{
876        char *acc_s, *room;
877        account_t *acc;
[a87754b]878       
[5a75d15]879        if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 )
880                return TRUE;
881       
882        if( ( acc_s = set_getstr( &ic->set, "account" ) ) &&
883            ( room = set_getstr( &ic->set, "room" ) ) &&
884            ( acc = account_get( ic->irc->b, acc_s ) ) &&
885            acc->ic && acc->prpl->chat_join )
886        {
887                char *nick;
888               
889                if( !( nick = set_getstr( &ic->set, "nick" ) ) )
890                        nick = ic->irc->user->nick;
891               
892                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
[03f3828]893                acc->prpl->chat_join( acc->ic, room, nick, NULL, &ic->set );
[5a75d15]894                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
895               
896                return FALSE;
897        }
898        else
899        {
900                irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name );
901                return FALSE;
902        }
[a87754b]903}
904
[bfb99ee]905static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
906{
907        struct groupchat *c = ic->data;
908       
[5a75d15]909        if( c && c->ic->acc->prpl->chat_leave )
[bfb99ee]910                c->ic->acc->prpl->chat_leave( c );
911       
[6963230]912        /* Remove references in both directions now. We don't need each other anymore. */
[cc20520]913        ic->data = NULL;
[5f74987]914        if( c )
915                c->ui_data = NULL;
[cc20520]916       
[bfb99ee]917        return TRUE;
918}
919
[9e27f18]920static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
921{
[4469e7e]922        struct groupchat *c = ic->data;
[5a75d15]923       
924        if( c == NULL )
925                return FALSE;
[4469e7e]926       
927        if( c->ic->acc->prpl->chat_topic == NULL )
928                irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name );
929        else
930        {
[5a75d15]931                /* TODO: Need more const goodness here, sigh */
932                char *topic = g_strdup( new );
[4469e7e]933                c->ic->acc->prpl->chat_topic( c, topic );
[5a75d15]934                g_free( topic );
[4469e7e]935        }
936               
[41e0c00]937        /* Whatever happened, the IM module should ack the topic change. */
[4469e7e]938        return FALSE;
[9e27f18]939}
940
[66b9e36a]941static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
942{
943        struct groupchat *c = ic->data;
[5a75d15]944        bee_user_t *bu = iu->bu;
945       
946        if( bu == NULL )
947                return FALSE;
[66b9e36a]948       
[5a75d15]949        if( c )
950        {
951                if( iu->bu->ic != c->ic )
952                        irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name );
953                else if( c->ic->acc->prpl->chat_invite )
954                        c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL );
955                else
956                        irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
957        }
958        else if( bu->ic->acc->prpl->chat_with &&
959                 strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 )
960        {
961                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
962                iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
963                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
964        }
[66b9e36a]965        else
[5a75d15]966        {
[66b9e36a]967                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
[5a75d15]968        }
[66b9e36a]969       
970        return TRUE;
971}
972
[5a75d15]973static char *set_eval_room_account( set_t *set, char *value );
[1c40aa7]974static char *set_eval_chat_type( set_t *set, char *value );
[5a75d15]975
976static gboolean bee_irc_channel_init( irc_channel_t *ic )
977{
[57a65600]978        set_t *s;
979
[5a75d15]980        set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
[1c40aa7]981        set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
[57a65600]982
983        s = set_add( &ic->set, "nick", NULL, NULL, ic );
984        s->flags |= SET_NULL_OK;
985
[5a75d15]986        set_add( &ic->set, "room", NULL, NULL, ic );
[69b896b]987        set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
[5a75d15]988       
[1c40aa7]989        /* chat_type == groupchat */
990        ic->flags |= IRC_CHANNEL_TEMP;
991       
[5a75d15]992        return TRUE;
993}
994
995static char *set_eval_room_account( set_t *set, char *value )
996{
997        struct irc_channel *ic = set->data;
[03f3828]998        account_t *acc, *oa;
[5a75d15]999       
1000        if( !( acc = account_get( ic->irc->b, value ) ) )
1001                return SET_INVALID;
1002        else if( !acc->prpl->chat_join )
1003        {
[e67e513]1004                irc_rootmsg( ic->irc, "Named chatrooms not supported on that account." );
[5a75d15]1005                return SET_INVALID;
1006        }
1007       
[03f3828]1008        if( set->value && ( oa = account_get( ic->irc->b, set->value ) ) &&
1009            oa->prpl->chat_free_settings )
1010                oa->prpl->chat_free_settings( oa, &ic->set );
1011       
1012        if( acc->prpl->chat_add_settings )
1013                acc->prpl->chat_add_settings( acc, &ic->set );
1014       
[e135cd09]1015        return g_strdup( acc->tag );
[5a75d15]1016}
1017
[1c40aa7]1018static char *set_eval_chat_type( set_t *set, char *value )
1019{
1020        struct irc_channel *ic = set->data;
1021       
1022        if( strcmp( value, "groupchat" ) == 0 )
1023                ic->flags |= IRC_CHANNEL_TEMP;
1024        else if( strcmp( value, "room" ) == 0 )
1025                ic->flags &= ~IRC_CHANNEL_TEMP;
1026        else
1027                return NULL;
1028       
1029        return value;
1030}
1031
[5a75d15]1032static gboolean bee_irc_channel_free( irc_channel_t *ic )
1033{
[b1af3e8]1034        struct groupchat *c = ic->data;
1035       
[5a75d15]1036        set_del( &ic->set, "account" );
1037        set_del( &ic->set, "chat_type" );
1038        set_del( &ic->set, "nick" );
1039        set_del( &ic->set, "room" );
[69b896b]1040        set_del( &ic->set, "translate_to_nicks" );
[5a75d15]1041       
[1c40aa7]1042        ic->flags &= ~IRC_CHANNEL_TEMP;
1043       
[b1af3e8]1044        /* That one still points at this channel. Don't. */
1045        if( c )
1046                c->ui_data = NULL;
1047       
[5a75d15]1048        return TRUE;
1049}
1050
1051const struct irc_channel_funcs irc_channel_im_chat_funcs = {
[a87754b]1052        bee_irc_channel_chat_privmsg,
[5a75d15]1053        bee_irc_channel_chat_join,
[bfb99ee]1054        bee_irc_channel_chat_part,
[9e27f18]1055        bee_irc_channel_chat_topic,
[66b9e36a]1056        bee_irc_channel_chat_invite,
[5a75d15]1057
1058        bee_irc_channel_init,
1059        bee_irc_channel_free,
[a87754b]1060};
1061
[aea8b68]1062
[e4816ea]1063/* IM->IRC: File transfers */
[17a6ee9]1064static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
1065{
1066        return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
1067}
1068
[a87754b]1069static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
[17a6ee9]1070{
1071        return dccs_recv_start( ft );
1072}
1073
[a87754b]1074static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
[17a6ee9]1075{
1076        return dcc_close( ft );
1077}
1078
[a87754b]1079static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
[17a6ee9]1080{
1081        dcc_file_transfer_t *df = file->priv;
1082
1083        if( file->bytes_transferred >= file->file_size )
1084                dcc_finish( file );
1085        else
1086                df->proto_finished = TRUE;
1087}
1088
[d860a8d]1089const struct bee_ui_funcs irc_ui_funcs = {
[5c7b45c]1090        bee_irc_imc_connected,
1091        bee_irc_imc_disconnected,
1092       
[81e04e1]1093        bee_irc_user_new,
[d860a8d]1094        bee_irc_user_free,
[1d39159]1095        bee_irc_user_fullname,
[6ef9065]1096        bee_irc_user_nick_hint,
[7e83e8e4]1097        bee_irc_user_group,
[d860a8d]1098        bee_irc_user_status,
[f012a9f]1099        bee_irc_user_msg,
[573dab0]1100        bee_irc_user_typing,
[d88c92a]1101        bee_irc_user_action_response,
[17a6ee9]1102       
[aea8b68]1103        bee_irc_chat_new,
1104        bee_irc_chat_free,
[27e2c66]1105        bee_irc_chat_log,
1106        bee_irc_chat_msg,
[aea8b68]1107        bee_irc_chat_add_user,
[b17ce85]1108        bee_irc_chat_remove_user,
[9e27f18]1109        bee_irc_chat_topic,
[d343eaa]1110        bee_irc_chat_name_hint,
[1aa74f55]1111        bee_irc_chat_invite,
[aea8b68]1112       
[17a6ee9]1113        bee_irc_ft_in_start,
1114        bee_irc_ft_out_start,
1115        bee_irc_ft_close,
1116        bee_irc_ft_finished,
[81e04e1]1117};
Note: See TracBrowser for help on using the repository browser.