Ebenen deaktivieren/loeschen mit Zuhoerern.

Optional koennen sich die Helfer zum Deaktivieren und
Loeschen einer Funktion auch darum kuemmern, dass die
Zuhoerer dabei ordentlich ausgetragen werden.

Change-Id: I3dd22c06b8c3c3cbfb311096516724318c46481d
diff --git a/p/daemon/channeld.c b/p/daemon/channeld.c
index c87fef1..e2e9e72 100644
--- a/p/daemon/channeld.c
+++ b/p/daemon/channeld.c
@@ -641,21 +641,52 @@
   return 0;
 }
 
+private void remove_all_members(struct channel_s ch)
+{
+  // Einer geloeschten/inaktiven Ebene kann man nicht zuhoeren: Ebenenname
+  // aus der Ebenenliste aller Mitglieder austragen. Dabei werden sowohl ein-,
+  // als auch temporaer ausgeschaltete Ebenen beruecksichtigt.
+  string chname = lower_case(ch.name);
+  foreach(object listener : ch.members)
+  {
+    string* pl_chans = listener->QueryProp(P_CHANNELS);
+    if (pointerp(pl_chans))
+    {
+      listener->SetProp(P_CHANNELS, pl_chans-({chname}));
+    }
+    pl_chans = listener->QueryProp(P_SWAP_CHANNELS);
+    if (pointerp(pl_chans))
+    {
+      listener->SetProp(P_SWAP_CHANNELS, pl_chans-({chname}));
+    }
+  }
+}
+
 // Deaktiviert eine Ebene, behaelt aber einige Stammdaten in channelC und die
 // History, so dass sie spaeter reaktiviert werden kann.
-private void deactivate_channel(string chname)
+// Wenn <force>, dann wird wie Ebene sogar deaktiviert, wenn noch Zuhoerer
+// anwesend sind.
+private void deactivate_channel(string chname, int force)
 {
   chname = lower_case(chname);
   struct channel_s ch = channels[chname];
+  // Deaktivieren kann man nur aktive Ebenen.
   if (!structp(ch))
     return;
-
-  // nur Ebenen ohne Zuhoerer deaktivieren.
+  // Falls sie noch Zuhoerer hat, muss man sich erstmal um die kuemmern.
   if (sizeof(ch.members))
   {
-    raise_error(
-        sprintf("[%s] Attempt to deactivate channel %s with listeners.\n",
-                dtime(), ch.name));
+    // ohne <force> nur Ebenen ohne Zuhoerer deaktivieren.
+    if (!force)
+    {
+      raise_error(
+          sprintf("Attempt to deactivate channel %s with listeners.\n",
+                  ch.name));
+    }
+    else
+    {
+      remove_all_members(ch);
+    }
   }
   // Einige Daten merken, damit sie reaktiviert werden kann, wenn jemand
   // einloggt, der die Ebene abonniert hat.
@@ -673,19 +704,30 @@
 }
 
 // Loescht eine Ebene vollstaendig inkl. Stammdaten und History.
-private void delete_channel(string chname)
+// Wenn <force>, dann wird wie Ebene sogar deaktiviert, wenn noch Zuhoerer
+// anwesend sind.
+private void delete_channel(string chname, int force)
 {
   chname = lower_case(chname);
   struct channel_s ch = channels[chname];
+  // Ist die Ebene noch aktiv?
   if (ch)
   {
-    // nur Ebenen ohne Zuhoerer loeschen. (Wenn der Aufrufer auch andere
-    // loeschen will, muss er vorher selber die Ebene leer raeumen, s.
-    // Kommandofunktion remove_channel().
+    // Und hat sie Zuhoerer?
     if (sizeof(ch.members))
-      raise_error(
-          sprintf("[%s] Attempt to delete channel %s with listeners.\n",
-                  dtime(), ch.name));
+    {
+      // ohne <force> nur Ebenen ohne Zuhoerer loeschen.
+      if (!force)
+      {
+        raise_error(
+            sprintf("Attempt to delete channel %s with listeners.\n",
+                    ch.name));
+      }
+      else
+      {
+        remove_all_members(ch);
+      }
+    }
     stats["dispose"]++;
     m_delete(channels, chname);
   }
@@ -1028,7 +1070,7 @@
         " die Ebene '" + ch.name + "', worauf diese sich in "
         "einem Blitz oktarinen Lichts aufloest.", MSG_EMOTE);
     }
-    deactivate_channel(lower_case(ch.name));
+    deactivate_channel(lower_case(ch.name),0);
   }
   return (0);
 }
@@ -1167,9 +1209,6 @@
 // Wird aus der Shell gerufen, fuer das Erzmagier-Kommando "kill".
 public int remove_channel(string chname, object pl)
 {
-  chname = lower_case(chname);
-  struct channel_s ch = channels[chname];
-
   //TODO: integrieren in access()?
   if (previous_object() != this_object())
   {
@@ -1179,30 +1218,8 @@
         !IS_ARCH(this_interactive()))
       return E_ACCESS_DENIED;
   }
-  // Wenn die Ebene aktiv ist (d.h. Zuhoerer hat), muessen die erst
-  // runtergeworfen werden.
-  if (ch)
-  {
-    // Einer geloeschten Ebene kann man nicht zuhoeren: Ebenenname aus der
-    // Ebenenliste aller Mitglieder austragen. Dabei werden sowohl ein-, als
-    // auch temporaer ausgeschaltete Ebenen beruecksichtigt.
-    foreach(object listener : ch.members)
-    {
-      string* pl_chans = listener->QueryProp(P_CHANNELS);
-      if (pointerp(pl_chans))
-      {
-        listener->SetProp(P_CHANNELS, pl_chans-({chname}));
-      }
-      pl_chans = listener->QueryProp(P_SWAP_CHANNELS);
-      if (pointerp(pl_chans))
-      {
-        listener->SetProp(P_SWAP_CHANNELS, pl_chans-({chname}));
-      }
-    }
-  }
-  // Dies auserhalb des Blocks oben ermoeglicht es, inaktive Ebenen bzw.
-  // deren Daten zu entfernen.
-  delete_channel(chname);
+
+  delete_channel(lower_case(chname), 1);
 
   return (0);
 }