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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "smbd/service_task.h"
25 #include "web_server/web_server.h"
26 #include "smbd/service_stream.h"
27 #include "smbd/service.h"
28 #include "lib/events/events.h"
29 #include "system/time.h"
30 #include "system/wait.h"
31 #include "lib/appweb/esp/esp.h"
32 #include "lib/appweb/ejs/ejsInternal.h"
33 #include "lib/util/dlinklist.h"
34 #include "lib/tls/tls.h"
35 #include "scripting/ejs/smbcalls.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);
201 status = data_blob_append(web, &web->output.content, buf, size);
202 if (!NT_STATUS_IS_OK(status)) return -1;
210 static void http_setHeader(EspHandle handle, const char *value, bool allowMultiple)
212 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
213 char *p = strchr(value, ':');
215 if (p && !allowMultiple && web->output.headers) {
217 for (i=0;web->output.headers[i];i++) {
218 if (strncmp(web->output.headers[i], value, (p+1)-value) == 0) {
219 web->output.headers[i] = talloc_strdup(web, value);
225 web->output.headers = str_list_add(web->output.headers, value);
226 talloc_steal(web, web->output.headers);
230 set a http response code
232 static void http_setResponseCode(EspHandle handle, int code)
234 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
235 web->output.response_code = code;
239 redirect to another web page
241 static void http_redirect(EspHandle handle, int code, char *url)
243 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
244 const char *host = web->input.host;
246 /* form the full url, unless it already looks like a url */
247 if (strchr(url, ':') == NULL) {
249 struct socket_address *socket_address = socket_get_my_addr(web->conn->socket, web);
250 if (socket_address == NULL) goto internal_error;
251 host = talloc_asprintf(web, "%s:%u",
252 socket_address->addr, socket_address->port);
254 if (host == NULL) goto internal_error;
256 char *p = strrchr(web->input.url, '/');
257 if (p == web->input.url) {
258 url = talloc_asprintf(web, "http%s://%s/%s",
259 tls_enabled(web->conn->socket)?"s":"",
262 int dirlen = p - web->input.url;
263 url = talloc_asprintf(web, "http%s://%s%*.*s/%s",
264 tls_enabled(web->conn->socket)?"s":"",
266 dirlen, dirlen, web->input.url,
269 if (url == NULL) goto internal_error;
273 http_setHeader(handle, talloc_asprintf(web, "Location: %s", url), 0);
275 /* make sure we give a valid redirect code */
276 if (code >= 300 && code < 400) {
277 http_setResponseCode(handle, code);
279 http_setResponseCode(handle, 302);
284 http_error(web, 500, "Internal server error");
291 static void http_setCookie(EspHandle handle, const char *name, const char *value,
292 int lifetime, const char *path, bool secure)
294 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
298 buf = talloc_asprintf(web, "Set-Cookie: %s=%s; path=%s; Expires=%s; %s",
299 name, value, path?path:"/",
300 http_timestring(web, time(NULL)+lifetime),
303 buf = talloc_asprintf(web, "Set-Cookie: %s=%s; path=%s; %s",
304 name, value, path?path:"/",
307 http_setHeader(handle, "Cache-control: no-cache=\"set-cookie\"", 0);
308 http_setHeader(handle, buf, 0);
313 return the session id
315 static const char *http_getSessionId(EspHandle handle)
317 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
318 return web->session->id;
324 static void http_createSession(EspHandle handle, int timeout)
326 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
328 web->session->lifetime = timeout;
329 http_setCookie(web, SAMBA_SESSION_KEY, web->session->id,
330 web->session->lifetime, "/", 0);
337 static void http_destroySession(EspHandle handle)
339 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
340 talloc_free(web->session);
346 setup for a raw http level error
348 void http_error(struct websrv_context *web, int code, const char *info)
351 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",
354 stream_terminate_connection(web->conn, "http_error: out of memory");
357 http_writeBlock(web, s, strlen(s));
358 http_setResponseCode(web, code);
359 http_output_headers(web);
360 EVENT_FD_NOT_READABLE(web->conn->event.fde);
361 EVENT_FD_WRITEABLE(web->conn->event.fde);
362 web->output.output_pending = True;
366 map a unix error code to a http error
368 void http_error_unix(struct websrv_context *web, const char *info)
380 info = talloc_asprintf(web, "%s<p>%s<p>\n", info, strerror(errno));
381 http_error(web, code, info);
386 a simple file request
388 static void http_simple_request(struct websrv_context *web)
390 const char *url = web->input.url;
394 path = http_local_path(web, url, lp_webapps_directory());
395 if (path == NULL) goto invalid;
398 web->output.fd = open(path, O_RDONLY);
399 if (web->output.fd == -1) {
400 DEBUG(0,("Failed to read file %s - %s\n", path, strerror(errno)));
401 http_error_unix(web, path);
405 if (fstat(web->output.fd, &st) != 0 || !S_ISREG(st.st_mode)) {
406 close(web->output.fd);
413 http_error(web, 400, "Malformed URL");
417 setup the standard ESP arrays
419 static void http_setup_arrays(struct esp_state *esp)
421 struct websrv_context *web = esp->web;
422 struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
423 struct EspRequest *req = esp->req;
424 struct socket_address *socket_address = socket_get_my_addr(web->conn->socket, esp);
425 struct socket_address *peer_address = socket_get_peer_addr(web->conn->socket, esp);
428 #define SETVAR(type, name, value) do { \
429 const char *v = value; \
430 if (v) espSetStringVar(req, type, name, v); \
433 SETVAR(ESP_REQUEST_OBJ, "CONTENT_LENGTH",
434 talloc_asprintf(esp, "%u", web->input.content_length));
435 SETVAR(ESP_REQUEST_OBJ, "QUERY_STRING", web->input.query_string);
436 SETVAR(ESP_REQUEST_OBJ, "POST_DATA",
438 web->input.partial.data,
439 web->input.partial.length));
440 SETVAR(ESP_REQUEST_OBJ, "REQUEST_METHOD", web->input.post_request?"POST":"GET");
441 SETVAR(ESP_REQUEST_OBJ, "REQUEST_URI", web->input.url);
442 p = strrchr(web->input.url, '/');
443 SETVAR(ESP_REQUEST_OBJ, "SCRIPT_NAME", p+1);
444 SETVAR(ESP_REQUEST_OBJ, "SCRIPT_FILENAME", web->input.url);
446 struct MprVar mpv = mprObject("socket_address");
447 mprSetPtrChild(&mpv, "socket_address", peer_address);
448 espSetVar(req, ESP_REQUEST_OBJ, "REMOTE_SOCKET_ADDRESS", mpv);
449 SETVAR(ESP_REQUEST_OBJ, "REMOTE_ADDR", peer_address->addr);
451 p = socket_get_peer_name(web->conn->socket, esp);
452 SETVAR(ESP_REQUEST_OBJ, "REMOTE_HOST", p);
453 SETVAR(ESP_REQUEST_OBJ, "REMOTE_USER", "");
454 SETVAR(ESP_REQUEST_OBJ, "CONTENT_TYPE", web->input.content_type);
456 SETVAR(ESP_REQUEST_OBJ, "SESSION_ID", web->session->id);
458 SETVAR(ESP_REQUEST_OBJ, "COOKIE_SUPPORT", web->input.cookie?"True":"False");
460 SETVAR(ESP_HEADERS_OBJ, "HTTP_REFERER", web->input.referer);
461 SETVAR(ESP_HEADERS_OBJ, "HOST", web->input.host);
462 SETVAR(ESP_HEADERS_OBJ, "ACCEPT_ENCODING", web->input.accept_encoding);
463 SETVAR(ESP_HEADERS_OBJ, "ACCEPT_LANGUAGE", web->input.accept_language);
464 SETVAR(ESP_HEADERS_OBJ, "ACCEPT_CHARSET", web->input.accept_charset);
465 SETVAR(ESP_HEADERS_OBJ, "COOKIE", web->input.cookie);
466 SETVAR(ESP_HEADERS_OBJ, "USER_AGENT", web->input.user_agent);
468 if (socket_address) {
469 SETVAR(ESP_SERVER_OBJ, "SERVER_ADDR", socket_address->addr);
470 SETVAR(ESP_SERVER_OBJ, "SERVER_NAME", socket_address->addr);
471 SETVAR(ESP_SERVER_OBJ, "SERVER_HOST", socket_address->addr);
472 SETVAR(ESP_SERVER_OBJ, "SERVER_PORT",
473 talloc_asprintf(esp, "%u", socket_address->port));
476 SETVAR(ESP_SERVER_OBJ, "DOCUMENT_ROOT", lp_webapps_directory());
477 SETVAR(ESP_SERVER_OBJ, "SERVER_PROTOCOL", tls_enabled(web->conn->socket)?"https":"http");
478 SETVAR(ESP_SERVER_OBJ, "SERVER_SOFTWARE", "SAMBA");
479 SETVAR(ESP_SERVER_OBJ, "GATEWAY_INTERFACE", "CGI/1.1");
480 SETVAR(ESP_SERVER_OBJ, "TLS_SUPPORT", tls_support(edata->tls_params)?"True":"False");
484 /* the esp scripting lirary generates exceptions when
485 it hits a major error. We need to catch these and
486 report a internal server error via http
488 static jmp_buf ejs_exception_buf;
489 static const char *exception_reason;
491 static void web_server_ejs_exception(const char *reason)
495 ejsSetErrorMsg(0, "%s", reason);
496 exception_reason = ep->error;
498 exception_reason = reason;
500 DEBUG(0,("%s", exception_reason));
501 longjmp(ejs_exception_buf, -1);
504 static void web_server_ejs_exception(const char *reason)
506 DEBUG(0,("%s", reason));
512 process a esp request
514 static void esp_request(struct esp_state *esp, const char *url)
516 struct websrv_context *web = esp->web;
519 char *emsg = NULL, *buf;
521 if (http_readFile(web, &buf, &size, url, lp_webapps_directory()) != 0) {
522 http_error_unix(web, url);
527 if (setjmp(ejs_exception_buf) != 0) {
528 http_error(web, 500, exception_reason);
533 res = espProcessRequest(esp->req, url, buf, &emsg);
534 if (res != 0 && emsg) {
535 http_writeBlock(web, "<pre>", 5);
536 http_writeBlock(web, emsg, strlen(emsg));
537 http_writeBlock(web, "</pre>", 6);
543 process a JSON RPC request
545 static void jsonrpc_request(struct esp_state *esp)
547 struct websrv_context *web = esp->web;
548 const char *path = http_local_path(web,
550 lp_jsonrpc_services_dir());
560 "error.setOrigin(jsonrpc.Constant.ErrorOrigin.Server); "
561 "error.setError(jsonrpc.Constant.ErrorCode.UnexpectedOutput, "
562 " global.errorString);"
565 /* Ensure we got a valid path. */
567 /* should never occur */
568 http_error(esp->web, 500, "Internal server error");
572 /* Ensure that the JSON-RPC server request script exists */
573 if (!file_exist(path)) {
574 http_error_unix(esp->web, path);
578 /* Call the server request script */
579 if (http_readFile(web, &buf, &size,
580 JSONRPC_SERVER, lp_jsonrpc_services_dir()) != 0) {
581 http_error_unix(web, JSONRPC_SERVER);
586 if (setjmp(ejs_exception_buf) != 0) {
587 http_error(web, 500, exception_reason);
592 res = espProcessRequest(esp->req, JSONRPC_SERVER, buf, &emsg);
593 if (res != 0 && emsg) {
594 /* Save the error in a string accessible from javascript */
595 global = ejsGetGlobalObject(0);
597 mprCreateProperty(global, "errorString", &v);
599 /* Create and send a JsonRpcError object */
604 http_writeBlock(web, "<pre>", 5);
605 http_writeBlock(web, emsg, strlen(emsg));
606 http_writeBlock(web, "</pre>", 6);
613 perform pre-authentication on every page if /scripting/preauth.esp
614 exists. If this script generates any non-whitepace output at all,
615 then we don't run the requested URL.
617 note that the preauth is run even for static pages such as images, but not
618 for JSON-RPC service requests which do their own authentication via the
621 static BOOL http_preauth(struct esp_state *esp)
623 const char *path = http_local_path(esp->web,
625 lp_webapps_directory());
628 http_error(esp->web, 500, "Internal server error");
631 if (!file_exist(path)) {
632 /* if the preath script is not installed then allow access */
635 esp_request(esp, HTTP_PREAUTH_URI);
636 for (i=0;i<esp->web->output.content.length;i++) {
637 if (!isspace(esp->web->output.content.data[i])) {
638 /* if the preauth has generated content, then force it
639 to be html, so that we can show the login page for
640 failed access to images */
641 http_setHeader(esp->web, "Content-Type: text/html", 0);
645 data_blob_free(&esp->web->output.content);
651 handling of + and % escapes in http variables
653 static const char *http_unescape(TALLOC_CTX *mem_ctx, const char *p)
655 char *s0 = talloc_strdup(mem_ctx, p);
657 if (s == NULL) return NULL;
661 if (*s == '+') *s = ' ';
662 if (*s == '%' && sscanf(s+1, "%02x", &v) == 1) {
664 memmove(s+1, s+3, strlen(s+3)+1);
673 set a form or GET variable
675 static void esp_putvar(struct esp_state *esp, const char *var, const char *value)
677 if (strcasecmp(var, SAMBA_SESSION_KEY) == 0) {
678 /* special case support for browsers without cookie
680 esp->web->input.session_key = talloc_strdup(esp, value);
682 mprSetPropertyValue(&esp->variables[ESP_FORM_OBJ],
683 http_unescape(esp, var),
684 mprCreateStringVar(http_unescape(esp, value), 0));
690 parse the variables in a POST style request
692 static NTSTATUS http_parse_post(struct esp_state *esp)
694 DATA_BLOB b = esp->web->input.partial;
700 p = memchr(b.data, '&', b.length);
704 len = p - (char *)b.data;
706 line = talloc_strndup(esp, (char *)b.data, len);
707 NT_STATUS_HAVE_NO_MEMORY(line);
709 p = strchr(line,'=');
712 esp_putvar(esp, line, p+1);
727 parse the variables in a GET style request
729 static NTSTATUS http_parse_get(struct esp_state *esp)
731 struct websrv_context *web = esp->web;
735 p = strchr(web->input.url, '?');
736 web->input.query_string = p+1;
739 s = talloc_strdup(esp, esp->web->input.query_string);
740 NT_STATUS_HAVE_NO_MEMORY(s);
742 for (tok=strtok_r(s,"&;", &pp);tok;tok=strtok_r(NULL,"&;", &pp)) {
746 esp_putvar(esp, tok, p+1);
753 called when a session times out
755 static void session_timeout(struct event_context *ev, struct timed_event *te,
756 struct timeval t, void *private)
758 struct session_data *s = talloc_get_type(private, struct session_data);
765 static int session_destructor(struct session_data *s)
767 DLIST_REMOVE(s->edata->sessions, s);
772 setup the session for this request
774 static void http_setup_session(struct esp_state *esp)
776 const char *session_key = SAMBA_SESSION_KEY;
778 const char *cookie = esp->web->input.cookie;
779 const char *key = NULL;
780 struct esp_data *edata = talloc_get_type(esp->web->task->private, struct esp_data);
781 struct session_data *s;
782 BOOL generated_key = False;
784 /* look for our session key */
785 if (cookie && (p = strstr(cookie, session_key)) &&
786 p[strlen(session_key)] == '=') {
787 p += strlen(session_key)+1;
788 key = talloc_strndup(esp, p, strcspn(p, ";"));
791 if (key == NULL && esp->web->input.session_key) {
792 key = esp->web->input.session_key;
793 } else if (key == NULL) {
794 key = generate_random_str_list(esp, 16, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
795 generated_key = True;
798 /* try to find this session in the existing session list */
799 for (s=edata->sessions;s;s=s->next) {
800 if (strcmp(key, s->id) == 0) {
806 /* create a new session */
807 s = talloc_zero(edata, struct session_data);
808 s->id = talloc_steal(s, key);
812 s->lifetime = lp_parm_int(-1, "web", "sessiontimeout", 900);
813 DLIST_ADD(edata->sessions, s);
814 talloc_set_destructor(s, session_destructor);
815 if (!generated_key) {
816 mprSetPropertyValue(&esp->variables[ESP_REQUEST_OBJ],
817 "SESSION_EXPIRED", mprCreateStringVar("True", 0));
821 http_setCookie(esp->web, session_key, key, s->lifetime, "/", 0);
824 mprCopyVar(&esp->variables[ESP_SESSION_OBJ], s->data, MPR_DEEP_COPY);
827 esp->web->session = s;
831 /* callbacks for esp processing */
832 static const struct Esp esp_control = {
833 .maxScriptSize = 60000,
834 .writeBlock = http_writeBlock,
835 .setHeader = http_setHeader,
836 .redirect = http_redirect,
837 .setResponseCode = http_setResponseCode,
838 .readFile = http_readFileFromWebappsDir,
839 .mapToStorage = http_mapToStorage,
840 .setCookie = http_setCookie,
841 .createSession = http_createSession,
842 .destroySession = http_destroySession,
843 .getSessionId = http_getSessionId
847 process a complete http request
849 void http_process_input(struct websrv_context *web)
852 struct esp_state *esp = NULL;
853 struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
854 struct smbcalls_context *smbcalls_ctx;
856 void *save_mpr_ctx = mprMemCtx();
857 void *ejs_save = ejs_save_state();
859 const char *file_type = NULL;
865 enum page_type page_type;
867 const char *extension;
868 const char *mime_type;
869 enum page_type page_type;
871 {"gif", "image/gif"},
872 {"png", "image/png"},
873 {"jpg", "image/jpeg"},
874 {"txt", "text/plain"},
875 {"ico", "image/x-icon"},
877 {"esp", "text/html", True}
881 * give the smbcalls a chance to find the event context
882 * and messaging context
884 smbcalls_ctx = talloc(web, struct smbcalls_context);
885 if (smbcalls_ctx == NULL) goto internal_error;
886 smbcalls_ctx->event_ctx = web->conn->event.ctx;
887 smbcalls_ctx->msg_ctx = web->conn->msg_ctx;
889 esp = talloc_zero(smbcalls_ctx, struct esp_state);
890 if (esp == NULL) goto internal_error;
896 if (espOpen(&esp_control) != 0) goto internal_error;
898 for (i=0;i<ARRAY_SIZE(esp->variables);i++) {
899 esp->variables[i] = mprCreateUndefinedVar();
901 esp->variables[ESP_HEADERS_OBJ] = mprCreateObjVar("headers", ESP_HASH_SIZE);
902 esp->variables[ESP_FORM_OBJ] = mprCreateObjVar("form", ESP_HASH_SIZE);
903 esp->variables[ESP_APPLICATION_OBJ] = mprCreateObjVar("application", ESP_HASH_SIZE);
904 esp->variables[ESP_COOKIES_OBJ] = mprCreateObjVar("cookies", ESP_HASH_SIZE);
905 esp->variables[ESP_FILES_OBJ] = mprCreateObjVar("files", ESP_HASH_SIZE);
906 esp->variables[ESP_REQUEST_OBJ] = mprCreateObjVar("request", ESP_HASH_SIZE);
907 esp->variables[ESP_SERVER_OBJ] = mprCreateObjVar("server", ESP_HASH_SIZE);
908 esp->variables[ESP_SESSION_OBJ] = mprCreateObjVar("session", ESP_HASH_SIZE);
910 if (edata->application_data) {
911 mprCopyVar(&esp->variables[ESP_APPLICATION_OBJ],
912 edata->application_data, MPR_DEEP_COPY);
915 smb_setup_ejs_functions(web_server_ejs_exception);
917 if (web->input.url == NULL) {
918 http_error(web, 400, "You must specify a GET or POST request");
919 mprSetCtx(save_mpr_ctx);
920 ejs_restore_state(ejs_save);
924 /* parse any form or get variables */
925 if (web->input.post_request) {
926 status = http_parse_post(esp);
927 if (!NT_STATUS_IS_OK(status)) {
928 http_error(web, 400, "Malformed POST data");
929 mprSetCtx(save_mpr_ctx);
930 ejs_restore_state(ejs_save);
934 if (strchr(web->input.url, '?')) {
935 status = http_parse_get(esp);
936 if (!NT_STATUS_IS_OK(status)) {
937 http_error(web, 400, "Malformed GET data");
938 mprSetCtx(save_mpr_ctx);
939 ejs_restore_state(ejs_save);
944 http_setup_session(esp);
946 esp->req = espCreateRequest(web, web->input.url, esp->variables);
947 if (esp->req == NULL) goto internal_error;
950 * Work out the mime type. First, we see if the request is a JSON-RPC
951 * service request. If not, we look at the extension.
953 if (strncmp(web->input.url,
955 sizeof(JSONRPC_REQUEST) - 1) == 0 &&
956 (web->input.url[sizeof(JSONRPC_REQUEST) - 1] == '\0' ||
957 web->input.url[sizeof(JSONRPC_REQUEST) - 1] == '/')) {
958 page_type = page_type_jsonrpc;
959 file_type = "text/json";
962 p = strrchr(web->input.url, '.');
964 page_type = page_type_esp;
965 file_type = "text/html";
967 for (i=0;p && i<ARRAY_SIZE(mime_types);i++) {
968 if (strcmp(mime_types[i].extension, p+1) == 0) {
969 page_type = mime_types[i].page_type;
970 file_type = mime_types[i].mime_type;
973 if (file_type == NULL) {
974 page_type = page_type_simple;
975 file_type = "text/html";
979 /* setup basic headers */
980 http_setResponseCode(web, 200);
981 http_setHeader(web, talloc_asprintf(esp, "Date: %s",
982 http_timestring(esp, time(NULL))), 0);
983 http_setHeader(web, "Server: Samba", 0);
984 http_setHeader(web, "Connection: close", 0);
985 http_setHeader(web, talloc_asprintf(esp, "Content-Type: %s", file_type), 0);
987 http_setup_arrays(esp);
990 * Do pre-authentication. If pre-authentication succeeds, do
991 * page-type-specific processing.
995 case page_type_simple:
996 if (http_preauth(esp)) {
997 http_simple_request(web);
1002 if (http_preauth(esp)) {
1003 esp_request(esp, web->input.url);
1007 case page_type_jsonrpc:
1008 jsonrpc_request(esp);
1012 if (web->conn == NULL) {
1013 /* the connection has been terminated above us, probably
1015 goto internal_error;
1018 if (!web->output.output_pending) {
1019 http_output_headers(web);
1020 EVENT_FD_WRITEABLE(web->conn->event.fde);
1021 web->output.output_pending = True;
1024 /* copy any application data to long term storage in edata */
1025 talloc_free(edata->application_data);
1026 edata->application_data = talloc_zero(edata, struct MprVar);
1027 mprSetCtx(edata->application_data);
1028 mprCopyVar(edata->application_data, &esp->variables[ESP_APPLICATION_OBJ],
1032 /* copy any session data */
1034 talloc_free(web->session->data);
1035 web->session->data = talloc_zero(web->session, struct MprVar);
1036 if (esp->variables[ESP_SESSION_OBJ].properties == NULL ||
1037 esp->variables[ESP_SESSION_OBJ].properties[0].numItems == 0) {
1038 talloc_free(web->session);
1039 web->session = NULL;
1041 mprSetCtx(web->session->data);
1042 mprCopyVar(web->session->data, &esp->variables[ESP_SESSION_OBJ],
1044 /* setup the timeout for the session data */
1046 talloc_free(web->session->te);
1047 web->session->te = event_add_timed(web->conn->event.ctx, web->session,
1048 timeval_current_ofs(web->session->lifetime, 0),
1049 session_timeout, web->session);
1054 mprSetCtx(save_mpr_ctx);
1055 ejs_restore_state(ejs_save);
1061 if (web->conn != NULL) {
1062 http_error(web, 500, "Internal server error");
1064 mprSetCtx(save_mpr_ctx);
1065 ejs_restore_state(ejs_save);
1070 parse one line of header input
1072 NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
1075 web->input.end_of_headers = True;
1076 } else if (strncasecmp(line,"GET ", 4)==0) {
1077 web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
1078 } else if (strncasecmp(line,"POST ", 5)==0) {
1079 web->input.post_request = True;
1080 web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
1081 } else if (strchr(line, ':') == NULL) {
1082 http_error(web, 400, "This server only accepts GET and POST requests");
1083 return NT_STATUS_INVALID_PARAMETER;
1084 } else if (strncasecmp(line,"Content-Length: ", 16)==0) {
1085 web->input.content_length = strtoul(&line[16], NULL, 10);
1087 #define PULL_HEADER(v, s) do { \
1088 if (strncmp(line, s, strlen(s)) == 0) { \
1089 web->input.v = talloc_strdup(web, &line[strlen(s)]); \
1090 return NT_STATUS_OK; \
1093 PULL_HEADER(content_type, "Content-Type: ");
1094 PULL_HEADER(user_agent, "User-Agent: ");
1095 PULL_HEADER(referer, "Referer: ");
1096 PULL_HEADER(host, "Host: ");
1097 PULL_HEADER(accept_encoding, "Accept-Encoding: ");
1098 PULL_HEADER(accept_language, "Accept-Language: ");
1099 PULL_HEADER(accept_charset, "Accept-Charset: ");
1100 PULL_HEADER(cookie, "Cookie: ");
1103 /* ignore all other headers for now */
1104 return NT_STATUS_OK;
1109 setup the esp processor - called at task initialisation
1111 NTSTATUS http_setup_esp(struct task_server *task)
1113 struct esp_data *edata;
1115 edata = talloc_zero(task, struct esp_data);
1116 NT_STATUS_HAVE_NO_MEMORY(edata);
1118 task->private = edata;
1120 edata->tls_params = tls_initialise(edata);
1121 NT_STATUS_HAVE_NO_MEMORY(edata->tls_params);
1123 return NT_STATUS_OK;