/*! \file conf.cpp * \brief Set up configuration information and static data. * * This parses the configuration files and controls configuration options used * to control the server and its behavior. */ #include "copyright.h" #include "autoconf.h" #include "config.h" #include "externs.h" #include "alloc.h" #define PCRE2_CODE_UNIT_WIDTH 8 #include using namespace std; // --------------------------------------------------------------------------- // CONFPARM: Data used to find fields in CONFDATA. // typedef struct confparm { const UTF8 *pname; // parm name int (*interpreter)(int *vp, UTF8 *str, void *pExtra, uint32_t nExtra, dbref player, UTF8 *cmd); // routine to interp parameter int flags; // control flags int rperms; // read permission flags. int *loc; // where to store value void *pExtra; // extra pointer for interpreter uint32_t nExtra; // extra data for interpreter } CONFPARM; // --------------------------------------------------------------------------- // External symbols. // CONFDATA mudconf; STATEDATA mudstate; int cf_set(UTF8 *, UTF8 *, dbref); CF_HAND(cf_cf_access); // --------------------------------------------------------------------------- // cf_init: Initialize mudconf to default values. // void cf_init(void) { int i; mudconf.indb = StringClone(T("netmux.db")); mudconf.outdb = StringClone(T("")); mudconf.crashdb = StringClone(T("")); mudconf.mail_db = StringClone(T("mail.db")); mudconf.comsys_db = StringClone(T("comsys.db")); mudconf.status_file = StringClone(T("shutdown.status")); mudconf.max_cache_size = 256LL*1024*1024; mudconf.cache_preload_depth = 1; mudconf.ip_address = nullptr; mudconf.ports.push_back(2860); #if defined(UNIX_SSL) || defined(_WIN32) mudconf.sslPorts.clear(); mudconf.ssl_certificate_file[0] = '\0'; mudconf.ssl_certificate_key[0] = '\0'; mudconf.ssl_certificate_password[0] = '\0'; #endif mudconf.init_size = 1000; mudconf.guest_char = -1; mudconf.guest_nuker = GOD; mudconf.number_guests = 30; mudconf.min_guests = 1; mux_strncpy(mudconf.guest_prefix, T("Guest"), sizeof(mudconf.guest_prefix)-1); mudconf.guest_file = StringClone(T("text/guest.txt")); mudconf.conn_file = StringClone(T("text/connect.txt")); mudconf.conn_help_file = StringClone(T("text/connect_help.txt")); mudconf.creg_file = StringClone(T("text/register.txt")); mudconf.regf_file = StringClone(T("text/create_reg.txt")); mudconf.motd_file = StringClone(T("text/motd.txt")); mudconf.wizmotd_file = StringClone(T("text/wizmotd.txt")); mudconf.quit_file = StringClone(T("text/quit.txt")); mudconf.down_file = StringClone(T("text/down.txt")); mudconf.full_file = StringClone(T("text/full.txt")); mudconf.site_file = StringClone(T("text/badsite.txt")); mudconf.crea_file = StringClone(T("text/newuser.txt")); mudconf.crash_msg[0] = '\0'; mudconf.motd_msg[0] = '\0'; mudconf.wizmotd_msg[0] = '\0'; mudconf.downmotd_msg[0] = '\0'; mudconf.fullmotd_msg[0] = '\0'; mudconf.dump_msg[0] = '\0'; mudconf.postdump_msg[0] = '\0'; mux_strncpy(mudconf.fixed_home_msg, T("You are fixed in place and cannot move."), sizeof(mudconf.fixed_home_msg)-1); mux_strncpy(mudconf.fixed_tel_msg, T("You are fixed in place and cannot teleport."), sizeof(mudconf.fixed_tel_msg)-1); mux_strncpy(mudconf.public_channel, T("Public"), sizeof(mudconf.public_channel)-1); mux_strncpy(mudconf.public_channel_alias, T("pub"), sizeof(mudconf.public_channel_alias)-1); mudconf.player_channels[0] = '\0'; mux_strncpy(mudconf.guests_channel, T("Guests"), sizeof(mudconf.guests_channel)-1); mux_strncpy(mudconf.guests_channel_alias, T("g"), sizeof(mudconf.guests_channel_alias)-1); mux_strncpy(mudconf.pueblo_msg, T(""), sizeof(mudconf.pueblo_msg)-1); #if defined(INLINESQL) mudconf.sql_server[0] = '\0'; mudconf.sql_user[0] = '\0'; mudconf.sql_password[0] = '\0'; mudconf.sql_database[0] = '\0'; #endif // INLINESQL mudconf.mail_server[0] = '\0'; mudconf.mail_ehlo[0] = '\0'; mudconf.mail_sendaddr[0]= '\0'; mudconf.mail_sendname[0]= '\0'; mudconf.mail_subject[0] = '\0'; mudconf.indent_desc = false; g_no_flash = false; mudconf.terse_nospoof = false; mudconf.name_spaces = true; #if defined(HAVE_WORKING_FORK) mudconf.fork_dump = true; mudstate.dumping = false; mudstate.dumper = 0; mudstate.dumped = 0; mudstate.write_protect = false; #endif // HAVE_WORKING_FORK mudconf.restrict_home = false; mudconf.have_zones = true; mudconf.paranoid_alloc = false; mudconf.sig_action = SA_DFLT; mudconf.max_name_protect = 5; mudconf.max_players = -1; mudconf.dump_interval = 3600; mudconf.check_interval = 600; mudconf.events_daily_hour = 7; mudconf.dump_offset = 0; mudconf.check_offset = 300; mudconf.idle_timeout = 3600; mudconf.conn_timeout = 120; mudconf.idle_interval = 60; mudconf.keepalive_interval = 60; mudconf.retry_limit = 3; // Failed-login budget per source address. Generous relative to how often // real players mistype (and dorms/NAT put many unrelated players behind // one address), but 10/minute makes brute-force-by-reconnect -- currently // unbounded -- useless. mudconf.login_fail_limit = 10; mudconf.login_fail_period = 60; // Collapse a consecutive run of refusals from one address rather than // logging each. Rhost ships the equivalent off (0); we default to 1 // because the collapse loses no signal -- you still get the first line // and an exact count -- while closing the refused-flood-fills-the-disk // path by default. mudconf.nospam_connect = 1; // Connection churn per source. Rhost ships 20/60s but gates it behind // lastsite_paranoia (off by default) and counts only CONSECUTIVE // connections, so one interleaved connection from elsewhere resets them. // Ours is a true per-source bucket -- it cannot be reset that way -- so // the same number would bite considerably harder. 40/60s still cuts // churn from unlimited to 40/minute while clearing a whole dorm // reconnecting at once after a reboot, which is the burst that matters. mudconf.max_lastsite_cnt = 40; mudconf.min_con_attempt = 60; mudconf.output_limit = 2 * LBUF_SIZE; // Pre-auth connections from one source address. Legit multi-play // (dorm/NAT, household, one player on 5+ alts) AUTHENTICATES promptly, // so it does not accumulate pre-auth sockets; a slowloris holds many // half-open ones. Refusal is transient and self-healing (a slot frees // the moment a peer authenticates or conn_timeout reaps it). mudconf.max_preauth_sitecons = 2; // Protocol-detect grace (#1074, #2193). 500ms is not a guess at an RTT // -- a WebSocket client's GET rides right behind the handshake's final // ACK, so the healthy case needs ~0ms regardless of distance. What the // window has to survive is that first packet being LOST: the retransmit // comes back on an RTO that Linux floors at 200ms. 500 covers one such // retransmit with headroom. 100 would sit in the dead zone -- past every // healthy client, short of every retransmit -- and would break real // WebSocket handshakes intermittently on a lossy link, which is a far // worse failure than a slow banner. Set 0 on a port that never serves // WebSocket: nothing to detect, banner at accept. mudconf.proto_detect_window = 500; // Pool memory budget: 0 = unlimited (off) by default. No non-zero // default is safe across deployments (a 256MB VPS and a 32GB host want // very different ceilings), so the admin sizes it per host; enabling it // converts a fatal OutOfMemory under a runaway command into a graceful // per-command abort. (#pool-oom) mudconf.pool_memory_limit = 0; g_pool_limit_bytes = 0; // Input backlog is drop-SENSITIVE (a dropped line silently corrupts a // user's paste — a code attribute is one legit ~32KB line, and @edit / // multi-attribute uploads are legit bursts), unlike output. So this is // a high anti-runaway BACKSTOP, not a primary throttle: 16 max-size // lines, far above any interactive burst, but bounded. In practice the // read/drain cadence + TCP flow control already bound the app queue // (docs/survey-resource-defenses.md); the proper form is read-side // backpressure, deferred. <=0 disables. mudconf.input_limit = 16 * LBUF_SIZE; mudconf.paycheck = 0; mudconf.paystart = 0; mudconf.paylimit = 10000; #ifdef REALITY_LVLS mudconf.no_levels = 0; mudconf.def_room_rx = 1; mudconf.def_room_tx = ~static_cast(0); mudconf.def_player_rx = 1; mudconf.def_player_tx = 1; mudconf.def_exit_rx = 1; mudconf.def_exit_tx = 1; mudconf.def_thing_rx = 1; mudconf.def_thing_tx = 1; #endif // REALITY_LVLS mudconf.start_quota = 20; mudconf.site_chars = 25; mudconf.payfind = 0; mudconf.digcost = 10; mudconf.linkcost = 1; mudconf.opencost = 1; mudconf.createmin = 10; mudconf.createmax = 505; mudconf.killmin = 10; mudconf.killmax = 100; mudconf.killguarantee = 100; mudconf.robotcost = 1000; mudconf.pagecost = 10; mudconf.searchcost = 100; mudconf.waitcost = 10; mudconf.machinecost = 64; mudconf.exit_quota = 1; mudconf.player_quota = 1; mudconf.room_quota = 1; mudconf.thing_quota = 1; mudconf.mail_expiration = 14; mudconf.queuemax = 100; mudconf.queue_chunk = 10; mudconf.active_q_chunk = 10; mudconf.sacfactor = 5; mudconf.sacadjust = -1; mudconf.trace_limit = 200; mudconf.float_precision = -1; mudconf.autozone = true; mudconf.jit_code_slots = 7; // all slots (#2129); 1 = old behaviour mudconf.jit_compile_cache_max = 2048; // #2130: cover the measured // ~1500-program live working set mudconf.jit_eval_brackets = true; // Default ON (#1325, 2026-07-28). The gates: the differential // harness executes every supported shape (140 chunks, declines all // deliberate), loops run under an aborting instruction budget with // the interpreter's error surface, the compiled path wins or ties // every benched shape (#1741), and the intermittent interpreter // crash (#1433) closed unreproduced on three machines including at // its original commit. A chunk the compiler cannot honestly run // declines to the interpreter, so OFF buys nothing but the slower // path; set lua_jit=0 to diagnose a suspected compiled-path bug. mudconf.lua_jit = true; mudconf.use_hostname = true; mudconf.clone_copy_cost = false; mudconf.dark_sleepers = true; mudconf.ex_flags = true; mudconf.exam_public = true; mudconf.fascist_tport = false; mudconf.idle_wiz_dark = false; mudconf.match_mine = true; mudconf.match_mine_pl = true; mudconf.pemit_players = false; mudconf.pemit_any = false; mudconf.player_listen = false; mudconf.pub_flags = true; mudconf.quiet_look = true; mudconf.quiet_whisper = true; mudconf.quotas = false; mudconf.read_rem_desc = false; mudconf.read_rem_name = false; mudconf.reset_players = false; mudconf.robot_speak = true; mudconf.safe_unowned = false; mudconf.safer_passwords = false; mudconf.safer_iter = false; mudconf.see_own_dark = true; mudconf.sweep_dark = false; mudconf.switch_df_all = true; mudconf.talk_mode_default = false; mudconf.terse_contents = true; mudconf.terse_exits = true; mudconf.terse_look = true; mudconf.terse_movemsg = true; mudconf.trace_topdown = true; // -- ??? Running SC on a non-SC DB may cause problems. // mudconf.space_compress = true; mudconf.start_room = 0; mudconf.start_home = NOTHING; mudconf.default_home = NOTHING; mudconf.master_room = NOTHING; mudconf.exit_parent = NOTHING; mudconf.player_parent = NOTHING; mudconf.room_parent = NOTHING; mudconf.thing_parent = NOTHING; for (i = FLAG_WORD1; i <= FLAG_WORD3; i++) { mudconf.player_flags.word[i] = 0; mudconf.room_flags.word[i] = 0; mudconf.exit_flags.word[i] = 0; mudconf.thing_flags.word[i] = 0; mudconf.robot_flags.word[i] = 0; mudconf.stripped_flags.word[i] = 0; } mudconf.robot_flags.word[FLAG_WORD1] |= ROBOT; mudconf.stripped_flags.word[FLAG_WORD1] = IMMORTAL | INHERIT | ROYALTY | WIZARD; mudconf.stripped_flags.word[FLAG_WORD2] = BLIND | CONNECTED | GAGGED | HEAD_FLAG | SLAVE | STAFF | SUSPECT; mudconf.vattr_flags = AF_ODARK; mux_strncpy(mudconf.mud_name, T("MUX"), sizeof(mudconf.mud_name)-1); // Empty means "follow the environment" -- the behaviour every existing // netmux.conf gets, since none of them set this (#1702). // mudconf.language[0] = '\0'; mux_strncpy(mudconf.one_coin, T("penny"), sizeof(mudconf.one_coin)-1); mux_strncpy(mudconf.many_coins, T("pennies"), sizeof(mudconf.many_coins)-1); mudconf.timeslice.SetSeconds(1); mudconf.cmd_quota_max = 100; mudconf.cmd_quota_incr = 1; mudconf.rpt_cmdsecs.SetSeconds(120); mudconf.max_cmdsecs.SetSeconds(60); mudconf.cache_tick_period.SetSeconds(30); mudconf.control_flags = 0xffffffff; // Everything for now... mudconf.log_options = LOG_ALWAYS | LOG_BUGS | LOG_SECURITY | LOG_NET | LOG_LOGIN | LOG_DBSAVES | LOG_CONFIGMODS | LOG_SHOUTS | LOG_STARTUP | LOG_WIZARD | LOG_SUSPECTCMDS | LOG_PROBLEMS | LOG_PCREATES | LOG_TIMEUSE; mudconf.log_info = LOGOPT_TIMESTAMP | LOGOPT_LOC; mudconf.markdata[0] = 0x01; mudconf.markdata[1] = 0x02; mudconf.markdata[2] = 0x04; mudconf.markdata[3] = 0x08; mudconf.markdata[4] = 0x10; mudconf.markdata[5] = 0x20; mudconf.markdata[6] = 0x40; mudconf.markdata[7] = 0x80; mudconf.lua_instruction_limit = 100000; mudconf.lua_memory_limit = 1048576; mudconf.func_nest_lim = 500; mudconf.func_invk_lim = 25000; mudconf.wild_invk_lim = 100000; mudconf.ntfy_nest_lim = 20; mudconf.lock_nest_lim = 20; mudconf.parent_nest_lim = 10; mudconf.zone_nest_lim = 20; mudconf.cache_names = true; mudconf.toad_recipient = -1; mudconf.eval_comtitle = true; mudconf.run_startup = true; mudconf.safe_wipe = false; mudconf.destroy_going_now = false; mudconf.nStackLimit = 10000; mudconf.hook_obj = NOTHING; mudconf.help_executor = NOTHING; mudconf.global_error_obj = NOTHING; mudconf.mail_per_hour = 50; mudconf.mail_max_per_player = 500; mudconf.email_per_hour = 50; mudconf.vattr_per_hour = 5000; mudconf.vlimit = 1000; mudconf.references_per_hour = 500; mudconf.pcreate_per_hour = 100; mudconf.lbuf_size = LBUF_SIZE; mudconf.attr_name_charset = 0; mudconf.exit_name_charset = 0; mudconf.player_name_charset = 0; mudconf.room_name_charset = 0; mudconf.thing_name_charset = 0; mudconf.password_methods = CRYPT_DEFAULT; // Work factor for newly generated $5$/$6$ password hashes (sha-crypt // rounds=; clamped to [1000, 999999999] at use). The default targets a // ~50 ms per-hash budget on server-class hardware (measured ~50 ms at // 50,000 rounds on a 2.5 GHz Xeon) -- chosen against the single-threaded // login-path DoS surface described below rather than to match a raw // OWASP iteration count, and still 10x the sha-crypt spec default. // Changing this propagates automatically: the stored rounds ride in each // hash, and a hash whose rounds differ from this policy is re-encoded on // the player's next successful login. // // What password hashing here does and does not buy: it protects the // passwords themselves in at-rest copies of the database (backups, // flatfiles, decommissioned disks) against offline guessing -- players // reuse passwords elsewhere, and a hash must not leak them to whoever // can read a copy of the file, operators included. It does not and // cannot protect against a hostile operator of the RUNNING game, who // sits in the authentication path and sees cleartext at every connect. // // Verification recomputes at the STORED rounds, so every login costs // that work in the single-threaded server: at the default, a burst of // 100 reconnects after a restart serializes ~5 s of hashing. Raising it // buys offline-guessing margin at the cost of that latency (~50 ms per // 50,000 rounds); the spec default 5000 (~5 ms) is the low end. Any // standard value is equally portable across platforms. // mudconf.password_hash_rounds = 50000; mudconf.default_charset = CHARSET_LATIN1; mudstate.events_flag = 0; mudstate.bReadingConfiguration = false; mudstate.bCanRestart = false; mudstate.panicking = false; mudstate.asserting = 0; mudstate.logging = 0; mudstate.epoch = 0; mudstate.generation = 0; mudstate.curr_executor = NOTHING; mudstate.curr_enactor = NOTHING; mudstate.shutdown_flag = false; mudstate.attr_next = A_USER_START; g_debug_cmd = T("< init >"); mudstate.curr_cmd = T("< none >"); mux_strncpy(mudstate.doing_hdr, T("Doing"), sizeof(mudstate.doing_hdr)-1); mudstate.badname_head = nullptr; mudstate.attrperm_list = nullptr; mudstate.mstat_ixrss[0] = 0; mudstate.mstat_ixrss[1] = 0; mudstate.mstat_idrss[0] = 0; mudstate.mstat_idrss[1] = 0; mudstate.mstat_isrss[0] = 0; mudstate.mstat_isrss[1] = 0; mudstate.mstat_secs[0] = 0; mudstate.mstat_secs[1] = 0; mudstate.mstat_curr = 0; mudstate.attr_iter_ctx.attrs.clear(); mudstate.attr_iter_ctx.pos = 0; mudstate.attr_iter_stack.clear(); mudstate.olist = ObjectListStack(); mudstate.min_size = 0; mudstate.db_top = 0; mudstate.db_size = 0; mudstate.mail_db_top = 0; mudstate.mail_db_size = 0; mudstate.freelist = NOTHING; mudstate.markbits = nullptr; mudstate.func_nest_lev = 0; mudstate.func_invk_ctr = 0; mudstate.wild_invk_ctr = 0; mudstate.ntfy_nest_lev = 0; mudstate.include_nest_lev = 0; mudstate.train_nest_lev = 0; mudstate.lock_nest_lev = 0; mudstate.zone_nest_num = 0; mudstate.pipe_nest_lev = 0; mudstate.inpipe = false; mudstate.pout = nullptr; mudstate.poutnew = nullptr; mudstate.poutbufc = nullptr; mudstate.poutobj = NOTHING; for (i = 0; i < MAX_GLOBAL_REGS; i++) { mudstate.global_regs[i] = nullptr; } mudstate.named_regs = nullptr; mudstate.vattr_numbers.clear(); #if defined(STUB_SLAVE) mudstate.pResultsSet = nullptr; mudstate.iRow = RS_TOP; #endif // STUB_SLAVE mudstate.nObjEvalNest = 0; mudstate.in_loop = 0; mudstate.switch_token = nullptr; mudstate.bStackLimitReached = false; mudstate.nStackNest = 0; mudstate.nHearNest = 0; mudstate.aHelpDesc = nullptr; mudstate.mHelpDesc = 0; mudstate.nHelpDesc = 0; mudstate.pIQueryControl = nullptr; mudstate.pIComsysControl = nullptr; mudstate.pIMailControl = nullptr; mudstate.pILuaControl = nullptr; // Note: pIGameEngine and pIPlayerSession are set by the driver before // LoadGame() calls cf_init(). Do not clear them here. } // --------------------------------------------------------------------------- // cf_log_notfound: Log a 'parameter not found' error. // void cf_log_notfound(dbref player, const UTF8 *cmd, const UTF8 *thingname, const UTF8 *thing) { if (mudstate.bReadingConfiguration) { STARTLOG(LOG_STARTUP, "CNF", "NFND"); Log.tinyprintf(T("%s: %s %s not found"), cmd, thingname, thing); ENDLOG; } else { notify(player, tprintf(M_("%s %s not found"), thingname, thing)); } } // --------------------------------------------------------------------------- // cf_log_alreadyexists: Log a 'already exists' error. // void cf_log_alreadyexists(dbref player, const UTF8* cmd, const UTF8* thingname, const UTF8* thing) { if (mudstate.bReadingConfiguration) { STARTLOG(LOG_STARTUP, "CNF", "NFND"); Log.tinyprintf(T("%s: %s %s already exists"), cmd, thingname, thing); ENDLOG; } else { notify(player, tprintf(M_("%s %s already exists"), thingname, thing)); } } // --------------------------------------------------------------------------- // cf_log_syntax: Log a syntax error. // void DCL_CDECL cf_log_syntax(dbref player, UTF8 *cmd, const UTF8 *fmt, ...) { va_list ap; va_start(ap, fmt); LBuf buf = LBuf_Src("cf_log_syntax"); mux_vsnprintf(buf, LBUF_SIZE, fmt, ap); if (mudstate.bReadingConfiguration) { STARTLOG(LOG_STARTUP, "CNF", "SYNTX") log_text(cmd); log_text(T(": ")); log_text(buf); ENDLOG; } else { notify(player, buf); } va_end(ap); } // --------------------------------------------------------------------------- // cf_status_from_succfail: Return command status from succ and fail info // static int cf_status_from_succfail(dbref player, UTF8 *cmd, int success, int failure) { // If any successes, return SUCCESS(0) if no failures or // PARTIAL_SUCCESS(1) if any failures. // if (success > 0) return ((failure == 0) ? 0 : 1); // No successes. If no failures indicate nothing done. Always return // FAILURE(-1) // if (failure == 0) { if (mudstate.bReadingConfiguration) { STARTLOG(LOG_STARTUP, "CNF", "NDATA") LBuf buff = LBuf_Src("cf_status_from_succfail.LOG"); mux_sprintf(buff, LBUF_SIZE, T("%s: Nothing to set"), cmd); log_text(buff); ENDLOG } else { notify(player, M_("Nothing to set")); } } return -1; } //--------------------------------------------------------------------------- // cf_rlevel // #ifdef REALITY_LVLS CF_HAND(cf_rlevel) { CONFDATA *mc = reinterpret_cast(vp); int i; if (mc->no_levels >= 32) { return 1; } for (i=0; *str && !mux_isspace[*str]; ++str) { if (i < 8) { mc->reality_level[mc->no_levels].name[i++] = *str; } } mc->reality_level[mc->no_levels].name[i] = '\0'; mc->reality_level[mc->no_levels].value = 1; strcpy(reinterpret_cast(mc->reality_level[mc->no_levels].attr), "DESC"); for (; *str && mux_isspace[*str]; ++str) { ; // Nothing. } for (i=0; *str && mux_isdigit[*str]; ++str) { i = i * 10 + (*str - '0'); } if (i) { mc->reality_level[mc->no_levels].value = static_cast(i); } for (; *str && mux_isspace[*str]; ++str) { ; // Nothing. } if (*str) { strncpy(reinterpret_cast(mc->reality_level[mc->no_levels].attr), reinterpret_cast(str), 32); } mc->no_levels++; return 0; } #endif // REALITY_LVLS // --------------------------------------------------------------------------- // cf_int_array: Setup array of integers. // static CF_HAND(cf_int_array) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(player); UNUSED_PARAMETER(cmd); int *aPorts = nullptr; try { aPorts = new int[nExtra]; } catch (...) { ; // Nothing. } if (nullptr == aPorts) { cf_log_syntax(player, cmd, T("Out of memory.")); return -1; } unsigned int nPorts = 0; UTF8 *p; string_token st(str, T(" \t\n\r")); while ( (p = st.parse()) != nullptr && nPorts < nExtra) { int unused; if (is_integer(p, &unused)) { aPorts[nPorts++] = mux_atoi64(p); } } auto *pvec = reinterpret_cast*>(vp); if (nPorts) { pvec->assign(aPorts, aPorts + nPorts); } delete [] aPorts; return 0; } // --------------------------------------------------------------------------- // cf_int: Set integer parameter. // static CF_HAND(cf_int) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); UNUSED_PARAMETER(player); UNUSED_PARAMETER(cmd); // Copy the numeric value to the parameter. // *vp = mux_atoi64(str); return 0; } // --------------------------------------------------------------------------- // cf_live_driver_int: Set an integer that the driver reads from g_dc on the // accept/login hot path. Plain cf_int only updates mudconf; g_dc is a // boot-time snapshot via GetConfig. After a runtime @admin, re-pull the // basket so defense/session knobs (rate limits, pre-auth cap, nospam, // input/output_limit, timeouts, max_players, cmd_quota_max, retry_limit) // take effect immediately — matching pool_memory_limit's live push. // static CF_HAND(cf_live_driver_int) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); UNUSED_PARAMETER(player); UNUSED_PARAMETER(cmd); *vp = mux_atoi64(str); if ( !mudstate.bReadingConfiguration && nullptr != g_driver_config_sync_fn) { g_driver_config_sync_fn(); } return 0; } // cf_live_lua_int: Set an integer the Lua module holds a private copy of. // // Same defect as #1222 was for the driver basket, in a different subsystem: // SetLimits is pushed once, during module discovery in engine_com.cpp, and // CLuaMod keeps m_nInsnLimit / m_nMemLimit thereafter. A plain cf_int writes // mudconf, config() reads the new value back, @admin answers "Set." -- and // InsnCountHook goes on comparing against the boot-time number until a // restart. Nothing warns (#1613). // // These are the containment knobs for a sandbox softcode can enter, so the // realistic moment to change one is during an incident: somebody is hammering // lua(), staff tightens the limit, the server says Set., and nothing happens // until the restart the incident is trying to avoid. // // Push both, not just the one that changed: SetLimits takes the pair and // mudconf holds the current value of each, so re-sending both is free and // cannot leave the module holding a mix of old and new. // static CF_HAND(cf_live_lua_int) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); UNUSED_PARAMETER(player); UNUSED_PARAMETER(cmd); *vp = mux_atoi64(str); // bReadingConfiguration gates the boot-time parse, which runs before // module discovery -- pILuaControl is still null there, and discovery // does the initial push itself. // if ( !mudstate.bReadingConfiguration && nullptr != mudstate.pILuaControl) { mudstate.pILuaControl->SetLimits( mudconf.lua_instruction_limit, mudconf.lua_memory_limit); } return 0; } // --------------------------------------------------------------------------- // cf_size: Set a size parameter with optional K/M/G suffix. // -1 means unlimited. // static CF_HAND(cf_size) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); UNUSED_PARAMETER(player); UNUSED_PARAMETER(cmd); int64_t *pSize = reinterpret_cast(vp); // Handle -1 (unlimited) directly. // UTF8 *p = str; while (mux_isspace(*p)) p++; if (p[0] == '-' && p[1] == '1' && (p[2] == '\0' || mux_isspace(p[2]))) { *pSize = -1; return 0; } // Reject negative values other than -1. // if (*p == '-') { return -1; } // mux_atoi64, not mux_atol: long is 32-bit on LLP64 (Windows), so a raw // byte count at or above 2^31 was truncated before the suffix multiplier // could be applied. Values in [2^31, 2^32) came out NEGATIVE, and every // consumer reads a negative size as "unlimited" (attrcache.cpp:506) -- // so `max_cache_size 3000000000` silently uncapped the cache instead of // capping it at 3 GB (#1373). // int64_t val = mux_atoi64(p); // Find the suffix character. // while (mux_isdigit(*p) || *p == '+') p++; while (mux_isspace(*p)) p++; switch (*p) { case 'k': case 'K': val *= 1024LL; break; case 'm': case 'M': val *= 1024LL * 1024; break; case 'g': case 'G': val *= 1024LL * 1024 * 1024; break; case '\0': break; default: return -1; } *pSize = val; return 0; } // --------------------------------------------------------------------------- // cf_pool_limit: Set the pool memory budget (bytes, K/M/G suffixes; 0 or -1 // = unlimited) and push it to the libmux allocator (g_pool_limit_bytes) so it // stays in sync on initial load and @readcache reload. (#pool-oom) // static CF_HAND(cf_pool_limit) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); int64_t v = 0; int rc = cf_size(reinterpret_cast(&v), str, nullptr, 0, player, cmd); if (rc != 0) { return rc; } if (v < 0) { v = 0; // -1 (unlimited) normalizes to 0 = off } *reinterpret_cast(vp) = v; g_pool_limit_bytes = static_cast(v); return 0; } // Defined below, next to cf_bool/cf_string/cf_option which also use it. // Declared here because cf_dbref and cf_seconds precede that definition. // static void live_sync_driver_config(void); // --------------------------------------------------------------------------- // cf_dbref: Set dbref parameter....looking for an ignoring the leading '#'. // static CF_HAND(cf_dbref) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); UNUSED_PARAMETER(player); UNUSED_PARAMETER(cmd); UTF8 *p = str; while (mux_isspace(*p)) { p++; } if (*p == '#') { p++; } // Copy the numeric value to the parameter. // *vp = mux_atoi64(p); // guest_char_num and player_starting_room are both in the driver basket // and read live (the guest-login gate and where a newly created player is // moved), so re-pull it here for the same reason as cf_seconds (#1222). // live_sync_driver_config(); return 0; } // --------------------------------------------------------------------------- // cf_seconds: Set CLinearTimeDelta in units of seconds. // static CF_HAND(cf_seconds) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); UNUSED_PARAMETER(player); UNUSED_PARAMETER(cmd); auto* pltd = reinterpret_cast(vp); pltd->SetSecondsString(str); // lag_limit, lag_maximum and timeslice all land in the driver basket and // are read live by net.cpp (the alarm-clock watchdog, the lag report // threshold, and command deferral). Without this the runtime @admin // updated mudconf -- so config() reported the new value -- while the // driver kept using the boot-time one until restart (#1222). // live_sync_driver_config(); return 0; } // --------------------------------------------------------------------------- // cf_bool: Set boolean parameter. // static NAMETAB bool_names[] = { {T("true"), 1, 0, true}, {T("false"), 1, 0, false}, {T("yes"), 1, 0, true}, {T("no"), 1, 0, false}, {T("1"), 1, 0, true}, {T("0"), 1, 0, false}, {static_cast(nullptr), 0, 0, 0} }; // #1085: after runtime conf changes that may affect g_dc, re-pull the // driver basket (same as cf_live_driver_int). // static void live_sync_driver_config(void) { if ( !mudstate.bReadingConfiguration && nullptr != g_driver_config_sync_fn) { g_driver_config_sync_fn(); } } static CF_HAND(cf_bool) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); int i; if (!search_nametab(GOD, bool_names, str, &i)) { cf_log_notfound(player, cmd, T("Value"), str); return -1; } const auto pb = reinterpret_cast(vp); *pb = isTRUE(i); live_sync_driver_config(); return 0; } // --------------------------------------------------------------------------- // cf_option: Select one option from many choices. // static CF_HAND(cf_option) { UNUSED_PARAMETER(nExtra); int i; if (!search_nametab(GOD, static_cast(pExtra), str, &i)) { cf_log_notfound(player, cmd, T("Value"), str); return -1; } *vp = i; live_sync_driver_config(); return 0; } // --------------------------------------------------------------------------- // cf_string: Set string parameter. // static CF_HAND(cf_string) { UNUSED_PARAMETER(pExtra); auto pc = reinterpret_cast(vp); // The following should never happen because extra is always a non-zero // constant in the config table. // if (nExtra <= 0) { return 1; } // Copy the string to the buffer if it is not too big. // int retval = 0; size_t nStr = strlen(reinterpret_cast(str)); if (nStr >= nExtra) { nStr = nExtra - 1; if (mudstate.bReadingConfiguration) { STARTLOG(LOG_STARTUP, "CNF", "NFND"); Log.tinyprintf(T("%s: String truncated"), cmd); ENDLOG; } else { notify(player, M_("String truncated")); } retval = 1; } memcpy(pc, str, nStr+1); pc[nStr] = '\0'; if (pc == mudconf.mud_name) { // We are changing the name of the MUD. Form a prefix from the // mudname and let the logger know. // UTF8 *buff = alloc_sbuf("cf_string.prefix"); UTF8 *p = buff; UTF8 *q = strip_color(mudconf.mud_name); size_t nLen = 0; while ( *q && nLen < SBUF_SIZE) { if (mux_isalnum(*q)) { *p++ = *q; nLen++; } q++; } *p = '\0'; Log.SetPrefix(buff); free_sbuf(buff); } live_sync_driver_config(); return retval; } // --------------------------------------------------------------------------- // cf_string_dyn: Set string parameter using dynamically allocated memory. // static CF_HAND(cf_string_dyn) { UNUSED_PARAMETER(pExtra); const auto ppc = reinterpret_cast(vp); // Allocate memory for buffer and copy string to it. If nExtra is non-zero, // then there is a size limitation as well. // int retval = 0; size_t nStr = strlen(reinterpret_cast(str)); if (nExtra && nStr >= nExtra) { nStr = nExtra - 1; if (mudstate.bReadingConfiguration) { STARTLOG(LOG_STARTUP, "CNF", "NFND"); Log.tinyprintf(T("%s: String truncated"), cmd); ENDLOG; } else { notify(player, M_("String truncated")); } retval = 1; } UTF8 *confbuff = StringCloneLen(str, nStr); // Free previous memory for buffer. // if (*ppc != nullptr) { MEMFREE(*ppc); } *ppc = confbuff; return retval; } // --------------------------------------------------------------------------- // cf_alias: define a generic hash table alias. // static CF_HAND(cf_alias) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); string_token st(str, T(" \t=,")); const UTF8* alias = st.parse(); UTF8* orig = st.parse(); if (orig) { size_t nCased; const UTF8 *pCased = mux_strupr(orig, nCased); auto pMap = reinterpret_cast(vp); auto it = pMap->find(std::vector(pCased, pCased + nCased)); if (it == pMap->end()) { cf_log_notfound(player, cmd, T("Entry"), orig); return -1; } void *cp = it->second; pCased = mux_strupr(alias, nCased); if (pMap->find(std::vector(pCased, pCased + nCased)) == pMap->end()) { pMap->emplace(std::vector(pCased, pCased + nCased), cp); } return 0; } return -1; } static CF_HAND(cf_attr_name_alias) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); string_token st(str, T(" \t=,")); const UTF8* alias = st.parse(); UTF8* orig = st.parse(); if (orig) { size_t nCased; const UTF8* pCased = mux_strupr(orig, nCased); const vector v(pCased, pCased + nCased); const auto it = mudstate.builtin_attribute_names.find(v); if (it == mudstate.builtin_attribute_names.end()) { cf_log_notfound(player, cmd, T("Entry"), orig); return -1; } pCased = mux_strupr(alias, nCased); const vector alias_vector(pCased, pCased + nCased); const auto it2 = mudstate.builtin_attribute_names.find(alias_vector); if (it == mudstate.builtin_attribute_names.end()) { mudstate.builtin_attribute_names.insert(make_pair(alias_vector, it->second)); } return 0; } return -1; } // cf_pronoun_group: Define a custom pronoun set. // // Syntax: pronoun_group ,,, [plural] // static CF_HAND(cf_pronoun_group) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); string_token st(str, T(" \t")); UTF8 *pName = st.parse(); UTF8 *pPronouns = st.parse(); if ( !pName || !pPronouns) { cf_log_syntax(player, cmd, T("Expected: pronoun_group ,,, [plural]")); return -1; } // Parse comma-separated pronouns. // string_token st2(pPronouns, T(",")); UTF8 *pSubj = st2.parse(); UTF8 *pObj = st2.parse(); UTF8 *pPoss = st2.parse(); UTF8 *pAposs = st2.parse(); if ( !pSubj || !pObj || !pPoss || !pAposs) { cf_log_syntax(player, cmd, T("Expected four comma-separated pronouns: ,,,")); return -1; } // Check for optional 'plural' keyword. // UTF8 *pPlural = st.parse(); bool bPlural = false; if ( pPlural && mux_stricmp(pPlural, T("plural")) == 0) { bPlural = true; } // Uppercase the name for lookup key. // size_t nCased; const UTF8 *pCased = mux_strupr(pName, nCased); vector vKey(pCased, pCased + nCased); // Check if this group name already exists. // auto it = mudstate.pronoun_groups.find(vKey); PRONOUN_SET *ps; if (it != mudstate.pronoun_groups.end()) { ps = it->second; } else { try { ps = new PRONOUN_SET; } catch (...) { return -1; } mudstate.pronoun_groups.insert(make_pair(vKey, ps)); } mux_strncpy(ps->subjective, pSubj, sizeof(ps->subjective) - 1); mux_strncpy(ps->objective, pObj, sizeof(ps->objective) - 1); mux_strncpy(ps->possessive, pPoss, sizeof(ps->possessive) - 1); mux_strncpy(ps->absolute, pAposs, sizeof(ps->absolute) - 1); ps->plural = bPlural; return 0; } static int function_name_helper(const dbref player, const UTF8* cmd, const UTF8* existing_name, const UTF8* new_name, bool delete_existing) { // Existing function name should exist. // size_t uppercase_length; const UTF8* uppercase = mux_strupr(existing_name, uppercase_length); const vector existing_name_vector(uppercase, uppercase + uppercase_length); const auto it_existing = mudstate.builtin_functions.find(existing_name_vector); if (it_existing == mudstate.builtin_functions.end()) { cf_log_notfound(player, cmd, T("Entry"), existing_name); return -1; } // New function name should not exist. // uppercase = mux_strupr(new_name, uppercase_length); vector new_function_name(uppercase, uppercase + uppercase_length); const auto it_new = mudstate.builtin_functions.find(new_function_name); if (it_new != mudstate.builtin_functions.end()) { cf_log_alreadyexists(player, cmd, T("Entry"), new_name); return -1; } mudstate.builtin_functions.insert(make_pair(new_function_name, it_existing->second)); if (delete_existing) { mudstate.builtin_functions.erase(it_existing); } jit_gate_note_function_table_change(); // (#2068) return 0; } // --------------------------------------------------------------------------- // cf_function_name: rename a function // static CF_HAND(cf_function_name) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); string_token st(str, T(" \t=,")); const UTF8* old_name = st.parse(); UTF8* new_name = st.parse(); if (!new_name) { cf_log_syntax(player, cmd, T("function_name oldname newname : %s"), str); return -1; } return function_name_helper(player, cmd, old_name, new_name, true); } // --------------------------------------------------------------------------- // cf_function_alias: create an alias for function // static CF_HAND(cf_function_alias) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); string_token st(str, T(" \t=,")); const UTF8* function_alias = st.parse(); UTF8* function_name = st.parse(); if (!function_name) { cf_log_syntax(player, cmd, T("function_alias alias funcname : %s"), str); return -1; } return function_name_helper(player, cmd, function_name, function_alias, false); } // --------------------------------------------------------------------------- // cf_flagalias: define a flag alias. // static CF_HAND(cf_flagalias) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); string_token st(str, T(" \t=,")); UTF8 *alias = st.parse(); UTF8 *orig = st.parse(); if ( nullptr == alias || '\0' == alias[0] || nullptr == orig || '\0' == orig[0]) { return -1; } bool success = false; int name_length; bool valid; UTF8 *name = MakeCanonicalFlagName(orig, &name_length, &valid); if (valid) { const vector original_name(name, name + name_length); const auto it_original = mudstate.flag_names_map.find(original_name); if (it_original != mudstate.flag_names_map.end()) { name = MakeCanonicalFlagName(alias, &name_length, &valid); vector alias_name(name, name + name_length); if (valid) { const auto it_alias_name = mudstate.flag_names_map.find(alias_name); if (it_alias_name == mudstate.flag_names_map.end()) { mudstate.flag_names_map.insert(make_pair(alias_name, it_original->second)); success = true; } } } } if (!success) { cf_log_notfound(player, cmd, T("Flag"), orig); } return (success ? 0 : -1); } // --------------------------------------------------------------------------- // cf_poweralias: define a power alias. // static CF_HAND(cf_poweralias) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); string_token st(str, T(" \t=,")); UTF8 *alias = st.parse(); UTF8* orig = st.parse(); if ( nullptr == alias || '\0' == alias[0] || nullptr == orig || '\0' == orig[0]) { return -1; } bool success = false; int nName; bool bValid; void *cp; UTF8 *pName = MakeCanonicalFlagName(orig, &nName, &bValid); if (bValid) { auto it = mudstate.powers_htab.find(std::vector(pName, pName + nName)); if (it != mudstate.powers_htab.end()) { cp = it->second; pName = MakeCanonicalFlagName(alias, &nName, &bValid); if (bValid) { mudstate.powers_htab.emplace(std::vector(pName, pName + nName), cp); success = true; } } } if (!success) { cf_log_notfound(player, cmd, T("Power"), orig); } return (success ? 0 : -1); } // --------------------------------------------------------------------------- // cf_modify_bits: set or clear bits in a flag word from a namelist. // CF_HAND(cf_modify_bits) { UNUSED_PARAMETER(nExtra); int f, success, failure; bool negate; // Walk through the tokens. // success = failure = 0; string_token st(str, T(" \t")); UTF8 *sp = st.parse(); while (sp != nullptr) { // Check for negation. // negate = false; if (*sp == '!') { negate = true; sp++; } // Set or clear the appropriate bit. // if (search_nametab(GOD, reinterpret_cast(pExtra), sp, &f)) { if (negate) *vp &= ~f; else *vp |= f; success++; } else { cf_log_notfound(player, cmd, T("Entry"), sp); failure++; } // Get the next token. // sp = st.parse(); } return cf_status_from_succfail(player, cmd, success, failure); } // --------------------------------------------------------------------------- // cf_set_flags: Clear flag word and then set from a flags htab. // static CF_HAND(cf_set_flags) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); int failure; int success = failure = 0; // Walk through the tokens. // string_token st(str, T(" \t")); UTF8 *sp = st.parse(); const auto flag_set = reinterpret_cast(vp); while (sp != nullptr) { // Canonical Flag Name. // int flag_name_length; bool valid; UTF8 *flag_name = MakeCanonicalFlagName(sp, &flag_name_length, &valid); if (valid) { vector flagname(flag_name, flag_name + flag_name_length); auto it = mudstate.flag_names_map.find(flagname); if (it != mudstate.flag_names_map.end()) { const FLAGNAMEENT* flag_name_entry = it->second; // Set the appropriate bit. // if (success == 0) { for (int i = FLAG_WORD1; i <= FLAG_WORD3; i++) { (*flag_set).word[i] = 0; } } FLAGBITENT* flag_bit_entity = flag_name_entry->fbe; if (flag_name_entry->bPositive) { (*flag_set).word[flag_bit_entity->flagflag] |= flag_bit_entity->flagvalue; } else { (*flag_set).word[flag_bit_entity->flagflag] &= ~(flag_bit_entity->flagvalue); } success++; } else { cf_log_notfound(player, cmd, T("Entry"), sp); failure++; } } // Get the next token // sp = st.parse(); } if ((success == 0) && (failure == 0)) { for (int i = FLAG_WORD1; i <= FLAG_WORD3; i++) { (*flag_set).word[i] = 0; } return 0; } if (success > 0) { return ((failure == 0) ? 0 : 1); } return -1; } // --------------------------------------------------------------------------- // cf_badname: Disallow use of player name/alias. // static CF_HAND(cf_badname) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(player); UNUSED_PARAMETER(cmd); if (nExtra) { badname_remove(str); } else { badname_add(str); } return 0; } // --------------------------------------------------------------------------- // cf_site: Update site information static CF_HAND(cf_site) { UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(vp); // The access list lives in the driver. Route the update through COM. // return site_update(str, player, cmd, nExtra); } // --------------------------------------------------------------------------- // cf_helpfile, cf_raw_helpfile: Add help files and their corresponding // command. // static int add_helpfile(dbref player, UTF8 *cmd, UTF8 *str, bool bEval) { // Parse the two arguments. // string_token st(str, T(" \t\n\r")); UTF8 *pCmdName = st.parse(); UTF8 *pBase = st.parse(); if (pBase == nullptr) { cf_log_syntax(player, cmd, T("Missing path for helpfile %s"), pCmdName); return -1; } if ( pCmdName[0] == '_' && pCmdName[1] == '_') { cf_log_syntax(player, cmd, T("Helpfile %s would conflict with the use of @addcommand."), pCmdName); return -1; } if (SBUF_SIZE <= strlen(reinterpret_cast(pBase))) { cf_log_syntax(player, cmd, T("Helpfile ‘%s’ filename too long"), pBase); return -1; } // Allocate an empty place in the table of help file hashes. // if (mudstate.aHelpDesc == nullptr) { mudstate.mHelpDesc = 4; mudstate.nHelpDesc = 0; try { mudstate.aHelpDesc = new HELP_DESC[mudstate.mHelpDesc]; } catch (...) { ; // Nothing. } if (nullptr == mudstate.aHelpDesc) { cf_log_syntax(player, cmd, T("Out of memory.")); return -1; } } else if (mudstate.mHelpDesc <= mudstate.nHelpDesc) { int newsize = mudstate.mHelpDesc + 4; HELP_DESC *q = nullptr; try { q = new HELP_DESC[newsize]; } catch (...) { ; // Nothing. } if (nullptr == mudstate.aHelpDesc) { cf_log_syntax(player, cmd, T("Out of memory.")); return -1; } memset(q, 0, sizeof(HELP_DESC)*newsize); memcpy(q, mudstate.aHelpDesc, sizeof(HELP_DESC)*mudstate.mHelpDesc); delete [] mudstate.aHelpDesc; mudstate.aHelpDesc = q; mudstate.mHelpDesc = newsize; } // Build HELP_DESC // HELP_DESC *pDesc = mudstate.aHelpDesc + mudstate.nHelpDesc; pDesc->CommandName = StringClone(pCmdName); pDesc->ht = nullptr; pDesc->pBaseFilename = StringClone(pBase); pDesc->bEval = bEval; // Build up Command Entry. // CMDENT_ONE_ARG *cmdp = nullptr; try { cmdp = new CMDENT_ONE_ARG; } catch (...) { ; // Nothing. } if (nullptr != cmdp) { cmdp->callseq = CS_ONE_ARG; cmdp->cmdname = StringClone(pCmdName); cmdp->extra = mudstate.nHelpDesc; cmdp->handler = do_help; cmdp->flags = CEF_ALLOC; cmdp->perms = CA_PUBLIC; cmdp->switches = nullptr; // Free any previously-allocated command at the same key. // const UTF8 *p = cmdp->cmdname; size_t nLen = strlen(reinterpret_cast(p)); std::vector key(p, p + nLen); auto it = mudstate.command_htab.find(key); if (it != mudstate.command_htab.end()) { CMDENT *old = static_cast(it->second); if (old && (old->flags & CEF_ALLOC)) { MEMFREE(old->cmdname); old->cmdname = nullptr; MEMFREE(old); } } mudstate.command_htab.erase(key); mudstate.command_htab.emplace(key, cmdp); p = tprintf(T("__%s"), cmdp->cmdname); nLen = strlen(reinterpret_cast(p)); std::vector key2(p, p + nLen); it = mudstate.command_htab.find(key2); if (it != mudstate.command_htab.end()) { CMDENT *old = static_cast(it->second); if (old && (old->flags & CEF_ALLOC)) { MEMFREE(old->cmdname); old->cmdname = nullptr; MEMFREE(old); } } mudstate.command_htab.erase(key2); mudstate.command_htab.emplace(key2, cmdp); } else { STARTLOG(LOG_PROBLEMS, "HLP", "MEM"); log_printf(T("cf_helpfile_add: out of memory registering help command.")); ENDLOG; return -1; } mudstate.nHelpDesc++; return 0; } static CF_HAND(cf_helpfile) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); return add_helpfile(player, cmd, str, true); } static CF_HAND(cf_raw_helpfile) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); return add_helpfile(player, cmd, str, false); } // @hook: run softcode before or after running a hardcode command, or softcode access. // Original idea from TinyMUSH 3, code from RhostMUSH. // Used with express permission of RhostMUSH developers. // Bludgeoned into MUX by Jake Nelson 7/2002. // static NAMETAB hook_names[] = { {T("after"), 3, 0, CEF_HOOK_AFTER}, {T("before"), 3, 0, CEF_HOOK_BEFORE}, {T("fail"), 3, 0, CEF_HOOK_AFAIL}, {T("ignore"), 3, 0, CEF_HOOK_IGNORE}, {T("igswitch"), 3, 0, CEF_HOOK_IGSWITCH}, {T("permit"), 3, 0, CEF_HOOK_PERMIT}, {T("args"), 3, 0, CEF_HOOK_ARGS}, {nullptr, 0, 0, 0} }; static CF_HAND(cf_hook) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); UNUSED_PARAMETER(player); UNUSED_PARAMETER(cmd); UTF8 *hookcmd, *hookptr, playbuff[201]; int hookflg; CMDENT *cmdp; int retval = -1; memset(playbuff, '\0', sizeof(playbuff)); mux_strncpy(playbuff, str, sizeof(playbuff)-1); string_token st(playbuff, T(" \t")); hookcmd = st.parse(); if (hookcmd != nullptr) { { size_t nHookCmd = strlen(reinterpret_cast(hookcmd)); auto it = mudstate.command_htab.find(std::vector(hookcmd, hookcmd + nHookCmd)); cmdp = (it != mudstate.command_htab.end()) ? static_cast(it->second) : nullptr; } } else { return retval; } if (!cmdp) { return retval; } int t = cmdp->flags; mux_strncpy(playbuff, str, sizeof(playbuff)-1); hookptr = st.parse(); while (hookptr != nullptr) { if ( hookptr[0] == '!' && hookptr[1] != '\0') { if (search_nametab(GOD, hook_names, hookptr+1, &hookflg)) { retval = 0; t = t & ~hookflg; } } else { if (search_nametab(GOD, hook_names, hookptr, &hookflg)) { retval = 0; t = t | hookflg; } } hookptr = st.parse(); } cmdp->flags = t; return retval; } static CF_HAND(cf_module) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); string_token st(str, T(" \t")); UTF8 *modname = st.parse(); UTF8 *modtype = st.parse(); enum MODTYPE { eInProc = 0, eLocal } eType = eInProc; if (nullptr == modname) { cf_log_syntax(player, cmd, T("Module name is missing.")); return -1; } if (nullptr == modtype) { eType = eInProc; } else if (strcmp(reinterpret_cast(modtype), "inproc") == 0) { eType = eInProc; } else if ( strcmp(reinterpret_cast(modtype), "local") == 0 || strcmp(reinterpret_cast(modtype), "slave") == 0) { eType = eLocal; } else { cf_log_syntax(player, cmd, T("load type is invalid.")); return -1; } #if !defined(STUB_SLAVE) if (eLocal == eType) { cf_log_syntax(player, cmd, T("StubSlave is not enabled.")); return -1; } #endif // !STUB_SLAVE #if defined(STUB_SLAVE) mux_ISlaveControl *pISlaveControl = nullptr; if (eLocal == eType) { MUX_RESULT mrSlave = mux_CreateInstance(CID_StubSlave, nullptr, UseSlaveProcess, IID_ISlaveControl, (void **)&pISlaveControl); if (MUX_FAILED(mrSlave) || nullptr == pISlaveControl) { cf_log_syntax(player, cmd, T("No StubSlave management interface available.")); return -1; } } #endif // STUB_SLAVE MUX_RESULT mr = MUX_S_OK; if ('!' == str[0]) { if (eInProc == eType) { mr = mux_RemoveModule(str+1); } #if defined(STUB_SLAVE) else { mr = pISlaveControl->RemoveModule(str+1); } #endif // STUB_SLAVE } else { LBuf buffer = LBuf_Src("cf_module"); bool bHaveFilename = true; #if defined(WINDOWS_FILES) size_t n; mux_sprintf(buffer, LBUF_SIZE, T(".\\bin\\%s.dll"), str); const UTF16 *filename = ConvertFromUTF8ToUTF16(buffer, n); if (nullptr == filename) { bHaveFilename = false; mr = MUX_E_FAIL; } #elif defined(UNIX_FILES) mux_sprintf(buffer, LBUF_SIZE, T("./bin/%s.so"), str); UTF8 *filename = buffer; #endif if ( bHaveFilename && eInProc == eType) { mr = mux_AddModule(str, filename); } #if defined(STUB_SLAVE) else if (bHaveFilename) { mr = pISlaveControl->AddModule(str, filename); } #endif // STUB_SLAVE } #if defined(STUB_SLAVE) if (nullptr != pISlaveControl) { pISlaveControl->Release(); pISlaveControl = nullptr; } #endif // STUB_SLAVE if (MUX_FAILED(mr)) { cf_log_notfound(player, cmd, T("Module"), str); return -1; } else { return 0; } } // --------------------------------------------------------------------------- // cf_include: Read another config file. Only valid during startup. // static CF_HAND(cf_include) { UNUSED_PARAMETER(vp); UNUSED_PARAMETER(pExtra); UNUSED_PARAMETER(nExtra); if (!mudstate.bReadingConfiguration) { return -1; } FILE *fp; if (!mux_fopen(&fp, str, T("rb"))) { cf_log_notfound(player, cmd, T("Config file"), str); return -1; } LBuf buf = LBuf_Src("cf_include"); if (nullptr == fgets(reinterpret_cast(buf.get()), LBUF_SIZE, fp)) { // Distinguish "nothing to read" from "could not read" (#1601). // fopen succeeds on a directory on both Linux and macOS, and the // read then fails with EISDIR -- so `-c somedir` used to look // exactly like an empty file and be silently accepted, leaving the // game on compiled-in defaults. An empty regular file sets feof // with no error and stays deliberately valid: it is the supported // way to ask for the defaults on purpose. // bool bReadFailed = (0 != ferror(fp)); // The original early return here skipped the fclose that the normal // path below does, leaking the handle for every empty include. // mux_fclose(fp); if (bReadFailed) { cf_log_notfound(player, cmd, T("Readable config file"), str); return -1; } return 0; } while (!feof(fp)) { UTF8 *zp = buf; // Remove comments. Anything after the '#' is a comment except if it // matches: whitespace + '#' + digit. // while (*zp != '\0') { if ( *zp == '#' && ( zp <= buf || !mux_isspace(zp[-1]) || !mux_isdigit(zp[1]))) { // Found a comment. // *zp = '\0'; } else { zp++; } } // Trim trailing spaces. // while ( buf < zp && mux_isspace(zp[-1])) { *(--zp) = '\0'; } // Process line. // UTF8 *cp = buf; // Trim leading spaces. // while (mux_isspace(*cp)) { cp++; } // Skip over command. // UTF8 *ap; for (ap = cp; *ap && !mux_isspace(*ap); ap++) { ; // Nothing. } // Terminate command. // if (*ap) { *ap++ = '\0'; } // Skip spaces between command and argument. // while (mux_isspace(*ap)) { ap++; } if (*cp) { cf_set(cp, ap, player); } if (nullptr == fgets(reinterpret_cast(buf.get()), LBUF_SIZE, fp)) { break; } } mux_fclose(fp); return 0; } // --------------------------------------------------------------------------- // conftable: Table for parsing the configuration file. static CONFPARM conftable[] = { {T("access"), cf_access, CA_GOD, CA_DISABLED, nullptr, access_nametab, 0}, {T("alias"), cf_cmd_alias, CA_GOD, CA_DISABLED, reinterpret_cast(&mudstate.command_htab), 0, 0}, {T("attr_access"), cf_attr_access, CA_GOD, CA_DISABLED, reinterpret_cast(&mudstate.attrperm_list), attraccess_nametab, 0}, {T("attr_alias"), cf_attr_name_alias,CA_GOD, CA_DISABLED, nullptr, nullptr, 0}, {T("attr_cmd_access"), cf_acmd_access, CA_GOD, CA_DISABLED, nullptr, access_nametab, 0}, {T("attr_name_charset"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.attr_name_charset, allow_charset_nametab, 0}, {T("autozone"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.autozone), nullptr, 0}, {T("bad_name"), cf_badname, CA_GOD, CA_DISABLED, nullptr, nullptr, 0}, {T("badsite_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.site_file), nullptr, SIZEOF_PATHNAME}, {T("cache_names"), cf_bool, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.cache_names), nullptr, 0}, {T("cache_tick_period"), cf_seconds, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.cache_tick_period), nullptr, 0}, {T("cache_max_size"), cf_size, CA_GOD, CA_GOD, reinterpret_cast(&mudconf.max_cache_size), nullptr, 0}, {T("cache_preload_depth"), cf_int, CA_GOD, CA_GOD, &mudconf.cache_preload_depth, nullptr, 0}, {T("check_interval"), cf_int, CA_GOD, CA_WIZARD, &mudconf.check_interval, nullptr, 0}, {T("check_offset"), cf_int, CA_GOD, CA_WIZARD, &mudconf.check_offset, nullptr, 0}, {T("clone_copies_cost"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.clone_copy_cost), nullptr, 0}, {T("command_quota_increment"), cf_int, CA_GOD, CA_WIZARD, &mudconf.cmd_quota_incr, nullptr, 0}, {T("command_quota_max"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.cmd_quota_max, nullptr, 0}, {T("comsys_database"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.comsys_db), nullptr, SIZEOF_PATHNAME}, {T("config_access"), cf_cf_access, CA_GOD, CA_DISABLED, nullptr, access_nametab, 0}, {T("conn_timeout"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.conn_timeout, nullptr, 0}, {T("connect_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.conn_file), nullptr, SIZEOF_PATHNAME}, {T("connect_help_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.conn_help_file), nullptr, SIZEOF_PATHNAME}, {T("connect_reg_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.creg_file), nullptr, SIZEOF_PATHNAME}, {T("lag_limit"), cf_seconds, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.max_cmdsecs), nullptr, 0}, {T("crash_database"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.crashdb), nullptr, SIZEOF_PATHNAME}, {T("crash_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast(mudconf.crash_msg), nullptr, GBUF_SIZE}, {T("create_max_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.createmax, nullptr, 0}, {T("create_min_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.createmin, nullptr, 0}, {T("dark_sleepers"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.dark_sleepers), nullptr, 0}, {T("default_charset"), cf_option, CA_GOD, CA_PUBLIC, &mudconf.default_charset, default_charset_nametab, 0}, {T("default_home"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.default_home, nullptr, 0}, {T("destroy_going_now"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.destroy_going_now), nullptr, 0}, {T("dig_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.digcost, nullptr, 0}, {T("down_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.down_file), nullptr, SIZEOF_PATHNAME}, {T("down_motd_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast(mudconf.downmotd_msg), nullptr, GBUF_SIZE}, {T("dump_interval"), cf_int, CA_GOD, CA_WIZARD, &mudconf.dump_interval, nullptr, 0}, {T("dump_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast(mudconf.dump_msg), nullptr, 256}, {T("dump_offset"), cf_int, CA_GOD, CA_WIZARD, &mudconf.dump_offset, nullptr, 0}, {T("earn_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.paylimit, nullptr, 0}, {T("eval_comtitle"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.eval_comtitle), nullptr, 0}, {T("events_daily_hour"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.events_daily_hour, nullptr, 0}, {T("examine_flags"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.ex_flags), nullptr, 0}, {T("examine_public_attrs"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.exam_public), nullptr, 0}, {T("exit_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast(&mudconf.exit_flags), nullptr, 0}, {T("exit_name_charset"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.exit_name_charset, allow_charset_nametab, 0}, {T("exit_quota"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.exit_quota, nullptr, 0}, {T("exit_parent"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.exit_parent, nullptr, 0}, {T("fascist_teleport"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.fascist_tport), nullptr, 0}, {T("find_money_chance"), cf_int, CA_GOD, CA_WIZARD, &mudconf.payfind, nullptr, 0}, {T("fixed_home_message"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast(mudconf.fixed_home_msg), nullptr, 128}, {T("fixed_tel_message"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast(mudconf.fixed_tel_msg), nullptr, 128}, {T("flag_access"), cf_flag_access, CA_GOD, CA_DISABLED, nullptr, nullptr, 0}, {T("flag_alias"), cf_flagalias, CA_GOD, CA_DISABLED, nullptr, nullptr, 0}, {T("flag_name"), cf_flag_name, CA_GOD, CA_DISABLED, nullptr, nullptr, 0}, {T("float_precision"), cf_int, CA_STATIC, CA_PUBLIC, &mudconf.float_precision, nullptr, 0}, {T("forbid_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_FORBID}, #if defined(HAVE_WORKING_FORK) {T("fork_dump"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.fork_dump), nullptr, 0}, #endif // HAVE_WORKING_FORK {T("full_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.full_file), nullptr, SIZEOF_PATHNAME}, {T("full_motd_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast(mudconf.fullmotd_msg), nullptr, GBUF_SIZE}, {T("function_access"), cf_func_access, CA_GOD, CA_DISABLED, nullptr, access_nametab, 0}, {T("function_alias"), cf_function_alias,CA_GOD, CA_DISABLED, nullptr, nullptr, 0}, {T("function_invocation_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.func_invk_lim, nullptr, 0}, {T("function_name"), cf_function_name, CA_GOD, CA_DISABLED, nullptr, nullptr, 0}, {T("function_recursion_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.func_nest_lim, nullptr, 0}, {T("global_error_obj"), cf_dbref, CA_GOD, CA_GOD, &mudconf.global_error_obj, nullptr, 0}, {T("good_name"), cf_badname, CA_GOD, CA_DISABLED, nullptr, nullptr, 1}, {T("guest_char_num"), cf_dbref, CA_STATIC, CA_WIZARD, &mudconf.guest_char, nullptr, 0}, {T("guest_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.guest_file), nullptr, SIZEOF_PATHNAME}, {T("guest_nuker"), cf_dbref, CA_GOD, CA_WIZARD, &mudconf.guest_nuker, nullptr, 0}, {T("guest_prefix"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast(mudconf.guest_prefix), nullptr, 32}, {T("guest_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_GUEST}, {T("guests_channel"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast(mudconf.guests_channel), nullptr, 32}, {T("guests_channel_alias"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast(mudconf.guests_channel_alias), nullptr, 32}, {T("have_zones"), cf_bool, CA_STATIC, CA_PUBLIC, reinterpret_cast(&mudconf.have_zones), nullptr, 0}, {T("help_executor"), cf_dbref, CA_GOD, CA_WIZARD, &mudconf.help_executor, nullptr, 0}, {T("helpfile"), cf_helpfile, CA_STATIC, CA_DISABLED, nullptr, nullptr, 0}, {T("hook_cmd"), cf_hook, CA_GOD, CA_GOD, nullptr, nullptr, 0}, {T("hook_obj"), cf_dbref, CA_GOD, CA_GOD, &mudconf.hook_obj, nullptr, 0}, {T("hostnames"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.use_hostname), nullptr, 0}, {T("idle_interval"), cf_int, CA_GOD, CA_WIZARD, &mudconf.idle_interval, nullptr, 0}, {T("idle_timeout"), cf_live_driver_int, CA_GOD, CA_PUBLIC, &mudconf.idle_timeout, nullptr, 0}, {T("idle_wiz_dark"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.idle_wiz_dark), nullptr, 0}, {T("include"), cf_include, CA_STATIC, CA_DISABLED, nullptr, nullptr, 0}, {T("indent_desc"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.indent_desc), nullptr, 0}, {T("initial_size"), cf_int, CA_STATIC, CA_WIZARD, &mudconf.init_size, nullptr, 0}, {T("input_database"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.indb), nullptr, SIZEOF_PATHNAME}, {T("ip_address"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.ip_address), nullptr, LBUF_SIZE}, {T("jit_code_slots"), cf_int, CA_GOD, CA_WIZARD, &mudconf.jit_code_slots, nullptr, 0}, {T("jit_compile_cache_max"), cf_int, CA_GOD, CA_WIZARD, &mudconf.jit_compile_cache_max, nullptr, 0}, {T("jit_eval_brackets"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.jit_eval_brackets), nullptr, 0}, {T("lua_jit"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.lua_jit), nullptr, 0}, {T("keepalive_interval"), cf_int, CA_GOD, CA_WIZARD, &mudconf.keepalive_interval, nullptr, 0}, {T("kill_guarantee_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.killguarantee, nullptr, 0}, {T("kill_max_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.killmax, nullptr, 0}, {T("kill_min_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.killmin, nullptr, 0}, {T("lag_maximum"), cf_seconds, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.rpt_cmdsecs), nullptr, 0}, {T("lbuf_size"), cf_int, CA_DISABLED, CA_PUBLIC, reinterpret_cast(&mudconf.lbuf_size), nullptr, 0}, {T("link_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.linkcost, nullptr, 0}, {T("list_access"), cf_ntab_access, CA_GOD, CA_DISABLED, reinterpret_cast(list_names), access_nametab, 0}, {T("lock_recursion_limit"), cf_int, CA_WIZARD, CA_PUBLIC, &mudconf.lock_nest_lim, nullptr, 0}, {T("log"), cf_modify_bits, CA_GOD, CA_DISABLED, &mudconf.log_options, logoptions_nametab, 0}, {T("log_options"), cf_modify_bits, CA_GOD, CA_DISABLED, &mudconf.log_info, logdata_nametab, 0}, {T("logout_cmd_access"), cf_ntab_access, CA_GOD, CA_DISABLED, reinterpret_cast(logout_cmdtable), access_nametab, 0}, {T("logout_cmd_alias"), cf_alias, CA_GOD, CA_DISABLED, reinterpret_cast(&mudstate.logout_cmd_htab),nullptr, 0}, {T("look_obey_terse"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.terse_look), nullptr, 0}, {T("lua_instruction_limit"), cf_live_lua_int, CA_GOD, CA_PUBLIC, &mudconf.lua_instruction_limit, nullptr, 0}, {T("lua_memory_limit"), cf_live_lua_int, CA_GOD, CA_PUBLIC, &mudconf.lua_memory_limit, nullptr, 0}, {T("machine_command_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.machinecost, nullptr, 0}, {T("mail_database"), cf_string_dyn, CA_GOD, CA_GOD, reinterpret_cast(&mudconf.mail_db), nullptr, SIZEOF_PATHNAME}, {T("mail_expiration"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.mail_expiration, nullptr, 0}, {T("mail_per_hour"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.mail_per_hour, nullptr, 0}, {T("mail_max_per_player"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.mail_max_per_player, nullptr, 0}, {T("email_per_hour"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.email_per_hour, nullptr, 0}, {T("master_room"), cf_dbref, CA_GOD, CA_WIZARD, &mudconf.master_room, nullptr, 0}, {T("match_own_commands"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.match_mine), nullptr, 0}, {T("max_cache_size"), cf_size, CA_GOD, CA_GOD, reinterpret_cast(&mudconf.max_cache_size), nullptr, 0}, {T("max_name_protect"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.max_name_protect, nullptr, 0}, {T("max_players"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.max_players, nullptr, 0}, {T("min_guests"), cf_int, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.min_guests), nullptr, 0}, {T("money_name_plural"), cf_string, CA_GOD, CA_PUBLIC, reinterpret_cast(mudconf.many_coins), nullptr, 32}, {T("money_name_singular"), cf_string, CA_GOD, CA_PUBLIC, reinterpret_cast(mudconf.one_coin), nullptr, 32}, {T("motd_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.motd_file), nullptr, SIZEOF_PATHNAME}, {T("motd_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast(mudconf.motd_msg), nullptr, GBUF_SIZE}, {T("module"), cf_module, CA_GOD, CA_WIZARD, nullptr, nullptr, 0}, {T("mud_name"), cf_string, CA_GOD, CA_PUBLIC, reinterpret_cast(mudconf.mud_name), nullptr, 32}, {T("language"), cf_string, CA_GOD, CA_PUBLIC, reinterpret_cast(mudconf.language), nullptr, 32}, {T("newuser_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.crea_file), nullptr, SIZEOF_PATHNAME}, {T("no_flash"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&g_no_flash), nullptr, 0}, {T("noguest_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_NOGUEST}, {T("nositemon_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_NOSITEMON}, {T("notify_recursion_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.ntfy_nest_lim, nullptr, 0}, {T("number_guests"), cf_int, CA_STATIC, CA_WIZARD, &mudconf.number_guests, nullptr, 0}, {T("open_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.opencost, nullptr, 0}, {T("output_database"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.outdb), nullptr, SIZEOF_PATHNAME}, {T("input_limit"), cf_live_driver_int, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.input_limit), nullptr, 0}, {T("max_preauth_sitecons"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.max_preauth_sitecons, nullptr, 0}, {T("pool_memory_limit"), cf_pool_limit, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.pool_memory_limit), nullptr, 0}, {T("proto_detect_window"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.proto_detect_window, nullptr, 0}, {T("output_limit"), cf_live_driver_int, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.output_limit), nullptr, 0}, {T("page_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.pagecost, nullptr, 0}, {T("paranoid_allocate"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.paranoid_alloc), nullptr, 0}, {T("parent_recursion_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.parent_nest_lim, nullptr, 0}, {T("password_hash_rounds"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.password_hash_rounds, nullptr, 0}, {T("password_methods"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.password_methods, method_nametab, 0}, {T("paycheck"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.paycheck, nullptr, 0}, {T("pemit_any_object"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.pemit_any), nullptr, 0}, {T("pemit_far_players"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.pemit_players), nullptr, 0}, {T("permit_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_PERMIT}, {T("player_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast(&mudconf.player_flags), nullptr, 0}, {T("player_listen"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.player_listen), nullptr, 0}, {T("player_match_own_commands"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.match_mine_pl), nullptr, 0}, {T("player_name_spaces"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.name_spaces), nullptr, 0}, {T("player_name_charset"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.player_name_charset, allow_charset_nametab, 0}, {T("player_parent"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.player_parent, nullptr, 0}, {T("player_queue_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.queuemax, nullptr, 0}, {T("player_quota"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.player_quota, nullptr, 0}, {T("player_starting_home"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.start_home, nullptr, 0}, {T("player_starting_room"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.start_room, nullptr, 0}, {T("port"), cf_int_array, CA_STATIC, CA_PUBLIC, reinterpret_cast(&mudconf.ports), nullptr, MAX_LISTEN_PORTS}, #if defined(UNIX_SSL) || defined(_WIN32) {T("port_ssl"), cf_int_array, CA_STATIC, CA_PUBLIC, reinterpret_cast(&mudconf.sslPorts), nullptr, MAX_LISTEN_PORTS}, #endif {T("postdump_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast(mudconf.postdump_msg), nullptr, 256}, {T("power_alias"), cf_poweralias, CA_GOD, CA_DISABLED, nullptr, nullptr, 0}, {T("pronoun_group"), cf_pronoun_group, CA_GOD, CA_DISABLED, nullptr, nullptr, 0}, {T("pcreate_per_hour"), cf_int, CA_STATIC, CA_PUBLIC, reinterpret_cast(&mudconf.pcreate_per_hour),nullptr, 0}, {T("public_channel"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast(mudconf.public_channel), nullptr, 32}, {T("public_channel_alias"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast(mudconf.public_channel_alias), nullptr, 32}, {T("player_channels"), cf_string, CA_STATIC, CA_PUBLIC, reinterpret_cast(mudconf.player_channels), nullptr, LBUF_SIZE}, {T("public_flags"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.pub_flags), nullptr, 0}, {T("pueblo_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast(mudconf.pueblo_msg), nullptr, GBUF_SIZE}, {T("queue_active_chunk"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.active_q_chunk, nullptr, 0}, {T("queue_idle_chunk"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.queue_chunk, nullptr, 0}, {T("quiet_look"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.quiet_look), nullptr, 0}, {T("quiet_whisper"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.quiet_whisper), nullptr, 0}, {T("quit_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.quit_file), nullptr, SIZEOF_PATHNAME}, {T("quotas"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.quotas), nullptr, 0}, {T("raw_helpfile"), cf_raw_helpfile,CA_STATIC, CA_DISABLED, nullptr, nullptr, 0}, {T("read_remote_desc"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.read_rem_desc), nullptr, 0}, {T("read_remote_name"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.read_rem_name), nullptr, 0}, {T("references_per_hour"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.references_per_hour, nullptr, 0}, {T("register_create_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.regf_file), nullptr, SIZEOF_PATHNAME}, {T("register_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_REGISTER}, {T("reset_players"), cf_bool, CA_GOD, CA_DISABLED, reinterpret_cast(&mudconf.reset_players), nullptr, 0}, {T("reset_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_RESET}, {T("restrict_home"), cf_bool, CA_GOD, CA_DISABLED, reinterpret_cast(&mudconf.restrict_home), nullptr, 0}, {T("max_lastsite_cnt"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.max_lastsite_cnt, nullptr, 0}, {T("min_con_attempt"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.min_con_attempt, nullptr, 0}, {T("nospam_connect"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.nospam_connect, nullptr, 0}, {T("login_fail_limit"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.login_fail_limit, nullptr, 0}, {T("login_fail_period"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.login_fail_period, nullptr, 0}, {T("retry_limit"), cf_live_driver_int, CA_GOD, CA_WIZARD, &mudconf.retry_limit, nullptr, 0}, {T("robot_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.robotcost, nullptr, 0}, {T("robot_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast(&mudconf.robot_flags), nullptr, 0}, {T("robot_speech"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.robot_speak), nullptr, 0}, {T("room_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast(&mudconf.room_flags), nullptr, 0}, {T("room_name_charset"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.room_name_charset, allow_charset_nametab, 0}, {T("room_parent"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.room_parent, nullptr, 0}, {T("room_quota"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.room_quota, nullptr, 0}, {T("run_startup"), cf_bool, CA_STATIC, CA_WIZARD, reinterpret_cast(&mudconf.run_startup), nullptr, 0}, {T("sacrifice_adjust"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.sacadjust, nullptr, 0}, {T("sacrifice_factor"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.sacfactor, nullptr, 0}, {T("safe_wipe"), cf_bool, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.safe_wipe), nullptr, 0}, {T("safer_iter"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.safer_iter), nullptr, 0}, {T("safer_passwords"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.safer_passwords), nullptr, 0}, {T("search_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.searchcost, nullptr, 0}, {T("see_owned_dark"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.see_own_dark), nullptr, 0}, {T("signal_action"), cf_option, CA_STATIC, CA_GOD, &mudconf.sig_action, sigactions_nametab, 0}, {T("site_chars"), cf_live_driver_int, CA_GOD, CA_WIZARD, reinterpret_cast(&mudconf.site_chars), nullptr, 0}, {T("sitemon_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_SITEMON}, {T("space_compress"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.space_compress), nullptr, 0}, #if defined(UNIX_SSL) || defined(_WIN32) {T("ssl_certificate_file"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.ssl_certificate_file),nullptr, 128}, {T("ssl_certificate_key"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.ssl_certificate_key), nullptr, 128}, {T("ssl_certificate_password"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.ssl_certificate_password), nullptr, 128}, #endif {T("starting_money"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.paystart, nullptr, 0}, {T("starting_quota"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.start_quota, nullptr, 0}, {T("status_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.status_file), nullptr, SIZEOF_PATHNAME}, {T("stripped_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast(&mudconf.stripped_flags), nullptr, 0}, {T("suspect_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_SUSPECT}, {T("sweep_dark"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.sweep_dark), nullptr, 0}, {T("switch_default_all"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.switch_df_all), nullptr, 0}, {T("talk_mode_default"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.talk_mode_default), nullptr, 0}, {T("terse_nospoof"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.terse_nospoof), nullptr, 0}, {T("terse_shows_contents"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.terse_contents), nullptr, 0}, {T("terse_shows_exits"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.terse_exits), nullptr, 0}, {T("terse_shows_move_messages"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.terse_movemsg), nullptr, 0}, {T("thing_flags"), cf_set_flags, CA_GOD, CA_DISABLED, reinterpret_cast(&mudconf.thing_flags), nullptr, 0}, {T("thing_name_charset"), cf_modify_bits, CA_GOD, CA_PUBLIC, &mudconf.thing_name_charset, allow_charset_nametab, 0}, {T("thing_parent"), cf_dbref, CA_GOD, CA_PUBLIC, &mudconf.thing_parent, nullptr, 0}, {T("thing_quota"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.thing_quota, nullptr, 0}, {T("timeslice"), cf_seconds, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.timeslice), nullptr, 0}, {T("toad_recipient"), cf_dbref, CA_GOD, CA_WIZARD, &mudconf.toad_recipient, nullptr, 0}, {T("trace_output_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.trace_limit, nullptr, 0}, {T("trace_topdown"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.trace_topdown), nullptr, 0}, {T("trust_site"), cf_site, CA_GOD, CA_DISABLED, nullptr, nullptr, HC_TRUST}, {T("unowned_safe"), cf_bool, CA_GOD, CA_PUBLIC, reinterpret_cast(&mudconf.safe_unowned), nullptr, 0}, {T("user_attr_access"), cf_modify_bits, CA_GOD, CA_DISABLED, &mudconf.vattr_flags, attraccess_nametab, 0}, {T("user_attr_per_hour"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.vattr_per_hour, nullptr, 0}, {T("vlimit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.vlimit, nullptr, 0}, {T("wait_cost"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.waitcost, nullptr, 0}, {T("wizard_motd_file"), cf_string_dyn, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf.wizmotd_file), nullptr, SIZEOF_PATHNAME}, {T("wizard_motd_message"), cf_string, CA_GOD, CA_WIZARD, reinterpret_cast(mudconf.wizmotd_msg), nullptr, GBUF_SIZE}, {T("zone_recursion_limit"), cf_int, CA_GOD, CA_PUBLIC, &mudconf.zone_nest_lim, nullptr, 0}, #ifdef REALITY_LVLS {T("reality_level"), cf_rlevel, CA_STATIC, CA_GOD, reinterpret_cast(&mudconf), nullptr, 0}, {T("def_room_rx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast(&mudconf.def_room_rx), nullptr, 0}, {T("def_room_tx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast(&mudconf.def_room_tx), nullptr, 0}, {T("def_player_rx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast(&mudconf.def_player_rx), nullptr, 0}, {T("def_player_tx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast(&mudconf.def_player_tx), nullptr, 0}, {T("def_exit_rx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast(&mudconf.def_exit_rx), nullptr, 0}, {T("def_exit_tx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast(&mudconf.def_exit_tx), nullptr, 0}, {T("def_thing_rx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast(&mudconf.def_thing_rx), nullptr, 0}, {T("def_thing_tx"), cf_int, CA_WIZARD, CA_PUBLIC, reinterpret_cast(&mudconf.def_thing_tx), nullptr, 0}, #endif // REALITY_LVLS #if defined(INLINESQL) {T("sql_server"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.sql_server), nullptr, 128}, {T("sql_user"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.sql_user), nullptr, 128}, {T("sql_password"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.sql_password), nullptr, 128}, {T("sql_database"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.sql_database), nullptr, 128}, #endif {T("mail_server"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.mail_server), nullptr, 128}, {T("mail_ehlo"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.mail_ehlo), nullptr, 128}, {T("mail_sendaddr"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.mail_sendaddr), nullptr, 128}, {T("mail_sendname"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.mail_sendname), nullptr, 128}, {T("mail_subject"), cf_string, CA_STATIC, CA_DISABLED, reinterpret_cast(mudconf.mail_subject), nullptr, 128}, { nullptr, nullptr, 0, 0, nullptr, nullptr, 0} }; // --------------------------------------------------------------------------- // cf_cf_access: Set access on config directives // CF_HAND(cf_cf_access) { UNUSED_PARAMETER(vp); CONFPARM *tp; UTF8 *ap; for (ap = str; *ap && !mux_isspace(*ap); ap++) { ; // Nothing } if (*ap) { *ap++ = '\0'; } for (tp = conftable; tp->pname; tp++) { if (!strcmp(reinterpret_cast(tp->pname), reinterpret_cast(str))) { // Cannot modify parameters set CA_STATIC. // if ( tp->flags & CA_STATIC && !mudstate.bReadingConfiguration) { notify(player, NOPERM_MESSAGE); STARTLOG(LOG_CONFIGMODS, "CFG", "PERM"); log_name(player); log_text(T(" tried to change access to static param: ")); log_text(tp->pname); ENDLOG; return -1; } return cf_modify_bits(&tp->flags, ap, pExtra, nExtra, player, cmd); } } cf_log_notfound(player, cmd, T("Config directive"), str); return -1; } // --------------------------------------------------------------------------- // cf_set: Set config parameter. // int cf_set(UTF8 *cp, UTF8 *ap, dbref player) { CONFPARM *tp; // Search the config parameter table for the command. If we find // it, call the handler to parse the argument. // for (tp = conftable; tp->pname; tp++) { if (!strcmp(reinterpret_cast(tp->pname), reinterpret_cast(cp))) { int i = -1; if ( (tp->flags & CA_DISABLED) == 0 && ( mudstate.bReadingConfiguration || check_access(player, tp->flags))) { if (!mudstate.bReadingConfiguration) { LBuf buff = LBuf_Src("cf_set"); mux_strncpy(buff, ap, LBUF_SIZE-1); i = tp->interpreter(tp->loc, ap, tp->pExtra, tp->nExtra, player, cp); STARTLOG(LOG_CONFIGMODS, "CFG", "UPDAT"); log_name(player); log_text(T(" entered config directive: ")); log_text(cp); log_text(T(" with args ‘")); log_text(buff); log_text(T("’. Status: ")); switch (i) { case 0: log_text(T("Success.")); break; case 1: log_text(T("Partial success.")); break; case -1: log_text(T("Failure.")); break; default: log_text(T("Strange.")); } ENDLOG; } else { i = tp->interpreter(tp->loc, ap, tp->pExtra, tp->nExtra, player, cp); } } else if (!mudstate.bReadingConfiguration) { notify(player, NOPERM_MESSAGE); } return i; } } // Config directive not found. Complain about it. // cf_log_notfound(player, T("Set"), T("Config directive"), cp); return -1; } // Validate important dbrefs. // void ValidateConfigurationDbrefs(void) { static struct { dbref *ploc; dbref dflt; } Table[] = { { &mudconf.default_home, NOTHING }, { &mudconf.exit_parent, NOTHING }, { &mudconf.global_error_obj, NOTHING }, { &mudconf.guest_char, NOTHING }, { &mudconf.guest_nuker, GOD }, { &mudconf.help_executor, NOTHING }, { &mudconf.hook_obj, NOTHING }, { &mudconf.master_room, NOTHING }, { &mudconf.player_parent, NOTHING }, { &mudconf.start_home, NOTHING }, { &mudconf.start_room, 0 }, { &mudconf.room_parent, NOTHING }, { &mudconf.thing_parent, NOTHING }, { &mudconf.toad_recipient, GOD }, { nullptr, NOTHING } }; for (int i = 0; nullptr != Table[i].ploc; i++) { if (*Table[i].ploc != Table[i].dflt) { if (!Good_obj(*Table[i].ploc)) { *Table[i].ploc = Table[i].dflt; } } } } // --------------------------------------------------------------------------- // do_admin: Command handler to set config params at runtime // void do_admin ( dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs, UTF8 *kw, UTF8 *value, const UTF8 *cargs[], int ncargs ) { UNUSED_PARAMETER(caller); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(key); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); int i = cf_set(kw, value, executor); if ((i >= 0) && !Quiet(executor)) { notify(executor, M_("Set.")); } ValidateConfigurationDbrefs(); } // --------------------------------------------------------------------------- // cf_read: Read in config parameters from named file // static struct { UTF8 **pFilename; const UTF8 *pSuffix; } DefaultSuffixes[] = { { &mudconf.outdb, T(".out") }, { &mudconf.crashdb, T(".CRASH") }, { 0, 0 } }; int cf_read(void) { int retval; mudstate.bReadingConfiguration = true; retval = cf_include(nullptr, mudconf.config_file, nullptr, 0, 0, const_cast(T("init"))); mudstate.bReadingConfiguration = false; // Fill in missing DB file names. // size_t nInDB = strlen(reinterpret_cast(mudconf.indb)); for (int i = 0; DefaultSuffixes[i].pFilename; i++) { UTF8 **p = DefaultSuffixes[i].pFilename; if (**p == '\0') { // The filename is an empty string so we should construct // a default filename. // const UTF8 *pSuffix = DefaultSuffixes[i].pSuffix; size_t nSuffix = strlen(reinterpret_cast(pSuffix)); UTF8 *buff = reinterpret_cast(MEMALLOC(nInDB + nSuffix + 1)); if (nullptr == buff) { STARTLOG(LOG_PROBLEMS, "CNF", "MEM"); log_printf(T("cf_verify: out of memory constructing default filename.")); ENDLOG; } else { memcpy(buff, mudconf.indb, nInDB); memcpy(buff + nInDB, pSuffix, nSuffix+1); MEMFREE(*p); *p = buff; } } } return retval; } // --------------------------------------------------------------------------- // list_cf_access: List access to config directives. // void list_cf_access(dbref player) { for (CONFPARM *tp = conftable; tp->pname; tp++) { if (God(player) || check_access(player, tp->flags)) { listset_nametab(player, access_nametab, tp->flags, tp->pname, true); } } } // --------------------------------------------------------------------------- // cf_display: Given a config parameter by name, return its value in some // sane fashion. // void cf_display(dbref player, const UTF8 *param_name, UTF8 *buff, UTF8 **bufc) { CONFPARM *tp; for (tp = conftable; tp->pname; tp++) { if (!mux_stricmp(tp->pname, param_name)) { if (check_access(player, tp->rperms)) { // The live-push handlers store a plain int at tp->loc exactly // as cf_int does; they differ only in re-pushing the value to // a subsystem holding its own copy. They have to be listed // here anyway, because this dispatch is by handler IDENTITY: // an unrecognised interpreter falls all the way through to // safe_noperm below and answers "#-1 PERMISSION DENIED". // // That answer is a lie -- check_access() has already passed // by this point -- and it is silent, so converting a knob to // a live handler makes config() stop reading it with no // build or test complaining. #1222 did exactly that to all // fourteen cf_live_driver_int knobs (max_players, // idle_timeout, retry_limit, output_limit, nospam_connect, // ...), which have been unreadable since; #1613 was about to // do it to the two Lua limits. // if ( tp->interpreter == cf_int || tp->interpreter == cf_live_driver_int || tp->interpreter == cf_live_lua_int) { safe_ltoa(*(tp->loc), buff, bufc); return; } else if (tp->interpreter == cf_dbref) { safe_chr('#', buff, bufc); safe_ltoa(*(tp->loc), buff, bufc); return; } else if (tp->interpreter == cf_bool) { bool *pb = reinterpret_cast(tp->loc); safe_bool(*pb, buff, bufc); return; } else if (tp->interpreter == cf_string) { safe_str(reinterpret_cast(tp->loc), buff, bufc); return; } else if (tp->interpreter == cf_string_dyn) { safe_str(*reinterpret_cast(tp->loc), buff, bufc); return; } else if (tp->interpreter == cf_int_array) { auto *pvec = reinterpret_cast *>(tp->loc); ITL itl; ItemToList_Init(&itl, buff, bufc); for (size_t i = 0; i < pvec->size(); i++) { if (!ItemToList_AddInteger(&itl, (*pvec)[i])) { break; } } ItemToList_Final(&itl); return; } else if (tp->interpreter == cf_size) { int64_t *pSize = reinterpret_cast(tp->loc); safe_i64toa(*pSize, buff, bufc); return; } else if (tp->interpreter == cf_seconds) { CLinearTimeDelta *pltd = reinterpret_cast(tp->loc); safe_str(pltd->ReturnSecondsString(7), buff, bufc); return; } } safe_noperm(buff, bufc); return; } } safe_nomatch(buff, bufc); } // --------------------------------------------------------------------------- // cf_list: List all config options the player can read. // void cf_list(dbref player, UTF8 *buff, UTF8 **bufc) { CONFPARM *tp; ITL itl; ItemToList_Init(&itl, buff, bufc); for (tp = conftable; tp->pname; tp++) { if (check_access(player, tp->rperms)) { if (!ItemToList_AddString(&itl, tp->pname)) { break; } } } ItemToList_Final(&itl); return; }