source: irc_im.c @ 3963fdd

Last change on this file since 3963fdd was 51a3d12, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-25T09:11:48Z

Allow change nicknames when moving contacts between groups. Also fixing a
bug that caused many empty channel mode changes being sent.

  • Property mode set to 100644
File size: 22.3 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        struct irc_control_channel *icc;
144        GSList *l;
[94d5da9c]145        gboolean match = FALSE;
[13c1a9f]146       
147        if( ic == NULL )
148        {
149                for( l = irc->channels; l; l = l->next )
150                {
151                        ic = l->data;
152                        /* TODO: Just add a type flag or so.. */
[9052bc1]153                        if( ic->f == irc->default_channel->f &&
154                            ( ic->flags & IRC_CHANNEL_JOINED ) )
[13c1a9f]155                                bee_irc_channel_update( irc, ic, iu );
156                }
157                return;
158        }
159        if( iu == NULL )
160        {
161                for( l = irc->users; l; l = l->next )
162                {
163                        iu = l->data;
164                        if( iu->bu )
165                                bee_irc_channel_update( irc, ic, l->data );
166                }
167                return;
168        }
169       
170        icc = ic->data;
171       
[94d5da9c]172        if( icc->type == IRC_CC_TYPE_DEFAULT )
173                match = TRUE;
[13c1a9f]174        else if( icc->type == IRC_CC_TYPE_GROUP )
[94d5da9c]175                match = iu->bu->group == icc->group;
[a067771]176        else if( icc->type == IRC_CC_TYPE_ACCOUNT )
[94d5da9c]177                match = iu->bu->ic->acc == icc->account;
[7a6ba50]178        else if( icc->type == IRC_CC_TYPE_PROTOCOL )
[94d5da9c]179                match = iu->bu->ic->acc->prpl == icc->protocol;
[13c1a9f]180       
[94d5da9c]181        if( !match )
[13c1a9f]182        {
[006a84f]183                irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
[13c1a9f]184        }
185        else
186        {
[94d5da9c]187                int mode = 0;
[13c1a9f]188               
[94d5da9c]189                if( !( iu->bu->flags & BEE_USER_ONLINE ) )
190                        mode = icc->modes[0];
191                else if( iu->bu->flags & BEE_USER_AWAY )
192                        mode = icc->modes[1];
[0e8b3e8]193                else
[94d5da9c]194                        mode = icc->modes[2];
195               
196                if( !mode )
197                        irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
198                else
199                {
200                        irc_channel_add_user( ic, iu );
201                        irc_channel_user_set_mode( ic, iu, mode );
202                }
[13c1a9f]203        }
204}
205
[f012a9f]206static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at )
207{
208        irc_t *irc = bee->ui_data;
209        irc_user_t *iu = (irc_user_t *) bu->ui_data;
210        char *dst, *prefix = NULL;
[21c87a7]211        char *wrapped, *ts = NULL;
212       
213        if( sent_at > 0 && set_getbool( &irc->b->set, "display_timestamps" ) )
214                ts = irc_format_timestamp( irc, sent_at );
[f012a9f]215       
[92c8d41]216        if( iu->last_channel )
[f012a9f]217        {
[92c8d41]218                dst = iu->last_channel->name;
219                prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ? : "" );
[f012a9f]220        }
221        else
222        {
[92c8d41]223                dst = irc->user->nick;
224                prefix = ts;
225                ts = NULL;
[f012a9f]226        }
227       
228        wrapped = word_wrap( msg, 425 );
229        irc_send_msg( iu, "PRIVMSG", dst, wrapped, prefix );
230       
231        g_free( wrapped );
232        g_free( prefix );
[21c87a7]233        g_free( ts );
[f012a9f]234       
235        return TRUE;
236}
237
[573dab0]238static gboolean bee_irc_user_typing( bee_t *bee, bee_user_t *bu, uint32_t flags )
239{
240        irc_t *irc = (irc_t *) bee->ui_data;
241       
242        if( set_getbool( &bee->set, "typing_notice" ) )
243                irc_send_msg_f( (irc_user_t *) bu->ui_data, "PRIVMSG", irc->user->nick,
244                                "\001TYPING %d\001", ( flags >> 8 ) & 3 );
245        else
246                return FALSE;
247       
248        return TRUE;
249}
250
[badd148]251static gboolean bee_irc_user_nick_update( irc_user_t *iu );
[6ef9065]252
[1d39159]253static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu )
254{
255        irc_user_t *iu = (irc_user_t *) bu->ui_data;
256        irc_t *irc = (irc_t *) bee->ui_data;
257        char *s;
258       
259        if( iu->fullname != iu->nick )
260                g_free( iu->fullname );
261        iu->fullname = g_strdup( bu->fullname );
262       
263        /* Strip newlines (unlikely, but IRC-unfriendly so they must go)
264           TODO(wilmer): Do the same with away msgs again! */
265        for( s = iu->fullname; *s; s ++ )
266                if( isspace( *s ) ) *s = ' ';
267       
268        if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) )
269        {
270                char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
271                irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
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       
532        if( ic == NULL )
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
[a87754b]617/* IRC->IM */
[619dd18]618static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
619
[a87754b]620static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
621{
622        struct groupchat *c = ic->data;
[69b896b]623        char *trans = NULL, *s;
[a87754b]624       
[5a75d15]625        if( c == NULL )
626                return FALSE;
[69b896b]627       
628        if( set_getbool( &ic->set, "translate_to_nicks" ) )
629        {
630                char nick[MAX_NICK_LENGTH+1];
631                irc_user_t *iu;
632               
633                strncpy( nick, msg, MAX_NICK_LENGTH );
634                nick[MAX_NICK_LENGTH] = '\0';
635                if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) )
636                {
637                        *s = '\0';
638                        if( ( iu = irc_user_by_name( ic->irc, nick ) ) &&
639                            iu->bu->nick && irc_channel_has_user( ic, iu ) )
640                        {
641                                trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL );
642                                msg = trans;
643                        }
644                }
645        }
646       
647        if( set_getbool( &ic->irc->b->set, "paste_buffer" ) )
[619dd18]648        {
649                int delay;
650               
651                if( ic->pastebuf == NULL )
652                        ic->pastebuf = g_string_new( msg );
653                else
654                {
655                        b_event_remove( ic->pastebuf_timer );
656                        g_string_append_printf( ic->pastebuf, "\n%s", msg );
657                }
658               
659                if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
660                        delay *= 1000;
661               
662                ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic );
663               
[69b896b]664                g_free( trans );
[619dd18]665                return TRUE;
666        }
667        else
668                bee_chat_msg( ic->irc->b, c, msg, 0 );
[a87754b]669       
[69b896b]670        g_free( trans );
[a87754b]671        return TRUE;
[5a75d15]672}
673
[619dd18]674static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
675{
676        irc_channel_t *ic = data;
677       
678        bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
679       
680        g_string_free( ic->pastebuf, TRUE );
681        ic->pastebuf = 0;
682        ic->pastebuf_timer = 0;
683       
684        return FALSE;
685}
686
[5a75d15]687static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
688{
689        char *acc_s, *room;
690        account_t *acc;
[a87754b]691       
[5a75d15]692        if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 )
693                return TRUE;
694       
695        if( ( acc_s = set_getstr( &ic->set, "account" ) ) &&
696            ( room = set_getstr( &ic->set, "room" ) ) &&
697            ( acc = account_get( ic->irc->b, acc_s ) ) &&
698            acc->ic && acc->prpl->chat_join )
699        {
700                char *nick;
701               
702                if( !( nick = set_getstr( &ic->set, "nick" ) ) )
703                        nick = ic->irc->user->nick;
704               
705                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
[03f3828]706                acc->prpl->chat_join( acc->ic, room, nick, NULL, &ic->set );
[5a75d15]707                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
708               
709                return FALSE;
710        }
711        else
712        {
713                irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name );
714                return FALSE;
715        }
[a87754b]716}
717
[bfb99ee]718static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
719{
720        struct groupchat *c = ic->data;
721       
[5a75d15]722        if( c && c->ic->acc->prpl->chat_leave )
[bfb99ee]723                c->ic->acc->prpl->chat_leave( c );
724       
725        return TRUE;
726}
727
[9e27f18]728static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
729{
[4469e7e]730        struct groupchat *c = ic->data;
[5a75d15]731       
732        if( c == NULL )
733                return FALSE;
[4469e7e]734       
735        if( c->ic->acc->prpl->chat_topic == NULL )
736                irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name );
737        else
738        {
[5a75d15]739                /* TODO: Need more const goodness here, sigh */
740                char *topic = g_strdup( new );
[4469e7e]741                c->ic->acc->prpl->chat_topic( c, topic );
[5a75d15]742                g_free( topic );
[4469e7e]743                return TRUE;
744        }
745               
746        return FALSE;
[9e27f18]747}
748
[66b9e36a]749static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
750{
751        struct groupchat *c = ic->data;
[5a75d15]752        bee_user_t *bu = iu->bu;
753       
754        if( bu == NULL )
755                return FALSE;
[66b9e36a]756       
[5a75d15]757        if( c )
758        {
759                if( iu->bu->ic != c->ic )
760                        irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name );
761                else if( c->ic->acc->prpl->chat_invite )
762                        c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL );
763                else
764                        irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
765        }
766        else if( bu->ic->acc->prpl->chat_with &&
767                 strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 )
768        {
769                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
770                iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
771                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
772        }
[66b9e36a]773        else
[5a75d15]774        {
[66b9e36a]775                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
[5a75d15]776        }
[66b9e36a]777       
778        return TRUE;
779}
780
[5a75d15]781static char *set_eval_room_account( set_t *set, char *value );
[1c40aa7]782static char *set_eval_chat_type( set_t *set, char *value );
[5a75d15]783
784static gboolean bee_irc_channel_init( irc_channel_t *ic )
785{
786        set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
[1c40aa7]787        set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
[5a75d15]788        set_add( &ic->set, "nick", NULL, NULL, ic );
789        set_add( &ic->set, "room", NULL, NULL, ic );
[69b896b]790        set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
[5a75d15]791       
[1c40aa7]792        /* chat_type == groupchat */
793        ic->flags |= IRC_CHANNEL_TEMP;
794       
[5a75d15]795        return TRUE;
796}
797
798static char *set_eval_room_account( set_t *set, char *value )
799{
800        struct irc_channel *ic = set->data;
[03f3828]801        account_t *acc, *oa;
[5a75d15]802       
803        if( !( acc = account_get( ic->irc->b, value ) ) )
804                return SET_INVALID;
805        else if( !acc->prpl->chat_join )
806        {
807                irc_usermsg( ic->irc, "Named chatrooms not supported on that account." );
808                return SET_INVALID;
809        }
810       
[03f3828]811        if( set->value && ( oa = account_get( ic->irc->b, set->value ) ) &&
812            oa->prpl->chat_free_settings )
813                oa->prpl->chat_free_settings( oa, &ic->set );
814       
815        if( acc->prpl->chat_add_settings )
816                acc->prpl->chat_add_settings( acc, &ic->set );
817       
[e135cd09]818        return g_strdup( acc->tag );
[5a75d15]819}
820
[1c40aa7]821static char *set_eval_chat_type( set_t *set, char *value )
822{
823        struct irc_channel *ic = set->data;
824       
825        if( strcmp( value, "groupchat" ) == 0 )
826                ic->flags |= IRC_CHANNEL_TEMP;
827        else if( strcmp( value, "room" ) == 0 )
828                ic->flags &= ~IRC_CHANNEL_TEMP;
829        else
830                return NULL;
831       
832        return value;
833}
834
[5a75d15]835static gboolean bee_irc_channel_free( irc_channel_t *ic )
836{
[b1af3e8]837        struct groupchat *c = ic->data;
838       
[5a75d15]839        set_del( &ic->set, "account" );
840        set_del( &ic->set, "chat_type" );
841        set_del( &ic->set, "nick" );
842        set_del( &ic->set, "room" );
[69b896b]843        set_del( &ic->set, "translate_to_nicks" );
[5a75d15]844       
[1c40aa7]845        ic->flags &= ~IRC_CHANNEL_TEMP;
846       
[b1af3e8]847        /* That one still points at this channel. Don't. */
848        if( c )
849                c->ui_data = NULL;
850       
[5a75d15]851        return TRUE;
852}
853
854const struct irc_channel_funcs irc_channel_im_chat_funcs = {
[a87754b]855        bee_irc_channel_chat_privmsg,
[5a75d15]856        bee_irc_channel_chat_join,
[bfb99ee]857        bee_irc_channel_chat_part,
[9e27f18]858        bee_irc_channel_chat_topic,
[66b9e36a]859        bee_irc_channel_chat_invite,
[5a75d15]860
861        bee_irc_channel_init,
862        bee_irc_channel_free,
[a87754b]863};
864
[aea8b68]865
[e4816ea]866/* IM->IRC: File transfers */
[17a6ee9]867static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
868{
869        return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
870}
871
[a87754b]872static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
[17a6ee9]873{
874        return dccs_recv_start( ft );
875}
876
[a87754b]877static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
[17a6ee9]878{
879        return dcc_close( ft );
880}
881
[a87754b]882static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
[17a6ee9]883{
884        dcc_file_transfer_t *df = file->priv;
885
886        if( file->bytes_transferred >= file->file_size )
887                dcc_finish( file );
888        else
889                df->proto_finished = TRUE;
890}
891
[d860a8d]892const struct bee_ui_funcs irc_ui_funcs = {
[5c7b45c]893        bee_irc_imc_connected,
894        bee_irc_imc_disconnected,
895       
[81e04e1]896        bee_irc_user_new,
[d860a8d]897        bee_irc_user_free,
[1d39159]898        bee_irc_user_fullname,
[6ef9065]899        bee_irc_user_nick_hint,
[7e83e8e4]900        bee_irc_user_group,
[d860a8d]901        bee_irc_user_status,
[f012a9f]902        bee_irc_user_msg,
[573dab0]903        bee_irc_user_typing,
[17a6ee9]904       
[aea8b68]905        bee_irc_chat_new,
906        bee_irc_chat_free,
[27e2c66]907        bee_irc_chat_log,
908        bee_irc_chat_msg,
[aea8b68]909        bee_irc_chat_add_user,
[b17ce85]910        bee_irc_chat_remove_user,
[9e27f18]911        bee_irc_chat_topic,
[d343eaa]912        bee_irc_chat_name_hint,
[aea8b68]913       
[17a6ee9]914        bee_irc_ft_in_start,
915        bee_irc_ft_out_start,
916        bee_irc_ft_close,
917        bee_irc_ft_finished,
[81e04e1]918};
Note: See TracBrowser for help on using the repository browser.