Properly preserve empty +A channels.
[ircu2.10.12-pk.git] / ircd / channel.c
index ca5bde22d19e59f39918ccf08504dc0a477ee6cc..f416f123dfd9f05cbf5996fc184914c2ef389647 100644 (file)
@@ -259,7 +259,7 @@ int sub1_from_channel(struct Channel* chptr)
    * who then will educate them on the use of Apass/upass.
    */
 
-  if (!(chptr->mode.mode & MODE_APASS))         /* If no Apass, destroy now. */
+  if (!chptr->mode.apass[0])         /* If no Apass, destroy now. */
     destruct_channel(chptr);
   else if (TStime() - chptr->creationtime < 172800)    /* Channel younger than 48 hours? */
     schedule_destruct_event_1m(chptr);         /* Get rid of it in approximately 4-5 minutes */
@@ -1399,7 +1399,15 @@ void del_invite(struct Client *cptr, struct Channel *chptr)
     }
 }
 
-/** @page zombie Explaination of Zombies
+/** @page zombie Explanation of Zombies
+ *
+ * Synopsis:
+ *
+ * A channel member is turned into a zombie when he is kicked from a
+ * channel but his server has not acknowledged the kick.  Servers that
+ * see the member as a zombie can accept actions he performed before
+ * being kicked, without allowing chanop operations from outsiders or
+ * desyncing the network.
  *
  * Consider:
  * <pre>
@@ -2428,7 +2436,8 @@ mode_parse_upass(struct ParseState *state, int *flag_p)
   }
 
   /* If a non-service user is trying to force it, refuse. */
-  if (state->flags & MODE_PARSE_FORCE && !IsChannelService(state->sptr)) {
+  if (state->flags & MODE_PARSE_FORCE && MyUser(state->sptr)
+      && !HasPriv(state->sptr, PRIV_APASS_OPMODE)) {
     send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
                "Use /JOIN", state->chptr->chname, " <AdminPass>.");
     return;
@@ -2535,7 +2544,8 @@ mode_parse_apass(struct ParseState *state, int *flag_p)
   }
 
   /* If a non-service user is trying to force it, refuse. */
-  if (state->flags & MODE_PARSE_FORCE && !IsChannelService(state->sptr)) {
+  if (state->flags & MODE_PARSE_FORCE && MyUser(state->sptr)
+      && !HasPriv(state->sptr, PRIV_APASS_OPMODE)) {
     send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
                "Use /JOIN", state->chptr->chname, " <AdminPass>.");
     return;
@@ -2623,7 +2633,7 @@ mode_parse_apass(struct ParseState *state, int *flag_p)
            "\" to remove the password and then immediately set a new one. "
            "IMPORTANT: YOU CANNOT RECOVER THIS PASSWORD, EVER; "
            "WRITE THE PASSWORD DOWN (don't store this rescue password on disk)! "
-           "Now set the channel user password (+u).");
+           "Now set the channel user password (+U).");
       }
     } else { /* remove the old apass */
       *state->chptr->mode.apass = '\0';
@@ -2683,7 +2693,7 @@ bmatch(struct Ban *old_ban, struct Ban *new_ban)
  * @param[in] newban Ban (or exception) to add (or remove).
  * @return Zero if \a newban could be applied, non-zero if not.
  */
-int apply_ban(struct Ban **banlist, struct Ban *newban)
+int apply_ban(struct Ban **banlist, struct Ban *newban, int do_free)
 {
   struct Ban *ban;
   size_t count = 0;
@@ -2694,7 +2704,8 @@ int apply_ban(struct Ban **banlist, struct Ban *newban)
     /* If a less specific entry is found, fail.  */
     for (ban = *banlist; ban; ban = ban->next) {
       if (!bmatch(ban, newban)) {
-        free_ban(newban);
+        if (do_free)
+          free_ban(newban);
         return 1;
       }
       if (!(ban->flags & (BAN_OVERLAPPED|BAN_DEL))) {
@@ -2705,7 +2716,7 @@ int apply_ban(struct Ban **banlist, struct Ban *newban)
     /* 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->flags |= BAN_OVERLAPPED | BAN_DEL;
       }
       banlist = &ban->next;
     }
@@ -2716,18 +2727,21 @@ int apply_ban(struct Ban **banlist, struct Ban *newban)
     /* Mark more specific entries. */
     for (ban = *banlist; ban; ban = ban->next) {
       if (!bmatch(newban, ban)) {
-        ban->flags |= BAN_OVERLAPPED;
+        ban->flags |= BAN_OVERLAPPED | BAN_DEL;
         remove_count++;
       }
     }
+    if (remove_count)
+        return 0;
     /* If no matches were found, fail. */
-    if (!remove_count) {
+    if (do_free)
       free_ban(newban);
-      return 3;
-    }
-    return 0;
+    else
+      MyFree(newban->banstr);
+    return 3;
   }
-  free_ban(newban);
+  if (do_free)
+    free_ban(newban);
   return 4;
 }
 
@@ -2738,7 +2752,7 @@ static void
 mode_parse_ban(struct ParseState *state, int *flag_p)
 {
   char *t_str, *s;
-  struct Ban *ban, *newban = 0;
+  struct Ban *ban, *newban;
 
   if (state->parc <= 0) { /* Not enough args, send ban list */
     if (MyUser(state->sptr) && !(state->done & DONE_BANLIST)) {
@@ -2772,12 +2786,6 @@ mode_parse_ban(struct ParseState *state, int *flag_p)
     return;
   }
 
-  if (!state->chptr->banlist) {
-    state->chptr->banlist = newban; /* add our ban with its flags */
-    state->done |= DONE_BANCLEAN;
-    return;
-  }
-
   /* Clear all ADD/DEL/OVERLAPPED flags from ban list. */
   if (!(state->done & DONE_BANCLEAN)) {
     for (ban = state->chptr->banlist; ban; ban = ban->next)
@@ -2794,7 +2802,7 @@ mode_parse_ban(struct ParseState *state, int *flag_p)
   set_ban_mask(newban, collapse(pretty_mask(t_str)));
   newban->who = cli_name(state->sptr);
   newban->when = TStime();
-  apply_ban(&state->chptr->banlist, newban);
+  apply_ban(&state->chptr->banlist, newban, 0);
 }
 
 /*
@@ -2829,8 +2837,7 @@ mode_process_bans(struct ParseState *state)
       continue;
     } else if (ban->flags & BAN_DEL) { /* Deleted a ban? */
       modebuf_mode_string(state->mbuf, MODE_DEL | MODE_BAN,
-                         ban->banstr,
-                         state->flags & MODE_PARSE_SET);
+                         ban->banstr, 1);
 
       if (state->flags & MODE_PARSE_SET) { /* Ok, make it take effect */
        if (prevban) /* clip it out of the list... */
@@ -2873,8 +2880,7 @@ mode_process_bans(struct ParseState *state)
        } else {
          /* add the ban to the buffer */
          modebuf_mode_string(state->mbuf, MODE_ADD | MODE_BAN,
-                             ban->banstr,
-                             !(state->flags & MODE_PARSE_SET));
+                             ban->banstr, 1);
 
          if (state->flags & MODE_PARSE_SET) { /* create a new ban */
            newban = make_ban(ban->banstr);