summaryrefslogtreecommitdiff
path: root/source4/web_server
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2005-05-27 00:29:58 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:17:03 -0500
commitfc45b63e478f6f891f0d04bf49423be30a63617d (patch)
treeb5671673fed4e1aef23ca21d840acd1c04bf287e /source4/web_server
parentb7158885afc40bcac857e6fc2cfd97e6c8f4c4f8 (diff)
downloadsamba-fc45b63e478f6f891f0d04bf49423be30a63617d.tar.gz
samba-fc45b63e478f6f891f0d04bf49423be30a63617d.tar.bz2
samba-fc45b63e478f6f891f0d04bf49423be30a63617d.zip
r6998: - added support for application[] data, which is global to all clients using the web server.
This allows for things like application['state'] = "shuttting down" and then every web client can see that the server is going down - added support for session[] data. This allows web pages to store long term data specific to this client. It relies on cookies. Sessions auto timeout (default timeout 5 minutes). The timeout can be set in the scripts. - changed from processing all .html files as esp, to only processing .esp files as esp. This makes it easier to compare the samba web server to appWeb as a reference implementation. - expanded the number of standard variables setup by esp. See the showvars.esp example page for all variables. (This used to be commit c418b23c2ea383da8fad21b62213ec01fd135ebb)
Diffstat (limited to 'source4/web_server')
-rw-r--r--source4/web_server/ejs/miniMpr.c9
-rw-r--r--source4/web_server/esp/esp.h6
-rw-r--r--source4/web_server/http.c329
-rw-r--r--source4/web_server/web_server.c7
-rw-r--r--source4/web_server/web_server.h8
5 files changed, 287 insertions, 72 deletions
diff --git a/source4/web_server/ejs/miniMpr.c b/source4/web_server/ejs/miniMpr.c
index 38241e5c71..46d9579c7e 100644
--- a/source4/web_server/ejs/miniMpr.c
+++ b/source4/web_server/ejs/miniMpr.c
@@ -37,16 +37,9 @@
static TALLOC_CTX *mpr_ctx;
-void mprFreeAll(void)
-{
- talloc_free(mpr_ctx);
- mpr_ctx = NULL;
-}
-
void mprSetCtx(TALLOC_CTX *ctx)
{
- talloc_free(mpr_ctx);
- mpr_ctx = talloc_new(ctx);
+ mpr_ctx = ctx;
}
void mprFree(void *ptr)
diff --git a/source4/web_server/esp/esp.h b/source4/web_server/esp/esp.h
index 5d343db96e..9e58bdf066 100644
--- a/source4/web_server/esp/esp.h
+++ b/source4/web_server/esp/esp.h
@@ -96,13 +96,13 @@ typedef struct Esp {
int maxScriptSize;
void (*createSession)(EspHandle handle, int timeout);
void (*destroySession)(EspHandle handle);
- char *(*getSessionId)(EspHandle handle);
+ const char *(*getSessionId)(EspHandle handle);
int (*mapToStorage)(EspHandle handle, char *path, int len, const char *uri,
int flags);
int (*readFile)(EspHandle handle, char **buf, int *len, const char *path);
void (*redirect)(EspHandle handle, int code, char *url);
- void (*setCookie)(EspHandle handle, char *name, char *value,
- int lifetime, char *path, bool secure);
+ void (*setCookie)(EspHandle handle, const char *name, const char *value,
+ int lifetime, const char *path, bool secure);
void (*setHeader)(EspHandle handle, const char *value, bool allowMultiple);
void (*setResponseCode)(EspHandle handle, int code);
int (*writeBlock)(EspHandle handle, char *buf, int size);
diff --git a/source4/web_server/http.c b/source4/web_server/http.c
index 25595a8ad7..5dd9e35edd 100644
--- a/source4/web_server/http.c
+++ b/source4/web_server/http.c
@@ -21,6 +21,7 @@
*/
#include "includes.h"
+#include "smbd/service_task.h"
#include "web_server/web_server.h"
#include "smbd/service_stream.h"
#include "lib/events/events.h"
@@ -28,23 +29,42 @@
#include "system/iconv.h"
#include "system/time.h"
#include "web_server/esp/esp.h"
+#include "dlinklist.h"
-/* state of the esp subsystem */
+#define SWAT_SESSION_KEY "_swat_session_"
+
+/*
+ context for long term storage in the web server, to support session[]
+ and application[] data. Stored in task->private.
+*/
+struct esp_data {
+ struct session_data {
+ struct session_data *next, *prev;
+ struct esp_data *edata;
+ const char *id;
+ struct MprVar *data;
+ struct timed_event *te;
+ int lifetime;
+ } *sessions;
+ struct MprVar *application_data;
+};
+
+/* state of the esp subsystem for a specific request */
struct esp_state {
struct websrv_context *web;
- struct MprVar variables[ESP_OBJ_MAX];
struct EspRequest *req;
+ struct MprVar variables[ESP_OBJ_MAX];
+ struct session_data *session;
};
/* destroy a esp session */
static int esp_destructor(void *ptr)
{
struct esp_state *esp = talloc_get_type(ptr, struct esp_state);
+
if (esp->req) {
espDestroyRequest(esp->req);
}
- espClose();
- mprFreeAll();
return 0;
}
@@ -118,7 +138,7 @@ static const char *http_local_path(struct websrv_context *web, const char *url)
if (path == NULL) return NULL;
if (directory_exist(path)) {
- path = talloc_asprintf_append(path, "/index.html");
+ path = talloc_asprintf_append(path, "/index.esp");
}
return path;
}
@@ -199,6 +219,7 @@ static void http_setHeader(EspHandle handle, const char *value, bool allowMultip
}
web->output.headers = str_list_add(web->output.headers, value);
+ talloc_steal(web, web->output.headers);
}
/*
@@ -256,16 +277,61 @@ internal_error:
}
-/* callbacks for esp processing */
-static const struct Esp esp_control = {
- .maxScriptSize = 60000,
- .writeBlock = http_writeBlock,
- .setHeader = http_setHeader,
- .redirect = http_redirect,
- .setResponseCode = http_setResponseCode,
- .readFile = http_readFile,
- .mapToStorage = http_mapToStorage
-};
+/*
+ setup a cookie
+*/
+static void http_setCookie(EspHandle handle, const char *name, const char *value,
+ int lifetime, const char *path, bool secure)
+{
+ struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
+ char *buf;
+
+ if (lifetime > 0) {
+ buf = talloc_asprintf(web, "Set-Cookie: %s=%s; path=%s; Expires=%s; %s",
+ name, value, path?path:"/",
+ http_timestring(web, time(NULL)+lifetime),
+ secure?"secure":"");
+ } else {
+ buf = talloc_asprintf(web, "Set-Cookie: %s=%s; path=%s; %s",
+ name, value, path?path:"/",
+ secure?"secure":"");
+ }
+ http_setHeader(handle, "Cache-control: no-cache=\"set-cookie\"", 0);
+ http_setHeader(handle, buf, 0);
+ talloc_free(buf);
+}
+
+/*
+ return the session id
+*/
+static const char *http_getSessionId(EspHandle handle)
+{
+ struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
+ return web->session->id;
+}
+
+/*
+ setup a session
+*/
+static void http_createSession(EspHandle handle, int timeout)
+{
+ struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
+ if (web->session) {
+ web->session->lifetime = timeout;
+ http_setCookie(web, SWAT_SESSION_KEY, web->session->id,
+ web->session->lifetime, "/", 0);
+ }
+}
+
+/*
+ destroy a session
+*/
+static void http_destroySession(EspHandle handle)
+{
+ struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
+ talloc_free(web->session);
+ web->session = NULL;
+}
/*
@@ -302,6 +368,7 @@ void http_error_unix(struct websrv_context *web, const char *info)
code = 403;
break;
}
+ info = talloc_asprintf(web, "%s<p>%s<p>\n", info, strerror(errno));
http_error(web, code, info);
}
@@ -347,37 +414,48 @@ static void http_setup_arrays(struct esp_state *esp)
struct EspRequest *req = esp->req;
char *p;
- espSetStringVar(req, ESP_REQUEST_OBJ, "CONTENT_LENGTH",
- talloc_asprintf(esp, "%u", web->input.content_length));
- if (web->input.query_string) {
- espSetStringVar(req, ESP_REQUEST_OBJ, "QUERY_STRING",
- web->input.query_string);
- }
- espSetStringVar(req, ESP_REQUEST_OBJ, "REQUEST_METHOD",
- web->input.post_request?"POST":"GET");
- espSetStringVar(req, ESP_REQUEST_OBJ, "REQUEST_URI", web->input.url);
- p = strrchr(web->input.url, '/');
- espSetStringVar(req, ESP_REQUEST_OBJ, "SCRIPT_NAME", p+1);
+#define SETVAR(type, name, value) do { \
+ const char *v = value; \
+ if (v) espSetStringVar(req, type, name, v); \
+} while (0)
- if (web->input.referer) {
- espSetStringVar(req, ESP_HEADERS_OBJ, "HTT_REFERER", web->input.referer);
- }
- if (web->input.user_agent) {
- espSetStringVar(req, ESP_HEADERS_OBJ, "USER_AGENT", web->input.user_agent);
+ SETVAR(ESP_REQUEST_OBJ, "CONTENT_LENGTH",
+ talloc_asprintf(esp, "%u", web->input.content_length));
+ SETVAR(ESP_REQUEST_OBJ, "QUERY_STRING", web->input.query_string);
+ SETVAR(ESP_REQUEST_OBJ, "REQUEST_METHOD", web->input.post_request?"POST":"GET");
+ SETVAR(ESP_REQUEST_OBJ, "REQUEST_URI", web->input.url);
+ p = strrchr(web->input.url, '/');
+ SETVAR(ESP_REQUEST_OBJ, "SCRIPT_NAME", p+1);
+ p = socket_get_peer_name(web->conn->socket, esp);
+ SETVAR(ESP_REQUEST_OBJ, "REMOTE_HOST", p);
+ SETVAR(ESP_REQUEST_OBJ, "REMOTE_ADDR", p);
+ SETVAR(ESP_REQUEST_OBJ, "REMOTE_USER", "");
+ SETVAR(ESP_REQUEST_OBJ, "CONTENT_TYPE", web->input.content_type);
+ if (web->session) {
+ SETVAR(ESP_REQUEST_OBJ, "SESSION_ID", web->session->id);
}
- espSetStringVar(req, ESP_SERVER_OBJ, "SERVER_ADDR",
- socket_get_my_addr(web->conn->socket, esp));
- espSetStringVar(req, ESP_SERVER_OBJ, "SERVER_PORT",
- talloc_asprintf(esp, "%u", socket_get_my_port(web->conn->socket)));
- espSetStringVar(req, ESP_SERVER_OBJ, "SERVER_PROTOCOL", "http");
- espSetStringVar(esp->req, ESP_REQUEST_OBJ, "SCRIPT_FILENAME", web->input.url);
+ SETVAR(ESP_HEADERS_OBJ, "HTT_REFERER", web->input.referer);
+ SETVAR(ESP_HEADERS_OBJ, "HOST", web->input.host);
+ SETVAR(ESP_HEADERS_OBJ, "ACCEPT_ENCODING", web->input.accept_encoding);
+ SETVAR(ESP_HEADERS_OBJ, "ACCEPT_LANGUAGE", web->input.accept_language);
+ SETVAR(ESP_HEADERS_OBJ, "ACCEPT_CHARSET", web->input.accept_charset);
+ SETVAR(ESP_HEADERS_OBJ, "COOKIE", web->input.cookie);
+ SETVAR(ESP_HEADERS_OBJ, "USER_AGENT", web->input.user_agent);
+
+ SETVAR(ESP_SERVER_OBJ, "SERVER_ADDR", socket_get_my_addr(web->conn->socket, esp));
+ SETVAR(ESP_SERVER_OBJ, "SERVER_NAME", socket_get_my_addr(web->conn->socket, esp));
+ SETVAR(ESP_SERVER_OBJ, "SERVER_HOST", socket_get_my_addr(web->conn->socket, esp));
+ SETVAR(ESP_SERVER_OBJ, "DOCUMENT_ROOT", lp_swat_directory());
+ SETVAR(ESP_SERVER_OBJ, "SERVER_PORT",
+ talloc_asprintf(esp, "%u", socket_get_my_port(web->conn->socket)));
+ SETVAR(ESP_SERVER_OBJ, "SERVER_PROTOCOL", "http");
+ SETVAR(ESP_SERVER_OBJ, "SERVER_SOFTWARE", "SWAT");
+ SETVAR(ESP_SERVER_OBJ, "GATEWAY_INTERFACE", "CGI/1.1");
+ SETVAR(ESP_REQUEST_OBJ, "SCRIPT_FILENAME", web->input.url);
}
-
-
-
/*
process a esp request
*/
@@ -503,25 +581,90 @@ static NTSTATUS http_parse_get(struct esp_state *esp)
}
/*
- setup some standard variables
+ called when a session times out
*/
-static void http_setup_vars(struct esp_state *esp)
+static void session_timeout(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private)
{
- int i;
+ struct session_data *s = talloc_get_type(private, struct session_data);
+ talloc_free(s);
+}
- for (i = 0; i < ESP_OBJ_MAX; i++) {
- esp->variables[i] = mprCreateUndefinedVar();
+/*
+ destroy a session
+ */
+static int session_destructor(void *ptr)
+{
+ struct session_data *s = talloc_get_type(ptr, struct session_data);
+ DLIST_REMOVE(s->edata->sessions, s);
+ return 0;
+}
+
+/*
+ setup the session for this request
+*/
+static void http_setup_session(struct esp_state *esp)
+{
+ const char *session_key = SWAT_SESSION_KEY;
+ char *p;
+ const char *cookie = esp->web->input.cookie;
+ const char *key = NULL;
+ struct esp_data *edata = talloc_get_type(esp->web->task->private, struct esp_data);
+ struct session_data *s;
+
+ /* look for our session key */
+ if (cookie && (p = strstr(cookie, session_key)) &&
+ p[strlen(session_key)] == '=') {
+ p += strlen(session_key)+1;
+ key = talloc_strndup(esp, p, strcspn(p, ";"));
}
- esp->variables[ESP_HEADERS_OBJ] = mprCreateObjVar("headers", ESP_HASH_SIZE);
- esp->variables[ESP_FORM_OBJ] = mprCreateObjVar("form", ESP_HASH_SIZE);
- esp->variables[ESP_APPLICATION_OBJ] = mprCreateObjVar("application", ESP_HASH_SIZE);
- esp->variables[ESP_COOKIES_OBJ] = mprCreateObjVar("cookies", ESP_HASH_SIZE);
- esp->variables[ESP_FILES_OBJ] = mprCreateObjVar("files", ESP_HASH_SIZE);
- esp->variables[ESP_REQUEST_OBJ] = mprCreateObjVar("request", ESP_HASH_SIZE);
- esp->variables[ESP_SERVER_OBJ] = mprCreateObjVar("server", ESP_HASH_SIZE);
- esp->variables[ESP_SESSION_OBJ] = mprCreateObjVar("session", ESP_HASH_SIZE);
+
+ if (key == NULL) {
+ key = generate_random_str_list(esp, 64, "0123456789");
+ }
+
+ /* try to find this session in the existing session list */
+ for (s=edata->sessions;s;s=s->next) {
+ if (strcmp(key, s->id) == 0) break;
+ }
+
+ if (s == NULL) {
+ /* create a new session */
+ s = talloc_zero(edata, struct session_data);
+ s->id = talloc_steal(s, key);
+ s->data = NULL;
+ s->te = NULL;
+ s->edata = edata;
+ s->lifetime = lp_parm_int(-1, "http", "sessiontimeout", 300);
+ DLIST_ADD(edata->sessions, s);
+ talloc_set_destructor(s, session_destructor);
+ }
+
+ http_setCookie(esp->web, session_key, key, s->lifetime, "/", 0);
+
+ if (s->data) {
+ mprCopyVar(&esp->variables[ESP_SESSION_OBJ], s->data, MPR_DEEP_COPY);
+ }
+
+ esp->web->session = s;
}
+
+/* callbacks for esp processing */
+static const struct Esp esp_control = {
+ .maxScriptSize = 60000,
+ .writeBlock = http_writeBlock,
+ .setHeader = http_setHeader,
+ .redirect = http_redirect,
+ .setResponseCode = http_setResponseCode,
+ .readFile = http_readFile,
+ .mapToStorage = http_mapToStorage,
+ .setCookie = http_setCookie,
+ .createSession = http_createSession,
+ .destroySession = http_destroySession,
+ .getSessionId = http_getSessionId
+};
+
/*
process a complete http request
*/
@@ -529,17 +672,22 @@ void http_process_input(struct websrv_context *web)
{
NTSTATUS status;
struct esp_state *esp;
+ struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
char *p;
int i;
const char *file_type = NULL;
+ BOOL esp_enable = False;
const struct {
const char *extension;
const char *mime_type;
+ BOOL esp_enable;
} mime_types[] = {
{"gif", "image/gif"},
{"png", "image/png"},
{"jpg", "image/jpeg"},
- {"txt", "text/plain"}
+ {"txt", "text/plain"},
+ {"ico", "image/x-icon"},
+ {"esp", "text/html", True}
};
esp = talloc_zero(web, struct esp_state);
@@ -549,12 +697,29 @@ void http_process_input(struct websrv_context *web)
mprSetCtx(esp);
- talloc_set_destructor(esp, esp_destructor);
-
if (espOpen(&esp_control) != 0) goto internal_error;
- http_setup_vars(esp);
-
+ for (i=0;i<ARRAY_SIZE(esp->variables);i++) {
+ esp->variables[i] = mprCreateUndefinedVar();
+ }
+ esp->variables[ESP_HEADERS_OBJ] = mprCreateObjVar("headers", ESP_HASH_SIZE);
+ esp->variables[ESP_FORM_OBJ] = mprCreateObjVar("form", ESP_HASH_SIZE);
+ esp->variables[ESP_APPLICATION_OBJ] = mprCreateObjVar("application", ESP_HASH_SIZE);
+ esp->variables[ESP_COOKIES_OBJ] = mprCreateObjVar("cookies", ESP_HASH_SIZE);
+ esp->variables[ESP_FILES_OBJ] = mprCreateObjVar("files", ESP_HASH_SIZE);
+ esp->variables[ESP_REQUEST_OBJ] = mprCreateObjVar("request", ESP_HASH_SIZE);
+ esp->variables[ESP_SERVER_OBJ] = mprCreateObjVar("server", ESP_HASH_SIZE);
+ esp->variables[ESP_SESSION_OBJ] = mprCreateObjVar("session", ESP_HASH_SIZE);
+
+ if (edata->application_data) {
+ mprCopyVar(&esp->variables[ESP_APPLICATION_OBJ],
+ edata->application_data, MPR_DEEP_COPY);
+ }
+
+ http_setup_session(esp);
+
+ talloc_set_destructor(esp, esp_destructor);
+
esp->req = espCreateRequest(web, web->input.url, esp->variables);
if (esp->req == NULL) goto internal_error;
@@ -562,7 +727,8 @@ void http_process_input(struct websrv_context *web)
http_error(web, 400, "You must specify a GET or POST request");
return;
}
-
+
+ /* parse any form or get variables */
if (web->input.post_request) {
status = http_parse_post(esp);
if (!NT_STATUS_IS_OK(status)) {
@@ -577,11 +743,12 @@ void http_process_input(struct websrv_context *web)
}
}
- /* process all html files as ESP */
+ /* work out the mime type */
p = strrchr(web->input.url, '.');
for (i=0;p && i<ARRAY_SIZE(mime_types);i++) {
if (strcmp(mime_types[i].extension, p+1) == 0) {
file_type = mime_types[i].mime_type;
+ esp_enable = mime_types[i].esp_enable;
}
}
if (file_type == NULL) {
@@ -596,11 +763,32 @@ void http_process_input(struct websrv_context *web)
http_setHeader(web, "Connection: close", 0);
http_setHeader(web, talloc_asprintf(esp, "Content-Type: %s", file_type), 0);
- if (strcmp(file_type, "text/html") == 0) {
+ if (esp_enable) {
esp_request(esp);
} else {
http_simple_request(web);
}
+
+ /* copy any application data to long term storage in edata */
+ talloc_free(edata->application_data);
+ edata->application_data = talloc_zero(edata, struct MprVar);
+ mprSetCtx(edata->application_data);
+ mprCopyVar(edata->application_data, &esp->variables[ESP_APPLICATION_OBJ], MPR_DEEP_COPY);
+
+ /* copy any session data */
+ if (web->session) {
+ talloc_free(web->session->data);
+ web->session->data = talloc_zero(web->session, struct MprVar);
+ mprSetCtx(web->session->data);
+ mprCopyVar(web->session->data, &esp->variables[ESP_SESSION_OBJ], MPR_DEEP_COPY);
+
+ /* setup the timeout for the session data */
+ talloc_free(web->session->te);
+ web->session->te = event_add_timed(web->conn->event.ctx, web->session,
+ timeval_current_ofs(web->session->lifetime, 0),
+ session_timeout, web->session);
+ }
+
talloc_free(esp);
return;
@@ -639,6 +827,9 @@ NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
PULL_HEADER(referer, "Referer: ");
PULL_HEADER(host, "Host: ");
PULL_HEADER(accept_encoding, "Accept-Encoding: ");
+ PULL_HEADER(accept_language, "Accept-Language: ");
+ PULL_HEADER(accept_charset, "Accept-Charset: ");
+ PULL_HEADER(cookie, "Cookie: ");
}
/* ignore all other headers for now */
@@ -646,3 +837,19 @@ NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
}
+/*
+ setup the esp processor - called at task initialisation
+*/
+NTSTATUS http_setup_esp(struct task_server *task)
+{
+ struct esp_data *edata;
+
+ edata = talloc(task, struct esp_data);
+ NT_STATUS_HAVE_NO_MEMORY(edata);
+
+ task->private = edata;
+ edata->sessions = NULL;
+ edata->application_data = NULL;
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/web_server/web_server.c b/source4/web_server/web_server.c
index 656e101c11..9bbf423d29 100644
--- a/source4/web_server/web_server.c
+++ b/source4/web_server/web_server.c
@@ -169,11 +169,13 @@ 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 websrv_context *web;
web = talloc_zero(conn, struct websrv_context);
if (web == NULL) goto failed;
+ web->task = task;
web->conn = conn;
conn->private = web;
web->output.fd = -1;
@@ -228,6 +230,11 @@ static void websrv_task_init(struct task_server *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;
+
return;
failed:
diff --git a/source4/web_server/web_server.h b/source4/web_server/web_server.h
index 0202c91ef7..f23ea90d55 100644
--- a/source4/web_server/web_server.h
+++ b/source4/web_server/web_server.h
@@ -25,6 +25,7 @@
context of one open web connection
*/
struct websrv_context {
+ struct task_server *task;
struct stream_connection *conn;
struct {
DATA_BLOB partial;
@@ -38,6 +39,10 @@ struct websrv_context {
const char *referer;
const char *host;
const char *accept_encoding;
+ const char *accept_language;
+ const char *accept_charset;
+ const char *cookie;
+ const char *session_key;
} input;
struct {
DATA_BLOB content;
@@ -46,4 +51,7 @@ struct websrv_context {
int response_code;
const char **headers;
} output;
+ struct session_data *session;
};
+
+