MudOSa4DGD/
MudOSa4DGD/bin/
MudOSa4DGD/data/
MudOSa4DGD/doc/
MudOSa4DGD/doc/driver/
MudOSa4DGD/doc/efun/bitstrings/
MudOSa4DGD/doc/efun/command/
MudOSa4DGD/doc/efun/communication/
MudOSa4DGD/doc/efun/heart_beat/
MudOSa4DGD/doc/efun/interactive/
MudOSa4DGD/doc/efun/inventory/
MudOSa4DGD/doc/efun/living/
MudOSa4DGD/doc/efun/mappings/
MudOSa4DGD/doc/efun/strings/
MudOSa4DGD/doc/efun/uid/
MudOSa4DGD/doc/funs/
MudOSa4DGD/doc/language/
MudOSa4DGD/mudlib/dgd/doc/
MudOSa4DGD/mudlib/dgd/lib/include/dgd/
MudOSa4DGD/mudlib/dgd/lib/std/
MudOSa4DGD/mudlib/dgd/lib/sys/
MudOSa4DGD/mudlib/dgd/log/
MudOSa4DGD/mudlib/log/
MudOSa4DGD/mudlib/std/include/
MudOSa4DGD/mudlib/std/obj/
/*
 * 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,
#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";
}