source4/: Fix prototypes for all functions in various subsystems.
[sfrench/samba-autobuild/.git] / source4 / web_server / web_server.c
index 0fdb0bfb4ffa521c3fe88cbeaa306d3cf7a5960e..338cc2b57a3784be13d64b2add3afd4922c9f14d 100644 (file)
@@ -4,6 +4,7 @@
    web server startup
 
    Copyright (C) Andrew Tridgell 2005
+   Copyright (C) Jelmer Vernooij 2008
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 */
 
 #include "includes.h"
-#include "smbd/service_task.h"
-#include "smbd/service_stream.h"
-#include "smbd/service.h"
 #include "web_server/web_server.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/tls/tls.h"
 #include "lib/events/events.h"
-#include "system/filesys.h"
-#include "system/network.h"
 #include "lib/socket/netif.h"
-#include "lib/tls/tls.h"
 #include "param/param.h"
 
+NTSTATUS server_service_web_init(void);
+
 /* don't allow connections to hang around forever */
 #define HTTP_TIMEOUT 120
 
@@ -39,9 +38,6 @@
 */
 static int websrv_destructor(struct websrv_context *web)
 {
-       if (web->output.fd != -1) {
-               close(web->output.fd);
-       }
        return 0;
 }
 
@@ -49,11 +45,11 @@ static int websrv_destructor(struct websrv_context *web)
   called when a connection times out. This prevents a stuck connection
   from hanging around forever
 */
-static void websrv_timeout(struct event_context *event_context, 
-                          struct timed_event *te, 
-                          struct timeval t, void *private)
+static void websrv_timeout(struct tevent_context *event_context, 
+                          struct tevent_timer *te, 
+                          struct timeval t, void *private_data)
 {
-       struct websrv_context *web = talloc_get_type(private, struct websrv_context);
+       struct websrv_context *web = talloc_get_type(private_data, struct websrv_context);
        struct stream_connection *conn = web->conn;
        web->conn = NULL;
        /* TODO: send a message to any running esp context on this connection
@@ -61,12 +57,92 @@ static void websrv_timeout(struct event_context *event_context,
        stream_terminate_connection(conn, "websrv_timeout: timed out"); 
 }
 
+/*
+  setup for a raw http level error
+*/
+void http_error(struct websrv_context *web, const char *status, const char *info)
+{
+       char *s;
+       s = talloc_asprintf(web,"<HTML><HEAD><TITLE>Error %s</TITLE></HEAD><BODY><H1>Error %s</H1><pre>%s</pre><p></BODY></HTML>\r\n\r\n", 
+                           status, status, info);
+       if (s == NULL) {
+               stream_terminate_connection(web->conn, "http_error: out of memory");
+               return;
+       }
+       websrv_output_headers(web, status, NULL);
+       websrv_output(web, s, strlen(s));
+}
+
+void websrv_output_headers(struct websrv_context *web, const char *status, struct http_header *headers)
+{
+       char *s;
+       DATA_BLOB b;
+       struct http_header *hdr;
+
+       s = talloc_asprintf(web, "HTTP/1.0 %s\r\n", status);
+       if (s == NULL) return;
+       for (hdr = headers; hdr; hdr = hdr->next) {
+               s = talloc_asprintf_append_buffer(s, "%s: %s\r\n", hdr->name, hdr->value);
+       }
+
+       s = talloc_asprintf_append_buffer(s, "\r\n");
+
+       b = web->output.content;
+       web->output.content = data_blob_string_const(s);
+       websrv_output(web, b.data, b.length);
+       data_blob_free(&b);
+}
+
+void websrv_output(struct websrv_context *web, void *data, size_t length)
+{
+       data_blob_append(web, &web->output.content, data, length);
+       EVENT_FD_NOT_READABLE(web->conn->event.fde);
+       EVENT_FD_WRITEABLE(web->conn->event.fde);
+       web->output.output_pending = true;
+}
+
+
+/*
+  parse one line of header input
+*/
+NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
+{
+       if (line[0] == 0) {
+               web->input.end_of_headers = true;
+       } else if (strncasecmp(line,"GET ", 4)==0) {
+               web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
+       } else if (strncasecmp(line,"POST ", 5)==0) {
+               web->input.post_request = true;
+               web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
+       } else if (strchr(line, ':') == NULL) {
+               http_error(web, "400 Bad request", "This server only accepts GET and POST requests");
+               return NT_STATUS_INVALID_PARAMETER;
+       } else if (strncasecmp(line, "Content-Length: ", 16)==0) {
+               web->input.content_length = strtoul(&line[16], NULL, 10);
+       } else {
+               struct http_header *hdr = talloc_zero(web, struct http_header);
+               char *colon = strchr(line, ':');
+               if (colon == NULL) {
+                       http_error(web, "500 Internal Server Error", "invalidly formatted header");
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               hdr->name = talloc_strndup(hdr, line, colon-line);
+               hdr->value = talloc_strdup(hdr, colon+1);
+               DLIST_ADD(web->input.headers, hdr);
+       }
+
+       /* ignore all other headers for now */
+       return NT_STATUS_OK;
+}
+
 /*
   called when a web connection becomes readable
 */
 static void websrv_recv(struct stream_connection *conn, uint16_t flags)
 {
-       struct websrv_context *web = talloc_get_type(conn->private, 
+       struct web_server_data *wdata;
+       struct websrv_context *web = talloc_get_type(conn->private_data,
                                                     struct websrv_context);
        NTSTATUS status;
        uint8_t buf[1024];
@@ -123,7 +199,9 @@ static void websrv_recv(struct stream_connection *conn, uint16_t flags)
                 destroy the stack variables being used by that
                 rendering process when we handle the timeout. */
                if (!talloc_reference(web->task, web)) goto failed;
-               http_process_input(web);
+               wdata = talloc_get_type(web->task->private_data, struct web_server_data);
+               if (wdata == NULL) goto failed;
+               wdata->http_process_input(wdata, web);
                talloc_unlink(web->task, web);
        }
        return;
@@ -133,12 +211,13 @@ failed:
 }
 
 
+
 /*
   called when a web connection becomes writable
 */
 static void websrv_send(struct stream_connection *conn, uint16_t flags)
 {
-       struct websrv_context *web = talloc_get_type(conn->private
+       struct websrv_context *web = talloc_get_type(conn->private_data,
                                                     struct websrv_context);
        NTSTATUS status;
        size_t nsent;
@@ -159,29 +238,7 @@ static void websrv_send(struct stream_connection *conn, uint16_t flags)
 
        web->output.nsent += nsent;
 
-       /* possibly read some more raw data from a file */
-       if (web->output.content.length == web->output.nsent && 
-           web->output.fd != -1) {
-               uint8_t buf[2048];
-               ssize_t nread;
-
-               data_blob_free(&web->output.content);
-               web->output.nsent = 0;
-
-               nread = read(web->output.fd, buf, sizeof(buf));
-               if (nread == -1 && errno == EINTR) {
-                       return;
-               }
-               if (nread <= 0) {
-                       close(web->output.fd);
-                       web->output.fd = -1;
-                       nread = 0;
-               }
-               web->output.content = data_blob_talloc(web, buf, nread);
-       }
-
-       if (web->output.content.length == web->output.nsent && 
-           web->output.fd == -1) {
+       if (web->output.content.length == web->output.nsent) {
                stream_terminate_connection(web->conn, "websrv_send: finished sending");
        }
 }
@@ -191,8 +248,8 @@ static void websrv_send(struct stream_connection *conn, uint16_t flags)
 */
 static void websrv_accept(struct stream_connection *conn)
 {
-       struct task_server *task = talloc_get_type(conn->private, struct task_server);
-       struct esp_data *edata = talloc_get_type(task->private, struct esp_data);
+       struct task_server *task = talloc_get_type(conn->private_data, struct task_server);
+       struct web_server_data *wdata = talloc_get_type(task->private_data, struct web_server_data);
        struct websrv_context *web;
        struct socket_context *tls_socket;
 
@@ -201,8 +258,7 @@ static void websrv_accept(struct stream_connection *conn)
 
        web->task = task;
        web->conn = conn;
-       conn->private = web;
-       web->output.fd = -1;
+       conn->private_data = web;
        talloc_set_destructor(web, websrv_destructor);
 
        event_add_timed(conn->event.ctx, web, 
@@ -210,7 +266,7 @@ static void websrv_accept(struct stream_connection *conn)
                        websrv_timeout, web);
 
        /* Overwrite the socket with a (possibly) TLS socket */
-       tls_socket = tls_init_server(edata->tls_params, conn->socket, 
+       tls_socket = tls_init_server(wdata->tls_params, conn->socket, 
                                     conn->event.fde, "GPHO");
        /* We might not have TLS, or it might not have initilised */
        if (tls_socket) {
@@ -241,65 +297,69 @@ static const struct stream_server_ops web_stream_ops = {
 static void websrv_task_init(struct task_server *task)
 {
        NTSTATUS status;
-       uint16_t port = lp_web_port(task->lp_ctx);
+       uint16_t port = lpcfg_web_port(task->lp_ctx);
        const struct model_ops *model_ops;
+       struct web_server_data *wdata;
 
        task_server_set_title(task, "task[websrv]");
 
        /* run the web server as a single process */
-       model_ops = process_model_byname("single");
+       model_ops = process_model_startup("single");
        if (!model_ops) goto failed;
 
-       if (lp_interfaces(task->lp_ctx) && lp_bind_interfaces_only(task->lp_ctx)) {
+       /* startup the Python processor - unfortunately we can't do this
+          per connection as that wouldn't allow for session variables */
+       wdata = talloc_zero(task, struct web_server_data);
+       if (wdata == NULL) goto failed;
+
+       task->private_data = wdata;
+
+       if (lpcfg_interfaces(task->lp_ctx) && lpcfg_bind_interfaces_only(task->lp_ctx)) {
                int num_interfaces;
                int i;
                struct interface *ifaces;
 
-               load_interfaces(lp_interfaces(task->lp_ctx), &ifaces);
+               load_interfaces(NULL, lpcfg_interfaces(task->lp_ctx), &ifaces);
 
                num_interfaces = iface_count(ifaces);
                for(i = 0; i < num_interfaces; i++) {
                        const char *address = iface_n_ip(ifaces, i);
-                       status = stream_setup_socket(task->event_ctx, model_ops, 
+                       status = stream_setup_socket(task,
+                                                    task->event_ctx,
+                                                    task->lp_ctx, model_ops,
                                                     &web_stream_ops, 
                                                     "ipv4", address, 
-                                                    &port, lp_socket_options(task->lp_ctx), 
+                                                    &port, lpcfg_socket_options(task->lp_ctx),
                                                     task);
                        if (!NT_STATUS_IS_OK(status)) goto failed;
                }
+
+               talloc_free(ifaces);
        } else {
-               status = stream_setup_socket(task->event_ctx, model_ops, 
-                                            &web_stream_ops, 
-                                            "ipv4", lp_socket_address(task->lp_ctx), 
-                                            &port, lp_socket_options(task->lp_ctx), task);
+               status = stream_setup_socket(task, task->event_ctx,
+                                            task->lp_ctx, model_ops,
+                                            &web_stream_ops,
+                                            "ipv4", lpcfg_socket_address(task->lp_ctx),
+                                            &port, lpcfg_socket_options(task->lp_ctx),
+                                            task);
                if (!NT_STATUS_IS_OK(status)) goto failed;
        }
 
-       /* startup the esp processor - unfortunately we can't do this
-          per connection as that wouldn't allow for session variables */
-       status = http_setup_esp(task);
-       if (!NT_STATUS_IS_OK(status)) goto failed;
+       wdata->tls_params = tls_initialise(wdata, task->lp_ctx);
+       if (wdata->tls_params == NULL) goto failed;
+
+       if (!wsgi_initialize(wdata)) goto failed;
+
 
        return;
 
 failed:
-       task_server_terminate(task, "websrv_task_init: failed to startup web server task");
+       task_server_terminate(task, "websrv_task_init: failed to startup web server task", true);
 }
 
 
-/*
-  called on startup of the web server service It's job is to start
-  listening on all configured sockets
-*/
-static NTSTATUS websrv_init(struct event_context *event_context, 
-                           struct loadparm_context *lp_ctx,
-                           const struct model_ops *model_ops)
-{      
-       return task_server_startup(event_context, model_ops, websrv_task_init);
-}
-
 /* called at smbd startup - register ourselves as a server service */
 NTSTATUS server_service_web_init(void)
 {
-       return register_server_service("web", websrv_init);
+       return register_server_service("web", websrv_task_init);
 }