#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#if 0
#include <sys/socket.h> /* for the interactive struct */
#include <sys/un.h>
#include <netinet/in.h>
#endif
#ifndef __WIN32__
#include <sys/wait.h>
#include <sys/signal.h>
#endif
#ifdef FCNTL
#include <fcntl.h>
#endif
#include <sys/stat.h>
#if 0
#include <sys/dir.h>
#else
#include <dirent.h>
#endif
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include "security.h"
#include "proto.h"
#include "regexp.h"
#include "lexer.h"
#include "y.tab.h"
#include "opcodes.h"
#include "comm.h"
#include "consts.h"
#include "hash.h"
#include "fsecure.h"
#include "lpc.h"
//#define LODEBUG
/* #define MAXHACK 64 */
extern int errno, num_made;
char **inherit_file;
char load_error[256] = "";
Shared * last_verb = NULL;
Shared * current_verb = NULL;
extern char **str_consts;
extern char *last_fun;
Obj *command_giver; /* Where the current command came from. */
extern Obj * Emulate;
/*
* Save the name of the current .i file, so we are able to remove it if it
* contains illegal include files.
*/
void secure_object(Obj * ob, char *real_name)
{
char wiz_name[100];
char *suname = 0; /* want a null ptr if no str */
char *np;
Shared * tr;
*wiz_name = 0;
if (sscanf(real_name, "players/%s", wiz_name) == 1)
{
np = strchr(wiz_name, '/');
if (np)
{
*np = '\0';
suname = wiz_name;
}
}
else
{
/* game driver - things in /obj unnamed, other things named */
if (sscanf(real_name, "guilds/%s", wiz_name) == 1)
{
np = strchr(wiz_name, '/');
if (np)
{
*(np + 1) = '\0';
}
}
else if (!(sscanf(real_name, "obj/%s", wiz_name) == 1))
{
strcpy(wiz_name, real_name);
np = strchr(wiz_name, '/');
if (np)
{
*(np + 1) = '\0';
}
}
/* add_name decides on the game driver's name */
}
#ifdef SECURE
tr = string_copy(real_name);
init_object_su(ob, tr, ob->code->file_was_suid); /* -1 means look up obj */
free_string(tr);
#endif
}
//
// Instantiate Class
//
Obj * instantiate_class(Class * code)
{
int i;
Obj * ret;
ret = get_empty_object(code->num_variables);
//assert(code != NULL);
//printf("instantiate %d\n", code->num_variables);
ret->code = code;
add_class_ref(code, "instantiate_class");
ret->name = shared_string_copy(code->name);
// Initialize variables...
//ret->variables = malloc(sizeof(Val) * code->num_variables);
for (i = 0; i < ret->code->num_variables; i++)
{
ret->variables[i].u.number = 0;
ret->variables[i].type = T_NUMBER;
}
return ret;
}
Obj * load_object(Shared * name)
{
Class * code;
Obj *ob, *save_cmd = NULL;
Val * tv;
if (Emulate)
{
tv = apply_single(C("this_player"), Emulate, 0);
if (tv && tv->type == T_OBJECT && tv->u.ob != NULL) save_cmd = tv->u.ob;
}
// printf("load_object 4\n");
// Create the single instance object (backward compat hack really)
code = find_class(name);
if (code == NULL) code = load_class(name);
if (code == NULL)
{
printf("Failed to load class: %s\n", name->str);
return NULL;
}
ob = instantiate_class(code);
code->ref--;
// Stick it in the object hash table
// NOTE: probably can obsolete this?
//printf("load_object 5\n");
if (ob != NULL)
{
Obj * x;
/* add name to fast object lookup table */
x = enter_object_hash(ob);
if (x == ob)
{
//secure_object(ob, code->name->str);
secure_object(ob, name->str);
}
// Initialise the implicit clone
apply_clean(C("*InitGlobals"), ob, 0);
apply_clean(C("reset"), ob, 0);
}
if (save_cmd && Emulate)
{
apply_clean(C("set_this_player"), Emulate, make_object(save_cmd));
//free_object(save_cmd, "ob_clone_object");
free_value(tv);
}
#ifdef LODEBUG
printf("LOAD: load_object complete (object %s (%d) entered), class %s (%d)\n", ob->name->str, ob->ref, ob->code->name->str, ob->code->ref);
#endif
return ob;
}
Obj *previous_ob;
Shared * make_new_name(Shared * str)
{
static int i;
char *p = malloc(str->length + 10); /* +10 is a LOT of names! */
Shared * q;
do
{
(void) sprintf(p, "%s#%d", str->str, i);
i++;
}
while (find_object_cstr(p));
q = string_copy(p);
free(p);
return q;
}
/*
* Save the command_giver, because reset() in the new object might change it.
* see also (ugh) caught_clone_object (near apply_single).
*/
Val * ob_clone_object(Class * code, Shared *name)
{
Obj *new_ob, *alt_ob, *save_cmd = NULL;
Val *ret;
Val * tv;
assert(code != NULL);
new_ob = instantiate_class(code);
tv = apply_single(C("this_player"), Emulate, 0);
if (tv && tv->type == T_OBJECT && tv->u.ob != NULL) save_cmd = tv->u.ob;
if (name)
{
new_ob->name = shared_string_copy(name);
}
else
{
new_ob->name = make_new_name(new_ob->code->name); /* in string names */
}
new_ob->cloned = 1;
#ifdef SECURE
// printf("ob_clone_object: %s\n", new_ob->name->str);
if (Scurrent())
init_object_su(new_ob, new_ob->code->name, new_ob->code->file_was_suid);
#endif
/* add name to fast object lookup table */
alt_ob = enter_object_hash(new_ob);
if (alt_ob != new_ob)
{
new_ob->destructed = 1;
free_object(new_ob, "do_clone_object");
strcpy(load_error, "creating a clone with an existing name");
return 0;
}
apply_clean(C("*InitGlobals"), new_ob, 0);
apply_clean(C("reset"), new_ob, 0);
if (save_cmd != NULL)
{
apply_clean(C("set_this_player"), Emulate, make_object(save_cmd));
free_value(tv);
//free_object(save_cmd, "ob_clone_object");
}
// did reset or InitGlobals trash our object?
if (new_ob->destructed)
{
ret = make_number(0);
}
else
{
ret = make_object(new_ob);
}
// printf("cloned object %s %d\n", new_ob->name->str, new_ob->ref);
return ret;
}
/* hmm - this all looks a bit suspect */
Val * clone_object(Shared *str1, Shared *name)
{
Class * code;
Val *x;
code = find_class(str1);
if (code == NULL) code = load_class(str1);
if (code == NULL) return NULL;
// printf("clone_object 3\n");
x = ob_clone_object(code, name);
// printf("clone_object 4\n");
return x;
}
Val * environment(Val * arg)
{
Val *ret;
Obj *ob = Scurrent();
if (arg && arg->type == T_OBJECT)
ob = arg->u.ob;
else if (arg && arg->type == T_STRING)
ob = find_object2(arg->u.string);
else
ob = Scurrent();
if (ob == 0 || ob->super == 0)
return Const(0);
ret = make_object(ob->super);
return ret;
}
/*
* To find if an object is present, we have to look in two inventory lists.
* The first list is the inventory of the current object. The second list is
* all things that have the same ->super as current_object. Also test the
* environment. If the second argument 'ob' is non zero, only search in the
* inventory of 'ob'. The argument 'ob' will be mandatory, later.
*/
static Val * object_present2(Val * v, Obj * ob);
Val * object_present(Val * v, Obj * ob)
{
Val *ret, *rep;
int specific = 0;
if (ob == 0)
ob = Scurrent();
else
specific = 1;
if (v->type == T_OBJECT)
{
if (specific)
{
if (v->u.ob->super == ob)
return make_object(v->u.ob);
else
return Const(0);
}
if (v->u.ob->super == ob ||
(v->u.ob->super == ob->super && ob->super != 0))
return make_object(v->u.ob);
return Const(0);
}
ret = object_present2(v, ob->contains);
if (ret) return ret;
if (specific) return Const(0);
if (ob->super)
{
//FIX: really need to add_ref to v (using copy_value)?
rep = apply_single(C("id"), ob->super, v);
// rep = apply_single(C("id"), ob->super, copy_value(v));
if (rep)
{
if (!(rep->type == T_NUMBER && rep->u.number == 0))
{
free_value(rep);
ret = make_object(ob->super);
return ret;
}
}
ret = object_present2(v, ob->super->contains);
if (ret)
return ret;
else
return Const(0);
}
return Const(0);
}
static Val * object_present2(Val * v, Obj * ob)
{
Val *ret, *pr;
char *p;
int count = 0, c, length;
Val item, *new_item = NULL;
item.type = T_STRING;
/* must free_string because it's a local one, not stack allocated */
length = v->u.string->length;
p = v->u.string->str + length - 1;
c = v->u.string->length;
while (p > v->u.string->str && *p >= '0' && *p <= '9')
{
p--;
c--;
}
if (p > v->u.string->str && *p == ' ')
{
count = atoi(p + 1) - 1;
item.u.string = string_ncopy(v->u.string->str, c-1);
}
else
{
item.u.string = shared_string_copy(v->u.string);
}
new_item = &item;
for (; ob; ob = ob->next_inv)
{
pr = apply_single(C("id"), ob, new_item);
if (pr)
{
if (pr->type == T_NUMBER && pr->u.number == 0) continue;
if (count-- > 0) continue;
free_value(pr);
ret = make_object(ob);
free_string(item.u.string);
return ret;
}
}
free_string(item.u.string);
return 0;
}
/* Find an object. If not loaded, load it ! */
//#define FODEBUG
Obj * find_object(Shared * str)
{
extern Obj * Master;
Obj * ob;
Val * ov;
/* Is the object a "normal" object? */
#ifdef FODEBUG
printf("find_object(%s)\n", str->str);
#endif
ob = find_object2(str);
if (ob != NULL) return ob;
#if 1
/* for virtual objects quick lookup */
if (Master != NULL)
{
#ifdef FODEBUG
printf("find_object virtual? (%s)\n", str->str);
#endif
ov = apply_single(C("FindObject"), Master, share_string(str));
if (ov)
{
if ((ov->type == T_OBJECT))
{
// CHECK:
// ov->u.ob->ref--;
return ov->u.ob;
#if 0
ob = ov->u.ob;
free_value(ov);
return ob;
#endif
}
else if ((ov->type == T_STRING))
{
if ((ob = find_object2(ov->u.string)))
{
free_value(ov);
return ob;
}
}
else
{
free_value(ov);
}
}
}
#endif
#ifdef FODEBUG
printf("find_object not virtual: load_object (%s)\n", str->str);
#endif
/* Ok - FindObject() might've forced us to load (recursively); */
/* don't load_object unless we have to. */
ob = find_object2(str);
if (ob != NULL) return ob;
ob = load_object(str);
//ob->ref = 0;
return ob;
}
/* Look for a loaded object. We use a hash tablefor this. */
Obj * find_object2(Shared * shstr)
{
return lookup_object_hash(shstr);
}
Obj * find_object_cstr(char * str)
{
Shared * lstr;
Obj * ret;
while (*str == '/') str++;
lstr = string_copy(str);
ret = lookup_object_hash(lstr);
free_string(lstr);
return ret;
}
/* fatal log */
FILE *flog = 0;
void fatal(const char *fmt,...)
{
static int in_fatal = 0;
/* Prevent double fatal. */
va_list args;
if (flog) /* in case of double fatal */
{
fflush(flog);
fclose(flog);
}
if (in_fatal)
{
abort();
}
in_fatal = 1;
flog = fopen(FATAL_LOG, "a");
va_start(args, fmt);
(void) vfprintf(stderr, fmt, args);
va_end(args);
if (Scurrent())
fprintf(stderr, "Current object was %s\n", Scurrent()->name->str);
if (flog && Scurrent())
{
fprintf(flog, fmt, args);
fprintf(flog, "Current object was %s\n", Scurrent()->name->str);
fprintf(flog, "Dump of variables:\n");
if (Scurrent())
{
int p;
for (p = 0; p < Scurrent()->code->num_variables; p++)
{
Val *v;
#if 1
fprintf(flog, "%20s: ",
Scurrent()->code->global_table[p]->u.string->str);
v = (&Scurrent()->variables[p]);
#endif
if (v == 0)
{
fprintf(flog, "<NULL>");
continue;
}
switch (v->type)
{
case T_NUMBER:
fprintf(flog, "%d", v->u.number);
break;
case T_REAL:
fprintf(flog, "%f", v->u.real);
break;
case T_STRING:
fprintf(flog, "\"%s\"", v->u.string->str);
break;
case T_POINTER:
fprintf(flog, "ARR[length %d]", v->u.vec->size);
break;
case T_OBJECT:
fprintf(flog, "OBJ(%s)", v->u.ob->name->str);
break;
default:
fprintf(flog, "<INVALID TYPE>\n");
return;
}
fprintf(flog, "\n");
}
}
}
fclose(flog);
outputbt();
abort();
/* Shut down the ipc communication. */
ipc_remove();
/*forced_ */ exit(0);
}
int num_error = 0;
/*
* Check that it is an legal path. No '..' are allowed.
*/
int legal_path(char *path)
{
char *p;
Shared * tmp;
int ret;
if (path == NULL) return 0;
/* if (strchr(path, ' ') != NULL) return 0; */
if (path[0] == '/')
path = &path[1];
if (strchr(path, '?') != NULL)
return 0;
for (p = strchr(path, '.'); p != NULL; p = strchr(p + 1, '.'))
{
if (p[1] == '.' || p[1] == '*')
return 0;
}
tmp = string_copy(path);
ret = valid_filename(tmp);
free_string(tmp);
return ret;
}