#define KEY_LEVEL "level"
#define KEY_INFO "info"
#define KEY_SEEN "seen"
+#define KEY_ACCESSEXPIRY "accessexpiry"
+#define KEY_CLVLEXPIRY "clvlexpiry"
+#define KEY_LASTLEVEL "lastlevel"
/* Ban data */
#define KEY_OWNER "owner"
{ "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
/* User management */
+ { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." },
+ { "CSMSG_CLVL_EXPIRED", "Your CLVL access has expired in %s." },
{ "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
{ "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
{ "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
{ "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
{ "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
+ { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." },
{ "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
{ "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
{ "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
{ "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
{ "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
{ "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
- { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
+ { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
{ "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
{ "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
{ "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
}
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, time_t seen, const char *info, time_t accessexpiry)
{
struct userData *ud;
ud->seen = seen;
ud->access = access;
ud->info = info ? strdup(info) : NULL;
+ ud->accessexpiry = accessexpiry ? accessexpiry : 0;
+ ud->clvlexpiry = 0;
+ ud->lastaccess = 0;
ud->prev = NULL;
ud->next = channel->users;
static void unregister_channel(struct chanData *channel, const char *reason);
+static void
+chanserv_expire_tempuser(void *data)
+{
+ struct userData *uData = data;
+ char *handle;
+
+ if (data) {
+ handle = strdup(uData->handle->handle);
+ if (uData->accessexpiry > 0) {
+ if (uData->present) {
+ struct userNode *user, *next_un = NULL;
+ struct handle_info *hi;
+
+ hi = get_handle_info(handle);
+ for (user = hi->users; user; user = next_un) {
+ struct mod_chanmode *change;
+ struct modeNode *mn;
+ unsigned int count = 0;
+
+ send_message(user, chanserv, "CSMSG_AUTO_DELETED", chanserv->nick, uData->channel->channel->name);
+ if (!(mn = GetUserMode(uData->channel->channel, user)) || !(mn->modes & MODE_CHANOP)) {
+ next_un = user->next_authed;
+ continue;
+ }
+
+ change = mod_chanmode_alloc(2);
+ change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
+ change->args[count++].u.member = mn;
+
+ if (count) {
+ change->argc = count;
+ mod_chanmode_announce(chanserv, uData->channel->channel, change);
+ }
+ mod_chanmode_free(change);
+ next_un = user->next_authed;
+ }
+ }
+ del_channel_user(uData, 1);
+ }
+ }
+}
+
+static void
+chanserv_expire_tempclvl(void *data)
+{
+ struct userData *uData = data;
+ char *handle;
+
+ if (data) {
+ handle = strdup(uData->handle->handle);
+ if (uData->clvlexpiry > 0) {
+ int changemodes = 0;
+ unsigned int mode = 0;
+
+ if (((uData->lastaccess == UL_PEON) || (uData->lastaccess == UL_HALFOP)) && (uData->access >= UL_OP)) {
+ changemodes = 1;
+ mode = MODE_REMOVE | MODE_CHANOP;
+ } else if ((uData->lastaccess == UL_PEON) && (uData->access == UL_HALFOP)) {
+ changemodes = 1;
+ mode = MODE_REMOVE | MODE_HALFOP;
+ } else
+ changemodes = 0;
+
+ if (uData->present) {
+ struct userNode *user, *next_un = NULL;
+ struct handle_info *hi;
+
+ hi = get_handle_info(handle);
+ for (user = hi->users; user; user = next_un) {
+ struct mod_chanmode *change;
+ struct modeNode *mn;
+ unsigned int count = 0;
+
+ send_message(user, chanserv, "CSMSG_CLVL_EXPIRED", uData->channel->channel->name);
+ if (!(mn = GetUserMode(uData->channel->channel, user))) {
+ next_un = user->next_authed;
+ continue;
+ }
+
+ if (changemodes == 0) {
+ next_un = user->next_authed;
+ continue;
+ }
+
+ change = mod_chanmode_alloc(2);
+ change->args[count].mode = mode;
+ change->args[count++].u.member = mn;
+
+ if (count) {
+ change->argc = count;
+ mod_chanmode_announce(chanserv, uData->channel->channel, change);
+ }
+ mod_chanmode_free(change);
+ next_un = user->next_authed;
+ }
+ }
+
+ uData->access = uData->lastaccess;
+ uData->lastaccess = 0;
+ uData->clvlexpiry = 0;
+ }
+ }
+}
+
void
del_channel_user(struct userData *user, int do_gc)
{
channel->userCount--;
userCount--;
+ timeq_del(0, chanserv_expire_tempuser, user, TIMEQ_IGNORE_WHEN);
+ timeq_del(0, chanserv_expire_tempclvl, user, TIMEQ_IGNORE_WHEN);
+
if(user->prev)
user->prev->next = user->next;
else
}
else
{
- actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL);
+ actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL, 0);
scan_user_presence(actee, NULL);
}
del_adduser_pending(ap);
channel = AddChannel(argv[1], now, NULL, NULL, NULL);
cData = register_channel(channel, user->handle_info->handle);
- scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
+ scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL, 0), NULL);
cData->modes = chanserv_conf.default_modes;
if(off_channel > 0)
cData->modes.modes_set |= MODE_REGISTERED;
return 0;
}
- actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
+ time_t accessexpiry = 0;
+ unsigned int duration = 0;
+ if (argc > 3) {
+ if ((duration = ParseInterval(argv[3])))
+ accessexpiry = now + duration;
+ }
+
+ actee = add_channel_user(channel->channel_info, handle, access, 0, NULL, accessexpiry);
scan_user_presence(actee, NULL);
+
+ if (duration > 0)
+ timeq_add(accessexpiry, chanserv_expire_tempuser, actee);
+
reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access), access);
return 1;
}
return 0;
}
+ time_t clvlexpiry = 0;
+ unsigned int duration = 0;
+ if (argc > 3) {
+ if ((duration = ParseInterval(argv[3])))
+ clvlexpiry = now + duration;
+ }
+
+ if (duration > 0) {
+ if (victim->accessexpiry > 0) {
+ reply("CSMSG_NO_BUMP_EXPIRY");
+ return 0;
+ }
+
+ victim->clvlexpiry = clvlexpiry;
+ victim->lastaccess = victim->access;
+ timeq_add(clvlexpiry, chanserv_expire_tempclvl, victim);
+ }
+
victim->access = new_access;
reply("CSMSG_CHANGED_ACCESS", handle->handle, user_level_name_from_level(new_access), new_access, channel->name);
return 1;
if(argc > 2)
{
access = user_level_from_name(argv[1], UL_OWNER);
+ char *useraccess = user_level_name_from_level(victim->access);
if(!access)
{
reply("CSMSG_INVALID_ACCESS", argv[1]);
return 0;
}
- if(access != victim->access)
+ if(strcasecmp(argv[1], useraccess))
{
reply("CSMSG_INCORRECT_ACCESS", handle->handle, user_level_name_from_level(victim->access), argv[1]);
return 0;
static CHANSERV_FUNC(cmd_mdelcoowner)
{
- return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
+ return cmd_mdel_user(user, channel, UL_COOWNER, UL_OWNER-1, argv[1], cmd);
}
static CHANSERV_FUNC(cmd_mdelmanager)
{
- return cmd_mdel_user(user, channel, UL_MANAGER, UL_MANAGER, argv[1], cmd);
+ return cmd_mdel_user(user, channel, UL_MANAGER, UL_COOWNER-1, argv[1], cmd);
}
static CHANSERV_FUNC(cmd_mdelop)
{
- return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
+ return cmd_mdel_user(user, channel, UL_OP, UL_MANAGER-1, argv[1], cmd);
}
-static CHANSERV_FUNC(cmd_mdelpeon)
+static CHANSERV_FUNC(cmd_mdelhalfop)
{
- return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
+ return cmd_mdel_user(user, channel, UL_HALFOP, UL_OP-1, argv[1], cmd);
}
-static CHANSERV_FUNC(cmd_mdelhalfop)
+static CHANSERV_FUNC(cmd_mdelpeon)
{
- return cmd_mdel_user(user, channel, UL_HALFOP, UL_HALFOP, argv[1], cmd);
+ return cmd_mdel_user(user, channel, UL_PEON, UL_HALFOP-1, argv[1], cmd);
}
+static CHANSERV_FUNC(cmd_levels)
+{
+ struct helpfile_table tbl;
+ int ii = 0;
+
+ tbl.length = 6 + 1; // 6 levels
+ tbl.width = 4;
+ tbl.flags = 0;
+ tbl.contents = calloc(tbl.length,sizeof(tbl.contents[0]));
+ tbl.contents[0] = calloc(tbl.width,sizeof(tbl.contents[0][0]));
+ tbl.contents[0][0] = "Level";
+ tbl.contents[0][1] = "From";
+ tbl.contents[0][2] = "-";
+ tbl.contents[0][3] = "To";
+
+ tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
+ tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_OWNER));
+ tbl.contents[ii][1] = msnprintf(4, "%d", UL_OWNER);
+ tbl.contents[ii][2] = msnprintf(2, " ");
+ tbl.contents[ii][3] = msnprintf(1, "");
+
+ tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
+ tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_COOWNER));
+ tbl.contents[ii][1] = msnprintf(4, "%d", UL_COOWNER);
+ tbl.contents[ii][2] = msnprintf(2, "-");
+ tbl.contents[ii][3] = msnprintf(4, "%d", UL_OWNER-1);
+
+ tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
+ tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_MANAGER));
+ tbl.contents[ii][1] = msnprintf(4, "%d", UL_MANAGER);
+ tbl.contents[ii][2] = msnprintf(2, "-");
+ tbl.contents[ii][3] = msnprintf(4, "%d", UL_COOWNER-1);
+
+ tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
+ tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_OP));
+ tbl.contents[ii][1] = msnprintf(4, "%d", UL_OP);
+ tbl.contents[ii][2] = msnprintf(2, "-");
+ tbl.contents[ii][3] = msnprintf(4, "%d", UL_MANAGER-1);
+
+ tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
+ tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_HALFOP));
+ tbl.contents[ii][1] = msnprintf(4, "%d", UL_HALFOP);
+ tbl.contents[ii][2] = msnprintf(2, "-");
+ tbl.contents[ii][3] = msnprintf(4, "%d", UL_OP-1);
+
+ tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
+ tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_PEON));
+ tbl.contents[ii][1] = msnprintf(4, "%d", UL_PEON);
+ tbl.contents[ii][2] = msnprintf(2, "-");
+ tbl.contents[ii][3] = msnprintf(4, "%d", UL_HALFOP-1);
+
+ table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
+ return 0;
+
+/*
+ reply("CSMSG_LEVELS_HEADER");
+ reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
+ reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
+ reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
+ reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
+ reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
+ reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
+ reply("CSMSG_BAR");
+ */
+}
+
/* trim_lamers.. */
static int
cmd_trim_bans(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, unsigned long duration)
lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED)
- lData.table.width = 5; /* with level = 5 */
+ lData.table.width = 6; /* with level = 6 */
else
- lData.table.width = 4; /* without = 4 */
+ lData.table.width = 5; /* without = 5 */
ary = malloc(lData.table.width*sizeof(**lData.table.contents));
lData.table.contents[0] = ary;
if(user->handle_info) {
ary[i] = "Last Seen";
seen_index = i++;
ary[i++] = "Status";
+ ary[i++] = "Expiry";
for(matches = 1; matches < lData.table.length; ++matches)
{
struct userData *uData = lData.users[matches-1];
ary[i++] = "Vacation";
else
ary[i++] = "Normal";
+
+ if ((uData->accessexpiry > 0) || (uData->clvlexpiry > 0)) {
+ char delay[INTERVALLEN];
+ time_t diff;
+
+ if (uData->accessexpiry > 0) {
+ diff = uData->accessexpiry - now;
+ intervalString(delay, diff, user->handle_info);
+ } else {
+ diff = uData->clvlexpiry - now;
+ intervalString(delay, diff, user->handle_info);
+ }
+ ary[i++] = delay;
+ } else
+ ary[i++] = "Never";
}
+
send_list(&lData);
for(matches = 1; matches < lData.table.length; ++matches)
{
{
if(force)
{
- new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
+ new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL, 0);
}
else
{
SetChannelTopic(channel, chanserv, chanserv, channel->channel_info->topic, 1);
}
+int
+trace_check_bans(struct userNode *user, struct chanNode *chan)
+{
+ struct banData *bData;
+ struct mod_chanmode *change;
+
+ change = find_matching_bans(&chan->banlist, user, NULL);
+ if (change)
+ return 1;
+
+ /* lamer list */
+ if (chan->channel_info) {
+ for(bData = chan->channel_info->bans; bData; bData = bData->next) {
+
+ if(!user_matches_glob(user, bData->mask, MATCH_USENICK))
+ continue;
+
+ if(bData)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+check_bans(struct userNode *user, const char *channel)
+{
+ struct chanNode *chan;
+ struct mod_chanmode change;
+ struct chanData *cData;
+ struct banData *bData;
+
+ if (!(chan = GetChannel(channel)))
+ return 0;
+
+ if(!(cData = chan->channel_info))
+ return 0;
+
+ mod_chanmode_init(&change);
+ change.argc = 1;
+
+ if(chan->banlist.used < MAXBANS)
+ {
+ /* Not joining through a ban. */
+ for(bData = cData->bans;
+ bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
+ bData = bData->next);
+
+ if(bData)
+ {
+ char kick_reason[MAXLEN];
+ sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
+
+ bData->triggered = now;
+ if(bData != cData->bans)
+ {
+ /* Shuffle the ban to the head of the list. */
+ if(bData->next)
+ bData->next->prev = bData->prev;
+ if(bData->prev)
+ bData->prev->next = bData->next;
+
+ bData->prev = NULL;
+ bData->next = cData->bans;
+
+ if(cData->bans)
+ cData->bans->prev = bData;
+
+ cData->bans = bData;
+ }
+
+ change.args[0].mode = MODE_BAN;
+ change.args[0].u.hostmask = bData->mask;
+ mod_chanmode_announce(chanserv, chan, &change);
+ KickChannelUser(user, chan, chanserv, kick_reason);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
/* Welcome to my worst nightmare. Warning: Read (or modify)
the code below at your own risk. */
static int
}
-
mod_chanmode_init(&change);
change.argc = 1;
{
struct handle_info *handle;
struct userData *uData;
- char *seen, *inf, *flags, *expires;
+ char *seen, *inf, *flags, *expires, *accessexpiry, *clvlexpiry, *lstacc;
time_t last_seen;
- unsigned short access;
+ unsigned short access, lastaccess = 0;
if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
{
last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
+ accessexpiry = database_get_data(rd->d.object, KEY_ACCESSEXPIRY, RECDB_QSTRING);
+ clvlexpiry = database_get_data(rd->d.object, KEY_CLVLEXPIRY, RECDB_QSTRING);
+ lstacc = database_get_data(rd->d.object, KEY_LASTLEVEL, RECDB_QSTRING);
+ lastaccess = lstacc ? atoi(lstacc) : 0;
+
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, last_seen, inf, 0);
uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
uData->expires = expires ? strtoul(expires, NULL, 0) : 0;
+ uData->accessexpiry = accessexpiry ? strtoul(accessexpiry, NULL, 0) : 0;
+ if (uData->accessexpiry > 0)
+ timeq_add(uData->accessexpiry, chanserv_expire_tempuser, uData);
+
+ uData->clvlexpiry = clvlexpiry ? strtoul(clvlexpiry, NULL, 0) : 0;
+ if (uData->clvlexpiry > 0)
+ timeq_add(uData->clvlexpiry, chanserv_expire_tempclvl, uData);
+
+ uData->lastaccess = lastaccess;
+
if((uData->flags & USER_SUSPENDED) && uData->expires)
{
if(uData->expires > now)
saxdb_start_record(ctx, uData->handle->handle, 0);
saxdb_write_int(ctx, KEY_LEVEL, uData->access);
saxdb_write_int(ctx, KEY_SEEN, uData->seen);
+ saxdb_write_int(ctx, KEY_ACCESSEXPIRY, uData->accessexpiry);
+ saxdb_write_int(ctx, KEY_CLVLEXPIRY, uData->clvlexpiry);
+ saxdb_write_int(ctx, KEY_LASTLEVEL, uData->lastaccess);
if(uData->flags)
saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
if(uData->expires)
DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
+ DEFINE_COMMAND(levels, 1, 0, NULL);
+
DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
reg_exit_func(chanserv_db_cleanup);
message_register_table(msgtab);
}
+