musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/* 
 * MusicMUD Daemon, version 1.0
 * Copyright (C) 1998-2003 Abigail Brady
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hopex that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 */
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>

#include "wordwrap.h"
#include "msi.h"

#include "musicmud.h"
#include "Player.h"
#include "zoneload.h"
#include "State.h"
#include "util.h"
#include "Pager.h"
#include "util.h"
#include "flags.h"
#include "Socket.h"
#include "pflags.h"
#include "vsprintf.h"
#include "misc.h"
#include "wordwrap.h"
#include "paths.h"
#include "config.h"
#include "pflagnames.h"
#include "colour.h"
#include "emsg.h"

extern "C" char *crypt(const char *, const char *);

Player::Player(const char *id, msi::player *p) : 
  MudObject(id), 
  state(0), idle_since(now), 
  on_since(now), time_since(now),
  need_prompt(1), had_prompt(0),
  p(p), curcol(0)

 {
    push_state(new PersonState("name"));
    players->add(*this);
}

Player::~Player() {
    players->remove(*this);
    while (state) pop_state();
    if (of) {
      xclose(of);
    }
}

void Player::push_state(PersonState *what) {
   what->previous = state;
   state = what;
}

void Player::push_state(const char *what) {
   push_state(new PersonState(what));
}

void Player::pop_state() {
	if (state) {
		PersonState *thing = state->previous;
		delete state;
		state = thing;
	}
}

void Player::set_state(const char *name) {
  state->set("state", name);
  state->unset("prompt");
}

string freadline(FILE *f, int esc=0) {
  char buffer[4000];
  buffer[0] = 0;
  fgets(buffer, 3000, f);
  if (feof(f))
    return buffer;
  buffer[3000] = 0;
  char *q = strchr(buffer, '\n');
  if (q) {
    *q = 0;
  }
  string s1="";
  q = buffer;
  while (q && *q) {
    if (esc) {
      if (*q=='\\') {
	q++;
	s1 += *q;
	q++;
	continue;
      }
    }
    s1 += *q;
    q++;
  }
  return s1;
}

string dollar_parse(const char *src)
{
  string t = "", fn = "";
  enum {
    NORMAL,
    HELP,
    INFO,
    POLICY,
    HREF
  } magmode = NORMAL;

  while (*src) {
    if (src[0]=='$') {
      if (src[1]=='$') {
	t += "$";
	src += 2;
	continue;
      }

      if (src[1]=='<') {
	magmode = HREF;
	src += 2;
	fn = "";
	continue;
      }

      if (src[1]=='[') {
	magmode = HELP;
	src += 2;
	fn = "";
	continue;
      }

      if (src[1]=='(') {
	magmode = INFO;
	src += 2;
	fn = "";
	continue;
      }

      if (src[1]=='{') {
	magmode = POLICY;
	src += 2;
	fn = "";
	continue;
      }

      if (strchr("])}>", src[1])) {
	src += 2;
	
	if (magmode == HREF) {
	  string link = fn;
	  if (link.substr(0, 7) != "http://" &&
	      link.substr(0, 6) != "ftp://" &&
	      link.substr(0, 5) != "news:" &&
	      link.substr(0, 7) != "mailto:" &&
              link.substr(0, 9) != "telnet://")
	    link = "http://" + link;
	  t += ssprintf("\3a href=\"%s\"\4&_%s&_\3/a\4", link, fn);
	} else {
	  const char *key = "info";
	  if (magmode == HELP) key = "help";
	  if (magmode == POLICY) key = "policy";
	  
	  t += "\3send href='";
	  t += key;
	  t += " ";
	  const char *topic = fn.c_str();
	  if (!strcmp(key, "help") && strncmp(topic, "help ", 5)==0)
	    topic += 5;
	  if (!strcmp(key, "info") && strncmp(topic, "info ", 5)==0)
	    topic += 5;
	  if (!strcmp(key, "info") && strncmp(topic, "policy ", 7)==0)
	    topic += 7;
	  t += topic;
	  t += "'\4";
	  t += "&_";
	  t += fn;
	  t += "&_";
	  t += "\3/send\4";
	}
	magmode = NORMAL;
	continue;
      }
    }

    if (magmode) {
      fn += *src;
      src++;
      continue;
    }
    
    t += *src;
    src++;
    continue;
  }

  return t;
}

bool Player::spewfile(const char *dir, const char *name, bool show_error,
		      const char *grepfor) {
    FILE *f = xopen(dir, name, "r");
    if (!f) {
      if (show_error) {
	printf("Cannot open : %s.\n", name);
      } 
      return false;
    }

    char tmpname[256];
    sprintf(tmpname, "spewfile.%i.%s", getpid(), id);
    FILE *of = xopen("tmp", tmpname, "w");
    if (!of) {
      xclose(f);
      return 0;
    }
    int curcol=0;

    int multi_line = 0;

    bool had_data = 0;

    while (1) {
      string s = freadline(f);
      if (feof(f) && !s.length())
	break;

      const char *buffer = s.c_str();

      if (buffer[0]=='@' && buffer[1]=='@' && buffer[2] == '!') {
	static int eek=0;
	if (!eek) {
	  eek++;
	  interpret(buffer+3);
	  eek--;
	}
	xclose(f);
	xclose(of);
	xunlink("tmp", tmpname);
	return true;
      }

      if (multi_line) {
	if (buffer[0]==']')
	  multi_line = false;
	continue;
      }
      if (buffer[0]==']') {
	continue;
      }
      if (buffer[0]=='[') {
	bool just_kill_it = false, reverse_sense = false;
	if (buffer[1]=='-') {
	  multi_line = true;
	  buffer++;
	}
	if (buffer[1]=='*') {
	  just_kill_it = true;
	  buffer++;
	}
	if (buffer[1]=='!') {
	  reverse_sense = true;
	  buffer++;
	}
	if (strncmp(buffer+1, "PFL_", 4)==0) {
	  bool allowed = false;
	  if (!strchr(buffer, ']'))
	    continue;
	  string priv = buffer;
	  priv = priv.substr(0, strchr(buffer, ']')-buffer);
	  buffer = strchr(buffer, ']');
	  if (buffer) {
	    buffer++;
	  }
	  PFlag pf = find_priv(priv.c_str()+1);
	  if (pf != PFL_NONE && get_priv(pf))
	    allowed = 1;
	  if (reverse_sense) allowed = !allowed;
	  if (!allowed) {
	    if (just_kill_it) {
	      break;
	    }
	    continue;
	  } else {
	    multi_line = false;
	  }
	}
      }

      if (buffer[0]=='@' && buffer[1]=='@' && buffer[2]=='I') {
	fprintf(of, "^W%s^n\n", buffer+4);
	int l = colour_strlen(buffer+4, colinfo(this));
	if (l>79)
	  l = 79;
	string s;
	while (l) {
	  s += "^-";
	  l--;
	}
	fprintf(of, "^B%s^n\n", s.c_str());
	had_data = 1;
	continue;
      }

      s = dollar_parse(buffer);
      buffer = s.c_str();

      curcol = 0;
      string temp = wordwrap(buffer, columns(this), 0, colinfo(this), &curcol);
      fprintf(of, "%s\n", temp.c_str());
      had_data = 1;
    }
    xclose(of);
    xclose(f);

    if (!had_data) {
      xunlink("tmp", tmpname);
      return 0;
    }

    f = xopen("tmp", tmpname, "r");
    xunlink("tmp", tmpname);

    PagerState *thing = new PagerState(f);
    push_state(thing);
    if (grepfor && strlen(grepfor)) {

      if (grepfor[0]=='|')
	thing->set("title", grepfor+1);
      else
	thing->set("grepfor", grepfor);
    }
    state_pager(this, NULL);
    return 1;
}

void Player::push_data() {
  if (!buffer.length()) 
    return;

  if (p)
    p->write(buffer.c_str(), colinfo(this));

  buffer = "";
}

void Player::send_data(const char *prompt, int want_noecho) {
  push_data();

  if (prompt && strlen(prompt) && p) {

    p->write(prompt, colinfo(this));

    if (want_noecho != -1) 
      p->noecho(want_noecho);
    
    p->eor();
  }
 
  if (p)
    p->flush();
}

void Player::real_spec_printf(const char *format, const PARMS &p)
{
    spec_buffer += formatprint(this, format, p);
}

bool Player::cancel_printf() {
  bool f = spec_buffer == "";
  spec_buffer = "";
  return f;
}

string term(msi::player *p)
{
  if (p)
    return p->term;
  else
    return "";
}

enum WithdrawStyle {
  NewStyle,
  ClrStyle,
  OldStyle,
  EditStyle,
};

WithdrawStyle whichstyle(Player *p) {
  string t = term(p->p);

  if (t == "cryotel" || t == "ucryotel" || t == "zmud")
    return ClrStyle;

  if (p->p->lineeditor)
    return EditStyle;

  if (p->get_flag(FL_CLRSTYLE))
    return ClrStyle;

  if (p->get_flag(FL_NEWSTYLE))
    return NewStyle;

  return OldStyle;
}

void Player::real_printf(const char *format, const PARMS &parms, MudObject *target, int snoop) {
  if (!format) 
    return;

  if (!target)
    target = this;

  if (spec_buffer.length()) {
    string a = spec_buffer;
    spec_buffer = "";
    printf("%s", a.c_str());
  }

  if (of) {
    string temp = formatprint(target, format, parms);
    fprintf(of, "%s", temp.c_str());
    return;
  }

  MudObject::real_printf(format, parms, target, snoop);

  if (p) {
    set("needredo", 0);
    if (had_prompt && get_int("style")==2) {
      switch (whichstyle(this)) {
      case EditStyle:
	set("needredo", 1);
      case ClrStyle:
	buffer += ssprintf("\r\033[%iD\033[0K\2", columns(this)); 
	break;
      case NewStyle: buffer += "\r"; break;
      case OldStyle: default: buffer += "\n"; break;
      }      
      had_prompt = 0;
    }
    
    string temp = formatprint(target, format, parms);
    seenbuffer += temp;

    if (!p->rawmode) {
      temp = wordwrap(temp.c_str(), columns(this), 0, colinfo(this), &curcol);
    }
 
    buffer += temp.c_str();
    if (!p->rawmode) {
      if (get_int("style")!=0) 
	need_prompt = 1;
    }

    if (buffer.length() > 4096) 
      send_data();
  } else {
    seenbuffer += formatprint(target, format, parms);
  }
}

State *Player::get_state() const {
  State *s = states->get(state->get("state"));
  if (!s) 
    s = states->get("cmd");
  return s;
}

int shouldset_titlebar(const char *prompt, Player *p)
{
  if (strstr(prompt, "%X")) return 1;

  if (strstr(prompt, "%x")) {
    if (!p->p)
      return 0;
    
    if (p->p->term == "xterm" ||
	p->p->term == "screen" ||
	p->p->term == "cryotel" ||
	p->p->term == "ucryotel" ||
	p->p->term == "dtterm" ||
	strstr(p->p->term.c_str(), "xterm")) {
      return 1;
    }
  }

  return 0;
}

static string wanted_titlebar(MudObject *who) {
  string buffer = "";

  const char *thismud = MUDNAME;

  if (who->owner) {
    const char *n = name(who->owner);
    if (who->get_priv(PFL_SEESTATS)) {
      if (n)
	return ssprintf("%s (%s) - %s@%s", bwname(who->owner), who->owner->id, bwname(who), thismud); 
      
      return ssprintf("%s - %s@%s", who->owner->id, bwname(who), thismud); 
    } else {
      if (n)
	return ssprintf("%s - %s@%s", bwname(who->owner), bwname(who), thismud);

      return ssprintf("??? - %s@%s", bwname(who), thismud); 
    }
  }

  return ssprintf("??? - %s@%s", bwname(who), thismud); 
}

string process_prompt(const MudObject *who, const string &f) {
  string buffer;
  const char *format = f.c_str();

  while (*format) {    
    switch (*format) {
    default:
      buffer += *format;
      format++;
      break;

    case '%': 
      {
	format++;
	switch (*(format++)) {
	case '%':
	  buffer += '%';
	  break;
	case 'r':
	  if(!format[0] || !format[1])
	    break;
	  buffer += ssprintf("%c", format[(who->get_int("remort")!=-1) ||
			  !who->get_priv(PFL_REMORT)]);
	  format+=2;
	  break;
	case 'h':
	  buffer += ssprintf( "%i", strength_of(who));
	  break;
	case 'H':
	  buffer += ssprintf( "%i", who->get_int("maxstrength"));
	  break;
	case 'l':
	  buffer += ssprintf("%i", privs_of(who));
	  break;
	case 'n':
	  buffer += ssprintf("%s", name(who)); 
	  break;
	case 'p':
	  {
	    MudObject *it = who->get_object("!it");
	    if (it && !it->nuke_me)
	      buffer += ssprintf("it: %s ", name(it));
	  }
	  {
	    MudObject *it = who->get_object("!them");
	    if (it && !it->nuke_me)
	      buffer += ssprintf("them: %s ", name(it));
	  }
	  {
	    NewWorld stuff;
	    for (int i=0;i<who->array_size("!them");i++) {
	      MudObject *it = who->array_get_object("!them", i);
	      if (it && !it->nuke_me)
		stuff.add(it);
	    }
	    if (stuff) {
	      buffer += ssprintf("them: %W ", &stuff);
	    }
	  }
	  {
	    MudObject *it = who->get_object("!him");
	    if (it && !it->nuke_me)
	      buffer += ssprintf("him: %s ", name(it));
	  }
	  {
	    MudObject *it = who->get_object("!her");
	    if (it && !it->nuke_me)
	      buffer += ssprintf("her: %s ", name(it));
	  }
	case 'X':
	case 'x':
	  break;
	default:
	  buffer += "%";
	  buffer += format[-1];
	}
      }
    }
  }
  return buffer;
}

string Player::get_prompt2() const {
  return process_prompt(this, get_prompt3());
}

string Player::get_prompt3() const {
  if (state && streq(state->get("state"), "cmd") && get("prompt.cmd")) {
    return get("prompt.cmd");
  }

  const char *localprompt = state->get("prompt");
  if (localprompt) {
    return localprompt;
  }

  State *s = states->get(state->get("state"));
  if (s) {
	const char *c = s->get_prompt();
	if (c) return c;
  }

  return ssprintf("bug : %s > ", state?state->get("state"):"no state");
}

string Player::get_prompt() const {
  if (p && p->rawmode)
    return "";

  string buffer = "";
  
  if (invis(this)>0)
    buffer += "(";

  if (get_flag(FL_HASMAIL)) {
    buffer += "^YNewMail^n ";
  }
  if (get("away")) {
    buffer += "^YAway^n ";
  }

  buffer += get_prompt2();

  if (invis(this)>0)
    buffer += "^n)";
  else
    buffer += "^n";

  return buffer;
}

Player *who_i_am = 0;
const char *what_i_am_doing = 0;

int cp2uni(int, charset);

string uni2mud(const string &blah)
{
  string b;
  unsigned const char *t = (unsigned const char *)blah.c_str();
  while (*t) {
    if (!(*t&0x80))  {
      b += *t;
      t++;
    } else {
      if (*t >= 0xc0 && *t <= 0xc3) {		
	int la = (*t - 0xc0) << 6;
	t++;	
	la |= (*t & 63);
	t++;
	b += la;
      } else if (*t >= 0xc4 && *t <= 0xdf) {
	int ucs = (*t - 0xc0) << 6;
	t++;
	ucs |= (*t & 63);
	t++;
	b += ssprintf("^#%i;", ucs);
      } else if (*t >= 0xe0) {
	int ucs = (*t - 0xe0) << 6;
	t++;
	ucs |= (*t & 63);
	t++;
	ucs <<= 6;
	ucs |= (*t & 63);
	t++;	
	b += ssprintf("^#%i;", ucs);
      } else {
	b += "?";
	while (t && *t&0x80)
	  t++;
      }
    }
  }
  return b;
}

static string cset2mud(charset cset, string what)
{
  if (cset == CHAR_LATIN1 || cset==CHAR_ASCII)
    return what;
  if (has_table(cset)) {
    string tmp;
    const char *t = what.c_str();
    while (*t) {
      int ch = cp2uni(*t, cset);
      if (ch < 0x100)
	tmp += ch;
      else
	tmp += ssprintf("^#%i;", ch);
      t++;
    }
    return tmp;
  }
  if (cset==CHAR_UNICODE)
    return uni2mud(what);
  return what;
}

void Player::input(const char *str) {
  if (!quit)
    handle_input(str);
}

void Player::handle_input(const char *temp)
{
  who_i_am = this;
  what_i_am_doing = temp;
  charset cset = get_charset(this);

  string la = cset2mud(cset, temp);
  had_prompt=0;
  curcol = 0;
  seenbuffer = "";
  if (get_state()) {
    try {
      get_state()->invoke(this, la.c_str());
    } catch (emsg & e) {
      printf("%s\n", e.msg.c_str());
    }
  }
  need_prompt = !nuke_me && !quit;
  if (temp[0] && !get_flag(FL_IDLING)) {
    idle_since = now;
  }
}

void Player::tryio(bool trytoreadstuff) {
    if (!p && !nuke_me && !quit) {
      if ((now-idle_since)>=(300)) {
	log(PFL_SEEINFO, 0, "player", "%s : idling out (linkdead)", id);
	interpret2("quit");
      }
    }
    int thingy_from = p?p->thingy_from:0;

    bool diepromptdie = false;
    if (p && p->term=="mushclient")
      diepromptdie = true;

    if ((quit) || nuke_me) {
      if (p) {
	send_data();
	delete p;
	p = NULL;
      }
      nuke_me = 1;
      need_prompt = 0;
    }

    if (quit)
      return;

    if (!p)
      return;

    if (trytoreadstuff) {
	int rval=p->read(this);

	if (rval>=0) {
	} else if (rval==0) {
	    need_prompt = 0;
	} else if (rval==-2) {
	    delete p;
	    p = NULL;
	    if (!quit) {
	      if (get_flag(FL_LOGGEDIN)) {
		oprintf(secret(this), "%#M has lost %s link.\n", this, his_or_her(this));
		log(PFL_SEEINFO, 0, "net", "%s is linkdead", id);
	      }
	      else
		if(thingy_from==17) /* webmake */
		  log(PFL_SEEINFO, LEV_INTERNAL, "net", "%s is linkdead", id);
		else
		  log(PFL_SEEINFO, 0, "net", "%s is linkdead", id);
	      
	      if (!get_flag(FL_LOGGEDIN)) {
		nuke_me = 1;
		vanish_contents(this);
	      } else {
		interpret("saveplayer");
	      }
	      
	      idle_since = now;
	    }
	}
    } 

    string extra_data;
    int want_noecho=0;

    if (get_int("style")!=4 && p && !p->rawmode) {
	if (need_prompt && (nuke_me!=1)) {
	  extra_data = get_prompt();

	  State *s = get_state();
	  if (streq(s->id, "cmd") && diepromptdie)
	    extra_data = "";

	  string fmt = get_prompt3();
	  if (p && shouldset_titlebar(fmt.c_str(), this)) {
	    string wanted = wanted_titlebar(this);
	    if (wanted != p->titlebar) {
	      p->titlebar = wanted;
	      extra_data += "\33]0;" + wanted + "\a\2";
	    }
	  }

	  if (p) {
	    State *s = get_state();
	    if (s && s->needs_noecho()) {
	      want_noecho = 1;
	    }
	    need_prompt=0;
	    if (extra_data.length())
	      had_prompt=1;
	  }
	}
    }
    
    if (!get_flag(FL_NOTIMEOUT)) {
      if ((now-idle_since)>=(get_flag(FL_LOGGEDIN)?(30*60):(5*60))) {
	log(PFL_SEEINFO, 0, "player", "%s : idling out", id);
	idle_since = now;
	printf("You idled out.\n");
	interpret2("quit");
      }
    }

    if (get_int("needredo", 0)) {
      extra_data += p->buffer;
      set("needredo", 0);
    }

    if (extra_data.length() || buffer.length())
      send_data(extra_data.c_str(), want_noecho);
}

void Player::execute() {
  while (todo.size()) {
    string s = *todo.begin();
    handle_input(s.c_str());
    todo.erase(todo.begin());
  }

  if (get_flag(FL_LOGGEDIN))
    MudObject::execute();  
}

void Player::set(const char *k, const char *v) {
  bool eep = false;
  if (streq(k, "id") && (players->get(id)==this)) {
    eep = true;
    players->remove(*this);
  }
  MudObject::set(k, v);
  if (eep) {
    players->add(*this);
  }
}

void Player::size_changed()
{
}

void Player::username(const char *s)
{
  if (get_state() && streq(get_state()->id, "name")) {
    input(s);
  }
}