Commit two patches from RH package maintainer (#956).
[bitlbee.git] / irc_im.c
blob44072720a6ebd7b4edc8bc06471da151595c310b
1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2002-2010 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* Some glue to put the IRC and the IM stuff together. */
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.
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.
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
26 #include "bitlbee.h"
27 #include "dcc.h"
29 /* IM->IRC callbacks: Simple IM/buddy-related stuff. */
31 static const struct irc_user_funcs irc_user_im_funcs;
33 static void bee_irc_imc_connected( struct im_connection *ic )
35 irc_t *irc = (irc_t*) ic->bee->ui_data;
37 irc_channel_auto_joins( irc, ic->acc );
40 static void bee_irc_imc_disconnected( struct im_connection *ic )
42 /* Maybe try to send /QUITs here instead of later on. */
45 static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu )
47 irc_user_t *iu;
48 irc_t *irc = (irc_t*) bee->ui_data;
49 char nick[MAX_NICK_LENGTH+1], *s;
51 memset( nick, 0, MAX_NICK_LENGTH + 1 );
52 strcpy( nick, nick_get( bu ) );
54 bu->ui_data = iu = irc_user_new( irc, nick );
55 iu->bu = bu;
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 );
62 if( ( s = strchr( bu->handle, '@' ) ) )
64 iu->host = g_strdup( s + 1 );
65 iu->user = g_strndup( bu->handle, s - bu->handle );
67 else
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 iu->host = g_strdup( bu->ic->acc->prpl->name );
76 while( ( s = strchr( iu->user, ' ' ) ) )
77 *s = '_';
79 if( bu->flags & BEE_USER_LOCAL )
81 char *s = set_getstr( &bee->set, "handle_unknown" );
83 if( strcmp( s, "add_private" ) == 0 )
84 iu->last_channel = NULL;
85 else if( strcmp( s, "add_channel" ) == 0 )
86 iu->last_channel = irc->default_channel;
89 iu->f = &irc_user_im_funcs;
91 return TRUE;
94 static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu )
96 return irc_user_free( bee->ui_data, (irc_user_t *) bu->ui_data );
99 static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old )
101 irc_t *irc = bee->ui_data;
102 irc_user_t *iu = bu->ui_data;
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;
110 if( ( bu->flags & BEE_USER_ONLINE ) != ( old->flags & BEE_USER_ONLINE ) )
112 if( bu->flags & BEE_USER_ONLINE )
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" );
118 else
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" );
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..." );
134 /* Reset this one since the info may have changed. */
135 iu->away_reply_timeout = 0;
137 bee_irc_channel_update( irc, NULL, iu );
139 return TRUE;
142 void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu )
144 GSList *l;
146 if( ic == NULL )
148 for( l = irc->channels; l; l = l->next )
150 ic = l->data;
151 /* TODO: Just add a type flag or so.. */
152 if( ic->f == irc->default_channel->f &&
153 ( ic->flags & IRC_CHANNEL_JOINED ) )
154 bee_irc_channel_update( irc, ic, iu );
156 return;
158 if( iu == NULL )
160 for( l = irc->users; l; l = l->next )
162 iu = l->data;
163 if( iu->bu )
164 bee_irc_channel_update( irc, ic, l->data );
166 return;
169 if( !irc_channel_wants_user( ic, iu ) )
171 irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
173 else
175 struct irc_control_channel *icc = ic->data;
176 int mode = 0;
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];
182 else
183 mode = icc->modes[2];
185 if( !mode )
186 irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL );
187 else
189 irc_channel_add_user( ic, iu );
190 irc_channel_user_set_mode( ic, iu, mode );
195 static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg_, time_t sent_at )
197 irc_t *irc = bee->ui_data;
198 irc_user_t *iu = (irc_user_t *) bu->ui_data;
199 const char *dst;
200 char *prefix = NULL;
201 char *wrapped, *ts = NULL;
202 char *msg = g_strdup( msg_ );
203 GSList *l;
205 if( sent_at > 0 && set_getbool( &irc->b->set, "display_timestamps" ) )
206 ts = irc_format_timestamp( irc, sent_at );
208 dst = irc_user_msgdest( iu );
209 if( dst != irc->user->nick )
211 /* if not messaging directly, call user by name */
212 prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ? : "" );
214 else
216 prefix = ts;
217 ts = NULL; /* don't double-free */
220 for( l = irc_plugins; l; l = l->next )
222 irc_plugin_t *p = l->data;
223 if( p->filter_msg_in )
225 char *s = p->filter_msg_in( iu, msg, 0 );
226 if( s )
228 if( s != msg )
229 g_free( msg );
230 msg = s;
232 else
234 /* Modules can swallow messages. */
235 return TRUE;
240 if( ( g_strcasecmp( set_getstr( &bee->set, "strip_html" ), "always" ) == 0 ) ||
241 ( ( bu->ic->flags & OPT_DOES_HTML ) && set_getbool( &bee->set, "strip_html" ) ) )
243 char *s = g_strdup( msg );
244 strip_html( s );
245 g_free( msg );
246 msg = s;
249 wrapped = word_wrap( msg, 425 );
250 irc_send_msg( iu, "PRIVMSG", dst, wrapped, prefix );
252 g_free( wrapped );
253 g_free( prefix );
254 g_free( msg );
255 g_free( ts );
257 return TRUE;
260 static gboolean bee_irc_user_typing( bee_t *bee, bee_user_t *bu, uint32_t flags )
262 irc_t *irc = (irc_t *) bee->ui_data;
264 if( set_getbool( &bee->set, "typing_notice" ) )
265 irc_send_msg_f( (irc_user_t *) bu->ui_data, "PRIVMSG", irc->user->nick,
266 "\001TYPING %d\001", ( flags >> 8 ) & 3 );
267 else
268 return FALSE;
270 return TRUE;
273 static gboolean bee_irc_user_action_response( bee_t *bee, bee_user_t *bu, const char *action, char * const args[], void *data )
275 irc_t *irc = (irc_t *) bee->ui_data;
276 GString *msg = g_string_new( "\001" );
278 g_string_append( msg, action );
279 while( *args )
281 if( strchr( *args, ' ' ) )
282 g_string_append_printf( msg, " \"%s\"", *args );
283 else
284 g_string_append_printf( msg, " %s", *args );
285 args ++;
287 g_string_append_c( msg, '\001' );
289 irc_send_msg( (irc_user_t *) bu->ui_data, "NOTICE", irc->user->nick, msg->str, NULL );
291 return TRUE;
294 static gboolean bee_irc_user_nick_update( irc_user_t *iu );
296 static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu )
298 irc_user_t *iu = (irc_user_t *) bu->ui_data;
299 char *s;
301 if( iu->fullname != iu->nick )
302 g_free( iu->fullname );
303 iu->fullname = g_strdup( bu->fullname );
305 /* Strip newlines (unlikely, but IRC-unfriendly so they must go)
306 TODO(wilmer): Do the same with away msgs again! */
307 for( s = iu->fullname; *s; s ++ )
308 if( isspace( *s ) ) *s = ' ';
310 if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) )
312 /* People don't like this /NOTICE. Meh, let's go back to the old one.
313 char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
314 irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
316 imcb_log( bu->ic, "User `%s' changed name to `%s'", iu->nick, iu->fullname );
319 bee_irc_user_nick_update( iu );
321 return TRUE;
324 static gboolean bee_irc_user_nick_hint( bee_t *bee, bee_user_t *bu, const char *hint )
326 bee_irc_user_nick_update( (irc_user_t*) bu->ui_data );
328 return TRUE;
331 static gboolean bee_irc_user_group( bee_t *bee, bee_user_t *bu )
333 irc_user_t *iu = (irc_user_t *) bu->ui_data;
334 irc_t *irc = (irc_t *) bee->ui_data;
335 bee_user_flags_t online;
337 /* Take the user offline temporarily so we can change the nick (if necessary). */
338 if( ( online = bu->flags & BEE_USER_ONLINE ) )
339 bu->flags &= ~BEE_USER_ONLINE;
341 bee_irc_channel_update( irc, NULL, iu );
342 bee_irc_user_nick_update( iu );
344 if( online )
346 bu->flags |= online;
347 bee_irc_channel_update( irc, NULL, iu );
350 return TRUE;
353 static gboolean bee_irc_user_nick_update( irc_user_t *iu )
355 bee_user_t *bu = iu->bu;
356 char *newnick;
358 if( bu->flags & BEE_USER_ONLINE )
359 /* Ignore if the user is visible already. */
360 return TRUE;
362 if( nick_saved( bu ) )
363 /* The user already assigned a nickname to this person. */
364 return TRUE;
366 newnick = nick_get( bu );
368 if( strcmp( iu->nick, newnick ) != 0 )
370 nick_dedupe( bu, newnick );
371 irc_user_set_nick( iu, newnick );
374 return TRUE;
377 void bee_irc_user_nick_reset( irc_user_t *iu )
379 bee_user_t *bu = iu->bu;
380 bee_user_flags_t online;
382 if( bu == FALSE )
383 return;
385 /* In this case, pretend the user is offline. */
386 if( ( online = bu->flags & BEE_USER_ONLINE ) )
387 bu->flags &= ~BEE_USER_ONLINE;
389 nick_del( bu );
390 bee_irc_user_nick_update( iu );
392 bu->flags |= online;
395 /* IRC->IM calls */
397 static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
399 static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg )
401 const char *away;
403 if( iu->bu == NULL )
404 return FALSE;
406 if( ( away = irc_user_get_away( iu ) ) &&
407 time( NULL ) >= iu->away_reply_timeout )
409 irc_send_num( iu->irc, 301, "%s :%s", iu->nick, away );
410 iu->away_reply_timeout = time( NULL ) +
411 set_getint( &iu->irc->b->set, "away_reply_timeout" );
414 if( iu->pastebuf == NULL )
415 iu->pastebuf = g_string_new( msg );
416 else
418 b_event_remove( iu->pastebuf_timer );
419 g_string_append_printf( iu->pastebuf, "\n%s", msg );
422 if( set_getbool( &iu->irc->b->set, "paste_buffer" ) )
424 int delay;
426 if( ( delay = set_getint( &iu->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
427 delay *= 1000;
429 iu->pastebuf_timer = b_timeout_add( delay, bee_irc_user_privmsg_cb, iu );
431 return TRUE;
433 else
435 bee_irc_user_privmsg_cb( iu, 0, 0 );
437 return TRUE;
441 static gboolean bee_irc_user_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
443 irc_user_t *iu = data;
444 char *msg;
445 GSList *l;
447 msg = g_string_free( iu->pastebuf, FALSE );
448 iu->pastebuf = NULL;
449 iu->pastebuf_timer = 0;
451 for( l = irc_plugins; l; l = l->next )
453 irc_plugin_t *p = l->data;
454 if( p->filter_msg_out )
456 char *s = p->filter_msg_out( iu, msg, 0 );
457 if( s )
459 if( s != msg )
460 g_free( msg );
461 msg = s;
463 else
465 /* Modules can swallow messages. */
466 iu->pastebuf = NULL;
467 g_free( msg );
468 return FALSE;
473 bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
475 g_free( msg );
477 return FALSE;
480 static gboolean bee_irc_user_ctcp( irc_user_t *iu, char *const *ctcp )
482 if( ctcp[1] && g_strcasecmp( ctcp[0], "DCC" ) == 0
483 && g_strcasecmp( ctcp[1], "SEND" ) == 0 )
485 if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
487 file_transfer_t *ft = dcc_request( iu->bu->ic, ctcp );
488 if ( ft )
489 iu->bu->ic->acc->prpl->transfer_request( iu->bu->ic, ft, iu->bu->handle );
491 return TRUE;
494 else if( g_strcasecmp( ctcp[0], "TYPING" ) == 0 )
496 if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1] )
498 int st = ctcp[1][0];
499 if( st >= '0' && st <= '2' )
501 st <<= 8;
502 iu->bu->ic->acc->prpl->send_typing( iu->bu->ic, iu->bu->handle, st );
505 return TRUE;
508 else if( g_strcasecmp( ctcp[0], "HELP" ) == 0 && iu->bu )
510 GString *supp = g_string_new( "Supported CTCPs:" );
511 GList *l;
513 if( iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request )
514 g_string_append( supp, " DCC SEND," );
515 if( iu->bu->ic && iu->bu->ic->acc->prpl->send_typing )
516 g_string_append( supp, " TYPING," );
517 if( iu->bu->ic->acc->prpl->buddy_action_list )
518 for( l = iu->bu->ic->acc->prpl->buddy_action_list( iu->bu ); l; l = l->next )
520 struct buddy_action *ba = l->data;
521 g_string_append_printf( supp, " %s (%s),",
522 ba->name, ba->description );
524 g_string_truncate( supp, supp->len - 1 );
525 irc_send_msg_f( iu, "NOTICE", iu->irc->user->nick, "\001HELP %s\001", supp->str );
526 g_string_free( supp, TRUE );
528 else if( iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->buddy_action )
530 iu->bu->ic->acc->prpl->buddy_action( iu->bu, ctcp[0], ctcp + 1, NULL );
533 return FALSE;
536 static const struct irc_user_funcs irc_user_im_funcs = {
537 bee_irc_user_privmsg,
538 bee_irc_user_ctcp,
542 /* IM->IRC: Groupchats */
543 const struct irc_channel_funcs irc_channel_im_chat_funcs;
545 static gboolean bee_irc_chat_new( bee_t *bee, struct groupchat *c )
547 irc_t *irc = bee->ui_data;
548 irc_channel_t *ic;
549 char *topic;
550 GSList *l;
551 int i;
553 /* Try to find a channel that expects to receive a groupchat.
554 This flag is set earlier in our current call trace. */
555 for( l = irc->channels; l; l = l->next )
557 ic = l->data;
558 if( ic->flags & IRC_CHANNEL_CHAT_PICKME )
559 break;
562 /* If we found none, just generate some stupid name. */
563 if( l == NULL ) for( i = 0; i <= 999; i ++ )
565 char name[16];
566 sprintf( name, "#chat_%03d", i );
567 if( ( ic = irc_channel_new( irc, name ) ) )
568 break;
571 if( ic == NULL )
572 return FALSE;
574 c->ui_data = ic;
575 ic->data = c;
577 topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
578 irc_channel_set_topic( ic, topic, irc->root );
579 g_free( topic );
581 return TRUE;
584 static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
586 irc_channel_t *ic = c->ui_data;
588 if( ic == NULL )
589 return FALSE;
591 if( ic->flags & IRC_CHANNEL_JOINED )
592 irc_channel_printf( ic, "Cleaning up channel, bye!" );
594 ic->data = NULL;
595 c->ui_data = NULL;
596 irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
598 return TRUE;
601 static gboolean bee_irc_chat_log( bee_t *bee, struct groupchat *c, const char *text )
603 irc_channel_t *ic = c->ui_data;
605 if( ic == NULL )
606 return FALSE;
608 irc_channel_printf( ic, "%s", text );
610 return TRUE;
613 static gboolean bee_irc_chat_msg( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at )
615 irc_t *irc = bee->ui_data;
616 irc_user_t *iu = bu->ui_data;
617 irc_channel_t *ic = c->ui_data;
618 char *ts = NULL;
620 if( ic == NULL )
621 return FALSE;
623 if( sent_at > 0 && set_getbool( &bee->set, "display_timestamps" ) )
624 ts = irc_format_timestamp( irc, sent_at );
626 irc_send_msg( iu, "PRIVMSG", ic->name, msg, ts );
627 g_free( ts );
629 return TRUE;
632 static gboolean bee_irc_chat_add_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
634 irc_t *irc = bee->ui_data;
635 irc_channel_t *ic = c->ui_data;
637 if( ic == NULL )
638 return FALSE;
640 irc_channel_add_user( ic, bu == bee->user ? irc->user : bu->ui_data );
642 return TRUE;
645 static gboolean bee_irc_chat_remove_user( bee_t *bee, struct groupchat *c, bee_user_t *bu )
647 irc_t *irc = bee->ui_data;
648 irc_channel_t *ic = c->ui_data;
650 if( ic == NULL || bu == NULL )
651 return FALSE;
653 /* TODO: Possible bug here: If a module removes $user here instead of just
654 using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
655 a broken state around here. */
656 irc_channel_del_user( ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL );
658 return TRUE;
661 static gboolean bee_irc_chat_topic( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu )
663 irc_channel_t *ic = c->ui_data;
664 irc_t *irc = bee->ui_data;
665 irc_user_t *iu;
667 if( ic == NULL )
668 return FALSE;
670 if( bu == NULL )
671 iu = irc->root;
672 else if( bu == bee->user )
673 iu = irc->user;
674 else
675 iu = bu->ui_data;
677 irc_channel_set_topic( ic, new, iu );
679 return TRUE;
682 static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const char *name )
684 irc_t *irc = bee->ui_data;
685 irc_channel_t *ic = c->ui_data, *oic;
686 char stripped[MAX_NICK_LENGTH+1], *full_name;
688 if( ic == NULL )
689 return FALSE;
691 /* Don't rename a channel if the user's in it already. */
692 if( ic->flags & IRC_CHANNEL_JOINED )
693 return FALSE;
695 strncpy( stripped, name, MAX_NICK_LENGTH );
696 stripped[MAX_NICK_LENGTH] = '\0';
697 irc_channel_name_strip( stripped );
698 if( set_getbool( &bee->set, "lcnicks" ) )
699 nick_lc( stripped );
701 if( stripped[0] == '\0' )
702 return FALSE;
704 full_name = g_strdup_printf( "#%s", stripped );
705 if( ( oic = irc_channel_by_name( irc, full_name ) ) )
707 char *type, *chat_type;
709 type = set_getstr( &oic->set, "type" );
710 chat_type = set_getstr( &oic->set, "chat_type" );
712 if( type && chat_type && oic->data == FALSE &&
713 strcmp( type, "chat" ) == 0 &&
714 strcmp( chat_type, "groupchat" ) == 0 )
716 /* There's a channel with this name already, but it looks
717 like it's not in use yet. Most likely the IRC client
718 rejoined the channel after a reconnect. Remove it so
719 we can reuse its name. */
720 irc_channel_free( oic );
722 else
724 g_free( full_name );
725 return FALSE;
729 g_free( ic->name );
730 ic->name = full_name;
732 return TRUE;
735 static gboolean bee_irc_chat_invite( bee_t *bee, bee_user_t *bu, const char *name, const char *msg )
737 char *channel, *s;
738 irc_t *irc = bee->ui_data;
739 irc_user_t *iu = bu->ui_data;
740 irc_channel_t *chan;
742 if( strchr( CTYPES, name[0] ) )
743 channel = g_strdup( name );
744 else
745 channel = g_strdup_printf( "#%s", name );
747 if( ( s = strchr( channel, '@' ) ) )
748 *s = '\0';
750 if( strlen( channel ) > MAX_NICK_LENGTH )
752 /* If the channel name is very long (like those insane GTalk
753 UUID names), try if we can use the inviter's nick. */
754 s = g_strdup_printf( "#%s", iu->nick );
755 if( irc_channel_by_name( irc, s ) == NULL )
757 g_free( channel );
758 channel = s;
762 if( ( chan = irc_channel_new( irc, channel ) ) &&
763 set_setstr( &chan->set, "type", "chat" ) &&
764 set_setstr( &chan->set, "chat_type", "room" ) &&
765 set_setstr( &chan->set, "account", bu->ic->acc->tag ) &&
766 set_setstr( &chan->set, "room", (char*) name ) )
768 /* I'm assuming that if the user didn't "chat add" the room
769 himself but got invited, it's temporary, so make this a
770 temporary mapping that is removed as soon as we /PART. */
771 chan->flags |= IRC_CHANNEL_TEMP;
773 else
775 irc_channel_free( chan );
776 chan = NULL;
778 g_free( channel );
780 irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name );
781 if( msg )
782 irc_send_msg( iu, "PRIVMSG", irc->user->nick, msg, NULL );
783 if( chan )
785 irc_send_msg_f( iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name );
786 irc_send_invite( iu, chan );
789 return TRUE;
792 /* IRC->IM */
793 static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond );
795 static gboolean bee_irc_channel_chat_privmsg( irc_channel_t *ic, const char *msg )
797 struct groupchat *c = ic->data;
798 char *trans = NULL, *s;
800 if( c == NULL )
801 return FALSE;
803 if( set_getbool( &ic->set, "translate_to_nicks" ) )
805 char nick[MAX_NICK_LENGTH+1];
806 irc_user_t *iu;
808 strncpy( nick, msg, MAX_NICK_LENGTH );
809 nick[MAX_NICK_LENGTH] = '\0';
810 if( ( s = strchr( nick, ':' ) ) || ( s = strchr( nick, ',' ) ) )
812 *s = '\0';
813 if( ( iu = irc_user_by_name( ic->irc, nick ) ) && iu->bu &&
814 iu->bu->nick && irc_channel_has_user( ic, iu ) )
816 trans = g_strconcat( iu->bu->nick, msg + ( s - nick ), NULL );
817 msg = trans;
822 if( set_getbool( &ic->irc->b->set, "paste_buffer" ) )
824 int delay;
826 if( ic->pastebuf == NULL )
827 ic->pastebuf = g_string_new( msg );
828 else
830 b_event_remove( ic->pastebuf_timer );
831 g_string_append_printf( ic->pastebuf, "\n%s", msg );
834 if( ( delay = set_getint( &ic->irc->b->set, "paste_buffer_delay" ) ) <= 5 )
835 delay *= 1000;
837 ic->pastebuf_timer = b_timeout_add( delay, bee_irc_channel_chat_privmsg_cb, ic );
839 g_free( trans );
840 return TRUE;
842 else
843 bee_chat_msg( ic->irc->b, c, msg, 0 );
845 g_free( trans );
846 return TRUE;
849 static gboolean bee_irc_channel_chat_privmsg_cb( gpointer data, gint fd, b_input_condition cond )
851 irc_channel_t *ic = data;
853 if( ic->data )
854 bee_chat_msg( ic->irc->b, ic->data, ic->pastebuf->str, 0 );
856 g_string_free( ic->pastebuf, TRUE );
857 ic->pastebuf = 0;
858 ic->pastebuf_timer = 0;
860 return FALSE;
863 static gboolean bee_irc_channel_chat_join( irc_channel_t *ic )
865 char *acc_s, *room;
866 account_t *acc;
868 if( strcmp( set_getstr( &ic->set, "chat_type" ), "room" ) != 0 )
869 return TRUE;
871 if( ( acc_s = set_getstr( &ic->set, "account" ) ) &&
872 ( room = set_getstr( &ic->set, "room" ) ) &&
873 ( acc = account_get( ic->irc->b, acc_s ) ) &&
874 acc->ic && acc->prpl->chat_join )
876 char *nick;
878 if( !( nick = set_getstr( &ic->set, "nick" ) ) )
879 nick = ic->irc->user->nick;
881 ic->flags |= IRC_CHANNEL_CHAT_PICKME;
882 acc->prpl->chat_join( acc->ic, room, nick, NULL, &ic->set );
883 ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
885 return FALSE;
887 else
889 irc_send_num( ic->irc, 403, "%s :Can't join channel, account offline?", ic->name );
890 return FALSE;
894 static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
896 struct groupchat *c = ic->data;
898 if( c && c->ic->acc->prpl->chat_leave )
899 c->ic->acc->prpl->chat_leave( c );
901 /* Remove references in both directions now. We don't need each other anymore. */
902 ic->data = NULL;
903 if( c )
904 c->ui_data = NULL;
906 return TRUE;
909 static gboolean bee_irc_channel_chat_topic( irc_channel_t *ic, const char *new )
911 struct groupchat *c = ic->data;
913 if( c == NULL )
914 return FALSE;
916 if( c->ic->acc->prpl->chat_topic == NULL )
917 irc_send_num( ic->irc, 482, "%s :IM network does not support channel topics", ic->name );
918 else
920 /* TODO: Need more const goodness here, sigh */
921 char *topic = g_strdup( new );
922 c->ic->acc->prpl->chat_topic( c, topic );
923 g_free( topic );
926 /* Whatever happened, the IM module should ack the topic change. */
927 return FALSE;
930 static gboolean bee_irc_channel_chat_invite( irc_channel_t *ic, irc_user_t *iu )
932 struct groupchat *c = ic->data;
933 bee_user_t *bu = iu->bu;
935 if( bu == NULL )
936 return FALSE;
938 if( c )
940 if( iu->bu->ic != c->ic )
941 irc_send_num( ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name );
942 else if( c->ic->acc->prpl->chat_invite )
943 c->ic->acc->prpl->chat_invite( c, iu->bu->handle, NULL );
944 else
945 irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
947 else if( bu->ic->acc->prpl->chat_with &&
948 strcmp( set_getstr( &ic->set, "chat_type" ), "groupchat" ) == 0 )
950 ic->flags |= IRC_CHANNEL_CHAT_PICKME;
951 iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
952 ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
954 else
956 irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
959 return TRUE;
962 static char *set_eval_room_account( set_t *set, char *value );
963 static char *set_eval_chat_type( set_t *set, char *value );
965 static gboolean bee_irc_channel_init( irc_channel_t *ic )
967 set_t *s;
969 set_add( &ic->set, "account", NULL, set_eval_room_account, ic );
970 set_add( &ic->set, "chat_type", "groupchat", set_eval_chat_type, ic );
972 s = set_add( &ic->set, "nick", NULL, NULL, ic );
973 s->flags |= SET_NULL_OK;
975 set_add( &ic->set, "room", NULL, NULL, ic );
976 set_add( &ic->set, "translate_to_nicks", "true", set_eval_bool, ic );
978 /* chat_type == groupchat */
979 ic->flags |= IRC_CHANNEL_TEMP;
981 return TRUE;
984 static char *set_eval_room_account( set_t *set, char *value )
986 struct irc_channel *ic = set->data;
987 account_t *acc, *oa;
989 if( !( acc = account_get( ic->irc->b, value ) ) )
990 return SET_INVALID;
991 else if( !acc->prpl->chat_join )
993 irc_rootmsg( ic->irc, "Named chatrooms not supported on that account." );
994 return SET_INVALID;
997 if( set->value && ( oa = account_get( ic->irc->b, set->value ) ) &&
998 oa->prpl->chat_free_settings )
999 oa->prpl->chat_free_settings( oa, &ic->set );
1001 if( acc->prpl->chat_add_settings )
1002 acc->prpl->chat_add_settings( acc, &ic->set );
1004 return g_strdup( acc->tag );
1007 static char *set_eval_chat_type( set_t *set, char *value )
1009 struct irc_channel *ic = set->data;
1011 if( strcmp( value, "groupchat" ) == 0 )
1012 ic->flags |= IRC_CHANNEL_TEMP;
1013 else if( strcmp( value, "room" ) == 0 )
1014 ic->flags &= ~IRC_CHANNEL_TEMP;
1015 else
1016 return NULL;
1018 return value;
1021 static gboolean bee_irc_channel_free( irc_channel_t *ic )
1023 struct groupchat *c = ic->data;
1025 set_del( &ic->set, "account" );
1026 set_del( &ic->set, "chat_type" );
1027 set_del( &ic->set, "nick" );
1028 set_del( &ic->set, "room" );
1029 set_del( &ic->set, "translate_to_nicks" );
1031 ic->flags &= ~IRC_CHANNEL_TEMP;
1033 /* That one still points at this channel. Don't. */
1034 if( c )
1035 c->ui_data = NULL;
1037 return TRUE;
1040 const struct irc_channel_funcs irc_channel_im_chat_funcs = {
1041 bee_irc_channel_chat_privmsg,
1042 bee_irc_channel_chat_join,
1043 bee_irc_channel_chat_part,
1044 bee_irc_channel_chat_topic,
1045 bee_irc_channel_chat_invite,
1047 bee_irc_channel_init,
1048 bee_irc_channel_free,
1052 /* IM->IRC: File transfers */
1053 static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
1055 return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
1058 static gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
1060 return dccs_recv_start( ft );
1063 static void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
1065 return dcc_close( ft );
1068 static void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
1070 dcc_file_transfer_t *df = file->priv;
1072 if( file->bytes_transferred >= file->file_size )
1073 dcc_finish( file );
1074 else
1075 df->proto_finished = TRUE;
1078 const struct bee_ui_funcs irc_ui_funcs = {
1079 bee_irc_imc_connected,
1080 bee_irc_imc_disconnected,
1082 bee_irc_user_new,
1083 bee_irc_user_free,
1084 bee_irc_user_fullname,
1085 bee_irc_user_nick_hint,
1086 bee_irc_user_group,
1087 bee_irc_user_status,
1088 bee_irc_user_msg,
1089 bee_irc_user_typing,
1090 bee_irc_user_action_response,
1092 bee_irc_chat_new,
1093 bee_irc_chat_free,
1094 bee_irc_chat_log,
1095 bee_irc_chat_msg,
1096 bee_irc_chat_add_user,
1097 bee_irc_chat_remove_user,
1098 bee_irc_chat_topic,
1099 bee_irc_chat_name_hint,
1100 bee_irc_chat_invite,
1102 bee_irc_ft_in_start,
1103 bee_irc_ft_out_start,
1104 bee_irc_ft_close,
1105 bee_irc_ft_finished,