{ "CSMSG_TOPIC_SET", "Topic is now '%s'." },
{ "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
{ "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
- { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
+ { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
{ "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
{ "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
{ "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
/* Channel configuration */
{ "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
+ { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
{ "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
{ "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
safestrncpy(bd->mask, mask, sizeof(bd->mask));
if(owner)
safestrncpy(bd->owner, owner, sizeof(bd->owner));
- bd->reason = reason ? strdup(reason) : NULL;
+ bd->reason = strdup(reason);
if(expires)
timeq_add(expires, expire_ban, bd);
static CHANSERV_FUNC(cmd_register)
{
- struct mod_chanmode *change;
struct handle_info *handle;
struct chanData *cData;
struct modeNode *mn;
return 0;
}
- if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
+ if(!IsHelping(user)
+ && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
{
reply("CSMSG_MUST_BE_OPPED", channel->name);
return 0;
scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
cData->modes = chanserv_conf.default_modes;
if(off_channel > 0)
- cData->modes.modes_set |= MODE_REGISTERED;
- change = mod_chanmode_dup(&cData->modes, 1);
- change->args[change->argc].mode = MODE_CHANOP;
- change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
- change->argc++;
- mod_chanmode_announce(chanserv, channel, change);
- mod_chanmode_free(change);
+ cData->modes.modes_set |= MODE_REGISTERED;
+ if (IsOffChannel(cData))
+ {
+ mod_chanmode_announce(chanserv, channel, &cData->modes);
+ }
+ else
+ {
+ struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
+ change->args[change->argc].mode = MODE_CHANOP;
+ change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
+ change->argc++;
+ mod_chanmode_announce(chanserv, channel, change);
+ mod_chanmode_free(change);
+ }
/* Initialize the channel's max user record. */
cData->max = channel->members.used;
static void
merge_data(struct chanData *source, struct chanData *target)
{
+ /* Use more recent visited and owner-transfer time; use older
+ * registered time. Bitwise or may_opchan. Use higher max.
+ * Do not touch last_refresh, ban count or user counts.
+ */
if(source->visited > target->visited)
target->visited = source->visited;
+ if(source->registered < target->registered)
+ target->registered = source->registered;
+ if(source->ownerTransfer > target->ownerTransfer)
+ target->ownerTransfer = source->ownerTransfer;
+ if(source->may_opchan)
+ target->may_opchan = 1;
+ if(source->max > target->max)
+ target->max = source->max;
}
static void
}
static int
-cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
+cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
{
struct userData *actor, *uData, *next;
char interval[INTERVALLEN];
{
next = uData->next;
- if((uData->seen > limit) || uData->present)
+ if((uData->seen > limit)
+ || uData->present
+ || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
continue;
if(((uData->access >= min_access) && (uData->access <= max_access))
{
unsigned long duration;
unsigned short min_level, max_level;
+ int vacation;
REQUIRE_PARAMS(3);
+ vacation = argc > 3 && !strcmp(argv[3], "vacation");
duration = ParseInterval(argv[2]);
if(duration < 60)
{
}
else if(!irccasecmp(argv[1], "users"))
{
- cmd_trim_users(user, channel, 0, 0, duration);
+ cmd_trim_users(user, channel, 0, 0, duration, vacation);
return 1;
}
else if(parse_level_range(&min_level, &max_level, argv[1]))
{
- cmd_trim_users(user, channel, min_level, max_level, duration);
+ cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
return 1;
}
else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
{
- cmd_trim_users(user, channel, min_level, min_level, duration);
+ cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
return 1;
}
else
}
static int
-bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
+bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
{
unsigned int ii;
if(IsService(mn->user))
continue;
- if(!user_matches_glob(mn->user, ban, 1))
+ if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
continue;
if(protect_user(mn->user, user, channel->channel_info))
{
for(ii = count = 0; ii < bans->used; ++ii)
{
- match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
+ match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
+ MATCH_USENICK | MATCH_VISIBLE);
if(match[ii])
count++;
}
if(!match[ii])
continue;
change->args[count].mode = MODE_REMOVE | MODE_BAN;
- change->args[count++].u.hostmask = bans->list[ii]->ban;
+ change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
}
+ assert(count == change->argc);
return change;
}
change = find_matching_bans(&channel->banlist, actee, mask);
if(change)
{
+ unsigned int ii;
+
modcmd_chanmode_announce(change);
+ for(ii = 0; ii < change->argc; ++ii)
+ free((char*)change->args[ii].u.hostmask);
mod_chanmode_free(change);
acted = 1;
}
while(ban)
{
if(actee)
- for( ; ban && !user_matches_glob(actee, ban->mask, 1);
+ for( ; ban && !user_matches_glob(actee, ban->mask,
+ MATCH_USENICK | MATCH_VISIBLE);
ban = ban->next);
else
for( ; ban && !match_ircglobs(mask, ban->mask);
for(ii=0; ii<channel->banlist.used; ii++)
{
change->args[ii].mode = MODE_REMOVE | MODE_BAN;
- change->args[ii].u.hostmask = channel->banlist.list[ii]->ban;
+ change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
}
modcmd_chanmode_announce(change);
+ for(ii = 0; ii < change->argc; ++ii)
+ free((char*)change->args[ii].u.hostmask);
mod_chanmode_free(change);
reply("CSMSG_BANS_REMOVED", channel->name);
return 1;
static CHANSERV_FUNC(cmd_open)
{
struct mod_chanmode *change;
+ unsigned int ii;
change = find_matching_bans(&channel->banlist, user, NULL);
if(!change)
change->modes_clear &= ~channel->channel_info->modes.modes_set;
modcmd_chanmode_announce(change);
reply("CSMSG_CHANNEL_OPENED", channel->name);
+ for(ii = 0; ii < change->argc; ++ii)
+ free((char*)change->args[ii].u.hostmask);
mod_chanmode_free(change);
return 1;
}
static CHANSERV_FUNC(cmd_bans)
{
+ struct userNode *search_u = NULL;
struct helpfile_table tbl;
- unsigned int matches = 0, timed = 0, ii;
+ unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
const char *msg_never, *triggered, *expires;
struct banData *ban, **bans;
- if(argc > 1)
- search = argv[1];
- else
+ if(argc < 2)
search = NULL;
+ else if(strchr(search = argv[1], '!'))
+ {
+ search = argv[1];
+ search_wilds = search[strcspn(search, "?*")];
+ }
+ else if(!(search_u = GetUserH(search)))
+ reply("MSG_NICK_UNKNOWN", search);
bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
for(ban = channel->channel_info->bans; ban; ban = ban->next)
{
- if(search && !match_ircglobs(search, ban->mask))
- continue;
+ if(search_u)
+ {
+ if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
+ continue;
+ }
+ else if(search)
+ {
+ if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
+ continue;
+ }
bans[matches++] = ban;
if(ban->expires)
timed = 1;
static CHANSERV_FUNC(cmd_mode)
{
+ struct userData *uData;
struct mod_chanmode *change;
-
+ short base_oplevel;
+
if(argc < 2)
{
change = &channel->channel_info->modes;
return 1;
}
- change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
+ uData = GetChannelUser(channel->channel_info, user->handle_info);
+ if (!uData)
+ base_oplevel = MAXOPLEVEL;
+ else if (uData->access >= UL_OWNER)
+ base_oplevel = 1;
+ else
+ base_oplevel = 1 + UL_OWNER - uData->access;
+ change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
if(!change)
{
reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
reply("CSMSG_CHANNEL_USERS", cData->userCount);
reply("CSMSG_CHANNEL_BANS", cData->banCount);
reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
- reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
privileged = IsStaff(user);
+ if(privileged)
+ reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
{
struct suspended *suspended = data;
struct chanNode *channel;
- struct mod_chanmode change;
if(!suspended->expires || (now < suspended->expires))
suspended->revoked = now;
channel = suspended->cData->channel;
suspended->cData->channel = channel;
suspended->cData->flags &= ~CHANNEL_SUSPENDED;
- mod_chanmode_init(&change);
- change.argc = 1;
- change.args[0].mode = MODE_CHANOP;
- change.args[0].u.member = AddChannelUser(chanserv, channel);
- mod_chanmode_announce(chanserv, channel, &change);
+ if(!IsOffChannel(suspended->cData))
+ {
+ struct mod_chanmode change;
+ mod_chanmode_init(&change);
+ change.argc = 1;
+ change.args[0].mode = MODE_CHANOP;
+ change.args[0].u.member = AddChannelUser(chanserv, channel);
+ mod_chanmode_announce(chanserv, channel, &change);
+ }
}
static CHANSERV_FUNC(cmd_csuspend)
{
memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
}
- else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
+ else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
{
reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
return 0;
new_owner = GetChannelAccess(cData, new_owner_hi);
if(!new_owner)
{
- reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
- return 0;
+ if(force)
+ {
+ new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
+ }
+ else
+ {
+ reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
+ return 0;
+ }
}
if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
{
unsigned int ii;
for(ii = 0; ii < channel->banlist.used; ii++)
{
- if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
+ if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
{
/* Riding a netburst. Naughty. */
KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
{
/* Not joining through a ban. */
for(bData = cData->bans;
- bData && !user_matches_glob(user, bData->mask, 1);
- bData = bData->next);
+ bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
+ bData = bData->next);
if(bData)
{
uData->present = 1;
}
}
+
+ /* If user joining normally (not during burst), apply op or voice,
+ * and send greeting/userinfo as appropriate.
+ */
if(!user->uplink->burst)
{
if(modes)
change.args[0].u.member = mNode;
mod_chanmode_announce(chanserv, channel, &change);
}
- if(greeting && !user->uplink->burst)
+ if(greeting)
send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
if(uData && info)
send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
|| IsSuspended(channel->channel_info))
continue;
for(jj = 0; jj < channel->banlist.used; ++jj)
- if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
+ if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
break;
if(jj < channel->banlist.used)
continue;
for(ban = channel->channel_info->bans; ban; ban = ban->next)
{
char kick_reason[MAXLEN];
- if(!user_matches_glob(user, ban->mask, 1))
+ if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
continue;
change.args[0].mode = MODE_BAN;
change.args[0].u.hostmask = ban->mask;
if(!bounce)
bounce = mod_chanmode_alloc(change->argc + 1 - ii);
bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
- bounce->args[bnc].u.hostmask = ban;
+ bounce->args[bnc].u.hostmask = strdup(ban);
bnc++;
send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
}
{
if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
mod_chanmode_announce(chanserv, channel, bounce);
+ for(ii = 0; ii < change->argc; ++ii)
+ if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
+ free((char*)bounce->args[ii].u.hostmask);
mod_chanmode_free(bounce);
}
}
continue;
/* Look for a matching ban already on the channel. */
for(jj = 0; jj < channel->banlist.used; ++jj)
- if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
+ if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
break;
/* Need not act if we found one. */
if(jj < channel->banlist.used)
/* Look for a matching ban in this channel. */
for(bData = channel->channel_info->bans; bData; bData = bData->next)
{
- if(!user_matches_glob(user, bData->mask, 1))
+ if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
continue;
change.args[0].u.hostmask = bData->mask;
mod_chanmode_announce(chanserv, channel, &change);
str = "+nt";
safestrncpy(mode_line, str, sizeof(mode_line));
ii = split_line(mode_line, 0, ArrayLength(modes), modes);
- if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
+ if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
+ && (change->argc < 2))
{
chanserv_conf.default_modes = *change;
mod_chanmode_free(change);
else
strlist = alloc_string_list(2);
chanserv_conf.old_ban_names = strlist;
- /* the variable itself is actually declared in proto-common.c; this is equally
- * parse issue. */
str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
off_channel = str ? atoi(str) : 0;
}
s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
+ if (!reason || !owner)
+ return;
set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
else
expires_time = 0;
- if(expires_time && (expires_time < now))
+ if(!reason || (expires_time && (expires_time < now)))
return;
bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
if(!IsSuspended(cData)
&& (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
&& (argc = split_line(str, 0, ArrayLength(argv), argv))
- && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
+ && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
cData->modes = *modes;
if(off_channel > 0)
cData->modes.modes_set |= MODE_REGISTERED;