TinyMAZE/
TinyMAZE/config/
TinyMAZE/doc/
TinyMAZE/run/msgs/
TinyMAZE/src/
TinyMAZE/src/db/
TinyMAZE/src/ident/
TinyMAZE/src/io/
TinyMAZE/src/prog/
TinyMAZE/src/softcode/
TinyMAZE/src/util/
#include <string.h>
#include <unistd.h>
#include "db.h"
#include "config.h"
#include "externs.h"

static void destroy_player P((OBJ *));

OBJ *create_guest(char *name, char *alias, char *password)
{
  OBJ *player;

/* Make sure that old guest id's don't hang around! */
  player = match_player(NULL, name);
  if(player)
  {
    if(Guest(player))
      destroy_player(player);
    else
      return(NULL);
  }

/* Make new player */
  player = create_player(name, password, CLASS_GUEST,
    find_object(config.guest_start));
  if(!player)
  {
    log_error("Failed in create_player");
    return(NULL);
  }

  player->creator = root_obj;

/* Lock guest to him/her-self */
  atr_add(player, "LOCK", tprintf("#%d", player->dbref));

/* Set guest's description */
  atr_add(player, "DESC", config.guest_description);

/* Set guest's alias */
  if(!match_player(NULL, alias))
    atr_add(player, "ALIAS", alias);

/* Zero their quota */
  atr_clr(player, "QUOTA");

  return(player);
}

void destroy_guest(OBJ *guest)
{
  if(!Guest(guest))
    return;

  destroy_player(guest);
}

OBJ *create_player(char *name, char *password, int class, OBJ *start)
{
  OBJ *player;
  
  if(!start)
  {
    log_error("Default starting room doesn't exist!");
    return(NULL);
  }

  if(!ok_player_name(NULL, strip_color(name), "") ||
    !ok_password(password) || strchr(name, ' '))
  {
    log_error("failed in name check");
    return(NULL);
  }
  
  player = new_object(TYPE_PLAYER, 1);

/* Initialize everything */
  SET(player->name, strip_color(name));
  atr_add(player, "RAINBOW", name);
  player->location = start;
  player->link = start;
  player->owner = player;
  player->pows = (int *)stack_alloc(sizeof(int)*NUM_POWS, 1, 0);
  player->flags = TYPE_PLAYER|PLAYER_NEWBIE|INHERIT_POWERS;
  player->class = CLASS_GUEST;
  atr_add(player, "PASSWORD", crypt(password, "XX"));
  giveto(player, config.initial_wealth);
  atr_add(player, "QUOTA", tprintf("%d", config.default_quota));
  atr_add(player, "MOTD", "1");
  atr_add(player, "CQUOTA", tprintf("%d", config.default_channel_quota));

  player->next_con = start->contents;
  start->contents = player;

  do_class(root_obj,
    tprintf("#%d", player->dbref), class_to_name(class));

  if(class != CLASS_GUEST)
    do_join_channel(player, "public");

  return(player);
}

static void destroy_player(OBJ *player)
{
  OBJ *thing;

  if(Typeof(player) != TYPE_PLAYER)
    return;

/* Destroy everything the player owns */
  for(thing = thing_list;thing;thing = thing->next)
    if(thing->owner == player)
      destroy_obj(thing);
  for(thing = room_list;thing;thing = thing->next)
    if(thing->owner == player)
      destroy_obj(thing);
  for(thing = exit_list;thing;thing = thing->next)
    if(thing->owner == player)
      destroy_obj(thing);
  
  while(boot_off(player));
  do_halt(player, "");
  rem_from_channels(player);
  remove_from_mlists(player);	/* remove player from mailing lists */
  destroy_obj(player);
}

char *create_random_password()
{
  char passwd[4096], list[4096];
  char c;
  int i, ctr = 0;

  for(c = 'A';c <= 'Z';++c)
    list[ctr++] = c;
  for(c = 'a';c <= 'z';++c)
    list[ctr++] = c;
  for(c = '0';c <= '9';++c)
    list[ctr++] = c;

  for(i = 0;i < 8;++i)
    passwd[i] = list[my_rand()%ctr];
  passwd[i] = '\0';

  return(stack_string_alloc(passwd, 0));
}

void do_pcreate(OBJ *creator, char *player_name, char *player_password)
{
  OBJ *player, *p;
  OBJ **plist;
  int i, j, ctr;
  char player_email[4096], *e;
  
  if(!*player_name)
  {
    ctr = 0;

    for(p = player_list;p;p = p->next)
      ctr++;
    plist = (OBJ **)stack_alloc(sizeof(OBJ *)*ctr, 0, 0);
    ctr = 0;
    for(p = player_list;p;p = p->next)
      plist[ctr++] = p;
    for(i = 0;i < ctr;++i)
      for(j = i+1;j < ctr;++j)
        if(plist[i]->create_time > plist[j]->create_time)
        {
          player = plist[i];
          plist[i] = plist[j];
          plist[j] = player;
        }

    for(i = 0;i < ctr;++i)
      notify(creator, tprintf("%s |+W|- %s |+W|- |+C|%s",
        my_ljust(unparse_object(creator, plist[i]), 25),
        my_ljust(name(plist[i]->creator), 15),
        mktm(plist[i]->create_time, "D", creator)));
    return;
  }

  if(!power(creator, POW_PCREATE))
  {
    notify(creator, perm_denied());
    return;
  }
  
  if(!*player_name)
  {
    notify(creator, "You must specify a name.");
    return;
  }

  if((player = match_player(NULL, player_name)))
  {
    notify(creator, tprintf("There is already a player named %s.",
      unparse_object(creator, player)));
    return;
  }

  if(!ok_player_name(NULL, strip_color(player_name), ""))
  {
    notify(creator, tprintf("Illegal player name '%s'", player_name));
    return;
  }

  if(!*player_password)
  {
    notify(creator, "You must specify the new player's email address.");
    return;
  }
  if((e = strchr(player_password, ',')))
  {
    strcpy(player_email, e+1);
    *e = '\0';
  }
  else
  {
    strcpy(player_email, player_password);
    player_password = create_random_password();
  }

  player = create_player(player_name, player_password,
    CLASS_CITIZEN, player_start_obj);
  if(!player)
  {
    notify(creator, tprintf("Failure creating '%s'", player_name));
    return;
  }
  
  notify(creator, tprintf("New player '%s' created with password '%s'",
    unparse_object(creator, player), player_password));
  log_sensitive(tprintf("%s pcreated %s",
    unparse_object(creator, creator), unparse_object(player, player)));

  player->creator = creator;

  atr_add(player, "EMAIL", player_email);
}

void do_password(OBJ *player, char *old, char *newobj)
{
  if(!*Pass(player) ||
    (strcmp(old, Pass(player)) && strcmp(crypt(old,"XX"), Pass(player))))
  {
    notify(player, "You must match your old password.");
    return;
  }
  if(!ok_password(newobj))
  {
    notify(player, "Bad new password.");
    return;
  }
  
  atr_add(player, "PASSWORD", crypt(newobj, "XX"));
  notify(player, "Password changed.");
}

int owns_stuff(OBJ *player)
{
  OBJ *o;
  int num = 0;

  for(o = thing_list;o;o = o->next)
    if(o->owner == player)
      num++;
  for(o = room_list;o;o = o->next)
    if(o->owner == player)
      num++;
  for(o = exit_list;o;o = o->next)
    if(o->owner == player)
      num++;

  return(num);
}

void do_nuke(OBJ *player, char *arg, char *reason)
{
  OBJ *victim;
  
  if(!power(player, POW_NUKE))
  {
    notify(player, perm_denied());
    return;
  }
  
  victim = match_object(player, arg, TYPE_PLAYER);
  if(!victim)
  {
    notify(player, no_match(arg));
    return;
  }

  if(!controls(player, victim, POW_NUKE))
  {
    notify(player, perm_denied());
    return;
  }

  if(owns_stuff(victim))
  {
    notify(player, "You must @wipeout their junk first.");
    return;
  }

  if(!*reason)
  {
    notify(player, "You must specify a reason for nuking someone.");
    return;
  }

  notify(player, tprintf("%s - nuked.", name(victim)));
  notify_all(tprintf("|+W|-- |+R|%s has been nuked, "
    "toasted, destroyed, put to death, removed from %s by %s.",
    name(victim), config.maze_name, name(player)), NULL);
  log_sensitive(tprintf("%s(#%d) executed: @nuke %s(#%d). Reason: %s",
    name(player), player->dbref, name(victim), victim->dbref, reason));

  destroy_player(victim);
}

void do_class(OBJ *player, char *arg1, char *class)
{
  int i, newlevel;
  OBJ *who;

  if(!*arg1)
    who = player;
  else
  {
    who = match_object(player, arg1, TYPE_PLAYER);
    if(!who)
    {
      notify(player, no_match(arg1));
      return;
    }
  }

  if(!*class)
  {
    class = class_to_name(get_class(who));

    notify(player, tprintf("%s is %s %s.", name(who),
      (my_is_vowel(*class))?"an":"a", class));
    return;
  }
  
  if(is_root(who))
  {
    notify(player, "Root may not be reclassified!");
    return;
  }

  i = name_to_class(class);

  if(i == -1)
  {
    notify(player, tprintf("'%s': no such classification", class));
    return;
  }

  newlevel = i;
  
  log_sensitive(tprintf("%s tried to: @class %s=%s",
    unparse_object(player, player), name(who), class));

  if(!controls(player, who, POW_CLASS) ||
    (newlevel >= get_class(player) && !is_root(player)))
  {
    notify(player, perm_denied());
    return;
  }
  
  log_sensitive(tprintf("%s executed: @class %s=%s",
    unparse_object(player, player), name(who), class));

  notify(player, tprintf("%s is now reclassified as: %s",
    name(who), class_to_name(newlevel)));
  notify(who, tprintf("You have been reclassified as: %s",
    class_to_name(newlevel)));

  who->class = newlevel;

  for(i = 0;i < NUM_POWS;i++)
    set_pow(who, i,
      powers[i].init[class_to_list_pos(newlevel)]);
}

void do_empower(OBJ *player, char *whostr, char *powstr)
{
  int pow;
  int powval;
  OBJ *who;
  char *i, *p;

  if(!power(player, POW_SETPOW))
  {
    notify(player, perm_denied());
    return;
  }

  i = strchr(powstr, ':');

  if(!i)
  {
    notify(player,
      "Badly formed power specification. need powertype:powerval");
    return;
  }
  *i++ = '\0';

  if(!string_compare(i, "yes"))
    powval = PW_YES;
  else if(!string_compare(i, "no"))
    powval = PW_NO;
  else if(!string_compare(i, "yeseq"))
    powval = PW_YESEQ;
  else if(!string_compare(i, "yeslt"))
    powval = PW_YESLT;
  else
  {
    notify(player,
      "The power value must be one of yes, no, yeseq, or yeslt");
    return;
  }

  if((pow = name_to_pow(powstr)) == -1)
  {
    notify(player, tprintf("Unknown power: %s", powstr));
    return;
  }

  if(!(who = match_object(player, whostr, TYPE_PLAYER)))
  {
    notify(player, no_match(whostr));
    return;
  }

  if(!controls(player, who, POW_SETPOW))
  {
    notify(player, perm_denied());
    return;
  }

  if(get_pow(player, pow) < powval && !is_root(player))
  {
    notify(player, "But you yourself don't have that power!");
    return;
  }

  if(powers[pow].max[class_to_list_pos(get_class(who))] < powval)
  {
    p = "";

    switch(powers[pow].max[class_to_list_pos(get_class(who))])
    {
      case PW_NO:    p = "NO";    break;
      case PW_YES:   p = "YES";   break;
      case PW_YESLT: p = "YESLT"; break;
      case PW_YESEQ: p = "YESEQ"; break;
    }

    notify(player,
      tprintf("Maximum value for that power for that class is: %s", p));
    return;
  }

  set_pow(who, pow, powval);
  log_sensitive(tprintf("%s(%d) granted %s(%d) power %s, level %s",
    name(player), player->dbref, name(who), who->dbref, powstr, i));
  if(powval != PW_NO)
  {
    notify(who, tprintf("You have been given the power of %s.",
      pow_to_name(pow)));
    notify(player, tprintf("%s has been given the power of %s.",
      name(who), pow_to_name(pow)));
  }

  switch(powval)
  {
    case PW_YES:
      notify(who, "You can use it on anyone.");
      break;
    case PW_YESEQ:
      notify(who, "You can use it on people your class and under.");
      break;
    case PW_YESLT:
      notify(who, "You can use it on people under your class.");
      break;
    case PW_NO:
      notify(who, tprintf("Your power of %s has been removed.",
        pow_to_name(pow)));
      notify(player, tprintf("%s power of %s has been removed.",
        poss(who), pow_to_name(pow)));
      break;
  }
}

void do_quota(OBJ *player, char *arg1, char *arg2)
{
  OBJ *who;
  int owned, limit;
  
  if(*arg2)
    if(!power(player, POW_SETQUOTA))
    {
      notify(player, perm_denied());
      return;
    }

  if(!*arg1)
    who = player;
  else
  {
    who = match_object(player, arg1, TYPE_PLAYER);
    if(!who)
    {
      notify(player, no_match(arg1));
      return;
    }
  }

  if(!controls(player, who, POW_SETQUOTA))
  {
    notify(player, perm_denied());
    return;
  }
  
  owned = owns_stuff(who);
  
  if(!*arg2)
    limit = atoi(atr_get(who, "QUOTA"));
  else
  {
    limit = atoi(arg2);

    if(limit < 0)
    {
      notify(player, "New limit cannot be a negative number!");
      return;
    }
    
    atr_add(who, "QUOTA", tprintf("%d", limit));
  }
  
  notify(player,
    tprintf("|+B|Objects|+W|: |+C|%d   |+B|Limit|+W|: |+C|%s", owned,
    (power(who, POW_NOQUOTA))?"UNLIMITED":tprintf("%d", limit)));
}

void do_inactive(OBJ *player)
{
  OBJ *i;
  int k, l;
  int ctr = 0;
  time_t last_on;
  float days_old;
  char buf[4096];
  struct plist_struct
  {
    OBJ *player;
    float days;
    int objs;
  } *plist = NULL;

  if(!power(player, POW_DB))
  {
    notify(player, perm_denied());
    return;
  }

  for(i = player_list;i;i = i->next)
  {
    if(is_root(i) || get_class(i) != CLASS_CITIZEN)
      continue;

    last_on = atol(atr_get(i, "LASTCONN"));
    if(!last_on)
      last_on = i->create_time;
    if(now-last_on > 15552000)   /* 180 days (believe it or not) */
    {
      days_old = (float)(now-last_on)/(60*60*24);

      if(!ctr)
        plist = (struct plist_struct *)stack_alloc(sizeof(struct plist_struct),
         0, 0);
      else
        plist = (struct plist_struct *)stack_realloc_tmp(plist,
          sizeof(struct plist_struct)*(ctr+1));

      plist[ctr].player = i;
      plist[ctr].days = days_old;
      plist[ctr].objs = owns_stuff(i);

      ctr++;
    }
  }

  if(!ctr)
    notify(player, "No players have been inactive for more than 180 days.");
  else
  {
    struct plist_struct ptemp;

    for(k = 0;k < ctr;++k)
      for(l = k+1;l < ctr;++l)
        if(plist[k].days < plist[l].days)
        {
          ptemp.player = plist[k].player;
          ptemp.days = plist[k].days;
          ptemp.objs = plist[k].objs;
          plist[k].player = plist[l].player;
          plist[k].days = plist[l].days;
          plist[k].objs = plist[l].objs;
          plist[l].player = ptemp.player;
          plist[l].days = ptemp.days;
          plist[l].objs = ptemp.objs;
        }

    for(k = 0;k < ctr;++k)
    {
      if(plist[k].objs)
        sprintf(buf, " |+R|%d object%s",
          plist[k].objs, check_plural(plist[k].objs, "", "s"));
      else
        *buf = '\0';

      notify(player, tprintf("%s |+W|(%.0f days inactive)%s",
        unparse_object(player, plist[k].player), plist[k].days, buf));
    }
    notify(player, tprintf("\n|+W|%d |B|inactive players found.", ctr));
  }
}

void do_complain(OBJ *player, char *arg1, char *arg2)
{
  OBJ *obj;

  if(!(obj = match_player(NULL, arg1)))
  {
    notify(player, no_match(arg1));
    return;
  }

  if(!*arg2)
  {
    notify(player, "You must specify a reason for this complaint.");
    return;
  }

  log_complaint(tprintf("%s(#%d) complains about %s(#%d) because: %s",
    name(player), player->dbref, name(obj), obj->dbref, arg2));

  notify(player, "Your complaint has been logged.");
}