/* * 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); } }