blob: 45f520a5b247c4ad2e09405de85f3a404862937b [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// thing/properties.c -- most general class (property handling)
4//
5// $Id: properties.c 6951 2008-08-09 23:08:31Z Zesstra $
6
7// Properties.c -- Propertyverwaltung
8// (c) 1993 Hate@MorgenGrauen, Mateese@NightFall
9// Idea and Code Flames and Destructions
10// -> *grin* thats the point actually :)
11//
12// Ueberarbeitet von Jof am 12.06.1994
13// Ueberarbeitet von Mandragon am 11.05.2003
14
15#pragma strict_types
16#pragma save_types
17#pragma range_check
18#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +020019
Zesstra830b2af2021-12-29 16:44:09 +010020#include <driver_info.h>
21
MG Mud User88f12472016-06-24 23:31:02 +020022#define NEED_PROTOTYPES
23
24#include "/sys/thing/properties.h"
25#include "/secure/wizlevels.h"
26
MG Mud User88f12472016-06-24 23:31:02 +020027// the mapping where the actual properties are stored. Direct initialization.
Zesstra3f3e7842022-01-16 22:33:03 +010028// Indexed with F_VALUE, F_MODE, F_SET_METHOD, F_QUERY_METHOD, F_VALIDATOR
29// F_MODE, F_SET_METHOD, F_QUERY_METHOD and F_VALIDATOR are usually 'sparse'
30// (i.e. there is no entry for most properties), therefore it is more
31// memory-efficient to store them like this than in one mapping, although it
32// requires more mapping lookups.
33private nosave mapping *prop = allocate(F_PROP_ENTRIES, ([]));
MG Mud User88f12472016-06-24 23:31:02 +020034
Zesstra375df1e2021-04-21 11:09:06 +020035// the mapping that is used for saving. During save_object/restore_object it
36// contains the properties with SAVE flag.
37// This is empty outside of a save_object() or restore_object() call!
MG Mud User88f12472016-06-24 23:31:02 +020038private mapping properties;
39
Zesstra830b2af2021-12-29 16:44:09 +010040// security-flag: wenn gesetzt und es gleich der aktuellen DI_EVAL_NUMBER ist,
41// ist dieser Ausfuehrungsthread in einer Auswertung von F_QUERY/SET_METHOD
42// (ggf. rekursiv) und es wird jeder Aufruf als extern gewertet bzgl.
43// PROTECTED/SECURED.
MG Mud User88f12472016-06-24 23:31:02 +020044private nosave int closure_call;
45
MG Mud User88f12472016-06-24 23:31:02 +020046
Zesstraf971f3a2021-04-13 11:48:37 +020047// Z.Zt. nur Abschalten des Resets noetig.
MG Mud User88f12472016-06-24 23:31:02 +020048protected void create() {
49 // Blueprints in /std benoetigenkeinen Reset ....
50 if (object_name()=="/std/thing/properties")
51 set_next_reset(-1);
52}
53
54protected void create_super() {
55 set_next_reset(-1);
56}
57
Zesstrad2c193e2022-01-16 22:49:38 +010058// Prueft, ob ein Zugriff auf eine Property erlaubt ist.
59// Properties, die SECURED oder PROTECTED sind, duerfen nur vom Objekt
60// selber, EM+ oder ROOT veraendert werden.
61#define SAVE_OBJECT 1
62#define PROP_SET 2
63#define PROTECTED_SET 3
64#define PROTECTED_DELETE 4
65#define SECURED_SET 5
66#define SECURED_DELETE 6
67nomask private int prop_check_privilege(int drop_priv, int mode,
68 int op=PROP_SET)
MG Mud User88f12472016-06-24 23:31:02 +020069{
Zesstrad2c193e2022-01-16 22:49:38 +010070 // Wenn closure_call in diesem Ausfuehrungsthread (Top-Level-Call) gesetzt
71 // wurde, zaehlt der Aufruf immer als drop_priv, auch wenn drop_priv 0 sein
72 // sollte.
73 drop_priv ||= (closure_call == driver_info(DI_EVAL_NUMBER));
MG Mud User88f12472016-06-24 23:31:02 +020074
Zesstrad2c193e2022-01-16 22:49:38 +010075 // Interne Calls oder eigenes Objekt (als letzter 'externer' Aufrufer via
76 // Callother) darf alles
77 if (!drop_priv || previous_object() == this_object())
78 return 1;
79
80 // Ab hier nur noch fremde Objekte
81 switch(op)
82 {
83 case SAVE_OBJECT:
84 // Eigentlich nur vom jeweils aktiven simul_efun-Objekt gerufen, aber
85 // der einfacheit halber sind alle ROOT-Objekte erlaubt. Aber nur
86 // direkten Caller pruefen, *nicht* ROOT_SECURITY!
87 if (get_euid(previous_object()) == ROOTID)
88 return 1;
89
90 case SECURED_DELETE:
91 // Das SECURED-Flag darf bei Properties nicht mehr geloescht werden
92 return 0;
93
94 case SECURED_SET:
95 if (geteuid(previous_object()) == ROOTID || ARCH_SECURITY)
96 return 1;
97
98 case PROTECTED_DELETE:
99 case PROTECTED_SET:
100 case PROP_SET:
101 // Properties, die schon SECURED oder PROTECTED sind, duerfen
102 // nur vom Objekt selber manipuliert werden
103 if (!(mode & (PROTECTED|SECURED)) // nicht geschuetzt
104 || (geteuid(previous_object()) == ROOTID || ARCH_SECURITY))
105 return 1;
106 }
107 return 0;
108}
MG Mud User88f12472016-06-24 23:31:02 +0200109
110// Set() -- provides direct access to a property, no filters
Zesstra5c0c4092021-09-08 23:27:54 +0200111// Type=F_VALUE und drop_priv=extern_call() by default
112public mixed Set(string name, mixed Value, int Type, int drop_priv)
MG Mud User88f12472016-06-24 23:31:02 +0200113{
Zesstrad2c193e2022-01-16 22:49:38 +0100114 int mode = prop[F_MODE][name];
Zesstra830b2af2021-12-29 16:44:09 +0100115 // Es ist verfuehrerisch, das 'drop_priv||extern_call()' durch 'drop_priv' zu
116 // ersetzen, weil extern_call() das default-argument fuer <drop_priv> ist.
117 // Das ist keine gute Idee, weil <drop_priv> unter der Kontrolle des Aufrufers
118 // ist und dieser 0 uebergeben kann. Sprich: wenn es 0 ist, muessen wir
119 // dennoch selber pruefen. Wir glauben aber immer, wenn es 1 ist, der
120 // Aufrufer hat nichts davon und das eigene Objekt darf Privilegien abgeben.
Zesstrad2c193e2022-01-16 22:49:38 +0100121 drop_priv = drop_priv||extern_call();
Zesstra5c0c4092021-09-08 23:27:54 +0200122
Zesstrad2c193e2022-01-16 22:49:38 +0100123 // Erstmal pruefen, ob Aenderung der Prop erlaubt ist.
124 if (!prop_check_privilege(drop_priv, mode, PROP_SET))
125 return -1;
126 // Bemerkung: aktuell koennen alle Objekte PROTECTED setzen. Loeschen
127 // koennen es ROOT, EM+ und ME ueber den Check oben.
128
129 // Soll SECURED geloescht werden und ist das erlaubt?
130 if ((Type==F_MODE||Type==F_MODE_AD) && (Value & SECURED)
131 && !prop_check_privilege(drop_priv, mode, SECURED_DELETE))
MG Mud User88f12472016-06-24 23:31:02 +0200132 return -2;
Zesstra5c0c4092021-09-08 23:27:54 +0200133
Zesstrad2c193e2022-01-16 22:49:38 +0100134 // Soll SECURED gesetzt werden und ist das erlaubt?
Zesstra830b2af2021-12-29 16:44:09 +0100135 if ((Type==F_MODE||Type==F_MODE_AS) && (Value&SECURED)
Zesstrad2c193e2022-01-16 22:49:38 +0100136 && !prop_check_privilege(drop_priv, mode, SECURED_SET) )
MG Mud User88f12472016-06-24 23:31:02 +0200137 return -3;
138
139 switch(Type)
140 {
141 // Je nach Modus Flags veraendern
142 case F_MODE_AS: prop[F_MODE][name]|= Value;
143 return prop[F_MODE][name];
144 case F_MODE_AD: prop[F_MODE][name]&= ~Value;
145 if (!prop[F_MODE][name]) prop[F_MODE]-=([name]);
146 return prop[F_MODE][name];
147 case F_MODE: prop[F_MODE][name]^= Value;
148 if (!prop[F_MODE][name]) prop[F_MODE]-=([name]);
149 return prop[F_MODE][name];
Zesstraf54f03a2021-12-29 19:55:08 +0100150
151 // Query- und Setmethoden laufen ab hier durch.
MG Mud User88f12472016-06-24 23:31:02 +0200152 case F_SET_METHOD:
Zesstraf54f03a2021-12-29 19:55:08 +0100153 case F_SET_MAPPER:
154 // SETMAPPED passend setzen
155 if (Type==F_SET_METHOD)
156 prop[F_MODE][name] &= ~SETMAPPED;
157 else
158 prop[F_MODE][name] |= SETMAPPED;
159 // Ab hier alles gleich
MG Mud User88f12472016-06-24 23:31:02 +0200160 // -1 als Setz-Methode: Nosetmethod setzen
161 if (Value == -1)
162 {
163 prop[F_SET_METHOD]-=([name]);
164 prop[F_MODE][name] |= NOSETMETHOD;
165 return 0;
166 }
Zesstraf54f03a2021-12-29 19:55:08 +0100167 // Kein break! Rest wie Querymethod
MG Mud User88f12472016-06-24 23:31:02 +0200168 case F_QUERY_METHOD:
Zesstra746046c2018-11-04 11:44:44 +0100169 // Ungebundene Lambda_Closure? Heutzutage ein Fehler.
170 if (closurep(Value) && !query_closure_object(Value))
MG Mud User88f12472016-06-24 23:31:02 +0200171 {
Zesstra746046c2018-11-04 11:44:44 +0100172 raise_error("Ungebundene Lambdas sind als Querymethoden "
173 "nicht mehr unterstuetzt.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200174 }
175 // Kein break!
176 default:
177 if (!Value) prop[Type]-=([name]);
178 else prop[Type][name] = Value;
179 }
180
181 // Gesamtwert zurueckgeben
182 return prop[Type][name];
183}
184
185
186// Direktes Auslesen der Property ohne Filter ...
Zesstra5c0c4092021-09-08 23:27:54 +0200187// Type = F_VALUE by default
188public mixed Query( string name, int Type )
MG Mud User88f12472016-06-24 23:31:02 +0200189{
190 if (pointerp(prop)) return prop[Type][name];
191 return 0;
192}
193
194// Property setzen unter Verwendung evtl. vorhandener Zugriffsfunktionen
Zesstraffcd0fe2021-09-08 23:33:17 +0200195public mixed SetProp( string name, mixed Value, int drop_priv)
MG Mud User88f12472016-06-24 23:31:02 +0200196{
MG Mud User88f12472016-06-24 23:31:02 +0200197 mixed result;
Zesstra10412ed2019-11-23 15:54:09 +0100198
Zesstraf54f03a2021-12-29 19:55:08 +0100199 int mode = prop[F_MODE][name];
MG Mud User88f12472016-06-24 23:31:02 +0200200 // NOSETMETHOD: Darf nicht gesetzt werden
Zesstraf54f03a2021-12-29 19:55:08 +0100201 if (mode & NOSETMETHOD ) return -1;
MG Mud User88f12472016-06-24 23:31:02 +0200202
203 // Set-Method abfragen, so vorhanden
Zesstra10412ed2019-11-23 15:54:09 +0100204 // TODO: nachdem alle moeglichen Werte als Set-Methode illegal sind, auf
205 // closure aendern.
206 mixed func = prop[F_SET_METHOD][name];
207 if (func)
MG Mud User88f12472016-06-24 23:31:02 +0200208 {
MG Mud User88f12472016-06-24 23:31:02 +0200209 // Wert als Set-Method? gleich zurueckgeben
210 if (!closurep(func)) return func;
211
212 // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene
213 // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das
214 // Objekt existiert auch noch (sonst waere func == 0).
215
216 // closure_call setzen, falls noch nicht gesetzt
Zesstra830b2af2021-12-29 16:44:09 +0100217 int set_closure_call = closure_call != driver_info(DI_EVAL_NUMBER);
218 if (set_closure_call)
219 closure_call = driver_info(DI_EVAL_NUMBER);
MG Mud User88f12472016-06-24 23:31:02 +0200220
221 // Dann mal die Closure aufrufen. Bei Fehler selbige loeschen
222 if (catch(result=funcall(func, Value, name);publish))
223 {
224 prop[F_SET_METHOD]-=([name]);
225 }
Zesstraf54f03a2021-12-29 19:55:08 +0100226 else // Erfolgreicher call
227 {
228 if (mode & SETMAPPED) // SM hat noch nicht selber gesetzt
Zesstrad2c193e2022-01-16 22:49:38 +0100229 result = Set( name, result, F_VALUE, drop_priv||extern_call() );
Zesstraf54f03a2021-12-29 19:55:08 +0100230 }
Zesstraffcd0fe2021-09-08 23:33:17 +0200231
Zesstra830b2af2021-12-29 16:44:09 +0100232 // Wenn dieser Aufruf closure_call gesetzt hat, wieder loeschen.
233 // Da 0 ein gueltiger Wert ist, wird um eins dekrementiert, damit das Flag
234 // fuer diese Top-Level-Ausfuehrung wieder freigegeben.
235 if (set_closure_call) --closure_call;
MG Mud User88f12472016-06-24 23:31:02 +0200236
237 // Und zurueckgeben
Zesstraf54f03a2021-12-29 19:55:08 +0100238 return result;
MG Mud User88f12472016-06-24 23:31:02 +0200239 }
240
Zesstraffcd0fe2021-09-08 23:33:17 +0200241 // _set_*-Methode vorhanden? falls ja, aufrufen.
MG Mud User88f12472016-06-24 23:31:02 +0200242 if (call_resolved(&result,this_object(),"_set_"+name,Value ))
243 return result;
244
245 // Letzte Moeglichkeit: Muss eine 'normale' Property sein
Zesstraffcd0fe2021-09-08 23:33:17 +0200246 // Es ist verfuehrerisch, das 'drop_priv||extern_call()' durch 'drop_priv'
247 // zu ersetzen, weil extern_call() das default-argument fuer <drop_priv>
248 // ist. Das ist keine gute Idee, weil <drop_priv> unter der Kontrolle des
249 // Aufrufers ist und dieser 0 uebergeben kann. Sprich: wenn es 0 ist,
250 // muessen wir dennoch selber pruefen. Wir glauben aber immer, wenn es 1
251 // ist und der Aufrufer Privilegien abgeben will.
252 return Set( name, Value, F_VALUE, drop_priv||extern_call() );
MG Mud User88f12472016-06-24 23:31:02 +0200253}
254
255
256// Property auslesen unter Verwendung evtl. vorhandener Zugriffsfunktionen
257public mixed QueryProp( string name )
258{
MG Mud User88f12472016-06-24 23:31:02 +0200259 mixed result;
MG Mud User88f12472016-06-24 23:31:02 +0200260 // Query-Methode vorhanden?
Zesstra10412ed2019-11-23 15:54:09 +0100261 mixed func = prop[F_QUERY_METHOD][name];
262 if (func)
MG Mud User88f12472016-06-24 23:31:02 +0200263 {
MG Mud User88f12472016-06-24 23:31:02 +0200264 // Wert als Query-Method? Gleich zurueckgeben ...
265 if (!closurep(func)) return func;
266
267 // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene
268 // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das
269 // Objekt existiert auch noch (sonst waere func == 0).
270
271 // closure_call setzen, falls noch nicht gesetzt
Zesstra830b2af2021-12-29 16:44:09 +0100272 int set_closure_call = closure_call != driver_info(DI_EVAL_NUMBER);
273 if (set_closure_call)
274 closure_call = driver_info(DI_EVAL_NUMBER);
Zesstrad2c193e2022-01-16 22:49:38 +0100275
MG Mud User88f12472016-06-24 23:31:02 +0200276 // Dann Mal die Closure aufrufen. Bei Fehler selbige loeschen
Zesstraa81eb292021-12-29 19:46:25 +0100277 if (catch(result=funcall(func, prop[F_VALUE][name]);publish))
MG Mud User88f12472016-06-24 23:31:02 +0200278 {
279 prop[F_QUERY_METHOD]-=([name]);
280 }
Zesstra830b2af2021-12-29 16:44:09 +0100281 // Wenn dieser Aufruf closure_call gesetzt hat, wieder loeschen.
282 // Da 0 ein gueltiger Wert ist, wird um eins dekrementiert, damit das Flag
283 // fuer diese Top-Level-Ausfuehrung wieder freigegeben.
284 if (set_closure_call) --closure_call;
MG Mud User88f12472016-06-24 23:31:02 +0200285
286 // Und zurueckgeben
287 return result;
288 }
MG Mud User88f12472016-06-24 23:31:02 +0200289 // _query_*-Methode vorhanden? falls ja, aufrufen.
Zesstrad2c193e2022-01-16 22:49:38 +0100290 else if (call_resolved(&result,this_object(),"_query_"+name))
MG Mud User88f12472016-06-24 23:31:02 +0200291 return result;
Zesstrad2c193e2022-01-16 22:49:38 +0100292
MG Mud User88f12472016-06-24 23:31:02 +0200293 // Hilft alles nichts. Es ist eine 'normale' Property ...
294 return prop[F_VALUE][name];
295}
296
Zesstrafaebc152021-07-06 22:21:47 +0200297// Addiert einen Wert zu einer Prop. Eigentlich ist es nur ein Short-Cut fuer
298// QueryProp und += und SetProp. Dementsprechend gehen auch hier nur
299// Typ-Kombinationen, die += auch kann.
300public mixed AddToProp(string pname,
301 <int|float|string|mixed*|mapping|bytes> add_value)
302{
303 mixed value = QueryProp(pname);
304 string err = catch(value += add_value; nolog);
305 if (err)
306 raise_error(sprintf("Nicht unterstuetzter Typ des Summanden: %s\n",
307 err[strstr(err,":")+2..]));
308
309 return SetProp(pname, value, extern_call());
310}
311
312// "subtrahiert" einen Wert von einer Prop. Eigentlich ist es nur ein
313// Short-Cut fuer QueryProp und -= und SetProp. Dementsprechend gehen auch
314// hier nur Typ-Kombinationen, die -= auch kann.
315public mixed SubFromProp(string pname,
316 <int|float|string|mixed*|mapping|bytes> sub_value)
317{
318 mixed value = QueryProp(pname);
319 string err = catch(value -= sub_value; nolog);
320 if (err)
321 raise_error(sprintf("Nicht unterstuetzter Typ des Subtrahenden: %s\n",
322 err[strstr(err,":")+2..]));
323
324 return SetProp(pname, value, extern_call());
325}
MG Mud User88f12472016-06-24 23:31:02 +0200326
Zesstra375df1e2021-04-21 11:09:06 +0200327// Viele Properties auf einen Schlag setzen.
328// genutzt von simul_efun zum Setzen aller Properties, welche im
329// restore_object() eingelesen wurden.
330// Andere objekt-externe Nutzung ausdruecklich **NICHT** supportet!
MG Mud User88f12472016-06-24 23:31:02 +0200331public void SetProperties( mapping props )
332{
Zesstrad2c193e2022-01-16 22:49:38 +0100333 int i, j;
334
MG Mud User88f12472016-06-24 23:31:02 +0200335 // Kein Mapping? Schlecht ...
336 if(!mappingp(props)) return;
337
Zesstrad2c193e2022-01-16 22:49:38 +0100338 string *names = m_indices(props);
339
MG Mud User88f12472016-06-24 23:31:02 +0200340 // Das SECURED-Flag darf nur durch das Objekt selber gesetzt werden:
Zesstrad2c193e2022-01-16 22:49:38 +0100341 // mode ist hier egal. Antwort cachen fuer den Loop
342 int no_secure = !prop_check_privilege(extern_call(), 0, SECURED_SET);
MG Mud User88f12472016-06-24 23:31:02 +0200343
344 j=sizeof(names);
345 while(j--)
346 {
Zesstrad2c193e2022-01-16 22:49:38 +0100347 if (prop_check_privilege(extern_call(), prop[F_MODE][names[j]],
348 PROP_SET))
MG Mud User88f12472016-06-24 23:31:02 +0200349 {
Zesstrad2c193e2022-01-16 22:49:38 +0100350 // Prop-Setzen erlaubt, aber ggf. SECURED flag loeschen, wenn nicht
351 // das nicht erlaubt ist (s.o.).
352 if (no_secure)
353 props[names[j], F_MODE] &= ~SECURED;
354 // jetzt die einzelnen Teile der Prop seztzen.
Zesstra3f3e7842022-01-16 22:33:03 +0100355 i=F_PROP_ENTRIES;
MG Mud User88f12472016-06-24 23:31:02 +0200356 while(i--)
357 {
358 if(props[names[j],i])
359 prop[i][names[j]] = props[names[j], i];
360 else
361 prop[i]-=([names[j]]);
362 }
363 }
364 }
365 return;
366}
367
368
369// Ein Mapping mit allen Properties zurueckgeben
Zesstra375df1e2021-04-21 11:09:06 +0200370// genutzt von simul_efun im save_object() zur Abfrage aller Properties, um
371// hieraus die gespeicherten zu bestimmen.
372// Andere objekt-externe Nutzung ausdruecklich **NICHT** supportet!
Zesstrad2c193e2022-01-16 22:49:38 +0100373// TODO fuer zukuenftige Features (wie private Properties) auf die simul_efun
374// beschraenken.
MG Mud User88f12472016-06-24 23:31:02 +0200375public mapping QueryProperties()
376{
377 mapping props;
378 int i, j;
379 string *names;
380
Zesstra3f3e7842022-01-16 22:33:03 +0100381 props = m_allocate( sizeof(prop[F_VALUE]), F_PROP_ENTRIES );
Zesstrad2c193e2022-01-16 22:49:38 +0100382
MG Mud User88f12472016-06-24 23:31:02 +0200383 if (pointerp(prop))
384 {
Zesstra3f3e7842022-01-16 22:33:03 +0100385 i=F_PROP_ENTRIES;
MG Mud User88f12472016-06-24 23:31:02 +0200386 while(i--)
387 {
388 names = m_indices(prop[i]);
389 j=sizeof(names);
390 while(j--) props[names[j], i] = prop[i][names[j]];
391 }
392 }
393 return props;
394}
395
396// Die Properties als urspruengliches Array zurueckgeben
397public mixed *__query_properties()
398{
399 if ( pointerp(prop) )
400 return(deep_copy(prop));
401 else
Zesstra3f3e7842022-01-16 22:33:03 +0100402 return allocate(F_PROP_ENTRIES, ([]));
MG Mud User88f12472016-06-24 23:31:02 +0200403}
404
405
406// mapping Properties setzen zum Speichern (per save_object())
Zesstra375df1e2021-04-21 11:09:06 +0200407// Aufruf nur aus simul_efun heraus (sinnvoll). Diese fragt alle Properties
408// via QueryProperties() ab, filtert alle nicht-gespeicherten Properties aus
409// und setzt ueber diese Funktion die gespeicherten in der tatsaechlich von
410// save_object() gespeicherten Variable <properties>.
MG Mud User88f12472016-06-24 23:31:02 +0200411public void _set_save_data(mixed data) { properties = data; }
412
413// mapping Properties zum Restoren zurueckgeben
Zesstra375df1e2021-04-21 11:09:06 +0200414// Aufruf nur aus simul_efun heraus (sinnvoll). Diese ruft nach dem
415// restore_object die restaurierten Properties hiermit ab und schreibt sie
416// nach Konditionierung via SetProperties() zurueck, damit die Daten (wieder)
417// in <prop> zur Verfuegung steheh.
MG Mud User88f12472016-06-24 23:31:02 +0200418public mixed _get_save_data() { return properties; }
419