fbmuck-6.01/contrib/jresolver/
fbmuck-6.01/contrib/jresolver/org/
fbmuck-6.01/contrib/jresolver/org/fuzzball/
fbmuck-6.01/docs/devel/
fbmuck-6.01/game/
fbmuck-6.01/game/logs/
fbmuck-6.01/game/muf/
fbmuck-6.01/scripts/
fbmuck-6.01/src_docs/
/* $Header: /cvsroot/fbmuck/fbmuck/src/predicates.c,v 1.8 2003/10/07 07:24:28 revar Exp $ */

/*
 * $Log: predicates.c,v $
 * Revision 1.8  2003/10/07 07:24:28  revar
 * Fixed possible crasher bug with autostart programs.
 *
 * Revision 1.7  2002/09/08 23:07:19  sombre
 * Fixed memory leak when toading online players.
 * Fixed remove_prop bug so it will remove props ending in /. (bug #537744)
 * Fixed potential buffer overrun with the CHECKRETURN and ABORT_MPI macros.
 * Fixed @omessage bug where player names would not be prefixed on additional
 *   newlines. (bug #562370)
 * Added IGNORING? ( d1 d2 -- i ) returns true if d1 is ignoring d2.
 * Added IGNORE_ADD ( d1 d2 -- ) adds d2 to d1's ignore list.
 * Added IGNORE_DEL ( d1 d2 -- ) removes d2 from d1's ignore list.
 * Added ARRAY_GET_IGNORELIST ( d -- a ) returns an array of d's ignores.
 * Added support for ignoring (gagging) players, ignores are mutual in that if
 *   player A ignores player B, A will not hear B, and B will not hear A.
 * Added ignore_prop @tune to specify the directory the ignore list is held under,
 *   if set blank ignore support is disabled, defaults to "@ignore/def".
 * Added max_ml4_preempt_count @tune to specify the maximum number of instructions
 *   an mlevel4 (wizbitted) program may run before it is aborted, if set to 0
 *   no limit is imposed.  Defaults to 0.
 * Added reserved_names @tune which when set to a smatch pattern will refuse any
 *   object creations or renames which match said pattern.  Defaults to "".
 * Added reserved_player_names @tune which when set to a smatch pattern will refuse
 *   any player creations or renames which match said pattern.  Defaults to "".
 *
 * Revision 1.6  2001/05/16 22:23:11  wog
 * Made ( and ) restricted to only player names.
 *
 * Revision 1.5  2001/05/16 22:14:34  wog
 * Prevented player names with ( or ) in them.
 *
 * Revision 1.4  2000/11/22 10:01:58  revar
 * Changed MPI from using Wizbit objects to give special permissions, to using
 * 'Blessed' properties.  Blessed props have few permissions restrictions.
 * Added @bless and @unbless wizard commands.
 * Added BLESSPROP and UNBLESSPROP muf primitives.
 * Added {bless} {unbless} and {revoke} MPI commands.
 * Fixed {listprops} crasher bug.
 *
 * Revision 1.3  2000/06/15 18:35:11  revar
 * Prettified some code formatting slightly.
 *
 * Revision 1.2  2000/03/29 12:21:02  revar
 * Reformatted all code into consistent format.
 * 	Tabs are 4 spaces.
 * 	Indents are one tab.
 * 	Braces are generally K&R style.
 * Added ARRAY_DIFF, ARRAY_INTERSECT and ARRAY_UNION to man.txt.
 * Rewrote restart script as a bourne shell script.
 *
 * Revision 1.1.1.1  1999/12/16 03:23:29  revar
 * Initial Sourceforge checkin, fb6.00a29
 *
 * Revision 1.1.1.1  1999/12/12 07:27:43  foxen
 * Initial FB6 CVS checkin.
 *
 * Revision 1.1  1996/06/12 02:47:32  foxen
 * Initial revision
 *
 * Revision 5.13  1994/03/14  12:20:58  foxen
 * Fb5.20 release checkpoint.
 *
 * Revision 5.12  1994/01/18  20:52:20  foxen
 * Version 5.15 release.
 *
 * Revision 5.11  1993/12/20  06:22:51  foxen
 * *** empty log message ***
 *
 * Revision 5.1  1993/12/17  00:07:33  foxen
 * initial revision.
 *
 * Revision 1.1  91/08/20  15:05:30  jearls
 * Initial revision
 *
 * Revision 1.2  91/03/24  01:19:30  lynx
 * added check for jump_ok or owned by owner of actions
 *
 * Revision 1.1  91/01/24  00:44:27  cks
 * changes for QUELL.
 *
 * Revision 1.0  91/01/22  20:54:34  cks
 * Initial revision
 *
 * Revision 1.8  90/09/18  08:01:39  rearl
 * Fixed a few broken things.
 *
 * Revision 1.7  90/09/16  04:42:42  rearl
 * Preparation code added for disk-based MUCK.
 *
 * Revision 1.6  90/09/10  02:22:30  rearl
 * Fixed some calls to pronoun_substitute.
 *
 * Revision 1.5  90/08/27  03:32:49  rearl
 * Major changes on several predicates, usage...
 *
 * Revision 1.4  90/08/15  03:07:34  rearl
 * Macroified some things.  Took out #ifdef GENDER.
 *
 * Revision 1.3  90/08/06  02:46:30  rearl
 * Put can_link() back in.  Added restricted() for flags.
 * With #define GOD_PRIV, sub-wizards can no longer set other
 * players to Wizard.
 *
 * Revision 1.2  90/08/02  18:54:04  rearl
 * Fixed controls() to return TRUE if exit is unlinked.  Everyone
 * controls an unlinked exit.  Got rid of can_link().
 *
 * Revision 1.1  90/07/19  23:03:58  casie
 * Initial revision
 *
 *
 */

#include "copyright.h"
#include "config.h"

/* Predicates for testing various conditions */

#include <ctype.h>

#include "db.h"
#include "props.h"
#include "interface.h"
#include "params.h"
#include "tune.h"
#include "externs.h"

int
OkObj(dbref obj)
{
	if (obj < 0 || obj >= db_top) {
		return 0;
	}
	if (Typeof(obj) == TYPE_GARBAGE) {
		return 0;
	}
	return 1;
}


int
can_link_to(dbref who, object_flag_type what_type, dbref where)
{
	if (where == HOME)
		return 1;
	if (where < 0 || where > db_top)
		return 0;
	switch (what_type) {
	case TYPE_EXIT:
		return (controls(who, where) || (FLAGS(where) & LINK_OK));
		/* NOTREACHED */
		break;
	case TYPE_PLAYER:
		return (Typeof(where) == TYPE_ROOM && (controls(who, where)
											   || Linkable(where)));
		/* NOTREACHED */
		break;
	case TYPE_ROOM:
		return ((Typeof(where) == TYPE_ROOM || Typeof(where) == TYPE_THING)
				&& (controls(who, where) || Linkable(where)));
		/* NOTREACHED */
		break;
	case TYPE_THING:
		return (
				(Typeof(where) == TYPE_ROOM || Typeof(where) == TYPE_PLAYER ||
				 Typeof(where) == TYPE_THING) && (controls(who, where) || Linkable(where)));
		/* NOTREACHED */
		break;
	case NOTYPE:
		return (controls(who, where) || (FLAGS(where) & LINK_OK) ||
				(Typeof(where) != TYPE_THING && (FLAGS(where) & ABODE)));
		/* NOTREACHED */
		break;
	}
	return 0;
}

int
can_link(dbref who, dbref what)
{
	return (controls(who, what) || ((Typeof(what) == TYPE_EXIT)
									&& DBFETCH(what)->sp.exit.ndest == 0));
}

/*
 * Revision 1.2 -- SECURE_TELEPORT
 * you can only jump with an action from rooms that you own
 * or that are jump_ok, and you cannot jump to players that are !jump_ok.
 */

int
could_doit(int descr, dbref player, dbref thing)
{
	dbref source, dest, owner;

	if (Typeof(thing) == TYPE_EXIT) {
		if (DBFETCH(thing)->sp.exit.ndest == 0) {
			return 0;
		}

		owner = OWNER(thing);
		source = DBFETCH(player)->location;
		dest = *(DBFETCH(thing)->sp.exit.dest);

		if (Typeof(dest) == TYPE_PLAYER) {
			dbref destplayer = dest;

			dest = DBFETCH(dest)->location;
			if (!(FLAGS(destplayer) & JUMP_OK) || (FLAGS(dest) & BUILDER)) {
				return 0;
			}
		}

		/* for actions */
		if ((DBFETCH(thing)->location != NOTHING) &&
			(Typeof(DBFETCH(thing)->location) != TYPE_ROOM)) {

			if ((Typeof(dest) == TYPE_ROOM || Typeof(dest) == TYPE_PLAYER) &&
				(FLAGS(source) & BUILDER)) return 0;

			if (tp_secure_teleport && Typeof(dest) == TYPE_ROOM) {
				if ((dest != HOME) && (!controls(owner, source))
					&& ((FLAGS(source) & JUMP_OK) == 0)) {
					return 0;
				}
			}
		}
	}

	return (eval_boolexp(descr, player, GETLOCK(thing), thing));
}


int
test_lock(int descr, dbref player, dbref thing, const char *lockprop)
{
	struct boolexp *lokptr;

	lokptr = get_property_lock(thing, lockprop);
	return (eval_boolexp(descr, player, lokptr, thing));
}


int
test_lock_false_default(int descr, dbref player, dbref thing, const char *lockprop)
{
	struct boolexp *lok = get_property_lock(thing, lockprop);

	if (lok == TRUE_BOOLEXP)
		return 0;
	return (eval_boolexp(descr, player, lok, thing));
}


int
can_doit(int descr, dbref player, dbref thing, const char *default_fail_msg)
{
	dbref loc;

	if ((loc = getloc(player)) == NOTHING)
		return 0;

	if (!Wizard(OWNER(player)) && Typeof(player) == TYPE_THING && (FLAGS(thing) & ZOMBIE)) {
		notify(player, "Sorry, but zombies can't do that.");
		return 0;
	}
	if (!could_doit(descr, player, thing)) {
		/* can't do it */
		if (GETFAIL(thing)) {
			exec_or_notify_prop(descr, player, thing, MESGPROP_FAIL, "(@Fail)");
		} else if (default_fail_msg) {
			notify(player, default_fail_msg);
		}
		if (GETOFAIL(thing) && !Dark(player)) {
			parse_oprop(descr, player, loc, thing, MESGPROP_OFAIL,
						   PNAME(player), "(@Ofail)");
		}
		return 0;
	} else {
		/* can do it */
		if (GETSUCC(thing)) {
			exec_or_notify_prop(descr, player, thing, MESGPROP_SUCC, "(@Succ)");
		}
		if (GETOSUCC(thing) && !Dark(player)) {
			parse_oprop(descr, player, loc, thing, MESGPROP_OSUCC,
						   NAME(player), "(@Osucc)");
		}
		return 1;
	}
}

int
can_see(dbref player, dbref thing, int can_see_loc)
{
	if (player == thing || Typeof(thing) == TYPE_EXIT || Typeof(thing) == TYPE_ROOM)
		return 0;

	if (can_see_loc) {
		switch (Typeof(thing)) {
		case TYPE_PROGRAM:
			return ((FLAGS(thing) & LINK_OK) || controls(player, thing));
		case TYPE_PLAYER:
			if (tp_dark_sleepers) {
				return (!Dark(thing) && online(thing));
			}
		default:
			return (!Dark(thing) || (controls(player, thing) && !(FLAGS(player) & STICKY)));
		}
	} else {
		/* can't see loc */
		return (controls(player, thing) && !(FLAGS(player) & STICKY));
	}
}

int
controls(dbref who, dbref what)
{
	dbref index;

	/* No one controls invalid objects */
	if (what < 0 || what >= db_top)
		return 0;

	/* No one controls garbage */
	if (Typeof(what) == TYPE_GARBAGE)
		return 0;

	if (Typeof(who) != TYPE_PLAYER)
		who = OWNER(who);

	/* Wizard controls everything else */
	if (Wizard(who))
		return 1;

	if (tp_realms_control) {
		/* Realm Owner controls everything under his environment. */
		for (index = what; index != NOTHING; index = getloc(index)) {
			if ((OWNER(index) == who) && (Typeof(index) == TYPE_ROOM)
				&& Wizard(index))
				return 1;
		}
	}

	/* exits are also controlled by the owners of the source and destination */
	/* ACTUALLY, THEY AREN'T.  IT OPENS A BAD MPI SECURITY HOLE. */
	/*
	 * if (Typeof(what) == TYPE_EXIT) {
	 *    int     i = DBFETCH(what)->sp.exit.ndest;
	 *
	 *    while (i > 0) {
	 *        if (who == OWNER(DBFETCH(what)->sp.exit.dest[--i]))
	 *            return 1;
	 *    }
	 *    if (who == OWNER(DBFETCH(what)->location))
	 *        return 1;
	 * }
	 */

	/* owners control their own stuff */
	return (who == OWNER(what));
}

int
restricted(dbref player, dbref thing, object_flag_type flag)
{
	switch (flag) {
	case ABODE:
		return (!TrueWizard(OWNER(player)) && (Typeof(thing) == TYPE_PROGRAM));
		/* NOTREACHED */
		break;
	case ZOMBIE:
		if (Typeof(thing) == TYPE_PLAYER)
			return (!(Wizard(OWNER(player))));
		if ((Typeof(thing) == TYPE_THING) && (FLAGS(OWNER(player)) & ZOMBIE))
			return (!(Wizard(OWNER(player))));
		return (0);
	case VEHICLE:
		if (Typeof(thing) == TYPE_PLAYER)
			return (!(Wizard(OWNER(player))));
		if (tp_wiz_vehicles) {
			if (Typeof(thing) == TYPE_THING)
				return (!(Wizard(OWNER(player))));
		} else {
			if ((Typeof(thing) == TYPE_THING) && (FLAGS(player) & VEHICLE))
				return (!(Wizard(OWNER(player))));
		}
		return (0);
	case DARK:
		if (!Wizard(OWNER(player))) {
			if (Typeof(thing) == TYPE_PLAYER)
				return (1);
			if (!tp_exit_darking && Typeof(thing) == TYPE_EXIT)
				return (1);
			if (!tp_thing_darking && Typeof(thing) == TYPE_THING)
				return (1);
		}
		return (0);

		/* NOTREACHED */
		break;
	case QUELL:
		/* You cannot quell or unquell another wizard. */
		return (TrueWizard(thing) && (thing != player) && (Typeof(thing) == TYPE_PLAYER));
		/* NOTREACHED */
		break;
	case MUCKER:
	case SMUCKER:
	case BUILDER:
		return (!Wizard(OWNER(player)));
		/* NOTREACHED */
		break;
	case WIZARD:
		if (Wizard(OWNER(player))) {

#ifdef GOD_PRIV
			return ((Typeof(thing) == TYPE_PLAYER) && !God(player));
#else							/* !GOD_PRIV */
			return 0;
#endif							/* GOD_PRIV */
		} else
			return 1;
		/* NOTREACHED */
		break;
	default:
		return 0;
		/* NOTREACHED */
		break;
	}
}

int
payfor(dbref who, int cost)
{
	who = OWNER(who);
	if (Wizard(who)) {
		return 1;
	} else if (PLAYER_PENNIES(who) >= cost) {
		PLAYER_ADD_PENNIES(who, -cost);
		DBDIRTY(who);
		return 1;
	} else {
		return 0;
	}
}

int
word_start(const char *str, const char let)
{
	int chk;

	for (chk = 1; *str; str++) {
		if (chk && *str == let)
			return 1;
		chk = *str == ' ';
	}
	return 0;
}

int
ok_name(const char *name)
{
	return (name
			&& *name
			&& *name != LOOKUP_TOKEN
			&& *name != REGISTERED_TOKEN
			&& *name != NUMBER_TOKEN
			&& !index(name, ARG_DELIMITER)
			&& !index(name, AND_TOKEN)
			&& !index(name, OR_TOKEN)
			&& !index(name, '\r')
			&& !index(name, ESCAPE_CHAR)
			&& !word_start(name, NOT_TOKEN)
			&& string_compare(name, "me")
			&& string_compare(name, "home")
			&& string_compare(name, "here")
			&& (
				!*tp_reserved_names ||
				!equalstr((char*)tp_reserved_names, (char*)name)
			));
}

int
ok_player_name(const char *name)
{
	const char *scan;

	if (!ok_name(name) || strlen(name) > PLAYER_NAME_LIMIT)
		return 0;
	

	for (scan = name; *scan; scan++) {
		if (!(isprint(*scan) && !isspace(*scan)) && *scan != '(' && *scan != ')') {	
		    /* was isgraph(*scan) */
			return 0;
		}
	}

	/* Check the name isn't reserved */
	if (*tp_reserved_player_names && equalstr((char*)tp_reserved_player_names, (char*)name))
		return 0;

	/* lookup name to avoid conflicts */
	return (lookup_player(name) == NOTHING);
}

int
ok_password(const char *password)
{
	const char *scan;

	if (*password == '\0')
		return 0;

	for (scan = password; *scan; scan++) {
		if (!(isprint(*scan) && !isspace(*scan))) {
			return 0;
		}
	}

	return 1;
}

int
isancestor(dbref parent, dbref child)
{
	while (child != NOTHING && child != parent) {
		child = getparent(child);
	}
	return child == parent;
}