summaryrefslogtreecommitdiff
path: root/source4/web_server/web_server.c
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2005-05-26 01:06:32 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:17:01 -0500
commitd70912a26af49db468af7ec88e9689b8176e0576 (patch)
treea52d8b9930fe23eaea47c40b0af34f324e45f854 /source4/web_server/web_server.c
parente5613a3ce9932fb76aef9f721cadabe69bd23be8 (diff)
downloadsamba-d70912a26af49db468af7ec88e9689b8176e0576.tar.gz
samba-d70912a26af49db468af7ec88e9689b8176e0576.tar.bz2
samba-d70912a26af49db468af7ec88e9689b8176e0576.zip
r6981: first version of the builtin web server for Samba4
This includes an embedded server side scripting system called 'esp' (see http://www.appwebserver.org/products/esp/esp.html) and javascript based scripting language called 'esj' (see http://www.appwebserver.org/products/ejs/ejs.html) The justification for including this scripting language is that it should make it much easier to write a high quality web interface for Samba4. The scripting language can call into any Samba4 library code (so for example it will be able to make ldb and loadparm calls), plus it provides easy support for forms, cookies, sessions etc. There is still quite a bit more work to do on the web server, but there is enough here now for people to look at and comment. I will be committing some sample web pages that test esp functionality shortly. (This used to be commit 26f0ba92c0c565ac9e4cb5a079d795d4262497dd)
Diffstat (limited to 'source4/web_server/web_server.c')
-rw-r--r--source4/web_server/web_server.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/source4/web_server/web_server.c b/source4/web_server/web_server.c
new file mode 100644
index 0000000000..9d161bdd8a
--- /dev/null
+++ b/source4/web_server/web_server.c
@@ -0,0 +1,252 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ web server startup
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "web_server/web_server.h"
+#include "lib/events/events.h"
+#include "system/filesys.h"
+
+/* don't allow connections to hang around forever */
+#define HTTP_TIMEOUT 30
+
+/*
+ destroy a web connection
+*/
+static int websrv_destructor(void *ptr)
+{
+ struct websrv_context *web = talloc_get_type(ptr, struct websrv_context);
+ if (web->output.fd != -1) {
+ close(web->output.fd);
+ }
+ return 0;
+}
+
+/*
+ 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)
+{
+ struct websrv_context *web = talloc_get_type(private, struct websrv_context);
+ stream_terminate_connection(web->conn, "websrv_context: timeout");
+}
+
+/*
+ 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 websrv_context);
+ NTSTATUS status;
+ uint8_t buf[1024];
+ size_t nread;
+ uint8_t *p;
+ DATA_BLOB b;
+
+ /* not the most efficient http parser ever, but good enough for us */
+ status = socket_recv(conn->socket, buf, sizeof(buf), &nread, 0);
+ if (NT_STATUS_IS_ERR(status)) goto failed;
+ if (!NT_STATUS_IS_OK(status)) return;
+
+ status = data_blob_append(web, &web->input.partial, buf, nread);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* parse any lines that are available */
+ b = web->input.partial;
+ while (!web->input.end_of_headers &&
+ (p=memchr(b.data, '\n', b.length))) {
+ const char *line = b.data;
+ *p = 0;
+ if (p != b.data && p[-1] == '\r') {
+ p[-1] = 0;
+ }
+ status = http_parse_header(web, line);
+ if (!NT_STATUS_IS_OK(status)) return;
+ b.length -= (p - b.data) + 1;
+ b.data = p+1;
+ }
+
+ /* keep any remaining bytes in web->input.partial */
+ if (b.length == 0) {
+ b.data = NULL;
+ }
+ b = data_blob_talloc(web, b.data, b.length);
+ data_blob_free(&web->input.partial);
+ web->input.partial = b;
+
+ /* we finish when we have both the full headers (terminated by
+ a blank line) and any post data, as indicated by the
+ content_length */
+ if (web->input.end_of_headers &&
+ web->input.partial.length == web->input.content_length) {
+ EVENT_FD_NOT_READABLE(web->conn->event.fde);
+ http_process_input(web);
+ }
+ return;
+
+failed:
+ stream_terminate_connection(conn, "websrv_recv: failed\n");
+}
+
+
+/*
+ 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);
+ NTSTATUS status;
+ size_t nsent;
+ DATA_BLOB b;
+
+ b = web->output.content;
+ b.data += web->output.nsent;
+ b.length -= web->output.nsent;
+
+ status = socket_send(conn->socket, &b, &nsent, 0);
+ if (NT_STATUS_IS_ERR(status)) {
+ stream_terminate_connection(web->conn, "socket_send: failed");
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ 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 == 0) {
+ close(web->output.fd);
+ web->output.fd = -1;
+ }
+ if (nread == -1 && errno == EINTR) {
+ return;
+ }
+ web->output.content = data_blob_talloc(web, buf, nread);
+ }
+
+ if (web->output.content.length == web->output.nsent) {
+ stream_terminate_connection(web->conn, NULL);
+ }
+}
+
+/*
+ establish a new connection to the web server
+*/
+static void websrv_accept(struct stream_connection *conn)
+{
+ struct websrv_context *web;
+
+ web = talloc_zero(conn, struct websrv_context);
+ if (web == NULL) goto failed;
+
+ web->conn = conn;
+ conn->private = web;
+ web->output.fd = -1;
+ talloc_set_destructor(web, websrv_destructor);
+
+ event_add_timed(conn->event.ctx, web,
+ timeval_current_ofs(HTTP_TIMEOUT, 0),
+ websrv_timeout, web);
+ return;
+
+failed:
+ talloc_free(conn);
+}
+
+
+static const struct stream_server_ops web_stream_ops = {
+ .name = "web",
+ .accept_connection = websrv_accept,
+ .recv_handler = websrv_recv,
+ .send_handler = websrv_send,
+};
+
+/*
+ startup the web server task
+*/
+static void websrv_task_init(struct task_server *task)
+{
+ NTSTATUS status;
+ uint16_t port = lp_swat_port();
+ const struct model_ops *model_ops;
+
+ /* run the web server as a single process */
+ model_ops = process_model_byname("single");
+ if (!model_ops) goto failed;
+
+ if (lp_interfaces() && lp_bind_interfaces_only()) {
+ int num_interfaces = iface_count();
+ int i;
+ for(i = 0; i < num_interfaces; i++) {
+ const char *address = iface_n_ip(i);
+ status = stream_setup_socket(task->event_ctx, model_ops,
+ &web_stream_ops,
+ "ipv4", address,
+ &port, task);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+ } else {
+ status = stream_setup_socket(task->event_ctx, model_ops,
+ &web_stream_ops,
+ "ipv4", lp_socket_address(),
+ &port, task);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ return;
+
+failed:
+ task_terminate(task, "Failed to startup web server task");
+}
+
+
+/*
+ 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,
+ 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);
+}