/* chanserv.c - Channel service bot
- * Copyright 2000-2006 srvx Development Team
+ * Copyright 2000-2007 srvx Development Team
*
* This file is part of srvx.
*
#include "global.h"
#include "modcmd.h"
#include "opserv.h" /* for opserv_bad_channel() */
+#include "nickserv.h" /* for oper_outranks() */
#include "saxdb.h"
#include "timeq.h"
{ "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
{ "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
{ "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
+ { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
{ "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
/* Removing yourself from a channel. */
{ "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
{ "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
{ "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
- { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
+ { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
{ "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
{ "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
{ "CSMSG_PEEK_INFO", "$b%s$b Status:" },
{ "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
{ "CSMSG_PEEK_MODES", "$bModes: $b%s" },
- { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
+ { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
{ "CSMSG_PEEK_OPS", "$bOps:$b" },
{ "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
/* User settings */
{ "CSMSG_USER_OPTIONS", "User Options:" },
- { "CSMSG_USER_PROTECTED", "That user is protected." },
+ { "CSMSG_USER_PROTECTED_2", "That user is protected." },
/* Toys */
{ "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
return 0; }
DECLARE_LIST(dnrList, struct do_not_register *);
-DEFINE_LIST(dnrList, struct do_not_register *);
+DEFINE_LIST(dnrList, struct do_not_register *)
static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
unsigned long channel_expire_frequency;
unsigned long dnr_expire_frequency;
- long info_delay;
- unsigned int adjust_delay;
- long channel_expire_delay;
+ unsigned long info_delay;
+ unsigned long adjust_delay;
+ unsigned long channel_expire_delay;
unsigned int nodelete_level;
unsigned int adjust_threshold;
}
static void
-chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
+chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
{
unsigned int eflags, argc;
char *argv[4];
}
static struct userData*
-add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
+add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
{
struct userData *ud;
- if(access > UL_OWNER)
+ if(access_level > UL_OWNER)
return NULL;
ud = calloc(1, sizeof(*ud));
ud->channel = channel;
ud->handle = handle;
ud->seen = seen;
- ud->access = access;
+ ud->access = access_level;
ud->info = info ? strdup(info) : NULL;
ud->prev = NULL;
static void expire_ban(void *data);
static struct banData*
-add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
+add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
{
struct banData *bd;
unsigned int ii, l1, l2;
/* Make sure there are no high-ranking users still in the channel. */
for(user=channel->users; user; user=user->next)
- if(user->present && (user->access >= UL_PRESENT))
+ if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
break;
if(user)
continue;
static void
expire_dnrs(UNUSED_ARG(void *data))
{
- dict_iterator_t it;
+ dict_iterator_t it, next;
struct do_not_register *dnr;
- for(it = dict_first(handle_dnrs); it; it = iter_next(it))
+ for(it = dict_first(handle_dnrs); it; it = next)
{
dnr = iter_data(it);
- if(!dnr->expires || dnr->expires > now)
- continue;
- dict_remove(handle_dnrs, dnr->chan_name + 1);
+ next = iter_next(it);
+ if(dnr->expires && dnr->expires <= now)
+ dict_remove(handle_dnrs, dnr->chan_name + 1);
}
- for(it = dict_first(plain_dnrs); it; it = iter_next(it))
+ for(it = dict_first(plain_dnrs); it; it = next)
{
dnr = iter_data(it);
- if(!dnr->expires || dnr->expires > now)
- continue;
- dict_remove(plain_dnrs, dnr->chan_name);
+ next = iter_next(it);
+ if(dnr->expires && dnr->expires <= now)
+ dict_remove(plain_dnrs, dnr->chan_name + 1);
}
- for(it = dict_first(mask_dnrs); it; it = iter_next(it))
+ for(it = dict_first(mask_dnrs); it; it = next)
{
dnr = iter_data(it);
- if(!dnr->expires || dnr->expires > now)
- continue;
- dict_remove(mask_dnrs, dnr->chan_name);
+ next = iter_next(it);
+ if(dnr->expires && dnr->expires <= now)
+ dict_remove(mask_dnrs, dnr->chan_name + 1);
}
if(chanserv_conf.dnr_expire_frequency)
}
static struct do_not_register *
-chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, const char *reason)
+chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
{
struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
struct userNode *user;
char buf1[INTERVALLEN];
char buf2[INTERVALLEN];
+ time_t feh;
user = extra;
if(dnr->set)
- strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&dnr->set));
+ {
+ feh = dnr->set;
+ strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
+ }
if(dnr->expires)
{
- strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&dnr->expires));
+ feh = dnr->expires;
+ strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
}
else if(dnr->set)
static CHANSERV_FUNC(cmd_noregister)
{
const char *target;
- time_t expiry, duration;
+ const char *reason;
+ unsigned long expiry, duration;
unsigned int matches;
if(argc < 2)
return 0;
}
- const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
+ reason = unsplit_string(argv + 3, argc - 3, NULL);
if((*target == '*') && !get_handle_info(target + 1))
{
reply("MSG_HANDLE_UNKNOWN", target + 1);
char *chan_mask;
char *setter_mask;
char *reason_mask;
- time_t min_set, max_set;
- time_t min_expires, max_expires;
+ unsigned long min_set, max_set;
+ unsigned long min_expires, max_expires;
unsigned int limit;
};
{
return !((dnr->set < search->min_set)
|| (dnr->set > search->max_set)
- || (dnr->expires && ((dnr->expires < search->min_expires)
- || (dnr->expires > search->max_expires)))
+ || (dnr->expires < search->min_expires)
+ || (search->max_expires
+ && ((dnr->expires == 0)
+ || (dnr->expires > search->max_expires)))
|| (search->chan_mask
&& !match_ircglob(dnr->chan_name, search->chan_mask))
|| (search->setter_mask
discrim->setter_mask = NULL;
discrim->reason_mask = NULL;
discrim->max_set = INT_MAX;
- discrim->max_expires = INT_MAX;
discrim->limit = 50;
for(ii=0; ii<argc; ++ii)
target->channel_info->channel = target;
channel->channel_info = NULL;
+ /* Check whether users are present in the new channel. */
+ for(uData = target->channel_info->users; uData; uData = uData->next)
+ scan_user_presence(uData, NULL);
+
reply("CSMSG_MOVE_SUCCESS", target->name);
sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
choice = (suData->seen > tuData->seen) ? suData : tuData;
else /* Otherwise, keep the higher access level. */
choice = (suData->access > tuData->access) ? suData : tuData;
+ /* Use the later seen time. */
+ if(suData->seen < tuData->seen)
+ suData->seen = tuData->seen;
+ else
+ tuData->seen = suData->seen;
/* Remove the user that wasn't picked. */
if(choice == tuData)
/* Update the user counts for the target channel; the
source counts are left alone. */
target->userCount++;
+
+ /* Check whether the user is in the target channel. */
+ scan_user_presence(suData, NULL);
}
/* Possible to assert (source->users == NULL) here. */
change.argc = 1;
change.args[0].mode = MODE_CHANOP;
change.args[0].u.member = GetUserMode(channel, chanserv);
+ if(!change.args[0].u.member)
+ {
+ reply("CSMSG_OUT_OF_CHANNEL", channel->name);
+ return 0;
+ }
mod_chanmode_announce(chanserv, channel, &change);
reply("CSMSG_OPCHAN_DONE", channel->name);
return 1;
struct userData *actee;
struct userData *actor, *real_actor;
struct handle_info *handle;
- unsigned short access, override = 0;
+ unsigned short access_level, override = 0;
REQUIRE_PARAMS(3);
return 0;
}
- access = user_level_from_name(argv[2], UL_OWNER);
- if(!access)
+ access_level = user_level_from_name(argv[2], UL_OWNER);
+ if(!access_level)
{
reply("CSMSG_INVALID_ACCESS", argv[2]);
return 0;
actor = GetChannelUser(channel->channel_info, user->handle_info);
real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
- if(actor->access <= access)
+ if(actor->access <= access_level)
{
reply("CSMSG_NO_BUMP_ACCESS");
return 0;
}
/* Trying to add someone with equal/more access? */
- if (!real_actor || real_actor->access <= access)
+ if (!real_actor || real_actor->access <= access_level)
override = CMD_LOG_OVERRIDE;
if(!(handle = modcmd_get_handle_info(user, argv[1])))
return 0;
}
- actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
+ actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
scan_user_presence(actee, NULL);
- reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
+ reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
return 1 | override;
}
struct handle_info *handle;
struct userData *victim;
struct userData *actor, *real_actor;
- unsigned short access, override = 0;
+ unsigned short access_level, override = 0;
char *chan_name;
REQUIRE_PARAMS(2);
if(argc > 2)
{
- access = user_level_from_name(argv[1], UL_OWNER);
- if(!access)
+ access_level = user_level_from_name(argv[1], UL_OWNER);
+ if(!access_level)
{
reply("CSMSG_INVALID_ACCESS", argv[1]);
return 0;
}
- if(access != victim->access)
+ if(access_level != victim->access)
{
reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
return 0;
}
else
{
- access = victim->access;
+ access_level = victim->access;
}
if((actor->access <= victim->access) && !IsHelping(user))
chan_name = strdup(channel->name);
del_channel_user(victim, 1);
- reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
+ reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
free(chan_name);
return 1 | override;
}
return 0;
}
- if((actor->access <= max_access) && !IsHelping(user))
+ if(actor->access <= max_access)
{
reply("CSMSG_NO_ACCESS");
return 0;
struct banData *bData, *next;
char interval[INTERVALLEN];
unsigned int count;
- time_t limit;
+ unsigned long limit;
count = 0;
limit = now - duration;
struct userData *actor, *uData, *next;
char interval[INTERVALLEN];
unsigned int count;
- time_t limit;
+ unsigned long limit;
actor = GetChannelAccess(channel->channel_info, user->handle_info);
if(min_access > max_access)
else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
{
struct handle_info *hi;
+ extern const char *titlehost_suffix;
char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
const char *accountname = argv[1] + 1;
return 0;
}
- snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
+ snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
victims = alloca(sizeof(victims[0]) * channel->members.used);
if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
/* If the ban matches an existing one exactly,
extend the expiration time if the provided
duration is longer. */
- if(duration && ((time_t)(now + duration) > bData->expires))
+ if(duration && (now + duration > bData->expires))
{
bData->expires = now + duration;
reset = 1;
if(argc < 2)
target_handle = user->handle_info;
- else if(!IsHelping(user))
+ else if(!IsStaff(user))
{
reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
return 0;
else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
return 0;
+ if(!oper_outranks(user, target_handle))
+ return 0;
+
if(!target_handle->channels)
{
reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
ary[3] = "Status";
for(matches = 1; matches < lData.table.length; ++matches)
{
- struct userData *uData = lData.users[matches-1];
char seen[INTERVALLEN];
+ uData = lData.users[matches-1];
ary = malloc(lData.table.width*sizeof(**lData.table.contents));
lData.table.contents[matches] = ary;
ary[0] = strtab(uData->access);
ary[3] = "Suspended";
else if(HANDLE_FLAGGED(uData->handle, FROZEN))
ary[3] = "Vacation";
+ else if(HANDLE_FLAGGED(uData->handle, BOT))
+ ary[3] = "Bot";
else
ary[3] = "Normal";
}
struct userData *uData;
struct mod_chanmode *change;
short base_oplevel;
+ char fmt[MAXLEN];
if(argc < 2)
{
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);
+ change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
if(!change)
{
reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
}
modcmd_chanmode_announce(change);
+ mod_chanmode_format(change, fmt);
mod_chanmode_free(change);
- reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
+ reply("CSMSG_MODES_SET", fmt);
return 1;
}
static CHANSERV_FUNC(cmd_netinfo)
{
- extern time_t boot_time;
+ extern unsigned long boot_time;
extern unsigned long burst_length;
char interval[INTERVALLEN];
char modes[MODELEN];
unsigned int n;
struct helpfile_table table;
+ int opcount = 0, voicecount = 0, srvcount = 0;
irc_make_chanmode(channel, modes);
reply("CSMSG_PEEK_INFO", channel->name);
reply("CSMSG_PEEK_TOPIC", channel->topic);
reply("CSMSG_PEEK_MODES", modes);
- reply("CSMSG_PEEK_USERS", channel->members.used);
table.length = 0;
table.width = 1;
for(n = 0; n < channel->members.used; n++)
{
mn = channel->members.list[n];
+ if(IsLocal(mn->user))
+ srvcount++;
+ else if(mn->modes & MODE_CHANOP)
+ opcount++;
+ else if(mn->modes & MODE_VOICE)
+ voicecount++;
+
if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
continue;
table.contents[table.length] = alloca(sizeof(**table.contents));
table.contents[table.length][0] = mn->user->nick;
table.length++;
}
+
+ reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
+ (channel->members.used - opcount - voicecount - srvcount));
+
if(table.length)
{
reply("CSMSG_PEEK_OPS");
msg = unsplit_string(argv + 1, argc - 1, NULL);
send_channel_message(channel, cmd->parent->bot, "%s", msg);
}
+ else if(*argv[1] == '*' && argv[1][1] != '\0')
+ {
+ struct handle_info *hi;
+ struct userNode *authed;
+
+ REQUIRE_PARAMS(3);
+ msg = unsplit_string(argv + 2, argc - 2, NULL);
+
+ if (!(hi = get_handle_info(argv[1] + 1)))
+ {
+ reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
+ return 0;
+ }
+
+ for (authed = hi->users; authed; authed = authed->next_authed)
+ send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
+ }
else if(GetUserH(argv[1]))
{
REQUIRE_PARAMS(3);
msg = unsplit_string(argv + 1, argc - 1, NULL);
send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
}
+ else if(*argv[1] == '*' && argv[1][1] != '\0')
+ {
+ struct handle_info *hi;
+ struct userNode *authed;
+
+ REQUIRE_PARAMS(3);
+ msg = unsplit_string(argv + 2, argc - 2, NULL);
+
+ if (!(hi = get_handle_info(argv[1] + 1)))
+ {
+ reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
+ return 0;
+ }
+
+ for (authed = hi->users; authed; authed = authed->next_authed)
+ send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
+ }
else if(GetUserH(argv[1]))
{
msg = unsplit_string(argv + 2, argc - 2, NULL);
{
struct suspended *suspended = data;
struct chanNode *channel;
+ unsigned int ii;
+ /* Update the channel registration data structure. */
if(!suspended->expires || (now < suspended->expires))
suspended->revoked = now;
channel = suspended->cData->channel;
suspended->cData->channel = channel;
suspended->cData->flags &= ~CHANNEL_SUSPENDED;
+
+ /* If appropriate, re-join ChanServ to the channel. */
if(!IsOffChannel(suspended->cData))
{
struct mod_chanmode change;
change.args[0].u.member = AddChannelUser(chanserv, channel);
mod_chanmode_announce(chanserv, channel, &change);
}
+
+ /* Mark everyone currently in the channel as present. */
+ for(ii = 0; ii < channel->members.used; ++ii)
+ {
+ struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
+ if(uData)
+ {
+ uData->present = 1;
+ uData->seen = now;
+ }
+ }
}
static CHANSERV_FUNC(cmd_csuspend)
{
struct suspended *suspended;
char reason[MAXLEN];
- time_t expiry, duration;
+ unsigned long expiry, duration;
struct userData *uData;
REQUIRE_PARAMS(3);
char *name;
char *registrar;
- time_t unvisited;
- time_t registered;
+ unsigned long unvisited;
+ unsigned long registered;
unsigned long flags;
unsigned int limit;
static CHANSERV_FUNC(cmd_unvisited)
{
struct chanData *cData;
- time_t interval = chanserv_conf.channel_expire_delay;
+ unsigned long interval = chanserv_conf.channel_expire_delay;
char buffer[INTERVALLEN];
unsigned int limit = 25, matches = 0;
static MODCMD_FUNC(chan_opt_modes)
{
struct mod_chanmode *new_modes;
- char modes[MODELEN];
+ char modes[MAXLEN];
if(argc > 1)
{
{
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, 0)))
+ else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
{
reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
return 0;
channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
{
struct chanData *cData = channel->channel_info;
- int count = charOptions[option].count, index;
+ int count = charOptions[option].count, idx;
if(argc > 1)
{
- index = atoi(argv[1]);
+ idx = atoi(argv[1]);
- if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
+ if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
{
- reply("CSMSG_INVALID_NUMERIC", index);
+ reply("CSMSG_INVALID_NUMERIC", idx);
/* Show possible values. */
- for(index = 0; index < count; index++)
- reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
+ for(idx = 0; idx < count; idx++)
+ reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
return 0;
}
- cData->chOpts[option] = charOptions[option].values[index].value;
+ cData->chOpts[option] = charOptions[option].values[idx].value;
}
else
{
/* Find current option value. */
find_value:
- for(index = 0;
- (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
- index++);
- if(index == count)
+ for(idx = 0;
+ (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
+ idx++);
+ if(idx == count)
{
/* Somehow, the option value is corrupt; reset it to the default. */
cData->chOpts[option] = charOptions[option].default_value;
}
}
- reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
+ reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
return 1;
}
static MODCMD_FUNC(user_opt_autoinvite)
{
+ if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
+ {
+ reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
+ }
return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
}
}
curr_user = owner;
}
- else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
+ else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
{
char delay[INTERVALLEN];
intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
static CHANSERV_FUNC(cmd_suspend)
{
struct handle_info *hi;
- struct userData *self, *real_self, *target;
+ struct userData *actor, *real_actor, *target;
unsigned int override = 0;
REQUIRE_PARAMS(2);
if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
- self = GetChannelUser(channel->channel_info, user->handle_info);
- real_self = GetChannelAccess(channel->channel_info, user->handle_info);
+ actor = GetChannelUser(channel->channel_info, user->handle_info);
+ real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
{
reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
return 0;
}
- if(target->access >= self->access)
+ if(target->access >= actor->access)
{
reply("MSG_USER_OUTRANKED", hi->handle);
return 0;
target->present = 0;
target->seen = now;
}
- if(!real_self || target->access >= real_self->access)
+ if(!real_actor || target->access >= real_actor->access)
override = CMD_LOG_OVERRIDE;
target->flags |= USER_SUSPENDED;
reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
static CHANSERV_FUNC(cmd_unsuspend)
{
struct handle_info *hi;
- struct userData *self, *real_self, *target;
+ struct userData *actor, *real_actor, *target;
unsigned int override = 0;
REQUIRE_PARAMS(2);
if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
- self = GetChannelUser(channel->channel_info, user->handle_info);
- real_self = GetChannelAccess(channel->channel_info, user->handle_info);
+ actor = GetChannelUser(channel->channel_info, user->handle_info);
+ real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
{
reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
return 0;
}
- if(target->access >= self->access)
+ if(target->access >= actor->access)
{
reply("MSG_USER_OUTRANKED", hi->handle);
return 0;
reply("CSMSG_NOT_SUSPENDED", hi->handle);
return 0;
}
- if(!real_self || target->access >= real_self->access)
+ if(!real_actor || target->access >= real_actor->access)
override = CMD_LOG_OVERRIDE;
target->flags &= ~USER_SUSPENDED;
scan_user_presence(target, NULL);
struct handle_info *hi;
struct userData *target;
const char *confirm_string;
- unsigned short access;
+ unsigned short access_level;
char *channel_name;
hi = user->handle_info;
reply("CSMSG_CONFIRM_DELETEME", confirm_string);
return 0;
}
- access = target->access;
+ access_level = target->access;
channel_name = strdup(channel->name);
del_channel_user(target, 1);
- reply("CSMSG_DELETED_YOU", access, channel_name);
+ reply("CSMSG_DELETED_YOU", access_level, channel_name);
free(channel_name);
return 1;
}
static void
chanserv_refresh_topics(UNUSED_ARG(void *data))
{
- unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
+ unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
struct chanData *cData;
char opt;
}
}
- /* ChanServ will not modify the limits in join-flooded channels.
- It will also skip DynLimit processing when the user (or srvx)
- is bursting in, because there are likely more incoming. */
+ /* ChanServ will not modify the limits in join-flooded channels,
+ or when there are enough slots left below the limit. */
if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
- && !user->uplink->burst
&& !channel->join_flooded
&& (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
{
else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
modes |= MODE_VOICE;
}
- if(uData->access >= UL_PRESENT)
+ if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
cData->visited = now;
if(cData->user_greeting)
greeting = cData->user_greeting;
}
if(greeting)
send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
- if(uData && info)
+ if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
}
return 0;
continue;
}
- if(channel->access >= UL_PRESENT)
+ if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
channel->channel->visited = now;
if(IsUserAutoOp(channel))
for(ii = 0; ii < user->channels.used; ++ii)
{
- struct chanNode *channel = user->channels.list[ii]->channel;
+ struct chanNode *chan = user->channels.list[ii]->channel;
struct banData *ban;
if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
- || !channel->channel_info
- || IsSuspended(channel->channel_info))
+ || !chan->channel_info
+ || IsSuspended(chan->channel_info))
continue;
- for(jj = 0; jj < channel->banlist.used; ++jj)
- if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
+ for(jj = 0; jj < chan->banlist.used; ++jj)
+ if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
break;
- if(jj < channel->banlist.used)
+ if(jj < chan->banlist.used)
continue;
- for(ban = channel->channel_info->bans; ban; ban = ban->next)
+ for(ban = chan->channel_info->bans; ban; ban = ban->next)
{
char kick_reason[MAXLEN];
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;
- mod_chanmode_announce(chanserv, channel, &change);
+ mod_chanmode_announce(chanserv, chan, &change);
sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
- KickChannelUser(user, channel, chanserv, kick_reason);
+ KickChannelUser(user, chan, chanserv, kick_reason);
ban->triggered = now;
break;
}
{
scan_user_presence(uData, mn->user);
uData->seen = now;
- if (uData->access >= UL_PRESENT)
+ if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
cData->visited = now;
}
if(IsHelping(mn->user) && IsSupportHelper(mn->user))
{
- unsigned int ii, jj;
- for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
- {
- for(jj = 0; jj < mn->user->channels.used; ++jj)
- if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
- break;
- if(jj < mn->user->channels.used)
+ unsigned int ii;
+ for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
+ struct chanNode *channel;
+ struct userNode *exclude;
+ /* When looking at the channel that is being /part'ed, we
+ * have to skip over the client that is leaving. For
+ * other channels, we must not do that.
+ */
+ channel = chanserv_conf.support_channels.list[ii];
+ exclude = (channel == mn->channel) ? mn->user : NULL;
+ if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
break;
}
if(ii == chanserv_conf.support_channels.used)
if(protect_user(victim, kicker, channel->channel_info))
{
- const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
+ const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
KickChannelUser(kicker, channel, chanserv, reason);
}
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, 0))
+ if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
&& (change->argc < 2))
{
chanserv_conf.default_modes = *change;
/* delimiter */
NULL
};
- unsigned int ii;
strlist = alloc_string_list(ArrayLength(list)-1);
for(ii=0; list[ii]; ii++)
string_list_append(strlist, strdup(list[ii]));
struct handle_info *handle;
struct userData *uData;
char *seen, *inf, *flags;
- time_t last_seen;
- unsigned short access;
+ unsigned long last_seen;
+ unsigned short access_level;
if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
{
return;
}
- access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
- if(access > UL_OWNER)
+ access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
+ if(access_level > UL_OWNER)
{
log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
return;
inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
- last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
+ last_seen = seen ? strtoul(seen, NULL, 0) : now;
flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
handle = get_handle_info(key);
if(!handle)
return;
}
- uData = add_channel_user(chan, handle, access, last_seen, inf);
+ uData = add_channel_user(chan, handle, access_level, last_seen, inf);
uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
}
{
struct banData *bData;
char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
- time_t set_time, triggered_time, expires_time;
+ unsigned long set_time, triggered_time, expires_time;
if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
{
if (!reason || !owner)
return;
- set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
- triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
+ set_time = set ? strtoul(set, NULL, 0) : now;
+ triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
if(s_expires)
- expires_time = (time_t)strtoul(s_expires, NULL, 0);
+ expires_time = strtoul(s_expires, NULL, 0);
else if(s_duration)
expires_time = set_time + atoi(s_duration);
else
dict_t previous;
str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
- suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
+ suspended->expires = str ? strtoul(str, NULL, 0) : 0;
str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
- suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
+ suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
- suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
+ suspended->issued = str ? strtoul(str, NULL, 0) : 0;
suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
}
str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
- cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
+ cData->registered = str ? strtoul(str, NULL, 0) : now;
str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
- cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
+ cData->visited = str ? strtoul(str, NULL, 0) : now;
str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
- cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
+ cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
cData->max = str ? atoi(str) : 0;
str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
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, 0))) {
+ && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
cData->modes = *modes;
if(off_channel > 0)
cData->modes.modes_set |= MODE_REGISTERED;
{
const char *setter, *reason, *str;
struct do_not_register *dnr;
- time_t expiry;
+ unsigned long expiry;
setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
if(!setter)
return;
}
str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
- expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
+ expiry = str ? strtoul(str, NULL, 0) : 0;
if(expiry && expiry <= now)
return;
dnr = chanserv_add_dnr(key, setter, expiry, reason);
saxdb_start_record(ctx, KEY_USERS, 1);
for(; uData; uData = uData->next)
{
- if((uData->access >= UL_PRESENT) && uData->present)
+ if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
high_present = 1;
saxdb_start_record(ctx, uData->handle->handle, 0);
saxdb_write_int(ctx, KEY_LEVEL, uData->access);
}
}
-#define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
+#if defined(GCC_VARMACROS)
+# define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
+#elif defined(C99_VARMACROS)
+# define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
+#endif
#define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
#define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
- DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
- DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
+ DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
+ DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
/* Channel options */
if(chanserv_conf.refresh_period)
{
- time_t next_refresh;
+ unsigned long next_refresh;
next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
timeq_add(next_refresh, chanserv_refresh_topics, NULL);
}