summaryrefslogtreecommitdiffstats
path: root/cpukit/shttpd/io_cgi.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/shttpd/io_cgi.c')
-rw-r--r--cpukit/shttpd/io_cgi.c122
1 files changed, 122 insertions, 0 deletions
diff --git a/cpukit/shttpd/io_cgi.c b/cpukit/shttpd/io_cgi.c
new file mode 100644
index 0000000000..f7efb6423f
--- /dev/null
+++ b/cpukit/shttpd/io_cgi.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * 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"
+
+static int
+write_cgi(struct stream *stream, const void *buf, size_t len)
+{
+ assert(stream->chan.sock != -1);
+ assert(stream->flags & FLAG_W);
+
+ return (send(stream->chan.sock, buf, len, 0));
+}
+
+static int
+read_cgi(struct stream *stream, void *buf, size_t len)
+{
+ struct headers parsed;
+ char status[4];
+ int n;
+
+ assert(stream->chan.sock != -1);
+ assert(stream->flags & FLAG_R);
+
+ stream->flags &= ~FLAG_DONT_CLOSE;
+
+ n = recv(stream->chan.sock, buf, len, 0);
+
+ if (stream->flags & FLAG_HEADERS_PARSED)
+ return (n);
+
+ if (n <= 0 && ERRNO != EWOULDBLOCK) {
+ send_server_error(stream->conn, 500, "Error running CGI");
+ return (n);
+ }
+
+ /*
+ * CGI script may output Status: and Location: headers, which
+ * may alter the status code. Buffer in headers, parse
+ * them, send correct status code and then forward all data
+ * from CGI script back to the remote end.
+ * Reply line was alredy appended to the IO buffer in
+ * decide_what_to_do(), with blank status code.
+ */
+
+ stream->flags |= FLAG_DONT_CLOSE;
+ io_inc_head(&stream->io, n);
+
+ stream->headers_len = get_headers_len(stream->io.buf, stream->io.head);
+ if (stream->headers_len < 0) {
+ stream->flags &= ~FLAG_DONT_CLOSE;
+ send_server_error(stream->conn, 500, "Bad headers sent");
+ elog(E_LOG, stream->conn, "CGI script sent invalid headers: "
+ "[%.*s]", stream->io.head - CGI_REPLY_LEN,
+ stream->io.buf + CGI_REPLY_LEN);
+ return (0);
+ }
+
+ /*
+ * If we did not received full headers yet, we must not send any
+ * data read from the CGI back to the client. Suspend sending by
+ * setting tail = head, which tells that there is no data in IO buffer
+ */
+
+ if (stream->headers_len == 0) {
+ stream->io.tail = stream->io.head;
+ return (0);
+ }
+
+ /* Received all headers. Set status code for the connection. */
+ (void) memset(&parsed, 0, sizeof(parsed));
+ parse_headers(stream->io.buf, stream->headers_len, &parsed);
+ stream->content_len = parsed.cl.v_big_int;
+ stream->conn->status = (int) parsed.status.v_big_int;
+
+ /* If script outputs 'Location:' header, set status code to 302 */
+ if (parsed.location.v_vec.len > 0)
+ stream->conn->status = 302;
+
+ /*
+ * If script did not output neither 'Location:' nor 'Status' headers,
+ * set the default status code 200, which means 'success'.
+ */
+ if (stream->conn->status == 0)
+ stream->conn->status = 200;
+
+ /* Append the status line to the beginning of the output */
+ (void) my_snprintf(status, sizeof(status), "%3d", stream->conn->status);
+ (void) memcpy(stream->io.buf + 9, status, 3);
+ DBG(("read_cgi: content len %lu status %s",
+ stream->content_len, status));
+
+ /* Next time, pass output directly back to the client */
+ assert((big_int_t) stream->headers_len <= stream->io.total);
+ stream->io.total -= stream->headers_len;
+ stream->io.tail = 0;
+ stream->flags |= FLAG_HEADERS_PARSED;
+
+ /* Return 0 because we've already shifted the head */
+ return (0);
+}
+
+static void
+close_cgi(struct stream *stream)
+{
+ assert(stream->chan.sock != -1);
+ (void) closesocket(stream->chan.sock);
+}
+
+const struct io_class io_cgi = {
+ "cgi",
+ read_cgi,
+ write_cgi,
+ close_cgi
+};