io_ssi.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. /*
  2. * Copyright (c) 2006,2007 Steven Johnson <sjohnson@sakuraindustries.com>
  3. * Copyright (c) 2007 Sergey Lyubka <valenok@gmail.com>
  4. * All rights reserved
  5. *
  6. * "THE BEER-WARE LICENSE" (Revision 42):
  7. * Sergey Lyubka wrote this file. As long as you retain this notice you
  8. * can do whatever you want with this stuff. If we meet some day, and you think
  9. * this stuff is worth it, you can buy me a beer in return.
  10. */
  11. #include "defs.h"
  12. #if !defined(NO_SSI)
  13. #define CMDBUFSIZ 512 /* SSI command buffer size */
  14. #define NEST_MAX 6 /* Maximum nesting level */
  15. struct ssi_func {
  16. struct llhead link;
  17. void *user_data;
  18. char *name;
  19. shttpd_callback_t func;
  20. };
  21. struct ssi_inc {
  22. int state; /* Buffering state */
  23. int cond; /* Conditional state */
  24. FILE *fp; /* Icluded file stream */
  25. char buf[CMDBUFSIZ]; /* SSI command buffer */
  26. size_t nbuf; /* Bytes in a command buffer */
  27. FILE *pipe; /* #exec stream */
  28. struct ssi_func func; /* #call function */
  29. };
  30. struct ssi {
  31. struct conn *conn; /* Connection we belong to */
  32. int nest; /* Current nesting level */
  33. struct ssi_inc incs[NEST_MAX]; /* Nested includes */
  34. };
  35. enum { SSI_PASS, SSI_BUF, SSI_EXEC, SSI_CALL };
  36. enum { SSI_GO, SSI_STOP }; /* Conditional states */
  37. static const struct vec st = {"<!--#", 5};
  38. void
  39. shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
  40. shttpd_callback_t func, void *user_data)
  41. {
  42. struct ssi_func *e;
  43. if ((e = malloc(sizeof(*e))) != NULL) {
  44. e->name = _shttpd_strdup(name);
  45. e->func = func;
  46. e->user_data = user_data;
  47. LL_TAIL(&ctx->ssi_funcs, &e->link);
  48. }
  49. }
  50. void
  51. _shttpd_ssi_func_destructor(struct llhead *lp)
  52. {
  53. struct ssi_func *e = LL_ENTRY(lp, struct ssi_func, link);
  54. free(e->name);
  55. free(e);
  56. }
  57. static const struct ssi_func *
  58. find_ssi_func(struct ssi *ssi, const char *name)
  59. {
  60. struct ssi_func *e;
  61. struct llhead *lp;
  62. LL_FOREACH(&ssi->conn->ctx->ssi_funcs, lp) {
  63. e = LL_ENTRY(lp, struct ssi_func, link);
  64. if (!strcmp(name, e->name))
  65. return (e);
  66. }
  67. return (NULL);
  68. }
  69. static void
  70. call(struct ssi *ssi, const char *name,
  71. struct shttpd_arg *arg, char *buf, int len)
  72. {
  73. const struct ssi_func *ssi_func;
  74. (void) memset(arg, 0, sizeof(*arg));
  75. /*
  76. * SSI function may be called with parameters. These parameters
  77. * are passed as arg->in.buf, arg->in.len vector.
  78. */
  79. arg->in.buf = strchr(name, ' ');
  80. if (arg->in.buf != NULL) {
  81. *arg->in.buf++ = '\0';
  82. arg->in.len = strlen(arg->in.buf);
  83. }
  84. if ((ssi_func = find_ssi_func(ssi, name)) != NULL) {
  85. arg->priv = ssi->conn;
  86. arg->user_data = ssi_func->user_data;
  87. arg->out.buf = buf;
  88. arg->out.len = len;
  89. ssi_func->func(arg);
  90. }
  91. }
  92. static int
  93. evaluate(struct ssi *ssi, const char *name)
  94. {
  95. struct shttpd_arg arg;
  96. call(ssi, name, &arg, NULL, 0);
  97. return (arg.flags & SHTTPD_SSI_EVAL_TRUE);
  98. }
  99. static void
  100. pass(struct ssi_inc *inc, void *buf, int *n)
  101. {
  102. if (inc->cond == SSI_GO) {
  103. (void) memcpy(buf, inc->buf, inc->nbuf);
  104. (*n) += inc->nbuf;
  105. }
  106. inc->nbuf = 0;
  107. inc->state = SSI_PASS;
  108. }
  109. static int
  110. get_path(struct conn *conn, const char *src,
  111. int src_len, char *dst, int dst_len)
  112. {
  113. static struct vec accepted[] = {
  114. {"\"", 1}, /* Relative to webserver CWD */
  115. {"file=\"", 6}, /* Relative to current URI */
  116. {"virtual=\"", 9}, /* Relative to document root */
  117. {NULL, 0},
  118. };
  119. struct vec *vec;
  120. const char *p, *root = conn->ctx->options[OPT_ROOT];
  121. int len;
  122. for (vec = accepted; vec->len > 0; vec++)
  123. if (src_len > vec->len && !memcmp(src, vec->ptr, vec->len)) {
  124. src += vec->len;
  125. src_len -= vec->len;
  126. if ((p = memchr(src, '"', src_len)) == NULL)
  127. break;
  128. if (vec->len == 6) {
  129. len = _shttpd_snprintf(dst, dst_len, "%s%c%s",
  130. root, DIRSEP, conn->uri);
  131. while (len > 0 && dst[len] != '/')
  132. len--;
  133. dst += len;
  134. dst_len -= len;
  135. } else if (vec->len == 9) {
  136. len = _shttpd_snprintf(dst, dst_len, "%s%c",
  137. root, DIRSEP);
  138. dst += len;
  139. dst_len -= len;
  140. }
  141. _shttpd_url_decode(src, p - src, dst, dst_len);
  142. return (1);
  143. }
  144. return (0);
  145. }
  146. static void
  147. do_include(struct ssi *ssi)
  148. {
  149. struct ssi_inc *inc = ssi->incs + ssi->nest;
  150. char buf[FILENAME_MAX];
  151. FILE *fp;
  152. assert(inc->nbuf >= 13);
  153. if (inc->cond == SSI_STOP) {
  154. /* Do nothing - conditional FALSE */
  155. } else if (ssi->nest >= (int) NELEMS(ssi->incs) - 1) {
  156. _shttpd_elog(E_LOG, ssi->conn,
  157. "ssi: #include: maximum nested level reached");
  158. } else if (!get_path(ssi->conn,
  159. inc->buf + 13, inc->nbuf - 13, buf, sizeof(buf))) {
  160. _shttpd_elog(E_LOG, ssi->conn, "ssi: bad #include: [%.*s]",
  161. inc->nbuf, inc->buf);
  162. } else if ((fp = fopen(buf, "r")) == NULL) {
  163. _shttpd_elog(E_LOG, ssi->conn,
  164. "ssi: fopen(%s): %s", buf, strerror(errno));
  165. } else {
  166. ssi->nest++;
  167. ssi->incs[ssi->nest].fp = fp;
  168. ssi->incs[ssi->nest].nbuf = 0;
  169. ssi->incs[ssi->nest].cond = SSI_GO;
  170. }
  171. }
  172. static char *
  173. trim_spaces(struct ssi_inc *inc)
  174. {
  175. char *p = inc->buf + inc->nbuf - 2;
  176. /* Trim spaces from the right */
  177. *p-- = '\0';
  178. while (isspace(* (unsigned char *) p))
  179. *p-- = '\0';
  180. /* Shift pointer to the start of attributes */
  181. for (p = inc->buf; !isspace(* (unsigned char *) p); p++);
  182. while (*p && isspace(* (unsigned char *) p)) p++;
  183. return (p);
  184. }
  185. static void
  186. do_if(struct ssi *ssi)
  187. {
  188. struct ssi_inc *inc = ssi->incs + ssi->nest;
  189. char *name = trim_spaces(inc);
  190. inc->cond = evaluate(ssi, name) ? SSI_GO : SSI_STOP;
  191. }
  192. static void
  193. do_elif(struct ssi *ssi)
  194. {
  195. struct ssi_inc *inc = ssi->incs + ssi->nest;
  196. char *name = trim_spaces(inc);
  197. if (inc->cond == SSI_STOP && evaluate(ssi, name))
  198. inc->cond = SSI_GO;
  199. else
  200. inc->cond = SSI_STOP;
  201. }
  202. static void
  203. do_endif(struct ssi *ssi)
  204. {
  205. ssi->incs[ssi->nest].cond = SSI_GO;
  206. }
  207. static void
  208. do_else(struct ssi *ssi)
  209. {
  210. struct ssi_inc *inc = ssi->incs + ssi->nest;
  211. inc->cond = inc->cond == SSI_GO ? SSI_STOP : SSI_GO;
  212. }
  213. static void
  214. do_call2(struct ssi *ssi, char *buf, int len, int *n)
  215. {
  216. struct ssi_inc *inc = ssi->incs + ssi->nest;
  217. struct shttpd_arg arg;
  218. call(ssi, inc->buf, &arg, buf, len);
  219. (*n) += arg.out.num_bytes;
  220. if (arg.flags & SHTTPD_END_OF_OUTPUT)
  221. inc->state = SSI_PASS;
  222. }
  223. static void
  224. do_call(struct ssi *ssi, char *buf, int len, int *n)
  225. {
  226. struct ssi_inc *inc = ssi->incs + ssi->nest;
  227. char *name = trim_spaces(inc);
  228. if (inc->cond == SSI_GO) {
  229. (void) memmove(inc->buf, name, strlen(name) + 1);
  230. inc->state = SSI_CALL;
  231. do_call2(ssi, buf, len, n);
  232. }
  233. }
  234. static void
  235. do_exec2(struct ssi *ssi, char *buf, int len, int *n)
  236. {
  237. struct ssi_inc *inc = ssi->incs + ssi->nest;
  238. int i, ch;
  239. for (i = 0; i < len; i++) {
  240. if ((ch = fgetc(inc->pipe)) == EOF) {
  241. inc->state = SSI_PASS;
  242. (void) pclose(inc->pipe);
  243. inc->pipe = NULL;
  244. break;
  245. }
  246. *buf++ = ch;
  247. (*n)++;
  248. }
  249. }
  250. static void
  251. do_exec(struct ssi *ssi, char *buf, int len, int *n)
  252. {
  253. struct ssi_inc *inc = ssi->incs + ssi->nest;
  254. char cmd[sizeof(inc->buf)], *e, *p;
  255. p = trim_spaces(inc);
  256. if (inc->cond == SSI_STOP) {
  257. /* Do nothing - conditional FALSE */
  258. } else if (*p != '"' || (e = strchr(p + 1, '"')) == NULL) {
  259. _shttpd_elog(E_LOG, ssi->conn, "ssi: bad exec(%s)", p);
  260. } else if (!_shttpd_url_decode(p + 1, e - p - 1, cmd, sizeof(cmd))) {
  261. _shttpd_elog(E_LOG, ssi->conn,
  262. "ssi: cannot url_decode: exec(%s)", p);
  263. } else if ((inc->pipe = popen(cmd, "r")) == NULL) {
  264. _shttpd_elog(E_LOG, ssi->conn, "ssi: popen(%s)", cmd);
  265. } else {
  266. inc->state = SSI_EXEC;
  267. do_exec2(ssi, buf, len, n);
  268. }
  269. }
  270. static const struct ssi_cmd {
  271. struct vec vec;
  272. void (*func)();
  273. } known_ssi_commands [] = {
  274. {{"include ", 8}, do_include },
  275. {{"if ", 3}, do_if },
  276. {{"elif ", 5}, do_elif },
  277. {{"else", 4}, do_else },
  278. {{"endif", 5}, do_endif },
  279. {{"call ", 5}, do_call },
  280. {{"exec ", 5}, do_exec },
  281. {{NULL, 0}, NULL }
  282. };
  283. static void
  284. do_command(struct ssi *ssi, char *buf, size_t len, int *n)
  285. {
  286. struct ssi_inc *inc = ssi->incs + ssi->nest;
  287. const struct ssi_cmd *cmd;
  288. assert(len > 0);
  289. assert(inc->nbuf <= len);
  290. inc->state = SSI_PASS;
  291. for (cmd = known_ssi_commands; cmd->func != NULL; cmd++)
  292. if (inc->nbuf > (size_t) st.len + cmd->vec.len &&
  293. !memcmp(inc->buf + st.len, cmd->vec.ptr, cmd->vec.len)) {
  294. cmd->func(ssi, buf, len, n);
  295. break;
  296. }
  297. if (cmd->func == NULL)
  298. pass(inc, buf, n);
  299. inc->nbuf = 0;
  300. }
  301. static int
  302. read_ssi(struct stream *stream, void *vbuf, size_t len)
  303. {
  304. struct ssi *ssi = stream->conn->ssi;
  305. struct ssi_inc *inc = ssi->incs + ssi->nest;
  306. char *buf = vbuf;
  307. int ch = EOF, n = 0;
  308. again:
  309. if (inc->state == SSI_CALL)
  310. do_call2(ssi, buf, len, &n);
  311. else if (inc->state == SSI_EXEC)
  312. do_exec2(ssi, buf, len, &n);
  313. while (n + inc->nbuf < len && (ch = fgetc(inc->fp)) != EOF)
  314. switch (inc->state) {
  315. case SSI_PASS:
  316. if (ch == '<') {
  317. inc->nbuf = 0;
  318. inc->buf[inc->nbuf++] = ch;
  319. inc->state = SSI_BUF;
  320. } else if (inc->cond == SSI_GO) {
  321. buf[n++] = ch;
  322. }
  323. break;
  324. /*
  325. * We are buffering whole SSI command, until closing "-->".
  326. * That means that when do_command() is called, we can rely
  327. * on that full command with arguments is buffered in and
  328. * there is no need for streaming.
  329. * Restrictions:
  330. * 1. The command must fit in CMDBUFSIZ
  331. * 2. HTML comments inside the command ? Not sure about this.
  332. */
  333. case SSI_BUF:
  334. if (inc->nbuf >= sizeof(inc->buf) - 1) {
  335. pass(inc, buf + n, &n);
  336. } else if (ch == '>' &&
  337. !memcmp(inc->buf + inc->nbuf - 2, "--", 2)) {
  338. do_command(ssi, buf + n, len - n, &n);
  339. inc = ssi->incs + ssi->nest;
  340. } else {
  341. inc->buf[inc->nbuf++] = ch;
  342. /* If not SSI tag, pass it */
  343. if (inc->nbuf <= (size_t) st.len &&
  344. memcmp(inc->buf, st.ptr, inc->nbuf) != 0)
  345. pass(inc, buf + n, &n);
  346. }
  347. break;
  348. case SSI_EXEC:
  349. case SSI_CALL:
  350. break;
  351. default:
  352. /* Never happens */
  353. abort();
  354. break;
  355. }
  356. if (ssi->nest > 0 && n + inc->nbuf < len && ch == EOF) {
  357. (void) fclose(inc->fp);
  358. inc->fp = NULL;
  359. ssi->nest--;
  360. inc--;
  361. goto again;
  362. }
  363. return (n);
  364. }
  365. static void
  366. close_ssi(struct stream *stream)
  367. {
  368. struct ssi *ssi = stream->conn->ssi;
  369. size_t i;
  370. for (i = 0; i < NELEMS(ssi->incs); i++) {
  371. if (ssi->incs[i].fp != NULL)
  372. (void) fclose(ssi->incs[i].fp);
  373. if (ssi->incs[i].pipe != NULL)
  374. (void) pclose(ssi->incs[i].pipe);
  375. }
  376. free(ssi);
  377. }
  378. void
  379. _shttpd_do_ssi(struct conn *c)
  380. {
  381. char date[64];
  382. struct ssi *ssi;
  383. (void) strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT",
  384. localtime(&_shttpd_current_time));
  385. c->loc.io.head = c->loc.headers_len = _shttpd_snprintf(c->loc.io.buf,
  386. c->loc.io.size,
  387. "HTTP/1.1 200 OK\r\n"
  388. "Date: %s\r\n"
  389. "Content-Type: text/html\r\n"
  390. "Connection: close\r\n\r\n",
  391. date);
  392. c->status = 200;
  393. c->loc.io_class = &_shttpd_io_ssi;
  394. c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
  395. if (c->method == METHOD_HEAD) {
  396. _shttpd_stop_stream(&c->loc);
  397. } else if ((ssi = calloc(1, sizeof(struct ssi))) == NULL) {
  398. _shttpd_send_server_error(c, 500,
  399. "Cannot allocate SSI descriptor");
  400. } else {
  401. ssi->incs[0].fp = fdopen(c->loc.chan.fd, "r");
  402. ssi->conn = c;
  403. c->ssi = ssi;
  404. }
  405. }
  406. const struct io_class _shttpd_io_ssi = {
  407. "ssi",
  408. read_ssi,
  409. NULL,
  410. close_ssi
  411. };
  412. #endif /* !NO_SSI */