Supervisoren ab jetzt ganz eigenstaendig.

SVs sind jetzt ganz unabhaengig vom CHANNELD und lesen
auch ihre Daten selber ein.
Auch der CHANNELD ist nicht mehr ein SV, sondern es
gibt einen unabhaengigen Default-SV. Das hat den
Nebeneffekt, dass der SV beibehalten wird, wenn der
CHANNELD neugeladen wird.

Change-Id: I3b3ae64f1d0f627ce4c97098e6c1b15796f9b941
diff --git a/p/daemon/channel-sv.c b/p/daemon/channel-sv.c
new file mode 100644
index 0000000..b089e8f
--- /dev/null
+++ b/p/daemon/channel-sv.c
@@ -0,0 +1,21 @@
+// channel-sv.c
+//
+// Standard-SV der Mudlib
+
+#pragma strong_types,rtt_checks
+#pragma no_shadow,no_clone, no_inherit
+
+#include "channel.h"
+
+inherit "/std/channel_supervisor";
+
+protected void create()
+{
+  ::create();
+  // Namen setzen, der auf den Ebenen genannt werden soll.
+  ch_set_sv_name(DEFAULTSVNAME);
+  // Das systemweite Init-File einlesen und die Zugriffsrechte fuer die Ebenen
+  // merken, in denen dieses Objekt SV ist.
+  ch_read_init_file();
+}
+
diff --git a/p/daemon/channel.h b/p/daemon/channel.h
index 9580326..668d7ec 100644
--- a/p/daemon/channel.h
+++ b/p/daemon/channel.h
@@ -11,6 +11,8 @@
 
 #define CHMASTER          "/p/daemon/channeld"
 #define CMNAME            "<MasteR>"
+#define DEFAULTSV         "/p/daemon/channel-sv"
+#define DEFAULTSVNAME     "Merlin"
 
 // Message types
 #define MSG_SAY           0
@@ -33,9 +35,15 @@
 #define C_LIST            "list"
 #define C_FIND            "find"
 
-// Flags
+// Ebenen-Flags, die Verhalten von Ebenen steuern (vom CHANNELD verwendet)
 #define CHF_FIXED_SUPERVISOR 1   // Kein Wechsel des SV erlaubt
 
+// Flags fuer Zugriffsverwaltung, nur benutzt von den Ebenen-Supervisoren
+// F_WIZARD kennzeichnet reine Magierebenen
+#define CH_ACCESS_WIZARD 1
+// Ebenen, auf denen keine Gaeste erlaubt sind, sind mit F_NOGUEST markiert.
+#define CH_ACCESS_NOGUEST 2
+
 #endif //__DAEMON_CHANNEL_H__
 
 // prototypes
diff --git a/p/daemon/channeld.c b/p/daemon/channeld.c
index 630093d..50d05cf 100644
--- a/p/daemon/channeld.c
+++ b/p/daemon/channeld.c
@@ -36,9 +36,6 @@
 #define MAX_INACTIVE_CHANNELS 500
 #define CMDS          ({C_FIND, C_LIST, C_JOIN, C_LEAVE, C_SEND, C_NEW})
 
-// Standard-Ebenen-Supervisor erben, Variablen nosave, die sollen hier nicht
-// gespeichert werden.
-nosave variables inherit "/std/channel_supervisor";
 
 // Datenstrukturen fuer die Ebenen.
 // Basisdaten, welche auch inaktive Ebenen in channelC haben
@@ -385,63 +382,40 @@
 private void setup(string* chinfo)
 {
   string desc = "- Keine Beschreibung -";
-  object supervisor = this_object();
-  int sv_recv, sv_send, sv_flags; // an den Supervisor weiterreichen
+  object supervisor;
   int chflags;
 
   if (sizeof(chinfo) && sizeof(chinfo[0]) > 1 && chinfo[0][0] == '\\')
     chinfo[0] = chinfo[0][1..];
 
-  switch (sizeof(chinfo))
-  {
-    // Alle Fallthroughs in dem switch() sind Absicht.
-    default:
-      if (stringp(chinfo[6]) && sizeof(chinfo[6]))
-        catch(supervisor = load_object(chinfo[6]); publish);
-      if (!objectp(supervisor))
-        supervisor = this_object();
-    case 6:
-      if (stringp(chinfo[5]))
-        desc = chinfo[5];
-    case 5:
-        chflags = to_int(chinfo[4]);
-    case 4:
-      sv_flags = to_int(chinfo[3]);
-    case 3:
-      sv_send = to_int(chinfo[2]);
-    case 2:
-      sv_recv = to_int(chinfo[1]);
-      break;
-
-    case 0:
-    case 1:
-      return;
-  }
-  // Zugriffsrechte im channel_supervisor konfigurieren. (Kann auch dieses
-  // Objekt selber sein...)
-  supervisor->ch_supervisor_setup(lower_case(chinfo[0]), sv_recv,
-                                  sv_send, sv_flags);
   // Wenn der channeld nur neugeladen wurde, aber das Mud nicht neugestartet,
-  // sind alle Ebenen noch da, weil sie im MEMORY liegen. Dann muessen wir das
-  // new() natuerlich ueberspringen.
-  if (!member(channels, lower_case(chinfo[0])))
+  // sind alle Ebenen noch da, weil sie im MEMORY liegen. D.h. ist die Ebene
+  // noch bekannt, muss nichts gemacht werden.
+  if (member(channels, lower_case(chinfo[0])))
+    return;
+
+  // Nur die Angabe des SV (Index 6) im initfile ist optional, alle Elemente
+  // davor muessen da sein.
+  if (sizeof(chinfo) < 6)
+    return;
+  // Bei genug Elementen schauen, ob der SV ladbar ist.
+  if (sizeof(chinfo) >= 7)
   {
-    if (new(chinfo[0], supervisor, desc, chflags) == E_ACCESS_DENIED)
-    {
-      log_file("CHANNEL", sprintf("[%s] %s: %O: error, access denied\n",
-        dtime(time()), chinfo[0], supervisor));
-    }
+    if (stringp(chinfo[6]) && sizeof(chinfo[6]))
+      catch(supervisor = load_object(chinfo[6]); publish);
   }
-  else
+  // Aber falls kein SV angegeben wird oder das Objekt nicht ladbar war, wird
+  // ein Default-SV genutzt.
+  if (!supervisor)
+    supervisor = load_object(DEFAULTSV);
+
+  desc = chinfo[5];
+  chflags = to_int(chinfo[4]);
+
+  if (new(chinfo[0], supervisor, desc, chflags) == E_ACCESS_DENIED)
   {
-    // aber auch falls die Ebene (noch) existiert: wenn der channeld
-    // Supervisor sein soll, muss er die Ebene in jedem Fall
-    // (neu) betreten. Dabei wird er dann wieder Supervisor.
-    // Das Recht hat er immer und access() erlaubt das sogar vor dem Pruefen,
-    // ob es einen Supervisor gibt (den es jetzt gerade nicht gibt fuer
-    // Ebenen, in denen der CHANNELD supervisor war).
-    if (supervisor == this_object())
-      join(chinfo[0], this_object());
+    log_file("CHANNEL", sprintf("[%s] %s: %O: error, access denied\n",
+      dtime(time()), chinfo[0], supervisor));
   }
   return;
 }
@@ -540,26 +514,19 @@
             "boot": capitalize(getuid(previous_object()) || "<Unbekannt>")]);
 
   // Das muss auch laufen, wenn wir die alten Ebenen aus dem MEMORY bekommen
-  // haben, weil es dafuer sorgt, dass das Mapping <admin> aus
-  // channel_supervisor wieder mit den Informationen aus .init befuellt wird,
-  // weil das nicht in MEMORY liegt (weil das vermutlich ein Grund ist, den
-  // channeld neuzuladen: zum neuen Einlesen der Ebenenrechte).
-  // Ausserdem ist der channeld selber ja ein neues Objekt und trotz MEMORY
-  // nirgendwo mehr als Listener/Supervisor eingetragen, wo er eingetragen
-  // sein sollte...
-  // initialize() und setup() koennen aber mit Ebenen umgehen, die es schon
-  // gibt 
+  // haben, weil es ja neue Ebenen geben koennte, die dann erstellt werden
+  // muessen (verschwundete werden aber nicht aufgeraeumt!)
   initialize();
+  // <MasteR>-Ebene betreten, damit der channeld auf seine Kommandos auf
+  // dieser Ebene reagieren kann.
+  this_object()->join(CMNAME, this_object());
 
   // Wenn wir die alten Ebenen nicht aus MEMORY hatten, gibts noch Dinge zu
   // erledigen.
   if (do_complete_init)
   {
-    // <MasteR>-Ebene erstellen. Channeld wird Ebenenbesitzer und somit auch
-    // Zuhoerer, damit er auf Kommandos auf dieser Ebene reagieren kann.
-    new(CMNAME, this_object(), "Zentrale Informationen zu den Ebenen");
     // Spieler muessen die Ebenen abonnieren. NPC und andere Objekte haben
-    // leider Pech gehabt.
+    // leider Pech gehabt, falls das nicht das erste Laden nach Reboot war.
     users()->RegisterChannels();
     // Die Zugriffskontrolle auf die Ebenen wird von der Funktion access()
     // erledigt. Weil sowohl externe Aufrufe aus dem Spielerobjekt, als auch
@@ -636,14 +603,14 @@
 }
 
 // name() - define the name of this object.
-string name()
+public varargs string name(int casus,int demon)
 {
   return CMNAME;
 }
 
-string Name()
+public varargs string Name(int casus, int demon)
 {
-  return CMNAME;
+  return capitalize(CMNAME);
 }
 
 // Low-level function for adding members without access checks
@@ -791,7 +758,7 @@
   object old_sv = ch.supervisor;
 
   ch.supervisor = new_sv;
-  ch.access_cl = symbol_function("check_ch_access", new_sv);
+  ch.access_cl = symbol_function("ch_check_access", new_sv);
 
   if (old_sv && new_sv
       && !old_sv->QueryProp(P_INVIS)
@@ -896,7 +863,7 @@
 varargs private int access(struct channel_s ch, object user, string cmd,
                            string txt)
 {
-  if (!ch)
+  if (!ch || !user)
     return 0;
 
   // Dieses Objekt und Root-Objekte duerfen auf der Ebene senden, ohne
@@ -905,16 +872,17 @@
   // Supervisoren. (z.B. kann dieses Objekt sogar Meldungen im Namen anderer
   // Objekte faken)
   // Die Pruefung erfolgt absichtlich vor assert_supervisor(), damit der
-  // CHANNELD z.b. Ebenen re-joinen kann und dort wieder supervisor werden
-  // kann.
+  // CHANNELD auch in temporaeren SV-losen Zustaenden was machen kann.
   if ( !previous_object(1) || !extern_call() ||
        previous_object(1) == this_object() ||
        getuid(previous_object(1)) == ROOTID)
     return 2;
 
   // Objekte duerfen keine Meldungen im Namen anderer Objekte faken, d.h. der
-  // vermeintliche <user> muss auch der Aufrufer sein.
-  if (!objectp(user) || previous_object(1) != user)
+  // vermeintliche <user> muss auch der Aufrufer sein. Ausser darf auch sonst
+  // kein Objekt was fuer ein anderes Objekt duerfen, sonst kann jemand z.B.
+  // eine History abfragen indem einfach ein anderes Objekt uebergeben wird.
+  if (previous_object(1) != user)
     return 0;
 
   if (IsBanned(user, cmd))
@@ -929,10 +897,11 @@
     return 1;
 
   // Das SV-Objekt wird gefragt, ob der Zugriff erlaubt ist. Dieses erfolgt
-  // fuer EM+ aber nur, wenn der CHANNELD selber das SV-Objekt ist, damit
+  // fuer EM+ aber nur, wenn es das Default-SV-Objekt ist, damit
   // nicht beliebige SV-Objekt EMs den Zugriff verweigern koennen. Ebenen mit
-  // CHANNELD als SV koennen aber natuerlich auch EM+ Zugriff verweigern.
-  if (IS_ARCH(previous_object(1)) && ch.supervisor != this_object())
+  // Default-SV koennen aber auch EM+ Zugriff verweigern.
+  if (IS_ARCH(previous_object(1))
+      && ch.supervisor != find_object(DEFAULTSV))
     return 1;
 
   return funcall(ch.access_cl, lower_case(ch.name), user, cmd, &txt);
@@ -941,7 +910,7 @@
 // Neue Ebene <ch> erstellen mit <owner> als Ebenenbesitzer.
 // <desc> kann die statische Beschreibung der Ebene sein oder eine Closure,
 // die dynamisch aktualisierte Infos ausgibt.
-// Das Objekt <owner> kann eine Funktion check_ch_access() definieren, die
+// Das Objekt <owner> sollte eine Funktion ch_check_access() definieren, die
 // gerufen wird, wenn eine Ebenenaktion vom Typ join/leave/send/list/users
 // eingeht.
 #define IGNORE  "^/xx"
@@ -1007,9 +976,9 @@
 
   ch.members = ({ owner });
   ch.supervisor = owner;
-  // check_ch_access() dient der Zugriffskontrolle und entscheidet, ob die
+  // ch_check_access() dient der Zugriffskontrolle und entscheidet, ob die
   // Nachricht gesendet werden darf oder nicht.
-  ch.access_cl = symbol_function("check_ch_access", owner);
+  ch.access_cl = symbol_function("ch_check_access", owner);
 
   m_add(channels, ch_name, ch);
 
@@ -1057,7 +1026,8 @@
      richtige ist. */
   if (!funcall(#'access, ch, joining, C_JOIN))
     return E_ACCESS_DENIED;
-
+  //TODO: Sollte der creator das Recht auf join haben, auch wenn der aktuelle
+  //SV es verweigert? (s.u.)
   int res = add_member(ch, joining);
   if (res != 1)
     return res;
@@ -1074,10 +1044,9 @@
 // Objekt <pl> verlaesst Ebene <ch>.
 // Zugriffsrechte werden nur der Vollstaendigkeit halber geprueft; es duerfte
 // normalerweise keinen Grund geben, das Verlassen einer Ebene zu verbieten.
-// Dies ist in check_ch_access() so geregelt, allerdings koennte dem Objekt
+// Dies ist in ch_check_access() so geregelt, allerdings koennte dem Objekt
 // <pl> das Verlassen auf Grund eines Banns verboten sein.
-// Wenn kein Spieler mehr auf der Ebene ist, loest sie sich auf, sofern nicht
-// noch ein Ebenenbesitzer eingetragen ist.
+// Wenn kein Zuhoerer mehr auf der Ebene ist, loest sie sich auf.
 public int leave(string chname, object leaving)
 {
   struct channel_s ch = channels[lower_case(chname)];
@@ -1155,8 +1124,8 @@
     return E_ACCESS_DENIED;
 
   // a<2 bedeutet effektiv a==1 (weil a==0 oben rausfaellt), was dem
-  // Rueckgabewert von check_ch_access() entspricht, wenn die Aktion zugelassen
-  // wird. access() allerdings 2 fuer "privilegierte" Objekte (z.B.
+  // Rueckgabewert von ch_check_access() entspricht, wenn die Aktion zugelassen
+  // wird. access() liefert allerdings 2 fuer "privilegierte" Objekte (z.B.
   // ROOT-Objekte oder den channeld selber). Der Effekt ist, dass diese
   // Objekte auf Ebenen senden duerfen, auf denen sie nicht zuhoeren.
   if (a < 2 && !IsChannelMember(ch, sender))
diff --git a/p/daemon/channeld.init b/p/daemon/channeld.init
index e8695b3..babf6d5 100644
--- a/p/daemon/channeld.init
+++ b/p/daemon/channeld.init
@@ -1,6 +1,7 @@
 # CHANNEL MASTER INIT FILE
 # To create a new channel:
 # <name>:<recv>:<send>:<accessflags>:<channelflags>:<desc>:<supervisor>
+<MasteR>:    0: 0: 0: 1:Zentrale Informationen zu den Ebenen
 Allgemein:   0: 0: 0: 0:Allgemeine Unterhaltungsebene
 Abenteuer:   0: 0: 0: 0:Fragen die Abenteuer betreffen:/secure/questmaster
 Grats:       0: 0: 0: 0:Gratulationen zu geloesten Abenteuern etc
diff --git a/p/daemon/channeld.init.testmud b/p/daemon/channeld.init.testmud
index ea9696d..508dc2b 100644
--- a/p/daemon/channeld.init.testmud
+++ b/p/daemon/channeld.init.testmud
@@ -3,6 +3,7 @@
 # <name>:<recv>:<send>:<accessflags>:<channelflags>:<desc>:<supervisor>
 #
 # Debug-Ebenen initialisieren.
+<MasteR>:    0: 0: 0: 1:Zentrale Informationen zu den Ebenen
 Debug:       20:60:1:1:Debug und Fehlermeldungen:/p/daemon/debug
 Entwicklung: 20:60:1:1:Fehler rund um Eigenentwicklungen:/p/daemon/debug
 Warnungen:   20:60:1:1:Laufzeit-Warnungen:/p/daemon/debug
diff --git a/std/channel_supervisor.c b/std/channel_supervisor.c
index bb57717..80ed899 100644
--- a/std/channel_supervisor.c
+++ b/std/channel_supervisor.c
@@ -1,43 +1,66 @@
-// /std/channel_supervisor.c
+/* /std/channel_supervisor.c
 //
-// Der Standard-Supervisor fuer Ebenen. Wird genutzt vom channeld fuer die
-// Ebenen, die der verwaltet.
+// Der Standard-Supervisor fuer Ebenen. Sollte von allen Objekten geerbt
+// werden, die Zugriffsrechte fuer Ebenen verwalten ("das Sagen" auf der Ebene
+// haben).
+// Das Objekt braucht ggf. eine eUID zum Dateizugriff. Zum Setzen dieser ist
+// der Erbende verantwortlich.
+// Standardmaessig wird /p/daemon/channeld.init[.testmud] eingelesen.
+// Erbende koennen ihre Daten auch voellig anderweitig einlesen/ermitteln und
+// Rechte voellig anders pruefen statt gegen ein Level zu pruefen.
+// Das einzig wichtige fuer die Rechtepruefung ist die Funktion
+// ch_check_access(), die vom CHANNELD gerufen wird. Sie muss 1 zurueckgeben,
+// falls die Aktion erlaubt wird und 0 anderenfalls.
+//
+// Benutzung fuer Standardebenen:
+//    Dieses Objekt erben, ggf. eine eUID setzen und ch_read_init_file()
+//    rufen.
+//    Zusaetzlich sollte dieses Objekt aber auch ein Namen haben, weil es
+//    damit auf den Ebenen angezeigt wird. (D.h. minimal name() und Name()
+//    sollten implementiert/geerbt sein.)
+*/
 
-#pragma strict_types,save_types, rtt_checks
+#pragma strict_types, rtt_checks
 #pragma no_shadow, no_clone
 
 #include <wizlevels.h>
+#include <regexp.h>
 #include "/p/daemon/channel.h"
 #include <living/description.h>
 
-// Indizes fuer Zugriffe auf das Mapping <admin>.
-#define RECV    0
-#define SEND    1
-#define FLAG    2
-
-// Ebenenflags, gespeichert in admin[ch, FLAG]
-// F_WIZARD kennzeichnet reine Magierebenen
-#define F_WIZARD 1
-// Ebenen, auf denen keine Gaeste erlaubt sind, sind mit F_NOGUEST markiert.
-#define F_NOGUEST 2
-
 /* Speichert Sende- und Empfangslevel sowie Flags zu den einzelnen Channeln.
- * Diese werden aber nur ausgewertet, wenn der Channeld die Rechtepruefung
- * fuer die jeweilige Ebene macht, d.h. in der Praxis bei Ebenen, bei denen
- * der CHANNELD der Supervisor ist.
- * Deswegen sind diese Daten auch nicht in der struct (<channel_s>) drin.
- * Wird beim Laden des Masters via create() -> initalize() -> setup() mit den
- * Daten aus dem Init-File ./channeld.init befuellt.
-   mapping admin = ([ string channel_name : int RECV_LVL,
-                                            int SEND_LVL,
-                                            int FLAG ]) */
-private mapping admin = m_allocate(0, 3);
+   mapping ch_access_data = ([ string channel_name : (<ch_access>) ]) */
+struct ch_access {
+    int recv;
+    int send;
+    int flags;
+};
+// nicht nosave hier, damit man es zwar nosave erben kann, aber ggf. auch
+// speichern kann
+protected mapping ch_access_data = m_allocate(0, 1);
 
-// check_ch_access() prueft die Zugriffsberechtigungen auf Ebenen.
+// oeffentlicher Name des Supervisors
+protected string ch_sv_name = "Generischer SV";
+
+protected void ch_set_sv_name(string newname)
+{
+  ch_sv_name = newname;
+}
+
+public varargs string name(int casus,int demon)
+{
+  return ch_sv_name;
+}
+
+public varargs string Name(int casus, int demon)
+{
+  return capitalize(name( casus, demon )||"");
+}
+
+// ch_check_access() prueft die Zugriffsberechtigungen auf Ebenen.
 //
+// Wird vom CHANNELD gerufen.
 // Gibt 1 zurueck, wenn Aktion erlaubt, 0 sonst.
-// Wird von access() gerufen; access() gibt das Ergebnis von
-// check_ch_access() zurueck.
 //
 // Verlassen (C_LEAVE) ist immer erlaubt. Die anderen Aktionen sind in zwei
 // Gruppen eingeteilt:
@@ -47,79 +70,144 @@
 //
 // Aktionen werden zugelassen, wenn Spieler/MagierLevel groesser ist als die
 // fuer die jeweilige Aktionsgruppe RECV oder SEND festgelegte Stufe.
-// Handelt es sich um eine Magierebene (F_WIZARD), muss die Magierstufe
-// des Spielers groesser sein als die Mindeststufe der Ebene. Ansonsten
-// wird gegen den Spielerlevel geprueft.
+// Handelt es sich um eine Magierebene (<accessflags> enthaelt das Flag
+// CH_ACCESS_WIZARD), muss die Magierstufe des Spielers groesser sein als die
+// Mindeststufe der Ebene. Ansonsten wird gegen den Spielerlevel geprueft.
+// Enthaelt <accessflags> das Flag CH_ACCESS_NOGUEST, darf die Ebene nicht von
+// Gaesten benutzt werden.
 //
 // Wenn RECV_LVL oder SEND_LVL auf -1 gesetzt ist, sind die Aktionen der
 // jeweiligen Gruppen komplett geblockt.
 
-public int check_ch_access(string ch, object pl, string cmd)
+public int ch_check_access(string ch, object user, string cmd)
 {
-  // <pl> ist Gast, es sind aber keine Gaeste zugelassen? Koennen wir
+  struct ch_access access = ch_access_data[ch];
+  // <user> ist Gast, es sind aber keine Gaeste zugelassen? Koennen wir
   // direkt ablehnen.
-  if ((admin[ch, FLAG] & F_NOGUEST) && ({int})pl->QueryGuest())
+  if ((access.flags & CH_ACCESS_NOGUEST) && ({int})user->QueryGuest())
     return 0;
 
   // Ebenso auf Magier- oder Seherebenen, wenn ein Spieler anfragt, der
   // noch kein Seher ist.
-  if ((admin[ch, FLAG] & F_WIZARD) && query_wiz_level(pl) < SEER_LVL)
+  if ((access.flags & CH_ACCESS_WIZARD) && query_wiz_level(user) < SEER_LVL)
     return 0;
 
   // Ebene ist Magierebene? Dann werden alle Stufenlimits gegen Magierlevel
   // geprueft, ansonsten gegen Spielerlevel.
-  int level = (admin[ch, FLAG] & F_WIZARD
-                  ? query_wiz_level(pl)
-                  : ({int})pl->QueryProp(P_LEVEL));
+  int level = (access.flags & CH_ACCESS_WIZARD
+                  ? query_wiz_level(user)
+                  : ({int})user->QueryProp(P_LEVEL));
 
   switch (cmd)
   {
     case C_FIND:
     case C_LIST:
     case C_JOIN:
-      if (admin[ch, RECV] == -1)
+      if (access.recv == -1)
         return 0;
-      if (admin[ch, RECV] <= level)
+      if (access.recv <= level)
         return 1;
       break;
 
     case C_SEND:
-      if (admin[ch, SEND] == -1)
+      if (access.send == -1)
         return 0;
-      if (admin[ch, SEND] <= level)
+      if (access.send <= level)
         return 1;
       break;
 
     // Verlassen ist immer erlaubt
     case C_LEAVE:
       return 1;
-
-    default:
-      break;
   }
   return 0;
 }
 
-// ggf. zum ueberschreiben
-protected int ch_caller_allowed(object caller)
+// ch_store_access() - Angaben zu Zugriffsrechten fuer eine Ebene merken
+//            Angaben kommen in folgender Reihenfolge:
+//            string* chinfo = ({ channel_name, receive_level, send_level,
+//                                adminflags, channelflags, description,
+//                                supervisor })
+//            mapping &data ist im Regelfall das <ch_access_data>, was per
+//              Referenz uebergeben wird (auch wenn das nicht noetig ist,
+//              macht es klar, was beabsichtigt ist).
+protected void ch_store_access(string* chinfo, mapping data)
 {
-  if (caller == find_object(CHMASTER))
-    return 1;
-  return 0;
-}
+  object supervisor;
 
-// stores the read and write levels and flags for a channel for
-// usage in check_ch_access(). By default it is called by channeld::setup()
-// during setup of a default channel in the supervisor configured for the
-// channel.
-// recv and send are minimum levels, flag is a combination of the F_* above.
-visible void ch_supervisor_setup(string chname, int recv, int send, int flag)
-{
-  if (!extern_call() || ch_caller_allowed(previous_object()))
+  // Nur die Angabe des SV (Index 6) im initfile ist optional, alle Elemente
+  // davor muessen da sein. Wenn kein Supervisor angegeben ist, wird der
+  // Default-SV angenommen.
+
+  if (sizeof(chinfo) >= 7)
   {
-    admin[chname, FLAG] = flag;
-    admin[chname, SEND] = send;
-    admin[chname, RECV] = recv;
+    if (stringp(chinfo[6]) && sizeof(chinfo[6]))
+      supervisor = find_object(chinfo[6]);
   }
+  else if (sizeof(chinfo) == 6)
+    supervisor = find_object(DEFAULTSV);
+  else
+    return;
+
+  // Nur Daten merken, wenn wir auch der Supervisor fuer die Ebene sein
+  // sollen.
+  if (supervisor != this_object())
+    return;
+
+  struct ch_access access = (<ch_access>
+                               recv: to_int(chinfo[1]),
+                               send: to_int(chinfo[2]),
+                               flags: to_int(chinfo[3])
+                             );
+
+  data[lower_case(chinfo[0])] = access;
+  // Rest der Ebenendaten interessiert uns nicht.
+}
+
+// Liest ein channeld.init file ein. Wenn keines angegeben wird, wird der
+// das Standardfile fuers Mud eingelesen. Die Angaben werden in das Mapping
+// <ch_access_data> uebernommen, dieses vorher aber nicht geloescht.
+// Der Rueckgabewert ist < 0 fuer Fehler, ansonsten die Groesse von
+// <ch_access_data> nach Einlesen des Files.
+protected varargs int ch_read_init_file(string fname)
+{
+  string ch_list;
+  if (!fname)
+  {
+#if !defined(__TESTMUD__) && MUDNAME=="MorgenGrauen"
+    fname = CHMASTER ".init";
+#else
+    fname = CHMASTER ".init.testmud";
+#endif
+  }
+
+  ch_list = read_file(fname);
+  if (!stringp(ch_list))
+    return -1;
+
+  // Channeldatensaetze erzeugen, dazu zuerst Datenfile in Zeilen zerlegen
+  // "Allgemein:   0: 0: 0: 0: Allgemeine Unterhaltungsebene"
+  // Danach drueberlaufen und in Einzelfelder splitten, dabei gleich die
+  // Trennzeichen (Doppelpunkt, Tab und Space) rausfiltern.
+  foreach(string ch : old_explode(ch_list, "\n"))
+  {
+    if (ch[0]=='#')
+      continue;
+    ch_store_access(regexplode(ch, ":[ \t]*", RE_OMIT_DELIM),
+                    &ch_access_data);
+  }
+  return sizeof(ch_access_data);
+}
+
+// Nur falls es mal gebraucht wird und  damit es schon jetzt alle rufen
+// koennen.
+protected void create()
+{
+}
+
+public varargs int remove(int silent)
+{
+  destruct(this_object());
+  return 1;
 }