1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
4 * Copyright 2002-2010 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* The IRC-based UI - Representing (virtual) channels. */
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
28 static char *set_eval_channel_type( set_t
*set
, char *value
);
29 static gint
irc_channel_user_cmp( gconstpointer a_
, gconstpointer b_
);
30 static const struct irc_channel_funcs control_channel_funcs
;
32 extern const struct irc_channel_funcs irc_channel_im_chat_funcs
;
34 irc_channel_t
*irc_channel_new( irc_t
*irc
, const char *name
)
38 if( !irc_channel_name_ok( name
) || irc_channel_by_name( irc
, name
) )
41 ic
= g_new0( irc_channel_t
, 1 );
43 ic
->name
= g_strdup( name
);
44 strcpy( ic
->mode
, CMODE
);
46 irc_channel_add_user( ic
, irc
->root
);
48 irc
->channels
= g_slist_append( irc
->channels
, ic
);
50 set_add( &ic
->set
, "auto_join", "false", set_eval_bool
, ic
);
51 set_add( &ic
->set
, "type", "control", set_eval_channel_type
, ic
);
54 set_setstr( &ic
->set
, "type", "control" );
55 else /* if( name[0] == '#' ) */
56 set_setstr( &ic
->set
, "type", "chat" );
61 irc_channel_t
*irc_channel_by_name( irc_t
*irc
, const char *name
)
65 for( l
= irc
->channels
; l
; l
= l
->next
)
67 irc_channel_t
*ic
= l
->data
;
69 if( irc_channel_name_cmp( name
, ic
->name
) == 0 )
76 irc_channel_t
*irc_channel_get( irc_t
*irc
, char *id
)
78 irc_channel_t
*ic
, *ret
= NULL
;
82 if( sscanf( id
, "%d", &nr
) == 1 && nr
< 1000 )
84 for( l
= irc
->channels
; l
; l
= l
->next
)
94 /* Exact match first: Partial match only sucks if there's a channel
96 if( ( ret
= irc_channel_by_name( irc
, id
) ) )
99 for( l
= irc
->channels
; l
; l
= l
->next
)
103 if( strstr( ic
->name
, id
) )
105 /* Make sure it's a unique match. */
116 int irc_channel_free( irc_channel_t
*ic
)
125 if( ic
->flags
& IRC_CHANNEL_JOINED
)
126 irc_channel_del_user( ic
, irc
->user
, IRC_CDU_KICK
, "Cleaning up channel" );
132 set_del( &ic
->set
, ic
->set
->key
);
134 irc
->channels
= g_slist_remove( irc
->channels
, ic
);
137 g_free( ic
->users
->data
);
138 ic
->users
= g_slist_remove( ic
->users
, ic
->users
->data
);
141 for( l
= irc
->users
; l
; l
= l
->next
)
143 irc_user_t
*iu
= l
->data
;
145 if( iu
->last_channel
== ic
)
146 iu
->last_channel
= irc
->default_channel
;
149 if( ic
->pastebuf_timer
) b_event_remove( ic
->pastebuf_timer
);
153 g_free( ic
->topic_who
);
159 struct irc_channel_free_data
166 static gboolean
irc_channel_free_callback( gpointer data
, gint fd
, b_input_condition cond
)
168 struct irc_channel_free_data
*d
= data
;
170 if( g_slist_find( irc_connection_list
, d
->irc
) &&
171 irc_channel_by_name( d
->irc
, d
->name
) == d
->ic
&&
172 !( d
->ic
->flags
& IRC_CHANNEL_JOINED
) )
173 irc_channel_free( d
->ic
);
180 /* Free the channel, but via the event loop, so after finishing whatever event
181 we're currently handling. */
182 void irc_channel_free_soon( irc_channel_t
*ic
)
184 struct irc_channel_free_data
*d
= g_new0( struct irc_channel_free_data
, 1 );
188 d
->name
= g_strdup( ic
->name
);
190 b_timeout_add( 0, irc_channel_free_callback
, d
);
193 static char *set_eval_channel_type( set_t
*set
, char *value
)
195 struct irc_channel
*ic
= set
->data
;
196 const struct irc_channel_funcs
*new;
198 if( strcmp( value
, "control" ) == 0 )
199 new = &control_channel_funcs
;
200 else if( ic
!= ic
->irc
->default_channel
&& strcmp( value
, "chat" ) == 0 )
201 new = &irc_channel_im_chat_funcs
;
205 /* TODO: Return values. */
206 if( ic
->f
&& ic
->f
->_free
)
211 if( ic
->f
&& ic
->f
->_init
)
217 int irc_channel_add_user( irc_channel_t
*ic
, irc_user_t
*iu
)
219 irc_channel_user_t
*icu
;
221 if( irc_channel_has_user( ic
, iu
) )
224 icu
= g_new0( irc_channel_user_t
, 1 );
227 ic
->users
= g_slist_insert_sorted( ic
->users
, icu
, irc_channel_user_cmp
);
229 irc_channel_update_ops( ic
, set_getstr( &ic
->irc
->b
->set
, "ops" ) );
231 if( iu
== ic
->irc
->user
|| ic
->flags
& IRC_CHANNEL_JOINED
)
233 ic
->flags
|= IRC_CHANNEL_JOINED
;
234 irc_send_join( ic
, iu
);
240 int irc_channel_del_user( irc_channel_t
*ic
, irc_user_t
*iu
, irc_channel_del_user_type_t type
, const char *msg
)
242 irc_channel_user_t
*icu
;
244 if( !( icu
= irc_channel_has_user( ic
, iu
) ) )
247 ic
->users
= g_slist_remove( ic
->users
, icu
);
250 if( !( ic
->flags
& IRC_CHANNEL_JOINED
) || type
== IRC_CDU_SILENT
) {}
251 /* Do nothing. The caller should promise it won't screw
252 up state of the IRC client. :-) */
253 else if( type
== IRC_CDU_PART
)
254 irc_send_part( ic
, iu
, msg
);
255 else if( type
== IRC_CDU_KICK
)
256 irc_send_kick( ic
, iu
, ic
->irc
->root
, msg
);
258 if( iu
== ic
->irc
->user
)
260 ic
->flags
&= ~IRC_CHANNEL_JOINED
;
262 if( ic
->irc
->status
& USTATUS_SHUTDOWN
)
264 /* Don't do anything fancy when we're shutting down anyway. */
266 else if( ic
->flags
& IRC_CHANNEL_TEMP
)
268 irc_channel_free_soon( ic
);
272 /* Flush userlist now. The user won't see it anyway. */
275 g_free( ic
->users
->data
);
276 ic
->users
= g_slist_remove( ic
->users
, ic
->users
->data
);
278 irc_channel_add_user( ic
, ic
->irc
->root
);
285 irc_channel_user_t
*irc_channel_has_user( irc_channel_t
*ic
, irc_user_t
*iu
)
289 for( l
= ic
->users
; l
; l
= l
->next
)
291 irc_channel_user_t
*icu
= l
->data
;
300 /* Find a channel we're currently in, that currently has iu in it. */
301 struct irc_channel
*irc_channel_with_user( irc_t
*irc
, irc_user_t
*iu
)
305 for( l
= irc
->channels
; l
; l
= l
->next
)
307 irc_channel_t
*ic
= l
->data
;
309 if( strcmp( set_getstr( &ic
->set
, "type" ), "control" ) != 0 )
312 if( ( ic
->flags
& IRC_CHANNEL_JOINED
) &&
313 irc_channel_has_user( ic
, iu
) )
317 /* If there was no match, try once more but just see if the user
318 *would* be in the channel, i.e. if s/he were online. */
322 for( l
= irc
->channels
; l
; l
= l
->next
)
324 irc_channel_t
*ic
= l
->data
;
326 if( strcmp( set_getstr( &ic
->set
, "type" ), "control" ) != 0 )
329 if( ( ic
->flags
& IRC_CHANNEL_JOINED
) &&
330 irc_channel_wants_user( ic
, iu
) )
337 int irc_channel_set_topic( irc_channel_t
*ic
, const char *topic
, const irc_user_t
*iu
)
340 ic
->topic
= g_strdup( topic
);
342 g_free( ic
->topic_who
);
344 ic
->topic_who
= g_strdup_printf( "%s!%s@%s", iu
->nick
, iu
->user
, iu
->host
);
346 ic
->topic_who
= NULL
;
348 ic
->topic_time
= time( NULL
);
350 if( ic
->flags
& IRC_CHANNEL_JOINED
)
351 irc_send_topic( ic
, TRUE
);
356 void irc_channel_user_set_mode( irc_channel_t
*ic
, irc_user_t
*iu
, irc_channel_user_flags_t flags
)
358 irc_channel_user_t
*icu
= irc_channel_has_user( ic
, iu
);
360 if( !icu
|| icu
->flags
== flags
)
363 if( ic
->flags
& IRC_CHANNEL_JOINED
)
364 irc_send_channel_user_mode_diff( ic
, iu
, icu
->flags
, flags
);
369 void irc_channel_set_mode( irc_channel_t
*ic
, const char *s
)
371 irc_t
*irc
= ic
->irc
;
375 char changes
[512], *p
, st2
= 2;
377 memset( m
, 0, sizeof( m
) );
379 for( t
= ic
->mode
; *t
; t
++ )
380 if( *t
< sizeof( m
) )
384 for( t
= s
; *t
; t
++ )
386 if( *t
== '+' || *t
== '-' )
388 else if( strchr( CMODES
, *t
) )
390 if( m
[(int)*t
] != st
)
393 st2
= st
, *p
++ = st
? '+' : '-';
401 memset( ic
->mode
, 0, sizeof( ic
->mode
) );
403 for( i
= 'A'; i
<= 'z' && strlen( ic
->mode
) < ( sizeof( ic
->mode
) - 1 ); i
++ )
405 ic
->mode
[strlen(ic
->mode
)] = i
;
407 if( *changes
&& ( ic
->flags
& IRC_CHANNEL_JOINED
) )
408 irc_write( irc
, ":%s!%s@%s MODE %s :%s", irc
->root
->nick
,
409 irc
->root
->user
, irc
->root
->host
, ic
->name
,
413 void irc_channel_auto_joins( irc_t
*irc
, account_t
*acc
)
417 for( l
= irc
->channels
; l
; l
= l
->next
)
419 irc_channel_t
*ic
= l
->data
;
420 gboolean aj
= set_getbool( &ic
->set
, "auto_join" );
424 ( type
= set_getstr( &ic
->set
, "chat_type" ) ) &&
425 strcmp( type
, "room" ) == 0 )
427 /* Bit of an ugly special case: Handle chatrooms here, we
428 can only auto-join them if their account is online. */
431 if( !aj
&& !( ic
->flags
& IRC_CHANNEL_JOINED
) )
432 /* Only continue if this one's marked as auto_join
433 or if we're in it already. (Possible if the
434 client auto-rejoined it before identyfing.) */
436 else if( !( acc_s
= set_getstr( &ic
->set
, "account" ) ) )
438 else if( account_get( irc
->b
, acc_s
) != acc
)
440 else if( acc
->ic
== NULL
|| !( acc
->ic
->flags
& OPT_LOGGED_IN
) )
447 irc_channel_add_user( ic
, irc
->user
);
452 void irc_channel_printf( irc_channel_t
*ic
, char *format
, ... )
457 va_start( params
, format
);
458 text
= g_strdup_vprintf( format
, params
);
461 irc_send_msg( ic
->irc
->root
, "PRIVMSG", ic
->name
, text
, NULL
);
465 gboolean
irc_channel_name_ok( const char *name_
)
467 const unsigned char *name
= (unsigned char*) name_
;
470 if( name_
[0] == '\0' )
473 /* Check if the first character is in CTYPES (#&) */
474 if( strchr( CTYPES
, name_
[0] ) == NULL
)
477 /* RFC 1459 keeps amazing me: While only a "few" chars are allowed
478 in nicknames, channel names can be pretty much anything as long
479 as they start with # or &. I'll be a little bit more strict and
480 disallow all non-printable characters. */
481 for( i
= 1; name
[i
]; i
++ )
482 if( name
[i
] <= ' ' || name
[i
] == ',' )
488 void irc_channel_name_strip( char *name
)
492 for( i
= j
= 0; name
[i
]; i
++ )
493 if( name
[i
] > ' ' && name
[i
] != ',' )
499 int irc_channel_name_cmp( const char *a_
, const char *b_
)
501 static unsigned char case_map
[256];
502 const unsigned char *a
= (unsigned char*) a_
, *b
= (unsigned char*) b_
;
505 if( case_map
['A'] == '\0' )
507 for( i
= 33; i
< 256; i
++ )
511 for( i
= 0; i
< 26; i
++ )
512 case_map
['A'+i
] = 'a' + i
;
517 case_map
['\\'] = '|';
520 if( !irc_channel_name_ok( a_
) || !irc_channel_name_ok( b_
) )
523 for( i
= 0; a
[i
] && b
[i
] && case_map
[a
[i
]] && case_map
[b
[i
]]; i
++ )
525 if( case_map
[a
[i
]] == case_map
[b
[i
]] )
528 return case_map
[a
[i
]] - case_map
[b
[i
]];
531 return case_map
[a
[i
]] - case_map
[b
[i
]];
534 static gint
irc_channel_user_cmp( gconstpointer a_
, gconstpointer b_
)
536 const irc_channel_user_t
*a
= a_
, *b
= b_
;
538 return irc_user_cmp( a
->iu
, b
->iu
);
541 void irc_channel_update_ops( irc_channel_t
*ic
, char *value
)
543 irc_channel_user_set_mode( ic
, ic
->irc
->root
,
544 ( strcmp( value
, "both" ) == 0 ||
545 strcmp( value
, "root" ) == 0 ) ? IRC_CHANNEL_USER_OP
: 0 );
546 irc_channel_user_set_mode( ic
, ic
->irc
->user
,
547 ( strcmp( value
, "both" ) == 0 ||
548 strcmp( value
, "user" ) == 0 ) ? IRC_CHANNEL_USER_OP
: 0 );
551 char *set_eval_irc_channel_ops( set_t
*set
, char *value
)
553 irc_t
*irc
= set
->data
;
556 if( strcmp( value
, "both" ) != 0 && strcmp( value
, "none" ) != 0 &&
557 strcmp( value
, "user" ) != 0 && strcmp( value
, "root" ) != 0 )
560 for( l
= irc
->channels
; l
; l
= l
->next
)
561 irc_channel_update_ops( l
->data
, value
);
566 /* Channel-type dependent functions, for control channels: */
567 static gboolean
control_channel_privmsg( irc_channel_t
*ic
, const char *msg
)
569 irc_t
*irc
= ic
->irc
;
573 /* Scan for non-whitespace chars followed by a colon: */
574 for( s
= msg
; *s
&& !isspace( *s
) && *s
!= ':' && *s
!= ','; s
++ ) {}
576 if( *s
== ':' || *s
== ',' )
580 memset( to
, 0, sizeof( to
) );
581 strncpy( to
, msg
, s
- msg
);
582 while( *(++s
) && isspace( *s
) ) {}
585 if( !( iu
= irc_user_by_name( irc
, to
) ) )
586 irc_channel_printf( ic
, "User does not exist: %s", to
);
588 ic
->last_target
= iu
;
590 else if( g_strcasecmp( set_getstr( &irc
->b
->set
, "default_target" ), "last" ) == 0 &&
591 ic
->last_target
&& g_slist_find( irc
->users
, ic
->last_target
) )
592 iu
= ic
->last_target
;
596 if( iu
&& iu
->f
->privmsg
)
598 iu
->last_channel
= ic
;
599 iu
->f
->privmsg( iu
, msg
);
605 static gboolean
control_channel_invite( irc_channel_t
*ic
, irc_user_t
*iu
)
607 struct irc_control_channel
*icc
= ic
->data
;
608 bee_user_t
*bu
= iu
->bu
;
613 if( icc
->type
!= IRC_CC_TYPE_GROUP
)
615 irc_send_num( ic
->irc
, 482, "%s :Invitations are only possible to fill_by=group channels", ic
->name
);
619 bu
->ic
->acc
->prpl
->add_buddy( bu
->ic
, bu
->handle
,
620 icc
->group
? icc
->group
->name
: NULL
);
625 static char *set_eval_by_account( set_t
*set
, char *value
);
626 static char *set_eval_fill_by( set_t
*set
, char *value
);
627 static char *set_eval_by_group( set_t
*set
, char *value
);
628 static char *set_eval_by_protocol( set_t
*set
, char *value
);
629 static char *set_eval_show_users( set_t
*set
, char *value
);
631 static gboolean
control_channel_init( irc_channel_t
*ic
)
633 struct irc_control_channel
*icc
;
635 set_add( &ic
->set
, "account", NULL
, set_eval_by_account
, ic
);
636 set_add( &ic
->set
, "fill_by", "all", set_eval_fill_by
, ic
);
637 set_add( &ic
->set
, "group", NULL
, set_eval_by_group
, ic
);
638 set_add( &ic
->set
, "protocol", NULL
, set_eval_by_protocol
, ic
);
640 /* When changing the default, also change it below. */
641 set_add( &ic
->set
, "show_users", "online+,away", set_eval_show_users
, ic
);
643 ic
->data
= icc
= g_new0( struct irc_control_channel
, 1 );
644 icc
->type
= IRC_CC_TYPE_DEFAULT
;
646 /* Have to run the evaluator to initialize icc->modes. */
647 set_setstr( &ic
->set
, "show_users", "online+,away" );
649 /* For scripts that care. */
650 irc_channel_set_mode( ic
, "+C" );
655 static gboolean
control_channel_join( irc_channel_t
*ic
)
657 bee_irc_channel_update( ic
->irc
, ic
, NULL
);
662 static char *set_eval_by_account( set_t
*set
, char *value
)
664 struct irc_channel
*ic
= set
->data
;
665 struct irc_control_channel
*icc
= ic
->data
;
668 if( !( acc
= account_get( ic
->irc
->b
, value
) ) )
672 if( icc
->type
== IRC_CC_TYPE_ACCOUNT
)
673 bee_irc_channel_update( ic
->irc
, ic
, NULL
);
675 return g_strdup( acc
->tag
);
678 static char *set_eval_fill_by( set_t
*set
, char *value
)
680 struct irc_channel
*ic
= set
->data
;
681 struct irc_control_channel
*icc
= ic
->data
;
683 if( strcmp( value
, "all" ) == 0 )
684 icc
->type
= IRC_CC_TYPE_DEFAULT
;
685 else if( strcmp( value
, "rest" ) == 0 )
686 icc
->type
= IRC_CC_TYPE_REST
;
687 else if( strcmp( value
, "group" ) == 0 )
688 icc
->type
= IRC_CC_TYPE_GROUP
;
689 else if( strcmp( value
, "account" ) == 0 )
690 icc
->type
= IRC_CC_TYPE_ACCOUNT
;
691 else if( strcmp( value
, "protocol" ) == 0 )
692 icc
->type
= IRC_CC_TYPE_PROTOCOL
;
696 bee_irc_channel_update( ic
->irc
, ic
, NULL
);
700 static char *set_eval_by_group( set_t
*set
, char *value
)
702 struct irc_channel
*ic
= set
->data
;
703 struct irc_control_channel
*icc
= ic
->data
;
705 icc
->group
= bee_group_by_name( ic
->irc
->b
, value
, TRUE
);
706 if( icc
->type
== IRC_CC_TYPE_GROUP
)
707 bee_irc_channel_update( ic
->irc
, ic
, NULL
);
709 return g_strdup( icc
->group
->name
);
712 static char *set_eval_by_protocol( set_t
*set
, char *value
)
714 struct irc_channel
*ic
= set
->data
;
715 struct irc_control_channel
*icc
= ic
->data
;
718 if( !( prpl
= find_protocol( value
) ) )
721 icc
->protocol
= prpl
;
722 if( icc
->type
== IRC_CC_TYPE_PROTOCOL
)
723 bee_irc_channel_update( ic
->irc
, ic
, NULL
);
728 static char *set_eval_show_users( set_t
*set
, char *value
)
730 struct irc_channel
*ic
= set
->data
;
731 struct irc_control_channel
*icc
= ic
->data
;
732 char **parts
= g_strsplit( value
, ",", 0 ), **part
;
735 memset( modes
, 0, 4 );
736 for( part
= parts
; *part
; part
++ )
738 char last
, modechar
= IRC_CHANNEL_USER_NONE
;
743 last
= (*part
)[strlen(*part
+1)];
745 modechar
= IRC_CHANNEL_USER_VOICE
;
746 else if( last
== '%' )
747 modechar
= IRC_CHANNEL_USER_HALFOP
;
748 else if( last
== '@' )
749 modechar
= IRC_CHANNEL_USER_OP
;
751 if( strncmp( *part
, "offline", 7 ) == 0 )
753 else if( strncmp( *part
, "away", 4 ) == 0 )
755 else if( strncmp( *part
, "online", 6 ) == 0 )
760 memcpy( icc
->modes
, modes
, 4 );
761 bee_irc_channel_update( ic
->irc
, ic
, NULL
);
771 /* Figure out if a channel is supposed to have the user, assuming s/he is
772 online or otherwise also selected by the show_users setting. Only works
773 for control channels, but does *not* check if this channel is of that
775 gboolean
irc_channel_wants_user( irc_channel_t
*ic
, irc_user_t
*iu
)
777 struct irc_control_channel
*icc
= ic
->data
;
784 case IRC_CC_TYPE_GROUP
:
785 return iu
->bu
->group
== icc
->group
;
786 case IRC_CC_TYPE_ACCOUNT
:
787 return iu
->bu
->ic
->acc
== icc
->account
;
788 case IRC_CC_TYPE_PROTOCOL
:
789 return iu
->bu
->ic
->acc
->prpl
== icc
->protocol
;
790 case IRC_CC_TYPE_DEFAULT
:
796 static gboolean
control_channel_free( irc_channel_t
*ic
)
798 struct irc_control_channel
*icc
= ic
->data
;
800 set_del( &ic
->set
, "account" );
801 set_del( &ic
->set
, "fill_by" );
802 set_del( &ic
->set
, "group" );
803 set_del( &ic
->set
, "protocol" );
804 set_del( &ic
->set
, "show_users" );
809 /* For scripts that care. */
810 irc_channel_set_mode( ic
, "-C" );
815 static const struct irc_channel_funcs control_channel_funcs
= {
816 control_channel_privmsg
,
817 control_channel_join
,
820 control_channel_invite
,
822 control_channel_init
,
823 control_channel_free
,