source: irc_im.c @ cc20520

Last change on this file since cc20520 was cc20520, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-12-02T12:14:09Z

Clean up pastebuf_timer when cleaning up channels, and properly clean up
channel-chatroom reference when leaving a chatroom. This fixes two very
similar crash bugs when leaving a chatroom within the paste_buffer_delay
period.

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