2 Unix SMB/CIFS implementation.
6 Copyright (C) Andrew Tridgell 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "smbd/service_task.h"
24 #include "web_server/web_server.h"
25 #include "smbd/service_stream.h"
26 #include "smbd/service.h"
27 #include "lib/events/events.h"
28 #include "system/time.h"
29 #include "system/wait.h"
30 #include "lib/appweb/esp/esp.h"
31 #include "lib/appweb/ejs/ejsInternal.h"
32 #include "lib/util/dlinklist.h"
33 #include "lib/tls/tls.h"
34 #include "scripting/ejs/smbcalls.h"
35 #include "param/param.h"
37 #define SAMBA_SESSION_KEY "SambaSessionId"
38 #define HTTP_PREAUTH_URI "/scripting/preauth.esp"
39 #define JSONRPC_REQUEST "/services"
40 #define JSONRPC_SERVER "/request.esp"
42 /* state of the esp subsystem for a specific request */
44 struct websrv_context *web;
45 struct EspRequest *req;
46 struct MprVar variables[ESP_OBJ_MAX];
47 struct session_data *session;
51 output the http headers
53 static void http_output_headers(struct websrv_context *web)
58 uint32_t content_length = 0;
59 const char *response_string = "Unknown Code";
62 const char *response_string;
68 { 304, "Not Modified" },
69 { 400, "Bad request" },
70 { 401, "Unauthorized" },
73 { 500, "Internal Server Error" },
74 { 501, "Not implemented" }
76 for (i=0;i<ARRAY_SIZE(codes);i++) {
77 if (codes[i].code == web->output.response_code) {
78 response_string = codes[i].response_string;
82 if (web->output.headers == NULL) return;
83 s = talloc_asprintf(web, "HTTP/1.0 %u %s\r\n",
84 web->output.response_code, response_string);
85 if (s == NULL) return;
86 for (i=0;web->output.headers[i];i++) {
87 s = talloc_asprintf_append(s, "%s\r\n", web->output.headers[i]);
90 /* work out the content length */
91 content_length = web->output.content.length;
92 if (web->output.fd != -1) {
94 fstat(web->output.fd, &st);
95 content_length += st.st_size;
97 s = talloc_asprintf_append(s, "Content-Length: %u\r\n\r\n", content_length);
98 if (s == NULL) return;
100 b = web->output.content;
101 web->output.content = data_blob_string_const(s);
102 data_blob_append(web, &web->output.content, b.data, b.length);
107 return the local path for a URL
109 static const char *http_local_path(struct websrv_context *web,
111 const char *base_dir)
116 /* check that the url is OK */
117 if (url[0] != '/') return NULL;
119 for (i=0;url[i];i++) {
120 if ((!isalnum((unsigned char)url[i]) && !strchr("./_-", url[i])) ||
121 (url[i] == '.' && strchr("/.", url[i+1]))) {
126 path = talloc_asprintf(web, "%s/%s", base_dir, url+1);
127 if (path == NULL) return NULL;
129 if (directory_exist(path)) {
130 path = talloc_asprintf_append(path, "/index.esp");
136 called when esp wants to read a file to support include() calls
138 static int http_readFile(EspHandle handle,
142 const char *base_dir)
144 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
149 path = http_local_path(web, path, base_dir);
150 if (path == NULL) goto failed;
152 fd = open(path, O_RDONLY);
153 if (fd == -1 || fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) goto failed;
155 *buf = talloc_size(handle, st.st_size+1);
156 if (*buf == NULL) goto failed;
158 if (read(fd, *buf, st.st_size) != st.st_size) goto failed;
160 (*buf)[st.st_size] = 0;
167 DEBUG(0,("Failed to read file %s - %s\n", path, strerror(errno)));
168 if (fd != -1) close(fd);
174 static int http_readFileFromWebappsDir(EspHandle handle,
179 return http_readFile(handle, buf, len, path, lp_webapps_directory());
185 called when esp wants to find the real path of a file
187 static int http_mapToStorage(EspHandle handle, char *path, int len, const char *uri, int flags)
189 if (uri == NULL || strlen(uri) >= len) return -1;
190 strncpy(path, uri, len);
195 called when esp wants to output something
197 static int http_writeBlock(EspHandle handle, const char *buf, int size)
199 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
200 if (!data_blob_append(web, &web->output.content, buf, size))
209 static void http_setHeader(EspHandle handle, const char *value, bool allowMultiple)
211 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
212 char *p = strchr(value, ':');
214 if (p && !allowMultiple && web->output.headers) {
216 for (i=0;web->output.headers[i];i++) {
217 if (strncmp(web->output.headers[i], value, (p+1)-value) == 0) {
218 web->output.headers[i] = talloc_strdup(web, value);
224 web->output.headers = str_list_add(web->output.headers, value);
225 talloc_steal(web, web->output.headers);
229 set a http response code
231 static void http_setResponseCode(EspHandle handle, int code)
233 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
234 web->output.response_code = code;
238 redirect to another web page
240 static void http_redirect(EspHandle handle, int code, char *url)
242 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
243 const char *host = web->input.host;
245 /* form the full url, unless it already looks like a url */
246 if (strchr(url, ':') == NULL) {
248 struct socket_address *socket_address = socket_get_my_addr(web->conn->socket, web);
249 if (socket_address == NULL) goto internal_error;
250 host = talloc_asprintf(web, "%s:%u",
251 socket_address->addr, socket_address->port);
253 if (host == NULL) goto internal_error;
255 char *p = strrchr(web->input.url, '/');
256 if (p == web->input.url) {
257 url = talloc_asprintf(web, "http%s://%s/%s",
258 tls_enabled(web->conn->socket)?"s":"",
261 int dirlen = p - web->input.url;
262 url = talloc_asprintf(web, "http%s://%s%*.*s/%s",
263 tls_enabled(web->conn->socket)?"s":"",
265 dirlen, dirlen, web->input.url,
268 if (url == NULL) goto internal_error;
272 http_setHeader(handle, talloc_asprintf(web, "Location: %s", url), 0);
274 /* make sure we give a valid redirect code */
275 if (code >= 300 && code < 400) {
276 http_setResponseCode(handle, code);
278 http_setResponseCode(handle, 302);
283 http_error(web, 500, "Internal server error");
290 static void http_setCookie(EspHandle handle, const char *name, const char *value,
291 int lifetime, const char *path, bool secure)
293 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
297 buf = talloc_asprintf(web, "Set-Cookie: %s=%s; path=%s; Expires=%s; %s",
298 name, value, path?path:"/",
299 http_timestring(web, time(NULL)+lifetime),
302 buf = talloc_asprintf(web, "Set-Cookie: %s=%s; path=%s; %s",
303 name, value, path?path:"/",
306 http_setHeader(handle, "Cache-control: no-cache=\"set-cookie\"", 0);
307 http_setHeader(handle, buf, 0);
312 return the session id
314 static const char *http_getSessionId(EspHandle handle)
316 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
317 return web->session->id;
323 static void http_createSession(EspHandle handle, int timeout)
325 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
327 web->session->lifetime = timeout;
328 http_setCookie(web, SAMBA_SESSION_KEY, web->session->id,
329 web->session->lifetime, "/", 0);
336 static void http_destroySession(EspHandle handle)
338 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
339 talloc_free(web->session);
345 setup for a raw http level error
347 void http_error(struct websrv_context *web, int code, const char *info)
350 s = talloc_asprintf(web,"<HTML><HEAD><TITLE>Error %u</TITLE></HEAD><BODY><H1>Error %u</H1><pre>%s</pre><p></BODY></HTML>\r\n\r\n",
353 stream_terminate_connection(web->conn, "http_error: out of memory");
356 http_writeBlock(web, s, strlen(s));
357 http_setResponseCode(web, code);
358 http_output_headers(web);
359 EVENT_FD_NOT_READABLE(web->conn->event.fde);
360 EVENT_FD_WRITEABLE(web->conn->event.fde);
361 web->output.output_pending = True;
365 map a unix error code to a http error
367 void http_error_unix(struct websrv_context *web, const char *info)
379 info = talloc_asprintf(web, "%s<p>%s<p>\n", info, strerror(errno));
380 http_error(web, code, info);
385 a simple file request
387 static void http_simple_request(struct websrv_context *web)
389 const char *url = web->input.url;
393 path = http_local_path(web, url, lp_webapps_directory());
394 if (path == NULL) goto invalid;
397 web->output.fd = open(path, O_RDONLY);
398 if (web->output.fd == -1) {
399 DEBUG(0,("Failed to read file %s - %s\n", path, strerror(errno)));
400 http_error_unix(web, path);
404 if (fstat(web->output.fd, &st) != 0 || !S_ISREG(st.st_mode)) {
405 close(web->output.fd);
412 http_error(web, 400, "Malformed URL");
416 setup the standard ESP arrays
418 static void http_setup_arrays(struct esp_state *esp)
420 struct websrv_context *web = esp->web;
421 struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
422 struct EspRequest *req = esp->req;
423 struct socket_address *socket_address = socket_get_my_addr(web->conn->socket, esp);
424 struct socket_address *peer_address = socket_get_peer_addr(web->conn->socket, esp);
427 #define SETVAR(type, name, value) do { \
428 const char *v = value; \
429 if (v) espSetStringVar(req, type, name, v); \
432 SETVAR(ESP_REQUEST_OBJ, "CONTENT_LENGTH",
433 talloc_asprintf(esp, "%u", web->input.content_length));
434 SETVAR(ESP_REQUEST_OBJ, "QUERY_STRING", web->input.query_string);
435 SETVAR(ESP_REQUEST_OBJ, "POST_DATA",
437 web->input.partial.data,
438 web->input.partial.length));
439 SETVAR(ESP_REQUEST_OBJ, "REQUEST_METHOD", web->input.post_request?"POST":"GET");
440 SETVAR(ESP_REQUEST_OBJ, "REQUEST_URI", web->input.url);
441 p = strrchr(web->input.url, '/');
442 SETVAR(ESP_REQUEST_OBJ, "SCRIPT_NAME", p+1);
443 SETVAR(ESP_REQUEST_OBJ, "SCRIPT_FILENAME", web->input.url);
445 struct MprVar mpv = mprObject("socket_address");
446 mprSetPtrChild(&mpv, "socket_address", peer_address);
447 espSetVar(req, ESP_REQUEST_OBJ, "REMOTE_SOCKET_ADDRESS", mpv);
448 SETVAR(ESP_REQUEST_OBJ, "REMOTE_ADDR", peer_address->addr);
450 p = socket_get_peer_name(web->conn->socket, esp);
451 SETVAR(ESP_REQUEST_OBJ, "REMOTE_HOST", p);
452 SETVAR(ESP_REQUEST_OBJ, "REMOTE_USER", "");
453 SETVAR(ESP_REQUEST_OBJ, "CONTENT_TYPE", web->input.content_type);
455 SETVAR(ESP_REQUEST_OBJ, "SESSION_ID", web->session->id);
457 SETVAR(ESP_REQUEST_OBJ, "COOKIE_SUPPORT", web->input.cookie?"True":"False");
459 SETVAR(ESP_HEADERS_OBJ, "HTTP_REFERER", web->input.referer);
460 SETVAR(ESP_HEADERS_OBJ, "HOST", web->input.host);
461 SETVAR(ESP_HEADERS_OBJ, "ACCEPT_ENCODING", web->input.accept_encoding);
462 SETVAR(ESP_HEADERS_OBJ, "ACCEPT_LANGUAGE", web->input.accept_language);
463 SETVAR(ESP_HEADERS_OBJ, "ACCEPT_CHARSET", web->input.accept_charset);
464 SETVAR(ESP_HEADERS_OBJ, "COOKIE", web->input.cookie);
465 SETVAR(ESP_HEADERS_OBJ, "USER_AGENT", web->input.user_agent);
467 if (socket_address) {
468 SETVAR(ESP_SERVER_OBJ, "SERVER_ADDR", socket_address->addr);
469 SETVAR(ESP_SERVER_OBJ, "SERVER_NAME", socket_address->addr);
470 SETVAR(ESP_SERVER_OBJ, "SERVER_HOST", socket_address->addr);
471 SETVAR(ESP_SERVER_OBJ, "SERVER_PORT",
472 talloc_asprintf(esp, "%u", socket_address->port));
475 SETVAR(ESP_SERVER_OBJ, "DOCUMENT_ROOT", lp_webapps_directory());
476 SETVAR(ESP_SERVER_OBJ, "SERVER_PROTOCOL", tls_enabled(web->conn->socket)?"https":"http");
477 SETVAR(ESP_SERVER_OBJ, "SERVER_SOFTWARE", "SAMBA");
478 SETVAR(ESP_SERVER_OBJ, "GATEWAY_INTERFACE", "CGI/1.1");
479 SETVAR(ESP_SERVER_OBJ, "TLS_SUPPORT", tls_support(edata->tls_params)?"True":"False");
483 /* the esp scripting lirary generates exceptions when
484 it hits a major error. We need to catch these and
485 report a internal server error via http
487 static jmp_buf ejs_exception_buf;
488 static const char *exception_reason;
490 static void web_server_ejs_exception(const char *reason)
494 ejsSetErrorMsg(0, "%s", reason);
495 exception_reason = ep->error;
497 exception_reason = reason;
499 DEBUG(0,("%s", exception_reason));
500 longjmp(ejs_exception_buf, -1);
503 static void web_server_ejs_exception(const char *reason)
505 DEBUG(0,("%s", reason));
511 process a esp request
513 static void esp_request(struct esp_state *esp, const char *url)
515 struct websrv_context *web = esp->web;
518 char *emsg = NULL, *buf;
520 if (http_readFile(web, &buf, &size, url, lp_webapps_directory()) != 0) {
521 http_error_unix(web, url);
526 if (setjmp(ejs_exception_buf) != 0) {
527 http_error(web, 500, exception_reason);
532 res = espProcessRequest(esp->req, url, buf, &emsg);
533 if (res != 0 && emsg) {
534 http_writeBlock(web, "<pre>", 5);
535 http_writeBlock(web, emsg, strlen(emsg));
536 http_writeBlock(web, "</pre>", 6);
542 process a JSON RPC request
544 static void jsonrpc_request(struct esp_state *esp)
546 struct websrv_context *web = esp->web;
547 const char *path = http_local_path(web,
549 lp_jsonrpc_services_dir());
559 "error.setOrigin(jsonrpc.Constant.ErrorOrigin.Server); "
560 "error.setError(jsonrpc.Constant.ErrorCode.UnexpectedOutput, "
561 " global.errorString);"
564 /* Ensure we got a valid path. */
566 /* should never occur */
567 http_error(esp->web, 500, "Internal server error");
571 /* Ensure that the JSON-RPC server request script exists */
572 if (!file_exist(path)) {
573 http_error_unix(esp->web, path);
577 /* Call the server request script */
578 if (http_readFile(web, &buf, &size,
579 JSONRPC_SERVER, lp_jsonrpc_services_dir()) != 0) {
580 http_error_unix(web, JSONRPC_SERVER);
585 if (setjmp(ejs_exception_buf) != 0) {
586 http_error(web, 500, exception_reason);
591 res = espProcessRequest(esp->req, JSONRPC_SERVER, buf, &emsg);
592 if (res != 0 && emsg) {
593 /* Save the error in a string accessible from javascript */
594 global = ejsGetGlobalObject(0);
596 mprCreateProperty(global, "errorString", &v);
598 /* Create and send a JsonRpcError object */
603 http_writeBlock(web, "<pre>", 5);
604 http_writeBlock(web, emsg, strlen(emsg));
605 http_writeBlock(web, "</pre>", 6);
612 perform pre-authentication on every page if /scripting/preauth.esp
613 exists. If this script generates any non-whitepace output at all,
614 then we don't run the requested URL.
616 note that the preauth is run even for static pages such as images, but not
617 for JSON-RPC service requests which do their own authentication via the
620 static BOOL http_preauth(struct esp_state *esp)
622 const char *path = http_local_path(esp->web,
624 lp_webapps_directory());
627 http_error(esp->web, 500, "Internal server error");
630 if (!file_exist(path)) {
631 /* if the preath script is not installed then allow access */
634 esp_request(esp, HTTP_PREAUTH_URI);
635 for (i=0;i<esp->web->output.content.length;i++) {
636 if (!isspace(esp->web->output.content.data[i])) {
637 /* if the preauth has generated content, then force it
638 to be html, so that we can show the login page for
639 failed access to images */
640 http_setHeader(esp->web, "Content-Type: text/html", 0);
644 data_blob_free(&esp->web->output.content);
650 handling of + and % escapes in http variables
652 static const char *http_unescape(TALLOC_CTX *mem_ctx, const char *p)
654 char *s0 = talloc_strdup(mem_ctx, p);
656 if (s == NULL) return NULL;
660 if (*s == '+') *s = ' ';
661 if (*s == '%' && sscanf(s+1, "%02x", &v) == 1) {
663 memmove(s+1, s+3, strlen(s+3)+1);
672 set a form or GET variable
674 static void esp_putvar(struct esp_state *esp, const char *var, const char *value)
676 if (strcasecmp(var, SAMBA_SESSION_KEY) == 0) {
677 /* special case support for browsers without cookie
679 esp->web->input.session_key = talloc_strdup(esp, value);
681 mprSetPropertyValue(&esp->variables[ESP_FORM_OBJ],
682 http_unescape(esp, var),
683 mprCreateStringVar(http_unescape(esp, value), 0));
689 parse the variables in a POST style request
691 static NTSTATUS http_parse_post(struct esp_state *esp)
693 DATA_BLOB b = esp->web->input.partial;
699 p = memchr(b.data, '&', b.length);
703 len = p - (char *)b.data;
705 line = talloc_strndup(esp, (char *)b.data, len);
706 NT_STATUS_HAVE_NO_MEMORY(line);
708 p = strchr(line,'=');
711 esp_putvar(esp, line, p+1);
726 parse the variables in a GET style request
728 static NTSTATUS http_parse_get(struct esp_state *esp)
730 struct websrv_context *web = esp->web;
734 p = strchr(web->input.url, '?');
735 web->input.query_string = p+1;
738 s = talloc_strdup(esp, esp->web->input.query_string);
739 NT_STATUS_HAVE_NO_MEMORY(s);
741 for (tok=strtok_r(s,"&;", &pp);tok;tok=strtok_r(NULL,"&;", &pp)) {
745 esp_putvar(esp, tok, p+1);
752 called when a session times out
754 static void session_timeout(struct event_context *ev, struct timed_event *te,
755 struct timeval t, void *private)
757 struct session_data *s = talloc_get_type(private, struct session_data);
764 static int session_destructor(struct session_data *s)
766 DLIST_REMOVE(s->edata->sessions, s);
771 setup the session for this request
773 static void http_setup_session(struct esp_state *esp)
775 const char *session_key = SAMBA_SESSION_KEY;
777 const char *cookie = esp->web->input.cookie;
778 const char *key = NULL;
779 struct esp_data *edata = talloc_get_type(esp->web->task->private, struct esp_data);
780 struct session_data *s;
781 BOOL generated_key = False;
783 /* look for our session key */
784 if (cookie && (p = strstr(cookie, session_key)) &&
785 p[strlen(session_key)] == '=') {
786 p += strlen(session_key)+1;
787 key = talloc_strndup(esp, p, strcspn(p, ";"));
790 if (key == NULL && esp->web->input.session_key) {
791 key = esp->web->input.session_key;
792 } else if (key == NULL) {
793 key = generate_random_str_list(esp, 16, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
794 generated_key = True;
797 /* try to find this session in the existing session list */
798 for (s=edata->sessions;s;s=s->next) {
799 if (strcmp(key, s->id) == 0) {
805 /* create a new session */
806 s = talloc_zero(edata, struct session_data);
807 s->id = talloc_steal(s, key);
811 s->lifetime = lp_parm_int(-1, "web", "sessiontimeout", 900);
812 DLIST_ADD(edata->sessions, s);
813 talloc_set_destructor(s, session_destructor);
814 if (!generated_key) {
815 mprSetPropertyValue(&esp->variables[ESP_REQUEST_OBJ],
816 "SESSION_EXPIRED", mprCreateStringVar("True", 0));
820 http_setCookie(esp->web, session_key, key, s->lifetime, "/", 0);
823 mprCopyVar(&esp->variables[ESP_SESSION_OBJ], s->data, MPR_DEEP_COPY);
826 esp->web->session = s;
830 /* callbacks for esp processing */
831 static const struct Esp esp_control = {
832 .maxScriptSize = 60000,
833 .writeBlock = http_writeBlock,
834 .setHeader = http_setHeader,
835 .redirect = http_redirect,
836 .setResponseCode = http_setResponseCode,
837 .readFile = http_readFileFromWebappsDir,
838 .mapToStorage = http_mapToStorage,
839 .setCookie = http_setCookie,
840 .createSession = http_createSession,
841 .destroySession = http_destroySession,
842 .getSessionId = http_getSessionId
846 process a complete http request
848 void http_process_input(struct websrv_context *web)
851 struct esp_state *esp = NULL;
852 struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
853 struct smbcalls_context *smbcalls_ctx;
855 void *save_mpr_ctx = mprMemCtx();
856 void *ejs_save = ejs_save_state();
858 const char *file_type = NULL;
864 enum page_type page_type;
866 const char *extension;
867 const char *mime_type;
868 enum page_type page_type;
870 {"gif", "image/gif"},
871 {"png", "image/png"},
872 {"jpg", "image/jpeg"},
873 {"txt", "text/plain"},
874 {"ico", "image/x-icon"},
876 {"esp", "text/html", True}
880 * give the smbcalls a chance to find the event context
881 * and messaging context
883 smbcalls_ctx = talloc(web, struct smbcalls_context);
884 if (smbcalls_ctx == NULL) goto internal_error;
885 smbcalls_ctx->event_ctx = web->conn->event.ctx;
886 smbcalls_ctx->msg_ctx = web->conn->msg_ctx;
888 esp = talloc_zero(smbcalls_ctx, struct esp_state);
889 if (esp == NULL) goto internal_error;
895 if (espOpen(&esp_control) != 0) goto internal_error;
897 for (i=0;i<ARRAY_SIZE(esp->variables);i++) {
898 esp->variables[i] = mprCreateUndefinedVar();
900 esp->variables[ESP_HEADERS_OBJ] = mprCreateObjVar("headers", ESP_HASH_SIZE);
901 esp->variables[ESP_FORM_OBJ] = mprCreateObjVar("form", ESP_HASH_SIZE);
902 esp->variables[ESP_APPLICATION_OBJ] = mprCreateObjVar("application", ESP_HASH_SIZE);
903 esp->variables[ESP_COOKIES_OBJ] = mprCreateObjVar("cookies", ESP_HASH_SIZE);
904 esp->variables[ESP_FILES_OBJ] = mprCreateObjVar("files", ESP_HASH_SIZE);
905 esp->variables[ESP_REQUEST_OBJ] = mprCreateObjVar("request", ESP_HASH_SIZE);
906 esp->variables[ESP_SERVER_OBJ] = mprCreateObjVar("server", ESP_HASH_SIZE);
907 esp->variables[ESP_SESSION_OBJ] = mprCreateObjVar("session", ESP_HASH_SIZE);
909 if (edata->application_data) {
910 mprCopyVar(&esp->variables[ESP_APPLICATION_OBJ],
911 edata->application_data, MPR_DEEP_COPY);
914 smb_setup_ejs_functions(web_server_ejs_exception);
916 if (web->input.url == NULL) {
917 http_error(web, 400, "You must specify a GET or POST request");
918 mprSetCtx(save_mpr_ctx);
919 ejs_restore_state(ejs_save);
923 /* parse any form or get variables */
924 if (web->input.post_request) {
925 status = http_parse_post(esp);
926 if (!NT_STATUS_IS_OK(status)) {
927 http_error(web, 400, "Malformed POST data");
928 mprSetCtx(save_mpr_ctx);
929 ejs_restore_state(ejs_save);
933 if (strchr(web->input.url, '?')) {
934 status = http_parse_get(esp);
935 if (!NT_STATUS_IS_OK(status)) {
936 http_error(web, 400, "Malformed GET data");
937 mprSetCtx(save_mpr_ctx);
938 ejs_restore_state(ejs_save);
943 http_setup_session(esp);
945 esp->req = espCreateRequest(web, web->input.url, esp->variables);
946 if (esp->req == NULL) goto internal_error;
949 * Work out the mime type. First, we see if the request is a JSON-RPC
950 * service request. If not, we look at the extension.
952 if (strncmp(web->input.url,
954 sizeof(JSONRPC_REQUEST) - 1) == 0 &&
955 (web->input.url[sizeof(JSONRPC_REQUEST) - 1] == '\0' ||
956 web->input.url[sizeof(JSONRPC_REQUEST) - 1] == '/')) {
957 page_type = page_type_jsonrpc;
958 file_type = "text/json";
961 p = strrchr(web->input.url, '.');
963 page_type = page_type_esp;
964 file_type = "text/html";
966 for (i=0;p && i<ARRAY_SIZE(mime_types);i++) {
967 if (strcmp(mime_types[i].extension, p+1) == 0) {
968 page_type = mime_types[i].page_type;
969 file_type = mime_types[i].mime_type;
972 if (file_type == NULL) {
973 page_type = page_type_simple;
974 file_type = "text/html";
978 /* setup basic headers */
979 http_setResponseCode(web, 200);
980 http_setHeader(web, talloc_asprintf(esp, "Date: %s",
981 http_timestring(esp, time(NULL))), 0);
982 http_setHeader(web, "Server: Samba", 0);
983 http_setHeader(web, "Connection: close", 0);
984 http_setHeader(web, talloc_asprintf(esp, "Content-Type: %s", file_type), 0);
986 http_setup_arrays(esp);
989 * Do pre-authentication. If pre-authentication succeeds, do
990 * page-type-specific processing.
994 case page_type_simple:
995 if (http_preauth(esp)) {
996 http_simple_request(web);
1001 if (http_preauth(esp)) {
1002 esp_request(esp, web->input.url);
1006 case page_type_jsonrpc:
1007 jsonrpc_request(esp);
1011 if (web->conn == NULL) {
1012 /* the connection has been terminated above us, probably
1014 goto internal_error;
1017 if (!web->output.output_pending) {
1018 http_output_headers(web);
1019 EVENT_FD_WRITEABLE(web->conn->event.fde);
1020 web->output.output_pending = True;
1023 /* copy any application data to long term storage in edata */
1024 talloc_free(edata->application_data);
1025 edata->application_data = talloc_zero(edata, struct MprVar);
1026 mprSetCtx(edata->application_data);
1027 mprCopyVar(edata->application_data, &esp->variables[ESP_APPLICATION_OBJ],
1031 /* copy any session data */
1033 talloc_free(web->session->data);
1034 web->session->data = talloc_zero(web->session, struct MprVar);
1035 if (esp->variables[ESP_SESSION_OBJ].properties == NULL ||
1036 esp->variables[ESP_SESSION_OBJ].properties[0].numItems == 0) {
1037 talloc_free(web->session);
1038 web->session = NULL;
1040 mprSetCtx(web->session->data);
1041 mprCopyVar(web->session->data, &esp->variables[ESP_SESSION_OBJ],
1043 /* setup the timeout for the session data */
1045 talloc_free(web->session->te);
1046 web->session->te = event_add_timed(web->conn->event.ctx, web->session,
1047 timeval_current_ofs(web->session->lifetime, 0),
1048 session_timeout, web->session);
1053 mprSetCtx(save_mpr_ctx);
1054 ejs_restore_state(ejs_save);
1060 if (web->conn != NULL) {
1061 http_error(web, 500, "Internal server error");
1063 mprSetCtx(save_mpr_ctx);
1064 ejs_restore_state(ejs_save);
1069 parse one line of header input
1071 NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
1074 web->input.end_of_headers = True;
1075 } else if (strncasecmp(line,"GET ", 4)==0) {
1076 web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
1077 } else if (strncasecmp(line,"POST ", 5)==0) {
1078 web->input.post_request = True;
1079 web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
1080 } else if (strchr(line, ':') == NULL) {
1081 http_error(web, 400, "This server only accepts GET and POST requests");
1082 return NT_STATUS_INVALID_PARAMETER;
1083 } else if (strncasecmp(line,"Content-Length: ", 16)==0) {
1084 web->input.content_length = strtoul(&line[16], NULL, 10);
1086 #define PULL_HEADER(v, s) do { \
1087 if (strncmp(line, s, strlen(s)) == 0) { \
1088 web->input.v = talloc_strdup(web, &line[strlen(s)]); \
1089 return NT_STATUS_OK; \
1092 PULL_HEADER(content_type, "Content-Type: ");
1093 PULL_HEADER(user_agent, "User-Agent: ");
1094 PULL_HEADER(referer, "Referer: ");
1095 PULL_HEADER(host, "Host: ");
1096 PULL_HEADER(accept_encoding, "Accept-Encoding: ");
1097 PULL_HEADER(accept_language, "Accept-Language: ");
1098 PULL_HEADER(accept_charset, "Accept-Charset: ");
1099 PULL_HEADER(cookie, "Cookie: ");
1102 /* ignore all other headers for now */
1103 return NT_STATUS_OK;
1108 setup the esp processor - called at task initialisation
1110 NTSTATUS http_setup_esp(struct task_server *task)
1112 struct esp_data *edata;
1114 edata = talloc_zero(task, struct esp_data);
1115 NT_STATUS_HAVE_NO_MEMORY(edata);
1117 task->private = edata;
1119 edata->tls_params = tls_initialise(edata);
1120 NT_STATUS_HAVE_NO_MEMORY(edata->tls_params);
1122 return NT_STATUS_OK;