+ send_reply(state->sptr, RPL_APASSWARN_CLEAR);
+ /* Revert everyone to MAXOPLEVEL. */
+ for (memb = state->chptr->members; memb; memb = memb->next_member) {
+ if (memb->status & MODE_CHANOP)
+ SetOpLevel(memb, MAXOPLEVEL);
+ }
+ }
+ }
+}
+
+/** Compare one ban's extent to another.
+ * This works very similarly to mmatch() but it knows about CIDR masks
+ * and ban exceptions. If both bans are CIDR-based, compare their
+ * address bits; otherwise, use mmatch().
+ * @param[in] old_ban One ban.
+ * @param[in] new_ban Another ban.
+ * @return Zero if \a old_ban is a superset of \a new_ban, non-zero otherwise.
+ */
+static int
+bmatch(struct Ban *old_ban, struct Ban *new_ban)
+{
+ int res;
+ assert(old_ban != NULL);
+ assert(new_ban != NULL);
+ /* A ban is never treated as a superset of an exception. */
+ if (!(old_ban->flags & BAN_EXCEPTION)
+ && (new_ban->flags & BAN_EXCEPTION))
+ return 1;
+ /* If either is not an address mask, match the text masks. */
+ if ((old_ban->flags & new_ban->flags & BAN_IPMASK) == 0)
+ return mmatch(old_ban->banstr, new_ban->banstr);
+ /* If the old ban has a longer prefix than new, it cannot be a superset. */
+ if (old_ban->addrbits > new_ban->addrbits)
+ return 1;
+ /* Compare the masks before the hostname part. */
+ old_ban->banstr[old_ban->nu_len] = new_ban->banstr[new_ban->nu_len] = '\0';
+ res = mmatch(old_ban->banstr, new_ban->banstr);
+ old_ban->banstr[old_ban->nu_len] = new_ban->banstr[new_ban->nu_len] = '@';
+ if (res)
+ return res;
+ /* If the old ban's mask mismatches, cannot be a superset. */
+ if (!ipmask_check(&new_ban->address, &old_ban->address, old_ban->addrbits))
+ return 1;
+ /* Otherwise it depends on whether the old ban's text is a superset
+ * of the new. */
+ return mmatch(old_ban->banstr, new_ban->banstr);
+}
+
+/** Add a ban from a ban list and mark bans that should be removed
+ * because they overlap.
+ *
+ * There are three invariants for a ban list. First, no ban may be
+ * more specific than another ban. Second, no exception may be more
+ * specific than another exception. Finally, no ban may be more
+ * specific than any exception.
+ *
+ * @param[in,out] banlist Pointer to head of list.
+ * @param[in] newban Ban (or exception) to add (or remove).
+ * @param[in] do_free If non-zero, free \a newban on failure.
+ * @return Zero if \a newban could be applied, non-zero if not.
+ */
+int apply_ban(struct Ban **banlist, struct Ban *newban, int do_free)
+{
+ struct Ban *ban;
+ size_t count = 0;
+
+ assert(newban->flags & (BAN_ADD|BAN_DEL));
+ if (newban->flags & BAN_ADD) {
+ size_t totlen = 0;
+ /* If a less specific *active* entry is found, fail. */
+ for (ban = *banlist; ban; ban = ban->next) {
+ if (!bmatch(ban, newban) && !(ban->flags & BAN_DEL)) {
+ if (do_free)
+ free_ban(newban);
+ return 1;
+ }
+ if (!(ban->flags & (BAN_OVERLAPPED|BAN_DEL))) {
+ count++;
+ totlen += strlen(ban->banstr);
+ }
+ }
+ /* Mark more specific entries and add this one to the end of the list. */
+ while ((ban = *banlist) != NULL) {
+ if (!bmatch(newban, ban)) {
+ ban->flags |= BAN_OVERLAPPED | BAN_DEL;
+ }
+ banlist = &ban->next;
+ }
+ *banlist = newban;
+ return 0;
+ } else if (newban->flags & BAN_DEL) {
+ size_t remove_count = 0;
+ /* Mark more specific entries. */
+ for (ban = *banlist; ban; ban = ban->next) {
+ if (!bmatch(newban, ban)) {
+ ban->flags |= BAN_OVERLAPPED | BAN_DEL;
+ remove_count++;
+ }