/* Remove all aliases for a standard attr. */ static int free_standard_attr_aliases(ATTR *a) { bool found_alias; ATTR *curr; const char *aliasname; int count = 0; /* Annoyingly convoluted because the ptab_delete will screw with the * counter used by ptab_nextextry_new */ do { found_alias = 0; curr = ptab_firstentry_new(&ptab_attrib, &aliasname); for (; curr; curr = ptab_nextentry_new(&ptab_attrib, &aliasname)) { if (!strcmp(AL_NAME(curr), AL_NAME(a)) && strcmp(AL_NAME(curr), aliasname)) { found_alias = 1; ptab_delete(&ptab_attrib, aliasname); count++; break; } } } while (found_alias); return count; }
/** Free all memory used by a standard attribute, and remove it from the hash * table if necessary. * \param a attr to remove * \param inserted has the attr been inserted into the hash table already? * \retval number of entries (including aliases) removed from the hash table */ static int free_standard_attr(ATTR *a, bool inserted) { int count = 0; if (!a) { return count; } /* If the attr has no name, there's no way it can be in the hash table */ if (AL_NAME(a)) { if (inserted) { count = free_standard_attr_aliases(a) + 1; ptab_delete(&ptab_attrib, AL_NAME(a)); } free((char *) AL_NAME(a)); } if (a->data != NULL_CHUNK_REFERENCE) { chunk_delete(a->data); } mush_free(a, "ATTR"); return count; }
/** Return a list of standard attributes. * This functions returns the list of standard attributes, separated by * spaces, in a statically allocated buffer. */ char * list_attribs(void) { ATTR *ap; const char *ptrs[BUFFER_LEN / 2]; static char buff[BUFFER_LEN]; char *bp; const char *name; int nptrs = -1, i; for (ap = ptab_firstentry_new(&ptab_attrib, &name); ap; ap = ptab_nextentry_new(&ptab_attrib, &name)) { if (strcmp(name, AL_NAME(ap))) continue; ptrs[++nptrs] = AL_NAME(ap); } bp = buff; if (nptrs >= 0) safe_str(ptrs[0], buff, &bp); for (i = 1; i < nptrs; i++) { safe_chr(' ', buff, &bp); safe_str(ptrs[i], buff, &bp); } *bp = '\0'; return buff; }
/** Decompile the standard attribute table, as per \@attribute/decompile * \param player The enactor * \param pattern Wildcard pattern of attrnames to decompile * \param retroactive Include the /retroactive switch? */ void do_decompile_attribs(dbref player, char *pattern, int retroactive) { ATTR *ap; const char *name; notify(player, T("@@ Standard Attributes:")); for (ap = ptab_firstentry_new(&ptab_attrib, &name); ap; ap = ptab_nextentry_new(&ptab_attrib, &name)) { if (strcmp(name, AL_NAME(ap))) continue; if (pattern && *pattern && !quick_wild(pattern, AL_NAME(ap))) continue; notify_format(player, "@attribute/access%s %s=%s", (retroactive ? "/retroactive" : ""), AL_NAME(ap), privs_to_string(attr_privs_view, AL_FLAGS(ap))); if (ap->flags & AF_RLIMIT) { notify_format(player, "@attribute/limit %s=%s", AL_NAME(ap), display_attr_limit(ap)); } else if (ap->flags & AF_ENUM) { notify_format(player, "@attribute/enum %s=%s", AL_NAME(ap), display_attr_limit(ap)); } } }
/** Display information on an attribute from the table. * \verbatim * Top-level function for @attribute. * \endverbatim * \param player the enactor. * \param name the name of the attribute. */ void do_attribute_info(dbref player, char *name) { ATTR *ap; if (!name || !*name) { notify(player, T("Which attribute do you mean?")); return; } /* Is this attribute in the table? */ if (*name == '@') name++; ap = aname_hash_lookup(name); if (!ap) { notify(player, T("That attribute isn't in the attribute table")); return; } notify_format(player, "%9s: %s", T("Attribute"), AL_NAME(ap)); if (ap->flags & AF_RLIMIT) { notify_format(player, "%9s: %s", T("Limit"), display_attr_limit(ap)); } else if (ap->flags & AF_ENUM) { notify_format(player, "%9s: %s", T("Enum"), display_attr_limit(ap)); } notify_format(player, "%9s: %s", T("Flags"), privs_to_string(attr_privs_view, AL_FLAGS(ap))); notify_format(player, "%9s: %s", T("Creator"), unparse_dbref(AL_CREATOR(ap))); return; }
/* Add a new, or restrict an existing, standard attribute from cnf file */ int cnf_attribute_access(char *attrname, char *opts) { ATTR *a; privbits flags = 0; upcasestr(attrname); if (!good_atr_name(attrname)) return 0; if (strcasecmp(opts, "none")) { flags = list_to_privs(attr_privs_set, opts, 0); if (!flags) return 0; } a = (ATTR *) ptab_find_exact(&ptab_attrib, attrname); if (a) { if (AF_Internal(a)) return 0; } else { a = (ATTR *) mush_malloc(sizeof(ATTR), "ATTR"); if (!a) return 0; AL_NAME(a) = strdup(attrname); a->data = NULL_CHUNK_REFERENCE; ptab_insert_one(&ptab_attrib, attrname, a); } AL_FLAGS(a) = flags; AL_CREATOR(a) = GOD; return 1; }
static void grep_add_attr(char *buff, char **bp, dbref player, int count, ATTR *attr, char *atrval) { if (buff) { if (count) safe_chr(' ', buff, bp); safe_str(AL_NAME(attr), buff, bp); } else { notify_format(player, "%s%s [#%d%s]:%s %s", ANSI_HILITE, AL_NAME(attr), Owner(AL_CREATOR(attr)), privs_to_letters(attr_privs_view, AL_FLAGS(attr)), ANSI_END, atrval); } }
void attr_write_all(PENNFILE *f) { int attrcount = 0, aliascount = 0; ATTR *a; const char *attrname; char *data; for (a = ptab_firstentry_new(&ptab_attrib, &attrname); a; a = ptab_nextentry_new(&ptab_attrib, &attrname)) { if (!strcmp(attrname, AL_NAME(a))) attrcount++; else aliascount++; } db_write_labeled_int(f, "attrcount", attrcount); for (a = ptab_firstentry_new(&ptab_attrib, &attrname); a; a = ptab_nextentry_new(&ptab_attrib, &attrname)) { if (strcmp(attrname, AL_NAME(a))) continue; /* skip aliases */ db_write_labeled_string(f, " name", AL_NAME(a)); db_write_labeled_string(f, " flags", privs_to_string(attr_privs_db, AL_FLAGS(a))); db_write_labeled_dbref(f, " creator", AL_CREATOR(a)); data = atr_value(a); db_write_labeled_string(f, " data", data); } db_write_labeled_int(f, "attraliascount", aliascount); for (a = ptab_firstentry_new(&ptab_attrib, &attrname); a; a = ptab_nextentry_new(&ptab_attrib, &attrname)) { if (!strcmp(attrname, AL_NAME(a))) continue; /* skip non-aliases */ db_write_labeled_string(f, " name", AL_NAME(a)); db_write_labeled_string(f, " alias", attrname); } }
static void display_attr_info(dbref player, ATTR *ap) { notify_format(player, "%9s: %s", T("Attribute"), AL_NAME(ap)); if (ap->flags & AF_RLIMIT) { notify_format(player, "%9s: %s", T("Limit"), display_attr_limit(ap)); } else if (ap->flags & AF_ENUM) { notify_format(player, "%9s: %s", T("Enum"), display_attr_limit(ap)); } notify_format(player, "%9s: %s", T("Flags"), privs_to_string(attr_privs_view, AL_FLAGS(ap))); notify_format(player, "%9s: %s", T("Creator"), unparse_dbref(AL_CREATOR(ap))); return; }
/** Add a new attribute. Called from db.c to add new attributes * to older databases which have their own attr table. * \param name name of attr to add * \param flags attribute flags (AF_*) */ void add_new_attr(char *name, uint32_t flags) { ATTR *ap; ap = (ATTR *) ptab_find_exact(&ptab_attrib, name); if (ap || !good_atr_name(name)) return; ap = (ATTR *) mush_malloc(sizeof(ATTR), "ATTR"); if (!ap) { do_log(LT_ERR, 0, 0, "add_new_attr: unable to malloc ATTR"); return; } AL_NAME(ap) = strdup(name); ap->data = NULL_CHUNK_REFERENCE; AL_FLAGS(ap) = flags; AL_CREATOR(ap) = 0; ptab_insert_one(&ptab_attrib, name, ap); }
/** Rename an attribute in the attribute table. * \verbatim * Top-level function for @attrib/rename. * \endverbatim * \param player the enactor. * \param old the name of the attribute to rename. * \param newname the new name (surprise!) */ void do_attribute_rename(dbref player, char *old, char *newname) { ATTR *ap; if (!old || !*old || !newname || !*newname) { notify(player, T("Which attributes do you mean?")); return; } upcasestr(old); upcasestr(newname); /* Is the new name valid? */ if (!good_atr_name(newname)) { notify(player, T("Invalid attribute name.")); return; } /* Is the new name already in use? */ ap = (ATTR *) ptab_find_exact(&ptab_attrib, newname); if (ap) { notify_format(player, T("The name %s is already used in the attribute table."), newname); return; } /* Is the old name a real attribute? */ ap = (ATTR *) ptab_find_exact(&ptab_attrib, old); if (!ap) { notify(player, T("That attribute isn't in the attribute table")); return; } /* Ok, take it out and put it back under the new name */ ptab_delete(&ptab_attrib, old); /* This causes a slight memory leak if you rename an attribute added via /access. But that doesn't happen often. Will fix someday. */ AL_NAME(ap) = strdup(newname); ptab_insert_one(&ptab_attrib, newname, ap); notify_format(player, T("Renamed %s to %s in attribute table."), old, newname); return; }
/** Return a list of standard attributes. * This functions returns the list of standard attributes, separated by * spaces, in a statically allocated buffer. */ char * list_attribs(void) { ATTR *ap; const char *ptrs[BUFFER_LEN / 2]; static char buff[BUFFER_LEN]; char *bp; int nptrs = 0, i; ap = (ATTR *) ptab_firstentry(&ptab_attrib); ptrs[0] = ""; while (ap) { ptrs[nptrs++] = AL_NAME(ap); ap = (ATTR *) ptab_nextentry(&ptab_attrib); } bp = buff; safe_str(ptrs[0], buff, &bp); for (i = 1; i < nptrs; i++) { safe_chr(' ', buff, &bp); safe_str(ptrs[i], buff, &bp); } *bp = '\0'; return buff; }
/** Populate a ufun_attrib struct from an obj/attr pair. * \verbatim Given an attribute [<object>/]<name> pair (which may include #lambda), * fetch its value, owner (thing), and pe_flags, and store in the struct * pointed to by ufun * \endverbatim * \param attrstring The obj/name of attribute. * \param executor Dbref of the executing object. * \param ufun Pointer to an allocated ufun_attrib struct to fill in. * \param flags A bitwise or of desired UFUN_* flags. * \return 0 on failure, true on success. */ bool fetch_ufun_attrib(const char *attrstring, dbref executor, ufun_attrib * ufun, int flags) { char *thingname, *attrname; char astring[BUFFER_LEN]; ATTR *attrib; if (!ufun) return 0; ufun->contents[0] = '\0'; ufun->errmess = (char *) ""; ufun->thing = executor; ufun->pe_flags = PE_UDEFAULT; ufun->ufun_flags = flags; ufun->thing = executor; thingname = NULL; if (!attrstring) return 0; strncpy(astring, attrstring, BUFFER_LEN); /* Split obj/attr */ if ((flags & UFUN_OBJECT) && ((attrname = strchr(astring, '/')) != NULL)) { thingname = astring; *(attrname++) = '\0'; } else { attrname = astring; } if (thingname && (flags & UFUN_LAMBDA) && (strcasecmp(thingname, "#lambda") == 0 || strncasecmp(thingname, "#apply", 6) == 0)) { /* It's a lambda. */ ufun->ufun_flags &= ~UFUN_NAME; ufun->thing = executor; if (strcasecmp(thingname, "#lambda") == 0) mush_strncpy(ufun->contents, attrname, BUFFER_LEN); else { /* #apply */ char *ucb = ufun->contents; unsigned nargs = 1, n; thingname += 6; if (*thingname) nargs = parse_uinteger(thingname); /* Limit between 1 and 10 arguments (%0-%9) */ if (nargs == 0) nargs = 1; if (nargs > 10) nargs = 10; safe_str(attrname, ufun->contents, &ucb); safe_chr('(', ufun->contents, &ucb); for (n = 0; n < nargs; n++) { if (n > 0) safe_chr(',', ufun->contents, &ucb); safe_format(ufun->contents, &ucb, "%%%u", n); } safe_chr(')', ufun->contents, &ucb); *ucb = '\0'; } ufun->attrname[0] = '\0'; return 1; } if (thingname) { /* Attribute is on something else. */ ufun->thing = noisy_match_result(executor, thingname, NOTYPE, MAT_EVERYTHING); if (!GoodObject(ufun->thing)) { ufun->errmess = (char *) "#-1 INVALID OBJECT"; return 0; } } attrib = (ATTR *) atr_get(ufun->thing, upcasestr(attrname)); if (attrib && AF_Internal(attrib)) { /* Regardless of whether we're doing permission checks, we should * never be showing internal attributes here */ attrib = NULL; } /* An empty attrib is the same as no attrib. */ if (attrib == NULL) { if (flags & UFUN_REQUIRE_ATTR) { if (!(flags & UFUN_IGNORE_PERMS) && !Can_Examine(executor, ufun->thing)) ufun->errmess = e_atrperm; return 0; } else { mush_strncpy(ufun->attrname, attrname, ATTRIBUTE_NAME_LIMIT + 1); return 1; } } if (!(flags & UFUN_IGNORE_PERMS) && !Can_Read_Attr(executor, ufun->thing, attrib)) { ufun->errmess = e_atrperm; return 0; } if (!(flags & UFUN_IGNORE_PERMS) && !CanEvalAttr(executor, ufun->thing, attrib)) { ufun->errmess = e_perm; return 0; } /* DEBUG attributes */ if (AF_NoDebug(attrib)) ufun->pe_flags |= PE_NODEBUG; /* No_Debug overrides Debug */ else if (AF_Debug(attrib)) ufun->pe_flags |= PE_DEBUG; if (flags & UFUN_NAME) { if (attrib->flags & AF_NONAME) ufun->ufun_flags &= ~UFUN_NAME; else if (attrib->flags & AF_NOSPACE) ufun->ufun_flags |= UFUN_NAME_NOSPACE; } /* Populate the ufun object */ mush_strncpy(ufun->contents, atr_value(attrib), BUFFER_LEN); mush_strncpy(ufun->attrname, AL_NAME(attrib), ATTRIBUTE_NAME_LIMIT + 1); /* We're good */ return 1; }
/** Add new standard attributes, or change permissions on them. * \verbatim * Given the name and permission string for an attribute, add it to * the attribute table (or modify the permissions if it's already * there). Permissions may be changed retroactively, which modifies * permissions on any copies of that attribute set on objects in the * database. This is the top-level code for @attribute/access. * \endverbatim * \param player the enactor. * \param name the attribute name. * \param perms a string of attribute permissions, space-separated. * \param retroactive if true, apply the permissions retroactively. */ void do_attribute_access(dbref player, char *name, char *perms, int retroactive) { ATTR *ap, *ap2; privbits flags = 0; int i; int insert = 0; /* Parse name and perms */ if (!name || !*name) { notify(player, T("Which attribute do you mean?")); return; } if (strcasecmp(perms, "none")) { flags = list_to_privs(attr_privs_set, perms, 0); if (!flags) { notify(player, T("I don't understand those permissions.")); return; } } upcasestr(name); /* Is this attribute already in the table? */ ap = (ATTR *) ptab_find_exact(&ptab_attrib, name); if (ap) { if (AF_Internal(ap)) { /* Don't muck with internal attributes */ notify(player, T("That attribute's permissions can not be changed.")); return; } } else { /* Create fresh if the name is ok */ if (!good_atr_name(name)) { notify(player, T("Invalid attribute name.")); return; } insert = 1; ap = (ATTR *) mush_malloc(sizeof(ATTR), "ATTR"); if (!ap) { notify(player, T("Critical memory failure - Alert God!")); do_log(LT_ERR, 0, 0, "do_attribute_access: unable to malloc ATTR"); return; } AL_NAME(ap) = strdup(name); ap->data = NULL_CHUNK_REFERENCE; } AL_FLAGS(ap) = flags; AL_CREATOR(ap) = player; /* Only insert when it's not already in the table */ if (insert) { ptab_insert_one(&ptab_attrib, name, ap); } /* Ok, now we need to see if there are any attributes of this name * set on objects in the db. If so, and if we're retroactive, set * perms/creator */ if (retroactive) { for (i = 0; i < db_top; i++) { if ((ap2 = atr_get_noparent(i, name))) { if (AL_FLAGS(ap2) & AF_ROOT) AL_FLAGS(ap2) = flags | AF_ROOT; else AL_FLAGS(ap2) = flags; AL_CREATOR(ap2) = player; } } } notify_format(player, T("%s -- Attribute permissions now: %s"), name, privs_to_string(attr_privs_view, flags)); }
void attr_read_all(PENNFILE *f) { ATTR *a; int c, found, count = 0; char alias[BUFFER_LEN]; /* Clear existing attributes */ ptab_free(&ptab_attrib); ptab_start_inserts(&ptab_attrib); db_read_this_labeled_int(f, "attrcount", &count); for (found = 0;;) { c = penn_fgetc(f); penn_ungetc(c, f); if (c != ' ') break; found++; if ((a = attr_read(f))) ptab_insert(&ptab_attrib, a->name, a); } ptab_end_inserts(&ptab_attrib); if (found != count) do_rawlog(LT_ERR, "WARNING: Actual number of attrs (%d) different than expected count (%d).", found, count); /* Assumes we'll always have at least one alias */ db_read_this_labeled_int(f, "attraliascount", &count); for (found = 0;;) { c = penn_fgetc(f); penn_ungetc(c, f); if (c != ' ') break; found++; if ((a = attr_alias_read(f, alias))) { upcasestr(alias); if (!good_atr_name(alias)) { do_rawlog(LT_ERR, "Bad attribute name on alias '%s' in db.", alias); } else if (aname_find_exact(strupper(alias))) { do_rawlog(LT_ERR, "Unable to alias attribute '%s' to '%s' in db: alias already in use.", AL_NAME(a), alias); } else if (!alias_attribute(AL_NAME(a), alias)) { do_rawlog(LT_ERR, "Unable to alias attribute '%s' to '%s' in db.", AL_NAME(a), alias); } } } if (found != count) do_rawlog(LT_ERR, "WARNING: Actual number of attr aliases (%d) different than expected count (%d).", found, count); return; }
static ATTR * attr_read(PENNFILE *f) { ATTR *a; char *tmp; dbref d = GOD; privbits flags = 0; a = (ATTR *) mush_malloc(sizeof(ATTR), "ATTR"); if (!a) { mush_panic("Not enough memory to add attribute in attr_read()!"); return NULL; } AL_NAME(a) = NULL; a->data = NULL_CHUNK_REFERENCE; AL_FLAGS(a) = 0; AL_CREATOR(a) = GOD; a->next = NULL; db_read_this_labeled_string(f, "name", &tmp); if (!good_atr_name(tmp)) { do_rawlog(LT_ERR, "Invalid attribute name '%s' in db.", tmp); (void) getstring_noalloc(f); /* flags */ (void) getstring_noalloc(f); /* creator */ (void) getstring_noalloc(f); /* data */ free_standard_attr(a, 0); return NULL; } AL_NAME(a) = strdup(tmp); db_read_this_labeled_string(f, "flags", &tmp); if (tmp && *tmp && strcasecmp(tmp, "none")) { flags = list_to_privs(attr_privs_db, tmp, 0); if (!flags) { do_rawlog(LT_ERR, "Invalid attribute flags for '%s' in db.", AL_NAME(a)); free((char *) AL_NAME(a)); (void) getstring_noalloc(f); /* creator */ (void) getstring_noalloc(f); /* data */ free_standard_attr(a, 0); return NULL; } } AL_FLAGS(a) = flags; db_read_this_labeled_dbref(f, "creator", &d); AL_CREATOR(a) = d; db_read_this_labeled_string(f, "data", &tmp); if (!tmp || !*tmp || !(AL_FLAGS(a) & (AF_ENUM | AF_RLIMIT))) { a->data = NULL_CHUNK_REFERENCE; } else if (AL_FLAGS(a) & AF_ENUM) { /* Store string as it is */ char *t = compress(tmp); a->data = chunk_create(t, strlen(t), 0); free(t); } else if (AL_FLAGS(a) & AF_RLIMIT) { /* Need to validate regexp */ char *t; pcre *re; const char *errptr; int erroffset; re = pcre_compile(tmp, PCRE_CASELESS, &errptr, &erroffset, tables); if (!re) { do_rawlog(LT_ERR, "Invalid regexp in limit for attribute '%s' in db.", AL_NAME(a)); free_standard_attr(a, 0); return NULL; } pcre_free(re); /* don't need it, just needed to check it */ t = compress(tmp); a->data = chunk_create(t, strlen(t), 0); free(t); } return a; }