/*
 *	@file 	esp.c
 *	@brief 	Embedded Server Pages (ESP) core processing.
 *	@overview Embedded Server Pages provides an efficient way to generate 
 *		dynamic pages using server-side Javascript. This code provides 
 *		core processing, and should be called by an associated web 
 *		server URL handler.
 */
/********************************* Copyright **********************************/
/*
 *	@copy	default
 *	
 *	Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
 *	
 *	This software is distributed under commercial and open source licenses.
 *	You may use the GPL open source license described below or you may acquire 
 *	a commercial license from Mbedthis Software. You agree to be fully bound 
 *	by the terms of either license. Consult the LICENSE.TXT distributed with 
 *	this software for full details.
 *	
 *	This software is open source; you can redistribute it and/or modify it 
 *	under the terms of the GNU General Public License as published by the 
 *	Free Software Foundation; either version 2 of the License, or (at your 
 *	option) any later version. See the GNU General Public License for more 
 *	details at: http://www.mbedthis.com/downloads/gplLicense.html
 *	
 *	This program is distributed WITHOUT ANY WARRANTY; without even the 
 *	implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *	
 *	This GPL license does NOT permit incorporating this software into 
 *	proprietary programs. If you are unable to comply with the GPL, you must
 *	acquire a commercial license to use this software. Commercial licenses 
 *	for this software and support services are available from Mbedthis 
 *	Software at http://www.mbedthis.com 
 *	
 *	@end
 */
/********************************** Includes **********************************/

#include	"esp.h"

#if BLD_FEATURE_ESP_MODULE

/*********************************** Locals ***********************************/
/*
 *	Master ESP control interface with the web server
 */

static const Esp *esp;

/***************************** Forward Declarations ***************************/

static int	buildScript(EspRequest *ep, char **jsBuf, char *input, char
				**errMsg);

/************************************ Code ************************************/
/*
 *	Called at server initialization
 */

int espOpen(const Esp *control)
{
	mprAssert(control);

#if BLD_FEATURE_MULTITHREAD
	ejsOpen(control->lock, control->unlock, control->lockData);
#else
	ejsOpen(0, 0, 0);
#endif

	/*
 	 *	Register the standard procedures
 	 */
	espRegisterProcs();

	/*
 	 *	Just for brain dead systems that don't zero global memory
 	 */
	esp = control;
	return 0;
}

/******************************************************************************/
/*
 *	Called at server termination
 */

void espClose()
{
	ejsClose();
}

/******************************************************************************/
/*
 *	Create for new ESP request. Assumed that this is called after all the 
 *	HTTP headers have been read but before POST data has been read. It is 
 *	expected that any session cookies have been read and that "variables" 
 *	contains references to all the environment objects including "session". 
 *	requestHandle is the web server request handle.
 */

EspRequest *espCreateRequest(EspHandle webServerRequestHandle, char *uri, 
	MprVar *variables)
{
	EspRequest	*ep;
	MprVar		*global;
#if BLD_FEATURE_LEGACY_API
	MprVar		*np;
	char		keyBuf[ESP_MAX_HEADER];
	int			i;
#endif

	mprAssert(variables);

	ep = mprMalloc(sizeof(EspRequest));
	if (ep == 0) {
		return 0;
	}
	memset(ep, 0, sizeof(EspRequest));
	ep->requestHandle = webServerRequestHandle;
	ep->esp = esp;
	ep->uri = mprStrdup(uri);
	ep->docPath = 0;
	ep->variables = variables;
	
	/*
 	 *	The handle passed to ejsOpenEngine is passed to every C function 
	 *	called by JavaScript.
	 */
	ep->eid = ejsOpenEngine((EjsHandle) ep, (EjsHandle) webServerRequestHandle);
	if (ep->eid < 0) {
		mprFree(ep);
		return 0;
	}

	/*
	 *	All these copies and SetProperties will only copy references 
	 *	They will increments the object ref counts.
	 */
	mprCopyVar(&variables[ESP_GLOBAL_OBJ], ejsGetGlobalObject(ep->eid), 
		MPR_SHALLOW_COPY);
	mprCopyVar(&variables[ESP_LOCAL_OBJ], ejsGetLocalObject(ep->eid), 
		MPR_SHALLOW_COPY);

	global = &variables[ESP_GLOBAL_OBJ];
	mprCreateProperty(global, "application", &variables[ESP_APPLICATION_OBJ]);
	mprCreateProperty(global, "cookies", &variables[ESP_COOKIES_OBJ]);
	mprCreateProperty(global, "files", &variables[ESP_FILES_OBJ]);
	mprCreateProperty(global, "form", &variables[ESP_FORM_OBJ]);
	mprCreateProperty(global, "headers", &variables[ESP_HEADERS_OBJ]);
	mprCreateProperty(global, "request", &variables[ESP_REQUEST_OBJ]);

	/*
	 *	FUTURE -- could server be shared across all requests for a given host
	 *	and be made read-only.
	 */
	mprCreateProperty(global, "server", &variables[ESP_SERVER_OBJ]);

#if BLD_FEATURE_SESSION
	mprCreateProperty(global, "session", &variables[ESP_SESSION_OBJ]);
#endif

#if BLD_FEATURE_LEGACY_API
	/*
	 *	DEPRECATED: 2.0
	 *	Define variables as globals. headers[] are prefixed with "HTTP_".
 	 *	NOTE: MaRequest::setVar does not copy into globals, whereas espSetVar
	 *	does if legacy_api is defined. So variables pre-defined by MaRequest 
 	 *	must be copied here into globals[].
	 *
	 *	NOTE: if a variable is in session[] and in form[], the form[] will
	 *	override being later in the variables[] list. Use mprSetProperty 
	 *	instead of mprCreateProperty to cover for this case.
	 */
	for (i = 0; i < ESP_OBJ_MAX; i++) {
		if (i == ESP_GLOBAL_OBJ || i == ESP_LOCAL_OBJ) {
			continue;
		}
		if (variables[i].type != MPR_TYPE_OBJECT) {
			continue;
		}
		np = mprGetFirstProperty(&variables[i], MPR_ENUM_DATA);
		while (np) {
			if (i == ESP_HEADERS_OBJ) {
				mprSprintf(keyBuf, sizeof(keyBuf) - 1, "HTTP_%s", np->name);
				mprSetProperty(global, keyBuf, np);
			} else {
				mprSetProperty(global, np->name, np);
			}
			np = mprGetNextProperty(&variables[i], np, MPR_ENUM_DATA);
		}
	}
#endif
	return ep;
}

/******************************************************************************/
 
void espDestroyRequest(EspRequest *ep)
{
	mprAssert(ep);
	mprAssert(ep->eid >= 0);

	mprFree(ep->uri);
	mprFree(ep->docPath);
	ejsCloseEngine(ep->eid);
	mprFree(ep);
}

/******************************************************************************/
/*
 *	The callback function will be called:
 *
 *		(fn)(EjsId eid, EspRequest *ep, argc, argv);
 *
 *	Callers can get their web server handle by calling:
 *
 *		rq = (requiredCast) espGetHandle(ep);
 */

void espDefineCFunction(EspRequest *ep, const char *functionName, EspCFunction fn, 
	void *thisPtr)
{
	mprAssert(functionName && *functionName);
	mprAssert(fn);

	if (ep) {
		ejsDefineCFunction(ep->eid, functionName, (MprCFunction) fn, 
			thisPtr, 0);
	} else {
		ejsDefineCFunction(-1, functionName, (MprCFunction) fn, thisPtr, 0);
	}
}

/******************************************************************************/

void espDefineStringCFunction(EspRequest *ep, const char *functionName, 
	EspStringCFunction fn, void *thisPtr)
{
	mprAssert(functionName && *functionName);
	mprAssert(fn);

	if (ep) {
		ejsDefineStringCFunction(ep->eid, functionName, (MprStringCFunction) fn,
			thisPtr, 0);
	} else {
		ejsDefineStringCFunction(-1, functionName, (MprStringCFunction) fn, 
			thisPtr, 0);
	}
}

/******************************************************************************/

void *espGetRequestHandle(EspRequest *ep)
{
	return ep->requestHandle;
}

/******************************************************************************/

EjsId espGetScriptHandle(EspRequest *ep)
{
	return ep->eid;
}

/******************************************************************************/

char *espGetStringVar(EspRequest *ep, EspEnvType oType, char *var, 
	char *defaultValue)
{
	MprVar	value;

	if (espGetVar(ep, oType, var, &value) < 0 || 
			value.type != MPR_TYPE_STRING) {
		return defaultValue;
	} 
	return value.string;
}

/******************************************************************************/

int espGetVar(EspRequest *ep, EspEnvType oType, char *var, MprVar *value)
{
	MprVar		*vp;

	mprAssert(ep);
	mprAssert(var);

	vp = mprGetProperty(&ep->variables[oType], var, 0);
	if (vp == 0) {
		return -1;
	}
	*value = *vp;
	return 0;
}

/******************************************************************************/
/*
 *	Process the ESP page. docBuf holds the page already. We expect that
 *	ep->variables holds all the pertinent environment variables.
 */

int espProcessRequest(EspRequest *ep, const char *docPath, char *docBuf, 
	char **errMsg)
{
	char	*jsBuf;

	mprAssert(ep);

	ep->docPath = mprStrdup(docPath);

	jsBuf = 0;
	if (buildScript(ep, &jsBuf, docBuf, errMsg) < 0) {
		return MPR_ERR_CANT_COMPLETE;
	}

	if (jsBuf) {
		mprLog(7, "esp: script is:\n%s\n", jsBuf);

		/*
		 *	Now evaluate the entire escript
		 *	MOB could cache the script 
		 */
		if (ejsEvalScript(ep->eid, jsBuf, 0, errMsg) < 0) {
			return MPR_ERR_ABORTED;
		}

		mprFree(jsBuf);
	}
	return 0;
}

/******************************************************************************/

void espRedirect(EspRequest *ep, int code, char *url)
{
	mprAssert(ep);
	mprAssert(url);

	ep->esp->redirect(ep->requestHandle, code, url);
}

/******************************************************************************/

void espError(EspRequest *ep, const char *fmt, ...)
{
	va_list		args;
	char		*buf;

	mprAssert(ep);
	mprAssert(fmt);

	va_start(args, fmt);
	mprAllocVsprintf(&buf, MPR_MAX_HEAP_SIZE, fmt, args);
	ejsSetErrorMsg(ep->eid, buf);
	mprFree(buf);
	va_end(args);
}

/******************************************************************************/

void espSetHeader(EspRequest *ep, char *header, bool allowMultiple)
{
	mprAssert(ep);

	ep->esp->setHeader(ep->requestHandle, header, allowMultiple);
}

/******************************************************************************/
/*
 *	Caller does not need to destroy the var
 */

MprVar *espGetResult(EspRequest *ep)
{
	mprAssert(ep);

	return ejsGetReturnValue(ep->eid);
}

/******************************************************************************/

void espSetReturn(EspRequest *ep, MprVar value)
{
	mprAssert(ep);

	ejsSetReturnValue(ep->eid, value);
}

/******************************************************************************/

void espSetReturnString(EspRequest *ep, const char *str)
{
	mprAssert(ep);

	ejsSetReturnValue(ep->eid, mprCreateStringVar(str, 0));
}

/******************************************************************************/

void espSetResponseCode(EspRequest *ep, int code)
{
	mprAssert(ep);

	ep->esp->setResponseCode(ep->requestHandle, code);
}

/******************************************************************************/

void espSetVar(EspRequest *ep, EspEnvType oType, char *var, MprVar value)
{
	mprCreatePropertyValue(&ep->variables[oType], var, value);
}

/******************************************************************************/

void espSetStringVar(EspRequest *ep, EspEnvType oType, 
					 const char *var, const char *value)
{
	/*
 	 *	Will create or update if already existing
 	 */
	mprCreatePropertyValue(&ep->variables[oType], var, 
		mprCreateStringVar(value, 0));
}

/******************************************************************************/

int espUnsetVar(EspRequest *ep, EspEnvType oType, char *var)
{
	return mprDeleteProperty(&ep->variables[oType], var);
}

/******************************************************************************/

int espWrite(EspRequest *ep, char *buf, int size)
{
	mprAssert(ep);
	mprAssert(buf);
	mprAssert(size >= 0);

	return ep->esp->writeBlock(ep->requestHandle, buf, size);
}

/******************************************************************************/

int espWriteString(EspRequest *ep, char *buf)
{
	mprAssert(ep);
	mprAssert(buf);

	return ep->esp->writeBlock(ep->requestHandle, buf, strlen(buf));
}

/******************************************************************************/

int espWriteFmt(EspRequest *ep, char *fmt, ...)
{
	va_list		args;
	char		*buf;
	int			rc, len;

	mprAssert(ep);
	mprAssert(fmt);

	va_start(args, fmt);
	len = mprAllocVsprintf(&buf, MPR_MAX_HEAP_SIZE, fmt, args);
	rc = ep->esp->writeBlock(ep->requestHandle, buf, len);
	mprFree(buf);
	va_end(args);
	return rc;
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
/*
 *	Get a javascript identifier. Must allow x.y['abc'] or x.y["abc"].
 *	Must be careful about quoting and only allow quotes inside []. 
 */

static int getIdentifier(EspParse *parse)
{
	int		atQuote, prevC, c;

	mprAssert(parse);

	atQuote = 0;
	prevC = 0;
	c = *parse->inp++;

	while (isalnum(c) || c == '_' || c == '.' || c == '[' || 
			c == ']' || c == '\'' || c == '\"') {
		if (c == '\'' || c == '\"') {
			if (c == atQuote) {
				atQuote = 0;
			} else if (prevC == '[') {
				atQuote = c;
			} else {
				break;
			}
		}
		if (parse->tokp >= parse->endp) {
			parse->token = (char*) mprRealloc(parse->token, 
				parse->tokLen + ESP_TOK_INCR);
			if (parse->token == 0) {
				return MPR_ERR_CANT_ALLOCATE;
			}
			parse->token[parse->tokLen] = '\0';
			parse->tokLen += ESP_TOK_INCR;
			parse->endp = &parse->token[parse->tokLen - 1];
		}
		*parse->tokp++ = c;
		prevC = c;
		c = *parse->inp++;
	}

	parse->inp--;
	*parse->tokp = '\0';

	return 0;
}

/******************************************************************************/
/*
 *	Get the next ESP input token. input points to the next input token.
 *	parse->token will hold the parsed token. The function returns the token id.
 */

static int getEspToken(int state, EspParse *parse)
{
	char	*cp;
	int		tid, done, c, quoted;

	tid = ESP_TOK_LITERAL;
	parse->tokp = parse->token;
	parse->tokp[0] = '\0';
	quoted = 0;

	c = *parse->inp++;
	for (done = 0; !done; c = *parse->inp++) {

		/*
		 *	Get room for more characters in the token buffer
		 */
		if (parse->tokp >= parse->endp) {
			parse->token = (char*) mprRealloc(parse->token, 
				parse->tokLen + ESP_TOK_INCR);
			if (parse->token == 0) {
				return ESP_TOK_ERR;
			}
			parse->token[parse->tokLen] = '\0';
			parse->tokp = &parse->token[parse->tokLen - 1];
			parse->tokLen += ESP_TOK_INCR;
			parse->endp = &parse->token[parse->tokLen - 1];
		}

		switch (c) {
		case 0:
			if (*parse->token) {
				done++;
				parse->inp--;
				break;
			}
			return ESP_TOK_EOF;

		default:
			if (c == '\"' && state != ESP_STATE_IN_ESP_TAG) {
				*parse->tokp++ = '\\';
			}
			*parse->tokp++ = c;
			quoted = 0;
			break;

		case '\\':
			quoted = 1;
			*parse->tokp++ = c;
			break;

		case '@':
			if (*parse->inp == '@' && state != ESP_STATE_IN_ESP_TAG) {
				if (quoted) {
					parse->tokp--;
					quoted = 0;
				} else {
					if (*parse->token) {
						parse->inp--;
					} else {
						parse->inp++;
						tid = ESP_TOK_ATAT;
						if (getIdentifier(parse) < 0) {
							return ESP_TOK_ERR;
						}
					}
					done++;
					break;
				}
			}
			*parse->tokp++ = c;
			break;

		case '<':
			if (*parse->inp == '%' && state != ESP_STATE_IN_ESP_TAG) {
				if (quoted) {
					parse->tokp--;
					quoted = 0;
					*parse->tokp++ = c;
					break;
				}
				if (*parse->token) {
					parse->inp--;
					done++;
					break;
				}
				parse->inp++;
				while (isspace((int) *parse->inp)) {
					parse->inp++;
				}
				if (*parse->inp == '=') {
					parse->inp++;
					while (isspace((int) *parse->inp)) {
						parse->inp++;
					}
					tid = ESP_TOK_EQUALS;
					if (getIdentifier(parse) < 0) {
						return ESP_TOK_ERR;
					}
					done++;
					break;
				}
				if (*parse->inp == 'i' && 
						strncmp(parse->inp, "include", 7) == 0 &&
						isspace((int) parse->inp[7])) {
					tid = ESP_TOK_INCLUDE;
					parse->inp += 7;
					while (isspace((int) *parse->inp)) {
						parse->inp++;
					}
					while (*parse->inp && !isspace((int) *parse->inp) && 
							*parse->inp != '%' && parse->tokp < parse->endp) {
						*parse->tokp++ = *parse->inp++;
					}
					*parse->tokp = '\0';
					if (parse->token[0] == '"') {
						parse->tokp = parse->token;
						for (cp = &parse->token[1]; *cp; ) {
							*parse->tokp++ = *cp++;
						}
						if (cp[-1] == '"') {
							parse->tokp--;
						}
						*parse->tokp = '\0';
					}
					
				} else {
					tid = ESP_TOK_START_ESP;
				}
				done++;
				break;
			}
			*parse->tokp++ = c;
			break;

		case '%':
			if (*parse->inp == '>' && state == ESP_STATE_IN_ESP_TAG) {
				if (quoted) {
					parse->tokp--;
					quoted = 0;
				} else {
					if (*parse->token) {
						parse->inp--;
					} else {
						tid = ESP_TOK_END_ESP;
						parse->inp++;
					}
					done++;
					break;
				}
			}
			*parse->tokp++ = c;
			break;
		}
	}

	*parse->tokp = '\0';
	parse->inp--;
	return tid;
}

/******************************************************************************/
/*
 *	Convert an ESP page into a JavaScript. We also expand include files.
 */

static int buildScript(EspRequest *ep, char **jsBuf, char *input, char **errMsg)
{
	EspParse	parse;
	char		path[MPR_MAX_FNAME], dir[MPR_MAX_FNAME], incPath[MPR_MAX_FNAME];
	char		*incBuf, *incText;
	int			state, tid, len, rc, maxScriptSize, incSize;

	mprAssert(ep);
	mprAssert(jsBuf);
	mprAssert(input);

	rc = 0;
	len = 0;
	state = ESP_STATE_BEGIN;
	if (errMsg) {
		*errMsg = 0;
	}

	memset(&parse, 0, sizeof(parse));
	parse.token = (char*) mprMalloc(ESP_TOK_INCR);
	if (parse.token == 0) {
		return MPR_ERR_CANT_ALLOCATE;
	}
	parse.token[0] = '\0';
	parse.tokLen = ESP_TOK_INCR;
	parse.endp = &parse.token[parse.tokLen - 1];
	parse.tokp = parse.token;
	parse.inBuf = input;
	parse.inp = parse.inBuf;

	maxScriptSize = esp->maxScriptSize;

	tid = getEspToken(state, &parse);
	while (tid != ESP_TOK_EOF && len >= 0) {

		switch (tid) {
		default:
		case ESP_TOK_ERR:
			mprFree(parse.token);
			return MPR_ERR_BAD_SYNTAX;
			
		case ESP_TOK_LITERAL:
			len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, 
				"write(\"", parse.token, "\");\n", 0);
			break;

		case ESP_TOK_ATAT:
			/*
			 *	Trick to get undefined variables to evaluate to "".
			 *	Catenate with "" to cause toString to run. 
			 */
			len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, 
				"write(\"\" + ", parse.token, ");\n", 0);
			break;

		case ESP_TOK_EQUALS:
			len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, 
				"write(\"\" + ", parse.token, ");\n", 0);
			state = ESP_STATE_IN_ESP_TAG;
			break;

		case ESP_TOK_START_ESP:
			state = ESP_STATE_IN_ESP_TAG;
			tid = getEspToken(state, &parse);
			while (tid != ESP_TOK_EOF && tid != ESP_TOK_EOF && 
					tid != ESP_TOK_END_ESP && len >= 0) {
				len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, 
					parse.token, 0);
				tid = getEspToken(state, &parse);
			}
			state = ESP_STATE_BEGIN;
			break;

		case ESP_TOK_END_ESP:
			state = ESP_STATE_BEGIN;
			break;

		case ESP_TOK_INCLUDE:
			if (parse.token[0] == '/') {
				mprStrcpy(incPath, sizeof(incPath), parse.token);
			} else {
				mprGetDirName(dir, sizeof(dir), ep->uri);
				mprSprintf(incPath, sizeof(incPath), "%s/%s", 
					dir, parse.token);
			}
			if (esp->mapToStorage(ep->requestHandle, path, sizeof(path),
					incPath, 0) < 0) {
				mprAllocSprintf(errMsg, MPR_MAX_STRING, 
					"Can't find include file: %s", path);
				rc = MPR_ERR_CANT_OPEN;
				break;
			}
			if (esp->readFile(ep->requestHandle, &incText, &incSize, path) < 0){
				mprAllocSprintf(errMsg, MPR_MAX_STRING, 
					"Can't read include file: %s", path);
				rc = MPR_ERR_CANT_READ;
				break;
			}
			incText[incSize] = '\0';

			/*
			 *	Recurse and process the include script
			 */
			incBuf = 0;
			if ((rc = buildScript(ep, &incBuf, incText, errMsg)) < 0) {
				mprFree(incText);
				mprFree(parse.token);
				return rc;
			}

			len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, incBuf, 0);
			mprFree(incText);
			mprFree(incBuf);
			state = ESP_STATE_IN_ESP_TAG;
			break;
		}
		tid = getEspToken(state, &parse);
	}
	mprFree(parse.token);
	if (len < 0) {
		mprAllocSprintf(errMsg, MPR_MAX_STRING, 
			"Script token is too big in %s.\nConfigured maximum is %d.", 
			path, maxScriptSize);
		return MPR_ERR_WONT_FIT;
	}
	return rc;
}

/******************************************************************************/
/******************************* Wrapped Routines *****************************/
/******************************************************************************/

int espCopyVar(EspRequest *ep, char *var, MprVar *value, int copyDepth)
{
	return ejsCopyVar(ep->eid, var, value, copyDepth);
}

/******************************************************************************/

MprVar espCreateObjVar(char *name, int hashSize)
{
	return ejsCreateObj(name, hashSize);
}

/******************************************************************************/

MprVar espCreateArrayVar(char *name, int size)
{
	return ejsCreateArray(name, size);
}

/******************************************************************************/

bool espDestroyVar(MprVar *obj)
{
	return ejsDestroyVar(obj);
}

/******************************************************************************/

MprVar *espCreateProperty(MprVar *obj, char *property, MprVar *newValue)
{
	return mprCreateProperty(obj, property, newValue);
}

/******************************************************************************/

MprVar *espCreatePropertyValue(MprVar *obj, char *property, MprVar newValue)
{
	return mprCreatePropertyValue(obj, property, newValue);
}

/******************************************************************************/

void espDefineFunction(EspRequest *ep, const char *functionName, char *args, char *body)
{
	ejsDefineFunction(ep->eid, functionName, args, body);
}

/******************************************************************************/

int espDeleteProperty(MprVar *obj, char *property)
{
	return mprDeleteProperty(obj, property);
}

/******************************************************************************/

int espDeleteVar(EspRequest *ep, char *var)
{
	return ejsDeleteVar(ep->eid, var);
}

/******************************************************************************/
int espEvalFile(EspRequest *ep, char *path, MprVar *result, char **emsg)
{
	return ejsEvalFile(ep->eid, path, result, emsg);
}

/******************************************************************************/

int espEvalScript(EspRequest *ep, char *script, MprVar *result, char **emsg)
{
	return ejsEvalScript(ep->eid, script, result, emsg);
}

/******************************************************************************/

int espGetPropertyCount(MprVar *obj, int includeFlags)
{
	if (obj->type != MPR_TYPE_OBJECT) {
		return MPR_ERR_BAD_STATE;
	}
	return mprGetPropertyCount(obj, includeFlags);
}

/******************************************************************************/

MprVar *espGetFirstProperty(MprVar *obj, int includeFlags)
{
	return mprGetFirstProperty(obj, includeFlags);
}

/******************************************************************************/

MprVar *espGetGlobalObject(EspRequest *ep)
{
	return ejsGetGlobalObject(ep->eid);
}

/******************************************************************************/

MprVar *espGetLocalObject(EspRequest *ep)
{
	return ejsGetLocalObject(ep->eid);
}

/******************************************************************************/

MprVar *espGetNextProperty(MprVar *obj, MprVar *currentProperty, 
	int includeFlags)
{
	return mprGetNextProperty(obj, currentProperty, includeFlags);
}

/******************************************************************************/

MprVar *espGetProperty(MprVar *obj, char *property, MprVar *value)
{
	return mprGetProperty(obj, property, value);
}

/******************************************************************************/

void *espGetThisPtr(EspRequest *ep)
{
	return ejsGetThisPtr(ep->eid);
}

/******************************************************************************/
#if XX_UNUSED_XX
 
int espReadProperty(MprVar *dest, MprVar *prop)
{
	mprAssert(prop);
	mprAssert(dest);

	*dest = *prop;
	return 0;
}

#endif
/******************************************************************************/

int espReadVar(EspRequest *ep, char *var, MprVar *value)
{
	return ejsReadVar(ep->eid, var, value);
}

/******************************************************************************/

int espRunFunction(EspRequest *ep, MprVar *obj, char *functionName, 
	MprArray *args)
{
	return ejsRunFunction(ep->eid, obj, functionName, args);
}

/******************************************************************************/

MprVar *espSetProperty(MprVar *obj, char *property, MprVar *newValue)
{
	return mprSetProperty(obj, property, newValue);
}

/******************************************************************************/

MprVar *espSetPropertyValue(MprVar *obj, char *property, MprVar newValue)
{
	return mprSetPropertyValue(obj, property, newValue);
}

/******************************************************************************/

int espWriteVar(EspRequest *ep, char *var, MprVar *value)
{
	return ejsWriteVar(ep->eid, var, value);
}

/******************************************************************************/

int espWriteVarValue(EspRequest *ep, char *var, MprVar value)
{
	return ejsWriteVarValue(ep->eid, var, value);
}

/******************************************************************************/
#if XX_UNUSED_XX

int espWriteProperty(MprVar *prop, MprVar *newValue)
{
	return mprWriteProperty(prop, newValue);
}

/******************************************************************************/

int espWritePropertyValue(MprVar *prop, MprVar newValue)
{
	return mprWritePropertyValue(prop, newValue);
}

#endif
/******************************************************************************/

#else	/* !BLD_FEATURE_ESP_MODULE */
void espDummy() {}

/******************************************************************************/
#endif /* BLD_FEATURE_ESP_MODULE */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim:tw=78
 * vim600: sw=4 ts=4 fdm=marker
 * vim<600: sw=4 ts=4
 */