Rekursionsabsicherung QM und SMs umgebaut

Statt auf einen Zeitstempel zu gehen, wird jetzt die DI_EVAL_NUMBER
dafuer benutzt, welche bei jeder neuen Toplevel-Ausfuehrung vom
Driver erhoeht wird. Wenn die Toplevel-Ausfuehrung abgeschlossen
ist, ist die Rekursion abgeschlossen und daher ist das ein besseres
Merkmal als ein sekundengenauer Zeitstempel.

Change-Id: Iabc28d7618b8f5a8caf6870c4f45307d434fb87f
diff --git a/std/thing/properties.c b/std/thing/properties.c
index 5852704..18d076f 100644
--- a/std/thing/properties.c
+++ b/std/thing/properties.c
@@ -17,6 +17,8 @@
 #pragma range_check
 #pragma no_clone
 
+#include <driver_info.h>
+
 #define NEED_PROTOTYPES
 
 #include "/sys/thing/properties.h"
@@ -35,7 +37,10 @@
 // This is empty outside of a save_object() or restore_object() call!
 private mapping properties;
 
-// security-flag
+// security-flag: wenn gesetzt und es gleich der aktuellen DI_EVAL_NUMBER ist,
+// ist dieser Ausfuehrungsthread in einer Auswertung von F_QUERY/SET_METHOD
+// (ggf. rekursiv) und es wird jeder Aufruf als extern gewertet bzgl.
+// PROTECTED/SECURED.
 private nosave int closure_call;
 
 
@@ -71,15 +76,15 @@
 
   // Properties, die SECURED oder PROTECTED sind, duerfen nur vom Objekt
   // selber, EM+ oder ROOT veraendert werden.
-  // Es ist verfuehrerisch, das 'drop_priv||extern_call()' durch 'drop_priv'
-  // zu ersetzen, weil extern_call() das default-argument fuer <drop_priv>
-  // ist. Das ist keine gute Idee, weil <drop_priv> unter der Kontrolle des
-  // Aufrufers ist und dieser 0 uebergeben kann. Sprich: wenn es 0 ist,
-  // muessen wir dennoch selber pruefen. Wir glauben aber immer, wenn es 1
-  // ist und der Aufrufer Privilegien abgeben will.
-  if ((prop[F_MODE][name]&(PROTECTED|SECURED))&&
-      (closure_call||drop_priv||extern_call()) &&
-       previous_object() != this_object() && !allowed())
+  // Es ist verfuehrerisch, das 'drop_priv||extern_call()' durch 'drop_priv' zu
+  // ersetzen, weil extern_call() das default-argument fuer <drop_priv> ist.
+  // Das ist keine gute Idee, weil <drop_priv> unter der Kontrolle des Aufrufers
+  // ist und dieser 0 uebergeben kann. Sprich: wenn es 0 ist, muessen wir
+  // dennoch selber pruefen. Wir glauben aber immer, wenn es 1 ist, der
+  // Aufrufer hat nichts davon und das eigene Objekt darf Privilegien abgeben.
+  drop_priv = drop_priv||extern_call()||closure_call==driver_info(DI_EVAL_NUMBER);
+  if ((prop[F_MODE][name]&(PROTECTED|SECURED))
+       && drop_priv && previous_object() != this_object() && !allowed())
     return -1;
 
   // Das SECURED-Flag darf bei Properties nicht mehr geloescht werden
@@ -88,10 +93,8 @@
     return -2;
 
   // Setzen duerfen das SECURED-Flag nur das Objekt selber, EM+ oder ROOT
-  // 'drop_priv||extern_call()' ist Absicht, s.o.
-  if ((Type==F_MODE||Type==F_MODE_AS)&&(Value&SECURED)&&
-      (closure_call || drop_priv || extern_call()) &&
-       previous_object() != this_object() && !allowed() )
+  if ((Type==F_MODE||Type==F_MODE_AS) && (Value&SECURED)
+      && drop_priv && previous_object() != this_object() && !allowed() )
     return -3;
 
   switch(Type)
@@ -154,8 +157,6 @@
   mixed func = prop[F_SET_METHOD][name];
   if (func)
   {
-    int flag;
-
     // Wert als Set-Method? gleich zurueckgeben
     if (!closurep(func)) return func;
 
@@ -164,8 +165,9 @@
     // Objekt existiert auch noch (sonst waere func == 0).
 
     // closure_call setzen, falls noch nicht gesetzt
-    if ((flag=closure_call<time()))
-      closure_call = time()+59;
+    int set_closure_call = closure_call != driver_info(DI_EVAL_NUMBER);
+    if (set_closure_call)
+      closure_call = driver_info(DI_EVAL_NUMBER);
 
     // Dann mal die Closure aufrufen. Bei Fehler selbige loeschen
     if (catch(result=funcall(func, Value, name);publish))
@@ -173,8 +175,10 @@
       prop[F_SET_METHOD]-=([name]);
     }
 
-    // Wenn closure_call gesetzt wurde, wieder loeschen
-    if (flag) closure_call = 0;
+    // Wenn dieser Aufruf closure_call gesetzt hat, wieder loeschen.
+    // Da 0 ein gueltiger Wert ist, wird um eins dekrementiert, damit das Flag
+    // fuer diese Top-Level-Ausfuehrung wieder freigegeben.
+    if (set_closure_call) --closure_call;
 
     // Und zurueckgeben
     return result; 
@@ -203,8 +207,6 @@
   mixed func = prop[F_QUERY_METHOD][name];
   if (func)
   {
-    int flag;
-
     // Wert als Query-Method? Gleich zurueckgeben ...
     if (!closurep(func)) return func;
  
@@ -213,16 +215,19 @@
     // Objekt existiert auch noch (sonst waere func == 0).
    
     // closure_call setzen, falls noch nicht gesetzt
-    if ((flag=closure_call<time()))
-      closure_call = time()+59;
+    int set_closure_call = closure_call != driver_info(DI_EVAL_NUMBER);
+    if (set_closure_call)
+      closure_call = driver_info(DI_EVAL_NUMBER);
     
     // Dann Mal die Closure aufrufen. Bei Fehler selbige loeschen
     if (catch(result=funcall(func);publish))
     {
       prop[F_QUERY_METHOD]-=([name]);
     }
-    // Wenn closure_call gesetzt wurde, wieder loeschen    
-    if (flag) closure_call = 0;
+    // Wenn dieser Aufruf closure_call gesetzt hat, wieder loeschen.
+    // Da 0 ein gueltiger Wert ist, wird um eins dekrementiert, damit das Flag
+    // fuer diese Top-Level-Ausfuehrung wieder freigegeben.
+    if (set_closure_call) --closure_call;
     
     // Und zurueckgeben
     return result;
@@ -279,7 +284,7 @@
   if(!mappingp(props)) return;
 
   // Setzen wir selber?
-  same_object = (!closure_call &&
+  same_object = (closure_call!=driver_info(DI_EVAL_NUMBER) &&
                  (!extern_call()||previous_object()==this_object()||
                   allowed()));
   names = m_indices(props);