/* * Copyright (c) 2006,2007 Steven Johnson * Copyright (c) 2007 Sergey Lyubka * All rights reserved * * "THE BEER-WARE LICENSE" (Revision 42): * Sergey Lyubka wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. */ #include "defs.h" #define CMDBUFSIZ 512 /* SSI command buffer size */ #define NEST_MAX 6 /* Maximum nesting level */ struct ssi_func { struct llhead link; void *user_data; char *name; shttpd_callback_t func; }; struct ssi_inc { int state; /* Buffering state */ int cond; /* Conditional state */ FILE *fp; /* Icluded file stream */ char buf[CMDBUFSIZ]; /* SSI command buffer */ size_t nbuf; /* Bytes in a command buffer */ FILE *pipe; /* #exec stream */ struct ssi_func func; /* #call function */ }; struct ssi { struct conn *conn; /* Connection we belong to */ int nest; /* Current nesting level */ struct ssi_inc incs[NEST_MAX]; /* Nested includes */ }; enum { SSI_PASS, SSI_BUF, SSI_EXEC, SSI_CALL }; enum { SSI_GO, SSI_STOP }; /* Conditional states */ static const struct vec st = {"". * That means that when do_command() is called, we can rely * on that full command with arguments is buffered in and * there is no need for streaming. * Restrictions: * 1. The command must fit in CMDBUFSIZ * 2. HTML comments inside the command ? Not sure about this. */ case SSI_BUF: if (inc->nbuf >= sizeof(inc->buf) - 1) { pass(inc, buf + n, &n); } else if (ch == '>' && !memcmp(inc->buf + inc->nbuf - 2, "--", 2)) { do_command(ssi, buf + n, len - n, &n); inc = ssi->incs + ssi->nest; } else { inc->buf[inc->nbuf++] = ch; /* If not SSI tag, pass it */ if (inc->nbuf <= (size_t) st.len && memcmp(inc->buf, st.ptr, inc->nbuf) != 0) pass(inc, buf + n, &n); } break; case SSI_EXEC: case SSI_CALL: break; default: /* Never happens */ abort(); break; } if (ssi->nest > 0 && n + inc->nbuf < len && ch == EOF) { (void) fclose(inc->fp); inc->fp = NULL; ssi->nest--; inc--; goto again; } return (n); } static void close_ssi(struct stream *stream) { struct ssi *ssi = stream->conn->ssi; size_t i; for (i = 0; i < NELEMS(ssi->incs); i++) { if (ssi->incs[i].fp != NULL) (void) fclose(ssi->incs[i].fp); if (ssi->incs[i].pipe != NULL) (void) pclose(ssi->incs[i].pipe); } free(ssi); } void do_ssi(struct conn *c) { char date[64]; struct ssi *ssi; (void) strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", localtime(¤t_time)); c->loc.io.head = c->loc.headers_len = my_snprintf(c->loc.io.buf, c->loc.io.size, "HTTP/1.1 200 OK\r\n" "Date: %s\r\n" "Content-Type: text/html\r\n" "Connection: close\r\n\r\n", date); c->status = 200; c->loc.io_class = &io_ssi; c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY; if (c->method == METHOD_HEAD) { stop_stream(&c->loc); } else if ((ssi = calloc(1, sizeof(struct ssi))) == NULL) { send_server_error(c, 500, "Cannot allocate SSI descriptor"); } else { ssi->incs[0].fp = fdopen(c->loc.chan.fd, "r"); ssi->conn = c; c->ssi = ssi; } } const struct io_class io_ssi = { "ssi", read_ssi, NULL, close_ssi };