/* 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; }
/** 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)); }
/** Limit an attribute's possible values, using either an enum or a * regexp /limit. * \verbatim * Given a name, restriction type and string for an attribute, * set its data value to said data and set a flag for limit or * enum. * * For an enum, the attr's data will be set to * <delim><pattern><delim>, so a simple strstr() can be used when * matching the pattern. * * An optional delimiter can be provided on the left hand side by using * @attr/enum <delim> <attrname>=<enum list> * \endverbatim * \param player the enactor. * \param name the attribute name. * \param type AF_RLIMIT for regexp, AF_ENUM for enum. * \param pattern The allowed pattern for the attribute. */ void do_attribute_limit(dbref player, char *name, int type, char *pattern) { ATTR *ap; char buff[BUFFER_LEN]; char *ptr, *bp; char delim = ' '; pcre *re; const char *errptr; int erroffset; int unset = 0; if (pattern && *pattern) { if (type == AF_RLIMIT) { /* Compile to regexp. */ re = pcre_compile(remove_markup(pattern, NULL), PCRE_CASELESS, &errptr, &erroffset, tables); if (!re) { notify(player, T("Invalid Regular Expression.")); return; } /* We only care if it's valid, we're not using it. */ free(re); /* Copy it to buff to be placed into ap->data. */ snprintf(buff, BUFFER_LEN, "%s", pattern); } else if (type == AF_ENUM) { ptr = name; /* Check for a delimiter: @attr/enum | attrname=foo */ if ((name = strchr(ptr, ' ')) != NULL) { *(name++) = '\0'; if (strlen(ptr) > 1) { notify(player, T("Delimiter must be one character.")); return; } delim = *ptr; } else { name = ptr; delim = ' '; } /* For speed purposes, we require the pattern to begin and end with * a delimiter. */ snprintf(buff, BUFFER_LEN, "%c%s%c", delim, pattern, delim); buff[BUFFER_LEN - 1] = '\0'; /* For sanity's sake, we'll enforce a properly delimited enum * with a quick and dirty squish(). * We already know we start with a delim, hence the +1 =). */ for (ptr = buff + 1, bp = buff + 1; *ptr; ptr++) { if (!(*ptr == delim && *(ptr - 1) == delim)) { *(bp++) = *ptr; } } *bp = '\0'; } else { /* Err, we got called with the wrong limit type? */ notify(player, T("Unknown limit type?")); return; } } else { unset = 1; } /* Parse name and perms */ if (!name || !*name) { notify(player, T("Which attribute do you mean?")); return; } upcasestr(name); if (*name == '@') name++; /* Is this attribute already in the table? */ ap = (ATTR *) ptab_find_exact(&ptab_attrib, name); if (!ap) { notify(player, T ("I don't know that attribute. Please use @attribute/access to create it, first.")); return; } if (AF_Internal(ap)) { /* Don't muck with internal attributes */ notify(player, T("That attribute's permissions cannot be changed.")); return; } /* All's good, set the data and the AF_RLIMIT or AF_ENUM flag. */ if (ap->data != NULL_CHUNK_REFERENCE) { chunk_delete(ap->data); } /* Clear any extant rlimit or enum flags */ ap->flags &= ~(AF_RLIMIT | AF_ENUM); if (unset) { if (ap->data != NULL_CHUNK_REFERENCE) { ap->data = NULL_CHUNK_REFERENCE; notify_format(player, T("%s -- Attribute limit or enum unset."), name); } else { notify_format(player, T("%s -- Attribute limit or enum already unset."), name); } } else { unsigned char *t = compress(buff); ap->data = chunk_create(t, u_strlen(t), 0); free(t); ap->flags |= type; notify_format(player, T("%s -- Attribute %s set to: %s"), name, type == AF_RLIMIT ? "limit" : "enum", display_attr_limit(ap)); } }