2007-01-25 06:40:21 +00:00
/*
* ircd - ratbox : A slightly useful ircd .
* m_join . c : Joins a channel .
*
* Copyright ( C ) 1990 Jarkko Oikarinen and University of Oulu , Co Center
* Copyright ( C ) 1996 - 2002 Hybrid Development Team
* Copyright ( C ) 2002 - 2005 ircd - ratbox development team
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
* USA
*
2007-05-27 13:07:27 +00:00
* $ Id : m_join . c 3494 2007 - 05 - 27 13 : 07 : 27 Z jilles $
2007-01-25 06:40:21 +00:00
*/
# include "stdinc.h"
# include "channel.h"
# include "client.h"
# include "common.h"
# include "hash.h"
2008-04-20 05:47:38 +00:00
# include "match.h"
2007-01-25 06:40:21 +00:00
# include "ircd.h"
# include "numeric.h"
# include "send.h"
# include "s_serv.h"
# include "s_conf.h"
# include "s_newconf.h"
# include "msg.h"
# include "parse.h"
# include "modules.h"
# include "packet.h"
static int m_join ( struct Client * , struct Client * , int , const char * * ) ;
static int ms_join ( struct Client * , struct Client * , int , const char * * ) ;
2008-04-07 14:26:59 +00:00
static int ms_sjoin ( struct Client * , struct Client * , int , const char * * ) ;
2007-01-25 06:40:21 +00:00
static int h_can_create_channel ;
static int h_channel_join ;
struct Message join_msgtab = {
" JOIN " , 0 , 0 , 0 , MFLG_SLOW ,
{ mg_unreg , { m_join , 2 } , { ms_join , 2 } , mg_ignore , mg_ignore , { m_join , 2 } }
} ;
2008-04-07 14:26:59 +00:00
struct Message sjoin_msgtab = {
" SJOIN " , 0 , 0 , 0 , MFLG_SLOW ,
{ mg_unreg , mg_ignore , mg_ignore , { ms_sjoin , 0 } , mg_ignore , mg_ignore }
} ;
mapi_clist_av1 join_clist [ ] = { & join_msgtab , & sjoin_msgtab , NULL } ;
2007-01-25 06:40:21 +00:00
mapi_hlist_av1 join_hlist [ ] = {
{ " can_create_channel " , & h_can_create_channel } ,
{ " channel_join " , & h_channel_join } ,
{ NULL , NULL } ,
} ;
2007-05-27 13:07:27 +00:00
DECLARE_MODULE_AV1 ( join , NULL , NULL , join_clist , join_hlist , NULL , " $Revision: 3494 $ " ) ;
2007-01-25 06:40:21 +00:00
2007-05-27 13:07:27 +00:00
static void do_join_0 ( struct Client * client_p , struct Client * source_p ) ;
2007-01-25 06:40:21 +00:00
static int check_channel_name_loc ( struct Client * source_p , const char * name ) ;
static void set_final_mode ( struct Mode * mode , struct Mode * oldmode ) ;
static void remove_our_modes ( struct Channel * chptr , struct Client * source_p ) ;
2008-04-07 14:26:59 +00:00
static void remove_ban_list ( struct Channel * chptr , struct Client * source_p ,
rb_dlink_list * list , char c , int cap , int mems ) ;
2007-01-25 06:40:21 +00:00
static char modebuf [ MODEBUFLEN ] ;
static char parabuf [ MODEBUFLEN ] ;
2008-04-07 14:26:59 +00:00
static const char * para [ MAXMODEPARAMS ] ;
2007-01-25 06:40:21 +00:00
static char * mbuf ;
2008-04-07 14:26:59 +00:00
static int pargs ;
2007-01-25 06:40:21 +00:00
/* Check what we will forward to, without sending any notices to the user
* - - jilles
*/
static struct Channel *
check_forward ( struct Client * source_p , struct Channel * chptr ,
char * key )
{
int depth = 0 , i ;
/* User is +Q */
if ( IsNoForward ( source_p ) )
return NULL ;
while ( depth < 16 )
{
chptr = find_channel ( chptr - > mode . forward ) ;
/* Can only forward to existing channels */
if ( chptr = = NULL )
return NULL ;
/* Already on there, show original error message */
if ( IsMember ( source_p , chptr ) )
return NULL ;
/* Juped. Sending a warning notice would be unfair */
if ( hash_find_resv ( chptr - > chname ) )
return NULL ;
/* Don't forward to +Q channel */
if ( chptr - > mode . mode & MODE_DISFORWARD )
return NULL ;
i = can_join ( source_p , chptr , key ) ;
if ( i = = 0 )
return chptr ;
if ( i ! = ERR_INVITEONLYCHAN & & i ! = ERR_NEEDREGGEDNICK & & i ! = ERR_THROTTLE & & i ! = ERR_CHANNELISFULL )
return NULL ;
depth + + ;
}
return NULL ;
}
/*
* m_join
* parv [ 0 ] = sender prefix
* parv [ 1 ] = channel
* parv [ 2 ] = channel password ( key )
*/
static int
m_join ( struct Client * client_p , struct Client * source_p , int parc , const char * parv [ ] )
{
static char jbuf [ BUFSIZE ] ;
struct Channel * chptr = NULL ;
struct ConfItem * aconf ;
char * name ;
char * key = NULL ;
int i , flags = 0 ;
char * p = NULL , * p2 = NULL ;
char * chanlist ;
char * mykey ;
int successful_join_count = 0 ; /* Number of channels successfully joined */
jbuf [ 0 ] = ' \0 ' ;
/* rebuild the list of channels theyre supposed to be joining.
* this code has a side effect of losing keys , but . .
*/
chanlist = LOCAL_COPY ( parv [ 1 ] ) ;
2008-04-20 05:20:25 +00:00
for ( name = rb_strtok_r ( chanlist , " , " , & p ) ; name ; name = rb_strtok_r ( NULL , " , " , & p ) )
2007-01-25 06:40:21 +00:00
{
/* check the length and name of channel is ok */
if ( ! check_channel_name_loc ( source_p , name ) | | ( strlen ( name ) > LOC_CHANNELLEN ) )
{
sendto_one_numeric ( source_p , ERR_BADCHANNAME ,
form_str ( ERR_BADCHANNAME ) , ( unsigned char * ) name ) ;
continue ;
}
2007-05-27 13:07:27 +00:00
/* join 0 parts all channels */
if ( * name = = ' 0 ' & & ( name [ 1 ] = = ' , ' | | name [ 1 ] = = ' \0 ' ) & & name = = chanlist )
{
( void ) strcpy ( jbuf , " 0 " ) ;
continue ;
}
2007-01-25 06:40:21 +00:00
/* check it begins with # or &, and local chans are disabled */
else if ( ! IsChannelName ( name ) )
{
sendto_one_numeric ( source_p , ERR_NOSUCHCHANNEL ,
form_str ( ERR_NOSUCHCHANNEL ) , name ) ;
continue ;
}
/* see if its resv'd */
if ( ! IsExemptResv ( source_p ) & & ( aconf = hash_find_resv ( name ) ) )
{
sendto_one_numeric ( source_p , ERR_BADCHANNAME ,
form_str ( ERR_BADCHANNAME ) , name ) ;
/* dont warn for opers */
if ( ! IsExemptJupe ( source_p ) & & ! IsOper ( source_p ) )
sendto_realops_snomask ( SNO_SPY , L_NETWIDE ,
" User %s (%s@%s) is attempting to join locally juped channel %s (%s) " ,
source_p - > name , source_p - > username ,
2007-01-31 23:57:18 +00:00
source_p - > orighost , name , aconf - > passwd ) ;
2007-01-25 06:40:21 +00:00
/* dont update tracking for jupe exempt users, these
* are likely to be spamtrap leaves
*/
else if ( IsExemptJupe ( source_p ) )
aconf - > port - - ;
continue ;
}
if ( splitmode & & ! IsOper ( source_p ) & & ( * name ! = ' & ' ) & &
ConfigChannel . no_join_on_split )
{
sendto_one ( source_p , form_str ( ERR_UNAVAILRESOURCE ) ,
me . name , source_p - > name , name ) ;
continue ;
}
if ( * jbuf )
( void ) strcat ( jbuf , " , " ) ;
2008-04-20 04:44:04 +00:00
( void ) rb_strlcat ( jbuf , name , sizeof ( jbuf ) ) ;
2007-01-25 06:40:21 +00:00
}
if ( parc > 2 )
{
mykey = LOCAL_COPY ( parv [ 2 ] ) ;
2008-04-20 05:20:25 +00:00
key = rb_strtok_r ( mykey , " , " , & p2 ) ;
2007-01-25 06:40:21 +00:00
}
2008-04-20 05:20:25 +00:00
for ( name = rb_strtok_r ( jbuf , " , " , & p ) ; name ;
key = ( key ) ? rb_strtok_r ( NULL , " , " , & p2 ) : NULL , name = rb_strtok_r ( NULL , " , " , & p ) )
2007-01-25 06:40:21 +00:00
{
hook_data_channel_activity hook_info ;
2007-05-27 13:07:27 +00:00
/* JOIN 0 simply parts all channels the user is in */
if ( * name = = ' 0 ' & & ! atoi ( name ) )
{
if ( source_p - > user - > channel . head = = NULL )
continue ;
do_join_0 ( & me , source_p ) ;
continue ;
}
2007-01-25 06:40:21 +00:00
/* look for the channel */
if ( ( chptr = find_channel ( name ) ) ! = NULL )
{
if ( IsMember ( source_p , chptr ) )
continue ;
flags = 0 ;
}
else
{
hook_data_client_approval moduledata ;
moduledata . client = source_p ;
moduledata . approved = 0 ;
call_hook ( h_can_create_channel , & moduledata ) ;
2007-12-17 23:17:25 +00:00
if ( moduledata . approved ! = 0 )
2007-01-25 06:40:21 +00:00
{
sendto_one ( source_p , form_str ( moduledata . approved ) ,
me . name , source_p - > name , name ) ;
continue ;
}
if ( splitmode & & ! IsOper ( source_p ) & & ( * name ! = ' & ' ) & &
ConfigChannel . no_create_on_split )
{
sendto_one ( source_p , form_str ( ERR_UNAVAILRESOURCE ) ,
me . name , source_p - > name , name ) ;
continue ;
}
flags = CHFL_CHANOP ;
}
2008-04-01 20:18:48 +00:00
if ( ( rb_dlink_list_length ( & source_p - > user - > channel ) > =
2007-01-25 06:40:21 +00:00
( unsigned long ) ConfigChannel . max_chans_per_user ) & &
( ! IsOper ( source_p ) | |
2008-04-01 20:18:48 +00:00
( rb_dlink_list_length ( & source_p - > user - > channel ) > =
2007-01-25 06:40:21 +00:00
( unsigned long ) ConfigChannel . max_chans_per_user * 3 ) ) )
{
sendto_one ( source_p , form_str ( ERR_TOOMANYCHANNELS ) ,
me . name , source_p - > name , name ) ;
if ( successful_join_count )
2008-04-01 23:53:20 +00:00
source_p - > localClient - > last_join_time = rb_current_time ( ) ;
2007-01-25 06:40:21 +00:00
return 0 ;
}
if ( flags = = 0 ) /* if channel doesn't exist, don't penalize */
successful_join_count + + ;
if ( chptr = = NULL ) /* If I already have a chptr, no point doing this */
{
chptr = get_or_create_channel ( source_p , name , NULL ) ;
if ( chptr = = NULL )
{
sendto_one ( source_p , form_str ( ERR_UNAVAILRESOURCE ) ,
me . name , source_p - > name , name ) ;
if ( successful_join_count > 0 )
successful_join_count - - ;
continue ;
}
}
if ( ! IsOper ( source_p ) & & ! IsExemptSpambot ( source_p ) )
check_spambot_warning ( source_p , name ) ;
/* can_join checks for +i key, bans etc */
if ( ( i = can_join ( source_p , chptr , key ) ) )
{
if ( ( i ! = ERR_NEEDREGGEDNICK & & i ! = ERR_THROTTLE & & i ! = ERR_INVITEONLYCHAN & & i ! = ERR_CHANNELISFULL ) | |
( ! ConfigChannel . use_forward | | ( chptr = check_forward ( source_p , chptr , key ) ) = = NULL ) )
{
sendto_one ( source_p , form_str ( i ) , me . name , source_p - > name , name ) ;
if ( successful_join_count > 0 )
successful_join_count - - ;
continue ;
}
sendto_one_numeric ( source_p , ERR_LINKCHANNEL , form_str ( ERR_LINKCHANNEL ) , name , chptr - > chname ) ;
}
/* add the user to the channel */
add_user_to_channel ( chptr , source_p , flags ) ;
if ( chptr - > mode . join_num & &
2008-04-01 23:53:20 +00:00
rb_current_time ( ) - chptr - > join_delta > = chptr - > mode . join_time )
2007-01-25 06:40:21 +00:00
{
chptr - > join_count = 0 ;
2008-04-01 23:53:20 +00:00
chptr - > join_delta = rb_current_time ( ) ;
2007-01-25 06:40:21 +00:00
}
chptr - > join_count + + ;
/* we send the user their join here, because we could have to
* send a mode out next .
*/
sendto_channel_local ( ALL_MEMBERS , chptr , " :%s!%s@%s JOIN :%s " ,
source_p - > name ,
source_p - > username , source_p - > host , chptr - > chname ) ;
/* its a new channel, set +nt and burst. */
if ( flags & CHFL_CHANOP )
{
2008-04-01 23:53:20 +00:00
chptr - > channelts = rb_current_time ( ) ;
2007-01-25 06:40:21 +00:00
chptr - > mode . mode | = MODE_TOPICLIMIT ;
chptr - > mode . mode | = MODE_NOPRIVMSGS ;
sendto_channel_local ( ONLY_CHANOPS , chptr , " :%s MODE %s +nt " ,
me . name , chptr - > chname ) ;
if ( * chptr - > chname = = ' # ' )
{
sendto_server ( client_p , chptr , CAP_TS6 , NOCAPS ,
" :%s SJOIN %ld %s +nt :@%s " ,
me . id , ( long ) chptr - > channelts ,
chptr - > chname , source_p - > id ) ;
}
}
else
{
sendto_server ( client_p , chptr , CAP_TS6 , NOCAPS ,
2007-02-20 00:34:28 +00:00
" :%s JOIN %ld %s + " ,
2007-01-25 06:40:21 +00:00
use_id ( source_p ) , ( long ) chptr - > channelts ,
2007-02-20 00:34:28 +00:00
chptr - > chname ) ;
2007-01-25 06:40:21 +00:00
}
del_invite ( chptr , source_p ) ;
if ( chptr - > topic ! = NULL )
{
sendto_one ( source_p , form_str ( RPL_TOPIC ) , me . name ,
source_p - > name , chptr - > chname , chptr - > topic ) ;
sendto_one ( source_p , form_str ( RPL_TOPICWHOTIME ) ,
me . name , source_p - > name , chptr - > chname ,
chptr - > topic_info , chptr - > topic_time ) ;
}
channel_member_names ( chptr , source_p , 1 ) ;
if ( successful_join_count )
2008-04-01 23:53:20 +00:00
source_p - > localClient - > last_join_time = rb_current_time ( ) ;
2007-01-25 06:40:21 +00:00
hook_info . client = source_p ;
hook_info . chptr = chptr ;
hook_info . key = key ;
call_hook ( h_channel_join , & hook_info ) ;
}
return 0 ;
}
/*
* ms_join
*
* inputs -
* output - none
* side effects - handles remote JOIN ' s sent by servers . In TSora
* remote clients are joined using SJOIN , hence a
* JOIN sent by a server on behalf of a client is an error .
* here , the initial code is in to take an extra parameter
* and use it for the TimeStamp on a new channel .
*/
static int
ms_join ( struct Client * client_p , struct Client * source_p , int parc , const char * parv [ ] )
{
struct Channel * chptr ;
2007-02-20 00:34:28 +00:00
static struct Mode mode ;
2007-01-25 06:40:21 +00:00
time_t oldts ;
time_t newts ;
int isnew ;
int keep_our_modes = YES ;
int keep_new_modes = YES ;
2008-04-01 22:47:17 +00:00
rb_dlink_node * ptr , * next_ptr ;
2007-01-25 06:40:21 +00:00
2007-05-27 13:07:27 +00:00
/* special case for join 0 */
if ( ( parv [ 1 ] [ 0 ] = = ' 0 ' ) & & ( parv [ 1 ] [ 1 ] = = ' \0 ' ) & & parc = = 2 )
{
do_join_0 ( client_p , source_p ) ;
return 0 ;
}
2007-01-25 06:40:21 +00:00
if ( parc < 4 )
return 0 ;
if ( ! IsChannelName ( parv [ 2 ] ) | | ! check_channel_name ( parv [ 2 ] ) )
return 0 ;
/* joins for local channels cant happen. */
if ( parv [ 2 ] [ 0 ] = = ' & ' )
return 0 ;
mbuf = modebuf ;
mode . key [ 0 ] = mode . forward [ 0 ] = ' \0 ' ;
mode . mode = mode . limit = mode . join_num = mode . join_time = 0 ;
if ( ( chptr = get_or_create_channel ( source_p , parv [ 2 ] , & isnew ) ) = = NULL )
return 0 ;
newts = atol ( parv [ 1 ] ) ;
oldts = chptr - > channelts ;
# ifdef IGNORE_BOGUS_TS
if ( newts < 800000000 )
{
sendto_realops_snomask ( SNO_DEBUG , L_ALL ,
" *** Bogus TS %ld on %s ignored from %s " ,
( long ) newts , chptr - > chname , client_p - > name ) ;
newts = ( oldts = = 0 ) ? oldts : 800000000 ;
}
# else
/* making a channel TS0 */
if ( ! isnew & & ! newts & & oldts )
{
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s NOTICE %s :*** Notice -- TS for %s changed from %ld to 0 " ,
me . name , chptr - > chname , chptr - > chname , ( long ) oldts ) ;
sendto_realops_snomask ( SNO_GENERAL , L_ALL ,
" Server %s changing TS on %s from %ld to 0 " ,
source_p - > name , chptr - > chname , ( long ) oldts ) ;
}
# endif
if ( isnew )
chptr - > channelts = newts ;
else if ( newts = = 0 | | oldts = = 0 )
chptr - > channelts = 0 ;
else if ( newts = = oldts )
;
else if ( newts < oldts )
{
keep_our_modes = NO ;
chptr - > channelts = newts ;
}
else
keep_new_modes = NO ;
/* Lost the TS, other side wins, so remove modes on this side */
if ( ! keep_our_modes )
{
2007-02-20 00:34:28 +00:00
set_final_mode ( & mode , & chptr - > mode ) ;
chptr - > mode = mode ;
2007-01-25 06:40:21 +00:00
remove_our_modes ( chptr , source_p ) ;
2008-04-01 22:47:17 +00:00
RB_DLINK_FOREACH_SAFE ( ptr , next_ptr , chptr - > invites . head )
2007-01-25 06:40:21 +00:00
{
del_invite ( chptr , ptr - > data ) ;
}
2007-02-20 00:34:28 +00:00
/* If setting -j, clear join throttle state -- jilles */
chptr - > join_count = chptr - > join_delta = 0 ;
2007-01-25 06:40:21 +00:00
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s NOTICE %s :*** Notice -- TS for %s changed from %ld to %ld " ,
me . name , chptr - > chname , chptr - > chname ,
( long ) oldts , ( long ) newts ) ;
2007-04-28 23:47:25 +00:00
/* Update capitalization in channel name, this makes the
* capitalization timestamped like modes are - - jilles */
strcpy ( chptr - > chname , parv [ 2 ] ) ;
2007-02-20 00:34:28 +00:00
if ( * modebuf ! = ' \0 ' )
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s MODE %s %s %s " ,
source_p - > servptr - > name ,
chptr - > chname , modebuf , parabuf ) ;
* modebuf = * parabuf = ' \0 ' ;
2007-01-25 06:40:21 +00:00
}
if ( ! IsMember ( source_p , chptr ) )
{
add_user_to_channel ( chptr , source_p , CHFL_PEON ) ;
if ( chptr - > mode . join_num & &
2008-04-01 23:53:20 +00:00
rb_current_time ( ) - chptr - > join_delta > = chptr - > mode . join_time )
2007-01-25 06:40:21 +00:00
{
chptr - > join_count = 0 ;
2008-04-01 23:53:20 +00:00
chptr - > join_delta = rb_current_time ( ) ;
2007-01-25 06:40:21 +00:00
}
chptr - > join_count + + ;
sendto_channel_local ( ALL_MEMBERS , chptr , " :%s!%s@%s JOIN :%s " ,
source_p - > name , source_p - > username ,
source_p - > host , chptr - > chname ) ;
}
sendto_server ( client_p , chptr , CAP_TS6 , NOCAPS ,
2007-02-20 00:34:28 +00:00
" :%s JOIN %ld %s + " ,
source_p - > id , ( long ) chptr - > channelts , chptr - > chname ) ;
2007-01-25 06:40:21 +00:00
return 0 ;
}
2008-04-07 14:26:59 +00:00
static int
ms_sjoin ( struct Client * client_p , struct Client * source_p , int parc , const char * parv [ ] )
{
static char buf_uid [ BUFSIZE ] ;
static const char empty_modes [ ] = " 0 " ;
struct Channel * chptr ;
struct Client * target_p , * fakesource_p ;
time_t newts ;
time_t oldts ;
static struct Mode mode , * oldmode ;
const char * modes ;
int args = 0 ;
int keep_our_modes = 1 ;
int keep_new_modes = 1 ;
int fl ;
int isnew ;
2008-06-08 06:32:17 +00:00
int mlen_uid ;
2008-04-07 14:26:59 +00:00
int len_nick ;
int len_uid ;
int len ;
int joins = 0 ;
const char * s ;
char * ptr_uid ;
char * p ;
int i , joinc = 0 , timeslice = 0 ;
static char empty [ ] = " " ;
rb_dlink_node * ptr , * next_ptr ;
if ( ! IsChannelName ( parv [ 2 ] ) | | ! check_channel_name ( parv [ 2 ] ) )
return 0 ;
/* SJOIN's for local channels can't happen. */
if ( * parv [ 2 ] = = ' & ' )
return 0 ;
modebuf [ 0 ] = parabuf [ 0 ] = mode . key [ 0 ] = mode . forward [ 0 ] = ' \0 ' ;
pargs = mode . mode = mode . limit = mode . join_num = mode . join_time = 0 ;
/* Hide connecting server on netburst -- jilles */
if ( ConfigServerHide . flatten_links & & ! HasSentEob ( source_p ) )
fakesource_p = & me ;
else
fakesource_p = source_p ;
mbuf = modebuf ;
newts = atol ( parv [ 1 ] ) ;
s = parv [ 3 ] ;
while ( * s )
{
switch ( * ( s + + ) )
{
case ' i ' :
mode . mode | = MODE_INVITEONLY ;
break ;
case ' n ' :
mode . mode | = MODE_NOPRIVMSGS ;
break ;
case ' p ' :
mode . mode | = MODE_PRIVATE ;
break ;
case ' s ' :
mode . mode | = MODE_SECRET ;
break ;
case ' m ' :
mode . mode | = MODE_MODERATED ;
break ;
case ' t ' :
mode . mode | = MODE_TOPICLIMIT ;
break ;
case ' r ' :
mode . mode | = MODE_REGONLY ;
break ;
case ' L ' :
mode . mode | = MODE_EXLIMIT ;
break ;
case ' P ' :
mode . mode | = MODE_PERMANENT ;
break ;
case ' c ' :
mode . mode | = MODE_NOCOLOR ;
break ;
case ' g ' :
mode . mode | = MODE_FREEINVITE ;
break ;
case ' z ' :
mode . mode | = MODE_OPMODERATE ;
break ;
case ' F ' :
mode . mode | = MODE_FREETARGET ;
break ;
case ' Q ' :
mode . mode | = MODE_DISFORWARD ;
break ;
case ' f ' :
2008-04-20 04:40:40 +00:00
rb_strlcpy ( mode . forward , parv [ 4 + args ] , sizeof ( mode . forward ) ) ;
2008-04-07 14:26:59 +00:00
args + + ;
if ( parc < 5 + args )
return 0 ;
break ;
case ' j ' :
sscanf ( parv [ 4 + args ] , " %d:%d " , & joinc , & timeslice ) ;
args + + ;
mode . join_num = joinc ;
mode . join_time = timeslice ;
if ( parc < 5 + args )
return 0 ;
break ;
case ' k ' :
2008-04-20 04:40:40 +00:00
rb_strlcpy ( mode . key , parv [ 4 + args ] , sizeof ( mode . key ) ) ;
2008-04-07 14:26:59 +00:00
args + + ;
if ( parc < 5 + args )
return 0 ;
break ;
case ' l ' :
mode . limit = atoi ( parv [ 4 + args ] ) ;
args + + ;
if ( parc < 5 + args )
return 0 ;
break ;
}
}
if ( parv [ args + 4 ] )
{
s = parv [ args + 4 ] ;
/* remove any leading spaces */
while ( * s = = ' ' )
s + + ;
}
else
s = " " ;
if ( ( chptr = get_or_create_channel ( source_p , parv [ 2 ] , & isnew ) ) = = NULL )
return 0 ; /* channel name too long? */
oldts = chptr - > channelts ;
oldmode = & chptr - > mode ;
# ifdef IGNORE_BOGUS_TS
if ( newts < 800000000 )
{
sendto_realops_snomask ( SNO_DEBUG , L_ALL ,
" *** Bogus TS %ld on %s ignored from %s " ,
( long ) newts , chptr - > chname , client_p - > name ) ;
newts = ( oldts = = 0 ) ? oldts : 800000000 ;
}
# else
if ( ! isnew & & ! newts & & oldts )
{
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s NOTICE %s :*** Notice -- TS for %s "
" changed from %ld to 0 " ,
me . name , chptr - > chname , chptr - > chname , ( long ) oldts ) ;
sendto_realops_snomask ( SNO_GENERAL , L_ALL ,
" Server %s changing TS on %s from %ld to 0 " ,
source_p - > name , chptr - > chname , ( long ) oldts ) ;
}
# endif
if ( isnew )
chptr - > channelts = newts ;
else if ( newts = = 0 | | oldts = = 0 )
chptr - > channelts = 0 ;
else if ( newts = = oldts )
;
else if ( newts < oldts )
{
/* If configured, kick people trying to join +i/+k
* channels by recreating them on split servers .
* Don ' t kick if the source has sent EOB ( services
* deopping everyone by TS - 1 SJOIN ) .
* - - jilles */
if ( ConfigChannel . kick_on_split_riding & &
! HasSentEob ( source_p ) & &
( ( mode . mode & MODE_INVITEONLY ) | |
( mode . key [ 0 ] ! = 0 & & irccmp ( mode . key , oldmode - > key ) ! = 0 ) ) )
{
struct membership * msptr ;
struct Client * who ;
int l = rb_dlink_list_length ( & chptr - > members ) ;
int b = rb_dlink_list_length ( & chptr - > banlist ) +
rb_dlink_list_length ( & chptr - > exceptlist ) +
rb_dlink_list_length ( & chptr - > invexlist ) +
rb_dlink_list_length ( & chptr - > quietlist ) ;
RB_DLINK_FOREACH_SAFE ( ptr , next_ptr , chptr - > locmembers . head )
{
msptr = ptr - > data ;
who = msptr - > client_p ;
sendto_one ( who , " :%s KICK %s %s :Net Rider " ,
me . name , chptr - > chname , who - > name ) ;
sendto_server ( NULL , chptr , CAP_TS6 , NOCAPS ,
" :%s KICK %s %s :Net Rider " ,
me . id , chptr - > chname ,
who - > id ) ;
remove_user_from_channel ( msptr ) ;
if ( - - l = = 0 )
break ;
}
if ( l = = 0 )
{
/* Channel was emptied, create a new one */
if ( ( chptr = get_or_create_channel ( source_p , parv [ 2 ] , & isnew ) ) = = NULL )
return 0 ; /* oops! */
/* If the source does not do TS6,
* nontimestamped bans have been sent to it ,
* but we have just lost those here . Let ' s
* warn the channel about this . Because
* of the kicks , any users on the channel
* will be at client_p . - - jilles */
if ( ! has_id ( source_p ) & & b > 0 )
sendto_one ( client_p , " :%s NOTICE %s :*** Notice -- possible ban desync on %s, please remove any bans just added by servers " , get_id ( & me , client_p ) , parv [ 2 ] , parv [ 2 ] ) ;
oldmode = & chptr - > mode ;
}
}
keep_our_modes = NO ;
chptr - > channelts = newts ;
}
else
keep_new_modes = NO ;
if ( ! keep_new_modes )
mode = * oldmode ;
else if ( keep_our_modes )
{
mode . mode | = oldmode - > mode ;
if ( oldmode - > limit > mode . limit )
mode . limit = oldmode - > limit ;
if ( strcmp ( mode . key , oldmode - > key ) < 0 )
strcpy ( mode . key , oldmode - > key ) ;
if ( oldmode - > join_num > mode . join_num | |
( oldmode - > join_num = = mode . join_num & &
oldmode - > join_time > mode . join_time ) )
{
mode . join_num = oldmode - > join_num ;
mode . join_time = oldmode - > join_time ;
}
if ( irccmp ( mode . forward , oldmode - > forward ) < 0 )
strcpy ( mode . forward , oldmode - > forward ) ;
}
else
{
/* If setting -j, clear join throttle state -- jilles */
if ( ! mode . join_num )
chptr - > join_count = chptr - > join_delta = 0 ;
}
set_final_mode ( & mode , oldmode ) ;
chptr - > mode = mode ;
/* Lost the TS, other side wins, so remove modes on this side */
if ( ! keep_our_modes )
{
remove_our_modes ( chptr , fakesource_p ) ;
RB_DLINK_FOREACH_SAFE ( ptr , next_ptr , chptr - > invites . head )
{
del_invite ( chptr , ptr - > data ) ;
}
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s NOTICE %s :*** Notice -- TS for %s changed from %ld to %ld " ,
me . name , chptr - > chname , chptr - > chname ,
( long ) oldts , ( long ) newts ) ;
/* Update capitalization in channel name, this makes the
* capitalization timestamped like modes are - - jilles */
strcpy ( chptr - > chname , parv [ 2 ] ) ;
}
if ( * modebuf ! = ' \0 ' )
sendto_channel_local ( ALL_MEMBERS , chptr , " :%s MODE %s %s %s " ,
fakesource_p - > name , chptr - > chname , modebuf , parabuf ) ;
* modebuf = * parabuf = ' \0 ' ;
if ( parv [ 3 ] [ 0 ] ! = ' 0 ' & & keep_new_modes )
modes = channel_modes ( chptr , source_p ) ;
else
modes = empty_modes ;
/* working on the presumption eventually itll be more efficient to
* build a TS6 buffer without checking its needed . .
*/
mlen_uid = rb_sprintf ( buf_uid , " :%s SJOIN %ld %s %s : " ,
use_id ( source_p ) , ( long ) chptr - > channelts , parv [ 2 ] , modes ) ;
ptr_uid = buf_uid + mlen_uid ;
mbuf = modebuf ;
para [ 0 ] = para [ 1 ] = para [ 2 ] = para [ 3 ] = empty ;
pargs = 0 ;
len_nick = len_uid = 0 ;
/* if theres a space, theres going to be more than one nick, change the
* first space to \ 0 , so s is just the first nick , and point p to the
* second nick
*/
if ( ( p = strchr ( s , ' ' ) ) ! = NULL )
{
* p + + = ' \0 ' ;
}
* mbuf + + = ' + ' ;
while ( s )
{
fl = 0 ;
for ( i = 0 ; i < 2 ; i + + )
{
if ( * s = = ' @ ' )
{
fl | = CHFL_CHANOP ;
s + + ;
}
else if ( * s = = ' + ' )
{
fl | = CHFL_VOICE ;
s + + ;
}
}
/* if the client doesnt exist or is fake direction, skip. */
if ( ! ( target_p = find_client ( s ) ) | |
( target_p - > from ! = client_p ) | | ! IsPerson ( target_p ) )
goto nextnick ;
/* we assume for these we can fit at least one nick/uid in.. */
/* check we can fit another status+nick+space into a buffer */
if ( ( mlen_uid + len_uid + IDLEN + 3 ) > ( BUFSIZE - 3 ) )
{
* ( ptr_uid - 1 ) = ' \0 ' ;
sendto_server ( client_p - > from , NULL , CAP_TS6 , NOCAPS , " %s " , buf_uid ) ;
ptr_uid = buf_uid + mlen_uid ;
len_uid = 0 ;
}
if ( keep_new_modes )
{
if ( fl & CHFL_CHANOP )
{
* ptr_uid + + = ' @ ' ;
len_nick + + ;
len_uid + + ;
}
if ( fl & CHFL_VOICE )
{
* ptr_uid + + = ' + ' ;
len_nick + + ;
len_uid + + ;
}
}
/* copy the nick to the two buffers */
len = rb_sprintf ( ptr_uid , " %s " , use_id ( target_p ) ) ;
ptr_uid + = len ;
len_uid + = len ;
if ( ! keep_new_modes )
fl = 0 ;
if ( ! IsMember ( target_p , chptr ) )
{
add_user_to_channel ( chptr , target_p , fl ) ;
sendto_channel_local ( ALL_MEMBERS , chptr , " :%s!%s@%s JOIN :%s " ,
target_p - > name ,
target_p - > username , target_p - > host , parv [ 2 ] ) ;
joins + + ;
}
if ( fl & CHFL_CHANOP )
{
* mbuf + + = ' o ' ;
para [ pargs + + ] = target_p - > name ;
/* a +ov user.. bleh */
if ( fl & CHFL_VOICE )
{
/* its possible the +o has filled up MAXMODEPARAMS, if so, start
* a new buffer
*/
if ( pargs > = MAXMODEPARAMS )
{
* mbuf = ' \0 ' ;
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s MODE %s %s %s %s %s %s " ,
fakesource_p - > name , chptr - > chname ,
modebuf ,
para [ 0 ] , para [ 1 ] , para [ 2 ] , para [ 3 ] ) ;
mbuf = modebuf ;
* mbuf + + = ' + ' ;
para [ 0 ] = para [ 1 ] = para [ 2 ] = para [ 3 ] = NULL ;
pargs = 0 ;
}
* mbuf + + = ' v ' ;
para [ pargs + + ] = target_p - > name ;
}
}
else if ( fl & CHFL_VOICE )
{
* mbuf + + = ' v ' ;
para [ pargs + + ] = target_p - > name ;
}
if ( pargs > = MAXMODEPARAMS )
{
* mbuf = ' \0 ' ;
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s MODE %s %s %s %s %s %s " ,
fakesource_p - > name ,
chptr - > chname ,
modebuf , para [ 0 ] , para [ 1 ] , para [ 2 ] , para [ 3 ] ) ;
mbuf = modebuf ;
* mbuf + + = ' + ' ;
para [ 0 ] = para [ 1 ] = para [ 2 ] = para [ 3 ] = NULL ;
pargs = 0 ;
}
nextnick :
/* p points to the next nick */
s = p ;
/* if there was a trailing space and p was pointing to it, then we
* need to exit . . this has the side effect of breaking double spaces
* in an sjoin . . but that shouldnt happen anyway
*/
if ( s & & ( * s = = ' \0 ' ) )
s = p = NULL ;
/* if p was NULL due to no spaces, s wont exist due to the above, so
* we cant check it for spaces . . if there are no spaces , then when
* we next get here , s will be NULL
*/
if ( s & & ( ( p = strchr ( s , ' ' ) ) ! = NULL ) )
{
* p + + = ' \0 ' ;
}
}
* mbuf = ' \0 ' ;
if ( pargs )
{
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s MODE %s %s %s %s %s %s " ,
fakesource_p - > name , chptr - > chname , modebuf ,
para [ 0 ] , CheckEmpty ( para [ 1 ] ) ,
CheckEmpty ( para [ 2 ] ) , CheckEmpty ( para [ 3 ] ) ) ;
}
if ( ! joins & & ! ( chptr - > mode . mode & MODE_PERMANENT ) & & isnew )
{
destroy_channel ( chptr ) ;
return 0 ;
}
/* Keep the colon if we're sending an SJOIN without nicks -- jilles */
if ( joins )
{
* ( ptr_uid - 1 ) = ' \0 ' ;
}
sendto_server ( client_p - > from , NULL , CAP_TS6 , NOCAPS , " %s " , buf_uid ) ;
/* if the source does TS6 we have to remove our bans. Its now safe
* to issue - b ' s to the non - ts6 servers , as the sjoin we ' ve just
* sent will kill any ops they have .
*/
if ( ! keep_our_modes & & source_p - > id [ 0 ] ! = ' \0 ' )
{
if ( rb_dlink_list_length ( & chptr - > banlist ) > 0 )
remove_ban_list ( chptr , fakesource_p , & chptr - > banlist , ' b ' , NOCAPS , ALL_MEMBERS ) ;
if ( rb_dlink_list_length ( & chptr - > exceptlist ) > 0 )
remove_ban_list ( chptr , fakesource_p , & chptr - > exceptlist ,
' e ' , CAP_EX , ONLY_CHANOPS ) ;
if ( rb_dlink_list_length ( & chptr - > invexlist ) > 0 )
remove_ban_list ( chptr , fakesource_p , & chptr - > invexlist ,
' I ' , CAP_IE , ONLY_CHANOPS ) ;
if ( rb_dlink_list_length ( & chptr - > quietlist ) > 0 )
remove_ban_list ( chptr , fakesource_p , & chptr - > quietlist ,
' q ' , NOCAPS , ALL_MEMBERS ) ;
chptr - > bants + + ;
}
return 0 ;
}
2007-05-27 13:07:27 +00:00
/*
* do_join_0
*
* inputs - pointer to client doing join 0
* output - NONE
* side effects - Use has decided to join 0. This is legacy
* from the days when channels were numbers not names . * sigh *
* There is a bunch of evilness necessary here due to
* anti spambot code .
*/
static void
do_join_0 ( struct Client * client_p , struct Client * source_p )
{
struct membership * msptr ;
struct Channel * chptr = NULL ;
2008-04-01 20:18:48 +00:00
rb_dlink_node * ptr ;
2007-05-27 13:07:27 +00:00
/* Finish the flood grace period... */
if ( MyClient ( source_p ) & & ! IsFloodDone ( source_p ) )
flood_endgrace ( source_p ) ;
sendto_server ( client_p , NULL , CAP_TS6 , NOCAPS , " :%s JOIN 0 " , use_id ( source_p ) ) ;
if ( source_p - > user - > channel . head & & MyConnect ( source_p ) & &
! IsOper ( source_p ) & & ! IsExemptSpambot ( source_p ) )
check_spambot_warning ( source_p , NULL ) ;
while ( ( ptr = source_p - > user - > channel . head ) )
{
msptr = ptr - > data ;
chptr = msptr - > chptr ;
sendto_channel_local ( ALL_MEMBERS , chptr , " :%s!%s@%s PART %s " ,
source_p - > name ,
source_p - > username , source_p - > host , chptr - > chname ) ;
remove_user_from_channel ( msptr ) ;
}
}
2007-01-25 06:40:21 +00:00
static int
check_channel_name_loc ( struct Client * source_p , const char * name )
{
s_assert ( name ! = NULL ) ;
if ( EmptyString ( name ) )
return 0 ;
if ( ConfigFileEntry . disable_fake_channels & & ! IsOper ( source_p ) )
{
for ( ; * name ; + + name )
{
if ( ! IsChanChar ( * name ) | | IsFakeChanChar ( * name ) )
return 0 ;
}
}
else
{
for ( ; * name ; + + name )
{
if ( ! IsChanChar ( * name ) )
return 0 ;
}
}
return 1 ;
}
static void
set_final_mode ( struct Mode * mode , struct Mode * oldmode )
{
int dir = MODE_QUERY ;
char * pbuf = parabuf ;
int len ;
int i ;
/* ok, first get a list of modes we need to add */
2008-04-07 14:26:59 +00:00
for ( i = 0 ; chmode_flags [ i ] . letter ; i + + )
2007-01-25 06:40:21 +00:00
{
2007-12-25 00:00:20 +00:00
if ( ( mode - > mode & chmode_flags [ i ] . mode ) & & ! ( oldmode - > mode & chmode_flags [ i ] . mode ) )
2007-01-25 06:40:21 +00:00
{
if ( dir ! = MODE_ADD )
{
* mbuf + + = ' + ' ;
dir = MODE_ADD ;
}
2007-12-25 00:00:20 +00:00
* mbuf + + = chmode_flags [ i ] . letter ;
2007-01-25 06:40:21 +00:00
}
}
/* now the ones we need to remove. */
2008-04-07 14:26:59 +00:00
for ( i = 0 ; chmode_flags [ i ] . letter ; i + + )
2007-01-25 06:40:21 +00:00
{
2007-12-25 00:00:20 +00:00
if ( ( oldmode - > mode & chmode_flags [ i ] . mode ) & & ! ( mode - > mode & chmode_flags [ i ] . mode ) )
2007-01-25 06:40:21 +00:00
{
if ( dir ! = MODE_DEL )
{
* mbuf + + = ' - ' ;
dir = MODE_DEL ;
}
2007-12-25 00:00:20 +00:00
* mbuf + + = chmode_flags [ i ] . letter ;
2007-01-25 06:40:21 +00:00
}
}
if ( oldmode - > limit & & ! mode - > limit )
{
if ( dir ! = MODE_DEL )
{
* mbuf + + = ' - ' ;
dir = MODE_DEL ;
}
* mbuf + + = ' l ' ;
}
if ( oldmode - > key [ 0 ] & & ! mode - > key [ 0 ] )
{
if ( dir ! = MODE_DEL )
{
* mbuf + + = ' - ' ;
dir = MODE_DEL ;
}
* mbuf + + = ' k ' ;
2008-04-01 20:21:53 +00:00
len = rb_sprintf ( pbuf , " %s " , oldmode - > key ) ;
2007-01-25 06:40:21 +00:00
pbuf + = len ;
}
if ( oldmode - > join_num & & ! mode - > join_num )
{
if ( dir ! = MODE_DEL )
{
* mbuf + + = ' - ' ;
dir = MODE_DEL ;
}
* mbuf + + = ' j ' ;
}
if ( oldmode - > forward [ 0 ] & & ! mode - > forward [ 0 ] )
{
if ( dir ! = MODE_DEL )
{
* mbuf + + = ' - ' ;
dir = MODE_DEL ;
}
* mbuf + + = ' f ' ;
}
if ( mode - > limit & & oldmode - > limit ! = mode - > limit )
{
if ( dir ! = MODE_ADD )
{
* mbuf + + = ' + ' ;
dir = MODE_ADD ;
}
* mbuf + + = ' l ' ;
2008-04-01 20:21:53 +00:00
len = rb_sprintf ( pbuf , " %d " , mode - > limit ) ;
2007-01-25 06:40:21 +00:00
pbuf + = len ;
}
if ( mode - > key [ 0 ] & & strcmp ( oldmode - > key , mode - > key ) )
{
if ( dir ! = MODE_ADD )
{
* mbuf + + = ' + ' ;
dir = MODE_ADD ;
}
* mbuf + + = ' k ' ;
2008-04-01 20:21:53 +00:00
len = rb_sprintf ( pbuf , " %s " , mode - > key ) ;
2007-01-25 06:40:21 +00:00
pbuf + = len ;
}
if ( mode - > join_num & & ( oldmode - > join_num ! = mode - > join_num | | oldmode - > join_time ! = mode - > join_time ) )
{
if ( dir ! = MODE_ADD )
{
* mbuf + + = ' + ' ;
dir = MODE_ADD ;
}
* mbuf + + = ' j ' ;
2008-04-01 20:21:53 +00:00
len = rb_sprintf ( pbuf , " %d:%d " , mode - > join_num , mode - > join_time ) ;
2007-01-25 06:40:21 +00:00
pbuf + = len ;
}
if ( mode - > forward [ 0 ] & & strcmp ( oldmode - > forward , mode - > forward ) & & ConfigChannel . use_forward )
{
if ( dir ! = MODE_ADD )
{
* mbuf + + = ' + ' ;
dir = MODE_ADD ;
}
* mbuf + + = ' f ' ;
2008-04-01 20:21:53 +00:00
len = rb_sprintf ( pbuf , " %s " , mode - > forward ) ;
2007-01-25 06:40:21 +00:00
pbuf + = len ;
}
* mbuf = ' \0 ' ;
}
/*
* remove_our_modes
*
* inputs -
* output -
* side effects -
*/
static void
remove_our_modes ( struct Channel * chptr , struct Client * source_p )
{
struct membership * msptr ;
2008-04-01 20:18:48 +00:00
rb_dlink_node * ptr ;
2007-01-25 06:40:21 +00:00
char lmodebuf [ MODEBUFLEN ] ;
char * lpara [ MAXMODEPARAMS ] ;
int count = 0 ;
int i ;
mbuf = lmodebuf ;
* mbuf + + = ' - ' ;
for ( i = 0 ; i < MAXMODEPARAMS ; i + + )
lpara [ i ] = NULL ;
2008-04-01 20:18:48 +00:00
RB_DLINK_FOREACH ( ptr , chptr - > members . head )
2007-01-25 06:40:21 +00:00
{
msptr = ptr - > data ;
if ( is_chanop ( msptr ) )
{
msptr - > flags & = ~ CHFL_CHANOP ;
lpara [ count + + ] = msptr - > client_p - > name ;
* mbuf + + = ' o ' ;
/* +ov, might not fit so check. */
if ( is_voiced ( msptr ) )
{
if ( count > = MAXMODEPARAMS )
{
* mbuf = ' \0 ' ;
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s MODE %s %s %s %s %s %s " ,
me . name , chptr - > chname ,
lmodebuf , lpara [ 0 ] , lpara [ 1 ] ,
lpara [ 2 ] , lpara [ 3 ] ) ;
/* preserve the initial '-' */
mbuf = lmodebuf ;
* mbuf + + = ' - ' ;
count = 0 ;
for ( i = 0 ; i < MAXMODEPARAMS ; i + + )
lpara [ i ] = NULL ;
}
msptr - > flags & = ~ CHFL_VOICE ;
lpara [ count + + ] = msptr - > client_p - > name ;
* mbuf + + = ' v ' ;
}
}
else if ( is_voiced ( msptr ) )
{
msptr - > flags & = ~ CHFL_VOICE ;
lpara [ count + + ] = msptr - > client_p - > name ;
* mbuf + + = ' v ' ;
}
else
continue ;
if ( count > = MAXMODEPARAMS )
{
* mbuf = ' \0 ' ;
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s MODE %s %s %s %s %s %s " ,
me . name , chptr - > chname , lmodebuf ,
lpara [ 0 ] , lpara [ 1 ] , lpara [ 2 ] , lpara [ 3 ] ) ;
mbuf = lmodebuf ;
* mbuf + + = ' - ' ;
count = 0 ;
for ( i = 0 ; i < MAXMODEPARAMS ; i + + )
lpara [ i ] = NULL ;
}
}
if ( count ! = 0 )
{
* mbuf = ' \0 ' ;
sendto_channel_local ( ALL_MEMBERS , chptr ,
" :%s MODE %s %s %s %s %s %s " ,
me . name , chptr - > chname , lmodebuf ,
EmptyString ( lpara [ 0 ] ) ? " " : lpara [ 0 ] ,
EmptyString ( lpara [ 1 ] ) ? " " : lpara [ 1 ] ,
EmptyString ( lpara [ 2 ] ) ? " " : lpara [ 2 ] ,
EmptyString ( lpara [ 3 ] ) ? " " : lpara [ 3 ] ) ;
}
}
2008-04-07 14:26:59 +00:00
/* remove_ban_list()
*
* inputs - channel , source , list to remove , char of mode , caps needed
* outputs -
* side effects - given list is removed , with modes issued to local clients
* and non - TS6 servers .
*/
static void
remove_ban_list ( struct Channel * chptr , struct Client * source_p ,
rb_dlink_list * list , char c , int cap , int mems )
{
static char lmodebuf [ BUFSIZE ] ;
static char lparabuf [ BUFSIZE ] ;
struct Ban * banptr ;
rb_dlink_node * ptr ;
rb_dlink_node * next_ptr ;
char * pbuf ;
int count = 0 ;
int cur_len , mlen , plen ;
pbuf = lparabuf ;
cur_len = mlen = rb_sprintf ( lmodebuf , " :%s MODE %s - " , source_p - > name , chptr - > chname ) ;
mbuf = lmodebuf + mlen ;
RB_DLINK_FOREACH_SAFE ( ptr , next_ptr , list - > head )
{
banptr = ptr - > data ;
/* trailing space, and the mode letter itself */
plen = strlen ( banptr - > banstr ) + 2 ;
if ( count > = MAXMODEPARAMS | | ( cur_len + plen ) > BUFSIZE - 4 )
{
/* remove trailing space */
* mbuf = ' \0 ' ;
* ( pbuf - 1 ) = ' \0 ' ;
sendto_channel_local ( mems , chptr , " %s %s " , lmodebuf , lparabuf ) ;
/* Tricky tricky. If we changed source_p to &me
* in ms_sjoin ( ) , this still won ' t send stuff
* where it should not be sent , because the
* real source_p does TS6 - - jilles */
sendto_server ( source_p , chptr , cap , CAP_TS6 , " %s %s " , lmodebuf , lparabuf ) ;
cur_len = mlen ;
mbuf = lmodebuf + mlen ;
pbuf = lparabuf ;
count = 0 ;
}
* mbuf + + = c ;
cur_len + = plen ;
pbuf + = rb_sprintf ( pbuf , " %s " , banptr - > banstr ) ;
count + + ;
free_ban ( banptr ) ;
}
* mbuf = ' \0 ' ;
* ( pbuf - 1 ) = ' \0 ' ;
sendto_channel_local ( mems , chptr , " %s %s " , lmodebuf , lparabuf ) ;
sendto_server ( source_p , chptr , cap , CAP_TS6 , " %s %s " , lmodebuf , lparabuf ) ;
list - > head = list - > tail = NULL ;
list - > length = 0 ;
}