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/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;
 }