source: irc_im.c @ d88c92a

Last change on this file since d88c92a was d88c92a, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-12-06T00:03:49Z

First bits of CTCP support to contacts. (Try /CTCP VERSION on a Jabber
contact.)

  • Property mode set to 100644
File size: 26.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
[d88c92a]282static gboolean bee_irc_user_action_response( bee_t *bee, bee_user_t *bu, const char *action, char * const args[], void *data )
283{
284        irc_t *irc = (irc_t *) bee->ui_data;
285        GString *msg = g_string_new( "\001" );
286       
287        g_string_append( msg, action );
288        while( *args )
289        {
290                if( strchr( *args, ' ' ) )
291                        g_string_append_printf( msg, " \"%s\"", *args );
292                else
293                        g_string_append_printf( msg, " %s", *args );
294                args ++;
295        }
296        g_string_append_c( msg, '\001' );
297       
298        irc_send_msg( (irc_user_t *) bu->ui_data, "NOTICE", irc->user->nick, msg->str, NULL );
299       
300        return TRUE;
301}
302
[badd148]303static gboolean bee_irc_user_nick_update( irc_user_t *iu );
[6ef9065]304
[1d39159]305static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu )
306{
307        irc_user_t *iu = (irc_user_t *) bu->ui_data;
308        char *s;
309       
310        if( iu->fullname != iu->nick )
311                g_free( iu->fullname );
312        iu->fullname = g_strdup( bu->fullname );
313       
314        /* Strip newlines (unlikely, but IRC-unfriendly so they must go)
315           TODO(wilmer): Do the same with away msgs again! */
316        for( s = iu->fullname; *s; s ++ )
317                if( isspace( *s ) ) *s = ' ';
318       
319        if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) )
320        {
[fda55fa]321                /* People don't like this /NOTICE. Meh, let's go back to the old one.
[1d39159]322                char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
323                irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
[fda55fa]324                */
325                imcb_log( bu->ic, "User `%s' changed name to `%s'", iu->nick, iu->fullname );
[1d39159]326        }
327       
[badd148]328        bee_irc_user_nick_update( iu );
[1d39159]329       
330        return TRUE;
331}
332
[6ef9065]333static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint )
334{
[badd148]335        bee_irc_user_nick_update( (irc_user_t*) bu->ui_data );
336       
337        return TRUE;
338}
339
340static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu )
341{
342        irc_user_t *iu = (irc_user_t *) bu->ui_data;
343        irc_t *irc = (irc_t *) bee->ui_data;
[51a3d12]344        bee_user_flags_t online;
345       
346        /* Take the user offline temporarily so we can change the nick (if necessary). */
347        if( ( online = bu->flags & BEE_USER_ONLINE ) )
348                bu->flags &= ~BEE_USER_ONLINE;
[badd148]349       
350        bee_irc_channel_update( irc, NULL, iu );
351        bee_irc_user_nick_update( iu );
352       
[51a3d12]353        if( online )
354        {
355                bu->flags |= online;
356                bee_irc_channel_update( irc, NULL, iu );
357        }
358       
[badd148]359        return TRUE;
360}
361
362static gboolean bee_irc_user_nick_update( irc_user_t *iu )
363{
364        bee_user_t *bu = iu->bu;
365        char *newnick;
[6ef9065]366       
367        if( bu->flags & BEE_USER_ONLINE )
368                /* Ignore if the user is visible already. */
369                return TRUE;
370       
[b1f818b]371        if( nick_saved( bu ) )
[6ef9065]372                /* The user already assigned a nickname to this person. */
373                return TRUE;
374       
[badd148]375        newnick = nick_get( bu );
[6ef9065]376       
377        if( strcmp( iu->nick, newnick ) != 0 )
378        {
[b1f818b]379                nick_dedupe( bu, newnick );
[6ef9065]380                irc_user_set_nick( iu, newnick );
381        }
382       
383        return TRUE;
384}
385
[a429907]386void bee_irc_user_nick_reset( irc_user_t *iu )
387{
388        bee_user_t *bu = iu->bu;
389        bee_user_flags_t online;
390       
391        if( bu == FALSE )
392                return;
393       
394        /* In this case, pretend the user is offline. */
395        if( ( online = bu->flags & BEE_USER_ONLINE ) )
396                bu->flags &= ~BEE_USER_ONLINE;
397       
398        nick_del( bu );
399        bee_irc_user_nick_update( iu );
400       
401        bu->flags |= online;
402}
403
[e4816ea]404/* IRC->IM calls */
405
[619dd18]406static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
407
[e4816ea]408static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg )
409{
[1c8e5f7]410        const char *away;
411       
[619dd18]412        if( iu->bu == NULL )
[e4816ea]413                return FALSE;
[1c8e5f7]414       
415        if( ( away = irc_user_get_away( iu ) ) &&
416            time( NULL ) >= iu->away_reply_timeout )
417        {
418                irc_send_num( iu->irc, 301, "%s :%s", iu->nick, away );
419                iu->away_reply_timeout = time( NULL ) +
420                        set_getint( &iu->irc->b->set, "away_reply_timeout" );
421        }
422       
[934db064]423        if( iu->pastebuf == NULL )
424                iu->pastebuf = g_string_new( msg );
425        else
426        {
427                b_event_remove( iu->pastebuf_timer );
428                g_string_append_printf( iu->pastebuf, "\n%s", msg );
429        }
430       
[1c8e5f7]431        if( set_getbool( &iu->irc->b->set, "paste_buffer" ) )
[619dd18]432        {
433                int delay;
434               
435                if( ( delay = set_getint( &iu->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
436                        delay *= 1000;
437               
438                iu->pastebuf_timer = b_timeout_add( delay, bee_irc_user_privmsg_cb, iu );
439               
440                return TRUE;
441        }
442        else
[934db064]443        {
444                bee_irc_user_privmsg_cb( iu, 0, 0 );
445               
446                return TRUE;
447        }
[619dd18]448}
449
450static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
451{
452        irc_user_t *iu = data;
[934db064]453        char *msg = g_string_free( iu->pastebuf, FALSE );
454        GSList *l;
455       
456        for( l = irc_plugins; l; l = l->next )
457        {
458                irc_plugin_t *p = l->data;
459                if( p->filter_msg_out )
460                {
461                        char *s = p->filter_msg_out( iu, msg, 0 );
462                        if( s )
463                        {
464                                if( s != msg )
465                                        g_free( msg );
466                                msg = s;
467                        }
468                        else
469                        {
470                                /* Modules can swallow messages. */
471                                iu->pastebuf = NULL;
472                                g_free( msg );
473                                return FALSE;
474                        }
475                }
476        }
[619dd18]477       
[934db064]478        bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
[619dd18]479       
[934db064]480        g_free( msg );
481        iu->pastebuf = NULL;
[619dd18]482        iu->pastebuf_timer = 0;
483       
484        return FALSE;
[e4816ea]485}
486
487static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp )
488{
489        if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0
490                    && g_strcasecmp( ctcp[1], "SEND" ) == 0 )
491        {
492                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
493                {
494                        file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp );
495                        if ( ft )
496                                iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle );
497                       
498                        return TRUE;
499                }
500        }
501        else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 )
502        {
503                if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] )
504                {
505                        int st = ctcp[1][0];
506                        if( st >= '0' && st <= '2' )
507                        {
508                                st <<= 8;
509                                iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st );
510                        }
511                       
512                        return TRUE;
513                }
514        }
[d88c92a]515        else if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->buddy_action )
516        {
517                iu->bu->ic->acc->prpl->buddy_action( iu->bu, ctcp[0], ctcp + 1, NULL );
518        }
[e4816ea]519       
520        return FALSE;
521}
522
523static const struct irc_user_funcs irc_user_im_funcs = {
524        bee_irc_user_privmsg,
525        bee_irc_user_ctcp,
526};
527
[aea8b68]528
[e4816ea]529/* IM->IRC: Groupchats */
[5a75d15]530const struct irc_channel_funcs irc_channel_im_chat_funcs;
[a87754b]531
532static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c )
[aea8b68]533{
534        irc_t *irc = bee->ui_data;
535        irc_channel_t *ic;
536        char *topic;
[eb37735]537        GSList *l;
[aea8b68]538        int i;
539       
[eb37735]540        /* Try to find a channel that expects to receive a groupchat.
[52a2521]541           This flag is set earlier in our current call trace. */
[eb37735]542        for( l = irc->channels; l; l = l->next )
543        {
544                ic = l->data;
545                if( ic->flags & IRC_CHANNEL_CHAT_PICKME )
546                        break;
547        }
548       
549        /* If we found none, just generate some stupid name. */
550        if( l == NULL ) for( i = 0; i <= 999; i ++ )
[aea8b68]551        {
552                char name[16];
[5a75d15]553                sprintf( name, "#chat_%03d", i );
[aea8b68]554                if( ( ic = irc_channel_new( irc, name ) ) )
555                        break;
556        }
557       
558        if( ic == NULL )
559                return FALSE;
560       
561        c->ui_data = ic;
562        ic->data = c;
563       
564        topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
565        irc_channel_set_topic( ic, topic, irc->root );
566        g_free( topic );
567       
568        return TRUE;
569}
570
[a87754b]571static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
[aea8b68]572{
573        irc_channel_t *ic = c->ui_data;
574       
[b1af3e8]575        if( ic == NULL )
576                return FALSE;
577       
[aea8b68]578        if( ic->flags & IRC_CHANNEL_JOINED )
579                irc_channel_printf( ic, "Cleaning up channel, bye!" );
580       
[5a75d15]581        ic->data = NULL;
[006a84f]582        irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
[aea8b68]583       
584        return TRUE;
585}
586
[a87754b]587static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text )
[aea8b68]588{
[27e2c66]589        irc_channel_t *ic = c->ui_data;
590       
[b1af3e8]591        if( ic == NULL )
592                return FALSE;
593       
[27e2c66]594        irc_channel_printf( ic, "%s", text );
[b17ce85]595       
596        return TRUE;
[aea8b68]597}
598
[a87754b]599static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at )
[aea8b68]600{
[27e2c66]601        irc_t *irc = bee->ui_data;
602        irc_user_t *iu = bu->ui_data;
603        irc_channel_t *ic = c->ui_data;
604        char *ts = NULL;
605       
[b1af3e8]606        if( ic == NULL )
607                return FALSE;
608       
[27e2c66]609        if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) )
610                ts = irc_format_timestamp( irc, sent_at );
611       
612        irc_send_msg( iu, "PRIVMSG", ic->name, msg, ts );
613        g_free( ts );
614       
615        return TRUE;
[aea8b68]616}
617
[a87754b]618static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
[aea8b68]619{
620        irc_t *irc = bee->ui_data;
[b1af3e8]621        irc_channel_t *ic = c->ui_data;
[aea8b68]622       
[b1af3e8]623        if( ic == NULL )
624                return FALSE;
625       
626        irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data );
[b17ce85]627       
628        return TRUE;
[aea8b68]629}
630
[a87754b]631static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
[aea8b68]632{
[b17ce85]633        irc_t *irc = bee->ui_data;
[b1af3e8]634        irc_channel_t *ic = c->ui_data;
635       
[d6657ce]636        if( ic == NULL || bu == NULL )
[b1af3e8]637                return FALSE;
[b17ce85]638       
[1c40aa7]639        /* TODO: Possible bug here: If a module removes $user here instead of just
640           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
641           a broken state around here. */
[b1af3e8]642        irc_channel_del_user( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL );
[b17ce85]643       
644        return TRUE;
[aea8b68]645}
646
[9e27f18]647static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu )
648{
[b1af3e8]649        irc_channel_t *ic = c->ui_data;
[9e27f18]650        irc_t *irc = bee->ui_data;
651        irc_user_t *iu;
652       
[b1af3e8]653        if( ic == NULL )
654                return FALSE;
655       
[9e27f18]656        if( bu == NULL )
657                iu = irc->root;
658        else if( bu == bee->user )
659                iu = irc->user;
660        else
661                iu = bu->ui_data;
662       
[b1af3e8]663        irc_channel_set_topic( ic, new, iu );
[9e27f18]664       
665        return TRUE;
666}
667
[d343eaa]668static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
669{
670        irc_t *irc = bee->ui_data;
[52a2521]671        irc_channel_t *ic = c->ui_data, *oic;
[d343eaa]672        char stripped[MAX_NICK_LENGTH+1], *full_name;
673       
[b1af3e8]674        if( ic == NULL )
675                return FALSE;
676       
[d343eaa]677        /* Don't rename a channel if the user's in it already. */
678        if( ic->flags & IRC_CHANNEL_JOINED )
679                return FALSE;
680       
681        strncpy( stripped, name, MAX_NICK_LENGTH );
682        stripped[MAX_NICK_LENGTH] = '\0';
[134a02c]683        irc_channel_name_strip( stripped );
[d343eaa]684        if( set_getbool( &bee->set, "lcnicks" ) )
685                nick_lc( stripped );
686       
[52a2521]687        if( stripped[0] == '\0' )
688                return FALSE;
[d343eaa]689       
[52a2521]690        full_name = g_strdup_printf( "#%s", stripped );
691        if( ( oic = irc_channel_by_name( irc, full_name ) ) )
[d343eaa]692        {
[52a2521]693                char *type, *chat_type;
694               
695                type = set_getstr( &oic->set, "type" );
696                chat_type = set_getstr( &oic->set, "chat_type" );
697               
698                if( type && chat_type && oic->data == FALSE &&
699                    strcmp( type, "chat" ) == 0 &&
700                    strcmp( chat_type, "groupchat" ) == 0 )
701                {
702                        /* There's a channel with this name already, but it looks
703                           like it's not in use yet. Most likely the IRC client
704                           rejoined the channel after a reconnect. Remove it so
705                           we can reuse its name. */
706                        irc_channel_free( oic );
707                }
708                else
709                {
710                        g_free( full_name );
711                        return FALSE;
712                }
[d343eaa]713        }
714       
[52a2521]715        g_free( ic->name );
716        ic->name = full_name;
717       
[d343eaa]718        return TRUE;
719}
720
[1aa74f55]721static gboolean bee_irc_chat_invite( bee_t *bee, bee_user_t *bu, const char *name, const char *msg )
722{
723        char *channel, *s;
724        irc_t *irc = bee->ui_data;
725        irc_user_t *iu = bu->ui_data;
726        irc_channel_t *chan;
727       
728        if( strchr( CTYPES, name[0] ) )
729                channel = g_strdup( name );
730        else
731                channel = g_strdup_printf( "#%s", name );
732       
733        if( ( s = strchr( channel, '@' ) ) )
734                *s = '\0';
735       
736        if( strlen( channel ) > MAX_NICK_LENGTH )
737        {
738                /* If the channel name is very long (like those insane GTalk
739                   UUID names), try if we can use the inviter's nick. */
740                s = g_strdup_printf( "#%s", iu->nick );
741                if( irc_channel_by_name( irc, s ) == NULL )
742                {
743                        g_free( channel );
744                        channel = s;
745                }
746        }
747       
748        if( ( chan = irc_channel_new( irc, channel ) ) &&
749            set_setstr( &chan->set, "type", "chat" ) &&
750            set_setstr( &chan->set, "chat_type", "room" ) &&
751            set_setstr( &chan->set, "account", bu->ic->acc->tag ) &&
752            set_setstr( &chan->set, "room", (char*) name ) )
753        {
754                /* I'm assuming that if the user didn't "chat add" the room
755                   himself but got invited, it's temporary, so make this a
756                   temporary mapping that is removed as soon as we /PART. */
757                chan->flags |= IRC_CHANNEL_TEMP;
758        }
759        else
760        {
761                irc_channel_free( chan );
762                chan = NULL;
763        }
764        g_free( channel );
765       
766        irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name );
767        if( msg )
768                irc_send_msg( iu, "PRIVMSG", irc->user->nick, msg, NULL );
769        if( chan )
770        {
771                irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name );
772                irc_send_invite( iu, chan );
773        }
774       
775        return TRUE;
776}
777
[a87754b]778/* IRC->IM */
[619dd18]779static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
780
[a87754b]781static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
782{
783        struct groupchat *c = ic->data;
[69b896b]784        char *trans = NULL, *s;
[a87754b]785       
[5a75d15]786        if( c == NULL )
787                return FALSE;
[69b896b]788       
789        if( set_getbool( &ic->set, "translate_to_nicks" ) )
790        {
791                char nick[MAX_NICK_LENGTH+1];
792                irc_user_t *iu;
793               
794                strncpy( nick, msg, MAX_NICK_LENGTH );
795                nick[MAX_NICK_LENGTH] = '\0';
796                if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) )
797                {
798                        *s = '\0';
[4c737ebd]799                        if( ( iu = irc_user_by_name( ic->irc, nick ) ) && iu->bu &&
[69b896b]800                            iu->bu->nick && irc_channel_has_user( ic, iu ) )
801                        {
802                                trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL );
803                                msg = trans;
804                        }
805                }
806        }
807       
808        if( set_getbool( &ic->irc->b->set, "paste_buffer" ) )
[619dd18]809        {
810                int delay;
811               
812                if( ic->pastebuf == NULL )
813                        ic->pastebuf = g_string_new( msg );
814                else
815                {
816                        b_event_remove( ic->pastebuf_timer );
817                        g_string_append_printf( ic->pastebuf, "\n%s", msg );
818                }
819               
820                if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
821                        delay *= 1000;
822               
823                ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic );
824               
[69b896b]825                g_free( trans );
[619dd18]826                return TRUE;
827        }
828        else
829                bee_chat_msg( ic->irc->b, c, msg, 0 );
[a87754b]830       
[69b896b]831        g_free( trans );
[a87754b]832        return TRUE;
[5a75d15]833}
834
[619dd18]835static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
836{
837        irc_channel_t *ic = data;
838       
[cc20520]839        if( ic->data )
840                bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
[619dd18]841       
842        g_string_free( ic->pastebuf, TRUE );
843        ic->pastebuf = 0;
844        ic->pastebuf_timer = 0;
845       
846        return FALSE;
847}
848
[5a75d15]849static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
850{
851        char *acc_s, *room;
852        account_t *acc;
[a87754b]853       
[5a75d15]854        if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 )
855                return TRUE;
856       
857        if( ( acc_s = set_getstr( &ic->set, "account" ) ) &&
858            ( room = set_getstr( &ic->set, "room" ) ) &&
859            ( acc = account_get( ic->irc->b, acc_s ) ) &&
860            acc->ic && acc->prpl->chat_join )
861        {
862                char *nick;
863               
864                if( !( nick = set_getstr( &ic->set, "nick" ) ) )
865                        nick = ic->irc->user->nick;
866               
867                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
[03f3828]868                acc->prpl->chat_join( acc->ic, room, nick, NULL, &ic->set );
[5a75d15]869                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
870               
871                return FALSE;
872        }
873        else
874        {
875                irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name );
876                return FALSE;
877        }
[a87754b]878}
879
[bfb99ee]880static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
881{
882        struct groupchat *c = ic->data;
883       
[5a75d15]884        if( c && c->ic->acc->prpl->chat_leave )
[bfb99ee]885                c->ic->acc->prpl->chat_leave( c );
886       
[cc20520]887        ic->data = NULL;
888       
[bfb99ee]889        return TRUE;
890}
891
[9e27f18]892static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
893{
[4469e7e]894        struct groupchat *c = ic->data;
[5a75d15]895       
896        if( c == NULL )
897                return FALSE;
[4469e7e]898       
899        if( c->ic->acc->prpl->chat_topic == NULL )
900                irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name );
901        else
902        {
[5a75d15]903                /* TODO: Need more const goodness here, sigh */
904                char *topic = g_strdup( new );
[4469e7e]905                c->ic->acc->prpl->chat_topic( c, topic );
[5a75d15]906                g_free( topic );
[4469e7e]907        }
908               
[41e0c00]909        /* Whatever happened, the IM module should ack the topic change. */
[4469e7e]910        return FALSE;
[9e27f18]911}
912
[66b9e36a]913static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
914{
915        struct groupchat *c = ic->data;
[5a75d15]916        bee_user_t *bu = iu->bu;
917       
918        if( bu == NULL )
919                return FALSE;
[66b9e36a]920       
[5a75d15]921        if( c )
922        {
923                if( iu->bu->ic != c->ic )
924                        irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name );
925                else if( c->ic->acc->prpl->chat_invite )
926                        c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL );
927                else
928                        irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
929        }
930        else if( bu->ic->acc->prpl->chat_with &&
931                 strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 )
932        {
933                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
934                iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
935                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
936        }
[66b9e36a]937        else
[5a75d15]938        {
[66b9e36a]939                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
[5a75d15]940        }
[66b9e36a]941       
942        return TRUE;
943}
944
[5a75d15]945static char *set_eval_room_account( set_t *set, char *value );
[1c40aa7]946static char *set_eval_chat_type( set_t *set, char *value );
[5a75d15]947
948static gboolean bee_irc_channel_init( irc_channel_t *ic )
949{
950        set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
[1c40aa7]951        set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
[5a75d15]952        set_add( &ic->set, "nick", NULL, NULL, ic );
953        set_add( &ic->set, "room", NULL, NULL, ic );
[69b896b]954        set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
[5a75d15]955       
[1c40aa7]956        /* chat_type == groupchat */
957        ic->flags |= IRC_CHANNEL_TEMP;
958       
[5a75d15]959        return TRUE;
960}
961
962static char *set_eval_room_account( set_t *set, char *value )
963{
964        struct irc_channel *ic = set->data;
[03f3828]965        account_t *acc, *oa;
[5a75d15]966       
967        if( !( acc = account_get( ic->irc->b, value ) ) )
968                return SET_INVALID;
969        else if( !acc->prpl->chat_join )
970        {
971                irc_usermsg( ic->irc, "Named chatrooms not supported on that account." );
972                return SET_INVALID;
973        }
974       
[03f3828]975        if( set->value && ( oa = account_get( ic->irc->b, set->value ) ) &&
976            oa->prpl->chat_free_settings )
977                oa->prpl->chat_free_settings( oa, &ic->set );
978       
979        if( acc->prpl->chat_add_settings )
980                acc->prpl->chat_add_settings( acc, &ic->set );
981       
[e135cd09]982        return g_strdup( acc->tag );
[5a75d15]983}
984
[1c40aa7]985static char *set_eval_chat_type( set_t *set, char *value )
986{
987        struct irc_channel *ic = set->data;
988       
989        if( strcmp( value, "groupchat" ) == 0 )
990                ic->flags |= IRC_CHANNEL_TEMP;
991        else if( strcmp( value, "room" ) == 0 )
992                ic->flags &= ~IRC_CHANNEL_TEMP;
993        else
994                return NULL;
995       
996        return value;
997}
998
[5a75d15]999static gboolean bee_irc_channel_free( irc_channel_t *ic )
1000{
[b1af3e8]1001        struct groupchat *c = ic->data;
1002       
[5a75d15]1003        set_del( &ic->set, "account" );
1004        set_del( &ic->set, "chat_type" );
1005        set_del( &ic->set, "nick" );
1006        set_del( &ic->set, "room" );
[69b896b]1007        set_del( &ic->set, "translate_to_nicks" );
[5a75d15]1008       
[1c40aa7]1009        ic->flags &= ~IRC_CHANNEL_TEMP;
1010       
[b1af3e8]1011        /* That one still points at this channel. Don't. */
1012        if( c )
1013                c->ui_data = NULL;
1014       
[5a75d15]1015        return TRUE;
1016}
1017
1018const struct irc_channel_funcs irc_channel_im_chat_funcs = {
[a87754b]1019        bee_irc_channel_chat_privmsg,
[5a75d15]1020        bee_irc_channel_chat_join,
[bfb99ee]1021        bee_irc_channel_chat_part,
[9e27f18]1022        bee_irc_channel_chat_topic,
[66b9e36a]1023        bee_irc_channel_chat_invite,
[5a75d15]1024
1025        bee_irc_channel_init,
1026        bee_irc_channel_free,
[a87754b]1027};
1028
[aea8b68]1029
[e4816ea]1030/* IM->IRC: File transfers */
[17a6ee9]1031static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
1032{
1033        return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
1034}
1035
[a87754b]1036static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
[17a6ee9]1037{
1038        return dccs_recv_start( ft );
1039}
1040
[a87754b]1041static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
[17a6ee9]1042{
1043        return dcc_close( ft );
1044}
1045
[a87754b]1046static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
[17a6ee9]1047{
1048        dcc_file_transfer_t *df = file->priv;
1049
1050        if( file->bytes_transferred >= file->file_size )
1051                dcc_finish( file );
1052        else
1053                df->proto_finished = TRUE;
1054}
1055
[d860a8d]1056const struct bee_ui_funcs irc_ui_funcs = {
[5c7b45c]1057        bee_irc_imc_connected,
1058        bee_irc_imc_disconnected,
1059       
[81e04e1]1060        bee_irc_user_new,
[d860a8d]1061        bee_irc_user_free,
[1d39159]1062        bee_irc_user_fullname,
[6ef9065]1063        bee_irc_user_nick_hint,
[7e83e8e4]1064        bee_irc_user_group,
[d860a8d]1065        bee_irc_user_status,
[f012a9f]1066        bee_irc_user_msg,
[573dab0]1067        bee_irc_user_typing,
[d88c92a]1068        bee_irc_user_action_response,
[17a6ee9]1069       
[aea8b68]1070        bee_irc_chat_new,
1071        bee_irc_chat_free,
[27e2c66]1072        bee_irc_chat_log,
1073        bee_irc_chat_msg,
[aea8b68]1074        bee_irc_chat_add_user,
[b17ce85]1075        bee_irc_chat_remove_user,
[9e27f18]1076        bee_irc_chat_topic,
[d343eaa]1077        bee_irc_chat_name_hint,
[1aa74f55]1078        bee_irc_chat_invite,
[aea8b68]1079       
[17a6ee9]1080        bee_irc_ft_in_start,
1081        bee_irc_ft_out_start,
1082        bee_irc_ft_close,
1083        bee_irc_ft_finished,
[81e04e1]1084};
Note: See TracBrowser for help on using the repository browser.