/** 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));
}
Beispiel #2
0
/** 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));
  }
}
Beispiel #3
0
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;
}
Beispiel #4
0
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;
}
Beispiel #6
0
/** 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);
  }
}
Beispiel #7
0
// 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;
}
Beispiel #9
0
/** 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);
}
Beispiel #10
0
/* 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;
  }
}
Beispiel #11
0
/** 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;
}
Beispiel #12
0
/** 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;
}
Beispiel #13
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;
}
Beispiel #14
0
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;
}
Beispiel #15
0
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;
}
Beispiel #16
0
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;
}
Beispiel #17
0
/** 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);
  }
}
Beispiel #18
0
/** 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);
  }
}
Beispiel #19
0
// 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
}
Beispiel #20
0
/** 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;
}
Beispiel #21
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);
}
Beispiel #22
0
/** 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);
}
Beispiel #23
0
/** 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;
}
Beispiel #24
0
/** 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;
}
Beispiel #25
0
/** 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;
}
Beispiel #26
0
/** 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;
}
Beispiel #27
0
/** 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;
}
Beispiel #28
0
/** 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;
    }
  }
}
Beispiel #29
0
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);
}
Beispiel #30
0
/** 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;

}