/** Is a name a valid player name when applied by player to thing? * Player names must be valid object names, but also not forbidden (unless * the player is a wizard). They are * subject to a different length limit, and subject to more stringent * restrictions on valid characters. Finally, it can't be the same as * an existing player name or alias unless it's one of theirs. * \param name name to check. * \param player player for permission checks. * \param thing player who will get the name. * \retval 1 name is valid for players. * \retval 0 name is not valid for players. */ int ok_player_name(const char *name, dbref player, dbref thing) { const char *scan, *good; dbref lookup; if (!ok_name(name, 0) || strlen(name) > (size_t) PLAYER_NAME_LIMIT) return 0; good = (PLAYER_NAME_SPACES || Wizard(player) ? " `$_-.,'" : "`$_-.,'"); /* Make sure that the name contains legal characters only */ for (scan = name; scan && *scan; scan++) { if (isalnum(*scan)) continue; if (!strchr(good, *scan)) return 0; } lookup = lookup_player(name); /* A player may only change to a forbidden name if they're already using that name. */ if (forbidden_name(name)) { if (!((GoodObject(player) && Wizard(player)) || (GoodObject(thing) && lookup == thing))) { return 0; } } return ((lookup == NOTHING) || (lookup == thing)); }
/** Open a new exit. * \verbatim * This is the top-level function for @open. It calls do_real_open() * to do the real work of opening both the exit forward and the exit back. * \endverbatim * \param player the enactor. * \param direction name of the exit forward. * \param links 1-based array, possibly containing name of destination, name of exit back, * and room to open initial exit from. */ void do_open(dbref player, const char *direction, char **links) { dbref forward; dbref source = NOTHING; if (links[3]) { source = match_result(player, links[3], TYPE_ROOM, MAT_HERE | MAT_ABSOLUTE | MAT_TYPE); if (!GoodObject(source)) { notify(player, T("Open from where?")); return; } } forward = do_real_open(player, direction, links[1], source); if (links[2] && GoodObject(forward) && GoodObject(Location(forward))) { char sourcestr[SBUF_LEN]; /* SBUF_LEN is the size used by unparse_dbref */ if (!GoodObject(source)) { if (IsRoom(player)) { source = player; } else if (IsExit(player)) { source = Home(player); } else { source = Location(player); } } strcpy(sourcestr, unparse_dbref(source)); do_real_open(player, links[2], sourcestr, Location(forward)); } }
static lock_list * getlockstruct(dbref thing, lock_type type) { lock_list *ll; dbref p = thing, ancestor = NOTHING; int cmp, count = 0, ancestor_in_chain = 0; if (GoodObject(thing)) ancestor = Ancestor_Parent(thing); do { for (; GoodObject(p); p = Parent(p)) { if (count++ > 100) return NULL; if (p == ancestor) ancestor_in_chain = 1; ll = Locks(p); while (ll && L_TYPE(ll)) { cmp = strcasecmp(L_TYPE(ll), type); if (cmp == 0) return (p != thing && (ll->flags & LF_PRIVATE)) ? NULL : ll; else if (cmp > 0) break; ll = ll->next; } } p = ancestor; } while (!ancestor_in_chain && !Orphan(thing) && GoodObject(ancestor)); return NULL; }
HS_DBREF CHSInterface::GetNextContent(HS_DBREF dbPrevContent) { #ifdef PENNMUSH if (!GoodObject(dbPrevContent)) { return HSNOTHING; } else { HS_DBREF dbNext = Next(dbPrevContent); return GoodObject(dbNext) ? dbNext : HSNOTHING; } #endif // PENNMUSH #if defined(TM3) || defined(MUX) HS_DBREF dbNext; if (!Good_obj(dbPrevContent)) { return HSNOTHING; } else { dbNext = Next(dbPrevContent); } return Good_obj(dbNext) ? dbNext : HSNOTHING; #endif }
/** Return the first object near another object that is visible to a player. * * BEWARE: * * first_visible() does not behave as intended. It _should_ return the first * object in `thing' that is !DARK. However, because of the controls() check * the function will return a DARK object if the player owns it. * * The behavior is left as is because so many functions in fundb.c rely on * the incorrect behavior to return expected values. The lv*() functions * also make rewriting this fairly pointless. * * \param player the looker. * \param thing an object in the location to be inspected. * \return dbref of first visible object or NOTHING. */ dbref first_visible(dbref player, dbref thing) { int lck = 0; int ldark; dbref loc; if (!GoodObject(thing) || IsRoom(thing)) return NOTHING; loc = IsExit(thing) ? Source(thing) : Location(thing); if (!GoodObject(loc)) return NOTHING; ldark = IsPlayer(loc) ? Opaque(loc) : Dark(loc); while (GoodObject(thing)) { if (can_interact(thing, player, INTERACT_SEE, NULL)) { if (DarkLegal(thing) || (ldark && !Light(thing))) { if (!lck) { if (See_All(player) || (loc == player) || controls(player, loc)) return thing; lck = 1; } if (controls(player, thing)) /* this is what causes DARK objects to show */ return thing; } else { return thing; } } thing = Next(thing); } return thing; }
/** Actually change the ownership of something, and fix bits. * \param player the enactor. * \param thing object to change ownership of. * \param newowner new owner for thing. * \param preserve if 1, preserve privileges and don't halt. */ void chown_object(dbref player, dbref thing, dbref newowner, int preserve) { (void) undestroy(player, thing); if (God(player)) { Owner(thing) = newowner; } else { Owner(thing) = Owner(newowner); } /* Don't allow circular zones */ Zone(thing) = NOTHING; if (GoodObject(Zone(newowner))) { dbref tmp; int ok_to_zone = 1; int zone_depth = MAX_ZONES; for (tmp = Zone(Zone(newowner)); GoodObject(tmp); tmp = Zone(tmp)) { if (tmp == thing) { notify(player, T("Circular zone broken.")); ok_to_zone = 0; break; } if (tmp == Zone(tmp)) /* Ran into an object zoned to itself */ break; zone_depth--; if (!zone_depth) { ok_to_zone = 0; notify(player, T("Overly deep zone chain broken.")); break; } } if (ok_to_zone) Zone(thing) = Zone(newowner); } clear_flag_internal(thing, "CHOWN_OK"); if (!preserve || !Wizard(player)) { clear_flag_internal(thing, "WIZARD"); clear_flag_internal(thing, "ROYALTY"); clear_flag_internal(thing, "TRUST"); set_flag_internal(thing, "HALT"); destroy_flag_bitmask("POWER", Powers(thing)); Powers(thing) = new_flag_bitmask("POWER"); do_halt(thing, "", thing); } else { if (preserve == 1 && (newowner != player) && Wizard(thing) && !Wizard(newowner)) { notify_format(player, T("Warning: WIZ flag reset on #%d because " "@CHOWN/PRESERVE is to a non-WIZ flagged third-party."), thing); clear_flag_internal(thing, "WIZARD"); } if (!null_flagmask("POWER", Powers(thing)) || Wizard(thing) || Royalty(thing) || Inherit(thing)) notify_format(player, T("Warning: @CHOWN/PRESERVE on an object (#%d) " "with WIZ, ROY, INHERIT, or @power privileges."), thing); } }
// Handles getting the contents of an object, which is often // specific to each game driver. void CHSInterface::GetContents(int loc, CSTLDbrefList & rlistDbrefs, int type) { int thing; #ifdef PENNMUSH // No change in code between versions for (thing = db[loc].contents; GoodObject(thing); thing = Next(thing)) { if (type != NOTYPE) { if (Typeof(thing) == type) { rlistDbrefs.push_back(thing); } } else { rlistDbrefs.push_back(thing); } } #endif #if defined(TM3) || defined(MUX) for (thing = Contents(loc); Good_obj(thing); thing = Next(thing)) { if (type != NOTYPE) { if (Typeof(thing) == type) rlistDbrefs.push_back(thing); } else rlistDbrefs.push_back(thing); } #endif }
/** Does params contain only acceptable HTML tag attributes? * Right now, this means: filter out SEND and XCH_CMD if * the player isn't privileged. Params may contain a space-separated * list of tag=value pairs. It's probably possible to fool this * checking. Needs more work, or removing HTML support. * \param player player using the attribute, or NOTHING for internal. * \param params the attributes to use. * \retval 1 params is acceptable. * \retval 0 params is not accpetable. */ int ok_tag_attribute(dbref player, const char *params) { const char *p, *q; if (!GoodObject(player) || Can_Pueblo_Send(player)) return 1; p = params; while (*p) { while (*p && isspace(*p)) p++; q = p; while (*q && *q != '=') q++; if (*q) { size_t n = q - p; /* Invalid params for non-priv'd. Turn to a hashtable if we ever get more? */ if (strncasecmp(p, "SEND", n) == 0 || strncasecmp(p, "XCH_CMD", n) == 0) return 0; while (*q && isspace(*q)) q++; while (*q && !isspace(*q)) q++; p = q; } else return 0; /* Malformed param without an = */ } return 1; }
/** The teach command. * \param player the enactor. * \param tbuf1 the command being taught. * \param list is tbuf1 an action list, or a single command? * \param parent_queue the queue entry to run the command in */ void do_teach(dbref player, const char *tbuf1, int list, MQUE *parent_queue) { dbref loc; int flags = QUEUE_RECURSE; char lesson[BUFFER_LEN], *lp; loc = speech_loc(player); if (!GoodObject(loc)) return; if (!Loud(player) && !eval_lock_with(player, loc, Speech_Lock, parent_queue->pe_info)) { fail_lock(player, loc, Speech_Lock, T("You may not speak here!"), NOTHING); return; } if (!tbuf1 || !*tbuf1) { notify(player, T("What command do you want to teach?")); return; } if (!list) flags |= QUEUE_NOLIST; lp = lesson; safe_format(lesson, &lp, T("%s types --> %s%s%s"), spname(player), ANSI_HILITE, tbuf1, ANSI_END); *lp = '\0'; notify_anything(player, player, na_loc, &loc, NULL, NA_INTER_HEAR | NA_PROPAGATE, lesson, NULL, loc, NULL); new_queue_actionlist(player, parent_queue->enactor, player, (char *) tbuf1, parent_queue, PE_INFO_SHARE, flags, NULL); }
/* utility for open and link */ static dbref parse_linkable_room(dbref player, const char *room_name) { dbref room; /* parse room */ if (!strcasecmp(room_name, "here")) { room = IsExit(player) ? Source(player) : Location(player); } else if (!strcasecmp(room_name, "home")) { return HOME; /* HOME is always linkable */ } else { room = parse_objid(room_name); } /* check room */ if (!GoodObject(room)) { notify(player, T("That is not a valid object.")); return NOTHING; } else if (Going(room)) { notify(player, T("That room is being destroyed. Sorry.")); return NOTHING; } else if (!can_link_to(player, room)) { notify(player, T("You can't link to that.")); return NOTHING; } else { return room; } }
/** Take an action on an object and trigger attributes. * \verbatim * executes the @attr, @oattr, @aattr for a command - gives a message * to the enactor and others in the room with the enactor, and executes * an action. We optionally load pe_regs into the queue. * \endverbatim * * \param player the enactor. * \param thing object being triggered. * \param what message attribute for enactor. * \param def default message to enactor. * \param owhat message attribute for others. * \param odef default message to others. * \param awhat action attribute to trigger. * \param loc location in which action is taking place. * \param pe_regs the pe_regs arguments for the evaluation/queueing * \param flags flags controlling type of interaction involved. * \retval 0 no attributes were present, only defaults were used if given. * \retval 1 some attributes were evaluated and used. */ int real_did_it(dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc, PE_REGS *pe_regs, int flags, int an_flags) { char buff[BUFFER_LEN], *bp; int attribs_used = 0; NEW_PE_INFO *pe_info = NULL; ufun_attrib ufun; if (!pe_info) { pe_info = make_pe_info("pe_info-real_did_it2"); } loc = (loc == NOTHING) ? Location(player) : loc; /* only give messages if the location is good */ if (GoodObject(loc)) { /* message to player */ if (what && *what) { if (fetch_ufun_attrib (what, thing, &ufun, UFUN_LOCALIZE | UFUN_REQUIRE_ATTR | UFUN_IGNORE_PERMS)) { attribs_used = 1; if (!call_ufun(&ufun, buff, thing, player, pe_info, pe_regs) && buff[0]) notify_by(thing, player, buff); } else if (def && *def) notify_by(thing, player, def); } /* message to neighbors */ if (!DarkLegal(player)) { if (owhat && *owhat && fetch_ufun_attrib(owhat, thing, &ufun, UFUN_LOCALIZE | UFUN_REQUIRE_ATTR | UFUN_IGNORE_PERMS | UFUN_NAME)) { attribs_used = 1; if (!call_ufun_int (&ufun, buff, thing, player, pe_info, pe_regs, (void *) AName(player, an_flags, NULL)) && buff[0]) notify_except2(player, loc, player, thing, buff, flags); } else if (odef && *odef) { bp = buff; safe_format(buff, &bp, "%s %s", AName(player, an_flags, NULL), odef); *bp = '\0'; notify_except2(player, loc, player, thing, buff, flags); } } } if (pe_info) { free_pe_info(pe_info); } if (awhat && *awhat) attribs_used = queue_attribute_base(thing, awhat, player, 0, pe_regs, 0) || attribs_used; return attribs_used; }
/** Are two objects near each other? * Returns 1 if obj1 is "nearby" object2. "Nearby" is a commutative * relation defined as: * obj1 is in the same room as obj2, obj1 is being carried by * obj2, or obj1 is carrying obj2. * Returns 0 if object isn't nearby or the input is invalid. * \param obj1 first object. * \param obj2 second object. * \retval 1 the objects are near each other. * \retval 0 the objects are not near each other. */ int nearby(dbref obj1, dbref obj2) { dbref loc1, loc2; if (!GoodObject(obj1) || !GoodObject(obj2)) return 0; if (IsRoom(obj1) && IsRoom(obj2)) return 0; loc1 = where_is(obj1); if (loc1 == obj2) return 1; loc2 = where_is(obj2); if ((loc2 == obj1) || (loc2 == loc1)) return 1; return 0; }
/** Add a lock to an object (primitive). * Set the lock type on thing to boolexp. * This is a primitive routine, to be called by other routines. * It will go somewhat wonky if given a NULL boolexp. * It will allocate memory if called with a string that is not already * in the lock table. * \param player the enactor, for permission checking. * \param thing object on which to set the lock. * \param type type of lock to set. * \param key lock boolexp pointer (should not be NULL!). * \param flags lock flags. * \retval 0 failure. * \retval 1 success. */ int add_lock(dbref player, dbref thing, lock_type type, boolexp key, int flags) { lock_list *ll, **t; lock_type real_type = type; if (!GoodObject(thing)) { return 0; } ll = getlockstruct_noparent(thing, type); if (ll) { if (!can_write_lock(player, thing, ll)) { free_boolexp(key); return 0; } /* We're replacing an existing lock. */ free_boolexp(ll->key); ll->key = key; ll->creator = player; if (flags != -1) ll->flags = flags; } else { ll = next_free_lock(); if (!ll) { /* Oh, this sucks */ do_log(LT_ERR, 0, 0, "Unable to malloc memory for lock_list!"); } else { real_type = st_insert(type, &lock_names); ll->type = real_type; ll->key = key; ll->creator = player; if (flags == -1) { const lock_list *l2 = get_lockproto(real_type); if (l2) ll->flags = l2->flags; else ll->flags = 0; } else { ll->flags = flags; } if (!can_write_lock(player, thing, ll)) { st_delete(real_type, &lock_names); free_boolexp(key); return 0; } t = &Locks(thing); while (*t && strcasecmp(L_TYPE(*t), L_TYPE(ll)) < 0) t = &L_NEXT(*t); L_NEXT(ll) = *t; *t = ll; } } return 1; }
int GoodSDB (int x) { if (x < MIN_SPACE_OBJECTS || x > max_space_objects) { return 0; } else if (!sdb[x].structure.type) { return 0; } else if (!SpaceObj(sdb[x].object) || !GoodObject(sdb[x].object)) { return 0; } else return 1; }
int name2sdb (char *name) /* Returns the sdb# of a named space object */ { register int i; for (i = MIN_SPACE_OBJECTS ; i <= max_space_objects ; ++i) if (sdb[i].structure.type) if (SpaceObj(sdb[i].object) && GoodObject(sdb[i].object)) if (local_wild_match(name, Name(sdb[i].object))) return i; return SENSOR_FAIL; }
int db2sdb (dbref name) /* Returns the sdb# of a DB# space object */ { register int i; for (i = MIN_SPACE_OBJECTS ; i <= max_space_objects ; ++i) if (sdb[i].structure.type) if (SpaceObj(sdb[i].object) && GoodObject(sdb[i].object)) if ( name==sdb[i].object) return i; return SENSOR_FAIL; }
/** Returns the apparent location of object. * This is the location for players and things, source for exits, and * NOTHING for rooms. * \param thing object to get location of. * \return apparent location of object (NOTHING for rooms). */ dbref where_is(dbref thing) { if (!GoodObject(thing)) return NOTHING; switch (Typeof(thing)) { case TYPE_ROOM: return NOTHING; case TYPE_EXIT: return Home(thing); default: return Location(thing); } }
/** Open a new exit. * \verbatim * This is the top-level function for @open. It calls do_real_open() * to do the real work of opening both the exit forward and the exit back. * \endverbatim * \param player the enactor. * \param direction name of the exit forward. * \param links 1-based array, possibly containing name of destination, name of exit back, * and room to open initial exit from. * \param pe_info the pe_info to use for any lock checks */ void do_open(dbref player, const char *direction, char **links, NEW_PE_INFO *pe_info) { dbref forward; dbref source = NOTHING; if (links[3]) { source = match_result(player, links[3], TYPE_ROOM, MAT_HERE | MAT_ABSOLUTE | MAT_TYPE); if (!GoodObject(source)) { notify(player, T("Open from where?")); return; } } if (links[5]) { if (!make_first_free_wrapper(player, links[5])) return; } if (links[4]) { if (!make_first_free_wrapper(player, links[4])) return; } forward = do_real_open(player, direction, links[1], source, pe_info); if (links[2] && *links[2] && GoodObject(forward) && GoodObject(Location(forward))) { char sourcestr[SBUF_LEN]; /* SBUF_LEN is the size used by unparse_dbref */ if (!GoodObject(source)) { source = speech_loc(player); } strcpy(sourcestr, unparse_dbref(source)); do_real_open(player, links[2], sourcestr, Location(forward), pe_info); } }
// Checks to see if the specified object exists, isn't garbage, // etc. HS_BOOL8 CHSInterface::ValidObject(HS_DBREF objnum) { #ifdef PENNMUSH // No change in code between versions if (GoodObject(objnum) && !IsGarbage(objnum)) return true; else return false; #endif #if defined(TM3) || defined(MUX) if (Good_obj(objnum) && !isGarbage(objnum)) return true; else return false; #endif }
/** Can a player control a thing? * The control rules are, in order: * Only God controls God. * Wizards control everything else. * Nothing else controls a wizard, and only royalty control royalty. * Mistrusted objects control only themselves. * Objects with the same owner control each other, unless the * target object is TRUST and the would-be controller isn't. * If ZMOs allow control, and you pass the ZMO, you control. * If the owner is a Zone Master, and you pass the ZM, you control. * If you pass the control lock, you control. * Otherwise, no dice. * \param who object attempting to control. * \param what object to be controlled. * \retval 1 who controls what. * \retval 0 who doesn't control what. */ int controls(dbref who, dbref what) { boolexp c; if (!GoodObject(what)) return 0; if (Guest(who)) return 0; if (what == who) return 1; if (God(what)) return 0; if (Wizard(who)) return 1; if (Wizard(what) || (Hasprivs(what) && !Hasprivs(who))) return 0; if (Mistrust(who)) return 0; if (Owns(who, what) && (!Inheritable(what) || Inheritable(who))) return 1; if (Inheritable(what) || IsPlayer(what)) return 0; if (!ZONE_CONTROL_ZMP && (Zone(what) != NOTHING) && eval_lock(who, Zone(what), Zone_Lock)) return 1; if (ZMaster(Owner(what)) && !IsPlayer(what) && eval_lock(who, Owner(what), Zone_Lock)) return 1; c = getlock_noparent(what, Control_Lock); if (c != TRUE_BOOLEXP) { if (eval_boolexp(who, c, what, NULL)) return 1; } return 0; }
/** Look up a player in the player list htab (or by dbref). * \param name name of player to find. * \return dbref of player, or NOTHING. */ dbref lookup_player(const char *name) { dbref d; if (!name || !*name) return NOTHING; if (*name == NUMBER_TOKEN) { d = parse_objid(name); if (GoodObject(d) && IsPlayer(d)) return d; else return NOTHING; } if (*name == LOOKUP_TOKEN) name++; return lookup_player_name(name); }
/** The say command. * \param player the enactor. * \param message the message to say. * \param pe_info pe_info to eval speechmod with */ void do_say(dbref player, const char *message, NEW_PE_INFO *pe_info) { dbref loc; PE_REGS *pe_regs; char modmsg[BUFFER_LEN]; char says[BUFFER_LEN]; char *sp; int mod = 0; loc = speech_loc(player); if (!GoodObject(loc)) return; if (!Loud(player) && !eval_lock_with(player, loc, Speech_Lock, pe_info)) { fail_lock(player, loc, Speech_Lock, T("You may not speak here!"), NOTHING); return; } if (*message == SAY_TOKEN && CHAT_STRIP_QUOTE) message++; pe_regs = pe_regs_create(PE_REGS_ARG, "do_say"); pe_regs_setenv_nocopy(pe_regs, 0, message); pe_regs_setenv_nocopy(pe_regs, 1, "\""); modmsg[0] = '\0'; if (call_attrib(player, "SPEECHMOD", modmsg, player, pe_info, pe_regs) && *modmsg != '\0') mod = 1; pe_regs_free(pe_regs); /* notify everybody */ notify_format(player, T("You say, \"%s\""), (mod ? modmsg : message)); sp = says; safe_format(says, &sp, T("%s says, \"%s\""), spname(player), (mod ? modmsg : message)); *sp = '\0'; notify_except(player, loc, player, says, NA_INTER_HEAR); }
/** Delete a lock from an object (primitive). * Another primitive routine. * \param player the enactor, for permission checking. * \param thing object on which to remove the lock. * \param type type of lock to remove. */ int delete_lock(dbref player, dbref thing, lock_type type) { lock_list *ll, **llp; if (!GoodObject(thing)) { return 0; } llp = &(Locks(thing)); while (*llp && strcasecmp((*llp)->type, type) != 0) { llp = &((*llp)->next); } if (*llp != NULL) { if (can_write_lock(player, thing, *llp)) { ll = *llp; *llp = ll->next; free_one_lock_list(ll); return 1; } else return 0; } else return 1; }
/** Add a lock to an object on db load. * Set the lock type on thing to boolexp. * Used only on db load, when we can't safely test the player's * permissions because they're not loaded yet. * This is a primitive routine, to be called by other routines. * It will go somewhat wonky if given a NULL boolexp. * It will allocate memory if called with a string that is not already * in the lock table. * \param player lock creator. * \param thing object on which to set the lock. * \param type type of lock to set. * \param key lock boolexp pointer (should not be NULL!). * \param flags lock flags. * \retval 0 failure. */ int add_lock_raw(dbref player, dbref thing, lock_type type, boolexp key, privbits flags) { lock_list *ll, **t; lock_type real_type = type; if (!GoodObject(thing)) { return 0; } ll = next_free_lock(Locks(thing)); if (!ll) { /* Oh, this sucks */ do_log(LT_ERR, 0, 0, "Unable to malloc memory for lock_list!"); } else { real_type = st_insert(type, &lock_names); ll->type = real_type; ll->key = key; ll->creator = player; if (flags == LF_DEFAULT) { const lock_list *l2 = get_lockproto(real_type); if (l2) ll->flags = l2->flags; else ll->flags = 0; } else { ll->flags = flags; } t = &Locks(thing); while (*t && strcasecmp(L_TYPE(*t), L_TYPE(ll)) < 0) t = &L_NEXT(*t); L_NEXT(ll) = *t; *t = ll; } return 1; }
/** Change an object's zone. * \verbatim * This implements @chzone. * \endverbatim * \param player the enactor. * \param name name of the object to change zone of. * \param newobj name of new ZMO. * \param noisy if 1, notify player about success and failure. * \param preserve was the /preserve switch given? * \param pe_info the pe_info for lock and permission checks * \retval 0 failed to change zone. * \retval 1 successfully changed zone. */ int do_chzone(dbref player, char const *name, char const *newobj, bool noisy, bool preserve, NEW_PE_INFO *pe_info) { dbref thing; dbref zone; int has_lock; if ((thing = noisy_match_result(player, name, NOTYPE, MAT_NEARBY)) == NOTHING) return 0; if (!newobj || !*newobj || !strcasecmp(newobj, "none")) zone = NOTHING; else { if ((zone = noisy_match_result(player, newobj, NOTYPE, MAT_EVERYTHING)) == NOTHING) return 0; } if (Zone(thing) == zone) { if (noisy) notify(player, T("That object is already in that zone.")); return 0; } if (!controls(player, thing)) { if (noisy) notify(player, T("You don't have the power to shift reality.")); return 0; } /* a player may change an object's zone to: * 1. NOTHING * 2. an object he controls * 3. an object with a chzone-lock that the player passes. * Note that an object with no chzone-lock isn't valid */ has_lock = (getlock(zone, Chzone_Lock) != TRUE_BOOLEXP); if (!(Wizard(player) || (zone == NOTHING) || controls(player, zone) || (has_lock && eval_lock_with(player, zone, Chzone_Lock, pe_info)))) { if (noisy) { if (has_lock) { fail_lock(player, zone, Chzone_Lock, T("You cannot move that object to that zone."), NOTHING); } else { notify(player, T("You cannot move that object to that zone.")); } } return 0; } /* Don't chzone object to itself for mortals! */ if ((zone == thing) && !Hasprivs(player)) { if (noisy) notify(player, T("You shouldn't zone objects to themselves!")); return 0; } /* Don't allow circular zones */ if (GoodObject(zone)) { dbref tmp; int zone_depth = MAX_ZONES; for (tmp = Zone(zone); GoodObject(tmp); tmp = Zone(tmp)) { if (tmp == thing) { notify(player, T("You can't make circular zones!")); return 0; } if (tmp == Zone(tmp)) /* Ran into an object zoned to itself */ break; zone_depth--; if (!zone_depth) { notify(player, T("Overly deep zone chain.")); return 0; } } } /* Don't allow chzone to objects without elocks! * If no lock is set, set a default lock (warn if zmo are used for control) * This checks for many trivial elocks (canuse/1, where &canuse=1) */ if (zone != NOTHING) check_zone_lock(player, zone, noisy); /* Warn Wiz/Royals when they zone their stuff */ if ((zone != NOTHING) && Hasprivs(Owner(thing))) { if (noisy) notify(player, T("Warning: @chzoning admin-owned object!")); } /* everything is okay, do the change */ Zone(thing) = zone; /* If we're not unzoning, and we're working with a non-player object, * we'll remove wizard, royalty, inherit, and powers, for security, unless * a wizard is changing the zone and explicitly says not to. */ if (!Wizard(player)) preserve = 0; if (!preserve && ((zone != NOTHING) && !IsPlayer(thing))) { /* if the object is a player, resetting these flags is rather * inconvenient -- although this may pose a bit of a security * risk. Be careful when @chzone'ing wizard or royal players. */ clear_flag_internal(thing, "WIZARD"); clear_flag_internal(thing, "ROYALTY"); clear_flag_internal(thing, "TRUST"); destroy_flag_bitmask("POWER", Powers(thing)); Powers(thing) = new_flag_bitmask("POWER"); } else { if (noisy && (zone != NOTHING)) { if (Hasprivs(thing)) notify(player, T("Warning: @chzoning a privileged player.")); if (Inherit(thing)) notify(player, T("Warning: @chzoning a TRUST player.")); } } if (noisy) notify(player, T("Zone changed.")); return 1; }
/** Parse access options into fields. * \param opts access options to read from. * \param who pointer to player to whom rule applies, or AMBIGUOUS. * \param can pointer to flags of allowed actions. * \param cant pointer to flags of disallowed actions. * \param player enactor. * \return number of options successfully parsed. * Parse options and return the appropriate can and cant bits. * Return the number of options successfully parsed. * This makes a copy of the options string, so it's not modified. */ int parse_access_options(const char *opts, dbref *who, uint32_t *can, uint32_t *cant, dbref player) { char myopts[BUFFER_LEN]; char *p; char *w; acsflag *c; int found, totalfound, first; if (!opts || !*opts) return 0; strcpy(myopts, opts); totalfound = 0; first = 1; if (who) *who = AMBIGUOUS; p = trim_space_sep(myopts, ' '); while ((w = split_token(&p, ' '))) { found = 0; if (first && who) { /* Check for a character */ first = 0; if (is_strict_integer(w)) { /* We have a dbref */ *who = parse_integer(w); if (*who != AMBIGUOUS && !GoodObject(*who)) *who = AMBIGUOUS; continue; } } if (*w == '!') { /* Found a negated warning */ w++; for (c = acslist; c->name; c++) { if (c->toggle && !strncasecmp(w, c->name, strlen(c->name))) { *cant |= c->flag; found++; } } } else { /* None is special */ if (!strncasecmp(w, "NONE", 4)) { *cant = ACS_DEFAULT; found++; } else { for (c = acslist; c->name; c++) { if (!strncasecmp(w, c->name, strlen(c->name))) { *can |= c->flag; found++; } } } } /* At this point, we haven't matched any warnings. */ if (!found) { if (GoodObject(player)) notify_format(player, T("Unknown access option: %s"), w); else do_log(LT_ERR, GOD, GOD, "Unknown access flag: %s", w); } else { totalfound += found; } } return totalfound; }
/** 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; }
/** Link an exit, room, player, or thing. * \verbatim * This is the top-level function for @link, which is used to link an * exit to a destination, set a player or thing's home, or set a * drop-to on a room. * * Linking an exit usually seizes ownership of the exit and costs 1 penny. * 1 penny is also transferred to the former owner. * \endverbatim * \param player the enactor. * \param name the name of the object to link. * \param room_name the name of the link destination. * \param preserve if 1, preserve ownership and zone data on exit relink. */ void do_link(dbref player, const char *name, const char *room_name, int preserve) { /* Use this to link to a room that you own. * It usually seizes ownership of the exit and costs 1 penny, * plus a penny transferred to the exit owner if they aren't you. * You must own the linked-to room AND specify it by room number. */ dbref thing; dbref room; if (!room_name || !*room_name) { do_unlink(player, name); return; } if (!IsRoom(player) && GoodObject(Location(player)) && IsExit(Location(player))) { notify(player, T("You somehow wound up in a exit. No biscuit.")); return; } if ((thing = noisy_match_result(player, name, TYPE_EXIT, MAT_EVERYTHING)) != NOTHING) { switch (Typeof(thing)) { case TYPE_EXIT: if ((room = check_var_link(room_name)) == NOTHING) room = parse_linkable_room(player, room_name); if (room == NOTHING) return; if (GoodObject(room) && !can_link_to(player, room)) { notify(player, T("Permission denied.")); break; } /* We may link an exit if it's unlinked and we pass the link-lock * or if we control it. */ if (!(controls(player, thing) || ((Location(thing) == NOTHING) && eval_lock(player, thing, Link_Lock)))) { notify(player, T("Permission denied.")); return; } if (preserve && !Wizard(player)) { notify(player, T("Permission denied.")); return; } /* handle costs */ if (Owner(thing) == Owner(player)) { if (!payfor(player, LINK_COST)) { notify_format(player, T("It costs %d %s to link this exit."), LINK_COST, ((LINK_COST == 1) ? MONEY : MONIES)); return; } } else { if (!payfor(player, LINK_COST + EXIT_COST)) { int a = LINK_COST + EXIT_COST; notify_format(player, T("It costs %d %s to link this exit."), a, ((a == 1) ? MONEY : MONIES)); return; } else if (!preserve) { /* pay the owner for his loss */ giveto(Owner(thing), EXIT_COST); chown_object(player, thing, player, 0); } } /* link has been validated and paid for; do it */ if (!preserve) { Owner(thing) = Owner(player); Zone(thing) = Zone(player); } Location(thing) = room; /* notify the player */ notify_format(player, T("Linked exit #%d to %s"), thing, unparse_object(player, room)); break; case TYPE_PLAYER: case TYPE_THING: if ((room = noisy_match_result(player, room_name, NOTYPE, MAT_EVERYTHING)) == NOTHING) { notify(player, T("No match.")); return; } if (IsExit(room)) { notify(player, T("That is an exit.")); return; } if (thing == room) { notify(player, T("You may not link something to itself.")); return; } /* abode */ if (!controls(player, room) && !Abode(room)) { notify(player, T("Permission denied.")); break; } if (!controls(player, thing)) { notify(player, T("Permission denied.")); } else if (room == HOME) { notify(player, T("Can't set home to home.")); } else { /* do the link */ Home(thing) = room; /* home */ if (!Quiet(player) && !(Quiet(thing) && (Owner(thing) == player))) notify(player, T("Home set.")); } break; case TYPE_ROOM: if ((room = parse_linkable_room(player, room_name)) == NOTHING) return; if ((room != HOME) && (!IsRoom(room))) { notify(player, T("That is not a room!")); return; } if (!controls(player, thing)) { notify(player, T("Permission denied.")); } else { /* do the link, in location */ Location(thing) = room; /* dropto */ notify(player, T("Dropto set.")); } break; default: notify(player, T("Internal error: weird object type.")); do_log(LT_ERR, NOTHING, NOTHING, "Weird object! Type of #%d is %d", thing, Typeof(thing)); break; } } }
static void ct_exit(dbref player, dbref i, warn_type flags) { dbref j, src, dst; int count = 0; int lt; /* i must be an exit, must be in a valid room, and must lead to a * different room * Remember, for exit i, Exits(i) = source room * and Location(i) = destination room */ dst = Destination(i); if ((flags & W_EXIT_UNLINKED) && (dst == NOTHING)) complain(player, i, "exit-unlinked", T("exit is unlinked; anyone can steal it")); if ((flags & W_EXIT_UNLINKED) && dst == AMBIGUOUS) { ATTR *a; const char *var = "DESTINATION"; a = atr_get(i, "DESTINATION"); if (!a) a = atr_get(i, "EXITTO"); if (a) var = "EXITTO"; if (!a) complain(player, i, "exit-unlinked", T("Variable exit has no %s attribute"), var); else { const char *x = atr_value(a); if (!x || !*x) complain(player, i, "exit-unlinked", T("Variable exit has empty %s attribute"), var); } } if (!Dark(i)) { if (flags & W_EXIT_MSGS) { lt = warning_lock_type(getlock(i, Basic_Lock)); if ((lt & W_UNLOCKED) && (!atr_get(i, "OSUCCESS") || !atr_get(i, "ODROP") || !atr_get(i, "SUCCESS"))) complain(player, i, "exit-msgs", T("possibly unlocked exit missing succ/osucc/odrop")); if ((lt & W_LOCKED) && !atr_get(i, "FAILURE")) complain(player, i, "exit-msgs", T("possibly locked exit missing fail")); } if (flags & W_EXIT_DESC) { if (!atr_get(i, "DESCRIBE")) complain(player, i, "exit-desc", T("exit is missing description")); } } src = Source(i); if (!GoodObject(src) || !IsRoom(src)) return; if (src == dst) return; /* Don't complain about exits linked to HOME or variable exits. */ if (!GoodObject(dst)) return; for (j = Exits(dst); GoodObject(j); j = Next(j)) if (Location(j) == src) { if (!(flags & W_EXIT_MULTIPLE)) return; else count++; } if ((count == 0) && (flags & W_EXIT_ONEWAY)) complain(player, i, "exit-oneway", T("exit has no return exit")); else if ((count > 1) && (flags & W_EXIT_MULTIPLE)) complain(player, i, "exit-multiple", T("exit has multiple (%d) return exits"), count); }
/** Clone an object. * \verbatim * This is the top-level function for @clone, which creates a duplicate * of a (non-player) object. * \endverbatim * \param player the enactor. * \param name the name of the object to clone. * \param newname the name to give the duplicate. * \param preserve if 1, preserve ownership and privileges on duplicate. * \paran newdbref the (unparsed) dbref to give the object, or NULL to use the next free * \return dbref of the duplicate, or NOTHING. */ dbref do_clone(dbref player, char *name, char *newname, int preserve, char *newdbref) { dbref clone, thing; char dbnum[BUFFER_LEN]; thing = noisy_match_result(player, name, NOTYPE, MAT_EVERYTHING); if ((thing == NOTHING)) return NOTHING; if (newname && *newname && !ok_name(newname, IsExit(thing))) { notify(player, T("That is not a reasonable name.")); return NOTHING; } if (!controls(player, thing) || IsPlayer(thing) || (IsRoom(thing) && !command_check_byname(player, "@dig")) || (IsExit(thing) && !command_check_byname(player, "@open")) || (IsThing(thing) && !command_check_byname(player, "@create"))) { notify(player, T("Permission denied.")); return NOTHING; } /* don't allow cloning of destructed things */ if (IsGarbage(thing)) { notify(player, T("There's nothing left of it to clone!")); return NOTHING; } if (preserve && !Wizard(player)) { notify(player, T("You cannot @CLONE/PRESERVE. Use normal @CLONE instead.")); return NOTHING; } if (!make_first_free_wrapper(player, newdbref)) { return NOTHING; } /* make sure owner can afford it */ switch (Typeof(thing)) { case TYPE_THING: if (can_pay_fees(player, Pennies(thing))) { clone = clone_object(player, thing, newname, preserve); notify_format(player, T("Cloned: Object %s."), unparse_dbref(clone)); if (IsRoom(player)) moveto(clone, player, player, "cloned"); else moveto(clone, Location(player), player, "cloned"); current_state.things++; local_data_clone(clone, thing); real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING, global_eval_context.wenv, 0); return clone; } return NOTHING; break; case TYPE_ROOM: if (can_pay_fees(player, ROOM_COST)) { clone = clone_object(player, thing, newname, preserve); Exits(clone) = NOTHING; notify_format(player, T("Cloned: Room #%d."), clone); current_state.rooms++; local_data_clone(clone, thing); real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING, global_eval_context.wenv, 0); return clone; } return NOTHING; break; case TYPE_EXIT: /* For exits, we don't want people to be able to link it to a location they can't with @open. So, all this stuff. */ switch (Location(thing)) { case NOTHING: strcpy(dbnum, "#-1"); break; case HOME: strcpy(dbnum, "home"); break; case AMBIGUOUS: strcpy(dbnum, "variable"); break; default: strcpy(dbnum, unparse_dbref(Location(thing))); } if (newname && *newname) clone = do_real_open(player, newname, dbnum, NOTHING); else clone = do_real_open(player, Name(thing), dbnum, NOTHING); if (!GoodObject(clone)) { return NOTHING; } else { atr_cpy(clone, thing); clone_locks(player, thing, clone); Zone(clone) = Zone(thing); Parent(clone) = Parent(thing); Flags(clone) = clone_flag_bitmask("FLAG", Flags(thing)); if (!preserve) { clear_flag_internal(clone, "WIZARD"); clear_flag_internal(clone, "ROYALTY"); Warnings(clone) = 0; /* zap warnings */ Powers(clone) = new_flag_bitmask("POWER"); /* zap powers */ } else { Warnings(clone) = Warnings(thing); Powers(clone) = clone_flag_bitmask("POWER", Powers(thing)); } if (Wizard(clone) || Royalty(clone) || Warnings(clone) || !null_flagmask("POWER", Powers(clone))) notify(player, T ("Warning: @CLONE/PRESERVE on an object with WIZ, ROY, @powers, or @warnings.")); notify_format(player, T("Cloned: Exit #%d."), clone); local_data_clone(clone, thing); return clone; } } return NOTHING; }