#pragma strict_types
/* Master File for HEAVEN7 mudlib */
#include "/include/mudlib.h"
#include "/include/cfg/levels.h"
#include "/include/cfg/master.cfg"
#ifdef AMYLAAR321
#include "/include/cfg/driver_hook.h"
#endif
#ifdef NATIVE_MODE /* in case master loads before simul-efun */
string master_file_name(object ob) {
string file;
file = file_name(ob);
if(file[0] == '/') file = file[1..(strlen(file)-1)];
return file;
}
#define file_name master_file_name
#endif /* NATIVE */
/***************************************************************************/
/* start-up flags */
void flag(string str) {
string file, arg;
if(sscanf(str, "echo %s", arg) == 1) {
write(arg + "\n");
return;
}
if(sscanf(str, "call %s %s", file, arg) == 2) {
arg = (string)call_other(file, arg);
write("Call Flag: "+ arg +"\n");
return;
}
write("Master: Unknown flag " + str + "\n");
}
/***************************************************************************/
/* object player is connected */
/*
* This function is called every time a player connects.
* input_to() can't be called from here.
*/
object connect() {
object ob;
string ret1, ret2;
write("\n");
#ifdef USE_DEBUG
return clone_object(DEBUG);
#endif /* force usuage of DEBUG-player */
#ifdef DEBUG
/* attempt to load WIZARD,
loads "inherit/base/base_obj",
"inherit/base/living",
"obj/player",
"obj/wizard"
*/
ret1 = (string)catch(call_other(WIZARD,"??"));
if(ret1) {
write("SWITCHING TO DEBUG OBJECT!!\n");
ret2 = (string)catch((ob = clone_object(DEBUG)));
if(ret2) {
write("FAILED!\n");
write("Error1: "+ret1+"\n");
write("Error2: "+ret2+"\n");
return 0;
}
else {
ob->set_error_msg(ret1);
}
}
else {
ob = clone_object(PLAYER);
}
#else
ob = clone_object(PLAYER);
#endif
return ob;
}
/****************************************************************************/
/* where are error logs written */
/*
* Get the owner of a file. This is called from the game driver, so as
* to be able to know which wizard should have the error.
*/
string get_wiz_name(string file) {
string name, domain, rest;
file = "/"+ file;
if (sscanf(file, WIZARD_DIR +"%s/%s", name, rest) == 2) {
return name;
}
if(sscanf(file, DOMAIN_DIR +"%s/w/%s/%s", domain, name, rest) == 3) {
return name;
}
return 0;
}
/*
* Write an error message into a log file. The error occured in the object
* 'file', giving the error message 'message'.
*/
void log_error(string file, string message) {
string name;
name = get_wiz_name(file);
if(!name) name = "log";
write_file("/log/"+name, message);
}
/****************************************************************************/
/* edit setup save */
/*
* The wizard object 'who' wants to save his ed setup. It is saved in the
* file /players/wiz_name/.edrc . A test should be added to make sure it is
* a call from a wizard.
*
* Don't care to prevent unauthorized access of this file. Only make sure
* that a number is given as argument.
*/
int save_ed_setup(object who, int code) {
string file;
if(!intp(code)) return 0;
file = WIZARD_DIR+(string)who->query_name(1)+"/"+ ED_SAVE;
rm(file);
return write_file(file,code+"");
}
/*
* Retrieve the ed setup. No meaning to defend this file read from
* unauthorized access.
*/
int retrieve_ed_setup(object who) {
string file;
int code;
file = WIZARD_DIR+(string)who->query_name(1)+"/"+ ED_SAVE;
if (file_size(file) <= 0) return 0;
sscanf(read_file(file), "%d", code);
return code;
}
/************************************************************************/
/* changed to security_level */
/*
* There are several occasions when the game driver wants to check if
* a player has permission to specific things.
*
* These types are implemented so far:
* "error messages": If the player is allowed to see runtime error
* messages.
* "trace": If the player is allowed to use tracing.
* "wizard": Is the player considered at least a "minimal" wizard ?
* "error messages": Is the player allowed to get run time error messages ?
*/
int query_player_level(string what) {
int security_level;
if(!this_player()) return 0;
security_level = (int)this_player()->query_security_level();
switch(what) {
case "wizard":
return (security_level >= SEC1);
case "error messages":
return (security_level >= SEC1);
case "trace":
return (security_level >= SEC4);
case "arch":
return (security_level >= SEC8);
}
}
/***************************************************************************/
/* a number of fn() in master will accept calls only from specific objects */
/* they form the basis of the security of the mud */
status valid_master_call(object ob) {
int i;
string file, *valid_master;
if(!ob) return 0;
file = file_name(ob);
for(i = sizeof(MASTER_FILES); i--; ) {
if(file == MASTER_FILES[i]) return 1;
}
#ifdef MASTER_DIR
for(i = sizeof(MASTER_DIR); i--; ) {
if(sscanf("/"+ file, MASTER_DIR[i], file)) return 1;
}
#endif /* MASTER_DIR */
return 0;
}
status valid_player_call(object ob) {
int i, arg;
string file, *valid_player;
if(!ob) {
return 0;
}
if(valid_master_call(ob)) return 1;
/* valid player files - are all cloned objects */
valid_player = ({
PLAYER, WIZARD,
#ifdef DEBUG
DEBUG,
#endif /* DEBUG */
});
file = file_name(ob);
for(i = sizeof(valid_player); i--; ) {
if(sscanf(file, valid_player[i] +"#%d", arg)) return 1;
}
return 0;
}
/****************************************************************************/
/*
* When an object is destructed, this function is called with every
* item in that room. We get the chance to save players !
*/
void destruct_environment_of(object ob) {
if(!interactive(ob)) return;
tell_object(ob,
"Everything you see is disolved.\n"+
"Luckily, you are transported somewhere...\n");
ob->move_player("is transfered#"+ VOID,0,1); /* domain safe move */
}
#ifndef MUDOS_DR /* mudos loads these at startup in config */
/**********************************************************************/
/* include directories */
/*
* Define where the '#include' statement is supposed to search for files.
* "." will automatically be searched first, followed in order as given
* below. The path should contain a '%s', which will be replaced by the file
* searched for.
*/
string *define_include_dirs() {
return
({"/include/%s", "/include/fn_specs/%s","/include/cfg/%s",
"/include/fn/%s","/include/skills/%s",});
}
#endif /* MUDOS_DR */
/**********************************************************************/
/*
* The master object is asked if it is ok to shadow object ob. Use
* previous_object() to find out who is asking.
*
* In this example, we allow shadowing as long as the victim object
* hasn't denied it with a query_prevent_shadow() returning 1.
*/
#ifdef MUSOS_DR
int valid_shadow(object ob) {
#else
int query_allow_shadow(object ob) {
#endif /* MUDOS_DR */
string file;
if(!sscanf(file_name(previous_object()),"obj/shadows/%s",file)) {
return 0; /* only shadows that have been inspected can be used */
}
/* do not allow shadows with our security fns in them */
if(function_exists("valid_read",previous_object())
|| function_exists("valid_write",previous_object())) {
return 0;
}
return !ob->query_prevent_shadow(previous_object());
}
/************************************************************************/
/* 3.2 allows two people to edit same file simultaneously!!
* here is a hack to stop that
*/
string *current_edit; /* list of files currently edited */
string *query_current_edit() { return current_edit; }
static status add_current_edit(string file) {
string name;
int i;
name = capitalize((string)this_player()->query_name(1));
if(!current_edit) current_edit = ({});
if((i = member_array(file, current_edit)) != -1) {
write("Sorry, "+ file +" is being edited by "+ current_edit[i+1] +".\n");
return 0;
}
current_edit += ({ file, name, });
return 1;
}
void remove_current_edit(string file) {
int i;
if(!current_edit) current_edit = ({});
if((i = member_array(file, current_edit)) == -1) return;
current_edit = current_edit[0..(i-1)]
+ current_edit[(i+2)..(sizeof(current_edit)-1)];
}
/************************************************************************/
/* where is file written if wizard goes net-dead during edit */
/*
* Give a file name for edit preferences to be saved in.
*/
string get_ed_buffer_save_file_name(string file) {
string *file_ar;
if(file && file != "")
remove_current_edit(((file[0] != '/') ? "/" : "")+ file);
file_ar=explode(file,"/");
file=file_ar[sizeof(file_ar)-1];
return WIZARD_DIR+(string)this_player()->query_name(1)+"/"+DEAD_ED+"/"+file;
}
#ifndef MUDOS_DR /* mudos loads simul efun before master */
/***************************************************************************/
/*
* Give a path to a simul_efun file. Observe that it is a string returned,
* not an object. But the object has to be loaded here. Return 0 if this
* feature isn't wanted.
*/
static string get_simul_efun_file() {
if(catch(call_other(SIMUL_EFUN, "??"))) {
write("Failed to load " + SIMUL_EFUN + "\n");
if(catch(call_other(SPARE_SIMUL_EFUN_FILE, "??"))) {
write("Failed to load spare " + SPARE_SIMUL_EFUN_FILE + "\n");
shutdown();
return 0;
}
return SPARE_SIMUL_EFUN_FILE;
}
return SIMUL_EFUN;
}
string get_simul_efun() {
#ifdef AMYLAAR321
return "/"+ get_simul_efun_file();
#else
return get_simul_efun_file();
#endif /* AMYLAAR321 */
}
#endif /* !MUDOS_DR */
/****************************************************************************/
/* file_size that can be used by all objects */
int master_file_size(string file) {
return file_size(file);
}
/****************************************************************************/
/* remove file for player object */
status master_remove_file(string file) {
return (valid_player_call(previous_object())) ? rm(file) : 0;
}
/**************************************************************************/
/* exec() is called in player.c */
/*
* Function name: valid_exec
* Description: Checks if a certain 'program' has the right to use exec()
* Arguments: name: Name of the 'program' that attempts to use exec()
* Note that this is different from file_name(),
* Programname is what 'function_exists' returns.
* NOTE, the absence of a leading slash in the name.
* Returns: True if exec() is allowed.
*/
int valid_exec(string name) {
if(name && name != "" && name[0] == '/') {
name = name[1..(strlen(name)-1)];
}
switch(name) {
case PLAYER+".c":
case WIZARD+".c":
return 1;
break;
}
write("Invalid Exec() call by object: "+name+"\n");
return 0;
}
#ifdef AMYLAAR
void runtime_error(string error,
string program,
string current_object,
int line) {
if(this_player() && query_ip_number(this_player())) {
if(!query_player_level("error messages") || !error) {
#ifdef ADMIN_NAME
write(ADMIN_NAME +" exclaims: Woops!!\n"+
" You have found a Space-Time Anomaly.\n");
#else
write("Kingbilly exclaims: Woops!!\n"+
" You have found a Space-Time Anomaly.\n");
#endif /* ADMIN_NAME */
}
else {
write(error +"\n");
write(((current_object)
? "program: "+ program +", object: "+ current_object
+" line "+ line +"\n"
: ""));
}
}
}
int heart_beat_error(object heart_beat,
string error,
string program,
string current_object,
int line) {
string wiz_name;
if(heart_beat && query_ip_number(heart_beat) ) {
#ifdef ADMIN_NAME
tell_object(heart_beat,ADMIN_NAME
+" tells you: You have no heart beat !\n");
#else
tell_object(heart_beat,"Kingbilly tells you: You have no heart beat !\n");
#endif /* ADMIN_NAME */
if(query_player_level("error messages")) {
tell_object(heart_beat, error +"\n");
tell_object(heart_beat, ((current_object)
? "program: "+program+", object: "+current_object+" line "+line+"\n"
: ""));
}
}
#ifdef LOG_HB_ERROR
if(environment(this_player())) {
write_file(LOG_HB_ERROR,"Room: "+ file_name(environment(this_player())) +
"\nError: "+ error +
"Program: "+ program +"\n"+
"Object: "+ current_object +
"\nLine: "+ line +"\n\n");
if(!(wiz_name = get_wiz_name(file_name(environment(this_player()))))) {
if(!(wiz_name = get_wiz_name(current_object))) {
return 0;
}
}
write_file(WIZARD_DIR+ wiz_name +"/HB_ERRORS",
"Room: "+ file_name(environment(this_player())) +"\n"+
"Error: "+ error +
"Program: "+ program +"\n"+
"Object: "+ current_object +"\n"+
"Line: "+ line +"\n\n");
}
#endif
return 0; /* Don't restart */
}
#endif /* AMYLAAR */
/************************************************************************/
/* used by ed() to resolve a full path name */
#ifdef AMYLAAR
mixed make_path_absolute(string path) {
return (string)this_player()->valid_read(path);
}
#endif /* AMYLAAR */
/*************************************************************************/
/* validates whether an object can be loaded */
/* in native it gives object its uid */
static mixed master_creator_file(string object_name) {
string wiz_name, domain, trailer;
if(sscanf("/"+object_name,WIZARD_DIR+"%s/%s",wiz_name,trailer)) {
return wiz_name;
}
if(sscanf("/"+object_name,DOMAIN_DIR+"%s/w/%s/%s",domain,wiz_name,trailer)) {
return wiz_name;
}
if(sscanf(object_name,"open/%s/%s", wiz_name, trailer)) {
if(wiz_name == "paste") return 0;
return wiz_name;
}
/* directories that should not have objects in them */
if(sscanf(object_name,"usr/%s", trailer)) return 0;
if(sscanf(object_name,"doc/%s", trailer)) return 0;
if(sscanf(object_name,"info/%s", trailer)) return 0;
if(sscanf("/"+object_name,WIZARD_DIR+"%s",trailer)) return 0;
if(sscanf("/"+object_name,DOMAIN_DIR+"%s/w/%s",domain,trailer)) return 0;
if(sscanf(object_name,"open/%s", trailer)) return 0;
if(sscanf(object_name,"log/%s", trailer)) return 0;
return 1; /* else legal */
}
mixed creator_file(string object_name) {
mixed creator_status;
if((creator_status = master_creator_file(object_name))) {
#ifdef NATIVE_MODE
return UID_ROOT;
#else
return creator_status;
#endif
}
return 0;
}
void move_or_destruct(object what, object to)
/* An error in this function can be very nasty. Note that unlimited recursion
* is likely to cause errors when environments are deeply nested
*/
{
#ifdef COMPAT_FLAG
do {
int res;
if (catch( res = transfer(what, to) )) res = 5;
if ( !(res && what) ) return;
} while( (res == 1 || res == 4 || res == 5) && (to = environment(to)) );
#else /* !COMPAT_FLAG */
if ( !catch( what->move(to, 1) ) ) return;
#endif /*COMPAT_FLAG */
/*
* Failed to move the object. Then, it is destroyed.
*/
destruct(what);
}
/******************************************************************/
/* snoops must routed via simul_efun */
int valid_snoop(object snooper, object snoopee) {
#ifndef MUDOS_DR
if(file_name(previous_object()) == get_simul_efun()) return 1;
#else
if(file_name(previous_object()) == SIMUL_EFUN) return 1;
#endif /* MUDOS_DR */
}
/****************************************************************/
/* snoops are usable by priviledged people */
int valid_query_snoop(object wiz) {
return (int)this_player()->query_security_level() >= SEC5;
}
#ifdef AMYLAAR
#ifdef COMPAT_FLAG
mixed *prepare_destruct1(object ob) {
object super;
super = environment(ob);
if (super) {
mixed error, *errors;
mixed weight;
object me;
me = this_object();
set_this_object(ob);
errors = ({});
if ( living(ob) ) {
if (error = catch(super->exit(ob),0))
errors = ({"exit"+": "+error});
}
if ( error = catch((weight = (mixed)ob->query_weight()),0) ) {
set_this_object(me);
return ({"query_weight"+": "+error}) + errors;
}
if (weight && intp(weight)) {
if (error = catch(super->add_weight(-weight),0)) {
set_this_object(me);
return ({"add_weight"+": "+error}) + errors;
}
}
set_this_object(me);
}
return ({});
}
#endif /* COMPAT_FLAG */
mixed prepare_destruct(object ob) {
object super;
mixed *errors;
int i;
#if 6 * 7 != 42 || 6 * 9 == 42
return "Preprocessor error";
#endif
#ifdef COMPAT_FLAG
errors = prepare_destruct1(ob);
for(i = sizeof(errors); i--; ) {
write(errors[i]);
}
#endif /* COMPAT_FLAG */
super = environment(ob);
if (!super) {
object item;
while ( item = first_inventory(ob) ) {
destruct_environment_of(item);
if (item && environment(item) == ob) destruct(item);
}
} else {
while ( first_inventory(ob) )
move_or_destruct(first_inventory(ob), super);
}
return 0; /* success */
}
/* privilege_violation is called when objects try to do illegal things,
* or files being compiled request a privileged efun.
*
* return values:
* 1: The caller/file is allowed to use the privilege.
* 0: The caller was probably misleaded; try to fix the error.
* -1: A real privilege violation. Handle it as error.
*/
int privilege_violation(string what, mixed who, mixed arg) {
switch(what) {
case "call_out_info":
return (valid_player_call(who)) ? 1 : -1;
break;
#ifdef INTERMUD
case "send_imp":
case "wizlist_info":
return (valid_player_call(who) || file_name(who) == INETD) ? 1 : -1;
break;
#else
case "wizlist_info":
return (valid_player_call(who)) ? 1 : -1;
break;
#endif /* INTERMUD */
case "shutdown":
return (valid_player_call(who) || file_name(who) == SHUTD) ? 1 : -1;
break;
case "nomask simul_efun":
case "set_auto_include_string":
case "add_worth":
case "bind_lambda":
case "get_extra_wizinfo":
case "rename_object":
case "set_extra_wizinfo":
case "set_extra_wizinfo_size":
case "set_this_object":
case "shadow_add_action":
return (valid_master_call(who)) ? 1 : -1;
break;
default:
return -1;
}
return -1;
}
#ifdef INTERMUD
void receive_imp(string sender,string msg) {
INETD->receive_udp(sender,msg);
}
#endif /* INTERMUD */
void dangling_lfun_closure() {
raise_error("dangling lfun closure\n");
}
#endif /* AMYLAAR */
void slow_shut_down(int minutes) {
shout("Game driver shouts: The memory is getting low !\n");
SHUTD->shut(minutes);
}
void remove_player(object victim) {
catch(victim->quit());
if(victim) destruct(victim);
}
#ifndef 312MASTER
/************************************************************************/
/* I don't know anyone who uses it?
* Parse_command() is a heavy load */
/*
* Default language functions used by parse_command() in non -o mode
*/
string *parse_command_id_list()
{
return ({ "one", "thing" });
}
string *parse_command_plural_id_list()
{
return ({ "ones", "things", "them" });
}
string *parse_command_adjectiv_id_list()
{
return ({ "iffish" });
}
string *parse_command_prepos_list()
{
return ({ "in", "on", "under", "behind", "beside" });
}
string parse_command_all_word()
{
return "all";
}
#endif /* 312MASTER */
UID_TYPE get_root_uid() { return UID_ROOT; }
#ifdef NATIVE_MODE /* mudos/native master fns */
string get_bb_uid() { return UID_BACKBONE; }
string domain_file(string str) {
string nom, tmp;
if(str[0] != '/') str = "/"+str;
if(sscanf(str, DOMAIN_DIR+"%s/%s", nom, tmp) == 2) return nom;
return 0;
}
string author_file(string str) { return get_wiz_name(str); }
int valid_seteuid(object ob, string id) {
return 1;
}
int valid_override(string file, string efun_call) { return 1; }
#endif /* mudos */
/***********************************************************************/
/* promote new wizards and domain wizards */
string master_create_wizard(string owner,string domain,object caller) {
string txt, tmp1, tmp2;
object access;
int tmp;
if(!owner) return 0;
if(!valid_player_call(caller)) {
write_file("/log/ILLEGALS",
"Create_wizard() called for owner, "+ owner
+" by object, "+ file_name(caller) +" at "+ ctime(time())+"\n");
return 0;
}
write_file("/log/WIZ",
"Name:"+ owner +", By: "+ (string)this_player()->query_name(1)
+" Time: "+ ctime(time()) +"\n");
/* promote wizard */
if(!domain) {
if(file_size(WIZARD_DIR+ owner) != -2) {
tell_object(caller,"Adding Wizard Directory...\n");
mkdir(WIZARD_DIR+ owner);
}
if(file_size(WIZARD_DIR+ owner +"/open") != -2) {
tell_object(caller,"Adding Open directory, ~ /open\n");
mkdir(WIZARD_DIR+owner+"/open");
}
if(file_size(WIZARD_DIR+ owner +"/private") != -2) {
tell_object(caller,"Adding Private directory, ~ /private\n");
mkdir(WIZARD_DIR+owner+"/private");
}
if(file_size(WIZARD_DIR +owner+"/"+DEAD_ED) != -2) {
tell_object(caller,"Adding dead edit directory, ~ /"+DEAD_ED+"\n");
mkdir(WIZARD_DIR+owner+"/"+DEAD_ED);
}
if(file_size(INIT_ACCESS) > 0
&& file_size(WIZARD_DIR+owner+"/access.c") < 0) {
tell_object(caller,"Adding Configurable Access Object...\n");
txt = "#define NAME \""+owner+"\"\n";
write_file(WIZARD_DIR+owner+"/access.c",txt);
txt = read_file(INIT_ACCESS);
write_file(WIZARD_DIR+owner+"/access.c",txt);
}
if(file_size(INIT_WORKROOM) > 0
&& file_size(WIZARD_DIR+owner+"/workroom.c") < 0) {
tell_object(caller,"Adding Workroom...\n");
txt = "inherit \"inherit/room2\"\;\n\n#define NAME \""+owner+"\"\n\n";
write_file(WIZARD_DIR+owner+"/workroom.c",txt);
txt = read_file(INIT_WORKROOM);
write_file(WIZARD_DIR+owner+"/workroom.c",txt);
}
}
else { /* Add wizard to domain */
if(file_size(DOMAIN_DIR+domain) != -2) {
if(query_player_level("arch")) { /* only arches can make New Domains */
write("Adding New Domain...\nMaking "+owner+" the High Lord.\n");
if(this_player() != caller) {
tell_object(caller,"Adding New Domain...\n"+
"You are now High Lord over "+ domain +".\n");
}
mkdir(DOMAIN_DIR+ domain);
txt = read_bytes(DOMAIN_DIR +"access.c",
0,
file_size(DOMAIN_DIR +"access.c"));
sscanf(txt, "%s});%s", tmp1, tmp2);
txt = "\""+owner+"\", ";
txt = extract(txt,0,15);
txt += "\""+domain+"\",\n";
txt = tmp1+txt +" });"+ tmp2;
rm(DOMAIN_DIR +"access.c");
write_file(DOMAIN_DIR +"access.c",txt);
if((access = find_object(DOMAIN_DIR +"access"))) {
destruct(access);
}
if(catch(call_other(DOMAIN_DIR +"access","??"))) {
write("Error In Reloading Head Domain Access Object!\n");
}
}
else {
write("You cannot add a New Domain.\n");
return 0;
}
}
if(file_size(DOMAIN_DIR+domain+"/access.c") < 0) {
txt = read_bytes(DOMAIN_DIR +"access.dom",
0,
file_size(DOMAIN_DIR +"access.dom"));
write_file(DOMAIN_DIR+domain+"/access.c",txt);
if(catch(call_other(DOMAIN_DIR+domain+"/access","??"))) {
write("Error In Reloading Domain Access Object!\n");
}
}
if(file_size(DOMAIN_DIR+ domain +"/w") != -2) {
mkdir(DOMAIN_DIR+ domain +"/w");
}
if(file_size(DOMAIN_DIR+ domain +"/w/"+ owner) != -2) {
tell_object(caller,"Adding Domain directory...\n");
mkdir(DOMAIN_DIR+ domain +"/w/"+ owner);
}
if(file_size(DOMAIN_DIR+ domain +"/w/"+ owner +"/access.c") < 0) {
tell_object(caller,"Adding Configurable Access Object...\n");
txt = read_bytes(DOMAIN_DIR +"p_access.dom",
0,
file_size(DOMAIN_DIR +"p_access.dom"));
write_file(DOMAIN_DIR+domain+"/w/"+owner+"/access.c",txt);
if(catch(call_other(DOMAIN_DIR+domain+"/w/"+owner+"/access","??"))) {
write("Error In Reloading Domain Creator Access Object!\n");
}
}
}
}
/*********************************************/
/* set up file security for amylaar or mudos */
#if defined(AMYLAAR) || defined(MUDOS_DR)
status restricted_path(string file) {
string *path;
int i;
/* these are paths that can only be write-accessed by objects that
* valid_player_call(object) returns 1.
*/
path = ({
"/include/%s", "/obj/%s", "/usr/%s", "/secure/%s", "/inherit/%s",
"/function/%s", "/skills/%s", "/objects/%s",
});
#ifdef MASTER_DIR
path += MASTER_DIR;
#endif /* MASTER_DIR */
for(i = sizeof(path); i--; ) {
if(sscanf(file, path[i], file)) return 1;
}
return 0;
}
#ifdef AMYLAAR
mixed valid_write(string path,
string eff_user,
string call_fun,
object caller) {
#else /* mudos */
mixed valid_write(string path,
mixed caller,
string call_fun) {
#endif /* mudos */
string domain, who, file;
int i;
if(!caller || stringp(caller)) caller = previous_object();
/* clean up file path */
if(!path) return 0;
path = "/"+ path;
while(sscanf(path,"%s//%s",path,file)) path += "/"+ file;
if(sscanf(path,"%s..%s",path,file)) return 0; /* illegal path */
/* master objects have access to everything */
if(valid_master_call(caller)) {
return extract(path,1);
}
/* validate caller for restricted paths */
if(restricted_path(path) && !valid_player_call(caller)) {
if(query_player_level("error messages")) {
write("Invalid Write Access by "+ file_name(caller)
+" for path "+ path +",\nEfun: "+ call_fun +"\n");
}
return 0;
}
/* make validations for specific efuns */
switch(call_fun) {
case "save_object":
if(sscanf(path,"/usr/%s",file)) {
return extract(path,1);
}
#if defined(MAIL_DIR) && defined(MAILER)
if(sscanf(path,"/"+ MAIL_DIR +"%s", file)) { /* only mailer here */
file = file_name(caller);
sscanf(file,"%s#%d",file,i);
#ifdef INTERMUD
if(file == "secure/UDP_CMD_DIR/mail") return extract(path,1);
#endif /* INTERMUD */
return (file == MAILER) ? extract(path,1) : 0;
}
#endif /* MAILER */
if(sscanf(path,"/banish/%s",file)) {
if(valid_player_call(caller)) return extract(path,1);
}
else {
#ifdef PARANOIA
if(!(who = get_wiz_name(extract(path,1)))) {
if(!sscanf(path,"/ob_saves/%s",file)) return 0;
return extract(path,1);
}
if(who == get_wiz_name(file_name(caller))) {
return extract(path,1);
}
return 0;
#else
return extract(path,1);
#endif /* PARANOIA */
}
break;
case "ed_start":
if(!valid_player_call(caller)) return 0;
remove_current_edit(path);
break;
case "mkdir": /* domain stuff */
if(!valid_player_call(caller)) return 0;
/* Necassary to make sure Access objects are automatically made! */
if(sscanf(path,DOMAIN_DIR +"%s",domain) == 1) {
if(sscanf(domain,"%s/%s",domain,file) != 2) {
write("Use 'domain' command to make a New Domain.\n");
return 0; /* only create_wizard() fn can add domain creators */
}
if(sscanf(path,DOMAIN_DIR +"%s/w/%s",domain,file) == 2) {
if(sscanf(file,"%s/%s",who,file) != 2) {
write("Use 'domain' command to add a New Creator Directory.\n");
return 0; /* only create_wizard() fn can add domain creators */
}
}
}
if(sscanf(path,WIZARD_DIR +"%s", file) == 1) {
if(sscanf(file,"%s/%s",who,file) != 2) {
write("Use 'promote' command to add a New Creator Directory.\n");
return 0; /* only create_wizard() fn can add creators */
}
}
break;
case "write_file":
if(sscanf(path,"/log/%s",file)) {
return extract(path,1);
}
case "cindent":
case "rmdir":
case "do_rename":
case "remove_file":
case "write_bytes":
#ifdef PARANOIA
if(get_wiz_name(extract(path,1)) == get_wiz_name(file_name(caller))) {
break;
}
if(!valid_player_call(caller)) return 0;
#endif /* PARANOIA */
break;
}
/* check the original interactive caller's access */
#ifdef AMYLAAR
if(this_interactive()) {
if(this_interactive() != this_player()) {
funcall(bind_lambda(#'enable_commands,this_interactive()));
}
caller = this_interactive();
#else /* mudos */
if(this_player(1)) {
caller = this_player(1);
#endif /* mudos */
/* validate unique master objects */
if((file = (string)caller->valid_write(path))) {
for(i = sizeof(MASTER_FILES); i--; ) {
if(path == MASTER_FILES[i]
&& !this_player()->secure(MASTER_SECURITY)) {
file = 0;
break;
}
}
}
/* validate master directories */
#ifdef MASTER_DIR
if(file) {
for(i = sizeof(MASTER_DIR); i--; ) {
if(sscanf(path,MASTER_DIR[i],who) == 1
&& !this_player()->secure(MASTER_SECURITY)) {
file = 0;
break;
}
}
}
#endif /* MASTER_DIR */
if(!stringp(file)) {
if(query_player_level("error messages")) {
write("Invalid Write Access: "+ path +", Efun: "+ call_fun +"\n");
}
else {
write("Bad file name.\n");
}
return 0;
}
return file;
}
path = (string)PLAYER->valid_write(path);
if(stringp(path)) return path;
return 0; /* access denied */
}
#ifdef AMYLAAR
mixed valid_read(string path,
string eff_user,
string call_fun,
object caller) {
#else /* mudos */
mixed valid_read(string path,
mixed caller,
string call_fun) {
#endif /* mudos */
mixed *error;
string file;
int i;
if(!caller || stringp(caller)) caller = previous_object();
#ifdef AMYLAAR
if(!path) {
if(call_fun == "ed_start") {
error = get_error_file((string)this_player()->query_real_name());
if(!error || error[3]) {
write("No error.\n");
return 0;
}
write(extract(error[0],1)+" line "+error[1]+": "+error[2]+"\n");
return error[0];
}
return 0;
}
#else
if(!path) return 0;
#endif /* AMYLAAR */
path = "/"+ path;
while(sscanf(path,"%s//%s",path,file)) path += "/"+ file;
/* master objects have access to everything */
if(valid_master_call(caller)) {
return extract(path,1);
}
switch(call_fun) {
case "restore_object": /* we have made the mail reader more secure */
#if defined(MAIL_DIR) && defined(MAILER)
if(sscanf(path,MAIL_DIR +"%s",file)) {
file = file_name(caller);
sscanf(file,"%s#%d",file,i);
#ifdef INTERMUD
if(file == "secure/UDP_CMD_DIR/mail") return 1;
#endif INTERMUD
if(file != MAILER) {
return 0;
}
}
#endif /* MAILER */
return 1;
break;
case "do_rename":
#ifdef INETRMUD
if(file_name(caller) == INETD) return 1;
#endif
break;
case "file_size":
return extract(path,1); /* allow valid read access to everyone */
break;
case "tail":
case "read_bytes":
case "read_file":
if(member_array(path, OPEN_FILES) != -1) {
return extract(path,1);
}
break;
case "ed_start":
if(!valid_player_call(caller)) return 0;
if(!add_current_edit(path)) return 0; /* someone is already editing */
break;
#ifdef MUDOS_DR
case "stat":
#endif
case "get_dir":
if(valid_player_call(caller)) {
return extract(path,1);
}
break;
case "print_file":
break;
}
if(this_player() && interactive(this_player())) {
file = (string)this_player()->valid_read(path);
if(!stringp(file)) {
if(query_player_level("error messages")) {
write("Invalid Read Access: "+ path +", Efun: "+ call_fun +"\n");
}
else {
write("Bad file name.\n");
}
return 0;
}
return file;
}
path = (string)PLAYER->valid_read(path);
if(stringp(path)) return path;
return 0;
}
#endif /* AMYLAAR || MUDOS */
/**************************************************************************/
/* wizlist */
#if defined(AMYLAAR)
#if HAVE_WIZLIST == 0
#include "/include/cfg/wizlist.h"
static void wiz_decay() {
mixed *wl;
int i;
wl = wizlist_info();
for (i=sizeof(wl); i--; ) {
set_extra_wizinfo(wl[i][WL_NAME], wl[i][WL_EXTRA] * 99 / 100);
}
call_out("wiz_decay", 3600);
}
void save_wiz_file() {
rm("/WIZLIST");
write_file(
"/WIZLIST",
implode(
map_array(wizlist_info(),
lambda(({'a}),
({#'sprintf, "%s %d %d\n",
({#'[, 'a, WL_NAME}),
({#'[, 'a, WL_COMMANDS}),
({#'[, 'a, WL_EXTRA})
})
)
), ""
)
);
}
void notify_shutdown() {
if(!valid_master_call(previous_object())) return;
save_wiz_file();
}
#endif /* WIZLIST */
void inaugurate_master(int arg)
{
#if HAVE_WIZLIST == 0
if (!arg)
set_extra_wizinfo(0, allocate(BACKBONE_WIZINFO_SIZE));
if (find_call_out("wiz_decay") < 0)
call_out("wiz_decay", 3600);
#endif /* HAVE_WIZLIST */
#if defined(AMYLAAR321)
set_driver_hook(
H_MOVE_OBJECT0,
unbound_lambda( ({'item, 'dest}), ({#',,
#ifdef NATIVE_MODE
({#'?, ({#'!=, 'item, ({#'this_object})}),
({#'raise_error,
"Illegal to move other object than this_object()\n"}) }),
#endif
#ifdef COMPAT_FLAG
({#'&&, ({#'living, 'item}), ({#'environment, 'item}), ({#',,
({#'efun::set_this_player, 'item}),
({#'call_other, ({#'environment, 'item}), "exit", 'item}),
}) }),
#endif
({#'efun::efun308, 'item, 'dest}),
({#'?, ({#'living, 'item}), ({#',,
({#'efun::set_this_player, 'item}),
({#'call_other, 'dest, "init"}),
({#'?, ({#'!=, ({#'environment, 'item}), 'dest}), ({#'return})}),
}) }),
({#'=, 'others, ({#'all_inventory, 'dest}) }),
({#'=, ({#'[, 'others, ({#'member, 'others, 'item}) }), 0}),
({#'filter_array, 'others,
({#'bind_lambda,
unbound_lambda( ({'ob, 'item}),
({#'?, ({#'living, 'ob}), ({#',,
({#'efun::set_this_player, 'ob}),
({#'call_other, 'item, "init"}),
}) })
)
}),
'item,
}),
({#'?, ({#'living, 'item}), ({#',,
({#'efun::set_this_player, 'item}),
({#'filter_objects, 'others, "init"}),
}) }),
({#'?, ({#'living, 'dest}), ({#',,
({#'efun::set_this_player, 'dest}),
({#'call_other, 'item, "init"}),
}) }),
}) )
);
#ifdef COMPAT_FLAG
set_driver_hook(
H_LOAD_UIDS,
unbound_lambda( ({'object_name}),
({#'?,
({#'==,
({#'sscanf, 'object_name, "players/%s", 'wiz_name}),
1,
}),
({#'?,
({#'==,
({#'sscanf, 'wiz_name, "%s/%s", 'start, 'trailer}),
2,
}),
({#'&&, ({#'strlen, 'start}), 'start}),
'wiz_name
}),
({#'&&,
({#'!=, ({#'[..], 'object_name, 0, 3}), "ftp/"}),
({#'!=, ({#'[..], 'object_name, 0, 4}), "open/"}),
})
})
)
);
set_driver_hook(
H_CLONE_UIDS,
unbound_lambda( ({'blueprint, 'new_name}), ({
#'||,
({#'creator, 'blueprint}),
({#'creator, ({#'previous_object})}),
1
}) )
);
set_driver_hook(H_CREATE_SUPER, "reset");
set_driver_hook(H_CREATE_OB, "reset");
set_driver_hook(H_CREATE_CLONE, "reset");
#else
/* the following closures illustrate how the 3.1.2 driver used to
* set (e)uids. It should be usable for tests, but to get better
* performance, you are encouraged to replace the function
* calls to get_bb_uid() and creator_file() by the equivalent code,
* even if you are satisfied with the current behaviour.
*/
set_driver_hook(
H_LOAD_UIDS,
unbound_lambda( ({'object_name}), ({
#'?,
({#'==,
({#'=, 'creator_name, ({#'creator_file, 'object_name})}),
({#'getuid, ({#'previous_object})}),
}),
({#'geteuid, ({#'previous_object})}),
({#'==, 'creator_name, ({#'get_bb_uid})}),
({#'geteuid, ({#'previous_object})}),
({#'({, 'creator_name, 1}),
}) )
);
set_driver_hook(
H_CLONE_UIDS,
unbound_lambda( ({ /* object */ 'blueprint, 'new_name}), ({
#'?,
({#'==,
({#'=, 'creator_name, ({#'creator_file, 'new_name})}),
({#'getuid, ({#'previous_object})}),
}),
({#'geteuid, ({#'previous_object})}),
({#'==, 'creator_name, ({#'get_bb_uid})}),
({#'geteuid, ({#'previous_object})}),
({#'({, 'creator_name, 1}),
}) )
);
#ifdef NATIVE_MODE
set_driver_hook(H_CREATE_OB, "create");
set_driver_hook(H_CREATE_CLONE, "create");
#else
set_driver_hook(H_CREATE_SUPER,
unbound_lambda(0, ({#',,
({#'call_other, ({#'this_object}), "create"}),
({#'call_other, ({#'this_object}), "reset"})
})
)
);
set_driver_hook(H_CREATE_OB,
unbound_lambda(0, ({#',,
({#'call_other, ({#'this_object}), "create"}),
({#'call_other, ({#'this_object}), "reset"})
})
)
);
set_driver_hook(H_CREATE_CLONE,
unbound_lambda(0, ({#',,
({#'call_other, ({#'this_object}), "create"}),
({#'call_other, ({#'this_object}), "reset"})
})
)
);
#endif
#endif
set_driver_hook(H_RESET, "reset");
set_driver_hook(H_CLEAN_UP, "clean_up");
#endif /* AMYLAAR321 */
}
#if defined(AMYLAAR321)
mixed current_time;
string *epilog(int eflag) {
if (eflag) return ({});
debug_message(sprintf("Loading init file %s\n", INIT_FILE));
current_time = rusage();
current_time = current_time[0] + current_time[1];
return explode(read_file(INIT_FILE), "\n");
}
void preload(string file) {
int last_time;
if (strlen(file) && file[0] != '#') {
last_time = current_time;
debug_message(sprintf("Preloading: %s", file));
call_other(file, "");
current_time = rusage();
current_time = current_time[0] + current_time[1];
debug_message(sprintf(" %.2f\n", (current_time - last_time)/1000.));
}
}
#endif /* AMYLAAR321 */
#endif /* AMYLAAR */