/*
* master.c
*
* The Master object. It handles global access to different
* resources managed by the DriverLib in MudOS alike for DGD.
*
* This object may be freely modified to suit a specific mudlib.
*
* Started by: Frank Schmidt, Jesus@NorseMUD
*
*/
#define __MASTER
#include <driver.h>
/* mudlib includes needed in this object: */
#include <std.h>
#include <levels.h>
/* maximum trace lines to use in runtime_error() */
#define TRACE_LINES 6
/* max filesize for runtime error log */
#define MAX_DEBUG_LOG 2000000
/* create us */
static void __CREATE_DEF() {
if (object_name(this_object()) != MASTER) {
illegal();
return;
}
::__CREATE_DEF();
#ifdef MUDOS_USER_ID
seteuid(getuid());
#endif
}
/* unmovable */
varargs int __MOVE_DEF(object dest) {
illegal();
}
void __DESTROY_DEF() {
if (!DRIVER_PRIV()) {
/* prevents illegal destruction */
illegal();
return;
}
::__DESTROY_DEF();
}
/* prevent cloning of Master object! */
int __QUERY_PREVENT_CLONE_DEF() {
return 1;
}
/*
* Typical functions defined and called by the DriverLib for
* letting this modifiable object decide how to deal with
* events and permissions
*/
/* return uid of file <str> */
string __CREATOR_FILE_DEF(string str) {
string wiz;
if (!str) return 0;
if (str[0] != '/')
str = "/" + str;
/* master? */
if (str == MASTER)
return ROOT_UID;
/* user? */
if (wiz=(string)::creator_file(str))
return wiz;
/* root? */
if (strcocmp(SECURE_DIR, str))
return ROOT_UID;
/* backbone? */
if (
strcocmp(STD_DIR, str) ||
strcocmp(COM_DIR, str) ||
strcocmp(ROOM_DIR, str) ||
strcocmp(OBJ_DIR, str)
)
return BACKBONE_UID;
/* noname? */
if (strcocmp(DOC_DIR, str))
return NONAME_UID;
return 0;
}
#ifdef MUDOS_USER_ID
/* valid eff. user id to set on object ob? */
int __VALID_SETEUID_DEF(object ob, string uid) {
string misc;
/* valid to let root/backbone uid set whatever it wants */
switch (misc = getuid(ob)) {
case ROOT_UID:
case BACKBONE_UID:
return 1;
}
/* valid to set euid = uid */
if (misc == uid)
return 1;
/* valid to let root/backbone creator set whatever it wants */
switch (misc = __CREATOR_FILE_DEF(file_name(ob))) {
case ROOT_UID:
case BACKBONE_UID:
return 1;
}
/* valid to set euid = creator */
if (misc == uid)
return 1;
/* always allow for master object */
if (ob == this_object())
return 1;
/* disallow */
return 0;
}
#endif
/* valid object to clone? */
int __VALID_OBJECT_DEF(object ob) {
return 1;
}
/* return a logon object for a user connection */
object __CONNECT_DEF() {
object login;
/* illegal attempt to connect()? */
if (!DRIVER_PRIV()) {
illegal();
return 0;
}
login = clone_object(I_LOGIN);
return login;
}
/* valid exec()? (passing a user connection from object to object) */
private mapping valid_exec;
private string *valid_exec_obs;
int __VALID_EXEC_DEF(object to, object from, object ob) {
string execstr;
/* always allow master */
if (ob == this_object())
return 1;
#if INFORM_DRIVER > 15
DRIVER->send("MV_exec("+file_name(to)+", "+file_name(from)+", "+file_name(ob)+")");
#endif
/* make list of valid objects? */
if (!valid_exec_obs)
#if 0
valid_exec_obs = ({ I_LOGIN, I_NEWPLAYER, I_PLAYER, I_SUBJECT });
#else
valid_exec_obs = ({ I_LOGIN });
#endif
if (member_array(source_file_name(ob), valid_exec_obs) == -1)
return 0;
/* check/make list of valid execs */
if (!valid_exec)
valid_exec = ([
("0->"+I_LOGIN) : 1,
(I_LOGIN+"->0") : 1,
#if 0
(I_LOGIN+"->0") : 1,
(I_LOGIN+ "->"+I_NEWPLAYER) : 1,
(I_LOGIN+ "->"+I_PLAYER) : 1,
(I_LOGIN+ "->"+I_SUBJECT) : 1,
(I_NEWPLAYER+"->"+I_PLAYER) : 1,
#endif
]);
execstr = source_file_name(from) + "->" + source_file_name(to);
#if INFORM_DRIVER > 15
DRIVER->send("MV_execstr = "+execstr);
#endif
return valid_exec[execstr];
}
/*
* Parse a user path, e.g. for supporting relative paths to the
* "current path". Currently only used by ed, but may be called by
* any mudlib objects.
*/
string __PARSE_USER_PATH_DEF(string path, object player, string func) {
/* dunno how to parse path without player object */
if (!player) return path;
switch (func) {
case "editor_read":
case "editor_write":
#if 0
/* relative player-path? */
if (path != "" && path[0] != '/')
path = "/" + ply->query_path() + "/" + path;
#endif
break;
}
/* return the final path */
return path;
}
/* file R/W permission functions */
#if 0
#else
/* no read- & write security! */
int __VALID_READ_DEF(string file, mixed eff_u, string func) {
/*DRIVER->send(sprintf("MV_READ(\"%s\", %O, '%s') -> %O", file, eff_u, func, calling_ob()));*/
return 1;
}
int __VALID_WRITE_DEF(string file, mixed eff_u, string func) {
/*DRIVER->send(sprintf("MV_WRITE(\"%s\", %O, '%s') -> %O", file, eff_u, func, calling_ob()));*/
return 1;
}
#endif
/* log a compile error to its appropiate files */
void __COMPILE_ERROR_DEF(string file, int line, string error) {
string name, message;
/* illegal attempt to log an error? */
if (!DRIVER_PRIV()) {
illegal();
return;
}
message = file + " at line " + (string)line + ": " + error;
#if INFORM_DRIVER > 20
DRIVER->notify("COMPILE_ERROR: "+message);
#endif
/* get the wizard/domain creator */
name = __CREATOR_FILE_DEF(file);
#if 1
switch (name) {
/* case 0:
case ROOT_UID:
case BACKBONE_UID:
break;*/
default:
/* Errors to /players/wizname/log/errors and /log/errors */
log_file("errors", message+"\n");
if (strcocmp(DOMAIN_DIR, file))
log_file(DOMAIN_DIR + name + LOCAL_ERROR_LOG, message);
else if (strcocmp(PLAYER_DIR, file))
log_file(PLAYER_DIR + name + LOCAL_ERROR_LOG, message);
break;
}
#else
/* simple: errors to /log/wizname */
if (!name)
name = "log";
catch(log_file(name, message));
#endif
}
/* handler of runtime errors (not compatible with MudOS') */
varargs void __RUNTIME_ERROR_DEF(string error, int caught, int ticks) {
mixed **trace, args;
int i, sz, line;
object player, player1;
/* illegal attempt to log an error? */
if (!DRIVER_PRIV()) {
illegal();
return;
}
player = this_player();
player1 = this_player1();
trace = call_trace();
/*if (!caught)*/ {
if ((sz=a_sizeof(trace) - 3) >= 0) {
int sz2;
string progname, objname, func;
string error_str, log_str, got_str, trace_str;
func = trace[sz][TRACE_FUNCTION];
progname = trace[sz][TRACE_PROGNAME];
/* innocent functions: (never their fault...) */
while (strcocmp(_DGD_DIR, progname) && member_array(func,
({ "call_other", "error", "runtime_error", "compile_error",
__RUNTIME_ERROR_FUNC, __COMPILE_ERROR_FUNC })) != -1) {
if (--sz < 0)
return;
func = trace[sz][TRACE_FUNCTION];
progname = trace[sz][TRACE_PROGNAME];
}
objname = trace[sz][TRACE_OBJNAME];
line = trace[sz][TRACE_LINE];
args = trace[sz][TRACE_FIRSTARG..];
/* log and show runtime error */
error_str = (caught ? "*Caught " : "Error in ") + "'"+func+"' in "+
progname+" at line "+line+":\n"+
error+ "\n" +
"Trace written to "+DEBUG_LOG+"\n";
/* see what parameters we got */
got_str = "";
if (sz2=a_sizeof(args)) {
got_str += "Got: "+func+"(";
for (i=0; i < sz2; ++i) {
#ifdef MUDOS_SPRINTF
got_str += sprintf("%O", args[i]);
#endif
if (i < sz2-1)
got_str += ", ";
}
got_str += ")\n";
}
/* log it to file */
log_str = "--- " + ctime(time()) + " --- ";
#ifdef MUDOS_USER_ID
log_str += (player ? (" TP="+geteuid(player)+",") : "") +
(player1 ? (" TP1="+geteuid(player1)) : "") + "\n";
#endif
log_str += (caught ? "Caught: " : "") +
error+ "\n" +
"Object: "+progname+" at line "+line+"\n"+
got_str;
/* find trace string */
trace_str = "";
for (i = sz; i > sz-TRACE_LINES && i >= 0; --i) {
string str;
objname = trace[i][TRACE_OBJNAME];
progname = trace[i][TRACE_PROGNAME];
func = trace[i][TRACE_FUNCTION];
line = trace[i][TRACE_LINE];
/* do not show AUTO object programs */
/*
if (progname == AUTO && strlen(func) > 3) {
switch (func[0..1]) {
case "bad":
progname = trace[i - 1][TRACE_PROGNAME];
func = trace[i - 1][TRACE_FUNCTION];
case "__":
break;
}
}*/
/* compose the trace string: */
str = "'"+func+"' in "+progname+".c";
if (progname != objname)
/* real object */
str += " ("+objname+")";
/* linenumber */
str += " at line "+line;
/* add to trace string */
trace_str += str + "\n";
}
#ifdef INFORM_DRIVER
/* write to driver */
# if INFORM_DRIVER <= 1
DRIVER->send("\n"+error_str + got_str + "\n" + trace_str);
# else
DRIVER->send("\n"+log_str + trace_str);
# endif
#endif
/* log to debug log file */
if (file_size(DEBUG_LOG) > MAX_DEBUG_LOG) {
rename_file(DEBUG_LOG, DEBUG_LOG+".old");
}
write_file(DEBUG_LOG, "\n"+log_str + trace_str);
/* write to the player */
if (player) {
if (player->query_level() < STUDENT && object_name(player) != DRIVER) {
message("info",
"You notice a delicate flaw in the creation of Odin.\n",
this_player());
}
else {
message("info", error_str, this_player());
}
}
}
}
}
#ifdef MUDOS_INVENTORY
/* environment is dested, what to do with inventory? */
void __DESTRUCT_ENV_OF_DEF(object ob) {
if (!DRIVER_PRIV()) {
/* prevents illegal epilog */
illegal();
return;
}
#ifdef R_VOID
if (interactive(ob)) {
message("info",
"The world suddenly dissolves before your eyes, and you "+
"feel the utter coldness of the void swallowing you.\n",
this_player());
ob->move(R_VOID);
}
#endif
}
#endif
/* return editor rescuefile (called 'get_save_file_name' in MudOS) */
string __GET_RESCUE_FILE_DEF(string file, mixed player) {
/* illegal attempt to rescue a file? */
if (!DRIVER_PRIV()) {
illegal();
return 0;
}
if (file) {
#ifdef MUDOS_USER_ID
if (objectp(player))
player = geteuid(player);
#else
if (objectp(player))
player = player->query_real_name();
#endif
if (!stringp(player) || !strlen(player))
player = __CREATOR_FILE_DEF(file);
if (player)
return PLAYER_DIR + player + "/.dead_ed_files/" + file;
}
}
/* return list of files to be preloaded */
varargs string *__EPILOG_DEF(int nopreload) {
int i;
string *files, *preloads, str;
if (!DRIVER_PRIV()) {
/* prevents illegal epilog */
illegal();
return 0;
}
if (nopreload) {
message("info", "No preloading.\n", this_player());
return 0;
}
if (str=read_file(PRELOAD_FILE)) {
files = explode(str, "\n");
preloads = ({ });
for (i=0; i < a_sizeof(files); ++i) {
if (strlen(files[i]) > 0 && files[i][0] != '#')
preloads += ({ files[i] });
}
}
else {
message("info",
"Couldn't find preload file '"+PRELOAD_FILE+"'.\n", this_player());
}
/* load domain access files */
message("info", "Loading domain access files.\n", this_player());
files = get_files(DOMAIN_DIR);
for (i=a_sizeof(files); --i >= 0; ) {
if (file_size(DOMAIN_DIR+files[i]) == -2 &&
file_size(DOMAIN_DIR+files[i]+"/file_access.c") > 0) {
string err;
if (err=catch(call_other(DOMAIN_DIR+files[i]+"/file_access", "??"))) {
message("info", "Got error: "+err+" when loading '"+
DOMAIN_DIR+files[i]+"/file_access.c'\n", this_player());
}
}
}
/* return result */
return preloads;
}
/* preload a file at driver startup */
void __PRELOAD_DEF(string file) {
string err;
if (file) {
message("info", "Preloading '"+file+"'...\n", this_player());
#if 0
if (err=catch(call_other(file, "??"))) {
message("info", "Got error: "+err+" when loading '"+file+"'\n",
this_player());
}
#else
compile_object(file);
#endif
}
}
/*
* Functions only supported and called by the mudlib.
* They are not really nescessary at all, and serves
* only as an example of additional functions needed in
* the Master object.
*/
/* list of valid objects to clone player objects */
private mapping valid_player_cloners;
int valid_clone_player(object cloner) {
string cwd;
/* always allow Master object */
if (cloner == this_object())
return 1;
/* check/make list of valid player cloners */
if (!valid_player_cloners)
valid_player_cloners = ([
"/std/obj/" : 1,
"/secure/" : 1,
"/com/g/" : 1
]);
cwd = directory_name(cloner);
return valid_player_cloners[cwd];
}
/* valid logon name? */
varargs int valid_name(string name, int noisy) {
int i, length;
if ((length = strlen(name)) > 12) {
if (noisy)
message("info", "Too long name.\n", this_player());
return 0;
}
name = tolower(name);
for (i = 0; i < length; i++) {
if (name[i] < 'a' || name[i] > 'z') {
if (noisy)
message("info",
"You can only use ordinary letters in your name.\n");
return 0;
}
}
return 1;
}
/* valid logon password? */
int valid_password(string pw, string name) {
if (strlen(pw) < 3) return 0;
pw = tolower(pw);
name = tolower(name);
if (strsrch(pw, name) != -1) return 0;
if (strsrch(name, pw) != -1) return 0;
/* accepted! */
return 1;
}
/* substitute common commands */
string query_command_substitution(string cmd) {
string exit;
/* substitute directions */
if ((exit=get_dir_abbrev(cmd)))
return exit;
return cmd;
}
/* return home directory of a wizard (used in sfun/path.c) */
string query_home_directory(string wizard) {
if (strlen(wizard)) {
if (wizard[0] >= 'A' && wizard[0] <= 'Z')
return DOMAIN_DIR + wizard;
return PLAYER_DIR + wizard;
}
}
/* valid snooping? */
int valid_snoop(object snooper, object snoopee) {
if (!snoopee || (snooper->query_level() >= LESSER_GOD))
return 1;
return 0;
}
/* short description of this object */
string short() {
return "The Master Object";
}