| // MorgenGrauen MUDlib |
| // |
| // thing/properties.c -- most general class (property handling) |
| // |
| // $Id: properties.c 6951 2008-08-09 23:08:31Z Zesstra $ |
| |
| // Properties.c -- Propertyverwaltung |
| // (c) 1993 Hate@MorgenGrauen, Mateese@NightFall |
| // Idea and Code Flames and Destructions |
| // -> *grin* thats the point actually :) |
| // |
| // Ueberarbeitet von Jof am 12.06.1994 |
| // Ueberarbeitet von Mandragon am 11.05.2003 |
| |
| #pragma strict_types |
| #pragma save_types |
| #pragma range_check |
| #pragma no_clone |
| |
| #include <driver_info.h> |
| |
| #define NEED_PROTOTYPES |
| |
| #include "/sys/thing/properties.h" |
| #include "/secure/wizlevels.h" |
| |
| // the mapping where the actual properties are stored. Direct initialization. |
| // Indexed with F_VALUE, F_MODE, F_SET_METHOD, F_QUERY_METHOD, F_VALIDATOR |
| // F_MODE, F_SET_METHOD, F_QUERY_METHOD and F_VALIDATOR are usually 'sparse' |
| // (i.e. there is no entry for most properties), therefore it is more |
| // memory-efficient to store them like this than in one mapping, although it |
| // requires more mapping lookups. |
| private nosave mapping *prop = allocate(F_PROP_ENTRIES, ([])); |
| |
| // the mapping that is used for saving. During save_object/restore_object it |
| // contains the properties with SAVE flag. |
| // This is empty outside of a save_object() or restore_object() call! |
| private mapping properties; |
| |
| |
| // Z.Zt. nur Abschalten des Resets noetig. |
| protected void create() { |
| // Blueprints in /std benoetigenkeinen Reset .... |
| if (object_name()=="/std/thing/properties") |
| set_next_reset(-1); |
| } |
| |
| protected void create_super() { |
| set_next_reset(-1); |
| } |
| |
| // Prueft, ob ein Zugriff auf eine Property erlaubt ist. |
| // Properties, die SECURED oder PROTECTED sind, duerfen nur vom Objekt |
| // selber, EM+ oder ROOT veraendert werden. |
| #define SAVE_OBJECT 1 |
| #define PROP_SET 2 |
| #define PROTECTED_SET 3 |
| #define PROTECTED_DELETE 4 |
| #define SECURED_SET 5 |
| #define SECURED_DELETE 6 |
| #define RAW_ACCESS 7 |
| nomask private int prop_check_privilege(int drop_priv, int mode, |
| int op=PROP_SET) |
| { |
| // Interne Calls oder eigenes Objekt (als letzter 'externer' Aufrufer via |
| // Callother) darf alles |
| if (!drop_priv || previous_object() == this_object()) |
| |
| return 1; |
| |
| // Ab hier nur noch fremde Objekte |
| switch(op) |
| { |
| case SAVE_OBJECT: |
| // Eigentlich nur vom jeweils aktiven simul_efun-Objekt gerufen, aber |
| // der einfacheit halber sind alle ROOT-Objekte erlaubt. Aber nur |
| // direkten Caller pruefen, *nicht* ROOT_SECURITY! |
| if (geteuid(previous_object()) == ROOTID) |
| return 1; |
| |
| case SECURED_DELETE: |
| // Das SECURED-Flag darf bei Properties nicht mehr geloescht werden |
| return 0; |
| |
| case SECURED_SET: |
| if (geteuid(previous_object()) == ROOTID || ARCH_SECURITY) |
| return 1; |
| |
| case RAW_ACCESS: |
| // RAW_ACCESS grants access to all properties and their raw, uncopied |
| // data. It is only for very specific objects and they *MUST NOT* change |
| // it or give user-code (or any other) access to it. |
| if (load_name(previous_object()) == "/obj/tools/MGtool") |
| return 1; |
| return 0; |
| |
| case PROTECTED_DELETE: |
| case PROTECTED_SET: |
| case PROP_SET: |
| // Properties, die schon SECURED oder PROTECTED sind, duerfen |
| // nur vom Objekt selber manipuliert werden |
| if (!(mode & (PROTECTED|SECURED)) // nicht geschuetzt |
| || (geteuid(previous_object()) == ROOTID || ARCH_SECURITY)) |
| return 1; |
| } |
| return 0; |
| } |
| |
| // Set() -- provides direct access to a property, no filters |
| // Type=F_VALUE und drop_priv=extern_call() by default |
| public mixed Set(string name, mixed Value, int Type, int drop_priv) |
| { |
| int mode = prop[F_MODE][name]; |
| // 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(); |
| |
| // Erstmal pruefen, ob Aenderung der Prop erlaubt ist. |
| if (!prop_check_privilege(drop_priv, mode, PROP_SET)) |
| return -1; |
| // Bemerkung: aktuell koennen alle Objekte PROTECTED setzen. Loeschen |
| // koennen es ROOT, EM+ und ME ueber den Check oben. |
| |
| // Soll SECURED geloescht werden und ist das erlaubt? |
| if ( (Type==F_MODE||Type==F_MODE_AD) && (Value & SECURED) |
| && (prop[F_MODE][name] & SECURED) |
| && !prop_check_privilege(drop_priv, mode, SECURED_DELETE)) |
| return -2; |
| |
| // Soll SECURED gesetzt werden und ist das erlaubt? |
| if ( (Type==F_MODE||Type==F_MODE_AS) && (Value&SECURED) |
| && !(prop[F_MODE][name] & SECURED) |
| && !prop_check_privilege(drop_priv, mode, SECURED_SET) ) |
| return -3; |
| |
| switch(Type) |
| { |
| // Je nach Modus Flags veraendern |
| case F_MODE_AS: |
| prop[F_MODE][name]|= Value; |
| return prop[F_MODE][name]; |
| case F_MODE_AD: |
| prop[F_MODE][name]&= ~Value; |
| if (!prop[F_MODE][name]) prop[F_MODE]-=([name]); |
| return prop[F_MODE][name]; |
| case F_MODE: |
| prop[F_MODE][name]^= Value; |
| if (!prop[F_MODE][name]) prop[F_MODE]-=([name]); |
| return prop[F_MODE][name]; |
| |
| case F_SET_METHOD: |
| if (!Value) |
| { |
| prop[Type]-=([name]); |
| break; |
| } |
| // Ungebundene Lambda_Closure? Heutzutage ein Fehler. |
| if (closurep(Value) && !query_closure_object(Value)) |
| { |
| raise_error("Ungebundene Lambdas sind als Setmethoden " |
| "nicht mehr unterstuetzt.\n"); |
| } |
| // -1 als Setz-Methode: Stattdessen NOSETMETHOD setzen (deprecated!) |
| if (Value == -1) |
| { |
| prop[F_SET_METHOD]-=([name]); |
| prop[F_MODE][name] |= NOSETMETHOD; |
| return 0; |
| } |
| // Zur Sicherheit SETMAPPED loeschen |
| prop[F_MODE][name] &= ~SETMAPPED; |
| prop[Type][name] = Value; |
| break; |
| |
| case F_SET_MAPPER: |
| // Als Type F_SET_METHOD speichern/eintragen. |
| Type = F_SET_METHOD; |
| if (!Value) |
| { |
| prop[F_MODE][name] &= ~SETMAPPED; |
| prop[Type]-=([name]); |
| break; |
| } |
| // Da wir hier keine Ruecksicht auf historischen Kram nehmen muessen, |
| // werden nur Closures akzeptiert. Und auch keine -1 als Alias fuer |
| // NOSETMETHOD. |
| if (!closurep(Value)) |
| { |
| raise_error("Fuer F_SET_MAPPER werden nur Closures unterstuetzt.\n"); |
| } |
| // Und keine ungebundenen Lambda-Closures. |
| if (!query_closure_object(Value)) |
| { |
| raise_error("Ungebundene Lambdas sind als Mapper " |
| "nicht unterstuetzt.\n"); |
| } |
| // SETMAPPED setzen |
| prop[F_MODE][name] |= SETMAPPED; |
| prop[Type][name] = Value; |
| break; |
| |
| case F_QUERY_METHOD: |
| if (!Value) |
| { |
| prop[Type]-=([name]); |
| break; |
| } |
| // Jetzt wirklich mal nen Fehler werfen und den Zopf abschneiden. |
| if (!closurep(Value)) |
| { |
| raise_error("Fuer F_QUERY_METHOD werden nur noch Closures " |
| "unterstuetzt.\n"); |
| } |
| // Ungebundene Lambda-Closure? Heutzutage ein Fehler. |
| if (!query_closure_object(Value)) |
| { |
| raise_error("Ungebundene Lambdas sind als Querymethoden " |
| "nicht mehr unterstuetzt.\n"); |
| } |
| prop[Type][name] = Value; |
| break; |
| |
| default: |
| if (!Value) prop[Type]-=([name]); |
| else prop[Type][name] = Value; |
| } |
| |
| // Gesamtwert zurueckgeben |
| return prop[Type][name]; |
| } |
| |
| |
| // Direktes Auslesen der Property ohne Filter ... |
| // Type = F_VALUE by default |
| public mixed Query( string name, int Type ) |
| { |
| if (pointerp(prop)) return prop[Type][name]; |
| return 0; |
| } |
| |
| // Property setzen unter Verwendung evtl. vorhandener Zugriffsfunktionen |
| public mixed SetProp( string name, mixed Value, int drop_priv) |
| { |
| mixed result; |
| |
| int mode = prop[F_MODE][name]; |
| // NOSETMETHOD: Darf nicht gesetzt werden |
| if (mode & NOSETMETHOD ) return -1; |
| |
| // Set-Method abfragen, so vorhanden |
| // TODO: nachdem alle moeglichen Werte als Set-Methode illegal sind, auf |
| // closure aendern. |
| mixed func = prop[F_SET_METHOD][name]; |
| if (func) |
| { |
| // Wert als Set-Method? gleich zurueckgeben |
| if (!closurep(func)) return func; |
| |
| // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene |
| // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das |
| // Objekt existiert auch noch (sonst waere func == 0). |
| |
| // Dann mal die Closure aufrufen. Bei Fehler selbige loeschen |
| if (catch(result=funcall(func, Value, name);publish)) |
| { |
| prop[F_SET_METHOD]-=([name]); |
| } |
| else // Erfolgreicher call |
| { |
| if (mode & SETMAPPED) // SM hat noch nicht selber gesetzt |
| result = Set( name, result, F_VALUE, drop_priv||extern_call() ); |
| } |
| |
| // Und zurueckgeben |
| return result; |
| } |
| |
| // _set_*-Methode vorhanden? falls ja, aufrufen. |
| if (call_resolved(&result,this_object(),"_set_"+name,Value )) |
| return result; |
| |
| // Letzte Moeglichkeit: Muss eine 'normale' Property sein |
| // 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. |
| return Set( name, Value, F_VALUE, drop_priv||extern_call() ); |
| } |
| |
| |
| // Property auslesen unter Verwendung evtl. vorhandener Zugriffsfunktionen |
| public mixed QueryProp( string name ) |
| { |
| mixed result; |
| // Query-Methode vorhanden? |
| mixed func = prop[F_QUERY_METHOD][name]; |
| if (func) |
| { |
| // Wert als Query-Method? Gleich zurueckgeben ... |
| if (!closurep(func)) return func; |
| |
| // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene |
| // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das |
| // Objekt existiert auch noch (sonst waere func == 0). |
| |
| // Dann Mal die Closure aufrufen. Bei Fehler selbige loeschen |
| if (catch(result=funcall(func, prop[F_VALUE][name]);publish)) |
| { |
| prop[F_QUERY_METHOD]-=([name]); |
| } |
| |
| // Und zurueckgeben |
| return result; |
| } |
| // _query_*-Methode vorhanden? falls ja, aufrufen. |
| else if (call_resolved(&result,this_object(),"_query_"+name)) |
| return result; |
| |
| // Hilft alles nichts. Es ist eine 'normale' Property ... |
| return prop[F_VALUE][name]; |
| } |
| |
| // Addiert einen Wert zu einer Prop. Eigentlich ist es nur ein Short-Cut fuer |
| // QueryProp und += und SetProp. Dementsprechend gehen auch hier nur |
| // Typ-Kombinationen, die += auch kann. |
| public mixed AddToProp(string pname, |
| <int|float|string|mixed*|mapping|bytes> add_value) |
| { |
| mixed value = QueryProp(pname); |
| string err = catch(value += add_value; nolog); |
| if (err) |
| raise_error(sprintf("Nicht unterstuetzter Typ des Summanden: %s\n", |
| err[strstr(err,":")+2..])); |
| |
| return SetProp(pname, value, extern_call()); |
| } |
| |
| // "subtrahiert" einen Wert von einer Prop. Eigentlich ist es nur ein |
| // Short-Cut fuer QueryProp und -= und SetProp. Dementsprechend gehen auch |
| // hier nur Typ-Kombinationen, die -= auch kann. |
| public mixed SubFromProp(string pname, |
| <int|float|string|mixed*|mapping|bytes> sub_value) |
| { |
| mixed value = QueryProp(pname); |
| string err = catch(value -= sub_value; nolog); |
| if (err) |
| raise_error(sprintf("Nicht unterstuetzter Typ des Subtrahenden: %s\n", |
| err[strstr(err,":")+2..])); |
| |
| return SetProp(pname, value, extern_call()); |
| } |
| |
| // Viele Properties auf einen Schlag setzen. |
| // Wurde genutzt von simul_efun zum Setzen aller Properties, welche im |
| // restore_object() eingelesen wurden. |
| // Nutzer in Lib: Magiershell (upd), Spellbooks Zaubis, Klerus, Lupe |
| // Andere objekt-externe Nutzung ausdruecklich **NICHT** supportet! |
| // Problem: ggf. Verlust von SECURED, wenn Props kopiert werden (im alten |
| // SECURED; im neuen nicht und nicht-privilegiertes Objekt |
| // Problem 2: beim Kopieren von einem alten in ein neues Objekt gehen alle |
| // internen QM/SM auf das eigene Objekt ersatzlos kaputt. |
| // Problem 3: beim "Clonen" von Objekten haben beide Clones dann geteilte |
| // Referenztypen wie Mappings/Structs/Arrays |
| public deprecated void SetProperties( mapping props ) |
| { |
| int i, j; |
| |
| // Kein Mapping? Schlecht ... |
| if(!mappingp(props)) return; |
| |
| string *names = m_indices(props); |
| |
| // Das SECURED-Flag darf nur durch das Objekt selber gesetzt werden: |
| // mode ist hier egal. Antwort cachen fuer den Loop |
| int no_secure = !prop_check_privilege(extern_call(), 0, SECURED_SET); |
| |
| j=sizeof(names); |
| while(j--) |
| { |
| if (prop_check_privilege(extern_call(), prop[F_MODE][names[j]], |
| PROP_SET)) |
| { |
| // Prop-Setzen erlaubt, aber ggf. SECURED flag loeschen, wenn nicht |
| // das nicht erlaubt ist (s.o.). |
| if (no_secure) |
| props[names[j], F_MODE] &= ~SECURED; |
| // jetzt die einzelnen Teile der Prop seztzen. |
| i=F_PROP_ENTRIES; |
| while(i--) |
| { |
| if(props[names[j],i]) |
| prop[i][names[j]] = props[names[j], i]; |
| else |
| prop[i]-=([names[j]]); |
| } |
| } |
| } |
| } |
| |
| |
| // Ein Mapping mit allen Properties zurueckgeben |
| // Wurde genutzt von simul_efun im save_object() zur Abfrage aller Properties, |
| // um hieraus die gespeicherten zu bestimmen. |
| // Nutzer in Lib: Magiershell (upd), Spellbooks Zaubis, Klerus, Lupe, |
| // Listmaster-Zaubis, Fingerdaemon, Kaempfergilde (Karteikarten) |
| // Andere objekt-externe Nutzung ausdruecklich **NICHT** supportet! |
| // TODO fuer zukuenftige Features (wie private Properties) auf die simul_efun |
| // beschraenken. |
| // Problem: QueryProperties: die Werte entsprechen Query, nicht QueryProp und |
| // Abfragende werten die QMs in der Regel nicht aus (und waere auch nicht |
| // dieselbe Abfragesituation und wuerde daher nicht funktionieren). |
| // Problem 2: Herausgabe interner Daten wie Closures, Referenztypen wie |
| // Mappings/Arrays/Structs etc. Diese koennen dann geaendert werden oder im |
| // Falle von Closures sind ggf. private lfuns rufbar (stimmt: momentan koennen |
| // die auch per Query abgefragt werden). |
| public deprecated mapping QueryProperties() |
| { |
| mapping props; |
| int i, j; |
| string *names; |
| |
| props = m_allocate( sizeof(prop[F_VALUE]), F_PROP_ENTRIES ); |
| |
| if (pointerp(prop)) |
| { |
| i=F_PROP_ENTRIES; |
| while(i--) |
| { |
| names = m_indices(prop[i]); |
| j=sizeof(names); |
| while(j--) props[names[j], i] = prop[i][names[j]]; |
| } |
| } |
| return props; |
| } |
| |
| // Die Properties als urspruengliches Array zurueckgeben, wird fuers MG-Tool |
| // benoetigt. |
| public mixed *__query_properties() |
| { |
| // RAW_ACCESS grants access to all properties and their raw, uncopied data. |
| // It is only for very specific objects. |
| if (!prop_check_privilege(extern_call(), 0, RAW_ACCESS)) |
| return 0; |
| |
| if ( pointerp(prop) ) |
| return prop; |
| else |
| return allocate(F_PROP_ENTRIES, ([])); |
| } |
| |
| |
| // Wird aus simul_efun (und nur von dort) gerufen, sollte die Properties aufs |
| // Speichern vorbereiten und alle *gespeicherten* Props in passender Form in |
| // der gespeicherten Variable <properties> ablegen. |
| public void _prop_prepare_save() |
| { |
| // 2 Werte pro Prop (Wert + Modusflags) speichern |
| // F_SET_METHOD, F_QUERY_METHOD werden nicht gespeichert. |
| // Anzahl gespeicherter Props raten, 20% der Props mit Modusflags |
| properties = m_allocate(sizeof(prop[F_MODE])/5, 2); |
| foreach(string pname, int mode: prop[F_MODE]) |
| { |
| if (mode & SAVE) |
| { |
| // Uralte, ungenutzte Flags loeschen sowie SETMAPPED (F_SET_METHOD wird |
| // nicht gespeichert) |
| properties[pname, F_MODE] = mode & |
| (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED|SETMAPPED)); |
| properties[pname, F_VALUE] = prop[F_VALUE][pname]; |
| } |
| } |
| } |
| |
| // Wird nach dem Speichern gerufen und kann ggf. aufraeumen. Vor allem wird |
| // der Inhalt von <properties> geloescht, da dies nur beim Speichern relevant |
| // ist (und auch nicht gecached werden kann!). |
| public void _prop_finalize_save() |
| { |
| properties = 0; |
| } |
| |
| // Konvertiert die Daten in <properties> wieder in die Laufzeitform und mergt |
| // sie mit den bereits existierenden Props. |
| public void _prop_finalize_restore() |
| { |
| // Da wir das eigene Savefile oder Savestring restaurieren, koennen wir |
| // alle Rechte ignorieren. Wer das Savefile schreiben kann, hat gewonnen... |
| // Und Savestrings muss das Objekt selber in seinen restore_object()-Aufruf |
| // schreiben. |
| foreach(string pname, mixed value, int mode: properties) |
| { |
| prop[F_VALUE][pname] = value; |
| // mode aus dem Savefile wird uebernommen, ausser SETMAPPED, was vom |
| // bisher gueltigem mode uebernommen werden muss. |
| if (prop[F_MODE][pname] & SETMAPPED) |
| mode &= SETMAPPED; |
| prop[F_MODE][pname] = mode; |
| // F_SET_METHOD und F_QUERY_METHOD werden beibehalten, sollten sie vor dem |
| // restore bereits gesetzt sein. Aus dem Savefile kommen beide nie. |
| } |
| properties = 0; |
| } |