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 "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.html");
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);
200 status = data_blob_append(web, &web->output.content, buf, size);
201 if (!NT_STATUS_IS_OK(status)) return -1;
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, "HTT_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
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;