-#define KEY_SUPPORT_CHANNEL "support_channel"
-#define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
-#define KEY_DB_BACKUP_FREQ "db_backup_freq"
-#define KEY_INFO_DELAY "info_delay"
-#define KEY_MAX_GREETLEN "max_greetlen"
-#define KEY_ADJUST_THRESHOLD "adjust_threshold"
-#define KEY_ADJUST_DELAY "adjust_delay"
-#define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
-#define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
-#define KEY_MAX_CHAN_USERS "max_chan_users"
-#define KEY_MAX_CHAN_BANS "max_chan_bans"
-#define KEY_NICK "nick"
-#define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
-#define KEY_8BALL_RESPONSES "8ball"
-#define KEY_OLD_BAN_NAMES "old_ban_names"
-#define KEY_REFRESH_PERIOD "refresh_period"
+#define KEY_SUPPORT_CHANNEL "support_channel"
+#define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
+#define KEY_DB_BACKUP_FREQ "db_backup_freq"
+#define KEY_INFO_DELAY "info_delay"
+#define KEY_MAX_GREETLEN "max_greetlen"
+#define KEY_ADJUST_THRESHOLD "adjust_threshold"
+#define KEY_ADJUST_DELAY "adjust_delay"
+#define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
+#define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
+#define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
+#define KEY_MAX_CHAN_USERS "max_chan_users"
+#define KEY_MAX_CHAN_BANS "max_chan_bans"
+#define KEY_NICK "nick"
+#define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
+#define KEY_8BALL_RESPONSES "8ball"
+#define KEY_OLD_BAN_NAMES "old_ban_names"
+#define KEY_REFRESH_PERIOD "refresh_period"
#define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
#define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
#define KEY_MAX_OWNED "max_owned"
#define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
#define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
#define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
#define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
#define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
#define KEY_MAX_OWNED "max_owned"
#define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
#define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
#define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
-#define KEY_NOTE_OPSERV_ACCESS "opserv_access"
-#define KEY_NOTE_CHANNEL_ACCESS "channel_access"
-#define KEY_NOTE_SETTER_ACCESS "setter_access"
-#define KEY_NOTE_VISIBILITY "visibility"
-#define KEY_NOTE_VIS_PRIVILEGED "privileged"
-#define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
-#define KEY_NOTE_VIS_ALL "all"
-#define KEY_NOTE_MAX_LENGTH "max_length"
-#define KEY_NOTE_SETTER "setter"
-#define KEY_NOTE_NOTE "note"
+#define KEY_NOTE_OPSERV_ACCESS "opserv_access"
+#define KEY_NOTE_CHANNEL_ACCESS "channel_access"
+#define KEY_NOTE_SETTER_ACCESS "setter_access"
+#define KEY_NOTE_VISIBILITY "visibility"
+#define KEY_NOTE_VIS_PRIVILEGED "privileged"
+#define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
+#define KEY_NOTE_VIS_ALL "all"
+#define KEY_NOTE_MAX_LENGTH "max_length"
+#define KEY_NOTE_SETTER "setter"
+#define KEY_NOTE_NOTE "note"
-#define KEY_REGISTERED "registered"
-#define KEY_REGISTRAR "registrar"
-#define KEY_SUSPENDED "suspended"
-#define KEY_PREVIOUS "previous"
-#define KEY_SUSPENDER "suspender"
-#define KEY_ISSUED "issued"
-#define KEY_REVOKED "revoked"
-#define KEY_SUSPEND_EXPIRES "suspend_expires"
-#define KEY_SUSPEND_REASON "suspend_reason"
-#define KEY_VISITED "visited"
-#define KEY_TOPIC "topic"
-#define KEY_GREETING "greeting"
-#define KEY_USER_GREETING "user_greeting"
-#define KEY_MODES "modes"
-#define KEY_FLAGS "flags"
-#define KEY_OPTIONS "options"
-#define KEY_USERS "users"
-#define KEY_BANS "bans"
-#define KEY_MAX "max"
-#define KEY_NOTES "notes"
-#define KEY_TOPIC_MASK "topic_mask"
-#define KEY_OWNER_TRANSFER "owner_transfer"
+#define KEY_REGISTERED "registered"
+#define KEY_REGISTRAR "registrar"
+#define KEY_SUSPENDED "suspended"
+#define KEY_PREVIOUS "previous"
+#define KEY_SUSPENDER "suspender"
+#define KEY_ISSUED "issued"
+#define KEY_REVOKED "revoked"
+#define KEY_SUSPEND_EXPIRES "suspend_expires"
+#define KEY_SUSPEND_REASON "suspend_reason"
+#define KEY_VISITED "visited"
+#define KEY_TOPIC "topic"
+#define KEY_GREETING "greeting"
+#define KEY_USER_GREETING "user_greeting"
+#define KEY_MODES "modes"
+#define KEY_FLAGS "flags"
+#define KEY_OPTIONS "options"
+#define KEY_USERS "users"
+#define KEY_BANS "bans"
+#define KEY_MAX "max"
+#define KEY_NOTES "notes"
+#define KEY_TOPIC_MASK "topic_mask"
+#define KEY_OWNER_TRANSFER "owner_transfer"
-#define KEY_OWNER "owner"
-#define KEY_REASON "reason"
-#define KEY_SET "set"
-#define KEY_DURATION "duration"
-#define KEY_EXPIRES "expires"
-#define KEY_TRIGGERED "triggered"
-
-#define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
+#define KEY_OWNER "owner"
+#define KEY_REASON "reason"
+#define KEY_SET "set"
+#define KEY_DURATION "duration"
+#define KEY_EXPIRES "expires"
+#define KEY_TRIGGERED "triggered"
+
+#define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
+#define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
{ "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$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_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
{ "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$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_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
{ "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_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_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." },
{ "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." },
- timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
+ timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
+}
+
+static void
+expire_dnrs(UNUSED_ARG(void *data))
+{
+ dict_iterator_t it, next;
+ struct do_not_register *dnr;
+
+ for(it = dict_first(handle_dnrs); it; it = next)
+ {
+ dnr = iter_data(it);
+ 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 = next)
+ {
+ dnr = iter_data(it);
+ 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 = next)
+ {
+ dnr = iter_data(it);
+ next = iter_next(it);
+ if(dnr->expires && dnr->expires <= now)
+ dict_remove(mask_dnrs, dnr->chan_name + 1);
+ }
+
+ if(chanserv_conf.dnr_expire_frequency)
+ timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
{
struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
strcpy(dnr->reason, reason);
dnr->set = now;
{
struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
strcpy(dnr->reason, reason);
dnr->set = now;
+static int dnr_print_func(struct do_not_register *dnr, void *extra)
+{
+ struct userNode *user;
+ char buf1[INTERVALLEN];
+ char buf2[INTERVALLEN];
+ time_t feh;
+
+ user = extra;
+ if(dnr->set)
+ {
+ feh = dnr->set;
+ strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
+ }
+ if(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)
+ {
+ send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
+ }
+ else
+ send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
+ return 0;
+}
+
- matches = 0;
- for(it = dict_first(handle_dnrs); it; it = iter_next(it))
- {
- dnr = iter_data(it);
- if(dnr->set)
- reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
- else
- reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
- matches++;
- }
- for(it = dict_first(plain_dnrs); it; it = iter_next(it))
- {
- dnr = iter_data(it);
- if(dnr->set)
- reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
- else
- reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
- matches++;
- }
- for(it = dict_first(mask_dnrs); it; it = iter_next(it))
- {
- dnr = iter_data(it);
- if(dnr->set)
- reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
- else
- reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
- matches++;
- }
-
+ matches = send_dnrs(user, handle_dnrs);
+ matches += send_dnrs(user, plain_dnrs);
+ matches += send_dnrs(user, mask_dnrs);
- else if(dict_find(plain_dnrs, chan_name, NULL))
+ reply("CSMSG_NO_SUCH_DNR", chan_name);
+ return 0;
+}
+
+struct dnr_search {
+ struct userNode *source;
+ char *chan_mask;
+ char *setter_mask;
+ char *reason_mask;
+ unsigned long min_set, max_set;
+ unsigned long min_expires, max_expires;
+ unsigned int limit;
+};
+
+static int
+dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
+{
+ return !((dnr->set < search->min_set)
+ || (dnr->set > search->max_set)
+ || (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
+ && !match_ircglob(dnr->setter, search->setter_mask))
+ || (search->reason_mask
+ && !match_ircglob(dnr->reason, search->reason_mask)));
+}
+
+static struct dnr_search *
+dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
+{
+ struct dnr_search *discrim;
+ unsigned int ii;
+
+ discrim = calloc(1, sizeof(*discrim));
+ discrim->source = user;
+ discrim->chan_mask = NULL;
+ discrim->setter_mask = NULL;
+ discrim->reason_mask = NULL;
+ discrim->max_set = INT_MAX;
+ discrim->limit = 50;
+
+ for(ii=0; ii<argc; ++ii)
- dict_remove(plain_dnrs, chan_name);
- reply("CSMSG_DNR_REMOVED", chan_name);
+ if(ii == argc - 1)
+ {
+ reply("MSG_MISSING_PARAMS", argv[ii]);
+ goto fail;
+ }
+ else if(0 == irccasecmp(argv[ii], "channel"))
+ {
+ discrim->chan_mask = argv[++ii];
+ }
+ else if(0 == irccasecmp(argv[ii], "setter"))
+ {
+ discrim->setter_mask = argv[++ii];
+ }
+ else if(0 == irccasecmp(argv[ii], "reason"))
+ {
+ discrim->reason_mask = argv[++ii];
+ }
+ else if(0 == irccasecmp(argv[ii], "limit"))
+ {
+ discrim->limit = strtoul(argv[++ii], NULL, 0);
+ }
+ else if(0 == irccasecmp(argv[ii], "set"))
+ {
+ const char *cmp = argv[++ii];
+ if(cmp[0] == '<') {
+ if(cmp[1] == '=')
+ discrim->min_set = now - ParseInterval(cmp + 2);
+ else
+ discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
+ } else if(cmp[0] == '=') {
+ discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
+ } else if(cmp[0] == '>') {
+ if(cmp[1] == '=')
+ discrim->max_set = now - ParseInterval(cmp + 2);
+ else
+ discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
+ } else {
+ discrim->max_set = now - (ParseInterval(cmp) - 1);
+ }
+ }
+ else if(0 == irccasecmp(argv[ii], "expires"))
+ {
+ const char *cmp = argv[++ii];
+ if(cmp[0] == '<') {
+ if(cmp[1] == '=')
+ discrim->max_expires = now + ParseInterval(cmp + 2);
+ else
+ discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
+ } else if(cmp[0] == '=') {
+ discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
+ } else if(cmp[0] == '>') {
+ if(cmp[1] == '=')
+ discrim->min_expires = now + ParseInterval(cmp + 2);
+ else
+ discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
+ } else {
+ discrim->min_expires = now + (ParseInterval(cmp) - 1);
+ }
+ }
+ else
+ {
+ reply("MSG_INVALID_CRITERIA", argv[ii]);
+ goto fail;
+ }
- dict_remove(mask_dnrs, chan_name);
- reply("CSMSG_DNR_REMOVED", chan_name);
+ int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
+ if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
+ target_fixed = 1;
+ }
+
+ if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
+ {
+ /* Check against account-based DNRs. */
+ dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
+ if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
+ dsf(dnr, data);
+ }
+ else if(target_fixed)
+ {
+ /* Check against channel-based DNRs. */
+ dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
+ if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
+ dsf(dnr, data);
- reply("CSMSG_NO_SUCH_DNR", chan_name);
+ /* Exhaustively search account DNRs. */
+ for(it = dict_first(handle_dnrs); it; it = next)
+ {
+ next = iter_next(it);
+ dnr = iter_data(it);
+ if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
+ break;
+ }
+
+ /* Do the same for channel DNRs. */
+ for(it = dict_first(plain_dnrs); it; it = next)
+ {
+ next = iter_next(it);
+ dnr = iter_data(it);
+ if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
+ break;
+ }
+
+ /* Do the same for wildcarded channel DNRs. */
+ for(it = dict_first(mask_dnrs); it; it = next)
+ {
+ next = iter_next(it);
+ dnr = iter_data(it);
+ if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
+ break;
+ }
+ }
+ return count;
+}
+
+static int
+dnr_remove_func(struct do_not_register *match, void *extra)
+{
+ struct userNode *user;
+ char *chan_name;
+
+ chan_name = alloca(strlen(match->chan_name) + 1);
+ strcpy(chan_name, match->chan_name);
+ user = extra;
+ if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
+ || dict_remove(plain_dnrs, chan_name)
+ || dict_remove(mask_dnrs, chan_name))
+ {
+ send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
+ }
+ return 0;
+}
+
+static int
+dnr_count_func(struct do_not_register *match, void *extra)
+{
+ return 0; (void)match; (void)extra;
+}
+
+static MODCMD_FUNC(cmd_dnrsearch)
+{
+ struct dnr_search *discrim;
+ dnr_search_func action;
+ struct svccmd *subcmd;
+ unsigned int matches;
+ char buf[MAXLEN];
+
+ sprintf(buf, "dnrsearch %s", argv[1]);
+ subcmd = dict_find(cmd->parent->commands, buf, NULL);
+ if(!subcmd)
+ {
+ reply("CSMSG_DNR_BAD_ACTION", argv[1]);
+ if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
+ return 0;
+ if(!irccasecmp(argv[1], "print"))
+ action = dnr_print_func;
+ else if(!irccasecmp(argv[1], "remove"))
+ action = dnr_remove_func;
+ else if(!irccasecmp(argv[1], "count"))
+ action = dnr_count_func;
+ else
+ {
+ reply("CSMSG_DNR_BAD_ACTION", argv[1]);
+ return 0;
+ }
+
+ discrim = dnr_search_create(user, cmd, argc-2, argv+2);
+ if(!discrim)
+ return 0;
+
+ if(action == dnr_print_func)
+ reply("CSMSG_DNR_SEARCH_RESULTS");
+ matches = dnr_search(discrim, action, user);
+ if(matches)
+ reply("MSG_MATCH_COUNT", matches);
+ else
+ reply("MSG_NO_MATCHES");
+ free(discrim);
- /* Flag to track whether the ban's been moved
- to the destination yet. */
- int moved = 0;
-
- /* Possible to assert (sbData->prev == NULL) here. */
- sNext = sbData->next;
-
- for(tbData = tFront; tbData; tbData = tNext)
- {
- tNext = tbData->next;
-
- /* Perform two comparisons between each source
- and target ban, conflicts are resolved by
- keeping the broader ban and copying the later
- expiration and triggered time. */
- if(match_ircglobs(tbData->mask, sbData->mask))
- {
- /* There is a broader ban in the target channel that
- overrides one in the source channel; remove the
- source ban and break. */
- if(sbData->expires > tbData->expires)
- tbData->expires = sbData->expires;
- if(sbData->triggered > tbData->triggered)
- tbData->triggered = sbData->triggered;
- del_channel_ban(sbData);
- break;
- }
- else if(match_ircglobs(sbData->mask, tbData->mask))
- {
- /* There is a broader ban in the source channel that
- overrides one in the target channel; remove the
- target ban, fall through and move the source over. */
- if(tbData->expires > sbData->expires)
- sbData->expires = tbData->expires;
- if(tbData->triggered > sbData->triggered)
- sbData->triggered = tbData->triggered;
- if(tbData == tFront)
- tFront = tNext;
- del_channel_ban(tbData);
- }
-
- /* Source bans can override multiple target bans, so
- we allow a source to run through this loop multiple
- times, but we can only move it once. */
- if(moved)
- continue;
- moved = 1;
-
- /* Remove the source ban from the source ban list. */
- if(sbData->next)
- sbData->next->prev = sbData->prev;
-
- /* Modify the source ban's associated channel. */
- sbData->channel = target;
-
- /* Insert the ban into the target channel's linked list. */
- sbData->prev = NULL;
- sbData->next = target->bans;
-
- if(target->bans)
- target->bans->prev = sbData;
- target->bans = sbData;
-
- /* Update the user counts for the target channel. */
- target->banCount++;
- }
+ /* Flag to track whether the ban's been moved
+ to the destination yet. */
+ int moved = 0;
+
+ /* Possible to assert (sbData->prev == NULL) here. */
+ sNext = sbData->next;
+
+ for(tbData = tFront; tbData; tbData = tNext)
+ {
+ tNext = tbData->next;
+
+ /* Perform two comparisons between each source
+ and target ban, conflicts are resolved by
+ keeping the broader ban and copying the later
+ expiration and triggered time. */
+ if(match_ircglobs(tbData->mask, sbData->mask))
+ {
+ /* There is a broader ban in the target channel that
+ overrides one in the source channel; remove the
+ source ban and break. */
+ if(sbData->expires > tbData->expires)
+ tbData->expires = sbData->expires;
+ if(sbData->triggered > tbData->triggered)
+ tbData->triggered = sbData->triggered;
+ del_channel_ban(sbData);
+ break;
+ }
+ else if(match_ircglobs(sbData->mask, tbData->mask))
+ {
+ /* There is a broader ban in the source channel that
+ overrides one in the target channel; remove the
+ target ban, fall through and move the source over. */
+ if(tbData->expires > sbData->expires)
+ sbData->expires = tbData->expires;
+ if(tbData->triggered > sbData->triggered)
+ sbData->triggered = tbData->triggered;
+ if(tbData == tFront)
+ tFront = tNext;
+ del_channel_ban(tbData);
+ }
+
+ /* Source bans can override multiple target bans, so
+ we allow a source to run through this loop multiple
+ times, but we can only move it once. */
+ if(moved)
+ continue;
+ moved = 1;
+
+ /* Remove the source ban from the source ban list. */
+ if(sbData->next)
+ sbData->next->prev = sbData->prev;
+
+ /* Modify the source ban's associated channel. */
+ sbData->channel = target;
+
+ /* Insert the ban into the target channel's linked list. */
+ sbData->prev = NULL;
+ sbData->next = target->bans;
+
+ if(target->bans)
+ target->bans->prev = sbData;
+ target->bans = sbData;
+
+ /* Update the user counts for the target channel. */
+ target->banCount++;
+ }
}
actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
scan_user_presence(actee, NULL);
reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
}
actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
scan_user_presence(actee, NULL);
reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
- struct banData *bData, *next;
-
- if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
- {
- reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
- free(ban);
- return 0;
- }
-
- if(action & ACTION_ADD_TIMED_BAN)
- {
- duration = ParseInterval(argv[2]);
-
- if(duration < 15)
- {
- reply("CSMSG_DURATION_TOO_LOW");
- free(ban);
- return 0;
- }
- else if(duration > (86400 * 365 * 2))
- {
- reply("CSMSG_DURATION_TOO_HIGH");
- free(ban);
- return 0;
- }
- }
-
- for(bData = channel->channel_info->bans; bData; bData = next)
- {
- if(match_ircglobs(bData->mask, ban))
- {
- int exact = !irccasecmp(bData->mask, ban);
-
- /* The ban is redundant; there is already a ban
- with the same effect in place. */
- if(exact)
- {
- if(bData->reason)
+ struct banData *bData, *next;
+
+ if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
+ {
+ reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
+ free(ban);
+ return 0;
+ }
+
+ if(action & ACTION_ADD_TIMED_BAN)
+ {
+ duration = ParseInterval(argv[2]);
+
+ if(duration < 15)
+ {
+ reply("CSMSG_DURATION_TOO_LOW");
+ free(ban);
+ return 0;
+ }
+ else if(duration > (86400 * 365 * 2))
+ {
+ reply("CSMSG_DURATION_TOO_HIGH");
+ free(ban);
+ return 0;
+ }
+ }
+
+ for(bData = channel->channel_info->bans; bData; bData = next)
+ {
+ if(match_ircglobs(bData->mask, ban))
+ {
+ int exact = !irccasecmp(bData->mask, ban);
+
+ /* The ban is redundant; there is already a ban
+ with the same effect in place. */
+ if(exact)
+ {
+ if(bData->reason)
- }
- if(exact && bData->expires)
- {
- int reset = 0;
-
- /* 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))
- {
- bData->expires = now + duration;
- reset = 1;
- }
- else if(!duration)
- {
- bData->expires = 0;
- reset = 1;
- }
-
- if(reset)
- {
- /* Delete the expiration timeq entry and
- requeue if necessary. */
- timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
-
- if(bData->expires)
- timeq_add(bData->expires, expire_ban, bData);
+ }
+ if(exact && bData->expires)
+ {
+ int reset = 0;
+
+ /* If the ban matches an existing one exactly,
+ extend the expiration time if the provided
+ duration is longer. */
+ if(duration && (now + duration > bData->expires))
+ {
+ bData->expires = now + duration;
+ reset = 1;
+ }
+ else if(!duration)
+ {
+ bData->expires = 0;
+ reset = 1;
+ }
+
+ if(reset)
+ {
+ /* Delete the expiration timeq entry and
+ requeue if necessary. */
+ timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
+
+ if(bData->expires)
+ timeq_add(bData->expires, expire_ban, bData);
- /* Assume all criteria require arguments. */
- if(i == (argc - 1))
- {
- send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
+ /* Assume all criteria require arguments. */
+ if(i == (argc - 1))
+ {
+ send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
+ goto fail;
+ }
+
+ if(!irccasecmp(argv[i], "name"))
+ search->name = argv[++i];
+ else if(!irccasecmp(argv[i], "registrar"))
+ search->registrar = argv[++i];
+ else if(!irccasecmp(argv[i], "unvisited"))
+ search->unvisited = ParseInterval(argv[++i]);
+ else if(!irccasecmp(argv[i], "registered"))
+ search->registered = ParseInterval(argv[++i]);
+ else if(!irccasecmp(argv[i], "flags"))
+ {
+ i++;
+ if(!irccasecmp(argv[i], "nodelete"))
+ search->flags |= CHANNEL_NODELETE;
+ else if(!irccasecmp(argv[i], "suspended"))
+ search->flags |= CHANNEL_SUSPENDED;
+ else if(!irccasecmp(argv[i], "unreviewed"))
+ search->flags |= CHANNEL_UNREVIEWED;
+ else
+ {
+ send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
+ goto fail;
+ }
+ }
+ else if(!irccasecmp(argv[i], "limit"))
+ search->limit = strtoul(argv[++i], NULL, 10);
+ else
+ {
+ send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
- }
-
- if(!irccasecmp(argv[i], "name"))
- search->name = argv[++i];
- else if(!irccasecmp(argv[i], "registrar"))
- search->registrar = argv[++i];
- else if(!irccasecmp(argv[i], "unvisited"))
- search->unvisited = ParseInterval(argv[++i]);
- else if(!irccasecmp(argv[i], "registered"))
- search->registered = ParseInterval(argv[++i]);
- else if(!irccasecmp(argv[i], "flags"))
- {
- i++;
- if(!irccasecmp(argv[i], "nodelete"))
- search->flags |= CHANNEL_NODELETE;
- else if(!irccasecmp(argv[i], "suspended"))
- search->flags |= CHANNEL_SUSPENDED;
- else
- {
- send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
- goto fail;
- }
- }
- else if(!irccasecmp(argv[i], "limit"))
- search->limit = strtoul(argv[++i], NULL, 10);
- else
- {
- send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
- goto fail;
- }
+static MODCMD_FUNC(chan_opt_unreviewed)
+{
+ struct chanData *cData = channel->channel_info;
+ int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
+
+ if(argc > 1)
+ {
+ int new_value;
+
+ /* The two directions can have different ACLs. */
+ if(enabled_string(argv[1]))
+ new_value = 1;
+ else if(disabled_string(argv[1]))
+ new_value = 0;
+ else
+ {
+ reply("MSG_INVALID_BINARY", argv[1]);
+ return 0;
+ }
+
+ if (new_value != value)
+ {
+ struct svccmd *subcmd;
+ char subcmd_name[32];
+
+ snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
+ subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
+ if(!subcmd)
+ {
+ reply("MSG_COMMAND_DISABLED", subcmd_name);
+ return 0;
+ }
+ else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
+ return 0;
+
+ if (new_value)
+ cData->flags |= CHANNEL_UNREVIEWED;
+ else
+ {
+ free(cData->registrar);
+ cData->registrar = strdup(user->handle_info->handle);
+ cData->flags &= ~CHANNEL_UNREVIEWED;
+ }
+ value = new_value;
+ }
+ }
+
+ if(value)
+ reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
+ else
+ reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
+ return 1;
+}
+
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);
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);
- /* Allow for a bit of padding so that the limit doesn't
- track the user count exactly, which could get annoying. */
- if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
- {
- timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
- timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
- }
+ /* Allow for a bit of padding so that the limit doesn't
+ track the user count exactly, which could get annoying. */
+ if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
+ {
+ timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
+ timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
+ }
- reg_server_link_func(handle_server_link);
-
- reg_new_channel_func(handle_new_channel);
- reg_join_func(handle_join);
- reg_part_func(handle_part);
- reg_kick_func(handle_kick);
- reg_topic_func(handle_topic);
- reg_mode_change_func(handle_mode);
- reg_nick_change_func(handle_nick_change);
+ if(nick)
+ {
+ reg_server_link_func(handle_server_link);
+ reg_new_channel_func(handle_new_channel);
+ reg_join_func(handle_join);
+ reg_part_func(handle_part);
+ reg_kick_func(handle_kick);
+ reg_topic_func(handle_topic);
+ reg_mode_change_func(handle_mode);
+ reg_nick_change_func(handle_nick_change);
+ reg_auth_func(handle_auth);
+ }
DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
+ DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
+ modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
+ modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
+ modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);