/** * \file wiz.c * * \brief Wizard commands in PennMUSH. * * */ #include "copyrite.h" #include "config.h" #ifdef I_UNISTD #include <unistd.h> #endif #include <string.h> #include <math.h> #ifdef I_SYS_TIME #include <sys/time.h> #else #include <time.h> #endif #include <stdlib.h> #include <ctype.h> #include <signal.h> #ifdef I_FCNTL #include <fcntl.h> #endif #ifdef WIN32 #include <windows.h> #include "process.h" #endif #include "conf.h" #include "externs.h" #include "mushdb.h" #include "attrib.h" #include "match.h" #include "access.h" #include "parse.h" #include "mymalloc.h" #include "flags.h" #include "lock.h" #include "log.h" #include "game.h" #include "command.h" #include "dbdefs.h" #include "extmail.h" #include "confmagic.h" extern dbref find_entrance(dbref door); struct db_stat_info *get_stats(dbref owner); extern dbref find_player_by_desc(int port); #ifndef WIN32 #ifdef I_SYS_FILE #include <sys/file.h> #endif #endif static int tport_dest_ok(dbref player, dbref victim, dbref dest); static int tport_control_ok(dbref player, dbref victim, dbref loc); static int mem_usage(dbref thing); static int raw_search(dbref player, const char *owner, const char *class, const char *restriction, const char *start, const char *stop, dbref **result, PE_Info * pe_info); #ifdef INFO_SLAVE void kill_info_slave(void); #endif extern char confname[BUFFER_LEN]; extern char errlog[BUFFER_LEN]; /** Create a player by Wizard fiat. * \verbatim * This implements @pcreate. * \endverbatim * \param creator the enactor. * \param player_name name of player to create. * \param player_password password for player. * \return dbref of created player object, or NOTHING if failure. */ dbref do_pcreate(dbref creator, const char *player_name, const char *player_password) { dbref player; if (!Create_Player(creator)) { notify(creator, T("You do not have the power over body and mind!")); return NOTHING; } if (!can_pay_fees(creator, 0)) return NOTHING; player = create_player(player_name, player_password, "None", "None"); if (player == NOTHING) { notify_format(creator, T("Failure creating '%s' (bad name)"), player_name); return NOTHING; } if (player == AMBIGUOUS) { notify_format(creator, T("Failure creating '%s' (bad password)"), player_name); return NOTHING; } notify_format(creator, T("New player '%s' (#%d) created with password '%s'"), player_name, player, player_password); do_log(LT_WIZ, creator, player, T("Player creation")); return player; } /** Set or check a player's quota. * \verbatim * This implements @quota and @squota. * \endverbatim * \param player the enactor. * \param arg1 name of player whose quota should be set or checked. * \param arg2 amount to set or adjust quota, ignored if checking. * \param set_q if 1, set quota; if 0, check quota. */ void do_quota(dbref player, const char *arg1, const char *arg2, int set_q) { dbref who, thing; int owned, limit, adjust; /* determine the victim */ if (!arg1 || !*arg1 || !strcmp(arg1, "me")) who = player; else { who = lookup_player(arg1); if (who == NOTHING) { notify(player, T("No such player.")); return; } } /* check permissions */ if (!Wizard(player) && set_q) { notify(player, T("Only wizards may change a quota.")); return; } if (!Do_Quotas(player) && !See_All(player) && (player != who)) { notify(player, T("You can't look at someone else's quota.")); return; } /* count up all owned objects */ owned = -1; /* a player is never included in his own * quota */ for (thing = 0; thing < db_top; thing++) { if (Owner(thing) == who) if (!IsGarbage(thing)) ++owned; } /* the quotas of priv'ed players are unlimited and cannot be set. */ if (NoQuota(who) || !USE_QUOTA) { notify_format(player, T("Objects: %d Limit: UNLIMITED"), owned); return; } /* if we're not doing a change, determine the mortal's quota limit. * RQUOTA is the objects _left_, not the quota itself. */ if (!set_q) { limit = get_current_quota(who); notify_format(player, T("Objects: %d Limit: %d"), owned, owned + limit); return; } /* set a new quota */ if (!arg2 || !*arg2) { limit = get_current_quota(who); notify_format(player, T("Objects: %d Limit: %d"), owned, owned + limit); notify(player, T("What do you want to set the quota to?")); return; } adjust = ((*arg2 == '+') || (*arg2 == '-')); if (adjust) limit = owned + get_current_quota(who) + atoi(arg2); else limit = atoi(arg2); if (limit < owned) /* always have enough quota for your objects */ limit = owned; (void) atr_add(Owner(who), "RQUOTA", tprintf("%d", limit - owned), GOD, NOTHING); notify_format(player, T("Objects: %d Limit: %d"), owned, limit); } /** Check or set quota globally. * \verbatim * This implements @allquota. * \endverbatim * \param player the enactor. * \param arg1 new quota limit, as a string. * \param quiet if 1, don't display every player's quota. */ void do_allquota(dbref player, const char *arg1, int quiet) { int oldlimit, limit, owned; dbref who, thing; if (!God(player)) { notify(player, T("Who do you think you are, GOD?")); return; } if (!arg1 || !*arg1) { limit = -1; } else if (!is_integer(arg1)) { notify(player, T("You can only set quotas to a number.")); return; } else { limit = parse_integer(arg1); if (limit < 0) { notify(player, T("You can only set quotas to a positive number.")); return; } } for (who = 0; who < db_top; who++) { if (!IsPlayer(who)) continue; /* count up all owned objects */ owned = -1; /* a player is never included in his own * quota */ for (thing = 0; thing < db_top; thing++) { if (Owner(thing) == who) if (!IsGarbage(thing)) ++owned; } if (NoQuota(who)) { if (!quiet) notify_format(player, "%s: Objects: %d Limit: UNLIMITED", Name(who), owned); continue; } if (!quiet) { oldlimit = get_current_quota(who); notify_format(player, "%s: Objects: %d Limit: %d", Name(who), owned, oldlimit); } if (limit != -1) { if (limit <= owned) (void) atr_add(who, "RQUOTA", "0", GOD, NOTHING); else (void) atr_add(who, "RQUOTA", tprintf("%d", limit - owned), GOD, NOTHING); } } if (limit == -1) notify(player, T("Quotas not changed.")); else notify_format(player, T("All quotas changed to %d."), limit); } static int tport_dest_ok(dbref player, dbref victim, dbref dest) { /* can player legitimately send something to dest */ if (Tel_Anywhere(player)) return 1; if (controls(player, dest)) return 1; /* beyond this point, if you don't control it and it's not a room, no hope */ if (!IsRoom(dest)) return 0; /* Check for a teleport lock. It fails if the player is not wiz or * royalty, and the room is tport-locked against the victim, and the * victim does not control the room. */ if (!eval_lock(victim, dest, Tport_Lock)) return 0; if (JumpOk(dest)) return 1; return 0; } static int tport_control_ok(dbref player, dbref victim, dbref loc) { /* can player legitimately move victim from loc */ if (God(victim) && !God(player)) return 0; if (Tel_Anything(player)) return 1; if (controls(player, victim)) return 1; /* mortals can't @tel HEAVY players just on basis of location ownership */ if (controls(player, loc) && (!Heavy(victim) || Owns(player, victim))) return 1; return 0; } /** Teleport something somewhere. * \verbatim * This implements @tel. * \endverbatim * \param player the enactor. * \param arg1 the object to teleport (or location if no object given) * \param arg2 the location to teleport to. * \param silent if 1, don't trigger teleport messagse. * \param inside if 1, always @tel to inventory, even of a player */ void do_teleport(dbref player, const char *arg1, const char *arg2, int silent, int inside) { dbref victim; dbref destination; dbref loc; const char *to; dbref absroom; /* "absolute room", for NO_TEL check */ /* get victim, destination */ if (*arg2 == '\0') { victim = player; to = arg1; } else { if ((victim = noisy_match_result(player, arg1, NOTYPE, MAT_OBJECTS | MAT_ENGLISH)) == NOTHING) { return; } to = arg2; } if (IsRoom(victim)) { notify(player, T("You can't teleport rooms.")); return; } if (IsGarbage(victim)) { notify(player, T("Garbage belongs in the garbage dump.")); return; } /* get destination */ if (!strcasecmp(to, "home")) { /* If the object is @tel'ing itself home, treat it the way we'd * treat a 'home' command */ if (player == victim) { if (Fixed(victim)) notify(player, T("You can't do that IC!")); else safe_tel(victim, HOME, silent); return; } else destination = Home(victim); } else { destination = match_result(player, to, TYPE_PLAYER, MAT_EVERYTHING); } switch (destination) { case NOTHING: notify(player, T("No match.")); break; case AMBIGUOUS: notify(player, T("I don't know which destination you mean!")); break; case HOME: destination = Home(victim); /* FALL THROUGH */ default: /* check victim, destination types, teleport if ok */ if (!GoodObject(destination) || IsGarbage(destination)) { notify(player, T("Bad destination.")); return; } if (recursive_member(destination, victim, 0) || (victim == destination)) { notify(player, T("Bad destination.")); return; } if (!Tel_Anywhere(player) && IsPlayer(victim) && IsPlayer(destination)) { notify(player, T("Bad destination.")); return; } if (IsExit(victim)) { /* Teleporting an exit means moving its source */ if (!IsRoom(destination)) { notify(player, T("Exits can only be teleported to other rooms.")); return; } if (Going(destination)) { notify(player, T("You can't move an exit to someplace that's crumbling.")); return; } if (!GoodObject(Home(victim))) loc = find_entrance(victim); else loc = Home(victim); /* Unlike normal teleport, you must control the destination * or have the open_anywhere power */ if (!(tport_control_ok(player, victim, loc) && (controls(player, destination) || Open_Anywhere(player)))) { notify(player, T("Permission denied.")); return; } /* Remove it from its old room */ Exits(loc) = remove_first(Exits(loc), victim); /* Put it into its new room */ Source(victim) = destination; PUSH(victim, Exits(destination)); if (!Quiet(player) && !(Quiet(victim) && (Owner(victim) == player))) notify(player, T("Teleported.")); return; } loc = Location(victim); /* if royal or wiz and destination is player, tel to location unless * using @tel/inside */ if (IsPlayer(destination) && Tel_Anywhere(player) && IsPlayer(victim) && !inside) { if (!silent && loc != Location(destination)) did_it(victim, victim, NULL, NULL, "OXTPORT", NULL, NULL, loc); safe_tel(victim, Location(destination), silent); if (!silent && loc != Location(destination)) did_it(victim, victim, "TPORT", NULL, "OTPORT", NULL, "ATPORT", Location(destination)); return; } /* check needed for NOTHING. Especially important for unlinked exits */ if ((absroom = Location(victim)) == NOTHING) { notify(victim, T("You're in the Void. This is not a good thing.")); /* At this point, they're in a bad location, so let's check * if home is valid before sending them there. */ if (!GoodObject(Home(victim))) Home(victim) = PLAYER_START; do_move(victim, "home", MOVE_NORMAL); return; } else { /* valid location, perform other checks */ /* if player is inside himself, send him home */ if (absroom == victim) { notify(player, T("What are you doing inside of yourself?")); if (Home(victim) == absroom) Home(victim) = PLAYER_START; do_move(victim, "home", MOVE_NORMAL); return; } /* find the "absolute" room */ absroom = absolute_room(victim); if (absroom == NOTHING) { notify(victim, T("You're in the void - sending you home.")); if (Home(victim) == Location(victim)) Home(victim) = PLAYER_START; do_move(victim, "home", MOVE_NORMAL); return; } /* if there are a lot of containers, send him home */ if (absroom == AMBIGUOUS) { notify(victim, T("You're in too many containers.")); if (Home(victim) == Location(victim)) Home(victim) = PLAYER_START; do_move(victim, "home", MOVE_NORMAL); return; } /* note that we check the NO_TEL status of the victim rather * than the player that issued the command. This prevents someone * in a NO_TEL room from having one of his objects @tel him out. * The control check, however, is detemined by command-giving * player. */ /* now check to see if the absolute room is set NO_TEL */ if (NoTel(absroom) && !controls(player, absroom) && !Tel_Anywhere(player)) { notify(player, T("Teleports are not allowed in this room.")); return; } /* Check leave lock on room, if necessary */ if (!controls(player, absroom) && !Tel_Anywhere(player) && !eval_lock(player, absroom, Leave_Lock)) { fail_lock(player, absroom, Leave_Lock, T("Teleports are not allowed in this room."), NOTHING); return; } /* Now check the Z_TEL status of the victim's room. * Just like NO_TEL above, except that if the room (or its * Zone Master Room, if any, is Z_TEL, * the destination must also be a room in the same zone */ if (GoodObject(Zone(absroom)) && (ZTel(absroom) || ZTel(Zone(absroom))) && !controls(player, absroom) && !Tel_Anywhere(player) && (Zone(absroom) != Zone(destination))) { notify(player, T("You may not teleport out of the zone from this room.")); return; } } if (!IsExit(destination)) { if (tport_control_ok(player, victim, Location(victim)) && tport_dest_ok(player, victim, destination) && (Tel_Anything(player) || (Tel_Anywhere(player) && (player == victim)) || (destination == Owner(victim)) || (!Fixed(Owner(victim)) && !Fixed(player)))) { if (!silent && loc != destination) did_it(victim, victim, NULL, NULL, "OXTPORT", NULL, NULL, loc); safe_tel(victim, destination, silent); if (!silent && loc != destination) did_it(victim, victim, "TPORT", NULL, "OTPORT", NULL, "ATPORT", destination); if ((victim != player) && !(Puppet(victim) && (Owner(victim) == Owner(player)))) { if (!Quiet(player) && !(Quiet(victim) && (Owner(victim) == player))) notify(player, T("Teleported.")); } return; } /* we can't do it */ fail_lock(player, destination, Enter_Lock, T("Permission denied."), Location(player)); return; } else { /* attempted teleport to an exit */ if (tport_control_ok(player, victim, Location(victim)) && (Tel_Anything(player) || (Tel_Anywhere(player) && (player == victim)) || (!Fixed(Owner(victim)) && !Fixed(player)))) { do_move(victim, to, MOVE_NORMAL); } else { notify(player, T("Permission denied.")); if (victim != player) notify_format(victim, T("%s tries to impose his will on you and fails."), Name(player)); } } } } /** Force an object to run a command. * \verbatim * This implements @force. * \endverbatim * \param player the enactor. * \param what name of the object to force. * \param command command to force the object to run. */ void do_force(dbref player, const char *what, char *command) { dbref victim; int j; if ((victim = match_controlled(player, what)) == NOTHING) { notify(player, "Sorry."); return; } if (options.log_forces) { if (Wizard(player)) { /* Log forces by wizards */ if (Owner(victim) != Owner(player)) do_log(LT_WIZ, player, victim, "** FORCE: %s", command); else do_log(LT_WIZ, player, victim, "FORCE: %s", command); } else if (Wizard(Owner(victim))) { /* Log forces of wizards */ do_log(LT_WIZ, player, victim, "** FORCE WIZ-OWNED: %s", command); } } if (God(victim) && !God(player)) { notify(player, T("You can't force God!")); return; } /* force victim to do command */ for (j = 0; j < 10; j++) global_eval_context.wnxt[j] = global_eval_context.wenv[j]; for (j = 0; j < NUMQ; j++) global_eval_context.rnxt[j] = global_eval_context.renv[j]; parse_que(victim, command, player); } /** Parse a force token command, but don't force with it. * \verbatim * This function hacks up something of the form "#<dbref> <action>", * finding the two args, and returns 1 if it's a sensible force, * otherwise 0. We know only that the command starts with #. * \endverbatim * \param command the command to parse. * \retval 1 sensible force command * \retval 0 command failed (no action given, etc.) */ int parse_force(char *command) { char *s; s = command + 1; while (*s && !isspace((unsigned char) *s)) { if (!isdigit((unsigned char) *s)) return 0; /* #1a is no good */ s++; } if (!*s) return 0; /* dbref with no action is no good */ *s = '='; /* Replace the first space with = so we have #3= <action> */ return 1; } extern struct db_stat_info current_state; /** Count up the number of objects of each type owned. * \param owner player to count for (or ANY_OWNER for all). * \return pointer to a static db_stat_info structure. */ struct db_stat_info * get_stats(dbref owner) { dbref i; static struct db_stat_info si; if (owner == ANY_OWNER) return ¤t_state; si.total = si.rooms = si.exits = si.things = si.players = si.garbage = 0; for (i = 0; i < db_top; i++) { if (owner == ANY_OWNER || owner == Owner(i)) { si.total++; if (IsGarbage(i)) { si.garbage++; } else { switch (Typeof(i)) { case TYPE_ROOM: si.rooms++; break; case TYPE_EXIT: si.exits++; break; case TYPE_THING: si.things++; break; case TYPE_PLAYER: si.players++; break; default: break; } } } } return &si; } /** The stats command. * \verbatim * This implements @stats. * \endverbatim * \param player the enactor. * \param name name of player to check object stats for. */ void do_stats(dbref player, const char *name) { struct db_stat_info *si; dbref owner; if (*name == '\0') owner = ANY_OWNER; else if (*name == '#') { owner = atoi(&name[1]); if (!GoodObject(owner)) owner = NOTHING; else if (!IsPlayer(owner)) owner = NOTHING; } else if (strcasecmp(name, "me") == 0) owner = player; else owner = lookup_player(name); if (owner == NOTHING) { notify_format(player, T("%s: No such player."), name); return; } if (!Search_All(player)) { if (owner != ANY_OWNER && owner != player) { notify(player, T("You need a search warrant to do that!")); return; } } si = get_stats(owner); if (owner == ANY_OWNER) { notify_format(player, T ("%d objects = %d rooms, %d exits, %d things, %d players, %d garbage."), si->total, si->rooms, si->exits, si->things, si->players, si->garbage); if (first_free != NOTHING) notify_format(player, T("The next object to be created will be #%d."), first_free); } else { notify_format(player, T ("%d objects = %d rooms, %d exits, %d things, %d players."), si->total - si->garbage, si->rooms, si->exits, si->things, si->players); } } /** Reset a player's password. * \verbatim * This implements @newpassword. * \endverbatim * \param player the executor. * \param cause the enactor. * \param name the name of the player whose password is to be reset. * \param password the new password for the player. */ void do_newpassword(dbref player, dbref cause, const char *name, const char *password) { dbref victim; if (!global_eval_context.process_command_port) { char pass_eval[BUFFER_LEN]; char const *sp; char *bp; sp = password; bp = pass_eval; process_expression(pass_eval, &bp, &sp, player, player, cause, PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; password = pass_eval; } if ((victim = lookup_player(name)) == NOTHING) { notify(player, T("No such player.")); } else if (*password != '\0' && !ok_password(password)) { /* Wiz can set null passwords, but not bad passwords */ notify(player, T("Bad password.")); } else if (God(victim) && !God(player)) { notify(player, T("You cannot change that player's password.")); } else { /* it's ok, do it */ (void) atr_add(victim, "XYXXY", mush_crypt(password), GOD, NOTHING); notify_format(player, T("Password for %s changed."), Name(victim)); notify_format(victim, T("Your password has been changed by %s."), Name(player)); do_log(LT_WIZ, player, victim, "*** NEWPASSWORD ***"); } } /** Disconnect a player, forcibly. * \verbatim * This implements @boot. * \endverbatim * \param player the enactor. * \param name name of the player or descriptor to boot. * \param flag the type of booting to do. */ void do_boot(dbref player, const char *name, enum boot_type flag) { dbref victim; DESC *d = NULL; victim = NOTHING; switch (flag) { case BOOT_SELF: /* self boot */ victim = player; break; case BOOT_DESC: /* boot by descriptor */ victim = find_player_by_desc(atoi(name)); if (victim == NOTHING) { d = port_desc(atoi(name)); if (!d && Can_Boot(player)) { notify(player, "There is no one connected on that descriptor."); return; } else victim = AMBIGUOUS; } break; case BOOT_SILENT: case BOOT_NAME: /* boot by name */ if ((victim = noisy_match_result(player, name, TYPE_PLAYER, MAT_LIMITED | MAT_ME)) == NOTHING) { notify(player, T("No such connected player.")); return; } if (victim == player) flag = BOOT_SELF; break; } if ((victim != player) && !Can_Boot(player)) { notify(player, T("You can't boot other people!")); return; } if (God(victim) && !God(player)) { notify(player, T("You're good. That's spelled with two 'o's.")); return; } /* look up descriptor */ switch (flag) { case BOOT_SILENT: case BOOT_NAME: d = player_desc(victim); break; case BOOT_DESC: d = port_desc(atoi(name)); break; case BOOT_SELF: d = inactive_desc(victim); break; } if (victim == player && (!d || d->descriptor == global_eval_context.process_command_port)) { notify(player, T("If you want to quit, use QUIT.")); return; } /* check for more errors */ if (!d) { if (flag == BOOT_SELF) notify(player, T("None of your connections are idle.")); else notify(player, T("That player isn't connected!")); } else { do_log(LT_WIZ, player, victim, "*** BOOT ***"); if (flag == BOOT_SELF) notify(player, T("You boot an idle self.")); else if (victim == AMBIGUOUS) notify_format(player, T("You booted unconnected port %s!"), name); else if (victim == player) notify(player, T("You boot a duplicate self.")); else { notify_format(player, T("You booted %s off!"), Name(victim)); if (flag != BOOT_SILENT) notify(victim, T("You are politely shown to the door.")); } boot_desc(d); } } /** Chown all of a player's objects. * \verbatim * This implements @chownall * \endverbatim * \param player the enactor. * \param name name of player whose objects are to be chowned. * \param target name of new owner for objects. * \param preserve if 1, keep privileges and don't halt objects. */ void do_chownall(dbref player, const char *name, const char *target, int preserve) { int i; dbref victim; dbref n_target; int count = 0; if (!Wizard(player)) { notify(player, T("Try asking them first!")); return; } if ((victim = noisy_match_result(player, name, TYPE_PLAYER, MAT_LIMITED)) == NOTHING) return; if (!target || !*target) { n_target = player; } else { if ((n_target = noisy_match_result(player, target, TYPE_PLAYER, MAT_LIMITED)) == NOTHING) return; } for (i = 0; i < db_top; i++) { if ((Owner(i) == victim) && (!IsPlayer(i))) { chown_object(player, i, n_target, preserve); count++; } } /* change quota (this command is wiz only and we can assume that * we intend for the recipient to get all the objects, so we * don't do a quota check earlier. */ change_quota(victim, count); change_quota(n_target, -count); notify_format(player, T("Ownership changed for %d objects."), count); } /** Change the zone of all of a player's objects. * \verbatim * This implements @chzoneall. * \endverbatim * \param player the enactor. * \param name name of player whose objects should be rezoned. * \param target string containing new zone master for objects. */ void do_chzoneall(dbref player, const char *name, const char *target) { int i; dbref victim; dbref zone; int count = 0; if (!Wizard(player)) { notify(player, T("You do not have the power to change reality.")); return; } if ((victim = noisy_match_result(player, name, TYPE_PLAYER, MAT_LIMITED)) == NOTHING) return; if (!target || !*target) { notify(player, T("No zone specified.")); return; } if (!strcasecmp(target, "none")) zone = NOTHING; else { switch (zone = match_result(player, target, NOTYPE, MAT_EVERYTHING)) { case NOTHING: notify(player, T("I can't seem to find that.")); return; case AMBIGUOUS: notify(player, T("I don't know which one you mean!")); return; } } /* Okay, now that we know we're not going to spew all sorts of errors, * call the normal do_chzone for all the relevant objects. This keeps * consistency on things like flag resetting, etc... */ for (i = 0; i < db_top; i++) { if (Owner(i) == victim && Zone(i) != zone) { count += do_chzone(player, unparse_dbref(i), target, 0); } } notify_format(player, T("Zone changed for %d objects."), count); } /*----------------------------------------------------------------------- * Nasty management: @kick, examine/debug */ /** Execute a number of commands off the queue immediately. * \verbatim * This implements @kick, which is nasty. * \endverbatim * \param player the enactor. * \param num string containing number of commands to run from the queue. */ void do_kick(dbref player, const char *num) { int n; if (!Wizard(player)) { notify(player, T("Permission denied.")); return; } if (!num || !*num) { notify(player, T("How many commands do you want to execute?")); return; } n = atoi(num); if (n <= 0) { notify(player, T("Number out of range.")); return; } n = do_top(n); notify_format(player, T("%d commands executed."), n); } /** examine/debug. * This implements examine/debug, which provides some raw values for * object structure elements of an examined object. * \param player the enactor. * \param name name of object to examine. */ void do_debug_examine(dbref player, const char *name) { MAIL *mp; dbref thing; if (!Hasprivs(player)) { notify(player, T("Permission denied.")); return; } /* find it */ thing = noisy_match_result(player, name, NOTYPE, MAT_EVERYTHING); if (!GoodObject(thing)) return; notify(player, object_header(player, thing)); notify_format(player, T("Flags value: %s"), bits_to_string("FLAG", Flags(thing), GOD, NOTHING)); notify_format(player, T("Powers value: %s"), bits_to_string("POWER", Powers(thing), GOD, NOTHING)); notify_format(player, "Next: %d", Next(thing)); notify_format(player, "Contents: %d", Contents(thing)); notify_format(player, "Pennies: %d", Pennies(thing)); switch (Typeof(thing)) { case TYPE_PLAYER: mp = desc_mail(thing); notify_format(player, T("First mail sender: %d"), mp ? mp->from : NOTHING); case TYPE_THING: notify_format(player, "Location: %d", Location(thing)); notify_format(player, "Home: %d", Home(thing)); break; case TYPE_EXIT: notify_format(player, "Destination: %d", Location(thing)); notify_format(player, "Source: %d", Source(thing)); break; case TYPE_ROOM: notify_format(player, "Drop-to: %d", Location(thing)); notify_format(player, "Exits: %d", Exits(thing)); break; case TYPE_GARBAGE: break; default: notify(player, T("Bad object type.")); } } /*------------------------------------------------------------------------- * Powers stuff */ /** Set a power on an object. * \verbatim * This implements @power. * \endverbatim * \param player the enactor. * \param name name of the object on which to set the power. * \param power name of the power to set. */ void do_power(dbref player, const char *name, const char *power) { int revoke_it = 0; char powerbuff[BUFFER_LEN], *p, *f; dbref thing; if (!power || !*power) { /* @power <power> */ do_flag_info("POWER", player, name); return; } if (!Wizard(player)) { notify(player, T("Only wizards may grant powers.")); return; } if ((thing = noisy_match_result(player, name, NOTYPE, MAT_EVERYTHING)) == NOTHING) return; if (Unregistered(thing)) { notify(player, T("You can't grant powers to unregistered players.")); return; } if (God(thing) && !God(player)) { notify(player, T("God is already all-powerful.")); return; } strcpy(powerbuff, power); p = trim_space_sep(powerbuff, ' '); if (*p == '\0') { notify(player, T("You must specify a power to set.")); return; } do { f = split_token(&p, ' '); revoke_it = 0; if (*f == NOT_TOKEN && *(f + 1)) { revoke_it = 1; f++; } set_power(player, thing, f, revoke_it); } while (p); } /*---------------------------------------------------------------------------- * Search functions */ /** User command to search the db for matching objects. * \verbatim * This implements @search. * \endverbatim * \param player the enactor. * \param arg1 name of player whose objects are to be searched. * \param arg3 additional search arguments. */ void do_search(dbref player, const char *arg1, char **arg3) { char tbuf[BUFFER_LEN], *arg2 = tbuf, *tbp; dbref *results = NULL; int nresults; /* parse first argument into two */ if (!arg1 || *arg1 == '\0') arg1 = "me"; /* First argument is a player, so we could have a quoted name */ if (PLAYER_NAME_SPACES && *arg1 == '\"') { for (; *arg1 && ((*arg1 == '\"') || isspace((unsigned char) *arg1)); arg1++) ; strcpy(tbuf, arg1); while (*arg2 && (*arg2 != '\"')) { while (*arg2 && (*arg2 != '\"')) arg2++; if (*arg2 == '\"') { *arg2++ = '\0'; while (*arg2 && isspace((unsigned char) *arg2)) arg2++; break; } } } else { strcpy(tbuf, arg1); while (*arg2 && !isspace((unsigned char) *arg2)) arg2++; if (*arg2) *arg2++ = '\0'; while (*arg2 && isspace((unsigned char) *arg2)) arg2++; } if (!*arg2) { if (!arg3[1] || !*arg3[1]) arg2 = (char *) ""; /* arg1 */ else { arg2 = (char *) arg1; /* arg2=arg3 */ tbuf[0] = '\0'; } } nresults = raw_search(player, tbuf, arg2, arg3[1], arg3[2], arg3[3], &results, NULL); if (nresults == 0) { notify(player, T("Nothing found.")); } else if (nresults > 0) { /* Split the results up by type and report. */ int n; int nthings = 0, nexits = 0, nrooms = 0, nplayers = 0; dbref *things, *exits, *rooms, *players; things = (dbref *) mush_malloc(sizeof(dbref) * nresults, "dbref_list"); exits = (dbref *) mush_malloc(sizeof(dbref) * nresults, "dbref_list"); rooms = (dbref *) mush_malloc(sizeof(dbref) * nresults, "dbref_list"); players = (dbref *) mush_malloc(sizeof(dbref) * nresults, "dbref_list"); for (n = 0; n < nresults; n++) { switch (Typeof(results[n])) { case TYPE_THING: things[nthings++] = results[n]; break; case TYPE_EXIT: exits[nexits++] = results[n]; break; case TYPE_ROOM: rooms[nrooms++] = results[n]; break; case TYPE_PLAYER: players[nplayers++] = results[n]; break; default: /* Unknown type. Ignore. */ do_rawlog(LT_ERR, T("Weird type for dbref #%d"), results[n]); } } if (nrooms) { notify(player, "\nROOMS:"); for (n = 0; n < nrooms; n++) { tbp = tbuf; safe_format(tbuf, &tbp, "%s [owner: ", object_header(player, rooms[n])); safe_str(object_header(player, Owner(rooms[n])), tbuf, &tbp); safe_chr(']', tbuf, &tbp); *tbp = '\0'; notify(player, tbuf); } } if (nexits) { dbref from, to; notify(player, "\nEXITS:"); for (n = 0; n < nexits; n++) { tbp = tbuf; if (Source(exits[n]) == NOTHING) from = NOTHING; else from = Source(exits[n]); to = Destination(exits[n]); safe_format(tbuf, &tbp, "%s [from ", object_header(player, exits[n])); safe_str((from == NOTHING) ? "NOWHERE" : object_header(player, from), tbuf, &tbp); safe_str(" to ", tbuf, &tbp); safe_str((to == NOTHING) ? "NOWHERE" : object_header(player, to), tbuf, &tbp); safe_chr(']', tbuf, &tbp); *tbp = '\0'; notify(player, tbuf); } } if (nthings) { notify(player, "\nOBJECTS:"); for (n = 0; n < nthings; n++) { tbp = tbuf; safe_format(tbuf, &tbp, "%s [owner: ", object_header(player, things[n])); safe_str(object_header(player, Owner(things[n])), tbuf, &tbp); safe_chr(']', tbuf, &tbp); *tbp = '\0'; notify(player, tbuf); } } if (nplayers) { int is_wizard = Search_All(player) || See_All(player); notify(player, "\nPLAYERS:"); for (n = 0; n < nplayers; n++) { tbp = tbuf; safe_str(object_header(player, players[n]), tbuf, &tbp); if (is_wizard) safe_format(tbuf, &tbp, T(" [location: %s]"), object_header(player, Location(players[n]))); *tbp = '\0'; notify(player, tbuf); } } notify(player, T("---------- Search Done ----------")); notify_format(player, T ("Totals: Rooms...%d Exits...%d Objects...%d Players...%d"), nrooms, nexits, nthings, nplayers); mush_free((Malloc_t) rooms, "dbref_list"); mush_free((Malloc_t) exits, "dbref_list"); mush_free((Malloc_t) things, "dbref_list"); mush_free((Malloc_t) players, "dbref_list"); } if (results) mush_free(results, "search_results"); } FUNCTION(fun_lsearch) { int nresults; dbref *results = NULL; int rev = !strcmp(called_as, "LSEARCHR"); if (!command_check_byname(executor, "@search")) { safe_str(T(e_perm), buff, bp); return; } if (!strcmp(called_as, "CHILDREN")) nresults = raw_search(executor, NULL, "PARENT", args[0], NULL, NULL, &results, pe_info); else nresults = raw_search(executor, args[0], args[1], args[2], args[3], args[4], &results, pe_info); if (nresults < 0) { safe_str("#-1", buff, bp); } else if (nresults == 0) { notify(executor, T("Nothing found.")); } else { int first = 1, n; if (!rev) { for (n = 0; n < nresults; n++) { if (first) first = 0; else if (safe_chr(' ', buff, bp)) break; if (safe_dbref(results[n], buff, bp)) break; } } else { for (n = nresults - 1; n >= 0; n--) { if (first) first = 0; else if (safe_chr(' ', buff, bp)) break; if (safe_dbref(results[n], buff, bp)) break; } } } if (results) mush_free(results, "search_results"); } /** Type of limitation to apply to a search of the db */ enum search_class { S_OWNER, /**< Limit to a single owner */ S_TYPE, /**< Limit to a single type */ S_PARENT, /**< Limit to objects with a given parent */ S_ZONE, /**< Limit to objects in a given zone */ S_FLAG, /**< Limit to objects with given flag characters */ S_POWER, /**< Limit to objects with given power */ S_EVAL, /**< Limit to objects for which an expression evals true */ S_NAME, /**< Limit to objects prefix-matching a given name */ S_LFLAG /**< Limit to objects with given flag names */ }; /* Does the actual searching */ static int raw_search(dbref player, const char *owner, const char *class, const char *restriction, const char *start, const char *stop, dbref **result, PE_Info * pe_info) { size_t result_size; size_t nresults = 0; enum search_class sclass = S_OWNER; int n; int restrict_type = NOTYPE; dbref restrict_obj = NOTHING, restrict_owner = ANY_OWNER; int is_wiz; dbref low = 0, high = db_top - 1; is_wiz = Search_All(player) || See_All(player); /* Range limits */ if (start && *start) { size_t offset = 0; if (start[0] == '#') offset = 1; low = parse_integer(start + offset); if (!GoodObject(low)) low = 0; } if (stop && *stop) { size_t offset = 0; if (stop[0] == '#') offset = 1; high = parse_integer(stop + offset); if (!GoodObject(high)) high = db_top - 1; } /* set limits on who we search */ if (!owner || !*owner || strcasecmp(owner, "all") == 0) restrict_owner = is_wiz ? ANY_OWNER : player; else if (strcasecmp(owner, "me") == 0) restrict_owner = player; else restrict_owner = lookup_player(owner); if (restrict_owner == NOTHING) { notify(player, T("Unknown owner.")); return -1; } /* Figure out the class */ if (!class || !*class || strcasecmp(class, "none") == 0) { sclass = S_OWNER; } else if (string_prefix("type", class)) { sclass = S_TYPE; if (string_prefix("things", restriction) || string_prefix("objects", restriction)) { restrict_type = TYPE_THING; } else if (string_prefix("rooms", restriction)) { restrict_type = TYPE_ROOM; } else if (string_prefix("exits", restriction)) { restrict_type = TYPE_EXIT; } else if (string_prefix("rooms", restriction)) { restrict_type = TYPE_ROOM; } else if (string_prefix("players", restriction)) { restrict_type = TYPE_PLAYER; } else { notify(player, T("Unknown type.")); return -1; } } else if (string_prefix("things", class) || string_prefix("objects", class)) { sclass = S_NAME; restrict_type = TYPE_THING; } else if (string_prefix("exits", class)) { sclass = S_NAME; restrict_type = TYPE_EXIT; } else if (string_prefix("rooms", class)) { sclass = S_NAME; restrict_type = TYPE_ROOM; } else if (string_prefix("players", class)) { sclass = S_NAME; restrict_type = TYPE_PLAYER; } else if (string_prefix("name", class)) { sclass = S_NAME; } else if (string_prefix("parent", class)) { sclass = S_PARENT; if (!is_objid(restriction)) { notify(player, T("Unknown parent.")); return -1; } restrict_obj = parse_objid(restriction); if (!GoodObject(restrict_obj)) { notify(player, T("Unknown parent.")); return -1; } } else if (string_prefix("zone", class)) { sclass = S_ZONE; if (!is_objid(restriction)) { notify(player, T("Unknown zone.")); return -1; } restrict_obj = parse_objid(restriction); if (!GoodObject(restrict_obj)) { notify(player, T("Unknown zone.")); return -1; } } else if (string_prefix("eval", class)) { sclass = S_EVAL; } else if (string_prefix("ethings", class) || string_prefix("eobjects", class)) { sclass = S_EVAL; restrict_type = TYPE_THING; } else if (string_prefix("eexits", class)) { sclass = S_EVAL; restrict_type = TYPE_EXIT; } else if (string_prefix("erooms", class)) { sclass = S_EVAL; restrict_type = TYPE_ROOM; } else if (string_prefix("eplayers", class)) { sclass = S_EVAL; restrict_type = TYPE_PLAYER; } else if (string_prefix("powers", class)) { /* Handle the checking later. */ sclass = S_POWER; if (!restriction || !*restriction) { notify(player, T("You must give a list of power names.")); return -1; } } else if (string_prefix("flags", class)) { /* Handle the checking later. */ sclass = S_FLAG; if (!restriction || !*restriction) { notify(player, T("You must give a string of flag characters.")); return -1; } } else if (string_prefix("lflags", class)) { /* Handle the checking later. */ sclass = S_LFLAG; if (!restriction || !*restriction) { notify(player, T("You must give a list of flag names.")); return -1; } } else { notify(player, T("Unknown search class.")); return -1; } if ((restrict_owner != ANY_OWNER && restrict_owner != player) && !(is_wiz || (sclass == S_TYPE && restrict_type == TYPE_PLAYER))) { notify(player, T("You need a search warrant to do that.")); return -1; } /* make sure player has money to do the search */ if (!payfor(player, FIND_COST)) { notify_format(player, T("Searches cost %d %s."), FIND_COST, ((FIND_COST == 1) ? MONEY : MONIES)); return -1; } result_size = (db_top / 4) + 1; *result = (dbref *) mush_malloc(sizeof(dbref) * result_size, "search_results"); if (!*result) mush_panic(T("Couldn't allocate memory in search!")); switch (sclass) { case S_OWNER: /* @search someone */ case S_TYPE: /* @search type=whatever */ for (n = low; n <= high; n++) { if ((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner) && (restrict_type == NOTYPE || Typeof(n) == restrict_type)) { if (nresults >= result_size) { dbref *newresults; result_size *= 2; newresults = (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size); if (!newresults) mush_panic(T("Couldn't reallocate memory in search!")); *result = newresults; } (*result)[nresults++] = (dbref) n; } } break; case S_ZONE: /* @search ZONE=#1234 */ for (n = low; n <= high; n++) { if ((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner) && Zone(n) == restrict_obj) { if (nresults >= result_size) { dbref *newresults; result_size *= 2; newresults = (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size); if (!newresults) mush_panic(T("Couldn't reallocate memory in search!")); *result = newresults; } (*result)[nresults++] = (dbref) n; } } break; case S_PARENT: /* @search parent=#1234 */ for (n = low; n <= high; n++) { if ((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner) && Parent(n) == restrict_obj) { if (nresults >= result_size) { dbref *newresults; result_size *= 2; newresults = (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size); if (!newresults) mush_panic(T("Couldn't reallocate memory in search!")); *result = newresults; } (*result)[nresults++] = (dbref) n; } } break; case S_NAME: /* @search (?:name|exits|objects|rooms|players|things)=name */ for (n = low; n <= high; n++) { if ((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner) && (restrict_type == NOTYPE || Typeof(n) == restrict_type) && string_match(Name(n), restriction)) { if (nresults >= result_size) { dbref *newresults; result_size *= 2; newresults = (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size); if (!newresults) mush_panic(T("Couldn't reallocate memory in search!")); *result = newresults; } (*result)[nresults++] = (dbref) n; } } break; case S_EVAL: /* @search (?:eval|ething|eroom|eplayer|eexit)=code */ { char *ebuf1; const char *ebuf2; char tbuf1[BUFFER_LEN]; char *bp; if (!restriction || !*restriction) break; for (n = low; n <= high; n++) { if (!((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner) && (restrict_type == NOTYPE || Typeof(n) == restrict_type))) continue; ebuf1 = replace_string("##", unparse_dbref(n), restriction); ebuf2 = ebuf1; bp = tbuf1; process_expression(tbuf1, &bp, &ebuf2, player, player, player, PE_DEFAULT, PT_DEFAULT, pe_info); mush_free((Malloc_t) ebuf1, "replace_string.buff"); *bp = '\0'; if (!parse_boolean(tbuf1)) continue; if (nresults >= result_size) { dbref *newresults; result_size *= 2; newresults = (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size); if (!newresults) mush_panic(T("Couldn't reallocate memory in search!")); *result = newresults; } (*result)[nresults++] = (dbref) n; if (pe_info && pe_info->fun_invocations >= FUNCTION_LIMIT) break; } } break; case S_FLAG: case S_LFLAG: case S_POWER: for (n = low; n <= high; n++) { if ((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner) && (restrict_type == NOTYPE || Typeof(n) == restrict_type) && ((sclass == S_POWER) ? flaglist_check_long("POWER", player, n, restriction, 1) : ((sclass == S_FLAG) ? flaglist_check("FLAG", player, n, restriction, 1) : flaglist_check_long("FLAG", player, n, restriction, 1)))) { if (nresults >= result_size) { dbref *newresults; result_size *= 2; newresults = (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size); if (!newresults) mush_panic(T("Couldn't reallocate memory in search!")); *result = newresults; } (*result)[nresults++] = (dbref) n; } } break; } return (int) nresults; } #ifdef WIN32 #pragma warning( disable : 4761) /* Disable bogus conversion warning */ #endif /* ARGSUSED */ FUNCTION(fun_hidden) { dbref it = match_thing(executor, args[0]); if (!See_All(executor)) { notify(executor, T("Permission denied.")); safe_str("#-1", buff, bp); return; } if ((it == NOTHING) || (!IsPlayer(it))) { notify(executor, T("Couldn't find that player.")); safe_str("#-1", buff, bp); return; } safe_boolean(hidden(it), buff, bp); return; } #ifdef WIN32 #pragma warning( default : 4761) /* Re-enable conversion warning */ #endif /* ARGSUSED */ FUNCTION(fun_quota) { int owned; /* Tell us player's quota */ dbref thing; dbref who = lookup_player(args[0]); if ((who == NOTHING) || !IsPlayer(who)) { notify(executor, T("Couldn't find that player.")); safe_str("#-1", buff, bp); return; } if (!(Do_Quotas(executor) || See_All(executor) || controls(executor, who))) { notify(executor, T("You can't see someone else's quota!")); safe_str("#-1", buff, bp); return; } if (NoQuota(who)) { /* Unlimited, but return a big number to be sensible */ safe_str("99999", buff, bp); return; } /* count up all owned objects */ owned = -1; /* a player is never included in his own * quota */ for (thing = 0; thing < db_top; thing++) { if (Owner(thing) == who) if (!IsGarbage(thing)) ++owned; } safe_integer(owned + get_current_quota(who), buff, bp); return; } /** Modify access rules for a site. * \verbatim * This implements @sitelock. * \endverbatim * \param player the enactor. * \param site name of site. * \param opts access rules to apply. * \param who string containing dbref of player to whom rule applies. * \param type sitelock operation to do. */ void do_sitelock(dbref player, const char *site, const char *opts, const char *who, enum sitelock_type type) { if (!Wizard(player)) { notify(player, T("Your delusions of grandeur have been noted.")); return; } if (opts && *opts) { int can, cant; dbref whod = AMBIGUOUS; /* Options form of the command. */ if (!site || !*site) { notify(player, T("What site did you want to lock?")); return; } can = cant = 0; if (!parse_access_options(opts, NULL, &can, &cant, player)) { notify(player, T("No valid options found.")); return; } if (who && *who) { /* Specify a character */ whod = lookup_player(who); if (!GoodObject(whod)) { notify(player, T("Who do you want to lock?")); return; } } add_access_sitelock(player, site, whod, can, cant); write_access_file(); if (whod != AMBIGUOUS) { notify_format(player, T("Site %s access options for %s(%s) set to %s"), site, Name(whod), unparse_dbref(whod), opts); do_log(LT_WIZ, player, NOTHING, T("*** SITELOCK *** %s for %s(%s) --> %s"), site, Name(whod), unparse_dbref(whod), opts); } else { notify_format(player, T("Site %s access options set to %s"), site, opts); do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s --> %s", site, opts); } return; } else { /* Backward-compatible non-options form of the command, * or @sitelock/name */ switch (type) { case SITELOCK_LIST: /* List bad sites */ do_list_access(player); return; case SITELOCK_ADD: add_access_sitelock(player, site, AMBIGUOUS, 0, ACS_CREATE); write_access_file(); notify_format(player, T("Site %s locked"), site); do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s", site); break; case SITELOCK_BAN: add_access_sitelock(player, site, AMBIGUOUS, 0, ACS_DEFAULT); write_access_file(); notify_format(player, T("Site %s banned"), site); do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s", site); break; case SITELOCK_CHECK:{ struct access *ap; char tbuf[BUFFER_LEN], *bp; int rulenum; if (!site || !*site) { do_list_access(player); return; } ap = site_check_access(site, AMBIGUOUS, &rulenum); bp = tbuf; format_access(ap, rulenum, AMBIGUOUS, tbuf, &bp); *bp = '\0'; notify(player, tbuf); break; } case SITELOCK_REMOVE:{ int n; n = remove_access_sitelock(site); if (n > 0) write_access_file(); notify_format(player, T("%d sitelocks removed."), n); break; } } } } /** Edit the list of restricted player names * \verbatim * This implements @sitelock/name * \endverbatim * \param player the player doing the command. * \param name the name (Actually, wildcard pattern) to restrict. */ void do_sitelock_name(dbref player, const char *name) { FILE *fp, *fptmp; char buffer[BUFFER_LEN]; char *p; if (!Wizard(player)) { notify(player, T("Your delusions of grandeur have been noted.")); return; } release_fd(); if (!name || !*name) { /* List bad names */ if ((fp = fopen(NAMES_FILE, FOPEN_READ)) == NULL) { notify(player, T("Unable to open names file.")); } else { notify(player, T("Any name matching these wildcard patterns is banned:")); while (fgets(buffer, sizeof buffer, fp)) { if ((p = strchr(buffer, '\r')) != NULL) *p = '\0'; else if ((p = strchr(buffer, '\n')) != NULL) *p = '\0'; notify(player, buffer); } fclose(fp); } } else if (name[0] == '!') { /* Delete a name */ if ((fp = fopen(NAMES_FILE, FOPEN_READ)) != NULL) { if ((fptmp = fopen("tmp.tmp", FOPEN_WRITE)) == NULL) { notify(player, T("Unable to delete name.")); fclose(fp); } else { while (fgets(buffer, sizeof buffer, fp)) { if ((p = strchr(buffer, '\r')) != NULL) *p = '\0'; else if ((p = strchr(buffer, '\n')) != NULL) *p = '\0'; if (strcasecmp(buffer, name + 1) == 0) /* Replace the name with #NAME, to allow things like keeping track of unlocked feature names. */ fprintf(fptmp, "#%s\n", buffer); else fprintf(fptmp, "%s\n", buffer); } fclose(fp); fclose(fptmp); if (rename_file("tmp.tmp", NAMES_FILE) == 0) { notify(player, T("Name removed.")); do_log(LT_WIZ, player, NOTHING, "*** UNLOCKED NAME *** %s", name + 1); } else { notify(player, T("Unable to delete name.")); } } } else notify(player, T("Unable to delete name.")); } else { /* Add a name */ if ((fp = fopen(NAMES_FILE, FOPEN_READ)) != NULL) { if ((fptmp = fopen("tmp.tmp", FOPEN_WRITE)) == NULL) { notify(player, T("Unable to lock name.")); } else { /* Read the names file, looking for #NAME and writing it without the commenting #. Otherwise, add the new name to the end of the file unless it's already present */ char commented[BUFFER_LEN + 1]; int found = 0; commented[0] = '#'; strcpy(commented + 1, name); while (fgets(buffer, sizeof buffer, fp) != NULL) { if ((p = strchr(buffer, '\r')) != NULL) *p = '\0'; else if ((p = strchr(buffer, '\n')) != NULL) *p = '\0'; if (strcasecmp(commented, buffer) == 0) { fprintf(fptmp, "%s\n", buffer + 1); found = 1; } else { fprintf(fptmp, "%s\n", buffer); if (strcasecmp(name, buffer) == 0) found = 1; } } if (!found) fprintf(fptmp, "%s\n", name); fclose(fp); fclose(fptmp); if (rename_file("tmp.tmp", NAMES_FILE) == 0) { notify_format(player, T("Name %s locked."), name); do_log(LT_WIZ, player, NOTHING, "*** NAMELOCK *** %s", name); } else notify(player, T("Unable to lock name.")); } } } reserve_fd(); } /*----------------------------------------------------------------- * Functions which give memory information on objects or players. * Source code originally by Kalkin, modified by Javelin */ static int mem_usage(dbref thing) { int k; ATTR *m; lock_list *l; k = sizeof(struct object); /* overhead */ k += strlen(Name(thing)) + 1; /* The name */ for (m = List(thing); m; m = AL_NEXT(m)) { k += sizeof(ATTR); if (AL_STR(m) && *AL_STR(m)) k += u_strlen(AL_STR(m)) + 1; /* NOTE! In the above, we're getting the size of the * compressed attrib, not the uncompressed one (as Kalkin did) * because (1) it's more efficient, (2) it's more accurate * since that's how the object is in memory. This relies on * compressed attribs being terminated with \0's, which they * are in compress.c. If that changes, this breaks. */ } for (l = Locks(thing); l; l = l->next) { k += sizeof(lock_list); k += sizeof_boolexp(l->key); } return k; } /* ARGSUSED */ FUNCTION(fun_objmem) { dbref thing; if (!Search_All(executor)) { safe_str(T(e_perm), buff, bp); return; } if (!strcasecmp(args[0], "me")) thing = executor; else if (!strcasecmp(args[0], "here")) thing = Location(executor); else { thing = noisy_match_result(executor, args[0], NOTYPE, MAT_OBJECTS); } if (!GoodObject(thing)) { safe_str(T(e_match), buff, bp); return; } if (!Can_Examine(executor, thing)) { safe_str(T(e_perm), buff, bp); return; } safe_integer(mem_usage(thing), buff, bp); } /* ARGSUSED */ FUNCTION(fun_playermem) { int tot = 0; dbref thing; dbref j; if (!Search_All(executor)) { safe_str(T(e_perm), buff, bp); return; } if (!strcasecmp(args[0], "me") && IsPlayer(executor)) thing = executor; else if (*args[0] && *args[0] == '*') thing = lookup_player(args[0] + 1); else if (*args[0] && *args[0] == '#') thing = atoi(args[0] + 1); else thing = lookup_player(args[0]); if (!GoodObject(thing) || !IsPlayer(thing)) { safe_str(T(e_match), buff, bp); return; } if (!Can_Examine(executor, thing)) { safe_str(T(e_perm), buff, bp); return; } for (j = 0; j < db_top; j++) if (Owner(j) == thing) tot += mem_usage(j); safe_integer(tot, buff, bp); } /** Reboot the game without disconnecting players. * \verbatim * This implements @shutdown/reboot, which performs a dump, saves * information about which player is associated with which socket, * and then re-execs the mush process without closing the sockets. * \endverbatim * \param player the enactor. * \param flag if 0, normal dump; if 1, paranoid dump. */ void do_reboot(dbref player, int flag) { if (player == NOTHING) { flag_broadcast(0, 0, T ("GAME: Reboot w/o disconnect from game account, please wait.")); } else { flag_broadcast(0, 0, T ("GAME: Reboot w/o disconnect by %s, please wait."), Name(Owner(player))); } if (flag) { globals.paranoid_dump = 1; globals.paranoid_checkpt = db_top / 5; if (globals.paranoid_checkpt < 1) globals.paranoid_checkpt = 1; } #ifdef HAS_OPENSSL close_ssl_connections(); #endif sql_shutdown(); shutdown_queues(); fork_and_dump(0); #ifndef PROFILING #ifndef WIN32 /* Some broken libcs appear to retain the itimer across exec! * So we make sure that if we get a SIGPROF in our next incarnation, * we ignore it until our proper handler is set up. */ ignore_signal(SIGPROF); #endif #endif dump_reboot_db(); #ifdef INFO_SLAVE kill_info_slave(); #endif local_shutdown(); end_all_logs(); #ifndef WIN32 execl("netmush", "netmush", confname, NULL); #else execl("pennmush.exe", "pennmush.exe", "/run", NULL); #endif /* WIN32 */ exit(1); /* Shouldn't ever get here, but just in case... */ }