/* wiz.c */

#include "copyright.h"

/* Wizard-only commands */

#ifdef WANT_ANSI
#ifdef __STDC__
#include <stddef.h>
#include <stdlib.h>
#endif /* __STDC__ */
#endif /* WANT_ANSI */

#include <ctype.h>

#include "mudconf.h"
#include "config.h"
#include "db.h"
#include "interface.h"
#include "match.h"
#include "externs.h"
#include "command.h"
#include "htab.h"

extern char *crypt();

void do_teleport(dbref player, dbref cause, int key, char *arg1, char *arg2)
{
dbref	victim, destination;
char	*to;

	/* get victim */

	if (*arg2 == '\0') {
		victim = player;
		to = arg1;
	} else {
		init_match(player, arg1, NOTYPE);
		match_neighbor();
		match_possession();
		match_me();
		match_absolute();
		match_player();
		victim = noisy_match_result();

		if (victim == NOTHING)
			return;
		to = arg2;
	}

	/* Validate type of victim */

	switch (Typeof(victim)) {
	case TYPE_PLAYER:
	case TYPE_THING:
		break;
	default:
		notify(player, "You can't teleport that.");
		return;
	}

	/* Fail if we don't control the victim or the victim's location */

	if (!Controls(player, victim) && !Controls(player, Location(victim))) {
		notify(player, "Permission denied.");
		return;
	}

	/* Check for teleporting home */

	if (!string_compare(to, "home")) {
		(void)move_via_teleport(victim, HOME, cause, 0);
		return;
	}

	/* Find out where to send the victim */

	init_match(player, to, NOTYPE);
	match_here();
	match_absolute();
	match_neighbor();
	match_me();
	match_player();
	match_exit();
	destination = match_result();

	switch (destination) {
	case NOTHING:
		notify(player, "No match.");
		return;
	case AMBIGUOUS:
		notify(player, "I don't know which destination you mean!");
		return;
	default:
		if (victim == destination) {
			notify(player, "Bad destination.");
			return;
		}
	}

	switch (Typeof(destination)) {
	case TYPE_ROOM:
	case TYPE_PLAYER:
	case TYPE_THING:

		/* You must control the destination, or it must be a JUMP_OK
		 * room where you pass its TELEPORT lock.
		 */

		if (!Controls(player, destination) &&
		    !(IS(destination, TYPE_ROOM, ROOM_JUMP_OK) &&
		      could_doit(player, destination, A_LTPORT))) {
			notify(player, "Permission denied.");
			return;
		}
		if (move_via_teleport(victim, destination, cause, 0)) {
			if (player != victim) {
				if (!Quiet(player))
					notify(player, "Teleported.");
				if (!Quiet(victim))
					notify(victim, "Teleported.");
			}
		}
		break;
	case TYPE_EXIT:
		if (Exits(destination) == Location(victim)) {
			move_exit(victim, destination, 0,
				"You can't go that way.", 0);
		} else {
			notify(player, "I can't find that exit.");
		}
	}
}

/* ---------------------------------------------------------------------------
 * do_force_prefixed: Interlude to do_force for the # command
 */

void do_force_prefixed (dbref player, dbref cause, int extra, char *command,
	char *args[], int nargs)
{
char	*cp;

	cp=parse_to(&command, ' ', 0);
	if (!command) return;
	while (*command && isspace(*command)) command++;
	if (*command)
		do_force(player, cause, extra, cp, command, args, nargs);
}

/* ---------------------------------------------------------------------------
 * do_force: Force an object to do something.
 */

void do_force(dbref player, dbref cause, int extra, char *what, char *command,
	char *args[], int nargs)
{
dbref	victim;

	if ((victim = match_controlled(player, what)) == NOTHING)
		return;

	/* force victim to do command */

	wait_que(victim, player, RU_ARG1_COPY, 0, NOTHING,
		command, args, nargs);
}

/* ---------------------------------------------------------------------------
 * do_toad: Turn a player into an object.
 */

void do_toad(dbref player, dbref cause, int key, char *toad, char *newowner)
{
dbref	victim, recipient, loc, aowner;
char	*buf;
int	count, aflags;

	init_match(player, toad, TYPE_PLAYER);
	match_neighbor();
	match_absolute();
	match_player();
	if ((victim = noisy_match_result()) == NOTHING) return;

	if (Typeof(victim) != TYPE_PLAYER) {
		notify(player, "Try @destroy instead.");
		return;
	}
	if (Wizard(victim)) {
		notify(player, "You can't toad a Wizard.");
		return;
	}

	if ((newowner != NULL) && *newowner) {
		init_match (player, newowner, TYPE_PLAYER);
		match_neighbor ();
		match_absolute ();
		match_player ();
		if ((recipient = noisy_match_result ()) == NOTHING)
			return;
	} else {
		recipient = player;
	}

	STARTLOG(LOG_WIZARD,"WIZ","TOAD")
		log_name_and_loc(victim);
		log_text((char *)" was @toaded by ");
		log_name(player);
	ENDLOG

	/* Clear everything out */

	if (key & TOAD_NO_CHOWN) {
		count = -1;
	} else {
		count = chown_all(victim, recipient);
		s_Owner(victim, recipient);	/* you get it */
	}
	s_Flags(victim, TYPE_THING|HALT);
	s_Pennies(victim, 1);

	/* notify people */

	loc = Location(victim);
	buf=alloc_mbuf("do_toad");
	sprintf(buf, "%s has been turned into a slimy toad!", Name(victim));
	notify_except2(loc, player, victim, player, buf, 1);
	sprintf(buf, "You toaded %s! (%d objects @chowned)", Name(victim),
		count + 1);
	notify(player, buf);

	/* Zap the name from the name hash table */

	delete_player_name(victim, Name(victim));
	sprintf(buf, "a slimy toad named %s", Name(victim));
	s_Name(victim, buf);
	free_mbuf(buf);

	/* Zap the alias too */

	buf = atr_pget(victim, A_ALIAS, &aowner, &aflags);
	delete_player_name(victim, buf);
	free_lbuf(buf);

	count = boot_off(victim,
		(char *)"You have been turned into a slimy toad!");
	notify(player, tprintf("%d connection%s closed.",
		count, (count==1 ? "" : "s")));
}

void do_newpassword(dbref player, dbref cause, int key,
	char *name, char *password)
{
dbref	victim;
char	*buf;

	if ((victim = lookup_player(player, name, 0)) == NOTHING) {
		notify(player, "No such player.");
		return;
	}

	if (*password != '\0' && !ok_password(password)) {

		/* Can set null passwords, but not bad passwords */
		notify(player, "Bad password");
		return;
	}

	if (God(victim) && !God(player)) {
		notify(player, "You cannot change that player's password.");
		return;
	}

	STARTLOG(LOG_WIZARD,"WIZ","PASS")
		log_name(player);
		log_text((char *)" changed the password of ");
		log_name(victim);
	ENDLOG

	/* it's ok, do it */

	s_Pass(victim, crypt(password, "XX"));
	buf=alloc_lbuf("do_newpassword");
	notify(player, "Password changed.");
	sprintf(buf, "Your password has been changed by %s.", Name(player));
	notify(victim, buf);
	free_lbuf(buf);
}

void do_boot(dbref player, dbref cause, int key, char *name)
{
dbref	victim;
char	*buf, *bp;
int	count;

	if (key & BOOT_PORT) {
		if (is_number(name)) {
			victim = atoi(name);
		} else {
			notify(player, "That's not a number!");
			return;
		}
		STARTLOG(LOG_WIZARD,"WIZ","BOOT")
			buf = alloc_sbuf("do_boot.port");
			sprintf(buf, "Port %d", victim);
			log_text(buf);
			log_text((char *)" was @booted by ");
			log_name(player);
			free_sbuf(buf);
		ENDLOG
	} else {
		init_match(player, name, TYPE_PLAYER);
		match_neighbor();
		match_absolute();
		match_player();
		if ((victim = noisy_match_result()) == NOTHING) return;
	
		if (God(victim)) {
			notify(player, "You cannot boot that player!");
			return;
		}

		if ((Typeof(victim) != TYPE_PLAYER && !God(player)) ||
		    (player == victim)) {
			notify(player, "You can only boot off other players!");
			return;
		}

		STARTLOG(LOG_WIZARD,"WIZ","BOOT")
			log_name_and_loc(victim);
			log_text((char *)" was @booted by ");
			log_name(player);
		ENDLOG
		notify(player, tprintf("You booted %s off!", Name(victim)));
	}
	if (key & BOOT_QUIET) {
		buf = NULL;
	} else {
		bp = buf = alloc_lbuf("do_boot.msg");
		safe_str(Name(player), buf, &bp);
		safe_str((char *)" gently shows you the door.", buf, &bp);
		*bp = '\0';
	}

	if (key & BOOT_PORT)
		count = boot_by_port(victim, !God(player), buf);
	else
		count = boot_off(victim, buf);
	notify(player, tprintf("%d connection%s closed.",
		count, (count==1 ? "" : "s")));
	if (buf)
		free_lbuf(buf);
}

/* ---------------------------------------------------------------------------
 * do_poor: Reduce the wealth of anyone over a specified amount.
 */

void do_poor(dbref player, dbref cause, int key, char *arg1)
{
dbref	a;
int	amt, curamt;

	if (!is_number(arg1))
		return;
	amt = atoi(arg1);
	DO_WHOLE_DB(a) {
		if (Typeof(a) == TYPE_PLAYER) {
			curamt = Pennies(a);
			if (amt < curamt)
				s_Pennies(a, amt);
		}
	}
}

/* ---------------------------------------------------------------------------
 * do_cut: Chop off a contents or exits chain after the named item.
 */

void do_cut(dbref player, dbref cause, int key, char *thing)
{
dbref	object;

	object = match_controlled(player, thing);
	switch (object) {
	case NOTHING:
		notify(player, "No match.");
		break;
	case AMBIGUOUS:
		notify(player, "I don't know which one");
		break;
	default:
		s_Next(object, NOTHING);
		notify(player, "Cut.");
	}
}

/* ---------------------------------------------------------------------------
 * count_quota, mung_quota, show_quota, do_quota: Manage quotas.
 */

static int count_quota (dbref player)
{
int	i, q;
	player = Owner(player);
	q = 0 - mudconf.player_quota;
	DO_WHOLE_DB(i) {
		if (Owner(i) != player)
			continue;
		if ((Flags(i) & GOING) && (Typeof(i) != TYPE_ROOM))
			continue;
		switch (Typeof(i)) {
		case TYPE_EXIT:		q += mudconf.exit_quota;	break;
		case TYPE_ROOM:		q += mudconf.room_quota;	break;
		case TYPE_THING:	q += mudconf.thing_quota;	break;
		case TYPE_PLAYER:	q += mudconf.player_quota;	break;
		}
	}
	return q;
}

static void mung_quotas (dbref player, int key, int value)
{
dbref	aowner;
int	aq, rq, xq, aflags;
char	*buff;

	if (key & QUOTA_FIX) {

		/* Get value of stuff owned and good value, set other value
		 * from that. */

		xq = count_quota(player);
		if (key & QUOTA_TOT) {
			buff = atr_get(Owner(player), A_RQUOTA,
				&aowner, &aflags);
			aq = atoi(buff) + xq;
			atr_add_raw(Owner(player), A_QUOTA, tprintf("%d", aq));
			free_lbuf(buff);
		} else {
			buff = atr_get(Owner(player), A_QUOTA,
				&aowner, &aflags);
			rq = atoi(buff) - xq;
			atr_add_raw(Owner(player), A_RQUOTA, tprintf("%d", rq));
			free_lbuf(buff);
		}
	} else {

		/* Obtain (or calculate) current relative and absolute quota */

		buff = atr_get(Owner(player), A_QUOTA,
			&aowner, &aflags);
		if (!*buff) {
			free_lbuf(buff);
			buff = atr_get(Owner(player), A_RQUOTA,
				&aowner, &aflags);
			rq = atoi(buff);
			free_lbuf(buff);
			aq = rq + count_quota(player);
		} else {
			aq = atoi(buff);
			free_lbuf(buff);
			buff = atr_get(Owner(player), A_RQUOTA,
				&aowner, &aflags);
			rq = atoi(buff);
			free_lbuf(buff);
		}

		/* Adjust values */

		if (key & QUOTA_REM) {
			aq += (value - rq);
			rq = value;
		} else {
			rq += (value - aq);
			aq = value;
		}

		/* Set both abs and relative quota */

		atr_add_raw(Owner(player), A_QUOTA, tprintf("%d", aq));
		atr_add_raw(Owner(player), A_RQUOTA, tprintf("%d", rq));
	}
}

static void show_quota (dbref player, dbref victim)
{
dbref	aowner;
int	aq, rq, aflags;
char	*buff;

	player = Owner(player);
	victim = Owner(victim);
	buff = atr_get(victim, A_QUOTA, &aowner, &aflags);
	aq = atoi(buff);
	free_lbuf(buff);
	buff = atr_get(victim, A_RQUOTA, &aowner, &aflags);
	rq = aq - atoi(buff);
	free_lbuf(buff);
	if (!Wizard(victim))
		notify(player, tprintf("%-16s Quota: %9d  Used: %9d",
			Name(victim), aq, rq));
	else
		notify(player, tprintf("%-16s Quota: UNLIMITED  Used: %9d",
			Name(victim), rq));
}
	
void do_quota (dbref player, dbref cause, int key, char *arg1, char *arg2)
{
dbref	who;
int	set, value, i;

	if(!(mudconf.quotas | Wizard(player))) {
		notify(player, "Quotas are not enabled.");
		return;
	} 

	if ((key & QUOTA_TOT) && (key & QUOTA_REM)) {
		notify(player, "Illegal combination of switches.");
		return;
	}

	value = 0;
	set = 0;
	if (key & QUOTA_ALL) {
		if (arg1 && *arg1) {
			value = atoi(arg1);
			set = 1;
		}

		if (set) {
			STARTLOG(LOG_WIZARD,"WIZ","QUOTA")
				log_name(player);
				log_text((char *)" changed everyone's quota");
			ENDLOG
		}
		DO_WHOLE_DB(i) {
			if (Typeof(i) == TYPE_PLAYER) {
				if (set)
					mung_quotas(i, key, value);
				show_quota(player, i);
			}
		}
		return;
	}

	if (!arg1 || *arg1 == '\0')
		who = player;
	else {
		init_match(player, arg1, TYPE_PLAYER);
		match_me();
		match_player();
		match_neighbor();
		match_absolute();
		if ((who = noisy_match_result()) == NOTHING)
			return;
	}

	if (!Wizard(player)) {
		if (arg2 && *arg2) {
			notify(player, "Permission denied.");
			return;
		}

		if (player != who) {
			notify(player, "Permission denied.");
			return;
		}
	}

	if (arg2 && *arg2) {
		set = 1;
		value = atoi(arg2);
	}

	if (set) {
		STARTLOG(LOG_WIZARD,"WIZ","QUOTA")
			log_name(player);
			log_text((char *)" changed the quota of ");
			log_name(who);
		ENDLOG
		mung_quotas(who, key, value);
	}
	show_quota(player, who);
}

/* --------------------------------------------------------------------------
 * do_motd: Wizard-settable message of the day (displayed on connect)
 */

void do_motd (dbref player, dbref cause, int key, char *message)
{
	switch (key) {
	case MOTD_ALL:
		strcpy(mudconf.motd_msg, message);
		if (!Quiet(player)) notify(player, "Set: MOTD.");
		break;
	case MOTD_WIZ:
		strcpy(mudconf.wizmotd_msg, message);
		if (!Quiet(player)) notify(player, "Set: Wizard MOTD.");
		break;
	case MOTD_DOWN:
		strcpy(mudconf.downmotd_msg, message);
		if (!Quiet(player)) notify(player, "Set: Down MOTD.");
		break;
	case MOTD_FULL:
		strcpy(mudconf.fullmotd_msg, message);
		if (!Quiet(player)) notify(player, "Set: Full MOTD.");
		break;
	case MOTD_LIST:
		if (Wizard(player)) {
			notify(player,
				tprintf("MOTD: %s", mudconf.motd_msg));
			notify(player,
				tprintf("Wizard MOTD: %s", mudconf.wizmotd_msg));
			notify(player,
				tprintf("Down MOTD: %s", mudconf.downmotd_msg));
			notify(player,
				tprintf("Full MOTD: %s", mudconf.fullmotd_msg));
		} else {
			if (Guest(player))
				fcache_send(player, mudstate.guest_fcache);
			else
				fcache_send(player, mudstate.motd_fcache);
			notify(player, mudconf.motd_msg);
		}
		break;
	default:
		notify(player, "Illegal combination of switches.");
	}
}

/* ---------------------------------------------------------------------------
 * do_enable: enable or disable global control flags
 */


NAMETAB enable_names[] = {
{(char *)"building",		1,	CA_PUBLIC,	CF_BUILD},
{(char *)"checkpointing",	2,	CA_PUBLIC,	CF_CHECKPOINT},
{(char *)"cleaning",		2,	CA_PUBLIC,	CF_DBCHECK},
{(char *)"dequeueing",		1,	CA_PUBLIC,	CF_DEQUEUE},
{(char *)"idlechecking",	2,	CA_PUBLIC,	CF_IDLECHECK},
{(char *)"interpret",		2,	CA_PUBLIC,	CF_INTERP},
{(char *)"local_rwho",		3,	CA_PUBLIC,	CF_ALLOW_RWHO},
{(char *)"logins",		3,	CA_PUBLIC,	CF_LOGIN},
{(char *)"transmit_rwho",	1,	CA_PUBLIC,	CF_RWHO_XMIT},
{ NULL,				0,	0,		0}};

void do_global (dbref player, dbref cause, int key, char *flag)
{
int	flagvalue;

	/* Set or clear the indicated flag */

	flagvalue = search_nametab(player, enable_names, flag);
	if (flagvalue == -1) {
		notify(player, "I don't know about that flag.");
	} else if (key == GLOB_ENABLE) {
		mudconf.control_flags |= flagvalue;
		if (!Quiet(player)) notify(player, "Enabled.");
	} else if (key == GLOB_DISABLE) {
		mudconf.control_flags &= ~flagvalue;
		if (!Quiet(player)) notify(player, "Disabled.");
	} else {
		notify(player, "Illegal combination of switches.");
	}
}