/* set.c */

#include "copyright.h"

/* commands which set parameters */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>

#include "config.h"
#include "db.h"
#include "match.h"
#include "interface.h"
#include "externs.h"

#ifdef NOCRYPT
char *crypt(s, t)
    char *s, *t;
{
  return (s);
}
#endif

void do_name(player, name, newname)
    dbref player;
    const char *name;
    char *newname;
{
  dbref thing;
  char *password;
  ATTR *a;
  const char *d;
#ifdef RWHO_SEND
  char tbuf1[BUFFER_LEN];
#endif

  if ((thing = match_controlled(player, name)) != NOTHING) {
    /* check for bad name */
    if (*newname == '\0') {
      notify(player, "Give it what new name?");
      return;
    }
    /* check for renaming a player */
    if (Typeof(thing) == TYPE_PLAYER) {
#ifdef GUEST_RESTRICT
      if (Guest(player)) {
	notify(player, "Guests may not rename themselves.");
	return;
      }
#endif
      /* split off password */
      for (password = newname;
	   *password && !isspace(*password);
	   password++) ;
      /* eat whitespace */
      if (*password) {
	*password++ = '\0';	/* terminate name */
	while (*password && isspace(*password))
	  password++;
      }
      /* check for null password */
      if (!*password) {
	notify(player,
	       "You must specify a password to change a player name.");
	notify(player, "E.g.: name player = newname password");
	return;
      } else if ((a = atr_get(player, "XYXXY")) &&
		 *(d = uncompress(a->value)) &&
		 strcmp(d , password) &&
		 strcmp(crypt(password, "XX"), d)) {
	notify(player, "Incorrect password.");
	return;
      } else if (string_compare(newname, db[player].name)
		 && !ok_player_name(newname)) {
	/* string_compare allows changing foo to Foo, etc. */
	notify(player, "You can't give a player that name.");
	return;
      }
      /* everything ok, notify */
      fprintf(stderr, "NAME CHANGE: %s(#%d) to %s\n",
	      db[thing].name, thing, newname);
      if (db[thing].flags & PLAYER_SUSPECT)
	raw_broadcast(WIZARD, "Broadcast: Suspect %s changed name to %s.",
		      db[thing].name, newname);
#ifdef RWHO_SEND      
      sprintf(tbuf1, "%d@%s", thing, MUDNAME);
      rwhocli_userlogout(tbuf1); 
#endif
      delete_player(thing);
      SET(db[thing].name, newname);
      add_player(thing);
#ifdef RWHO_SEND
      rwhocli_userlogin(tbuf1, newname, time((time_t *) 0));
#endif
      notify(player, "Name set.");
      return;
    } else {
      if (!ok_name(newname)) {
	notify(player, "That is not a reasonable name.");
	return;
      }
    }

    /* everything ok, change the name */
    SET(db[thing].name, newname);
    notify(player, "Name set.");
  }
}

void do_lock(player, name, keyname, locktype)
    dbref player;
    const char *name;
    const char *keyname;
    int locktype;
{
  dbref thing;
  struct boolexp *key;
  init_match(player, name, NOTYPE);
  match_everything();

  switch (thing = match_result()) {
    case NOTHING:
      notify(player, "I don't see what you want to lock!");
      return;
    case AMBIGUOUS:
      notify(player, "I don't know which one you want to lock!");
      return;
    default:
      if (!controls(player, thing)) {
	notify(player, "You can't lock that!");
	return;
      }
      break;
  }

  key = parse_boolexp(player, keyname);
  if (key == TRUE_BOOLEXP) {
    notify(player, "I don't understand that key.");
  } else {
    /* everything ok, do it */
    switch (locktype) {
    case BASICLOCK:
      free_boolexp(db[thing].key);
      db[thing].key = key;
      break;
    case USELOCK:
      free_boolexp(db[thing].usekey);
      db[thing].usekey = key;
      break;
    case ENTERLOCK:
      free_boolexp(db[thing].enterkey);
      db[thing].enterkey = key;
      break;
    }
    notify(player, "Locked.");
  }
}

void do_unlock(player, name, locktype)
    dbref player;
    const char *name;
    int locktype;
{
  dbref thing;
  if ((thing = match_controlled(player, name)) != NOTHING) {
    switch (locktype) {
    case BASICLOCK:
      free_boolexp(db[thing].key);
      db[thing].key = TRUE_BOOLEXP;
      break;
    case USELOCK:
      free_boolexp(db[thing].usekey);
      db[thing].usekey = TRUE_BOOLEXP;
      break;
    case ENTERLOCK:
      free_boolexp(db[thing].enterkey);
      db[thing].enterkey = TRUE_BOOLEXP;
      break;
    }
    notify(player, "Unlocked.");
  }
}

void do_unlink(player, name)
    dbref player;
    const char *name;
{
  dbref exit;
  init_match(player, name, TYPE_EXIT);
  match_exit();
  match_here();
  if (Wizard(player)) {
    match_absolute();
  }
  switch (exit = match_result()) {
    case NOTHING:
      notify(player, "Unlink what?");
      break;
    case AMBIGUOUS:
      notify(player, "I don't know which one you mean!");
      break;
    default:
      if (!controls(player, exit)) {
	notify(player, "Permission denied.");
      } else {
	switch (Typeof(exit)) {
	  case TYPE_EXIT:
	    db[exit].location = NOTHING;
	    notify(player, "Unlinked.");
	    break;
	  case TYPE_ROOM:
	    db[exit].location = NOTHING;
	    notify(player, "Dropto removed.");
	    break;
	  default:
	    notify(player, "You can't unlink that!");
	    break;
	}
      }
  }
}

void do_chown(player, name, newobj)
    dbref player;
    const char *name;
    const char *newobj;
{
  dbref thing;
  dbref owner = NOTHING;
  init_match(player, name, TYPE_THING);
  match_possession();
  match_here();
  match_exit();
  if(Wizard(player)) {
   match_player();
   match_absolute();
  }
  switch (thing = match_result()) {
    case NOTHING:
      notify(player, "You don't have that!");
      return;
    case AMBIGUOUS:
      notify(player, "I don't know which you mean!");
      return;
    default:
      if(!*newobj || !string_compare(newobj, "me")) {
	owner = player;
      } else {
        if ((owner = lookup_player(newobj)) == NOTHING) {
	  notify(player, "I couldn't find that player.");
	  return;
        }
      }
      if (Typeof(thing) == TYPE_PLAYER && !God(player)) {
	notify(player, "Players always own themselves.");
	return;
      } else if (!controls(player, thing) &&
                 (!(db[thing].flags & CHOWN_OK) ||
                  ((Typeof(thing) == TYPE_THING) &&
                   (db[thing].location != player) &&
		   !Wizard(player))) ||
                 !controls(player, owner)) {
	notify(player, "Permission denied.");
      } else if (!payfor(player, OBJECT_COST)) {
	notify(player, "You don't have enough money.");
      } else {
	giveto(db[thing].owner, OBJECT_COST);
        if(God(player)) {
	  db[thing].owner = owner;
	} else {
	  db[thing].owner = db[owner].owner;
	}
	db[thing].zone = db[owner].zone;
	db[thing].flags &= ~CHOWN_OK;
	db[thing].flags &= ~WIZARD;
#ifdef ROYALTY_FLAG
	db[thing].flags &= ~ROYALTY;
#endif
	db[thing].flags &= ~INHERIT;
	db[thing].flags |= HALT;
	notify(player, "Owner changed.");
      }
  }
}

void do_chzone(player, name, newobj)
     dbref player;
     const char *name;
     const char *newobj;
{
  dbref thing;
  dbref zone;

  if (!Wizard(player)) {
    notify(player, "Only wizards may change the zone of an object.");
    return;
  }

  init_match(player, name, NOTYPE);
  match_neighbor();
  match_possession();
  match_here();
  match_exit();
  match_me();
  if (Wizard(player)) {
    match_player();
    match_absolute();
  }

  switch (thing = match_result()) {
  case NOTHING:
    notify(player, "I can't seem to find that.");
    return;
  case AMBIGUOUS:
    notify(player, "I don't know which one you mean!");
    return;
  }

  if (Typeof(thing) == TYPE_PLAYER && !God(player)) {
    notify(player, "Only God may change a player's zone.");
    return;
  }
  
  if (!string_compare(newobj, "none"))
    zone = NOTHING;
  else {
    init_match(player, newobj, TYPE_THING);
    match_neighbor();
    match_possession();
    match_absolute();
    switch (zone = match_result()) {
    case NOTHING:
      notify(player, "I can't seem to find that.");
      return;
    case AMBIGUOUS:
      notify(player, "I don't know which one you mean!");
      return;
    }
    if (Typeof(zone) != TYPE_THING) {
      notify(player, "Zones may only be defined by objects.");
      return;
    }
  }

  db[thing].zone = zone;
  db[thing].flags &= ~WIZARD;
#ifdef ROYALTY_FLAG
  db[thing].flags &= ~ROYALTY;
#endif
  db[thing].flags &= ~INHERIT;
  notify(player, "Zone changed.");
}

void do_set(player, name, flag)
    dbref player;
    const char *name;
    const char *flag;
{
  dbref thing;
  int res;
  char *p;
  char tbuf1[BUFFER_LEN];
  object_flag_type f;
  int her;
  /* find thing */
  if ((thing = match_controlled(player, name)) == NOTHING)
    return;
  if (God(thing) && !God(player)) {
    notify(player, "Only God can set himself!");
    return;
  }
#ifdef INHERIT_FLAG
  if (!Inherit(player) && Inherit(thing)) {
    notify(player, "Authorization failed.");
    return;
  }
#endif
#ifdef DESTROY
  /*
   * The GOING flag can only be affected by set in one special case--A
   * room that is set to be destroy may be saved.
   */
  if (string_prefix("!GOING", flag) &&
      (Typeof(thing) == TYPE_ROOM) && (db[thing].flags & GOING)) {
    db[thing].flags ^= GOING;
    if (db[thing].owner > 0)
      notify(db[thing].owner, "Your room has been spared from destruction.");
    return;
  }
#endif				/* DESTROY */
  her = Hearer(thing);
  /* check for attribute set first */
  p = index(flag, ':');
  if (p) {
    ATTR *attrb = NULL;
    ATTR *a = NULL;

    *p++ = '\0';
    /* check for predefined attribute match */
    attrb = atr_match(flag);
    /* check for _ */
    if (*p == '_') {
      dbref thing1;
      char *q;

      q = index(p, '/');
      if (! (q && *q)) {
	notify(player, "I need an object and an attribute.");
	return;
      }
      *q++ = '\0';
      init_match(player, p, NOTYPE);
      match_everything();
      thing1 = noisy_match_result();
      if(thing1 == NOTHING) return;
      a = atr_get(thing1, q);
      if(!a) {
	notify(player, "No such attribute.");
	return;
      }
      strcpy(tbuf1, uncompress(a->value));
    } else {
      strcpy(tbuf1, p);
    }
    if(attrb) {
      if((attrb->flags & AF_WIZARD) && !Wizard(player)) {
	notify(player, "Sorry, only wizards can modify that.");
	return;
      }
      res = atr_add(thing, attrb->name, tbuf1, player, NOTHING);
      if(!res) {
	notify(player, "No such attribute to reset.");
	return;
      }
      if(res == -1) {
	notify(player, "That attribute cannot be changed by you.");
	return;
      }
    } else {
      res = atr_add(thing, flag, tbuf1, player, NOTHING);
      if(!res) {
	notify(player, "No such attribute to reset.");
	return;
      }
      if(res == -1) {
	notify(player, "That attribute cannot be changed by you.");
	return;
      }
    }
    /* one special case for listen */
    if (attrb && !string_compare(attrb->name, "LISTEN") && !her)
      notify_except(db[getloc(thing)].contents, thing,
		    tprintf("%s grows ears and can now hear.", db[thing].name));
    if(!Quiet(player) && !Quiet(thing)) 
      notify(player, tprintf("%s - Set.", db[thing].name));
    return;
  }
  /* move p past NOT_TOKEN if present */
  for (p = (char *) flag; *p && (*p == NOT_TOKEN || isspace(*p)); p++) ;

  /* identify flag */
  if (*p == '\0') {
    notify(player, "You must specify a flag to set.");
    return;
  }
  f = find_flag (p, thing);
  if (f == 0) {
    notify(player, "I don't recognize that flag.");
    return;
  }
  if (f == -1) {
    notify(player, "Broken function. Notify god.");
    return;
  }
  /* check unsettable flags */
#ifdef DESTROY
  if (f == GOING) {
    notify(player,
      "The GOING flag may only be used to stop the destruction of rooms.");
    return;
  }
#endif
  if ((f == TYPE_EXIT) || (f == TYPE_PLAYER) || (f == TYPE_ROOM)) {
    notify(player,
      "You cannot set that flag.");
    return;
  }
  /* check for restricted flag */
  if (!Wizard(player)) {
    switch (Typeof(thing)) {
      case TYPE_ROOM:
	if (f == ROOM_TEMPLE) {
	  notify(player, "Permission denied.");
	  return;
	}
	break;
      case TYPE_PLAYER:
#ifdef RESTRICTED_BUILDING
	if (f == PLAYER_BUILD) {
	  notify(player, "Permission denied.");
	  return;
	}
#endif				/* RESTRICTED_BUILDING */
	if (f == PLAYER_CONNECT) {
	  notify(player, "You cannot set that flag.");
	  return;
	}
	if (f == DARK) {
	  notify(player, "Permission denied.");
	  notify(player, "Use UNFINDABLE to hide from whereis command.");
	  return;
	}
	if (f == PLAYER_SUSPECT) {
	  notify(player, "Permission denied.");
	  return;
	}
	break;
      case TYPE_THING:
	if (f == THING_IMMORTAL) {
	  notify(player, "Permission denied.");
	  return;
	}
	break;
      }
  }

#ifdef ROYALTY_FLAG
  if ((f == ROYALTY) &&
      !((Royalty(player) && Typeof(thing) != TYPE_PLAYER)
	|| Wizard(player) || God(player))) {
    notify(player, "Permission denied.");
    return;
  }
#endif

  if ((f == WIZARD) &&
      !((Wizard(player) && db[thing].owner == player) || God(player))) {
    notify(player, "Permission denied.");
    return;
  }

  if ((Typeof(thing) == TYPE_PLAYER) && (db[thing].owner == thing) &&
      (f == PLAYER_GAGGED) && (!Wizard(player) || Wizard(thing))) {
    notify(player, "You cannot gag that!");
    return;
  }
  /* else everything is ok, do the set */
  if (*flag == NOT_TOKEN) {
    /* reset the flag */
    db[thing].flags &= ~f;
    if (f == WIZARD)
      fprintf(stderr, "** WIZFLAG RESET ** %s(#%d) --> %s(#%d)\n",
	      db[player].name, player, db[thing].name, thing);
#ifdef ROYALTY_FLAG
    if (f == ROYALTY)
      fprintf(stderr, "** ROYAL FLAG RESET ** %s(#%d) --> %s(#%d)\n",
	      db[player].name, player, db[thing].name, thing);
#endif
#ifdef RWHO_SEND
    if(f == DARK && Typeof(thing) == TYPE_PLAYER) {
      sprintf(tbuf1,"%d@%s", thing, MUDNAME);
      rwhocli_userlogin(tbuf1, db[thing].name, time((time_t *) 0));
    }
#endif
    if((f == QUIET) || (!Quiet(player) && !Quiet(thing)))
      notify(player, "Flag reset.");
  } else {
    /* set the flag */
    db[thing].flags |= f;
    if (f == WIZARD)
      fprintf(stderr, "** WIZFLAG SET ** %s(#%d) --> %s(#%d)\n",
	      db[player].name, player, db[thing].name, thing);
#ifdef ROYALTY_FLAG
    if (f == ROYALTY)
      fprintf(stderr, "** ROYAL FLAG SET ** %s(#%d) --> %s(#%d)\n",
	      db[player].name, player, db[thing].name, thing);
#endif
#ifdef RWHO_SEND
    if(f == DARK && Typeof(thing) == TYPE_PLAYER) {
      sprintf(tbuf1, "%d@%s", thing, MUDNAME);
      rwhocli_userlogout(tbuf1);
    }
#endif
    if ((Typeof(thing) == TYPE_THING) && (f == THING_PUPPET) && !her) {
      sprintf(tbuf1,"%s grows ears and can now hear.", db[thing].name);
      notify_except(db[db[thing].location].contents, 0, tbuf1);
    }
    if((f == QUIET) || (!Quiet(player) && !Quiet(thing)))
      notify(player, "Flag set.");
  }
}


/* check for abbreviated set command */
int test_set(player, command, arg1, arg2)
    dbref player;
    char *command;
    char *arg1;
    char *arg2;
{
  int a;
  char tbuf1[BUFFER_LEN];

  if (command[0] != '@' && command[0] != '&')
    return (0);

  /* added to make this 2.0 compatible. with '&' equivalent to '@_' */
  if (command[0] == '&') {
    sprintf(tbuf1, "%s:%s", command + 1, arg2);
    do_set(player, arg1, tbuf1);
    return (1);
  }

  /* first character is '@' */

  /* check if it's a regular attribute */
  for (a = 0; attr[a].name; a++)
    if (string_prefix(attr[a].name, command + 1)) {
      sprintf(tbuf1, "%s:%s", attr[a].name, arg2);
      do_set(player, arg1, tbuf1);
      return (1);
    }

  /* else treat it as a user-defined one */
  if(command[1] != '_')
    return (0);
  sprintf(tbuf1, "%s:%s", command + 2, arg2);
  do_set(player, arg1, tbuf1);
  return (1);
}

void do_gedit(player, it, argv)
    dbref player;
    char *it;
    char *argv[];
{
  ALIST *a;
  dbref thing;
  char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN];
  char *q;

  if(!(it && *it)) {
    notify(player, "I need to know what you want to edit.");
    return;
  }
  strcpy(tbuf1, it);
  q = index(tbuf1, '/');
  if (!(q && *q)) {
    notify(player, "I need to know what you want to edit.");
    return;
  }
  *q++ = '\0';
  init_match(player, tbuf1, NOTYPE);
  match_everything();
  thing = noisy_match_result();

  if(thing == NOTHING || !controls(player, thing)) {
    notify(player, "Permission denied.");
    return;
  }
  if(*q == '_') q++;
 
  for(a = db[thing].list; a; a = AL_NEXT(a)) {
    strcpy(tbuf2, q);
    if(!AL_BAD(a) && wild_match(tbuf2, AL_NAME(a))) {
      sprintf(tbuf2, "%s/%s", tbuf1, AL_NAME(a));
      do_edit(player, tbuf2, argv);
    }
  }
}

void do_edit(player, it, argv)
    dbref player;
    char *it;
    char *argv[];
{
  dbref thing;
  int d, len, res;
  ATTR *a;
  char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN];
  char *r, *q, *s , *val;

  if(!(it && *it)) {
    notify(player, "I need to know what you want to edit.");
    return;
  }
  strcpy(tbuf1, it);
  q = index(tbuf1, '/');
  if(!(q && *q)) {
    notify(player, "I need to know what you want to edit.");
    return;
  }
  *q++ = '\0';

  init_match(player, tbuf1, NOTYPE);
  match_everything();
  thing = noisy_match_result();

  if (thing == NOTHING || !controls(player, thing)) {
    notify(player, "Permission denied.");
    return;
  }

  if (!argv[1] || !*argv[1]) {
    notify(player, "Nothing to do.");
    return;
  }

  val = argv[1];
  r = (argv[2]) ? argv[2] : (char *) "";
 
  if(*q == '_') q++;

  a = atr_get(thing, q);
  if(!a) {
    notify(player, "No such attribute, try set instead.");
    return;
  }
  if((a->flags & AF_LOCKED) && db[player].owner != db[a->creator].owner) {
    notify(player, "You need to control an attribute to edit it.");
    return;
  }
  s = (char *) uncompress(a->value);
  len = strlen(val);
  for (d = 0; (d < BUFFER_LEN) && *s;)
    if (strncmp(val, s, len) == 0) {
      if ((d + strlen(r)) < BUFFER_LEN) {
	strcpy(tbuf2 + d, r);
	d += strlen(r);
	s += len;
      } else
	tbuf2[d++] = *s++;
    } else
      tbuf2[d++] = *s++;
  tbuf2[d++] = 0;
  res = atr_add(thing, a->name, tbuf2, player, NOTHING);
  if(!res) {
    notify(player, "That attribute seems to have vanished!");
    return;
  }
  if(res == -1) {
    notify(player, "You don't have the power to change that.");
    return;
  }
  if(!Quiet(player) && !Quiet(thing))
    notify(player, tprintf("%s - Set: %s", a->name, tbuf2));
}

void do_trigger(player, object, argv)
    dbref player;
    char *object;
    char *argv[];
{
  dbref thing;
  int a;
  char *s;
  char tbuf1[BUFFER_LEN];

  strcpy(tbuf1, object);
  for(s = tbuf1; *s && (*s != '/'); s++);
  if(!*s) {
    notify(player, "I need to know what attribute to trigger.");
    return;
  }
  *s++ = '\0';

  init_match(player, tbuf1, NOTYPE);
  match_everything();
  thing = noisy_match_result();

  if(thing == NOTHING) return;

  if (!controls(player, thing) && string_compare(s, "DOES")) {
    notify(player, "Permission denied.");
    return;
  }

  if (God(thing) && !God(player)) {
    notify(player, "You can't trigger God!");
    return;
  }

#ifdef INHERIT_FLAG
  if (!Inherit(player) && Inherit(thing)) {
    notify(player, "Authorization failed.");
    return;
  }
#endif

  for (a = 0; a < 10; a++)
    wptr[a] = argv[a + 1];
  did_it(player, thing, NULL, NULL, NULL, NULL, s, NOTHING);
  if(!Quiet(player) && !Quiet(thing))
    notify(player, tprintf("%s - Triggered.", db[thing].name));
}


/* for lack of a better place, the use code is here */

void do_use(player, what)
     dbref player;
     const char *what;
{
  dbref thing;
  init_match(player, what, TYPE_THING);
  match_neighbor();
  match_possession();
  match_absolute();
  if ((thing = noisy_match_result()) != NOTHING) {
    if (!eval_boolexp(player, db[thing].usekey, thing, 0, USELOCK)) {
      notify(player, "Permission denied.");
      return;
    } else
      did_it(player, thing, "USE", "Used.", "OUSE", NULL, "AUSE", NOTHING);
  }
}