/* set.c */
#include "os.h"
#include "copyright.h"

/* commands which set parameters */
#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 (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 ((int)*password); password++);
      /* eat whitespace */
      if (*password) {
        *password++ = '\0';     /* terminate name */
        while (*password && isspace ((int)*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 (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 (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 (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 (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 (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 (dbref player, const char *name, 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 ((int)*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 (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 (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 (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 (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 (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);
  }
}