jst.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*
  2. jst.c -- JavaScript templates
  3. Copyright (c) All Rights Reserved. See details at the end of the file.
  4. */
  5. /********************************* Includes ***********************************/
  6. #include "goahead.h"
  7. #include "js.h"
  8. #if ME_GOAHEAD_JAVASCRIPT
  9. /********************************** Locals ************************************/
  10. static WebsHash websJstFunctions = -1; /* Symbol table of functions */
  11. /***************************** Forward Declarations ***************************/
  12. static char *strtokcmp(char *s1, char *s2);
  13. static char *skipWhite(char *s);
  14. /************************************* Code ***********************************/
  15. /*
  16. Process requests and expand all scripting commands. We read the entire web page into memory and then process. If
  17. you have really big documents, it is better to make them plain HTML files rather than Javascript web pages.
  18. Return true to indicate the request was handled, even for errors.
  19. */
  20. static bool jstHandler(Webs *wp)
  21. {
  22. WebsFileInfo sbuf;
  23. char *token, *lang, *result, *ep, *cp, *buf, *nextp, *last;
  24. ssize len;
  25. int rc, jid;
  26. assert(websValid(wp));
  27. assert(wp->filename && *wp->filename);
  28. assert(wp->ext && *wp->ext);
  29. buf = 0;
  30. if ((jid = jsOpenEngine(wp->vars, websJstFunctions)) < 0) {
  31. websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot create JavaScript engine");
  32. goto done;
  33. }
  34. jsSetUserHandle(jid, wp);
  35. if (websPageStat(wp, &sbuf) < 0) {
  36. websError(wp, HTTP_CODE_NOT_FOUND, "Cannot stat %s", wp->filename);
  37. goto done;
  38. }
  39. if (websPageOpen(wp, O_RDONLY | O_BINARY, 0666) < 0) {
  40. websError(wp, HTTP_CODE_NOT_FOUND, "Cannot open URL: %s", wp->filename);
  41. goto done;
  42. }
  43. /*
  44. Create a buffer to hold the web page in-memory
  45. */
  46. len = sbuf.size;
  47. if ((buf = walloc(len + 1)) == NULL) {
  48. websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot get memory");
  49. goto done;
  50. }
  51. buf[len] = '\0';
  52. if (websPageReadData(wp, buf, len) != len) {
  53. websError(wp, HTTP_CODE_NOT_FOUND, "Cannot read %s", wp->filename);
  54. goto done;
  55. }
  56. websPageClose(wp);
  57. websWriteHeaders(wp, (ssize) -1, 0);
  58. websWriteHeader(wp, "Pragma", "no-cache");
  59. websWriteHeader(wp, "Cache-Control", "no-cache");
  60. websWriteEndHeaders(wp);
  61. /*
  62. Scan for the next "<%"
  63. */
  64. last = buf;
  65. for (rc = 0; rc == 0 && *last && ((nextp = strstr(last, "<%")) != NULL); ) {
  66. websWriteBlock(wp, last, (nextp - last));
  67. nextp = skipWhite(nextp + 2);
  68. /*
  69. Decode the language
  70. */
  71. token = "language";
  72. if ((lang = strtokcmp(nextp, token)) != NULL) {
  73. if ((cp = strtokcmp(lang, "=javascript")) != NULL) {
  74. /* Ignore */;
  75. } else {
  76. cp = nextp;
  77. }
  78. nextp = cp;
  79. }
  80. /*
  81. Find tailing bracket and then evaluate the script
  82. */
  83. if ((ep = strstr(nextp, "%>")) != NULL) {
  84. *ep = '\0';
  85. last = ep + 2;
  86. nextp = skipWhite(nextp);
  87. /*
  88. Handle backquoted newlines
  89. */
  90. for (cp = nextp; *cp; ) {
  91. if (*cp == '\\' && (cp[1] == '\r' || cp[1] == '\n')) {
  92. *cp++ = ' ';
  93. while (*cp == '\r' || *cp == '\n') {
  94. *cp++ = ' ';
  95. }
  96. } else {
  97. cp++;
  98. }
  99. }
  100. if (*nextp) {
  101. result = NULL;
  102. if (jsEval(jid, nextp, &result) == 0) {
  103. /*
  104. On an error, discard all output accumulated so far and store the error in the result buffer.
  105. Be careful if the user has called websError() already.
  106. */
  107. rc = -1;
  108. if (websValid(wp)) {
  109. if (result) {
  110. websWrite(wp, "<h2><b>Javascript Error: %s</b></h2>\n", result);
  111. websWrite(wp, "<pre>%s</pre>", nextp);
  112. wfree(result);
  113. } else {
  114. websWrite(wp, "<h2><b>Javascript Error</b></h2>\n%s\n", nextp);
  115. }
  116. websWrite(wp, "</body></html>\n");
  117. rc = 0;
  118. }
  119. goto done;
  120. }
  121. }
  122. } else {
  123. websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Unterminated script in %s: \n", wp->filename);
  124. goto done;
  125. }
  126. }
  127. /*
  128. Output any trailing HTML page text
  129. */
  130. if (last && *last && rc == 0) {
  131. websWriteBlock(wp, last, strlen(last));
  132. }
  133. /*
  134. Common exit and cleanup
  135. */
  136. done:
  137. if (websValid(wp)) {
  138. websPageClose(wp);
  139. if (jid >= 0) {
  140. jsCloseEngine(jid);
  141. }
  142. }
  143. websDone(wp);
  144. wfree(buf);
  145. return 1;
  146. }
  147. static void closeJst()
  148. {
  149. if (websJstFunctions != -1) {
  150. hashFree(websJstFunctions);
  151. websJstFunctions = -1;
  152. }
  153. }
  154. PUBLIC int websJstOpen()
  155. {
  156. websJstFunctions = hashCreate(WEBS_HASH_INIT * 2);
  157. websDefineJst("write", websJstWrite);
  158. websDefineHandler("jst", 0, jstHandler, closeJst, 0);
  159. return 0;
  160. }
  161. /*
  162. Define a Javascript function. Bind an Javascript name to a C procedure.
  163. */
  164. PUBLIC int websDefineJst(char *name, WebsJstProc fn)
  165. {
  166. return jsSetGlobalFunctionDirect(websJstFunctions, name, (JsProc) fn);
  167. }
  168. /*
  169. Javascript write command. This implemements <% write("text"); %> command
  170. */
  171. PUBLIC int websJstWrite(int jid, Webs *wp, int argc, char **argv)
  172. {
  173. int i;
  174. assert(websValid(wp));
  175. for (i = 0; i < argc; ) {
  176. assert(argv);
  177. if (websWriteBlock(wp, argv[i], strlen(argv[i])) < 0) {
  178. return -1;
  179. }
  180. if (++i < argc) {
  181. if (websWriteBlock(wp, " ", 1) < 0) {
  182. return -1;
  183. }
  184. }
  185. }
  186. return 0;
  187. }
  188. /*
  189. Find s2 in s1. We skip leading white space in s1. Return a pointer to the location in s1 after s2 ends.
  190. */
  191. static char *strtokcmp(char *s1, char *s2)
  192. {
  193. ssize len;
  194. s1 = skipWhite(s1);
  195. len = strlen(s2);
  196. for (len = strlen(s2); len > 0 && (tolower((uchar) *s1) == tolower((uchar) *s2)); len--) {
  197. if (*s2 == '\0') {
  198. return s1;
  199. }
  200. s1++;
  201. s2++;
  202. }
  203. if (len == 0) {
  204. return s1;
  205. }
  206. return NULL;
  207. }
  208. static char *skipWhite(char *s)
  209. {
  210. assert(s);
  211. if (s == NULL) {
  212. return s;
  213. }
  214. while (*s && isspace((uchar) *s)) {
  215. s++;
  216. }
  217. return s;
  218. }
  219. #endif /* ME_GOAHEAD_JAVASCRIPT */
  220. /*
  221. Copyright (c) Embedthis Software. All Rights Reserved.
  222. This software is distributed under commercial and open source licenses.
  223. You may use the Embedthis GoAhead open source license or you may acquire
  224. a commercial license from Embedthis Software. You agree to be fully bound
  225. by the terms of either license. Consult the LICENSE.md distributed with
  226. this software for full details and other copyrights.
  227. */