source: irc_im.c @ e252d8c

Last change on this file since e252d8c was 0406d6a6, checked in by dequis <dx@…>, at 2014-09-21T10:53:44Z

Fix word_wrap memory leak...

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