3 * @brief Embedded Server Pages (ESP) core processing.
4 * @overview The ESP handler provides an efficient way to generate
5 * dynamic pages using server-side Javascript. This code provides
6 * core processing, and should be called by an associated web
9 /********************************* Copyright **********************************/
13 * Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
15 * This software is distributed under commercial and open source licenses.
16 * You may use the GPL open source license described below or you may acquire
17 * a commercial license from Mbedthis Software. You agree to be fully bound
18 * by the terms of either license. Consult the LICENSE.TXT distributed with
19 * this software for full details.
21 * This software is open source; you can redistribute it and/or modify it
22 * under the terms of the GNU General Public License as published by the
23 * Free Software Foundation; either version 2 of the License, or (at your
24 * option) any later version. See the GNU General Public License for more
25 * details at: http://www.mbedthis.com/downloads/gplLicense.html
27 * This program is distributed WITHOUT ANY WARRANTY; without even the
28 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
30 * This GPL license does NOT permit incorporating this software into
31 * proprietary programs. If you are unable to comply with the GPL, you must
32 * acquire a commercial license to use this software. Commercial licenses
33 * for this software and support services are available from Mbedthis
34 * Software at http://www.mbedthis.com
38 /********************************** Includes **********************************/
42 #if BLD_FEATURE_ESP_MODULE
44 /*********************************** Locals ***********************************/
46 * Master ESP control interface with the web server
49 static const Esp *esp;
51 /***************************** Forward Declarations ***************************/
53 static int buildScript(EspRequest *ep, char **jsBuf, char *input, char
56 /************************************ Code ************************************/
58 * Called at server initialization
61 int espOpen(const Esp *control)
65 #if BLD_FEATURE_MULTITHREAD
66 ejsOpen(control->lock, control->unlock, control->lockData);
72 * Register the standard procedures
77 * Just for brain dead systems that don't zero global memory
83 /******************************************************************************/
85 * Called at server termination
93 /******************************************************************************/
95 * Create for new ESP request. Assumed that this is called after all the
96 * HTTP headers have been read but before POST data has been read. It is
97 * expected that any session cookies have been read and that "variables"
98 * contains references to all the environment objects including "session".
99 * requestHandle is the web server request handle.
102 EspRequest *espCreateRequest(EspHandle webServerRequestHandle, char *uri,
107 #if BLD_FEATURE_LEGACY_API
109 char keyBuf[ESP_MAX_HEADER];
113 mprAssert(variables);
115 ep = mprMalloc(sizeof(EspRequest));
119 memset(ep, 0, sizeof(EspRequest));
120 ep->requestHandle = webServerRequestHandle;
122 ep->uri = mprStrdup(uri);
124 ep->variables = variables;
127 * The handle passed to ejsOpenEngine is passed to every C function
128 * called by JavaScript.
130 ep->eid = ejsOpenEngine((EjsHandle) ep, (EjsHandle) webServerRequestHandle);
137 * All these copies and SetProperties will only copy references
138 * They will increments the object ref counts.
140 mprCopyVar(&variables[ESP_GLOBAL_OBJ], ejsGetGlobalObject(ep->eid),
142 mprCopyVar(&variables[ESP_LOCAL_OBJ], ejsGetLocalObject(ep->eid),
145 global = &variables[ESP_GLOBAL_OBJ];
146 mprCreateProperty(global, "application", &variables[ESP_APPLICATION_OBJ]);
147 mprCreateProperty(global, "cookies", &variables[ESP_COOKIES_OBJ]);
148 mprCreateProperty(global, "files", &variables[ESP_FILES_OBJ]);
149 mprCreateProperty(global, "form", &variables[ESP_FORM_OBJ]);
150 mprCreateProperty(global, "headers", &variables[ESP_HEADERS_OBJ]);
151 mprCreateProperty(global, "request", &variables[ESP_REQUEST_OBJ]);
154 * FUTURE -- could server be shared across all requests for a given host
155 * and be made read-only.
157 mprCreateProperty(global, "server", &variables[ESP_SERVER_OBJ]);
159 #if BLD_FEATURE_SESSION
160 mprCreateProperty(global, "session", &variables[ESP_SESSION_OBJ]);
163 #if BLD_FEATURE_LEGACY_API
166 * Define variables as globals. headers[] are prefixed with "HTTP_".
167 * NOTE: MaRequest::setVar does not copy into globals, whereas espSetVar
168 * does if legacy_api is defined. So variables pre-defined by MaRequest
169 * must be copied here into globals[].
\r
171 * NOTE: if a variable is in session[] and in form[], the form[] will
172 * override being later in the variables[] list. Use mprSetProperty
173 * instead of mprCreateProperty to cover for this case.
175 for (i = 0; i < ESP_OBJ_MAX; i++) {
176 if (i == ESP_GLOBAL_OBJ || i == ESP_LOCAL_OBJ) {
179 if (variables[i].type != MPR_TYPE_OBJECT) {
182 np = mprGetFirstProperty(&variables[i], MPR_ENUM_DATA);
184 if (i == ESP_HEADERS_OBJ) {
185 mprSprintf(keyBuf, sizeof(keyBuf) - 1, "HTTP_%s", np->name);
186 mprSetProperty(global, keyBuf, np);
188 mprSetProperty(global, np->name, np);
190 np = mprGetNextProperty(&variables[i], np, MPR_ENUM_DATA);
197 /******************************************************************************/
199 void espDestroyRequest(EspRequest *ep)
202 mprAssert(ep->eid >= 0);
205 mprFree(ep->docPath);
206 ejsCloseEngine(ep->eid);
210 /******************************************************************************/
212 * The callback function will be called:
214 * (fn)(EjsId eid, EspRequest *ep, argc, argv);
216 * Callers can get their web server handle by calling:
218 * rq = (requiredCast) espGetHandle(ep);
221 void espDefineCFunction(EspRequest *ep, const char *functionName, EspCFunction fn,
224 mprAssert(functionName && *functionName);
228 ejsDefineCFunction(ep->eid, functionName, (MprCFunction) fn,
231 ejsDefineCFunction(-1, functionName, (MprCFunction) fn, thisPtr, 0);
235 /******************************************************************************/
237 void espDefineStringCFunction(EspRequest *ep, const char *functionName,
238 EspStringCFunction fn, void *thisPtr)
240 mprAssert(functionName && *functionName);
244 ejsDefineStringCFunction(ep->eid, functionName, (MprStringCFunction) fn,
247 ejsDefineStringCFunction(-1, functionName, (MprStringCFunction) fn,
252 /******************************************************************************/
254 void *espGetRequestHandle(EspRequest *ep)
256 return ep->requestHandle;
259 /******************************************************************************/
261 EjsId espGetScriptHandle(EspRequest *ep)
266 /******************************************************************************/
268 char *espGetStringVar(EspRequest *ep, EspEnvType oType, char *var,
273 if (espGetVar(ep, oType, var, &value) < 0 ||
274 value.type != MPR_TYPE_STRING) {
280 /******************************************************************************/
282 int espGetVar(EspRequest *ep, EspEnvType oType, char *var, MprVar *value)
289 vp = mprGetProperty(&ep->variables[oType], var, 0);
297 /******************************************************************************/
299 * Process the ESP page. docBuf holds the page already. We expect that
300 * ep->variables holds all the pertinent environment variables.
303 int espProcessRequest(EspRequest *ep, const char *docPath, char *docBuf,
310 ep->docPath = mprStrdup(docPath);
313 if (buildScript(ep, &jsBuf, docBuf, errMsg) < 0) {
314 return MPR_ERR_CANT_COMPLETE;
318 mprLog(7, "esp: script is:\n%s\n", jsBuf);
321 * Now evaluate the entire escript
322 * MOB could cache the script
324 if (ejsEvalScript(ep->eid, jsBuf, 0, errMsg) < 0) {
325 return MPR_ERR_ABORTED;
333 /******************************************************************************/
335 void espRedirect(EspRequest *ep, int code, char *url)
340 ep->esp->redirect(ep->requestHandle, code, url);
343 /******************************************************************************/
345 void espError(EspRequest *ep, const char *fmt, ...)
354 mprAllocVsprintf(&buf, MPR_MAX_HEAP_SIZE, fmt, args);
355 ejsSetErrorMsg(ep->eid, buf);
360 /******************************************************************************/
362 void espSetHeader(EspRequest *ep, char *header, bool allowMultiple)
366 ep->esp->setHeader(ep->requestHandle, header, allowMultiple);
369 /******************************************************************************/
371 * Caller does not need to destroy the var
374 MprVar *espGetResult(EspRequest *ep)
378 return ejsGetReturnValue(ep->eid);
381 /******************************************************************************/
383 void espSetReturn(EspRequest *ep, MprVar value)
387 ejsSetReturnValue(ep->eid, value);
390 /******************************************************************************/
392 void espSetReturnString(EspRequest *ep, const char *str)
396 ejsSetReturnValue(ep->eid, mprCreateStringVar(str, 0));
399 /******************************************************************************/
401 void espSetResponseCode(EspRequest *ep, int code)
405 ep->esp->setResponseCode(ep->requestHandle, code);
408 /******************************************************************************/
410 void espSetVar(EspRequest *ep, EspEnvType oType, char *var, MprVar value)
412 mprCreatePropertyValue(&ep->variables[oType], var, value);
415 /******************************************************************************/
417 void espSetStringVar(EspRequest *ep, EspEnvType oType,
418 const char *var, const char *value)
421 * Will create or update if already existing
423 mprCreatePropertyValue(&ep->variables[oType], var,
424 mprCreateStringVar(value, 0));
427 /******************************************************************************/
429 int espUnsetVar(EspRequest *ep, EspEnvType oType, char *var)
431 return mprDeleteProperty(&ep->variables[oType], var);
434 /******************************************************************************/
436 int espWrite(EspRequest *ep, char *buf, int size)
440 mprAssert(size >= 0);
442 return ep->esp->writeBlock(ep->requestHandle, buf, size);
445 /******************************************************************************/
447 int espWriteString(EspRequest *ep, char *buf)
452 return ep->esp->writeBlock(ep->requestHandle, buf, strlen(buf));
455 /******************************************************************************/
457 int espWriteFmt(EspRequest *ep, char *fmt, ...)
467 len = mprAllocVsprintf(&buf, MPR_MAX_HEAP_SIZE, fmt, args);
468 rc = ep->esp->writeBlock(ep->requestHandle, buf, len);
474 /******************************************************************************/
475 /******************************************************************************/
476 /******************************************************************************/
478 * Get a javascript identifier. Must allow x.y['abc'] or x.y["abc"].
479 * Must be careful about quoting and only allow quotes inside [].
482 static int getIdentifier(EspParse *parse)
484 int atQuote, prevC, c;
492 while (isalnum(c) || c == '_' || c == '.' || c == '[' ||
493 c == ']' || c == '\'' || c == '\"') {
494 if (c == '\'' || c == '\"') {
497 } else if (prevC == '[') {
503 if (parse->tokp >= parse->endp) {
504 parse->token = (char*) mprRealloc(parse->token,
505 parse->tokLen + ESP_TOK_INCR);
506 if (parse->token == 0) {
507 return MPR_ERR_CANT_ALLOCATE;
509 parse->token[parse->tokLen] = '\0';
510 parse->tokLen += ESP_TOK_INCR;
511 parse->endp = &parse->token[parse->tokLen - 1];
524 /******************************************************************************/
526 * Get the next ESP input token. input points to the next input token.
527 * parse->token will hold the parsed token. The function returns the token id.
530 static int getEspToken(int state, EspParse *parse)
533 int tid, done, c, quoted;
535 tid = ESP_TOK_LITERAL;
536 parse->tokp = parse->token;
537 parse->tokp[0] = '\0';
541 for (done = 0; !done; c = *parse->inp++) {
544 * Get room for more characters in the token buffer
546 if (parse->tokp >= parse->endp) {
547 parse->token = (char*) mprRealloc(parse->token,
548 parse->tokLen + ESP_TOK_INCR);
549 if (parse->token == 0) {
552 parse->token[parse->tokLen] = '\0';
\r
553 parse->tokp = &parse->token[parse->tokLen - 1];
554 parse->tokLen += ESP_TOK_INCR;
555 parse->endp = &parse->token[parse->tokLen - 1];
568 if (c == '\"' && state != ESP_STATE_IN_ESP_TAG) {
569 *parse->tokp++ = '\\';
581 if (*parse->inp == '@' && state != ESP_STATE_IN_ESP_TAG) {
591 if (getIdentifier(parse) < 0) {
603 if (*parse->inp == '%' && state != ESP_STATE_IN_ESP_TAG) {
616 while (isspace((int) *parse->inp)) {
619 if (*parse->inp == '=') {
621 while (isspace((int) *parse->inp)) {
624 tid = ESP_TOK_EQUALS;
625 if (getIdentifier(parse) < 0) {
631 if (*parse->inp == 'i' &&
632 strncmp(parse->inp, "include", 7) == 0 &&
633 isspace((int) parse->inp[7])) {
634 tid = ESP_TOK_INCLUDE;
636 while (isspace((int) *parse->inp)) {
639 while (*parse->inp && !isspace((int) *parse->inp) &&
640 *parse->inp != '%' && parse->tokp < parse->endp) {
641 *parse->tokp++ = *parse->inp++;
644 if (parse->token[0] == '"') {
645 parse->tokp = parse->token;
646 for (cp = &parse->token[1]; *cp; ) {
647 *parse->tokp++ = *cp++;
656 tid = ESP_TOK_START_ESP;
665 if (*parse->inp == '>' && state == ESP_STATE_IN_ESP_TAG) {
673 tid = ESP_TOK_END_ESP;
690 /******************************************************************************/
692 * Convert an ESP page into a JavaScript. We also expand include files.
695 static int buildScript(EspRequest *ep, char **jsBuf, char *input, char **errMsg)
698 char path[MPR_MAX_FNAME], dir[MPR_MAX_FNAME], incPath[MPR_MAX_FNAME];
699 char *incBuf, *incText;
700 int state, tid, len, rc, maxScriptSize, incSize;
708 state = ESP_STATE_BEGIN;
713 memset(&parse, 0, sizeof(parse));
714 parse.token = (char*) mprMalloc(ESP_TOK_INCR);
715 if (parse.token == 0) {
716 return MPR_ERR_CANT_ALLOCATE;
718 parse.token[0] = '\0';
719 parse.tokLen = ESP_TOK_INCR;
720 parse.endp = &parse.token[parse.tokLen - 1];
721 parse.tokp = parse.token;
723 parse.inp = parse.inBuf;
725 maxScriptSize = esp->maxScriptSize;
727 tid = getEspToken(state, &parse);
728 while (tid != ESP_TOK_EOF && len >= 0) {
733 mprFree(parse.token);
734 return MPR_ERR_BAD_SYNTAX;
736 case ESP_TOK_LITERAL:
737 len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0,
738 "write(\"", parse.token, "\");\n", 0);
743 * Trick to get undefined variables to evaluate to "".
744 * Catenate with "" to cause toString to run.
746 len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0,
747 "write(\"\" + ", parse.token, ");\n", 0);
751 len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0,
752 "write(\"\" + ", parse.token, ");\n", 0);
753 state = ESP_STATE_IN_ESP_TAG;
756 case ESP_TOK_START_ESP:
757 state = ESP_STATE_IN_ESP_TAG;
758 tid = getEspToken(state, &parse);
759 while (tid != ESP_TOK_EOF && tid != ESP_TOK_EOF &&
760 tid != ESP_TOK_END_ESP && len >= 0) {
761 len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0,
763 tid = getEspToken(state, &parse);
765 state = ESP_STATE_BEGIN;
768 case ESP_TOK_END_ESP:
769 state = ESP_STATE_BEGIN;
772 case ESP_TOK_INCLUDE:
773 if (parse.token[0] == '/') {
774 mprStrcpy(incPath, sizeof(incPath), parse.token);
776 mprGetDirName(dir, sizeof(dir), ep->uri);
777 mprSprintf(incPath, sizeof(incPath), "%s/%s",
780 if (esp->mapToStorage(ep->requestHandle, path, sizeof(path),
782 mprAllocSprintf(errMsg, MPR_MAX_STRING,
783 "Can't find include file: %s", path);
784 rc = MPR_ERR_CANT_OPEN;
787 if (esp->readFile(ep->requestHandle, &incText, &incSize, path) < 0){
788 mprAllocSprintf(errMsg, MPR_MAX_STRING,
789 "Can't read include file: %s", path);
790 rc = MPR_ERR_CANT_READ;
793 incText[incSize] = '\0';
796 * Recurse and process the include script
799 if ((rc = buildScript(ep, &incBuf, incText, errMsg)) < 0) {
801 mprFree(parse.token);
805 len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, incBuf, 0);
808 state = ESP_STATE_IN_ESP_TAG;
811 tid = getEspToken(state, &parse);
813 mprFree(parse.token);
815 mprAllocSprintf(errMsg, MPR_MAX_STRING,
816 "Script token is too big in %s.\nConfigured maximum is %d.",
817 path, maxScriptSize);
818 return MPR_ERR_WONT_FIT;
823 /******************************************************************************/
824 /******************************* Wrapped Routines *****************************/
825 /******************************************************************************/
827 int espCopyVar(EspRequest *ep, char *var, MprVar *value, int copyDepth)
829 return ejsCopyVar(ep->eid, var, value, copyDepth);
832 /******************************************************************************/
834 MprVar espCreateObjVar(char *name, int hashSize)
836 return ejsCreateObj(name, hashSize);
839 /******************************************************************************/
841 MprVar espCreateArrayVar(char *name, int size)
843 return ejsCreateArray(name, size);
846 /******************************************************************************/
848 bool espDestroyVar(MprVar *obj)
850 return ejsDestroyVar(obj);
853 /******************************************************************************/
855 MprVar *espCreateProperty(MprVar *obj, char *property, MprVar *newValue)
857 return mprCreateProperty(obj, property, newValue);
860 /******************************************************************************/
862 MprVar *espCreatePropertyValue(MprVar *obj, char *property, MprVar newValue)
864 return mprCreatePropertyValue(obj, property, newValue);
867 /******************************************************************************/
869 void espDefineFunction(EspRequest *ep, const char *functionName, char *args, char *body)
871 ejsDefineFunction(ep->eid, functionName, args, body);
874 /******************************************************************************/
876 int espDeleteProperty(MprVar *obj, char *property)
878 return mprDeleteProperty(obj, property);
881 /******************************************************************************/
883 int espDeleteVar(EspRequest *ep, char *var)
885 return ejsDeleteVar(ep->eid, var);
888 /******************************************************************************/
889 int espEvalFile(EspRequest *ep, char *path, MprVar *result, char **emsg)
891 return ejsEvalFile(ep->eid, path, result, emsg);
894 /******************************************************************************/
896 int espEvalScript(EspRequest *ep, char *script, MprVar *result, char **emsg)
898 return ejsEvalScript(ep->eid, script, result, emsg);
901 /******************************************************************************/
903 int espGetPropertyCount(MprVar *obj, int includeFlags)
905 if (obj->type != MPR_TYPE_OBJECT) {
906 return MPR_ERR_BAD_STATE;
908 return mprGetPropertyCount(obj, includeFlags);
911 /******************************************************************************/
913 MprVar *espGetFirstProperty(MprVar *obj, int includeFlags)
915 return mprGetFirstProperty(obj, includeFlags);
918 /******************************************************************************/
920 MprVar *espGetGlobalObject(EspRequest *ep)
922 return ejsGetGlobalObject(ep->eid);
925 /******************************************************************************/
927 MprVar *espGetLocalObject(EspRequest *ep)
929 return ejsGetLocalObject(ep->eid);
932 /******************************************************************************/
934 MprVar *espGetNextProperty(MprVar *obj, MprVar *currentProperty,
937 return mprGetNextProperty(obj, currentProperty, includeFlags);
940 /******************************************************************************/
942 MprVar *espGetProperty(MprVar *obj, char *property, MprVar *value)
944 return mprGetProperty(obj, property, value);
947 /******************************************************************************/
949 void *espGetThisPtr(EspRequest *ep)
951 return ejsGetThisPtr(ep->eid);
954 /******************************************************************************/
957 int espReadProperty(MprVar *dest, MprVar *prop)
967 /******************************************************************************/
969 int espReadVar(EspRequest *ep, char *var, MprVar *value)
971 return ejsReadVar(ep->eid, var, value);
974 /******************************************************************************/
976 int espRunFunction(EspRequest *ep, MprVar *obj, char *functionName,
979 return ejsRunFunction(ep->eid, obj, functionName, args);
982 /******************************************************************************/
984 MprVar *espSetProperty(MprVar *obj, char *property, MprVar *newValue)
986 return mprSetProperty(obj, property, newValue);
989 /******************************************************************************/
991 MprVar *espSetPropertyValue(MprVar *obj, char *property, MprVar newValue)
993 return mprSetPropertyValue(obj, property, newValue);
996 /******************************************************************************/
998 int espWriteVar(EspRequest *ep, char *var, MprVar *value)
1000 return ejsWriteVar(ep->eid, var, value);
1003 /******************************************************************************/
1005 int espWriteVarValue(EspRequest *ep, char *var, MprVar value)
1007 return ejsWriteVarValue(ep->eid, var, value);
1010 /******************************************************************************/
1013 int espWriteProperty(MprVar *prop, MprVar *newValue)
1015 return mprWriteProperty(prop, newValue);
1018 /******************************************************************************/
1020 int espWritePropertyValue(MprVar *prop, MprVar newValue)
1022 return mprWritePropertyValue(prop, newValue);
1026 /******************************************************************************/
1028 #else /* !BLD_FEATURE_ESP_MODULE */
1031 /******************************************************************************/
1032 #endif /* BLD_FEATURE_ESP_MODULE */
1040 * vim600: sw=4 ts=4 fdm=marker
1041 * vim<600: sw=4 ts=4