/* * MusicMUD - Login module * 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 hope 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 <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <ctype.h> #include <dirent.h> #include "musicmud.h" #include "State.h" #include "pflags.h" #include "misc.h" #include "Verb.h" #include "verbs.h" #include "units.h" #include "msi.h" #include "mailboard.h" #include "hooks.h" #include "trap.h" #include "nations.h" #define MODULE "state" #include "verbmodule.h" #define ARRAY_SIZE(a) (sizeof(a)/(sizeof((a)[0]))) static bool valid_char(char what) { return isalpha(what); } const char *illegal_names[] = { "i", "you", "he", "she", "they", "it", "me", "him", "her", "them", "my", "your","his", "their", "mine","yours", "hers", "its", "all", "a", "an", "the", "some", "any", "here", "this", "that", "those", "these", "self", "myself", "someone", "somebody", "something",}; static const char *legal_name(const char *who) { if (!who) return "is null"; if (strlen(who)>12) return "too long"; if (strlen(who)<2) return "too short"; if (planet->get(who) || verbs->get(who)) return "used internally"; for (size_t i=0;i<ARRAY_SIZE(illegal_names);i++) { if (strcasecmp(who, illegal_names[i])==0) return "used internally"; } while (*who) { if (!valid_char(*who)) { return "Name must comprise solely letters"; } who++; } return 0; } static char rsalt() { int r = random_number(64); if (r < 26) return 'a' + r; if (r < 52) return 'A' + r - 26; if (r < 62) return '0' + r - 52; if (r == 62) return '.'; return '/'; } static void state_setname(Player *p, const char *name) { if (!name) return; if (!*name) { p->printf("Ok then.\n"); p->interpret("quit"); return; } string lname = make_lower(name); if (lname == "new") { p->printf("To create a new character, just enter the name.\n"); return; } if (lname == "guest") { p->printf("We don't have a guest account. To create an account, just enter the name you want to use.\n"); log(PFL_SEEINFO, 0, "net", "%s wanted to log in as guest", p->id); return; } if (lname == "who" || lname == "users") { p->interpret(lname.c_str()); log(PFL_SEEINFO, 0, "net", "%s did '%s'", p->id, lname.c_str()); return; } OfflinePlayer ply(0); ply.grab(0, lname.c_str()); if (!!ply) { if (level_of(ply) < mud->get_int("levellock")) { p->printf("The mud is currently closed. Try again later.\n"); p->interpret("quit"); return; } p->set_state("password"); PersonState *ps = p->get_person_state(); ps->set("id", lname); ps->set("password", ply->get("password")); if (!ply->get_priv(PFL_INVIS)) { ps->set("maxinvis", 0); ps->set("invis", 0); } else { ps->set("maxinvis", privs_of(ply)); ps->set("invis", invis(ply)); } ps->set("attempts", 0); p->printf("\nWe already have a '%s'. If this isn't you, just hit return to be prompted for another name.\n\n", ply->get("name")); log(PFL_SEEINFO, 0, "net", "logging in as %s", ps->get("id")); return; } if (const char *reason=legal_name(lname.c_str())) { p->printf("You can't use '%s' : %s.\n", name, reason); return; } Player *pl; int i; foreach(players, pl, i) if (pl != p) { PersonState *per = pl->get_person_state(); if (!pl->get_flag(FL_LOGGEDIN)) { if (streq(per->get("id"), lname.c_str())) { p->printf("You can't use that name as someone is already creating a character with it.\n"); return; } } } if (mud->get_int("levellock")!=-1) { p->printf("Creation of new players is temporarily disabled. Try again later.\n"); p->interpret("quit"); return; } p->set_state("confirm"); PersonState *ps = p->get_person_state(); ps->set("name", ssprintf("^p%#s^n", name)); ps->set("id", lname); ps->set("prompt", ssprintf("Did I hear correctly, ^p%s^n? ", ps->get("name"))); return; } static void state_confirm(Player *p, const char *name) { if (!name) return; if (no(name)) { p->set_state("name"); return; } if (yes(name)) { PersonState *ps = p->get_person_state(); log(PFL_SEEINFO, 0, "net", "creating a new char called %s", ps->get("id")); p->set_state("askpwd"); return; } p->printf("Yes or no?\n"); } static void state_askpass(Player *p, const char *w) { if (strlen(w)<2) { p->printf("Your password is too short.\n"); return; } char salt[3] = { rsalt(), rsalt(), 0 }; PersonState *ps = p->get_person_state(); ps->set("password", crypt(w, salt)); p->set_state("conpwd"); } static void state_conpass(Player *p, const char *w) { PersonState *ps = p->get_person_state(); const char *np = crypt(w, ps->get("password")); if (streq(np, ps->get("password"))) { p->set_state("sex"); } else { p->printf("Passwords didn't match. Try again.\n"); p->set_state("askpwd"); } } MudObject *find_nation(const char *str) { World<MudObject> nats = nations(true); MudObject *o; int i; foreach_alpha(&nats, o, i) { if (ssprintf("%i", i)==str) return o; if (o->get("nation") && strcasecmp(o->get("nation"), str)==0) return o; if (o->get("name") && strcasecmp(o->get("name"), str)==0) return o; if (o->get("country") && strcasecmp(o->get("country"), str)==0) return o; if (o->get("shortcountry") && strcasecmp(o->get("shortcountry"), str)==0) return o; } return 0; } static void state_nation(Player *p, const char *w) { PersonState *ps = p->get_person_state(); if (!w) { World<MudObject> nats = nations(true); if (nats.getsize()==0 || nats.getsize()==1) { if (nats.getsize()) ps->set("nation", nats.get(0)->get("nation")); goto nextstate; } MudObject *o; int i; p->printf("Available nations : \n"); foreach_alpha(&nats, o, i) { p->printf(" %i) - %s \n", i, o->get("name")); } p->set_state("nation"); return; } { MudObject *n = find_nation(w); if (!n) { p->printf("Can't find that nation.\n"); return; } ps->set("nation", n->get("nation")); } nextstate: if (!p->spewfile(DATA_INFO, "newbiemotd.i", 0)) { p->spewfile(DATA_INFO, "motd.i", 0); } p->set_state("newbiemotd"); return; } static void state_sex(Player *p, const char *w) { if (!w) return; if (strlen(w)!=1) return; switch (tolower(*w)) { case 'm': case 'f': case 'n': case 'a': { PersonState *ps = p->get_person_state(); ps->set("gender", w); state_nation(p, 0); } default: return; } } static bool bad_place(MudObject *o) { if (!o->owner) return 1; if (o->owner == mud) return 1; return 0; } static void clone_newbiekit(MudObject *p) { int kitsize = mud->array_size("$newbiekit"); for (int i=0;i<kitsize;i++) { if (MudObject *w = mud->array_get_object("$newbiekit", i)) { w = clone_object(w, p); w->set(KEY_WORNBY, p->id); } } } static void housekeeping(Player *p) { p->set("logincount", p->get_int("logincount", 0)+1); p->set("lastlogin", now); if (p->p) p->set("hostname", p->p->get_ip()); p->set_bflag(FL_LOGGEDIN, 1); } static void announce_entry(MudObject *p) { if (invis(p)) return; MudObject *smith = MUD_ANNOUNCER; if (!smith) return; if (const char *dob = p->get("finger.dob")) { if (strlen(dob)==strlen("1979-01-01")) { dob += 5; char today[100]; struct tm* t = localtime(&now); strftime(today, 100, "%m-%d", t); if (streq(dob, today)) { smith->interpretf("int Happy birthday, %M^i!", p); return; } } } smith->interpretf("int Welcome, %M^i.", p); } static void state_newbiemotd(Player *p, const char *w) { bool frob = 0; DIR *d = opendir(DATA_USERS); if (d) { frob = 1; // If there are any FILES in the vardata/users directory, then // we don't frob this user to master user. while (dirent *de=readdir(d)) { if (de->d_name[0]=='.') continue; if (strchr(de->d_name, '#')) continue; if (strchr(de->d_name, '~')) continue; struct stat s; string f = DATA_USERS; f += "/"; f += de->d_name; stat(f.c_str(), &s); if (S_ISDIR(s.st_mode)) { continue; } frob = 0; } closedir(d); } PersonState *ps = p->get_person_state(); p->set("short", ps->get("id")); p->set("id", ps->get("id")); p->set("name", ps->get("name")); p->set("password", ps->get("password")); p->set("level", 1); p->set("gender", ps->get("gender")); p->set("nation", ps->get("nation")); p->set("created", now); p->set("maxstrength", 200); p->set(KEY_STRENGTH, 200); set_mass_capacity(p, 20 * KILOGRAM); p->set("style", 2); p->set("colours", "classic"); if (MUD_NEWBIESTART) set_owner(p, MUD_NEWBIESTART); if (bad_place(p) && MUD_DEFHOME) set_owner(p, MUD_DEFHOME); if (frob) p->set("privs", LEV_CHIEF); MudObject *no = natobj(p); if (!no || !dotrap(E_ONNEWBIESTART, p, no)) { /* ::: newbie_start o1==their nation; return 1 to not clone default newbie kit for them */ clone_newbiekit(p); } log(PFL_SEEINFO, 0, "net", "\a^Mcreated^n (nation:%s) in %s^n", p->get("nation"), p->owner->id); announce_entry(p); p->oprintf(secret(p) && cansee, "%s\n", build_setin(p, setqin).c_str()); p->lprintf(secret(p) && cansee, "%s\n", build_setin(p, setqin).c_str()); housekeeping(p); p->interpret("look"); p->interpret("calc_level"); p->interpret("save"); p->pop_state(); p->push_state("cmd"); } static void load_player(Player *p, const char *nm) { FILE *f = xopen(DATA_USERS, nm, "r"); if (!f) return; string buf; const char *buffer; buf = getline(f); while (buf != "}") { if (feof(f)) break; buf = getproperty(f); buffer = buf.c_str(); p->load(buffer); } p->set("id", nm); NewWorld eq; while (1) { buf = getproperty(f); buffer = buf.c_str(); if (feof(f)) { break; } string cloneof = buf.substr(buf.find(' ')+1); cloneof = cloneof.substr(0, cloneof.find(' ')); MudObject *k = 0; if (planet->get(cloneof.c_str())) k = clone_object(planet->get(cloneof.c_str()), p, NULL); if (k) eq.add(*k); while (buf != "}") { if (feof(f)) { break; } buf = getproperty(f); if (k) { k->load(buf.c_str()); } } } MudObject *o; int i; foreach(&eq, o, i) { int i2 = o->get_int("!inid"); if (i2 != -1) { MudObject *m; int j; foreach(&eq, m, j) { if (m->get_int("!id")==i2) { set_owner(o, m); continue; } } } } foreach(&eq, o, i) { MudObject *r = o->get_object("$rider"); if (r == p) p->set("$mount", o->id); o->unset("$rider"); r = o->get_object("$wielder"); if (r == p) { p->set(KEY_WIELD, o->id); } else { o->unset("$wielder"); } } xclose(f); } static void verb_buffer(Player *who, int, const char *[]) { if (!who->get_flag(FL_NOBUFFER)) { char *s = strdup(who->seenbuffer.c_str()), *s2 = s; s = strtok(s, "\n"); who->printf("\n&=LW-->^n\n"); while (s) { who->printf("%s\n", s); s = strtok(NULL, "\n"); } who->seenbuffer = ""; who->printf("\n^n&=LW<--^n\n"); free(s2); } } static void state_password(Player *p, const char *w) { if (!w) return; if (!*w) { p->pop_state(); p->push_state("name"); return; } PersonState *ps = p->get_person_state(); if (!ps->get("password")) { p->printf("That's odd, you have no password set? This message should never happen.\n"); return; } if (!ps->get("id")) { p->printf("That's odd, you have no id set? This message should never happen.\n"); return; } const char *np = crypt(w, ps->get("password")); if (!streq(np, ps->get("password"))) { p->printf("Bad password.\n"); int sofar = ps->get_int("attempts", 0); sofar++; ps->set("attempts", sofar); if (sofar==3) { log(PFL_HOSTS, 0, "net", "third bad password for %s, killing", ps->get("id")); p->printf("If you cannot remember your password, try emailing the administrators to get it reset.\n"); p->interpret("quit"); } else { log(PFL_HOSTS, 0, "net", "bad password for %s", ps->get("id")); } return; } if (Player *o=players->get(ps->get("id"))) { if (!o->p) { p->printf("Welcome back : you were linkdead.\n"); p->send_data(); o->p = p->p; p->p = 0; p->nuke_me = 1; o->oprintf(secret(o) && cansee, "%#M %[has/have] reconnected.\n", o); o->set("hostname", o->p->get_ip()); o->time_since = now; if (get_charset(o)==CHAR_UNICODE) { o->printf("\033%G\2"); } verb_buffer(o, 0, 0); log(o, PFL_SEEINFO, 0, "net", "reconnected."); return; } else { p->printf("Someone called that already here. Swapping you.\n"); p->send_data(); o->printf("Someone has entered the game as you.\n"); o->send_data(); delete o->p; o->p = p->p; p->p = 0; p->nuke_me = 1; o->set("hostname", o->p->get_ip()); if (get_charset(o)==CHAR_UNICODE) { o->printf("\033%G\2"); } verb_buffer(o, 0, 0); log(o, PFL_SEEINFO, 0, "net", "reconnected."); return; } } if (planet->get(ps->get("id"))) { return; } p->interpret("info motd"); if (ps != p->get_person_state()) p->pop_state(); p->printf("\n"); p->printf("^WTip of the day:^n %s^n\n", random_tip().c_str()); p->printf("\n"); if (ps->get_int("maxinvis", 0)!=0) { p->set_state("invis"); p->printf("Enter vis level (^W0-%i^n), '^Wi^n' for full invisibility.\n", ps->get_int("maxinvis")); ps->set("prompt", ssprintf("or press ^W[^CEnter^W]^n to keep vis level (Current : ^W%i^n): ", ps->get_int("invis"))); } else p->set_state("motd"); return; } static time_t dateof(const char *name) { struct stat buf; if (stat(name, &buf)==-1) return 0; return buf.st_mtime; } void alert_to_newstuff(Player *p) { time_t last = p->get_int("lastlogin"); struct stat buf; string f = ssprintf(DATA_MAIL "/%s.new", p->id); if (stat(f.c_str(), &buf)==0) { p->printf("^YYou have new mail!^n\a\n"); p->set_bflag(FL_HASMAIL, 1); unlink(f.c_str()); } MessageFile msg(DATA_NEWS, MUD_MAINBOARD); if (msg.count_new(p)) { p->printf("^GThe main board has new postings.^n\n"); } if (last != -1) { if (dateof(DATA_INFO "/news.i")>=last) { p->printf("Info news has been ^Rupdated^n.\n"); } if (privs_of(p)>=LEV_CAPTAIN && dateof(DATA_INFO "/wiznews.i")>=last) { p->printf("Info wiznews has been ^Rupdated^n.\n"); } if(BUGWATCH_NEWSTUFF) BUGWATCH_NEWSTUFF(p); } } void state_motd(Player *p, const char *w) { PersonState *ps = p->get_person_state(); if (streq(ps->get("state"), "invis") && *w) { if (tolower(*w)=='i') { ps->set("invis", ps->get_int("maxinvis")); } else { int i = atoi(w); if (i >= 0 && i <= ps->get_int("maxinvis")) { ps->set("invis", i); } } } if (!ps->get("id")) { p->printf("We've lost your id. Argh.\n"); return; } load_player(p, ps->get("id")); p->set_bflag(FL_LOGGEDIN, 0); p->unset("away"); if (get_charset(p)==CHAR_UNICODE) { p->printf("\033%G\2"); } if (!p->get("colours")) { p->set("colours", "classic"); } p->set("invis", ps->get_int("invis", 0)); if (MudObject *home=p->get_object("home")) { set_owner(p, home); p->unset("home"); } else { set_owner(p, MUD_DEFHOME); } p->set_bflag(FL_LOGGEDIN, 1); if (invis(p)) log(PFL_SEEINFO, invis(p), "net", "(level %i) logged into %s with invis %i", privs_of(p), p->owner ? p->owner->id : "???", invis(p)); else log(PFL_SEEINFO, 0, "net", "(level %i) logged into %s", privs_of(p), p->owner ? p->owner->id : "???"); p->set_bflag(FL_LOGGEDIN, 0); announce_entry(p); p->oprintf(secret(p) && cansee, "%s\n", build_setin(p, setqin).c_str()); p->lprintf(secret(p) && cansee, "%s\n", build_setin(p, setqin).c_str()); p->unset("mission"); p->interpret("calc_level"); p->set(KEY_STRENGTH, p->get_int("maxstrength")); p->set_bflag(FL_LOGGEDIN, 1); p->set_bflag(FL_SITTING, 0); p->set_bflag(FL_HANDSTIED, 0); p->set_bflag(FL_SLEEPING, 0); p->unset(KEY_SITON); p->unset(KEY_SITONN); p->interpret("look"); alert_to_newstuff(p); housekeeping(p); p->pop_state(); p->push_state("cmd"); } static bool verb_password(Player *who, int, const char **) { who->push_state("chpass1"); return true; } static void state_chpass1(Player *p, const char *w) { const char *pwd = p->get("password"); if (streq(crypt(w, p->get("password")), pwd)) { p->set_state("chpass2"); } else { p->printf("Wrong password.\n"); p->pop_state(); } } static void state_chpass2(Player *p, const char *w) { if (!w || !*w) { p->pop_state(); return; } if (strlen(w)<3) { p->printf("New password is too short. Pick a longer one.\n"); return; } char salt[3] = { rsalt(), rsalt(), 0 }; p->set("newpass", crypt(w, salt)); p->set_state("chpass3"); } static void state_chpass3(Player *p, const char *w) { if (streq(p->get("newpass"), crypt(w, p->get("newpass")))) { p->set("password", p->get("newpass")); p->pop_state(); } else { p->pop_state(); p->printf("Passwords didn't match. Try again.\n"); } } static bool verb_klock(Player *who, int, const char **) { who->printf("Connection locked, awaiting password.\n"); who->push_state("unlock"); return true; } static void state_unlock(Player *who, const char *what) { const char *pwd = who->get("password"); if (!pwd) { who->printf("OK.\n"); who->pop_state(); return ; } if (streq(crypt(what, who->get("password")), pwd)) { who->pop_state(); who->printf("OK.\n"); } else { who->printf("^WWrong.\n"); } } static bool verb_possess(Player *who, int argc, const char **argv) { MudObject *target = find_object(who, argv[1]); if (!target) { who->printf("Alias who?\n"); return true; } if (is_player(target)) { who->printf("You cannot possess players.\n"); return true; } who->snoop(target); log(PFL_SEEINFO, 0, "admin", "possesses %s (%s)", target->id, target->get("name")); PersonState *s = new PersonState("alias"); char prompt[256]; sprintf(prompt, "|%s^n|", target->get("name")); s->set("prompt", prompt); s->set("alias", target->id); who->push_state(s); return true; } static void state_alias(Player *p, const char *w) { const char *al = p->get_person_state()->get("alias"); MudObject *t = al?0:planet->get(al); if (streq(w, ".") || streq(w, "**")) { p->pop_state(); if (t) { p->desnoop(t); log(PFL_SEEINFO, 0, "admin", "unpossesses %s (%s)", t->id, t->get("name")); } return; } if (*w == '*') { p->interpret(w+1); return; } if (t) { t->interpret(w); } } const char *player_id(); extern int which_player; static bool verb_become(Player *p, int argc, const char **argv) { Player *np = new Player(player_id(), p->p); np->setf("short", "player%i", which_player-1); np->setf("name", "[Logging In %I]", which_player-1); np->set("desc", "This player has no description"); set_owner(np, mud); if (np->p->thingy_from==1) { np->set_bflag(FL_COLOUR, 1); } if (!argv[1]) { np->spewfile(DATA_INFO, "issue.i"); } else { state_setname(np, argv[1]); } planet->add(*np); p->p = 0; p->interpret("quit"); return true; } extern "C" void reboot_recover(Player *p, const char *w) { load_player(p, w); p->pop_state(); p->push_state("cmd"); MudObject *ho = p->get_object("home"); p->set("reboothome", ho?ho->id:0); p->set_bflag(FL_LOGGEDIN, 1); p->set(KEY_STRENGTH, p->get_int("maxstrength")); } void startup() { ADD_STATE("name", "By what name shall I call you? ", state_setname, false); // creating a new char ADD_STATE("confirm", "Did I hear correctly? ", state_confirm, false); ADD_STATE("askpwd", "Choose your password: ", state_askpass, true); ADD_STATE("conpwd", "Confirm your password: ", state_conpass, true); ADD_STATE("sex", "Sex (m/f/n/a): ", state_sex, false); ADD_STATE("newbiemotd","Press ^Benter^n to continue^n : ", state_newbiemotd, false); ADD_STATE("nation", "Nation: ", state_nation, false); // logging in ADD_STATE("password", "Password: ", state_password, true); ADD_STATE("motd", "Press ^Benter^n to continue^n : ", state_motd, false); ADD_STATE("invis", "What invisibility level? : ", state_motd, false); // changing password AUTO_VERB(password, 5, 0, PFL_NONE); ADD_STATE("chpass1", "Enter the ^ROLD^n password:", state_chpass1,true); ADD_STATE("chpass2", "Enter the ^RNEW ^npassword:", state_chpass2,true); ADD_STATE("chpass3", "Confirm the ^RNEW ^npassword:", state_chpass3,true); // keyboard locking AUTO_VERB(klock, 5, 0, PFL_NONE); ADD_STATE("unlock", "Password : ", state_unlock, true); // alias AUTO_VERB(possess, 5, 0, PFL_FORCE); ADD_STATE("alias", "Aliased...", state_alias, false); // become AUTO_VERB(become, 5, 0, PFL_BECOME); }