source: irc_im.c @ c1d40e7

Last change on this file since c1d40e7 was d6657ce, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-09-05T23:30:40Z

Fix NULL pointer dereference when removing nicks from groupchats.

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