PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/lighttpd/src/mod_cml_lua.c

https://github.com/clibrepo/04f6ea02286af71632b72d73554979a61da424cf681886674b21ecd595715c12
C | 469 lines | 329 code | 104 blank | 36 comment | 66 complexity | 71fc696095acee10b50b0a36eab98ac4 MD5 | raw file
  1. #include "mod_cml.h"
  2. #include "mod_cml_funcs.h"
  3. #include "log.h"
  4. #include "stream.h"
  5. #include "stat_cache.h"
  6. #include <assert.h>
  7. #include <stdio.h>
  8. #include <errno.h>
  9. #include <time.h>
  10. #include <string.h>
  11. #ifdef USE_OPENSSL
  12. # include <openssl/md5.h>
  13. #else
  14. # include "md5.h"
  15. #endif
  16. #define HASHLEN 16
  17. typedef unsigned char HASH[HASHLEN];
  18. #define HASHHEXLEN 32
  19. typedef char HASHHEX[HASHHEXLEN+1];
  20. #ifdef USE_OPENSSL
  21. #define IN const
  22. #else
  23. #define IN
  24. #endif
  25. #define OUT
  26. #ifdef HAVE_LUA_H
  27. #include <lua.h>
  28. #include <lualib.h>
  29. #include <lauxlib.h>
  30. typedef struct {
  31. stream st;
  32. int done;
  33. } readme;
  34. static const char * load_file(lua_State *L, void *data, size_t *size) {
  35. readme *rm = data;
  36. UNUSED(L);
  37. if (rm->done) return 0;
  38. *size = rm->st.size;
  39. rm->done = 1;
  40. return rm->st.start;
  41. }
  42. static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
  43. int curelem;
  44. lua_pushstring(L, varname);
  45. curelem = lua_gettop(L);
  46. lua_gettable(L, LUA_GLOBALSINDEX);
  47. /* it should be a table */
  48. if (!lua_isstring(L, curelem)) {
  49. lua_settop(L, curelem - 1);
  50. return -1;
  51. }
  52. buffer_copy_string(b, lua_tostring(L, curelem));
  53. lua_pop(L, 1);
  54. assert(curelem - 1 == lua_gettop(L));
  55. return 0;
  56. }
  57. static int lua_to_c_is_table(lua_State *L, const char *varname) {
  58. int curelem;
  59. lua_pushstring(L, varname);
  60. curelem = lua_gettop(L);
  61. lua_gettable(L, LUA_GLOBALSINDEX);
  62. /* it should be a table */
  63. if (!lua_istable(L, curelem)) {
  64. lua_settop(L, curelem - 1);
  65. return 0;
  66. }
  67. lua_settop(L, curelem - 1);
  68. assert(curelem - 1 == lua_gettop(L));
  69. return 1;
  70. }
  71. static int c_to_lua_push(lua_State *L, int tbl, const char *key, size_t key_len, const char *val, size_t val_len) {
  72. lua_pushlstring(L, key, key_len);
  73. lua_pushlstring(L, val, val_len);
  74. lua_settable(L, tbl);
  75. return 0;
  76. }
  77. static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
  78. size_t is_key = 1;
  79. size_t i;
  80. char *key = NULL, *val = NULL;
  81. key = qrystr->ptr;
  82. /* we need the \0 */
  83. for (i = 0; i < qrystr->used; i++) {
  84. switch(qrystr->ptr[i]) {
  85. case '=':
  86. if (is_key) {
  87. val = qrystr->ptr + i + 1;
  88. qrystr->ptr[i] = '\0';
  89. is_key = 0;
  90. }
  91. break;
  92. case '&':
  93. case '\0': /* fin symbol */
  94. if (!is_key) {
  95. /* we need at least a = since the last & */
  96. /* terminate the value */
  97. qrystr->ptr[i] = '\0';
  98. c_to_lua_push(L, tbl,
  99. key, strlen(key),
  100. val, strlen(val));
  101. }
  102. key = qrystr->ptr + i + 1;
  103. val = NULL;
  104. is_key = 1;
  105. break;
  106. }
  107. }
  108. return 0;
  109. }
  110. #if 0
  111. int cache_export_cookie_params(server *srv, connection *con, plugin_data *p) {
  112. data_unset *d;
  113. UNUSED(srv);
  114. if (NULL != (d = array_get_element(con->request.headers, "Cookie"))) {
  115. data_string *ds = (data_string *)d;
  116. size_t key = 0, value = 0;
  117. size_t is_key = 1, is_sid = 0;
  118. size_t i;
  119. /* found COOKIE */
  120. if (!DATA_IS_STRING(d)) return -1;
  121. if (ds->value->used == 0) return -1;
  122. if (ds->value->ptr[0] == '\0' ||
  123. ds->value->ptr[0] == '=' ||
  124. ds->value->ptr[0] == ';') return -1;
  125. buffer_reset(p->session_id);
  126. for (i = 0; i < ds->value->used; i++) {
  127. switch(ds->value->ptr[i]) {
  128. case '=':
  129. if (is_key) {
  130. if (0 == strncmp(ds->value->ptr + key, "PHPSESSID", i - key)) {
  131. /* found PHP-session-id-key */
  132. is_sid = 1;
  133. }
  134. value = i + 1;
  135. is_key = 0;
  136. }
  137. break;
  138. case ';':
  139. if (is_sid) {
  140. buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
  141. }
  142. is_sid = 0;
  143. key = i + 1;
  144. value = 0;
  145. is_key = 1;
  146. break;
  147. case ' ':
  148. if (is_key == 1 && key == i) key = i + 1;
  149. if (is_key == 0 && value == i) value = i + 1;
  150. break;
  151. case '\0':
  152. if (is_sid) {
  153. buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
  154. }
  155. /* fin */
  156. break;
  157. }
  158. }
  159. }
  160. return 0;
  161. }
  162. #endif
  163. int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
  164. lua_State *L;
  165. readme rm;
  166. int ret = -1;
  167. buffer *b = buffer_init();
  168. int header_tbl = 0;
  169. rm.done = 0;
  170. stream_open(&rm.st, fn);
  171. /* push the lua file to the interpreter and see what happends */
  172. L = luaL_newstate();
  173. luaL_openlibs(L);
  174. /* register functions */
  175. lua_register(L, "md5", f_crypto_md5);
  176. lua_register(L, "file_mtime", f_file_mtime);
  177. lua_register(L, "file_isreg", f_file_isreg);
  178. lua_register(L, "file_isdir", f_file_isreg);
  179. lua_register(L, "dir_files", f_dir_files);
  180. #ifdef HAVE_MEMCACHE_H
  181. lua_pushliteral(L, "memcache_get_long");
  182. lua_pushlightuserdata(L, p->conf.mc);
  183. lua_pushcclosure(L, f_memcache_get_long, 1);
  184. lua_settable(L, LUA_GLOBALSINDEX);
  185. lua_pushliteral(L, "memcache_get_string");
  186. lua_pushlightuserdata(L, p->conf.mc);
  187. lua_pushcclosure(L, f_memcache_get_string, 1);
  188. lua_settable(L, LUA_GLOBALSINDEX);
  189. lua_pushliteral(L, "memcache_exists");
  190. lua_pushlightuserdata(L, p->conf.mc);
  191. lua_pushcclosure(L, f_memcache_exists, 1);
  192. lua_settable(L, LUA_GLOBALSINDEX);
  193. #endif
  194. /* register CGI environment */
  195. lua_pushliteral(L, "request");
  196. lua_newtable(L);
  197. lua_settable(L, LUA_GLOBALSINDEX);
  198. lua_pushliteral(L, "request");
  199. header_tbl = lua_gettop(L);
  200. lua_gettable(L, LUA_GLOBALSINDEX);
  201. c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
  202. c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
  203. c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
  204. c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root));
  205. if (!buffer_is_empty(con->request.pathinfo)) {
  206. c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
  207. }
  208. c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
  209. c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
  210. /* register GET parameter */
  211. lua_pushliteral(L, "get");
  212. lua_newtable(L);
  213. lua_settable(L, LUA_GLOBALSINDEX);
  214. lua_pushliteral(L, "get");
  215. header_tbl = lua_gettop(L);
  216. lua_gettable(L, LUA_GLOBALSINDEX);
  217. buffer_copy_string_buffer(b, con->uri.query);
  218. cache_export_get_params(L, header_tbl, b);
  219. buffer_reset(b);
  220. /* 2 default constants */
  221. lua_pushliteral(L, "CACHE_HIT");
  222. lua_pushnumber(L, 0);
  223. lua_settable(L, LUA_GLOBALSINDEX);
  224. lua_pushliteral(L, "CACHE_MISS");
  225. lua_pushnumber(L, 1);
  226. lua_settable(L, LUA_GLOBALSINDEX);
  227. /* load lua program */
  228. if (lua_load(L, load_file, &rm, fn->ptr) || lua_pcall(L,0,1,0)) {
  229. log_error_write(srv, __FILE__, __LINE__, "s",
  230. lua_tostring(L,-1));
  231. goto error;
  232. }
  233. /* get return value */
  234. ret = (int)lua_tonumber(L, -1);
  235. lua_pop(L, 1);
  236. /* fetch the data from lua */
  237. lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
  238. if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
  239. response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
  240. }
  241. if (ret == 0) {
  242. /* up to now it is a cache-hit, check if all files exist */
  243. int curelem;
  244. time_t mtime = 0;
  245. if (!lua_to_c_is_table(L, "output_include")) {
  246. log_error_write(srv, __FILE__, __LINE__, "s",
  247. "output_include is missing or not a table");
  248. ret = -1;
  249. goto error;
  250. }
  251. lua_pushstring(L, "output_include");
  252. curelem = lua_gettop(L);
  253. lua_gettable(L, LUA_GLOBALSINDEX);
  254. /* HOW-TO build a etag ?
  255. * as we don't just have one file we have to take the stat()
  256. * from all base files, merge them and build the etag from
  257. * it later.
  258. *
  259. * The mtime of the content is the mtime of the freshest base file
  260. *
  261. * */
  262. lua_pushnil(L); /* first key */
  263. while (lua_next(L, curelem) != 0) {
  264. stat_cache_entry *sce = NULL;
  265. /* key' is at index -2 and value' at index -1 */
  266. if (lua_isstring(L, -1)) {
  267. const char *s = lua_tostring(L, -1);
  268. /* the file is relative, make it absolute */
  269. if (s[0] != '/') {
  270. buffer_copy_string_buffer(b, p->basedir);
  271. buffer_append_string(b, lua_tostring(L, -1));
  272. } else {
  273. buffer_copy_string(b, lua_tostring(L, -1));
  274. }
  275. if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) {
  276. /* stat failed */
  277. switch(errno) {
  278. case ENOENT:
  279. /* a file is missing, call the handler to generate it */
  280. if (!buffer_is_empty(p->trigger_handler)) {
  281. ret = 1; /* cache-miss */
  282. log_error_write(srv, __FILE__, __LINE__, "s",
  283. "a file is missing, calling handler");
  284. break;
  285. } else {
  286. /* handler not set -> 500 */
  287. ret = -1;
  288. log_error_write(srv, __FILE__, __LINE__, "s",
  289. "a file missing and no handler set");
  290. break;
  291. }
  292. break;
  293. default:
  294. break;
  295. }
  296. } else {
  297. chunkqueue_append_file(con->write_queue, b, 0, sce->st.st_size);
  298. if (sce->st.st_mtime > mtime) mtime = sce->st.st_mtime;
  299. }
  300. } else {
  301. /* not a string */
  302. ret = -1;
  303. log_error_write(srv, __FILE__, __LINE__, "s",
  304. "not a string");
  305. break;
  306. }
  307. lua_pop(L, 1); /* removes value'; keeps key' for next iteration */
  308. }
  309. lua_settop(L, curelem - 1);
  310. if (ret == 0) {
  311. data_string *ds;
  312. char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
  313. buffer tbuf;
  314. con->file_finished = 1;
  315. ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
  316. /* no Last-Modified specified */
  317. if ((mtime) && (NULL == ds)) {
  318. strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
  319. response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
  320. tbuf.ptr = timebuf;
  321. tbuf.used = sizeof(timebuf);
  322. tbuf.size = sizeof(timebuf);
  323. } else if (ds) {
  324. tbuf.ptr = ds->value->ptr;
  325. tbuf.used = ds->value->used;
  326. tbuf.size = ds->value->size;
  327. } else {
  328. tbuf.size = 0;
  329. tbuf.used = 0;
  330. tbuf.ptr = NULL;
  331. }
  332. if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf)) {
  333. /* ok, the client already has our content,
  334. * no need to send it again */
  335. chunkqueue_reset(con->write_queue);
  336. ret = 0; /* cache-hit */
  337. }
  338. } else {
  339. chunkqueue_reset(con->write_queue);
  340. }
  341. }
  342. if (ret == 1 && !buffer_is_empty(p->trigger_handler)) {
  343. /* cache-miss */
  344. buffer_copy_string_buffer(con->uri.path, p->baseurl);
  345. buffer_append_string_buffer(con->uri.path, p->trigger_handler);
  346. buffer_copy_string_buffer(con->physical.path, p->basedir);
  347. buffer_append_string_buffer(con->physical.path, p->trigger_handler);
  348. chunkqueue_reset(con->write_queue);
  349. }
  350. error:
  351. lua_close(L);
  352. stream_close(&rm.st);
  353. buffer_free(b);
  354. return ret /* cache-error */;
  355. }
  356. #else
  357. int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
  358. UNUSED(srv);
  359. UNUSED(con);
  360. UNUSED(p);
  361. UNUSED(fn);
  362. /* error */
  363. return -1;
  364. }
  365. #endif