PennMUSH Community

root/1.8.3/trunk/src/attrib.c

Revision 1134, 60.1 kB (checked in by shawnw, 11 months ago)

7471: atr_add decl/prototype mismatch

Line 
1 /**
2  * \file attrib.c
3  *
4  * \brief Manipulate attributes on objects.
5  *
6  *
7  */
8 #include "copyrite.h"
9
10 #include "config.h"
11 #include <string.h>
12 #include <ctype.h>
13 #include "conf.h"
14 #include "externs.h"
15 #include "chunk.h"
16 #include "attrib.h"
17 #include "dbdefs.h"
18 #include "match.h"
19 #include "parse.h"
20 #include "htab.h"
21 #include "privtab.h"
22 #include "mymalloc.h"
23 #include "strtree.h"
24 #include "flags.h"
25 #include "mushdb.h"
26 #include "lock.h"
27 #include "log.h"
28 #include "confmagic.h"
29
30 #ifdef WIN32
31 #pragma warning( disable : 4761)        /* disable warning re conversion */
32 #endif
33
34 /** A string tree of attribute names in use, to save us memory since
35  * many are duplicated.
36  */
37 StrTree atr_names;
38 /** Table of attribute flags. */
39 extern PRIV attr_privs_set[];
40 extern PRIV attr_privs_view[];
41
42 /** A string to hold the name of a missing prefix branch, set by
43  * can_write_attr_internal.  Again, gross and ugly.  Please fix.
44  */
45 static char missing_name[ATTRIBUTE_NAME_LIMIT + 1];
46
47 /*======================================================================*/
48
49 /** How many attributes go in a "page" of attribute memory? */
50 #define ATTRS_PER_PAGE (200)
51
52 slab *attrib_slab = NULL;
53
54 static int real_atr_clr(dbref thinking, char const *atr, dbref player,
55                         int we_are_wiping);
56
57 static ATTR *alloc_atr(const void *hint);
58 static void free_atr(ATTR *);
59 static void atr_free_one(ATTR *);
60 static ATTR *find_atr_pos_in_list(ATTR ***pos, char const *name);
61 static int can_create_attr(dbref player, dbref obj, char const *atr_name,
62                            uint32_t flags);
63 static ATTR *find_atr_in_list(ATTR *atr, char const *name);
64 static ATTR *atr_get_with_parent(dbref obj, char const *atrname, dbref *parent);
65
66 /*======================================================================*/
67
68 /** Initialize the attribute string tree.
69  */
70 void
71 init_atr_name_tree(void)
72 {
73   st_init(&atr_names);
74 }
75
76 /** Lookup table for good_atr_name */
77 extern char atr_name_table[UCHAR_MAX + 1];
78
79 /** Decide if a name is valid for an attribute.
80  * A good attribute name is at least one character long, no more than
81  * ATTRIBUTE_NAME_LIMIT characters long, and every character is a
82  * valid character. An attribute name may not start or end with a backtick.
83  * An attribute name may not contain multiple consecutive backticks.
84  * \param s a string to test for validity as an attribute name.
85  */
86 int
87 good_atr_name(char const *s)
88 {
89   const unsigned char *a;
90   int len = 0;
91   if (!s || !*s)
92     return 0;
93   if (*s == '`')
94     return 0;
95   if (strstr(s, "``"))
96     return 0;
97   for (a = (const unsigned char *) s; *a; a++, len++)
98     if (!atr_name_table[*a])
99       return 0;
100   if (*(s + len - 1) == '`')
101     return 0;
102   return len <= ATTRIBUTE_NAME_LIMIT;
103 }
104
105 /** Find an attribute table entry, given a name.
106  * A trivial wrapper around aname_hash_lookup.
107  * \param string an attribute name.
108  */
109 ATTR *
110 atr_match(const char *string)
111 {
112   return aname_hash_lookup(string);
113 }
114
115 /** Find the first attribute branching off the specified attribute.
116  * \param branch the attribute to look under
117  */
118 ATTR *
119 atr_sub_branch(ATTR *branch)
120 {
121   char const *name, *n2;
122   size_t len;
123
124   name = AL_NAME(branch);
125   len = strlen(name);
126   for (branch = AL_NEXT(branch); branch; branch = AL_NEXT(branch)) {
127     n2 = AL_NAME(branch);
128     if (strlen(n2) <= len)
129       return NULL;
130     if (n2[len] == '`') {
131       if (!strncmp(n2, name, len))
132         return branch;
133       else
134         return NULL;
135     }
136   }
137   return NULL;
138 }
139
140 /** Scan an attribute list for an attribute with the specified name.
141  * This continues from whatever start point is passed in.
142  * \param atr the start of the list to search from
143  * \param name the attribute name to look for
144  * \return the matching attribute, or NULL
145  */
146 static ATTR *
147 find_atr_in_list(ATTR *atr, char const *name)
148 {
149   int comp;
150
151   while (atr) {
152     comp = strcoll(name, AL_NAME(atr));
153     if (comp < 0)
154       return NULL;
155     if (comp == 0)
156       return atr;
157     atr = AL_NEXT(atr);
158   }
159
160   return NULL;
161 }
162
163 /** Find the place to insert/delete an attribute with the specified name.
164  * \param pos a pointer to the ATTR ** holding the list position
165  * \param name the attribute name to look for
166  * \return the matching attribute, or NULL if no matching attribute
167  */
168 static ATTR *
169 find_atr_pos_in_list(ATTR ***pos, char const *name)
170 {
171   int comp;
172
173   while (**pos) {
174     comp = strcoll(name, AL_NAME(**pos));
175     if (comp < 0)
176       return NULL;
177     if (comp == 0)
178       return **pos;
179     *pos = &AL_NEXT(**pos);
180   }
181
182   return NULL;
183 }
184
185 /** Convert a string of attribute flags to a bitmask.
186  * Given a space-separated string of attribute flags, look them up
187  * and return a bitmask of them if player is permitted to use
188  * all of those flags.
189  * \param player the dbref to use for privilege checks.
190  * \param p a space-separated string of attribute flags.
191  * \param bits pointer to a privbits to store the attribute flags mask.
192  * \return 0 on success, -1 on error
193  */
194 int
195 string_to_atrflag(dbref player, char const *p, privbits *bits)
196 {
197   privbits f;
198   f = string_to_privs(attr_privs_set, p, 0);
199   if (!f)
200     return -1;
201   if (!Hasprivs(player) && (f & AF_MDARK))
202     return -1;
203   if (!See_All(player) && (f & AF_WIZARD))
204     return -1;
205   f &= ~AF_INTERNAL;
206   *bits = f;
207   return 0;
208 }
209
210 /** Convert a string of attribute flags to a pair of bitmasks.
211  * Given a space-separated string of attribute flags, look them up
212  * and return bitmasks of those to set or clear
213  * if player is permitted to use all of those flags.
214  * \param player the dbref to use for privilege checks.
215  * \param p a space-separated string of attribute flags.
216  * \param setbits pointer to address of bitmask to set.
217  * \param clrbits pointer to address of bitmask to clear.
218  * \return 0 on success or -1 on error.
219  */
220 int
221 string_to_atrflagsets(dbref player, char const *p, privbits *setbits,
222                       privbits *clrbits)
223 {
224   int f;
225   *setbits = *clrbits = 0;
226   f = string_to_privsets(attr_privs_set, p, setbits, clrbits);
227   if (f <= 0)
228     return -1;
229   if (!Hasprivs(player) && ((*setbits & AF_MDARK) || (*clrbits & AF_MDARK)))
230     return -1;
231   if (!See_All(player) && ((*setbits & AF_WIZARD) || (*clrbits & AF_WIZARD)))
232     return -1;
233   return 0;
234 }
235
236 /** Convert an attribute flag bitmask into a list of the full
237  * names of the flags.
238  * \param mask the bitmask of attribute flags to display.
239  * \return a pointer to a static buffer with the full names of the flags.
240  */
241 const char *
242 atrflag_to_string(privbits mask)
243 {
244   return privs_to_string(attr_privs_view, mask);
245 }
246
247 /*======================================================================*/
248
249 /** Traversal routine for Can_Read_Attr.
250  * This function determines if an attribute can be read by examining
251  * the tree path to the attribute.  This is not the full Can_Read_Attr
252  * check; only the stuff after See_All (just to avoid function calls
253  * when the answer is trivialized by special powers).  If the specified
254  * player is NOTHING, then we're doing a generic mortal visibility check.
255  * \param player the player trying to do the read.
256  * \param obj the object targetted for the read (may be a child of a parent!).
257  * \param atr the attribute being interrogated.
258  * \retval 0 if the player cannot read the attribute.
259  * \retval 1 if the player can read the attribute.
260  */
261 int
262 can_read_attr_internal(dbref player, dbref obj, ATTR *atr)
263 {
264   static char name[ATTRIBUTE_NAME_LIMIT + 1];
265   char *p;
266   int cansee;
267   int canlook;
268   dbref target;
269   dbref ancestor;
270   int visible;
271   int parent_depth;
272   visible = (player == NOTHING);
273   if (visible) {
274     cansee = (Visual(obj) &&
275               eval_lock(PLAYER_START, obj, Examine_Lock) &&
276               eval_lock(MASTER_ROOM, obj, Examine_Lock));
277     canlook = 0;
278   } else {
279     cansee = controls(player, obj) ||
280       (Visual(obj) && eval_lock(player, obj, Examine_Lock));
281     canlook = can_look_at(player, obj);
282   }
283
284   /* Take an easy out if there is one... */
285   /* If we can't see the attribute itself, then that's easy. */
286   if (AF_Internal(atr) || AF_Mdark(atr) ||
287       !(cansee ||
288         (AF_Visual(atr) && (!AF_Nearby(atr) || canlook)) ||
289         (!visible && !Mistrust(player) &&
290          (Owner(AL_CREATOR(atr)) == Owner(player)))))
291     return 0;
292   /* If the attribute isn't on a branch, then that's also easy. */
293   if (!strchr(AL_NAME(atr), '`'))
294     return 1;
295   /* Nope, we actually have to go looking for the attribute in a tree. */
296   strcpy(name, AL_NAME(atr));
297   ancestor = Ancestor_Parent(obj);
298   target = obj;
299   parent_depth = 0;
300   while (parent_depth < MAX_PARENTS && GoodObject(target)) {
301     /* If the ancestor of the object is in its explict parent chain,
302      * we use it there, and don't check the ancestor later.
303      */
304     if (target == ancestor)
305       ancestor = NOTHING;
306     atr = List(target);
307     /* Check along the branch for permissions... */
308     for (p = strchr(name, '`'); p; p = strchr(p + 1, '`')) {
309       *p = '\0';
310       atr = find_atr_in_list(atr, name);
311       if (!atr || (target != obj && AF_Private(atr))) {
312         *p = '`';
313         goto continue_target;
314       }
315       if (AF_Internal(atr) || AF_Mdark(atr) ||
316           !(cansee ||
317             (AF_Visual(atr) && (!AF_Nearby(atr) || canlook)) ||
318             (!visible && !Mistrust(player) &&
319              (Owner(AL_CREATOR(atr)) == Owner(player)))))
320         return 0;
321       *p = '`';
322     }
323
324     /* Now actually find the attribute. */
325     atr = find_atr_in_list(atr, name);
326     if (atr)
327       return 1;
328
329   continue_target:
330
331     /* Attribute wasn't on this object.  Check a parent or ancestor. */
332     parent_depth++;
333     target = Parent(target);
334     if (!GoodObject(target)) {
335       parent_depth = 0;
336       target = ancestor;
337     }
338   }
339
340   return 0;
341 }
342
343 /** Utility define for can_write_attr_internal and can_create_attr.
344  * \param p the player trying to write
345  * \param a the attribute to be written
346  * \param s obey the safe flag?
347  */
348 #define Cannot_Write_This_Attr(p,a,s) \
349   (!God((p)) && \
350    (AF_Internal((a)) || \
351     ((s) && AF_Safe((a))) || \
352     !(Wizard((p)) || \
353       (!AF_Wizard((a)) && \
354        (!AF_Locked((a)) || (AL_CREATOR((a)) == Owner((p))))))))
355
356 /** Traversal routine for Can_Write_Attr.
357  * This function determines if an attribute can be written by examining
358  * the tree path to the attribute.  As a side effect, missing_name is
359  * set to the name of a missing prefix branch, if any.  Yes, side effects
360  * are evil.  Please fix if you can.
361  * \param player the player trying to do the write.
362  * \param obj the object targetted for the write.
363  * \param atr the attribute being interrogated.
364  * \param safe whether to check the safe attribute flag.
365  * \retval 0 if the player cannot write the attribute.
366  * \retval 1 if the player can write the attribute.
367  */
368 int
369 can_write_attr_internal(dbref player, dbref obj, ATTR *atr, int safe)
370 {
371   char *p;
372   missing_name[0] = '\0';
373   if (Cannot_Write_This_Attr(player, atr, safe))
374     return 0;
375   strcpy(missing_name, AL_NAME(atr));
376   atr = List(obj);
377   for (p = strchr(missing_name, '`'); p; p = strchr(p + 1, '`')) {
378     *p = '\0';
379     atr = find_atr_in_list(atr, missing_name);
380     if (!atr)
381       return 0;
382     if (Cannot_Write_This_Attr(player, atr, safe)) {
383       missing_name[0] = '\0';
384       return 0;
385     }
386     *p = '`';
387   }
388
389   return 1;
390 }
391
392 /** Utility define for atr_add and can_create_attr */
393 #define set_default_flags(atr,flags) \
394   do { \
395     ATTR *std = atr_match(AL_NAME((atr))); \
396     if (std && !strcmp(AL_NAME(std), AL_NAME((atr)))) { \
397       AL_FLAGS(atr) = AL_FLAGS(std) | flags; \
398     } else { \
399         AL_FLAGS(atr) = flags; \
400     } \
401   } while (0)
402
403 /** Can an attribute of specified name be created?
404  * This function determines if an attribute can be created by examining
405  * the tree path to the attribute, and the standard attribute flags for
406  * those parts of the path that don't exist yet.
407  * \param player the player trying to do the write.
408  * \param obj the object targetted for the write.
409  * \param atr the attribute being interrogated.
410  * \param flags the default flags to add to the attribute.
411  * \retval 0 if the player cannot write the attribute.
412  * \retval 1 if the player can write the attribute.
413  */
414 static int
415 can_create_attr(dbref player, dbref obj, char const *atr_name, uint32_t flags)
416 {
417   char *p;
418   ATTR tmpatr, *atr;
419   int num_new = 1;
420   missing_name[0] = '\0';
421
422   atr = &tmpatr;
423   AL_CREATOR(atr) = player;
424   AL_NAME(atr) = atr_name;
425   set_default_flags(atr, flags);
426   if (Cannot_Write_This_Attr(player, atr, 1))
427     return AE_ERROR;
428
429   strcpy(missing_name, atr_name);
430   atr = List(obj);
431   for (p = strchr(missing_name, '`'); p; p = strchr(p + 1, '`')) {
432     *p = '\0';
433     if (atr != &tmpatr)
434       atr = find_atr_in_list(atr, missing_name);
435     if (!atr) {
436       atr = &tmpatr;
437       AL_CREATOR(atr) = Owner(player);
438     }
439     if (atr == &tmpatr) {
440       AL_NAME(atr) = missing_name;
441       set_default_flags(atr, flags);
442       num_new++;
443     }
444     /* Only GOD can create an AF_NODUMP attribute (used for semaphores)
445      * or add a leaf to a tree with such an attribute
446      */
447     if ((AL_FLAGS(atr) & AF_NODUMP) && (player != GOD)) {
448       missing_name[0] = '\0';
449       return AE_ERROR;
450     }
451     if (Cannot_Write_This_Attr(player, atr, 1)) {
452       missing_name[0] = '\0';
453       return AE_ERROR;
454     }
455     *p = '`';
456   }
457
458   if ((AttrCount(obj) + num_new) >
459       (Many_Attribs(obj) ? HARD_MAX_ATTRCOUNT : MAX_ATTRCOUNT)) {
460     do_log(LT_ERR, player, obj,
461            T("Attempt by %s(%d) to create too many attributes on %s(%d)"),
462            Name(player), player, Name(obj), obj);
463     return AE_TOOMANY;
464   }
465
466   return AE_OKAY;
467 }
468
469 /*======================================================================*/
470
471 /** Do the work of creating the attribute entry on an object.
472  * This doesn't do any permissions checking.  You should do that yourself.
473  * \param thing the object to hold the attribute
474  * \param atr_name the name for the attribute
475  */
476 static ATTR *
477 create_atr(dbref thing, char const *atr_name, const ATTR *hint)
478 {
479   ATTR *ptr, **ins;
480   char const *name;
481
482   /* put the name in the string table */
483   name = st_insert(atr_name, &atr_names);
484   if (!name)
485     return NULL;
486
487   /* allocate a new page, if needed */
488   ptr = alloc_atr(hint);
489   if (ptr == NULL) {
490     st_delete(name, &atr_names);
491     return NULL;
492   }
493
494   /* initialize atr */
495   AL_NAME(ptr) = name;
496   ptr->data = NULL_CHUNK_REFERENCE;
497   AL_FLAGS(ptr) = 0;
498
499   /* link it in */
500   ins = &List(thing);
501   (void) find_atr_pos_in_list(&ins, AL_NAME(ptr));
502   AL_NEXT(ptr) = *ins;
503   *ins = ptr;
504   AttrCount(thing)++;
505
506   return ptr;
507 }
508
509 /** Add an attribute to an object, dangerously.
510  * This is a stripped down version of atr_add, without duplicate checking,
511  * permissions checking, attribute count checking, or auto-ODARKing. 
512  * If anyone uses this outside of database load or atr_cpy (below),
513  * I will personally string them up by their toes.  - Alex
514  * \param thing object to set the attribute on.
515  * \param atr name of the attribute to set.
516  * \param s value of the attribute to set.
517  * \param player the attribute creator.
518  * \param flags bitmask of attribute flags for this attribute.
519  * \param derefs the initial deref count to use for the attribute value.
520  */
521 void
522 atr_new_add(dbref thing, const char *RESTRICT atr, const char *RESTRICT s,
523             dbref player, uint32_t flags, uint8_t derefs)
524 {
525   ATTR *ptr;
526   char *p, root_name[ATTRIBUTE_NAME_LIMIT + 1];
527
528   if (!EMPTY_ATTRS && !*s && !(flags & AF_ROOT))
529     return;
530
531   /* Don't fail on a bad name, but do log it */
532   if (!good_atr_name(atr))
533     do_rawlog(LT_ERR, T("Bad attribute name %s on object %s"), atr,
534               unparse_dbref(thing));
535
536   ptr = create_atr(thing, atr, List(thing));
537   if (!ptr)
538     return;
539
540   strcpy(root_name, atr);
541   if ((p = strrchr(root_name, '`'))) {
542     ATTR *root = NULL;
543     *p = '\0';
544     root = find_atr_in_list(List(thing), root_name);
545     if (!root) {
546       do_rawlog(LT_ERR, T("Missing root attribute '%s' on object #%d!\n"),
547                 root_name, thing);
548       root = create_atr(thing, root_name, ptr);
549       set_default_flags(root, 0);
550       AL_FLAGS(root) |= AF_ROOT;
551       AL_CREATOR(root) = player;
552       if (!EMPTY_ATTRS) {
553         unsigned char *t = compress(" ");
554         if (!t) {
555           mush_panic(T("Unable to allocate memory in atr_new_add()!"));
556         }
557         root->data = chunk_create(t, u_strlen(t), 0);
558         free(t);
559       }
560     } else {
561       if (!(AL_FLAGS(root) & AF_ROOT))  /* Upgrading old database */
562         AL_FLAGS(root) |= AF_ROOT;
563     }
564   }
565
566   AL_FLAGS(ptr) = flags;
567   AL_FLAGS(ptr) &= ~AF_COMMAND & ~AF_LISTEN;
568   AL_CREATOR