/** Check for CHARGES on thing and, if present, lower. * \param thing object being used. * \retval 0 charges was set to 0 * \retval 1 charges not set, or was > 0 */ int charge_action(dbref thing) { ATTR *b; char tbuf2[BUFFER_LEN]; int num; /* check if object has # of charges */ b = atr_get_noparent(thing, "CHARGES"); if (!b) { return 1; /* no CHARGES */ } else { strcpy(tbuf2, atr_value(b)); num = atoi(tbuf2); if (num > 0) { /* charges left, decrement and execute */ (void) atr_add(thing, "CHARGES", tprintf("%d", num - 1), Owner(b->creator), 0); return 1; } else { /* no charges left, try to execute runout */ return 0; } } }
/** Retrieve the amount of quote remaining to a player. * Figure out a player's quota. Add the RQUOTA attribute if he doesn't * have one already. This function returns the REMAINING quota, not * the TOTAL limit. * \param who player to check. * \return player's remaining quota. */ int get_current_quota(dbref who) { ATTR *a; int i; int limit; int owned = 0; /* if he's got an RQUOTA attribute, his remaining quota is that */ a = atr_get_noparent(Owner(who), "RQUOTA"); if (a) return parse_integer(atr_value(a)); /* else, count up his objects. If he has less than the START_QUOTA, * then his remaining quota is that minus his number of current objects. * Otherwise, it's his current number of objects. Add the attribute * if he doesn't have it. */ for (i = 0; i < db_top; i++) if (Owner(i) == Owner(who)) owned++; owned--; /* don't count the player himself */ if (owned <= START_QUOTA) limit = START_QUOTA - owned; else limit = owned; (void) atr_add(Owner(who), "RQUOTA", tprintf("%d", limit), GOD, 0); return limit; }
static dbref make_player(const char *name, const char *password, const char *host, const char *ip) { dbref player; char temp[SBUF_LEN]; char *flaglist, *flagname; char flagbuff[BUFFER_LEN]; player = new_object(); /* initialize everything */ set_name(player, name); Location(player) = PLAYER_START; Home(player) = PLAYER_START; Owner(player) = player; Parent(player) = NOTHING; Type(player) = TYPE_PLAYER; Flags(player) = new_flag_bitmask("FLAG"); strcpy(flagbuff, options.player_flags); flaglist = trim_space_sep(flagbuff, ' '); if (*flaglist != '\0') { while (flaglist) { flagname = split_token(&flaglist, ' '); twiddle_flag_internal("FLAG", player, flagname, 0); } } if (Suspect_Site(host, player) || Suspect_Site(ip, player)) set_flag_internal(player, "SUSPECT"); set_initial_warnings(player); /* Modtime tracks login failures */ ModTime(player) = (time_t) 0; (void) atr_add(player, pword_attr, password_hash(password, NULL), GOD, 0); giveto(player, START_BONUS); /* starting bonus */ (void) atr_add(player, "LAST", show_time(mudtime, 0), GOD, 0); (void) atr_add(player, "LASTSITE", host, GOD, 0); (void) atr_add(player, "LASTIP", ip, GOD, 0); (void) atr_add(player, "LASTFAILED", " ", GOD, 0); snprintf(temp, sizeof temp, "%d", START_QUOTA); (void) atr_add(player, "RQUOTA", temp, GOD, 0); (void) atr_add(player, "MAILCURF", "0", GOD, AF_LOCKED | AF_NOPROG | AF_WIZARD); add_folder_name(player, 0, "inbox"); /* link him to PLAYER_START */ PUSH(player, Contents(PLAYER_START)); add_player(player); add_lock(GOD, player, Basic_Lock, parse_boolexp(player, "=me", Basic_Lock), LF_DEFAULT); add_lock(GOD, player, Enter_Lock, parse_boolexp(player, "=me", Basic_Lock), LF_DEFAULT); add_lock(GOD, player, Use_Lock, parse_boolexp(player, "=me", Basic_Lock), LF_DEFAULT); current_state.players++; local_data_create(player); return player; }
/** Update the LASTFAILED attribute on a failed connection. * \param player dbref of player. * \param host host from which connection attempt failed. */ void check_lastfailed(dbref player, const char *host) { char last_place[BUFFER_LEN], *bp; bp = last_place; safe_format(last_place, &bp, T("%s on %s"), host, show_time(mudtime, 0)); *bp = '\0'; (void) atr_add(player, "LASTFAILED", last_place, GOD, 0); }
/** Processing related to players' last connections. * Here we check to see if a player gets a paycheck, tell them their * last connection site, and update all their LAST* attributes. * \param player dbref of player. * \param host hostname of player's current connection. * \param ip ip address of player's current connection. */ void check_last(dbref player, const char *host, const char *ip) { char *s; ATTR *a; ATTR *h; char last_time[MAX_COMMAND_LEN / 8]; char last_place[MAX_COMMAND_LEN]; /* compare to last connect see if player gets salary */ s = show_time(mudtime, 0); a = atr_get_noparent(player, "LAST"); if (a && (strncmp(atr_value(a), s, 10) != 0)) giveto(player, Paycheck(player)); /* tell the player where he last connected from */ if (!Guest(player)) { h = atr_get_noparent(player, "LASTSITE"); if (h && a) { strcpy(last_place, atr_value(h)); strcpy(last_time, atr_value(a)); notify_format(player, T("Last connect was from %s on %s."), last_place, last_time); } /* How about last failed connection */ h = atr_get_noparent(player, "LASTFAILED"); if (h && a) { strcpy(last_place, atr_value(h)); if (strlen(last_place) > 2) notify_format(player, T("Last FAILED connect was from %s."), last_place); } } /* if there is no Lastsite, then the player is newly created. * the extra variables are a kludge to work around some weird * behavior involving uncompress. */ /* set the new attributes */ (void) atr_add(player, "LAST", s, GOD, 0); (void) atr_add(player, "LASTSITE", host, GOD, 0); (void) atr_add(player, "LASTIP", ip, GOD, 0); (void) atr_add(player, "LASTFAILED", " ", GOD, 0); }
/* board a ship through the ship object */ void board_ship(dbref player, char *which, char *code) { ATTR *a; char *bcode; dbref obj, nav, bay; int security; /* find the shipobj */ obj = match_result(player, which, TYPE_THING, MAT_NEAR_THINGS); if (!IsShipObj(obj)) { notify(player, "Can't find that ship."); return; } /* see if it's got an HSNAV */ nav = atr_parse_dbref(obj, "HSNAV"); if (!IsShip(nav)) { notify(player, "That is not a valid ship object."); return; } /* check the boarding code, if necessary */ security = atr_parse_integer(nav, "SECURITY", 0); if (security) { a = atr_get(nav, "BOARDING_CODE"); if (a) { bcode = atr_value(a); if (strcmp(bcode, code)) { notify(player, "Invalid boarding code."); return; } } } /* check for a BAY on the nav, otherwise go to the nav's location */ bay = atr_parse_dbref(nav, "BAY"); if (!RealGoodObject(bay)) { bay = Location(nav); } atr_add(player, "HSPACE", unparse_dbref(nav), hs_options.space_wiz, 0); notify_except(obj, Location(player), player, tprintf("%s boards the %s.", Name(player), Name(obj)), 0); notify_except(obj, bay, player, tprintf("%s boards through the main hatch.", Name(player)), 0); moveto(player, bay, hs_options.space_wiz, NULL); }
/** Check a player's password against a given string. * * First checks new-style formatted password strings * If that doesn't match, tries old-style SHA0 password * strings, and upgrades the stored password. * If that doesn't match, tries really-old-style crypt(3) * password strings, and upgrades the stored password. * If that doesn't work, you lose. * * \param player dbref of player. * \param password plaintext password string to check. * \retval 1 password matches (or player has no password). * \retval 0 password fails to match. */ bool password_check(dbref player, const char *password) { ATTR *a; char *saved; /* read the password and compare it */ if (!(a = atr_get_noparent(player, pword_attr))) return 1; /* No password attribute */ saved = strdup(atr_value(a)); if (!saved) return 0; if (!password_comp(saved, password)) { /* Nope. Try SHA0. */ char *passwd = mush_crypt_sha0(password); if (strcmp(saved, passwd) != 0) { /* Not SHA0 either. Try old-school crypt(); */ #ifdef HAVE_CRYPT if (strcmp(crypt(password, "XX"), saved) != 0) { /* Nope */ #endif /* HAVE_CRYPT */ /* See if it's a MUX password */ if (!check_mux_password(saved, password)) { /* As long as it's not obviously encrypted, check for a * plaintext password. */ if (strlen(password) < 4 || *password == '$' || (password[0] == 'X' && password[1] == 'X') || strcmp(saved, password)) { free(saved); return 0; } } #ifdef HAVE_CRYPT } #endif } /* Something worked. Change password to SHS-encrypted */ do_rawlog(LT_CONN, "Updating password format for player #%d", player); (void) atr_add(player, pword_attr, password_hash(password, NULL), GOD, 0); } /* Success! */ free(saved); return 1; }
// Adds an attribute with a value to an object. void CHSInterface::AtrAdd(int obj, const char *atrname, char *val, int owner, int flags) { if (false == ValidObject(obj)) { hs_log(HSPrintf("AtrAdd() invalid object: %d, atr: %s, val: %s", obj, atrname, val)); return; } #ifdef PENNMUSH // No change in code between versions atr_add(obj, atrname, val, owner, flags); #endif #if defined(TM3) || defined(MUX) my_atr_put(obj, atrname, val); #endif }
/** Change a player's password. * \verbatim * This function implements @password. * \endverbatim * \param executor the executor. * \param enactor the enactor. * \param old player's current password. * \param newobj player's desired new password. * \param queue_entry the queue entry \@password is being executed in */ void do_password(dbref executor, dbref enactor, const char *old, const char *newobj, MQUE *queue_entry) { if (!queue_entry->port) { char old_eval[BUFFER_LEN]; char new_eval[BUFFER_LEN]; char const *sp; char *bp; sp = old; bp = old_eval; if (process_expression(old_eval, &bp, &sp, executor, executor, enactor, PE_DEFAULT, PT_DEFAULT, NULL)) return; *bp = '\0'; old = old_eval; sp = newobj; bp = new_eval; if (process_expression(new_eval, &bp, &sp, executor, executor, enactor, PE_DEFAULT, PT_DEFAULT, NULL)) return; *bp = '\0'; newobj = new_eval; } if (!password_check(executor, old)) { notify(executor, T("The old password that you entered was incorrect.")); } else if (!ok_password(newobj)) { notify(executor, T("Bad new password.")); } else { (void) atr_add(executor, pword_attr, password_hash(newobj, NULL), GOD, 0); notify(executor, T("You have changed your password.")); } }
/** Create an exit. * This function opens an exit and optionally links it. * \param player the enactor. * \param direction the name of the exit. * \param linkto the room to link to, as a string. * \param pseudo a phony location for player if a back exit is needed. This is bpass by do_open() as the source room of the back exit. * \return dbref of the new exit, or NOTHING. */ dbref do_real_open(dbref player, const char *direction, const char *linkto, dbref pseudo) { dbref loc = (pseudo != NOTHING) ? pseudo : (IsExit(player) ? Source(player) : (IsRoom(player) ? player : Location(player))); dbref new_exit; char *flaglist, *flagname; char flagbuff[BUFFER_LEN]; char *name = NULL; char *alias = NULL; if (!command_check_byname(player, "@dig")) { notify(player, T("Permission denied.")); return NOTHING; } if ((loc == NOTHING) || (!IsRoom(loc))) { notify(player, T("Sorry, you can only make exits out of rooms.")); return NOTHING; } if (Going(loc)) { notify(player, T("You can't make an exit in a place that's crumbling.")); return NOTHING; } if (!*direction) { notify(player, T("Open where?")); return NOTHING; } else if (ok_object_name ((char *) direction, player, NOTHING, TYPE_EXIT, &name, &alias) < 1) { notify(player, T("That's a strange name for an exit!")); if (name) mush_free(name, "name.newname"); if (alias) mush_free(alias, "name.newname"); return NOTHING; } if (!Open_Anywhere(player) && !controls(player, loc)) { notify(player, T("Permission denied.")); } else if (can_pay_fees(player, EXIT_COST)) { /* create the exit */ new_exit = new_object(); /* initialize everything */ set_name(new_exit, name); if (alias && *alias != ALIAS_DELIMITER) atr_add(new_exit, "ALIAS", alias, player, 0); Owner(new_exit) = Owner(player); Zone(new_exit) = Zone(player); Source(new_exit) = loc; Type(new_exit) = TYPE_EXIT; Flags(new_exit) = new_flag_bitmask("FLAG"); strcpy(flagbuff, options.exit_flags); flaglist = trim_space_sep(flagbuff, ' '); if (*flaglist != '\0') { while (flaglist) { flagname = split_token(&flaglist, ' '); twiddle_flag_internal("FLAG", new_exit, flagname, 0); } } mush_free(name, "name.newname"); if (alias) mush_free(alias, "name.newname"); /* link it in */ PUSH(new_exit, Exits(loc)); /* and we're done */ notify_format(player, T("Opened exit %s"), unparse_dbref(new_exit)); /* check second arg to see if we should do a link */ if (linkto && *linkto != '\0') { notify(player, T("Trying to link...")); if ((loc = check_var_link(linkto)) == NOTHING) loc = parse_linkable_room(player, linkto); if (loc != NOTHING) { if (!payfor(player, LINK_COST)) { notify_format(player, T("You don't have enough %s to link."), MONIES); } else { /* it's ok, link it */ Location(new_exit) = loc; notify_format(player, T("Linked exit #%d to #%d"), new_exit, loc); } } } current_state.exits++; local_data_create(new_exit); queue_event(player, "OBJECT`CREATE", "%s", unparse_objid(new_exit)); return new_exit; } if (name) mush_free(name, "name.newname"); if (alias) mush_free(alias, "name.newname"); return NOTHING; }
/* leave a flying ship through the escape pod */ void emergency_eject(dbref player) { dbref nav, pad; hship *ship; hcelestial *cel, *min_cel; ATTR *a; char *r, *s; char buff[512]; double dist, min_dist; dbref min_pad; hship *sptr, *min_ship; /* check for a BAY */ a = atr_get(Location(player), "BAY"); if (!a) { /* no BAY, see if we're next to the nav console */ ship = find_ship(player); if (ship) { if (Location(ship->objnum) != Location(player)) { notify(player, "You can't eject from here."); return; } } } else { /* there's a BAY, see if the ship is valid */ nav = parse_dbref(atr_value(a)); if (!IsShip(nav)) { notify(player, "You can't eject from here."); return; } ship = find_ship_by_nav(nav); } if (!ship) { notify(player, "You can't eject from here."); return; } /* only eject when flying, not when landing or docking */ if (!ship->uid || ship->landed || ship->docked) { notify(player, "You may only eject while flying."); return; } /* find a planet with a drop pad */ min_pad = NOTHING; min_dist = 1000000.0; min_cel = NULL; for (cel = ship->uid->head_celestial; cel; cel = cel->next) { if (!HasFlag(cel->type, HS_PLANET)) continue; pad = atr_parse_dbref(cel->objnum, "DROPPADS"); if (!RealGoodObject(pad)) continue; dist = ship_celestial_distance(ship, cel); if (dist < min_dist) { min_dist = dist; min_pad = pad; min_cel = cel; } } min_ship = NULL; for (sptr = ship->uid->head_ship; sptr; sptr = sptr->next) { if (min_cel) break; if (!HasFlag(sptr->type, HS_STATION | HS_CAPITAL)) continue; pad = atr_parse_dbref(sptr->objnum, "BAY"); if (!RealGoodObject(pad)) continue; dist = ship_distance(ship, sptr); if (dist < min_dist) { min_cel = NULL; min_ship = sptr; min_dist = dist; min_pad = pad; } } if (!RealGoodObject(min_pad)) { notify(player, "There is nowhere to eject to!"); return; } /* finish up by setting HSPACE and notifying everybody of the move */ if (min_ship) { atr_add(player, "HSPACE", unparse_dbref(min_ship->objnum), hs_options.space_wiz, 0); } else if (min_cel) { atr_add(player, "HSPACE", unparse_dbref(min_cel->objnum), hs_options.space_wiz, 0); } else { SPACEWALL("Weird problem in eject."); notify(player, "Bad space object. Contact an administrator."); return; } notify_except(Location(player), Location(player), player, tprintf("%s ejects in an emergency escape pod!", Name(player)), 0); notify_except(min_pad, min_pad, player, tprintf("%s crash lands in an emergency escape pod!", Name(player)), 0); if (min_ship) { notify_consoles(min_ship, tprintf("%s%s-%s Emergency ejection pod automatically tractored into the docking back.", ANSI_HILITE, ANSI_GREEN, ANSI_NORMAL)); } moveto(player, min_pad, hs_options.space_wiz, NULL); }
/* leave a landed/docked ship through the hatch */ void disembark(dbref player) { dbref nav, obj, newobj; hship *ship; ATTR *a; int security; /* check if we can disembark from here */ a = atr_get(Location(player), "BAY"); if (!a) { /* no BAY, check if we're near the nav console */ ship = find_ship(player); if (ship) { if (Location(ship->objnum) != Location(player)) { notify(player, "You can't disembark from here."); return; } } } else { /* there's a BAY here, make sure it's a good one */ nav = parse_dbref(atr_value(a)); if (!IsShip(nav)) { notify(player, "You can't disembark from here."); return; } ship = find_ship_by_nav(nav); } if (!ship) { notify(player, "You can't disembark from here."); return; } /* no ditching in space, or early after launching, or prematurely when landing */ if ((ship->uid || ship->landing || ship->launching) && !ship->linked) { notify(player, "You can't disembark while in space."); return; } obj = atr_parse_dbref(ship->objnum, "SHIPOBJ"); if (!RealGoodObject(obj)) { notify(player, "This ship can not be disembarked."); return; } /* check whether we're docking or landing, save the new space object */ if (ship->landed) { newobj = ship->landed->objnum; } else if (ship->docked) { newobj = ship->docked->objnum; } else if (ship->linked) { newobj = ship->linked->objnum; } else { notify(player, "You can't disembark while in space."); return; } if (ship->linked) { /* check the boarding code, if necessary */ security = atr_parse_integer(ship->linked->objnum, "SECURITY", 0); if (security) { notify(player, "Unable to use boarding link while the other ship has security enabled."); return; } security = atr_parse_integer(ship->objnum, "SECURITY", 0); if (security) { notify(player, "Unable to use boarding link while security is enabled."); return; } obj = atr_parse_dbref(ship->linked->objnum, "BAY"); if (!RealGoodObject(obj)) obj = Location(ship->linked->objnum); moveto(player, obj, hs_options.space_wiz, NULL); } else { moveto(player, Location(obj), hs_options.space_wiz, NULL); } /* finish up by setting HSPACE attribute and notifying everybody about the move */ atr_add(player, "HSPACE", unparse_dbref(newobj), hs_options.space_wiz, 0); notify_except(obj, Location(player), player, tprintf("%s disembarks through the main hatch.", Name(player)), 0); notify_except(obj, Location(obj), player, tprintf("%s disembarks from the %s.", Name(player), Name(obj)), 0); return; }
/* man console command */ void man_console(dbref player, char *where) { hship *ship; hconsole *con; dbref console; dbref manner, manning; hweapon *pri, *sec; char *prompt; int ptype, stype, type; if (Typeof(player) != TYPE_PLAYER && hs_options.forbid_puppets) { notify(player, "Only players may man this console."); return; } console = match_result(player, where, TYPE_THING, MAT_NEAR_THINGS); switch (console) { case NOTHING: notify(player, "I don't see that here."); return; case AMBIGUOUS: notify(player, "I don't know which you mean!"); return; } /* don't need to do this now because we want to allow you to reman consoles easily for equipment changes */ /*manner = get_user(console); if (manner == player) { notify(player, "You are already manning that console."); return; }*/ if (!IsConsole(console) && !IsShip(console)) { notify(player, "You cannot man that."); return; } if (IsConsole(console)) { con = find_console(console); if (!con) { notify(player, "That console has not been activated."); return; } pri = &(con->primary); sec = &(con->secondary); prompt = &(con->prompt); ship = find_ship(console); } else if (IsShip(console)) { con = NULL; ship = find_ship_by_nav(console); if (ship) { pri = &(ship->primary); sec = &(ship->secondary); prompt = &(ship->prompt); } } else{ ship = NULL; } if (!ship) { notify(player, "Invalid ship object. Please contact your flight mechanic immediately!"); return; } if (InCombat(ship)) { notify(player, "Can't reload weapons in combat!"); stype = sec->type; ptype = pri->type; } else { ptype = load_weapon(player, pri, HS_PRIMARY); stype = load_weapon(player, sec, HS_SECONDARY); } type = HasFlag(ptype | stype, HS_ANY_WEAPON); if (con) { /* this is an auxiliary console */ /* no missiles on the aux consoles */ if (HasFlag(type, HS_MISSILE)) { clear_weapon(pri); clear_weapon(sec); notify(player, "You may not man an auxiliary console with missiles equipped."); return; } if (HasFlag(type, HS_WIRETAP)) con->type = HS_OPS; else if (HasFlag(type, HS_CAPACITOR)) con->type = HS_ENG; else if (HasFlag(type, HS_CANNON | HS_EMITTER | HS_WEAPON)) con->type = HS_GUN; else con->type = HS_CIV; } else { /* this is a nav console */ /* no cans, caps or taps */ if (HasFlag(type, HS_CANNON)) { clear_weapon(pri); clear_weapon(sec); notify(player, "You may not man a navigation console with a cannon equipped."); return; } } /* the equipment slots check out, and the console has been set to the appropriate type, if needed */ if (prompt) { *prompt = HasFlag(atr_parse_integer(player, "HSPROMPT_FREQUENCY", 1), HS_PROMPT_FREQUENCY); FlagOn(*prompt, atr_parse_flags(player, hs_prompt_flags, "HSPROMPT_FLAGS")); } manning = get_console(player); if (manning != NOTHING) { manner = get_user(manning); if (manner == player) { con = find_console(manning); if (con) { load_weapon(manning, &(con->primary), HS_PRIMARY); load_weapon(manning, &(con->secondary), HS_SECONDARY); } set_user(manning, NOTHING); notify(player, tprintf("You unman the %s.", Name(manning))); notify_except(manning, Location(manning), player, tprintf("%s unmans the %s.", Name(player), Name(manning)), 0); } } /* set the HSPACE attribute in case something was fishy */ if (!IsSim(ship->objnum)) { atr_add(player, "HSPACE", unparse_dbref(ship->objnum), hs_options.space_wiz, 0); } set_user(console, player); execute_trigger(console, "AMAN", ship); if (con) { notify(player, tprintf("You man the %s (%s%s%s).", Name(console), ANSI_HILITE, STR(hs_console_types, con->type), ANSI_NORMAL)); notify_except(console, Location(console), player, tprintf("%s mans the %s (%s%s%s).", Name(player), Name(console), ANSI_HILITE, STR(hs_console_types, con->type), ANSI_NORMAL), 0); } else { notify(player, tprintf("You man the %s.", Name(console))); notify_except(console, Location(console), player, tprintf("%s mans the %s.", Name(player), Name(console)), 0); } }
/** Add or subtract from a player's quota. * \param who object whose owner has the quota changed. * \param payment amount to add to quota (may be negative). */ void change_quota(dbref who, int payment) { (void) atr_add(Owner(who), "RQUOTA", tprintf("%d", get_current_quota(who) + payment), GOD, 0); }
/** Clone an object. * \verbatim * This is the top-level function for @clone, which creates a duplicate * of a (non-player) object. * \endverbatim * \param player the enactor. * \param name the name of the object to clone. * \param newname the name to give the duplicate. * \param preserve if 1, preserve ownership and privileges on duplicate. * \param newdbref the (unparsed) dbref to give the object, or NULL to use the next free * \param pe_info The pe_info to use for lock and \@command priv checks * \return dbref of the duplicate, or NOTHING. */ dbref do_clone(dbref player, char *name, char *newname, int preserve, char *newdbref, NEW_PE_INFO *pe_info) { dbref clone, thing; char dbnum[BUFFER_LEN]; thing = noisy_match_result(player, name, NOTYPE, MAT_EVERYTHING); if (thing == NOTHING) return NOTHING; if (newname && *newname && !ok_name(newname, IsExit(thing))) { notify(player, T("That is not a reasonable name.")); return NOTHING; } if (!controls(player, thing) || IsPlayer(thing) || (IsRoom(thing) && !command_check_byname(player, "@dig", pe_info)) || (IsExit(thing) && !command_check_byname(player, "@open", pe_info)) || (IsThing(thing) && !command_check_byname(player, "@create", pe_info))) { notify(player, T("Permission denied.")); return NOTHING; } /* don't allow cloning of destructed things */ if (IsGarbage(thing)) { notify(player, T("There's nothing left of it to clone!")); return NOTHING; } if (preserve && !Wizard(player)) { notify(player, T("You cannot @CLONE/PRESERVE. Use normal @CLONE instead.")); return NOTHING; } if (!make_first_free_wrapper(player, newdbref)) { return NOTHING; } /* make sure owner can afford it */ switch (Typeof(thing)) { case TYPE_THING: if (can_pay_fees(player, Pennies(thing))) { clone = clone_object(player, thing, newname, preserve); notify_format(player, T("Cloned: Object %s."), unparse_dbref(clone)); if (IsRoom(player)) moveto(clone, player, player, "cloned"); else moveto(clone, Location(player), player, "cloned"); current_state.things++; local_data_clone(clone, thing, preserve); real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING, NULL, 0, 0); return clone; } return NOTHING; break; case TYPE_ROOM: if (can_pay_fees(player, ROOM_COST)) { clone = clone_object(player, thing, newname, preserve); Exits(clone) = NOTHING; notify_format(player, T("Cloned: Room #%d."), clone); current_state.rooms++; local_data_clone(clone, thing, preserve); real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING, NULL, 0, 0); return clone; } return NOTHING; break; case TYPE_EXIT: /* For exits, we don't want people to be able to link it to a location they can't with @open. So, all this stuff. */ switch (Location(thing)) { case NOTHING: strcpy(dbnum, "#-1"); break; case HOME: strcpy(dbnum, "home"); break; case AMBIGUOUS: strcpy(dbnum, "variable"); break; default: strcpy(dbnum, unparse_dbref(Location(thing))); } if (newname && *newname) clone = do_real_open(player, newname, dbnum, NOTHING, pe_info); else clone = do_real_open(player, Name(thing), dbnum, NOTHING, pe_info); if (!GoodObject(clone)) { return NOTHING; } else { char *alias_val = NULL; ATTR *alias_attr = NULL; alias_attr = atr_get_noparent(clone, "ALIAS"); if (alias_attr) { alias_val = safe_atr_value(alias_attr, "atrval.do_clone"); atr_clr(clone, "ALIAS", GOD); } atr_cpy(clone, thing); if (alias_val) { atr_add(clone, "ALIAS", alias_val, player, 0); mush_free(alias_val, "atrval.do_clone"); } clone_locks(player, thing, clone); Zone(clone) = Zone(thing); Parent(clone) = Parent(thing); Flags(clone) = clone_flag_bitmask("FLAG", Flags(thing)); if (!preserve) { clear_flag_internal(clone, "WIZARD"); clear_flag_internal(clone, "ROYALTY"); Warnings(clone) = 0; /* zap warnings */ Powers(clone) = new_flag_bitmask("POWER"); /* zap powers */ } else { Warnings(clone) = Warnings(thing); Powers(clone) = clone_flag_bitmask("POWER", Powers(thing)); } if (Wizard(clone) || Royalty(clone) || Warnings(clone) || !null_flagmask("POWER", Powers(clone))) notify(player, T ("Warning: @CLONE/PRESERVE on an object with WIZ, ROY, @powers, or @warnings.")); notify_format(player, T("Cloned: Exit #%d."), clone); local_data_clone(clone, thing, preserve); return clone; } } return NOTHING; }
/** Attempt to register a new player at the connect screen. * If registration is allowed, a new player object is created with * a random password which is emailed to the registering player. * \param name name of player to register. * \param email email address to send registration details. * \param host host from which registration is being attempted. * \param ip ip address from which registration is being attempted. * \return dbref of created player or NOTHING if creation failed. */ dbref email_register_player(DESC *d, const char *name, const char *email, const char *host, const char *ip) { char *p; char passwd[20]; static char elems[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; int i, len; bool resend = 0; dbref player = NOTHING; FILE *fp; size_t NELEMS = sizeof(elems) - 1; char sbuff[260]; if (!check_fails(ip)) { return NOTHING; } if (strlen(options.sendmail_prog) == 0) return NOTHING; if (!ok_player_name(name, NOTHING, NOTHING)) { /* Check for re-registration request */ player = lookup_player(name); if (GoodObject(player)) { ATTR *a; a = atr_get(player, "LASTLOGOUT"); if (!a) { a = atr_get(player, "REGISTERED_EMAIL"); if (a && !strcasecmp(atr_value(a), email)) resend = 1; } } if (!resend) { do_log(LT_CONN, 0, 0, "Failed registration (bad name) from %s", host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, mark_failed(ip), "register: bad name", name); return NOTHING; } } if (!resend) { /* Make sure that the email address is kind of valid. A valid * address must contain a @. Let the mailer sort it out beyond * that. Also, to prevent someone from using the MUSH to mailbomb * another site, let's make sure that the site to which the user * wants the email sent is also allowed to use the register * command. If there's an @, we check whatever's after the last @ * (since @foo.bar:user@host is a valid email). */ if ((p = strrchr(email, '@'))) { p++; if (!Site_Can_Register(p)) { if (!Deny_Silent_Site(p, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "Failed registration (bad site in email: %s) from %s", email, host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, mark_failed(ip), "register: bad site in email", name); } return NOTHING; } } else { if (!Deny_Silent_Site(host, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "Failed registration (bad email: %s) from %s", email, host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, mark_failed(ip), "register: sitelocked host", name); } return NOTHING; } if (DBTOP_MAX && (db_top >= DBTOP_MAX + 1) && (first_free == NOTHING)) { /* Oops, out of db space! */ do_log(LT_CONN, 0, 0, "Failed registration (no db space) from %s", host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, count_failed(ip), "register: no db space left to create!", name); return NOTHING; } } /* Come up with a random password of length 7-12 chars */ len = get_random_u32(7, 12); for (i = 0; i < len; i++) passwd[i] = elems[get_random_u32(0, NELEMS - 1)]; passwd[len] = '\0'; /* If we've made it here, we can send the email and create the * character. Email first, since that's more likely to go bad. * Some security precautions we'll take: * 1) We'll use sendmail -t, so we don't pass user-given values to a shell. * 2) We'll cross our fingers and hope nobody uses this to spam. */ release_fd(); snprintf(sbuff, sizeof sbuff, "%s -t", options.sendmail_prog); if ((fp = popen(sbuff, "w")) == NULL) { do_log(LT_CONN, 0, 0, "Failed registration of %s by %s: unable to open sendmail", name, email); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s,%d", d->descriptor, ip, count_failed(ip), "register: Unable to open sendmail!", name, 1); reserve_fd(); return NOTHING; } fprintf(fp, "Subject: "); fprintf(fp, T("[%s] Registration of %s\n"), MUDNAME, name); fprintf(fp, "To: %s\n", email); fprintf(fp, "Precedence: junk\n"); fprintf(fp, "\n"); fprintf(fp, T("This is an automated message.\n")); fprintf(fp, "\n"); fprintf(fp, T("Your requested player, %s, has been created.\n"), name); fprintf(fp, T("The password is %s\n"), passwd); fprintf(fp, "\n"); fprintf(fp, T("To access this character, connect to %s and type:\n"), MUDNAME); fprintf(fp, "\tconnect \"%s\" %s\n", name, passwd); fprintf(fp, "\n"); i = pclose(fp); reserve_fd(); if (i != 0) { /* Mailer exited with an error code. Log it. */ do_rawlog( LT_CONN, "When attempting to email a password to a newly registered player,\n" "\tthe mailer exited with error code %d.\n" "\t(Check /usr/include/sysexits.h if present for the meaning.)", i); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s,%d", d->descriptor, ip, count_failed(ip), "register: Unable to send email", name, i); return NOTHING; } else if (resend) { /* Reset the password */ (void) atr_add(player, pword_attr, password_hash(passwd, NULL), GOD, 0); return player; } else { /* Ok, all's well, make a player */ player = make_player(name, passwd, host, ip); queue_event(SYSEVENT, "PLAYER`CREATE", "%s,%s,%s,%d,%s", unparse_objid(player), name, "register", d->descriptor, email); (void) atr_add(player, "REGISTERED_EMAIL", email, GOD, 0); return player; } }