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"
40 /* state of the esp subsystem for a specific request */
42 struct websrv_context
*web
;
43 struct EspRequest
*req
;
44 struct MprVar variables
[ESP_OBJ_MAX
];
45 struct session_data
*session
;
49 output the http headers
51 static void http_output_headers(struct websrv_context
*web
)
56 uint32_t content_length
= 0;
57 const char *response_string
= "Unknown Code";
60 const char *response_string
;
66 { 304, "Not Modified" },
67 { 400, "Bad request" },
68 { 401, "Unauthorized" },
71 { 500, "Internal Server Error" },
72 { 501, "Not implemented" }
74 for (i
=0;i
<ARRAY_SIZE(codes
);i
++) {
75 if (codes
[i
].code
== web
->output
.response_code
) {
76 response_string
= codes
[i
].response_string
;
80 if (web
->output
.headers
== NULL
) return;
81 s
= talloc_asprintf(web
, "HTTP/1.0 %u %s\r\n",
82 web
->output
.response_code
, response_string
);
83 if (s
== NULL
) return;
84 for (i
=0;web
->output
.headers
[i
];i
++) {
85 s
= talloc_asprintf_append_buffer(s
, "%s\r\n", web
->output
.headers
[i
]);
88 /* work out the content length */
89 content_length
= web
->output
.content
.length
;
90 if (web
->output
.fd
!= -1) {
92 fstat(web
->output
.fd
, &st
);
93 content_length
+= st
.st_size
;
95 s
= talloc_asprintf_append_buffer(s
, "Content-Length: %u\r\n\r\n", content_length
);
96 if (s
== NULL
) return;
98 b
= web
->output
.content
;
99 web
->output
.content
= data_blob_string_const(s
);
100 data_blob_append(web
, &web
->output
.content
, b
.data
, b
.length
);
105 return the local path for a URL
107 static const char *http_local_path(struct websrv_context
*web
,
109 const char *base_dir
)
114 /* check that the url is OK */
115 if (url
[0] != '/') return NULL
;
117 for (i
=0;url
[i
];i
++) {
118 if ((!isalnum((unsigned char)url
[i
]) && !strchr("./_-", url
[i
])) ||
119 (url
[i
] == '.' && strchr("/.", url
[i
+1]))) {
124 path
= talloc_asprintf(web
, "%s/%s", base_dir
, url
+1);
125 if (path
== NULL
) return NULL
;
127 if (directory_exist(path
)) {
128 path
= talloc_asprintf_append_buffer(path
, "/index.esp");
134 called when esp wants to read a file to support include() calls
136 static int http_readFile(EspHandle handle
,
140 const char *base_dir
)
142 struct websrv_context
*web
= talloc_get_type(handle
,
143 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_array(handle
, char, 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_readFileFromSwatDir(EspHandle handle
, char **buf
, int *len
,
176 return http_readFile(handle
, buf
, len
, path
,
177 lp_swat_directory(global_loadparm
));
183 called when esp wants to find the real path of a file
185 static int http_mapToStorage(EspHandle handle
, char *path
, int len
, const char *uri
, int flags
)
187 if (uri
== NULL
|| strlen(uri
) >= len
) return -1;
188 strncpy(path
, uri
, len
);
193 called when esp wants to output something
195 static int http_writeBlock(EspHandle handle
, const char *buf
, int size
)
197 struct websrv_context
*web
= talloc_get_type(handle
, struct websrv_context
);
198 if (!data_blob_append(web
, &web
->output
.content
, buf
, size
))
207 static void http_setHeader(EspHandle handle
, const char *value
, bool allowMultiple
)
209 struct websrv_context
*web
= talloc_get_type(handle
, struct websrv_context
);
210 char *p
= strchr(value
, ':');
212 if (p
&& !allowMultiple
&& web
->output
.headers
) {
214 for (i
=0;web
->output
.headers
[i
];i
++) {
215 if (strncmp(web
->output
.headers
[i
], value
, (p
+1)-value
) == 0) {
216 web
->output
.headers
[i
] = talloc_strdup(web
, value
);
222 web
->output
.headers
= str_list_add(web
->output
.headers
, value
);
223 talloc_steal(web
, web
->output
.headers
);
227 set a http response code
229 static void http_setResponseCode(EspHandle handle
, int code
)
231 struct websrv_context
*web
= talloc_get_type(handle
, struct websrv_context
);
232 web
->output
.response_code
= code
;
236 redirect to another web page
238 static void http_redirect(EspHandle handle
, int code
, char *url
)
240 struct websrv_context
*web
= talloc_get_type(handle
, struct websrv_context
);
241 const char *host
= web
->input
.host
;
243 /* form the full url, unless it already looks like a url */
244 if (strchr(url
, ':') == NULL
) {
246 struct socket_address
*socket_address
= socket_get_my_addr(web
->conn
->socket
, web
);
247 if (socket_address
== NULL
) goto internal_error
;
248 host
= talloc_asprintf(web
, "%s:%u",
249 socket_address
->addr
, socket_address
->port
);
251 if (host
== NULL
) goto internal_error
;
253 char *p
= strrchr(web
->input
.url
, '/');
254 if (p
== web
->input
.url
) {
255 url
= talloc_asprintf(web
, "http%s://%s/%s",
256 tls_enabled(web
->conn
->socket
)?"s":"",
259 int dirlen
= p
- web
->input
.url
;
260 url
= talloc_asprintf(web
, "http%s://%s%*.*s/%s",
261 tls_enabled(web
->conn
->socket
)?"s":"",
263 dirlen
, dirlen
, web
->input
.url
,
266 if (url
== NULL
) goto internal_error
;
270 http_setHeader(handle
, talloc_asprintf(web
, "Location: %s", url
), 0);
272 /* make sure we give a valid redirect code */
273 if (code
>= 300 && code
< 400) {
274 http_setResponseCode(handle
, code
);
276 http_setResponseCode(handle
, 302);
281 http_error(web
, 500, "Internal server error");
288 static void http_setCookie(EspHandle handle
, const char *name
, const char *value
,
289 int lifetime
, const char *path
, bool secure
)
291 struct websrv_context
*web
= talloc_get_type(handle
, struct websrv_context
);
295 buf
= talloc_asprintf(web
, "Set-Cookie: %s=%s; path=%s; Expires=%s; %s",
296 name
, value
, path
?path
:"/",
297 http_timestring(web
, time(NULL
)+lifetime
),
300 buf
= talloc_asprintf(web
, "Set-Cookie: %s=%s; path=%s; %s",
301 name
, value
, path
?path
:"/",
304 http_setHeader(handle
, "Cache-control: no-cache=\"set-cookie\"", 0);
305 http_setHeader(handle
, buf
, 0);
310 return the session id
312 static const char *http_getSessionId(EspHandle handle
)
314 struct websrv_context
*web
= talloc_get_type(handle
, struct websrv_context
);
315 return web
->session
->id
;
321 static void http_createSession(EspHandle handle
, int timeout
)
323 struct websrv_context
*web
= talloc_get_type(handle
, struct websrv_context
);
325 web
->session
->lifetime
= timeout
;
326 http_setCookie(web
, SAMBA_SESSION_KEY
, web
->session
->id
,
327 web
->session
->lifetime
, "/", 0);
334 static void http_destroySession(EspHandle handle
)
336 struct websrv_context
*web
= talloc_get_type(handle
, struct websrv_context
);
337 talloc_free(web
->session
);
343 setup for a raw http level error
345 void http_error(struct websrv_context
*web
, int code
, const char *info
)
348 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",
351 stream_terminate_connection(web
->conn
, "http_error: out of memory");
354 http_writeBlock(web
, s
, strlen(s
));
355 http_setResponseCode(web
, code
);
356 http_output_headers(web
);
357 EVENT_FD_NOT_READABLE(web
->conn
->event
.fde
);
358 EVENT_FD_WRITEABLE(web
->conn
->event
.fde
);
359 web
->output
.output_pending
= true;
363 map a unix error code to a http error
365 void http_error_unix(struct websrv_context
*web
, const char *info
)
377 info
= talloc_asprintf(web
, "%s<p>%s<p>\n", info
, strerror(errno
));
378 http_error(web
, code
, info
);
383 a simple file request
385 static void http_simple_request(struct websrv_context
*web
)
387 const char *url
= web
->input
.url
;
391 path
= http_local_path(web
, url
, lp_swat_directory(web
->task
->lp_ctx
));
392 if (path
== NULL
) goto invalid
;
395 web
->output
.fd
= open(path
, O_RDONLY
);
396 if (web
->output
.fd
== -1) {
397 DEBUG(0,("Failed to read file %s - %s\n", path
, strerror(errno
)));
398 http_error_unix(web
, path
);
402 if (fstat(web
->output
.fd
, &st
) != 0 || !S_ISREG(st
.st_mode
)) {
403 close(web
->output
.fd
);
410 http_error(web
, 400, "Malformed URL");
414 setup the standard ESP arrays
416 static void http_setup_arrays(struct esp_state
*esp
)
418 struct websrv_context
*web
= esp
->web
;
419 struct esp_data
*edata
= talloc_get_type(web
->task
->private, struct esp_data
);
420 struct EspRequest
*req
= esp
->req
;
421 struct socket_address
*socket_address
= socket_get_my_addr(web
->conn
->socket
, esp
);
422 struct socket_address
*peer_address
= socket_get_peer_addr(web
->conn
->socket
, esp
);
425 #define SETVAR(type, name, value) do { \
426 const char *v = value; \
427 if (v) espSetStringVar(req, type, name, v); \
430 SETVAR(ESP_REQUEST_OBJ
, "CONTENT_LENGTH",
431 talloc_asprintf(esp
, "%u", web
->input
.content_length
));
432 SETVAR(ESP_REQUEST_OBJ
, "QUERY_STRING", web
->input
.query_string
);
433 SETVAR(ESP_REQUEST_OBJ
, "POST_DATA",
435 web
->input
.partial
.data
,
436 web
->input
.partial
.length
));
437 SETVAR(ESP_REQUEST_OBJ
, "REQUEST_METHOD", web
->input
.post_request
?"POST":"GET");
438 SETVAR(ESP_REQUEST_OBJ
, "REQUEST_URI", web
->input
.url
);
439 p
= strrchr(web
->input
.url
, '/');
440 SETVAR(ESP_REQUEST_OBJ
, "SCRIPT_NAME", p
+1);
441 SETVAR(ESP_REQUEST_OBJ
, "SCRIPT_FILENAME", web
->input
.url
);
443 struct MprVar mpv
= mprObject("socket_address");
444 mprSetPtrChild(&mpv
, "socket_address", peer_address
);
445 espSetVar(req
, ESP_REQUEST_OBJ
, "REMOTE_SOCKET_ADDRESS", mpv
);
446 SETVAR(ESP_REQUEST_OBJ
, "REMOTE_ADDR", peer_address
->addr
);
448 p
= socket_get_peer_name(web
->conn
->socket
, esp
);
449 SETVAR(ESP_REQUEST_OBJ
, "REMOTE_HOST", p
);
450 SETVAR(ESP_REQUEST_OBJ
, "REMOTE_USER", "");
451 SETVAR(ESP_REQUEST_OBJ
, "CONTENT_TYPE", web
->input
.content_type
);
453 SETVAR(ESP_REQUEST_OBJ
, "SESSION_ID", web
->session
->id
);
455 SETVAR(ESP_REQUEST_OBJ
, "COOKIE_SUPPORT", web
->input
.cookie
?"true":"false");
457 SETVAR(ESP_HEADERS_OBJ
, "HTTP_REFERER", web
->input
.referer
);
458 SETVAR(ESP_HEADERS_OBJ
, "HOST", web
->input
.host
);
459 SETVAR(ESP_HEADERS_OBJ
, "ACCEPT_ENCODING", web
->input
.accept_encoding
);
460 SETVAR(ESP_HEADERS_OBJ
, "ACCEPT_LANGUAGE", web
->input
.accept_language
);
461 SETVAR(ESP_HEADERS_OBJ
, "ACCEPT_CHARSET", web
->input
.accept_charset
);
462 SETVAR(ESP_HEADERS_OBJ
, "COOKIE", web
->input
.cookie
);
463 SETVAR(ESP_HEADERS_OBJ
, "USER_AGENT", web
->input
.user_agent
);
465 if (socket_address
) {
466 SETVAR(ESP_SERVER_OBJ
, "SERVER_ADDR", socket_address
->addr
);
467 SETVAR(ESP_SERVER_OBJ
, "SERVER_NAME", socket_address
->addr
);
468 SETVAR(ESP_SERVER_OBJ
, "SERVER_HOST", socket_address
->addr
);
469 SETVAR(ESP_SERVER_OBJ
, "SERVER_PORT",
470 talloc_asprintf(esp
, "%u", socket_address
->port
));
473 SETVAR(ESP_SERVER_OBJ
, "DOCUMENT_ROOT", lp_swat_directory(esp
->web
->task
->lp_ctx
));
474 SETVAR(ESP_SERVER_OBJ
, "SERVER_PROTOCOL", tls_enabled(web
->conn
->socket
)?"https":"http");
475 SETVAR(ESP_SERVER_OBJ
, "SERVER_SOFTWARE", "SAMBA");
476 SETVAR(ESP_SERVER_OBJ
, "GATEWAY_INTERFACE", "CGI/1.1");
477 SETVAR(ESP_SERVER_OBJ
, "TLS_SUPPORT", tls_support(edata
->tls_params
)?"true":"false");
481 /* the esp scripting lirary generates exceptions when
482 it hits a major error. We need to catch these and
483 report a internal server error via http
485 static jmp_buf ejs_exception_buf
;
486 static const char *exception_reason
;
488 static void web_server_ejs_exception(const char *reason
)
492 ejsSetErrorMsg(0, "%s", reason
);
493 exception_reason
= ep
->error
;
495 exception_reason
= reason
;
497 DEBUG(0,("%s", exception_reason
));
498 longjmp(ejs_exception_buf
, -1);
501 static void web_server_ejs_exception(const char *reason
)
503 DEBUG(0,("%s", reason
));
509 process a esp request
511 static void esp_request(struct esp_state
*esp
, const char *url
)
513 struct websrv_context
*web
= esp
->web
;
516 char *emsg
= NULL
, *buf
;
518 if (http_readFile(web
, &buf
, &size
, url
, lp_swat_directory(esp
->web
->task
->lp_ctx
)) != 0) {
519 http_error_unix(web
, url
);
524 if (setjmp(ejs_exception_buf
) != 0) {
525 http_error(web
, 500, exception_reason
);
530 res
= espProcessRequest(esp
->req
, url
, buf
, &emsg
);
531 if (res
!= 0 && emsg
) {
532 http_writeBlock(web
, "<pre>", 5);
533 http_writeBlock(web
, emsg
, strlen(emsg
));
534 http_writeBlock(web
, "</pre>", 6);
540 perform pre-authentication on every page if /scripting/preauth.esp
541 exists. If this script generates any non-whitepace output at all,
542 then we don't run the requested URL.
544 note that the preauth is run even for static pages such as images
546 static bool http_preauth(struct esp_state
*esp
)
548 const char *path
= http_local_path(esp
->web
,
550 lp_swat_directory(esp
->web
->task
->lp_ctx
));
553 http_error(esp
->web
, 500, "Internal server error");
556 if (!file_exist(path
)) {
557 /* if the preath script is not installed then allow access */
560 esp_request(esp
, HTTP_PREAUTH_URI
);
561 for (i
=0;i
<esp
->web
->output
.content
.length
;i
++) {
562 if (!isspace(esp
->web
->output
.content
.data
[i
])) {
563 /* if the preauth has generated content, then force it
564 to be html, so that we can show the login page for
565 failed access to images */
566 http_setHeader(esp
->web
, "Content-Type: text/html", 0);
570 data_blob_free(&esp
->web
->output
.content
);
576 handling of + and % escapes in http variables
578 static const char *http_unescape(TALLOC_CTX
*mem_ctx
, const char *p
)
580 char *s0
= talloc_strdup(mem_ctx
, p
);
582 if (s
== NULL
) return NULL
;
586 if (*s
== '+') *s
= ' ';
587 if (*s
== '%' && sscanf(s
+1, "%02x", &v
) == 1) {
589 memmove(s
+1, s
+3, strlen(s
+3)+1);
598 set a form or GET variable
600 static void esp_putvar(struct esp_state
*esp
, const char *var
, const char *value
)
602 if (strcasecmp(var
, SAMBA_SESSION_KEY
) == 0) {
603 /* special case support for browsers without cookie
605 esp
->web
->input
.session_key
= talloc_strdup(esp
, value
);
607 mprSetPropertyValue(&esp
->variables
[ESP_FORM_OBJ
],
608 http_unescape(esp
, var
),
609 mprCreateStringVar(http_unescape(esp
, value
), 0));
615 parse the variables in a POST style request
617 static NTSTATUS
http_parse_post(struct esp_state
*esp
)
619 DATA_BLOB b
= esp
->web
->input
.partial
;
625 p
= memchr(b
.data
, '&', b
.length
);
629 len
= p
- (char *)b
.data
;
631 line
= talloc_strndup(esp
, (char *)b
.data
, len
);
632 NT_STATUS_HAVE_NO_MEMORY(line
);
634 p
= strchr(line
,'=');
637 esp_putvar(esp
, line
, p
+1);
652 parse the variables in a GET style request
654 static NTSTATUS
http_parse_get(struct esp_state
*esp
)
656 struct websrv_context
*web
= esp
->web
;
660 p
= strchr(web
->input
.url
, '?');
661 web
->input
.query_string
= p
+1;
664 s
= talloc_strdup(esp
, esp
->web
->input
.query_string
);
665 NT_STATUS_HAVE_NO_MEMORY(s
);
667 for (tok
=strtok_r(s
,"&;", &pp
);tok
;tok
=strtok_r(NULL
,"&;", &pp
)) {
671 esp_putvar(esp
, tok
, p
+1);
678 called when a session times out
680 static void session_timeout(struct event_context
*ev
, struct timed_event
*te
,
681 struct timeval t
, void *private)
683 struct session_data
*s
= talloc_get_type(private, struct session_data
);
690 static int session_destructor(struct session_data
*s
)
692 DLIST_REMOVE(s
->edata
->sessions
, s
);
697 setup the session for this request
699 static void http_setup_session(struct esp_state
*esp
)
701 const char *session_key
= SAMBA_SESSION_KEY
;
703 const char *cookie
= esp
->web
->input
.cookie
;
704 const char *key
= NULL
;
705 struct esp_data
*edata
= talloc_get_type(esp
->web
->task
->private, struct esp_data
);
706 struct session_data
*s
;
707 bool generated_key
= false;
709 /* look for our session key */
710 if (cookie
&& (p
= strstr(cookie
, session_key
)) &&
711 p
[strlen(session_key
)] == '=') {
712 p
+= strlen(session_key
)+1;
713 key
= talloc_strndup(esp
, p
, strcspn(p
, ";"));
716 if (key
== NULL
&& esp
->web
->input
.session_key
) {
717 key
= esp
->web
->input
.session_key
;
718 } else if (key
== NULL
) {
719 key
= generate_random_str_list(esp
, 16, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
720 generated_key
= true;
723 /* try to find this session in the existing session list */
724 for (s
=edata
->sessions
;s
;s
=s
->next
) {
725 if (strcmp(key
, s
->id
) == 0) {
731 /* create a new session */
732 s
= talloc_zero(edata
, struct session_data
);
733 s
->id
= talloc_steal(s
, key
);
737 s
->lifetime
= lp_parm_int(esp
->web
->task
->lp_ctx
, NULL
, "web", "sessiontimeout", 900);
738 DLIST_ADD(edata
->sessions
, s
);
739 talloc_set_destructor(s
, session_destructor
);
740 if (!generated_key
) {
741 mprSetPropertyValue(&esp
->variables
[ESP_REQUEST_OBJ
],
742 "SESSION_EXPIRED", mprCreateStringVar("true", 0));
746 http_setCookie(esp
->web
, session_key
, key
, s
->lifetime
, "/", 0);
749 mprCopyVar(&esp
->variables
[ESP_SESSION_OBJ
], s
->data
, MPR_DEEP_COPY
);
752 esp
->web
->session
= s
;
756 /* callbacks for esp processing */
757 static const struct Esp esp_control
= {
758 .maxScriptSize
= 60000,
759 .writeBlock
= http_writeBlock
,
760 .setHeader
= http_setHeader
,
761 .redirect
= http_redirect
,
762 .setResponseCode
= http_setResponseCode
,
763 .readFile
= http_readFileFromSwatDir
,
764 .mapToStorage
= http_mapToStorage
,
765 .setCookie
= http_setCookie
,
766 .createSession
= http_createSession
,
767 .destroySession
= http_destroySession
,
768 .getSessionId
= http_getSessionId
772 process a complete http request
774 void http_process_input(struct websrv_context
*web
)
777 struct esp_state
*esp
= NULL
;
778 struct esp_data
*edata
= talloc_get_type(web
->task
->private, struct esp_data
);
779 struct smbcalls_context
*smbcalls_ctx
;
781 void *save_mpr_ctx
= mprMemCtx();
782 void *ejs_save
= ejs_save_state();
784 const char *file_type
= NULL
;
789 enum page_type page_type
;
791 const char *extension
;
792 const char *mime_type
;
793 enum page_type page_type
;
795 {"gif", "image/gif"},
796 {"png", "image/png"},
797 {"jpg", "image/jpeg"},
798 {"txt", "text/plain"},
799 {"ico", "image/x-icon"},
801 {"esp", "text/html", true}
805 * give the smbcalls a chance to find the event context
806 * and messaging context
808 smbcalls_ctx
= talloc(web
, struct smbcalls_context
);
809 if (smbcalls_ctx
== NULL
) goto internal_error
;
810 smbcalls_ctx
->event_ctx
= web
->conn
->event
.ctx
;
811 smbcalls_ctx
->msg_ctx
= web
->conn
->msg_ctx
;
813 esp
= talloc_zero(smbcalls_ctx
, struct esp_state
);
814 if (esp
== NULL
) goto internal_error
;
820 if (espOpen(&esp_control
) != 0) goto internal_error
;
822 for (i
=0;i
<ARRAY_SIZE(esp
->variables
);i
++) {
823 esp
->variables
[i
] = mprCreateUndefinedVar();
825 esp
->variables
[ESP_HEADERS_OBJ
] = mprCreateObjVar("headers", ESP_HASH_SIZE
);
826 esp
->variables
[ESP_FORM_OBJ
] = mprCreateObjVar("form", ESP_HASH_SIZE
);
827 esp
->variables
[ESP_APPLICATION_OBJ
] = mprCreateObjVar("application", ESP_HASH_SIZE
);
828 esp
->variables
[ESP_COOKIES_OBJ
] = mprCreateObjVar("cookies", ESP_HASH_SIZE
);
829 esp
->variables
[ESP_FILES_OBJ
] = mprCreateObjVar("files", ESP_HASH_SIZE
);
830 esp
->variables
[ESP_REQUEST_OBJ
] = mprCreateObjVar("request", ESP_HASH_SIZE
);
831 esp
->variables
[ESP_SERVER_OBJ
] = mprCreateObjVar("server", ESP_HASH_SIZE
);
832 esp
->variables
[ESP_SESSION_OBJ
] = mprCreateObjVar("session", ESP_HASH_SIZE
);
834 if (edata
->application_data
) {
835 mprCopyVar(&esp
->variables
[ESP_APPLICATION_OBJ
],
836 edata
->application_data
, MPR_DEEP_COPY
);
839 smb_setup_ejs_functions(web_server_ejs_exception
);
841 if (web
->input
.url
== NULL
) {
842 http_error(web
, 400, "You must specify a GET or POST request");
843 mprSetCtx(save_mpr_ctx
);
844 ejs_restore_state(ejs_save
);
848 /* parse any form or get variables */
849 if (web
->input
.post_request
) {
850 status
= http_parse_post(esp
);
851 if (!NT_STATUS_IS_OK(status
)) {
852 http_error(web
, 400, "Malformed POST data");
853 mprSetCtx(save_mpr_ctx
);
854 ejs_restore_state(ejs_save
);
858 if (strchr(web
->input
.url
, '?')) {
859 status
= http_parse_get(esp
);
860 if (!NT_STATUS_IS_OK(status
)) {
861 http_error(web
, 400, "Malformed GET data");
862 mprSetCtx(save_mpr_ctx
);
863 ejs_restore_state(ejs_save
);
868 http_setup_session(esp
);
870 esp
->req
= espCreateRequest(web
, web
->input
.url
, esp
->variables
);
871 if (esp
->req
== NULL
) goto internal_error
;
873 p
= strrchr(web
->input
.url
, '.');
875 page_type
= page_type_esp
;
876 file_type
= "text/html";
878 for (i
=0;p
&& i
<ARRAY_SIZE(mime_types
);i
++) {
879 if (strcmp(mime_types
[i
].extension
, p
+1) == 0) {
880 page_type
= mime_types
[i
].page_type
;
881 file_type
= mime_types
[i
].mime_type
;
884 if (file_type
== NULL
) {
885 page_type
= page_type_simple
;
886 file_type
= "text/html";
889 /* setup basic headers */
890 http_setResponseCode(web
, 200);
891 http_setHeader(web
, talloc_asprintf(esp
, "Date: %s",
892 http_timestring(esp
, time(NULL
))), 0);
893 http_setHeader(web
, "Server: Samba", 0);
894 http_setHeader(web
, "Connection: close", 0);
895 http_setHeader(web
, talloc_asprintf(esp
, "Content-Type: %s", file_type
), 0);
897 http_setup_arrays(esp
);
900 * Do pre-authentication. If pre-authentication succeeds, do
901 * page-type-specific processing.
905 case page_type_simple
:
906 if (http_preauth(esp
)) {
907 http_simple_request(web
);
912 if (http_preauth(esp
)) {
913 esp_request(esp
, web
->input
.url
);
918 if (web
->conn
== NULL
) {
919 /* the connection has been terminated above us, probably
924 if (!web
->output
.output_pending
) {
925 http_output_headers(web
);
926 EVENT_FD_WRITEABLE(web
->conn
->event
.fde
);
927 web
->output
.output_pending
= true;
930 /* copy any application data to long term storage in edata */
931 talloc_free(edata
->application_data
);
932 edata
->application_data
= talloc_zero(edata
, struct MprVar
);
933 mprSetCtx(edata
->application_data
);
934 mprCopyVar(edata
->application_data
, &esp
->variables
[ESP_APPLICATION_OBJ
],
938 /* copy any session data */
940 talloc_free(web
->session
->data
);
941 web
->session
->data
= talloc_zero(web
->session
, struct MprVar
);
942 if (esp
->variables
[ESP_SESSION_OBJ
].properties
== NULL
||
943 esp
->variables
[ESP_SESSION_OBJ
].properties
[0].numItems
== 0) {
944 talloc_free(web
->session
);
947 mprSetCtx(web
->session
->data
);
948 mprCopyVar(web
->session
->data
, &esp
->variables
[ESP_SESSION_OBJ
],
950 /* setup the timeout for the session data */
952 talloc_free(web
->session
->te
);
953 web
->session
->te
= event_add_timed(web
->conn
->event
.ctx
, web
->session
,
954 timeval_current_ofs(web
->session
->lifetime
, 0),
955 session_timeout
, web
->session
);
960 mprSetCtx(save_mpr_ctx
);
961 ejs_restore_state(ejs_save
);
967 if (web
->conn
!= NULL
) {
968 http_error(web
, 500, "Internal server error");
970 mprSetCtx(save_mpr_ctx
);
971 ejs_restore_state(ejs_save
);
976 parse one line of header input
978 NTSTATUS
http_parse_header(struct websrv_context
*web
, const char *line
)
981 web
->input
.end_of_headers
= true;
982 } else if (strncasecmp(line
,"GET ", 4)==0) {
983 web
->input
.url
= talloc_strndup(web
, &line
[4], strcspn(&line
[4], " \t"));
984 } else if (strncasecmp(line
,"POST ", 5)==0) {
985 web
->input
.post_request
= true;
986 web
->input
.url
= talloc_strndup(web
, &line
[5], strcspn(&line
[5], " \t"));
987 } else if (strchr(line
, ':') == NULL
) {
988 http_error(web
, 400, "This server only accepts GET and POST requests");
989 return NT_STATUS_INVALID_PARAMETER
;
990 } else if (strncasecmp(line
,"Content-Length: ", 16)==0) {
991 web
->input
.content_length
= strtoul(&line
[16], NULL
, 10);
993 #define PULL_HEADER(v, s) do { \
994 if (strncmp(line, s, strlen(s)) == 0) { \
995 web->input.v = talloc_strdup(web, &line[strlen(s)]); \
996 return NT_STATUS_OK; \
999 PULL_HEADER(content_type
, "Content-Type: ");
1000 PULL_HEADER(user_agent
, "User-Agent: ");
1001 PULL_HEADER(referer
, "Referer: ");
1002 PULL_HEADER(host
, "Host: ");
1003 PULL_HEADER(accept_encoding
, "Accept-Encoding: ");
1004 PULL_HEADER(accept_language
, "Accept-Language: ");
1005 PULL_HEADER(accept_charset
, "Accept-Charset: ");
1006 PULL_HEADER(cookie
, "Cookie: ");
1009 /* ignore all other headers for now */
1010 return NT_STATUS_OK
;
1015 setup the esp processor - called at task initialisation
1017 NTSTATUS
http_setup_esp(struct task_server
*task
)
1019 struct esp_data
*edata
;
1021 edata
= talloc_zero(task
, struct esp_data
);
1022 NT_STATUS_HAVE_NO_MEMORY(edata
);
1024 task
->private = edata
;
1026 edata
->tls_params
= tls_initialise(edata
, task
->lp_ctx
);
1027 NT_STATUS_HAVE_NO_MEMORY(edata
->tls_params
);
1029 return NT_STATUS_OK
;