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"
36 #define SAMBA_SESSION_KEY "SambaSessionId"
37 #define HTTP_PREAUTH_URI "/scripting/preauth.esp"
38 #define JSONRPC_REQUEST "/services"
39 #define JSONRPC_SERVER "/request.esp"
41 /* state of the esp subsystem for a specific request */
43 struct websrv_context *web;
44 struct EspRequest *req;
45 struct MprVar variables[ESP_OBJ_MAX];
46 struct session_data *session;
50 output the http headers
52 static void http_output_headers(struct websrv_context *web)
57 uint32_t content_length = 0;
58 const char *response_string = "Unknown Code";
61 const char *response_string;
67 { 304, "Not Modified" },
68 { 400, "Bad request" },
69 { 401, "Unauthorized" },
72 { 500, "Internal Server Error" },
73 { 501, "Not implemented" }
75 for (i=0;i<ARRAY_SIZE(codes);i++) {
76 if (codes[i].code == web->output.response_code) {
77 response_string = codes[i].response_string;
81 if (web->output.headers == NULL) return;
82 s = talloc_asprintf(web, "HTTP/1.0 %u %s\r\n",
83 web->output.response_code, response_string);
84 if (s == NULL) return;
85 for (i=0;web->output.headers[i];i++) {
86 s = talloc_asprintf_append(s, "%s\r\n", web->output.headers[i]);
89 /* work out the content length */
90 content_length = web->output.content.length;
91 if (web->output.fd != -1) {
93 fstat(web->output.fd, &st);
94 content_length += st.st_size;
96 s = talloc_asprintf_append(s, "Content-Length: %u\r\n\r\n", content_length);
97 if (s == NULL) return;
99 b = web->output.content;
100 web->output.content = data_blob_string_const(s);
101 data_blob_append(web, &web->output.content, b.data, b.length);
106 return the local path for a URL
108 static const char *http_local_path(struct websrv_context *web,
110 const char *base_dir)
115 /* check that the url is OK */
116 if (url[0] != '/') return NULL;
118 for (i=0;url[i];i++) {
119 if ((!isalnum((unsigned char)url[i]) && !strchr("./_-", url[i])) ||
120 (url[i] == '.' && strchr("/.", url[i+1]))) {
125 path = talloc_asprintf(web, "%s/%s", base_dir, url+1);
126 if (path == NULL) return NULL;
128 if (directory_exist(path)) {
129 path = talloc_asprintf_append(path, "/index.esp");
135 called when esp wants to read a file to support include() calls
137 static int http_readFile(EspHandle handle,
141 const char *base_dir)
143 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
148 path = http_local_path(web, path, base_dir);
149 if (path == NULL) goto failed;
151 fd = open(path, O_RDONLY);
152 if (fd == -1 || fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) goto failed;
154 *buf = talloc_size(handle, st.st_size+1);
155 if (*buf == NULL) goto failed;
157 if (read(fd, *buf, st.st_size) != st.st_size) goto failed;
159 (*buf)[st.st_size] = 0;
166 DEBUG(0,("Failed to read file %s - %s\n", path, strerror(errno)));
167 if (fd != -1) close(fd);
173 static int http_readFileFromWebappsDir(EspHandle handle,
178 return http_readFile(handle, buf, len, path, lp_webapps_directory());
184 called when esp wants to find the real path of a file
186 static int http_mapToStorage(EspHandle handle, char *path, int len, const char *uri, int flags)
188 if (uri == NULL || strlen(uri) >= len) return -1;
189 strncpy(path, uri, len);
194 called when esp wants to output something
196 static int http_writeBlock(EspHandle handle, const char *buf, int size)
198 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
199 if (!data_blob_append(web, &web->output.content, buf, size))
208 static void http_setHeader(EspHandle handle, const char *value, bool allowMultiple)
210 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
211 char *p = strchr(value, ':');
213 if (p && !allowMultiple && web->output.headers) {
215 for (i=0;web->output.headers[i];i++) {
216 if (strncmp(web->output.headers[i], value, (p+1)-value) == 0) {
217 web->output.headers[i] = talloc_strdup(web, value);
223 web->output.headers = str_list_add(web->output.headers, value);
224 talloc_steal(web, web->output.headers);
228 set a http response code
230 static void http_setResponseCode(EspHandle handle, int code)
232 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
233 web->output.response_code = code;
237 redirect to another web page
239 static void http_redirect(EspHandle handle, int code, char *url)
241 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
242 const char *host = web->input.host;
244 /* form the full url, unless it already looks like a url */
245 if (strchr(url, ':') == NULL) {
247 struct socket_address *socket_address = socket_get_my_addr(web->conn->socket, web);
248 if (socket_address == NULL) goto internal_error;
249 host = talloc_asprintf(web, "%s:%u",
250 socket_address->addr, socket_address->port);
252 if (host == NULL) goto internal_error;
254 char *p = strrchr(web->input.url, '/');
255 if (p == web->input.url) {
256 url = talloc_asprintf(web, "http%s://%s/%s",
257 tls_enabled(web->conn->socket)?"s":"",
260 int dirlen = p - web->input.url;
261 url = talloc_asprintf(web, "http%s://%s%*.*s/%s",
262 tls_enabled(web->conn->socket)?"s":"",
264 dirlen, dirlen, web->input.url,
267 if (url == NULL) goto internal_error;
271 http_setHeader(handle, talloc_asprintf(web, "Location: %s", url), 0);
273 /* make sure we give a valid redirect code */
274 if (code >= 300 && code < 400) {
275 http_setResponseCode(handle, code);
277 http_setResponseCode(handle, 302);
282 http_error(web, 500, "Internal server error");
289 static void http_setCookie(EspHandle handle, const char *name, const char *value,
290 int lifetime, const char *path, bool secure)
292 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
296 buf = talloc_asprintf(web, "Set-Cookie: %s=%s; path=%s; Expires=%s; %s",
297 name, value, path?path:"/",
298 http_timestring(web, time(NULL)+lifetime),
301 buf = talloc_asprintf(web, "Set-Cookie: %s=%s; path=%s; %s",
302 name, value, path?path:"/",
305 http_setHeader(handle, "Cache-control: no-cache=\"set-cookie\"", 0);
306 http_setHeader(handle, buf, 0);
311 return the session id
313 static const char *http_getSessionId(EspHandle handle)
315 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
316 return web->session->id;
322 static void http_createSession(EspHandle handle, int timeout)
324 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
326 web->session->lifetime = timeout;
327 http_setCookie(web, SAMBA_SESSION_KEY, web->session->id,
328 web->session->lifetime, "/", 0);
335 static void http_destroySession(EspHandle handle)
337 struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
338 talloc_free(web->session);
344 setup for a raw http level error
346 void http_error(struct websrv_context *web, int code, const char *info)
349 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",
352 stream_terminate_connection(web->conn, "http_error: out of memory");
355 http_writeBlock(web, s, strlen(s));
356 http_setResponseCode(web, code);
357 http_output_headers(web);
358 EVENT_FD_NOT_READABLE(web->conn->event.fde);
359 EVENT_FD_WRITEABLE(web->conn->event.fde);
360 web->output.output_pending = True;
364 map a unix error code to a http error
366 void http_error_unix(struct websrv_context *web, const char *info)
378 info = talloc_asprintf(web, "%s<p>%s<p>\n", info, strerror(errno));
379 http_error(web, code, info);
384 a simple file request
386 static void http_simple_request(struct websrv_context *web)
388 const char *url = web->input.url;
392 path = http_local_path(web, url, lp_webapps_directory());
393 if (path == NULL) goto invalid;
396 web->output.fd = open(path, O_RDONLY);
397 if (web->output.fd == -1) {
398 DEBUG(0,("Failed to read file %s - %s\n", path, strerror(errno)));
399 http_error_unix(web, path);
403 if (fstat(web->output.fd, &st) != 0 || !S_ISREG(st.st_mode)) {
404 close(web->output.fd);
411 http_error(web, 400, "Malformed URL");
415 setup the standard ESP arrays
417 static void http_setup_arrays(struct esp_state *esp)
419 struct websrv_context *web = esp->web;
420 struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
421 struct EspRequest *req = esp->req;
422 struct socket_address *socket_address = socket_get_my_addr(web->conn->socket, esp);
423 struct socket_address *peer_address = socket_get_peer_addr(web->conn->socket, esp);
426 #define SETVAR(type, name, value) do { \
427 const char *v = value; \
428 if (v) espSetStringVar(req, type, name, v); \
431 SETVAR(ESP_REQUEST_OBJ, "CONTENT_LENGTH",
432 talloc_asprintf(esp, "%u", web->input.content_length));
433 SETVAR(ESP_REQUEST_OBJ, "QUERY_STRING", web->input.query_string);
434 SETVAR(ESP_REQUEST_OBJ, "POST_DATA",
436 web->input.partial.data,
437 web->input.partial.length));
438 SETVAR(ESP_REQUEST_OBJ, "REQUEST_METHOD", web->input.post_request?"POST":"GET");
439 SETVAR(ESP_REQUEST_OBJ, "REQUEST_URI", web->input.url);
440 p = strrchr(web->input.url, '/');
441 SETVAR(ESP_REQUEST_OBJ, "SCRIPT_NAME", p+1);
442 SETVAR(ESP_REQUEST_OBJ, "SCRIPT_FILENAME", web->input.url);
444 struct MprVar mpv = mprObject("socket_address");
445 mprSetPtrChild(&mpv, "socket_address", peer_address);
446 espSetVar(req, ESP_REQUEST_OBJ, "REMOTE_SOCKET_ADDRESS", mpv);
447 SETVAR(ESP_REQUEST_OBJ, "REMOTE_ADDR", peer_address->addr);
449 p = socket_get_peer_name(web->conn->socket, esp);
450 SETVAR(ESP_REQUEST_OBJ, "REMOTE_HOST", p);
451 SETVAR(ESP_REQUEST_OBJ, "REMOTE_USER", "");
452 SETVAR(ESP_REQUEST_OBJ, "CONTENT_TYPE", web->input.content_type);
454 SETVAR(ESP_REQUEST_OBJ, "SESSION_ID", web->session->id);
456 SETVAR(ESP_REQUEST_OBJ, "COOKIE_SUPPORT", web->input.cookie?"True":"False");
458 SETVAR(ESP_HEADERS_OBJ, "HTTP_REFERER", web->input.referer);
459 SETVAR(ESP_HEADERS_OBJ, "HOST", web->input.host);
460 SETVAR(ESP_HEADERS_OBJ, "ACCEPT_ENCODING", web->input.accept_encoding);
461 SETVAR(ESP_HEADERS_OBJ, "ACCEPT_LANGUAGE", web->input.accept_language);
462 SETVAR(ESP_HEADERS_OBJ, "ACCEPT_CHARSET", web->input.accept_charset);
463 SETVAR(ESP_HEADERS_OBJ, "COOKIE", web->input.cookie);
464 SETVAR(ESP_HEADERS_OBJ, "USER_AGENT", web->input.user_agent);
466 if (socket_address) {
467 SETVAR(ESP_SERVER_OBJ, "SERVER_ADDR", socket_address->addr);
468 SETVAR(ESP_SERVER_OBJ, "SERVER_NAME", socket_address->addr);
469 SETVAR(ESP_SERVER_OBJ, "SERVER_HOST", socket_address->addr);
470 SETVAR(ESP_SERVER_OBJ, "SERVER_PORT",
471 talloc_asprintf(esp, "%u", socket_address->port));
474 SETVAR(ESP_SERVER_OBJ, "DOCUMENT_ROOT", lp_webapps_directory());
475 SETVAR(ESP_SERVER_OBJ, "SERVER_PROTOCOL", tls_enabled(web->conn->socket)?"https":"http");
476 SETVAR(ESP_SERVER_OBJ, "SERVER_SOFTWARE", "SAMBA");
477 SETVAR(ESP_SERVER_OBJ, "GATEWAY_INTERFACE", "CGI/1.1");
478 SETVAR(ESP_SERVER_OBJ, "TLS_SUPPORT", tls_support(edata->tls_params)?"True":"False");
482 /* the esp scripting lirary generates exceptions when
483 it hits a major error. We need to catch these and
484 report a internal server error via http
486 static jmp_buf ejs_exception_buf;
487 static const char *exception_reason;
489 static void web_server_ejs_exception(const char *reason)
493 ejsSetErrorMsg(0, "%s", reason);
494 exception_reason = ep->error;
496 exception_reason = reason;
498 DEBUG(0,("%s", exception_reason));
499 longjmp(ejs_exception_buf, -1);
502 static void web_server_ejs_exception(const char *reason)
504 DEBUG(0,("%s", reason));
510 process a esp request
512 static void esp_request(struct esp_state *esp, const char *url)
514 struct websrv_context *web = esp->web;
517 char *emsg = NULL, *buf;
519 if (http_readFile(web, &buf, &size, url, lp_webapps_directory()) != 0) {
520 http_error_unix(web, url);
525 if (setjmp(ejs_exception_buf) != 0) {
526 http_error(web, 500, exception_reason);
531 res = espProcessRequest(esp->req, url, buf, &emsg);
532 if (res != 0 && emsg) {
533 http_writeBlock(web, "<pre>", 5);
534 http_writeBlock(web, emsg, strlen(emsg));
535 http_writeBlock(web, "</pre>", 6);
541 process a JSON RPC request
543 static void jsonrpc_request(struct esp_state *esp)
545 struct websrv_context *web = esp->web;
546 const char *path = http_local_path(web,
548 lp_jsonrpc_services_dir());
558 "error.setOrigin(jsonrpc.Constant.ErrorOrigin.Server); "
559 "error.setError(jsonrpc.Constant.ErrorCode.UnexpectedOutput, "
560 " global.errorString);"
563 /* Ensure we got a valid path. */
565 /* should never occur */
566 http_error(esp->web, 500, "Internal server error");
570 /* Ensure that the JSON-RPC server request script exists */
571 if (!file_exist(path)) {
572 http_error_unix(esp->web, path);
576 /* Call the server request script */
577 if (http_readFile(web, &buf, &size,
578 JSONRPC_SERVER, lp_jsonrpc_services_dir()) != 0) {
579 http_error_unix(web, JSONRPC_SERVER);
584 if (setjmp(ejs_exception_buf) != 0) {
585 http_error(web, 500, exception_reason);
590 res = espProcessRequest(esp->req, JSONRPC_SERVER, buf, &emsg);
591 if (res != 0 && emsg) {
592 /* Save the error in a string accessible from javascript */
593 global = ejsGetGlobalObject(0);
595 mprCreateProperty(global, "errorString", &v);
597 /* Create and send a JsonRpcError object */
602 http_writeBlock(web, "<pre>", 5);
603 http_writeBlock(web, emsg, strlen(emsg));
604 http_writeBlock(web, "</pre>", 6);
611 perform pre-authentication on every page if /scripting/preauth.esp
612 exists. If this script generates any non-whitepace output at all,
613 then we don't run the requested URL.
615 note that the preauth is run even for static pages such as images, but not
616 for JSON-RPC service requests which do their own authentication via the
619 static BOOL http_preauth(struct esp_state *esp)
621 const char *path = http_local_path(esp->web,
623 lp_webapps_directory());
626 http_error(esp->web, 500, "Internal server error");
629 if (!file_exist(path)) {
630 /* if the preath script is not installed then allow access */
633 esp_request(esp, HTTP_PREAUTH_URI);
634 for (i=0;i<esp->web->output.content.length;i++) {
635 if (!isspace(esp->web->output.content.data[i])) {
636 /* if the preauth has generated content, then force it
637 to be html, so that we can show the login page for
638 failed access to images */
639 http_setHeader(esp->web, "Content-Type: text/html", 0);
643 data_blob_free(&esp->web->output.content);
649 handling of + and % escapes in http variables
651 static const char *http_unescape(TALLOC_CTX *mem_ctx, const char *p)
653 char *s0 = talloc_strdup(mem_ctx, p);
655 if (s == NULL) return NULL;
659 if (*s == '+') *s = ' ';
660 if (*s == '%' && sscanf(s+1, "%02x", &v) == 1) {
662 memmove(s+1, s+3, strlen(s+3)+1);
671 set a form or GET variable
673 static void esp_putvar(struct esp_state *esp, const char *var, const char *value)
675 if (strcasecmp(var, SAMBA_SESSION_KEY) == 0) {
676 /* special case support for browsers without cookie
678 esp->web->input.session_key = talloc_strdup(esp, value);
680 mprSetPropertyValue(&esp->variables[ESP_FORM_OBJ],
681 http_unescape(esp, var),
682 mprCreateStringVar(http_unescape(esp, value), 0));
688 parse the variables in a POST style request
690 static NTSTATUS http_parse_post(struct esp_state *esp)
692 DATA_BLOB b = esp->web->input.partial;
698 p = memchr(b.data, '&', b.length);
702 len = p - (char *)b.data;
704 line = talloc_strndup(esp, (char *)b.data, len);
705 NT_STATUS_HAVE_NO_MEMORY(line);
707 p = strchr(line,'=');
710 esp_putvar(esp, line, p+1);
725 parse the variables in a GET style request
727 static NTSTATUS http_parse_get(struct esp_state *esp)
729 struct websrv_context *web = esp->web;
733 p = strchr(web->input.url, '?');
734 web->input.query_string = p+1;
737 s = talloc_strdup(esp, esp->web->input.query_string);
738 NT_STATUS_HAVE_NO_MEMORY(s);
740 for (tok=strtok_r(s,"&;", &pp);tok;tok=strtok_r(NULL,"&;", &pp)) {
744 esp_putvar(esp, tok, p+1);
751 called when a session times out
753 static void session_timeout(struct event_context *ev, struct timed_event *te,
754 struct timeval t, void *private)
756 struct session_data *s = talloc_get_type(private, struct session_data);
763 static int session_destructor(struct session_data *s)
765 DLIST_REMOVE(s->edata->sessions, s);
770 setup the session for this request
772 static void http_setup_session(struct esp_state *esp)
774 const char *session_key = SAMBA_SESSION_KEY;
776 const char *cookie = esp->web->input.cookie;
777 const char *key = NULL;
778 struct esp_data *edata = talloc_get_type(esp->web->task->private, struct esp_data);
779 struct session_data *s;
780 BOOL generated_key = False;
782 /* look for our session key */
783 if (cookie && (p = strstr(cookie, session_key)) &&
784 p[strlen(session_key)] == '=') {
785 p += strlen(session_key)+1;
786 key = talloc_strndup(esp, p, strcspn(p, ";"));
789 if (key == NULL && esp->web->input.session_key) {
790 key = esp->web->input.session_key;
791 } else if (key == NULL) {
792 key = generate_random_str_list(esp, 16, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
793 generated_key = True;
796 /* try to find this session in the existing session list */
797 for (s=edata->sessions;s;s=s->next) {
798 if (strcmp(key, s->id) == 0) {
804 /* create a new session */
805 s = talloc_zero(edata, struct session_data);
806 s->id = talloc_steal(s, key);
810 s->lifetime = lp_parm_int(-1, "web", "sessiontimeout", 900);
811 DLIST_ADD(edata->sessions, s);
812 talloc_set_destructor(s, session_destructor);
813 if (!generated_key) {
814 mprSetPropertyValue(&esp->variables[ESP_REQUEST_OBJ],
815 "SESSION_EXPIRED", mprCreateStringVar("True", 0));
819 http_setCookie(esp->web, session_key, key, s->lifetime, "/", 0);
822 mprCopyVar(&esp->variables[ESP_SESSION_OBJ], s->data, MPR_DEEP_COPY);
825 esp->web->session = s;
829 /* callbacks for esp processing */
830 static const struct Esp esp_control = {
831 .maxScriptSize = 60000,
832 .writeBlock = http_writeBlock,
833 .setHeader = http_setHeader,
834 .redirect = http_redirect,
835 .setResponseCode = http_setResponseCode,
836 .readFile = http_readFileFromWebappsDir,
837 .mapToStorage = http_mapToStorage,
838 .setCookie = http_setCookie,
839 .createSession = http_createSession,
840 .destroySession = http_destroySession,
841 .getSessionId = http_getSessionId
845 process a complete http request
847 void http_process_input(struct websrv_context *web)
850 struct esp_state *esp = NULL;
851 struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
852 struct smbcalls_context *smbcalls_ctx;
854 void *save_mpr_ctx = mprMemCtx();
855 void *ejs_save = ejs_save_state();
857 const char *file_type = NULL;
863 enum page_type page_type;
865 const char *extension;
866 const char *mime_type;
867 enum page_type page_type;
869 {"gif", "image/gif"},
870 {"png", "image/png"},
871 {"jpg", "image/jpeg"},
872 {"txt", "text/plain"},
873 {"ico", "image/x-icon"},
875 {"esp", "text/html", True}
879 * give the smbcalls a chance to find the event context
880 * and messaging context
882 smbcalls_ctx = talloc(web, struct smbcalls_context);
883 if (smbcalls_ctx == NULL) goto internal_error;
884 smbcalls_ctx->event_ctx = web->conn->event.ctx;
885 smbcalls_ctx->msg_ctx = web->conn->msg_ctx;
887 esp = talloc_zero(smbcalls_ctx, struct esp_state);
888 if (esp == NULL) goto internal_error;
894 if (espOpen(&esp_control) != 0) goto internal_error;
896 for (i=0;i<ARRAY_SIZE(esp->variables);i++) {
897 esp->variables[i] = mprCreateUndefinedVar();
899 esp->variables[ESP_HEADERS_OBJ] = mprCreateObjVar("headers", ESP_HASH_SIZE);
900 esp->variables[ESP_FORM_OBJ] = mprCreateObjVar("form", ESP_HASH_SIZE);
901 esp->variables[ESP_APPLICATION_OBJ] = mprCreateObjVar("application", ESP_HASH_SIZE);
902 esp->variables[ESP_COOKIES_OBJ] = mprCreateObjVar("cookies", ESP_HASH_SIZE);
903 esp->variables[ESP_FILES_OBJ] = mprCreateObjVar("files", ESP_HASH_SIZE);
904 esp->variables[ESP_REQUEST_OBJ] = mprCreateObjVar("request", ESP_HASH_SIZE);
905 esp->variables[ESP_SERVER_OBJ] = mprCreateObjVar("server", ESP_HASH_SIZE);
906 esp->variables[ESP_SESSION_OBJ] = mprCreateObjVar("session", ESP_HASH_SIZE);
908 if (edata->application_data) {
909 mprCopyVar(&esp->variables[ESP_APPLICATION_OBJ],
910 edata->application_data, MPR_DEEP_COPY);
913 smb_setup_ejs_functions(web_server_ejs_exception);
915 if (web->input.url == NULL) {
916 http_error(web, 400, "You must specify a GET or POST request");
917 mprSetCtx(save_mpr_ctx);
918 ejs_restore_state(ejs_save);
922 /* parse any form or get variables */
923 if (web->input.post_request) {
924 status = http_parse_post(esp);
925 if (!NT_STATUS_IS_OK(status)) {
926 http_error(web, 400, "Malformed POST data");
927 mprSetCtx(save_mpr_ctx);
928 ejs_restore_state(ejs_save);
932 if (strchr(web->input.url, '?')) {
933 status = http_parse_get(esp);
934 if (!NT_STATUS_IS_OK(status)) {
935 http_error(web, 400, "Malformed GET data");
936 mprSetCtx(save_mpr_ctx);
937 ejs_restore_state(ejs_save);
942 http_setup_session(esp);
944 esp->req = espCreateRequest(web, web->input.url, esp->variables);
945 if (esp->req == NULL) goto internal_error;
948 * Work out the mime type. First, we see if the request is a JSON-RPC
949 * service request. If not, we look at the extension.
951 if (strncmp(web->input.url,
953 sizeof(JSONRPC_REQUEST) - 1) == 0 &&
954 (web->input.url[sizeof(JSONRPC_REQUEST) - 1] == '\0' ||
955 web->input.url[sizeof(JSONRPC_REQUEST) - 1] == '/')) {
956 page_type = page_type_jsonrpc;
957 file_type = "text/json";
960 p = strrchr(web->input.url, '.');
962 page_type = page_type_esp;
963 file_type = "text/html";
965 for (i=0;p && i<ARRAY_SIZE(mime_types);i++) {
966 if (strcmp(mime_types[i].extension, p+1) == 0) {
967 page_type = mime_types[i].page_type;
968 file_type = mime_types[i].mime_type;
971 if (file_type == NULL) {
972 page_type = page_type_simple;
973 file_type = "text/html";
977 /* setup basic headers */
978 http_setResponseCode(web, 200);
979 http_setHeader(web, talloc_asprintf(esp, "Date: %s",
980 http_timestring(esp, time(NULL))), 0);
981 http_setHeader(web, "Server: Samba", 0);
982 http_setHeader(web, "Connection: close", 0);
983 http_setHeader(web, talloc_asprintf(esp, "Content-Type: %s", file_type), 0);
985 http_setup_arrays(esp);
988 * Do pre-authentication. If pre-authentication succeeds, do
989 * page-type-specific processing.
993 case page_type_simple:
994 if (http_preauth(esp)) {
995 http_simple_request(web);
1000 if (http_preauth(esp)) {
1001 esp_request(esp, web->input.url);
1005 case page_type_jsonrpc:
1006 jsonrpc_request(esp);
1010 if (web->conn == NULL) {
1011 /* the connection has been terminated above us, probably
1013 goto internal_error;
1016 if (!web->output.output_pending) {
1017 http_output_headers(web);
1018 EVENT_FD_WRITEABLE(web->conn->event.fde);
1019 web->output.output_pending = True;
1022 /* copy any application data to long term storage in edata */
1023 talloc_free(edata->application_data);
1024 edata->application_data = talloc_zero(edata, struct MprVar);
1025 mprSetCtx(edata->application_data);
1026 mprCopyVar(edata->application_data, &esp->variables[ESP_APPLICATION_OBJ],
1030 /* copy any session data */
1032 talloc_free(web->session->data);
1033 web->session->data = talloc_zero(web->session, struct MprVar);
1034 if (esp->variables[ESP_SESSION_OBJ].properties == NULL ||
1035 esp->variables[ESP_SESSION_OBJ].properties[0].numItems == 0) {
1036 talloc_free(web->session);
1037 web->session = NULL;
1039 mprSetCtx(web->session->data);
1040 mprCopyVar(web->session->data, &esp->variables[ESP_SESSION_OBJ],
1042 /* setup the timeout for the session data */
1044 talloc_free(web->session->te);
1045 web->session->te = event_add_timed(web->conn->event.ctx, web->session,
1046 timeval_current_ofs(web->session->lifetime, 0),
1047 session_timeout, web->session);
1052 mprSetCtx(save_mpr_ctx);
1053 ejs_restore_state(ejs_save);
1059 if (web->conn != NULL) {
1060 http_error(web, 500, "Internal server error");
1062 mprSetCtx(save_mpr_ctx);
1063 ejs_restore_state(ejs_save);
1068 parse one line of header input
1070 NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
1073 web->input.end_of_headers = True;
1074 } else if (strncasecmp(line,"GET ", 4)==0) {
1075 web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
1076 } else if (strncasecmp(line,"POST ", 5)==0) {
1077 web->input.post_request = True;
1078 web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
1079 } else if (strchr(line, ':') == NULL) {
1080 http_error(web, 400, "This server only accepts GET and POST requests");
1081 return NT_STATUS_INVALID_PARAMETER;
1082 } else if (strncasecmp(line,"Content-Length: ", 16)==0) {
1083 web->input.content_length = strtoul(&line[16], NULL, 10);
1085 #define PULL_HEADER(v, s) do { \
1086 if (strncmp(line, s, strlen(s)) == 0) { \
1087 web->input.v = talloc_strdup(web, &line[strlen(s)]); \
1088 return NT_STATUS_OK; \
1091 PULL_HEADER(content_type, "Content-Type: ");
1092 PULL_HEADER(user_agent, "User-Agent: ");
1093 PULL_HEADER(referer, "Referer: ");
1094 PULL_HEADER(host, "Host: ");
1095 PULL_HEADER(accept_encoding, "Accept-Encoding: ");
1096 PULL_HEADER(accept_language, "Accept-Language: ");
1097 PULL_HEADER(accept_charset, "Accept-Charset: ");
1098 PULL_HEADER(cookie, "Cookie: ");
1101 /* ignore all other headers for now */
1102 return NT_STATUS_OK;
1107 setup the esp processor - called at task initialisation
1109 NTSTATUS http_setup_esp(struct task_server *task)
1111 struct esp_data *edata;
1113 edata = talloc_zero(task, struct esp_data);
1114 NT_STATUS_HAVE_NO_MEMORY(edata);
1116 task->private = edata;
1118 edata->tls_params = tls_initialise(edata);
1119 NT_STATUS_HAVE_NO_MEMORY(edata->tls_params);
1121 return NT_STATUS_OK;