source: irc_im.c @ 5eab298f

Last change on this file since 5eab298f was 664bac3, checked in by dequis <dx@…>, at 2015-01-16T19:50:25Z

irc-im: fixed invalid memory reading on chat leave

When a chat is left, prpl->chat_leave() is invoked, which is suppose
to free the groupchat. Since the data is now freed, or suppose to have
been freed, attempting to modify the data will result in bad things.
This simply removes an assignment operation, which was unneeded due to
the memory already being freed.

  • Property mode set to 100644
File size: 27.1 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Some glue to put the IRC and the IM stuff together.                  */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
24*/
25
26#include "bitlbee.h"
27#include "dcc.h"
28
29/* IM->IRC callbacks: Simple IM/buddy-related stuff. */
30
31static const struct irc_user_funcs irc_user_im_funcs;
32
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
45static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu )
46{
47        irc_user_t *iu;
48        irc_t *irc = (irc_t*) bee->ui_data;
49        char nick[MAX_NICK_LENGTH+1], *s;
50       
51        memset( nick, 0, MAX_NICK_LENGTH + 1 );
52        strcpy( nick, nick_get( bu ) );
53       
54        bu->ui_data = iu = irc_user_new( irc, nick );
55        iu->bu = bu;
56       
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       
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 );
70                if( bu->ic->acc->server )
71                        iu->host = g_strdup( bu->ic->acc->server );
72                else
73                {
74                        char *s;
75                        for( s = bu->ic->acc->tag; g_ascii_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                }
84        }
85       
86        while( ( s = strchr( iu->user, ' ' ) ) )
87                *s = '_';
88       
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 )
94                        iu->last_channel = NULL;
95                else if( strcmp( s, "add_channel" ) == 0 )
96                        iu->last_channel = irc->default_channel;
97        }
98       
99        iu->f = &irc_user_im_funcs;
100       
101        return TRUE;
102}
103
104static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu )
105{
106        return irc_user_free( bee->ui_data, (irc_user_t *) bu->ui_data );
107}
108
109static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old )
110{
111        irc_t *irc = bee->ui_data;
112        irc_user_t *iu = bu->ui_data;
113       
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       
120        if( ( bu->flags & BEE_USER_ONLINE ) != ( old->flags & BEE_USER_ONLINE ) )
121        {
122                if( bu->flags & BEE_USER_ONLINE )
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                }
128                else
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" );
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..." );
141                }
142        }
143       
144        /* Reset this one since the info may have changed. */
145        iu->away_reply_timeout = 0;
146       
147        bee_irc_channel_update( irc, NULL, iu );
148       
149        return TRUE;
150}
151
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.. */
162                        if( ic->f == irc->default_channel->f &&
163                            ( ic->flags & IRC_CHANNEL_JOINED ) )
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       
179        if( !irc_channel_wants_user( ic, iu ) )
180        {
181                irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
182        }
183        else
184        {
185                struct irc_control_channel *icc = ic->data;
186                int mode = 0;
187               
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];
192                else
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                }
202        }
203}
204
205static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg_, time_t sent_at )
206{
207        irc_t *irc = bee->ui_data;
208        irc_user_t *iu = (irc_user_t *) bu->ui_data;
209        const char *dst;
210        char *prefix = NULL;
211        char *wrapped, *ts = NULL;
212        char *msg = g_strdup( msg_ );
213        GSList *l;
214       
215        if( sent_at > 0 && set_getbool( &irc->b->set, "display_timestamps" ) )
216                ts = irc_format_timestamp( irc, sent_at );
217       
218        dst = irc_user_msgdest( iu );
219        if( dst != irc->user->nick )
220        {
221                /* if not messaging directly, call user by name */
222                prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ? : "" );
223        }
224        else
225        {
226                prefix = ts;
227                ts = NULL;      /* don't double-free */
228        }
229       
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       
259        wrapped = word_wrap( msg, 425 );
260        irc_send_msg( iu, "PRIVMSG", dst, wrapped, prefix );
261       
262        g_free( wrapped );
263        g_free( prefix );
264        g_free( msg );
265        g_free( ts );
266       
267        return TRUE;
268}
269
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
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
304static gboolean bee_irc_user_nick_update( irc_user_t *iu );
305
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( g_ascii_isspace( *s ) ) *s = ' ';
319       
320        if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) )
321        {
322                /* People don't like this /NOTICE. Meh, let's go back to the old one.
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 );
325                */
326                imcb_log( bu->ic, "User `%s' changed name to `%s'", iu->nick, iu->fullname );
327        }
328       
329        bee_irc_user_nick_update( iu );
330       
331        return TRUE;
332}
333
334static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint )
335{
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;
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;
350       
351        bee_irc_channel_update( irc, NULL, iu );
352        bee_irc_user_nick_update( iu );
353       
354        if( online )
355        {
356                bu->flags |= online;
357                bee_irc_channel_update( irc, NULL, iu );
358        }
359       
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;
367       
368        if( bu->flags & BEE_USER_ONLINE )
369                /* Ignore if the user is visible already. */
370                return TRUE;
371       
372        if( nick_saved( bu ) )
373                /* The user already assigned a nickname to this person. */
374                return TRUE;
375       
376        newnick = nick_get( bu );
377       
378        if( strcmp( iu->nick, newnick ) != 0 )
379        {
380                nick_dedupe( bu, newnick );
381                irc_user_set_nick( iu, newnick );
382        }
383       
384        return TRUE;
385}
386
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
405/* IRC->IM calls */
406
407static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
408
409static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg )
410{
411        const char *away;
412       
413        if( iu->bu == NULL )
414                return FALSE;
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       
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       
432        if( set_getbool( &iu->irc->b->set, "paste_buffer" ) )
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
444        {
445                bee_irc_user_privmsg_cb( iu, 0, 0 );
446               
447                return TRUE;
448        }
449}
450
451static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
452{
453        irc_user_t *iu = data;
454        char *msg;
455        GSList *l;
456       
457        msg = g_string_free( iu->pastebuf, FALSE );
458        iu->pastebuf = NULL;
459        iu->pastebuf_timer = 0;
460       
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        }
482       
483        bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
484       
485        g_free( msg );
486       
487        return FALSE;
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        }
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        }
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        }
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
551
552/* IM->IRC: Groupchats */
553const struct irc_channel_funcs irc_channel_im_chat_funcs;
554
555static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c )
556{
557        irc_t *irc = bee->ui_data;
558        irc_channel_t *ic;
559        char *topic;
560        GSList *l;
561        int i;
562       
563        /* Try to find a channel that expects to receive a groupchat.
564           This flag is set earlier in our current call trace. */
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 ++ )
574        {
575                char name[16];
576                sprintf( name, "#chat_%03d", i );
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
594static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
595{
596        irc_channel_t *ic = c->ui_data;
597       
598        if( ic == NULL )
599                return FALSE;
600       
601        if( ic->flags & IRC_CHANNEL_JOINED )
602                irc_channel_printf( ic, "Cleaning up channel, bye!" );
603       
604        ic->data = NULL;
605        c->ui_data = NULL;
606        irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
607       
608        return TRUE;
609}
610
611static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text )
612{
613        irc_channel_t *ic = c->ui_data;
614       
615        if( ic == NULL )
616                return FALSE;
617       
618        irc_channel_printf( ic, "%s", text );
619       
620        return TRUE;
621}
622
623static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at )
624{
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 *wrapped, *ts = NULL;
629       
630        if( ic == NULL )
631                return FALSE;
632       
633        if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) )
634                ts = irc_format_timestamp( irc, sent_at );
635       
636        wrapped = word_wrap( msg, 425 );
637        irc_send_msg( iu, "PRIVMSG", ic->name, wrapped, ts );
638        g_free( ts );
639        g_free( wrapped );
640       
641        return TRUE;
642}
643
644static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
645{
646        irc_t *irc = bee->ui_data;
647        irc_channel_t *ic = c->ui_data;
648       
649        if( ic == NULL )
650                return FALSE;
651       
652        irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data );
653       
654        return TRUE;
655}
656
657static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
658{
659        irc_t *irc = bee->ui_data;
660        irc_channel_t *ic = c->ui_data;
661       
662        if( ic == NULL || bu == NULL )
663                return FALSE;
664       
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. */
668        irc_channel_del_user( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL );
669       
670        return TRUE;
671}
672
673static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu )
674{
675        irc_channel_t *ic = c->ui_data;
676        irc_t *irc = bee->ui_data;
677        irc_user_t *iu;
678       
679        if( ic == NULL )
680                return FALSE;
681       
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       
689        irc_channel_set_topic( ic, new, iu );
690       
691        return TRUE;
692}
693
694static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
695{
696        irc_t *irc = bee->ui_data;
697        irc_channel_t *ic = c->ui_data, *oic;
698        char stripped[MAX_NICK_LENGTH+1], *full_name;
699       
700        if( ic == NULL )
701                return FALSE;
702       
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';
709        irc_channel_name_strip( stripped );
710        if( set_getbool( &bee->set, "lcnicks" ) )
711                nick_lc( irc, stripped );
712       
713        if( stripped[0] == '\0' )
714                return FALSE;
715       
716        full_name = g_strdup_printf( "#%s", stripped );
717        if( ( oic = irc_channel_by_name( irc, full_name ) ) )
718        {
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                }
739        }
740       
741        g_free( ic->name );
742        ic->name = full_name;
743       
744        return TRUE;
745}
746
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
804/* IRC->IM */
805static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
806
807static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
808{
809        struct groupchat *c = ic->data;
810        char *trans = NULL, *s;
811       
812        if( c == NULL )
813                return FALSE;
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';
825                        if( ( iu = irc_user_by_name( ic->irc, nick ) ) && iu->bu &&
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" ) )
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               
851                g_free( trans );
852                return TRUE;
853        }
854        else
855                bee_chat_msg( ic->irc->b, c, msg, 0 );
856       
857        g_free( trans );
858        return TRUE;
859}
860
861static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
862{
863        irc_channel_t *ic = data;
864       
865        if( ic->data )
866                bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
867       
868        g_string_free( ic->pastebuf, TRUE );
869        ic->pastebuf = 0;
870        ic->pastebuf_timer = 0;
871       
872        return FALSE;
873}
874
875static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
876{
877        char *acc_s, *room;
878        account_t *acc;
879       
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;
894                acc->prpl->chat_join( acc->ic, room, nick, NULL, &ic->set );
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        }
904}
905
906static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
907{
908        struct groupchat *c = ic->data;
909       
910        if( c && c->ic->acc->prpl->chat_leave )
911                c->ic->acc->prpl->chat_leave( c );
912       
913        /* Remove the reference. We don't need it anymore. */
914        ic->data = NULL;
915       
916        return TRUE;
917}
918
919static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
920{
921        struct groupchat *c = ic->data;
922       
923        if( c == NULL )
924                return FALSE;
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        {
930                /* TODO: Need more const goodness here, sigh */
931                char *topic = g_strdup( new );
932                c->ic->acc->prpl->chat_topic( c, topic );
933                g_free( topic );
934        }
935               
936        /* Whatever happened, the IM module should ack the topic change. */
937        return FALSE;
938}
939
940static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
941{
942        struct groupchat *c = ic->data;
943        bee_user_t *bu = iu->bu;
944       
945        if( bu == NULL )
946                return FALSE;
947       
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        }
964        else
965        {
966                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
967        }
968       
969        return TRUE;
970}
971
972static char *set_eval_room_account( set_t *set, char *value );
973static char *set_eval_chat_type( set_t *set, char *value );
974
975static gboolean bee_irc_channel_init( irc_channel_t *ic )
976{
977        set_t *s;
978
979        set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
980        set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
981
982        s = set_add( &ic->set, "nick", NULL, NULL, ic );
983        s->flags |= SET_NULL_OK;
984
985        set_add( &ic->set, "room", NULL, NULL, ic );
986        set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
987       
988        /* chat_type == groupchat */
989        ic->flags |= IRC_CHANNEL_TEMP;
990       
991        return TRUE;
992}
993
994static char *set_eval_room_account( set_t *set, char *value )
995{
996        struct irc_channel *ic = set->data;
997        account_t *acc, *oa;
998       
999        if( !( acc = account_get( ic->irc->b, value ) ) )
1000                return SET_INVALID;
1001        else if( !acc->prpl->chat_join )
1002        {
1003                irc_rootmsg( ic->irc, "Named chatrooms not supported on that account." );
1004                return SET_INVALID;
1005        }
1006       
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       
1014        return g_strdup( acc->tag );
1015}
1016
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
1031static gboolean bee_irc_channel_free( irc_channel_t *ic )
1032{
1033        struct groupchat *c = ic->data;
1034       
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" );
1039        set_del( &ic->set, "translate_to_nicks" );
1040       
1041        ic->flags &= ~IRC_CHANNEL_TEMP;
1042       
1043        /* That one still points at this channel. Don't. */
1044        if( c )
1045                c->ui_data = NULL;
1046       
1047        return TRUE;
1048}
1049
1050const struct irc_channel_funcs irc_channel_im_chat_funcs = {
1051        bee_irc_channel_chat_privmsg,
1052        bee_irc_channel_chat_join,
1053        bee_irc_channel_chat_part,
1054        bee_irc_channel_chat_topic,
1055        bee_irc_channel_chat_invite,
1056
1057        bee_irc_channel_init,
1058        bee_irc_channel_free,
1059};
1060
1061
1062/* IM->IRC: File transfers */
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
1068static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
1069{
1070        return dccs_recv_start( ft );
1071}
1072
1073static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
1074{
1075        return dcc_close( ft );
1076}
1077
1078static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
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
1088const struct bee_ui_funcs irc_ui_funcs = {
1089        bee_irc_imc_connected,
1090        bee_irc_imc_disconnected,
1091       
1092        bee_irc_user_new,
1093        bee_irc_user_free,
1094        bee_irc_user_fullname,
1095        bee_irc_user_nick_hint,
1096        bee_irc_user_group,
1097        bee_irc_user_status,
1098        bee_irc_user_msg,
1099        bee_irc_user_typing,
1100        bee_irc_user_action_response,
1101       
1102        bee_irc_chat_new,
1103        bee_irc_chat_free,
1104        bee_irc_chat_log,
1105        bee_irc_chat_msg,
1106        bee_irc_chat_add_user,
1107        bee_irc_chat_remove_user,
1108        bee_irc_chat_topic,
1109        bee_irc_chat_name_hint,
1110        bee_irc_chat_invite,
1111       
1112        bee_irc_ft_in_start,
1113        bee_irc_ft_out_start,
1114        bee_irc_ft_close,
1115        bee_irc_ft_finished,
1116};
Note: See TracBrowser for help on using the repository browser.