source: irc_im.c @ c347a12

Last change on this file since c347a12 was c92ee728, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-07-01T22:43:02Z

Use account tag instead of protocol/network name for generating hostmasks.

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