r7072: moved the esp hooks calls to the ejs level, so we can call them from
[samba.git] / source / web_server / http.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    http handling code
5
6    Copyright (C) Andrew Tridgell 2005
7    
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.
12    
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.
17    
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.
21 */
22
23 #include "includes.h"
24 #include "smbd/service_task.h"
25 #include "web_server/web_server.h"
26 #include "smbd/service_stream.h"
27 #include "lib/events/events.h"
28 #include "system/filesys.h"
29 #include "system/iconv.h"
30 #include "system/time.h"
31 #include "web_server/esp/esp.h"
32 #include "dlinklist.h"
33
34 #define SWAT_SESSION_KEY "_swat_session_"
35
36 /* state of the esp subsystem for a specific request */
37 struct esp_state {
38         struct websrv_context *web;
39         struct EspRequest *req;
40         struct MprVar variables[ESP_OBJ_MAX];
41         struct session_data *session;
42 };
43
44 /* destroy a esp session */
45 static int esp_destructor(void *ptr)
46 {
47         struct esp_state *esp = talloc_get_type(ptr, struct esp_state);
48
49         if (esp->req) {
50                 espDestroyRequest(esp->req);
51         }
52         return 0;
53 }
54
55 /*
56   output the http headers
57 */
58 static void http_output_headers(struct websrv_context *web)
59 {
60         int i;
61         char *s;
62         DATA_BLOB b;
63         uint32_t content_length = 0;
64         const char *response_string = "Unknown Code";
65         const struct {
66                 unsigned code;
67                 const char *response_string;
68         } codes[] = {
69                 { 200, "OK" },
70                 { 301, "Moved" },
71                 { 302, "Found" },
72                 { 303, "Method" },
73                 { 304, "Not Modified" },
74                 { 400, "Bad request" },
75                 { 401, "Unauthorized" },
76                 { 403, "Forbidden" },
77                 { 404, "Not Found" },
78                 { 500, "Internal Server Error" },
79                 { 501, "Not implemented" }
80         };
81         for (i=0;i<ARRAY_SIZE(codes);i++) {
82                 if (codes[i].code == web->output.response_code) {
83                         response_string = codes[i].response_string;
84                 }
85         }
86
87         if (web->output.headers == NULL) return;
88         s = talloc_asprintf(web, "HTTP/1.0 %u %s\r\n", 
89                             web->output.response_code, response_string);
90         if (s == NULL) return;
91         for (i=0;web->output.headers[i];i++) {
92                 s = talloc_asprintf_append(s, "%s\r\n", web->output.headers[i]);
93         }
94
95         /* work out the content length */
96         content_length = web->output.content.length;
97         if (web->output.fd != -1) {
98                 struct stat st;
99                 fstat(web->output.fd, &st);
100                 content_length += st.st_size;
101         }
102         s = talloc_asprintf_append(s, "Content-Length: %u\r\n\r\n", content_length);
103         if (s == NULL) return;
104
105         b = web->output.content;
106         web->output.content.data = s;
107         web->output.content.length = strlen(s);
108         data_blob_append(web, &web->output.content, b.data, b.length);
109         data_blob_free(&b);
110 }
111
112 /*
113   return the local path for a URL
114 */
115 static const char *http_local_path(struct websrv_context *web, const char *url)
116 {
117         int i;
118         char *path;
119
120         /* check that the url is OK */
121         if (url[0] != '/') return NULL;
122
123         for (i=0;url[i];i++) {
124                 if ((!isalnum(url[i]) && !strchr("./", url[i])) ||
125                     (url[i] == '.' && strchr("/.", url[i+1]))) {
126                         return NULL;
127                 }
128         }
129
130         path = talloc_asprintf(web, "%s/%s", lp_swat_directory(), url+1);
131         if (path == NULL) return NULL;
132
133         if (directory_exist(path)) {
134                 path = talloc_asprintf_append(path, "/index.esp");
135         }
136         return path;
137 }
138
139 /*
140   called when esp wants to read a file to support include() calls
141 */
142 static int http_readFile(EspHandle handle, char **buf, int *len, const char *path)
143 {
144         struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
145         int fd = -1;
146         struct stat st;
147         *buf = NULL;
148
149         path = http_local_path(web, path);
150         if (path == NULL) goto failed;
151
152         fd = open(path, O_RDONLY);
153         if (fd == -1 || fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) goto failed;
154
155         *buf = talloc_size(handle, st.st_size+1);
156         if (*buf == NULL) goto failed;
157
158         if (read(fd, *buf, st.st_size) != st.st_size) goto failed;
159
160         (*buf)[st.st_size] = 0;
161
162         close(fd);
163         *len = st.st_size;
164         return 0;
165
166 failed:
167         if (fd != -1) close(fd);
168         talloc_free(*buf);
169         *buf = NULL;
170         return -1;
171 }
172
173 /*
174   called when esp wants to find the real path of a file
175 */
176 static int http_mapToStorage(EspHandle handle, char *path, int len, const char *uri, int flags)
177 {
178         if (uri == NULL || strlen(uri) >= len) return -1;
179         strncpy(path, uri, len);
180         return 0;
181 }
182
183 /*
184   called when esp wants to output something
185 */
186 static int http_writeBlock(EspHandle handle, char *buf, int size)
187 {
188         struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
189         NTSTATUS status;
190         status = data_blob_append(web, &web->output.content, buf, size);
191         if (!NT_STATUS_IS_OK(status)) return -1;
192         return size;
193 }
194
195
196 /*
197   set a http header
198 */
199 static void http_setHeader(EspHandle handle, const char *value, bool allowMultiple)
200 {
201         struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
202         char *p = strchr(value, ':');
203
204         if (p && !allowMultiple && web->output.headers) {
205                 int i;
206                 for (i=0;web->output.headers[i];i++) {
207                         if (strncmp(web->output.headers[i], value, (p+1)-value) == 0) {
208                                 web->output.headers[i] = talloc_strdup(web, value);
209                                 return;
210                         }
211                 }
212         }
213
214         web->output.headers = str_list_add(web->output.headers, value);
215         talloc_steal(web, web->output.headers);
216 }
217
218 /*
219   set a http response code
220 */
221 static void http_setResponseCode(EspHandle handle, int code)
222 {
223         struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
224         web->output.response_code = code;
225 }
226
227 /*
228   redirect to another web page
229  */
230 static void http_redirect(EspHandle handle, int code, char *url)
231 {
232         struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
233         const char *host = web->input.host;
234         
235         /* form the full url, unless it already looks like a url */
236         if (strchr(url, ':') == NULL) {
237                 if (host == NULL) {
238                         host = talloc_asprintf(web, "%s:%u",
239                                                socket_get_my_addr(web->conn->socket, web),
240                                                socket_get_my_port(web->conn->socket));
241                 }
242                 if (host == NULL) goto internal_error;
243                 if (url[0] != '/') {
244                         char *p = strrchr(web->input.url, '/');
245                         if (p == web->input.url) {
246                                 url = talloc_asprintf(web, "http%s://%s/%s", 
247                                                       web->tls_session?"s":"",
248                                                       host, url);
249                         } else {
250                                 int dirlen = p - web->input.url;
251                                 url = talloc_asprintf(web, "http%s://%s%*.*s/%s",
252                                                       web->tls_session?"s":"",
253                                                       host, 
254                                                       dirlen, dirlen, web->input.url,
255                                                       url);
256                         }
257                         if (url == NULL) goto internal_error;
258                 }
259         }
260
261         http_setHeader(handle, talloc_asprintf(web, "Location: %s", url), 0);
262
263         /* make sure we give a valid redirect code */
264         if (code >= 300 && code < 400) {
265                 http_setResponseCode(handle, code);
266         } else {
267                 http_setResponseCode(handle, 302);
268         }
269         return;
270
271 internal_error:
272         http_error(web, 500, "Internal server error");
273 }
274
275
276 /*
277   setup a cookie
278 */
279 static void http_setCookie(EspHandle handle, const char *name, const char *value, 
280                            int lifetime, const char *path, bool secure)
281 {
282         struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
283         char *buf;
284         
285         if (lifetime > 0) {
286                 buf = talloc_asprintf(web, "Set-Cookie: %s=%s; path=%s; Expires=%s; %s",
287                                       name, value, path?path:"/", 
288                                       http_timestring(web, time(NULL)+lifetime),
289                                       secure?"secure":"");
290         } else {
291                 buf = talloc_asprintf(web, "Set-Cookie: %s=%s; path=%s; %s",
292                                       name, value, path?path:"/", 
293                                       secure?"secure":"");
294         }
295         http_setHeader(handle, "Cache-control: no-cache=\"set-cookie\"", 0);
296         http_setHeader(handle, buf, 0);
297         talloc_free(buf);
298 }
299
300 /*
301   return the session id
302 */
303 static const char *http_getSessionId(EspHandle handle)
304 {
305         struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
306         return web->session->id;
307 }
308
309 /*
310   setup a session
311 */
312 static void http_createSession(EspHandle handle, int timeout)
313 {
314         struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
315         if (web->session) {
316                 web->session->lifetime = timeout;
317                 http_setCookie(web, SWAT_SESSION_KEY, web->session->id, 
318                                web->session->lifetime, "/", 0);
319         }
320 }
321
322 /*
323   destroy a session
324 */
325 static void http_destroySession(EspHandle handle)
326 {
327         struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
328         talloc_free(web->session);
329         web->session = NULL;
330 }
331
332
333 /*
334   setup for a raw http level error
335 */
336 void http_error(struct websrv_context *web, int code, const char *info)
337 {
338         char *s;
339         s = talloc_asprintf(web,"<HTML><HEAD><TITLE>Error %u</TITLE></HEAD><BODY><H1>Error %u</H1>%s<p></BODY></HTML>\r\n\r\n", 
340                             code, code, info);
341         if (s == NULL) {
342                 stream_terminate_connection(web->conn, "http_error: out of memory");
343                 return;
344         }
345         http_writeBlock(web, s, strlen(s));
346         http_setResponseCode(web, code);
347         http_output_headers(web);
348         EVENT_FD_NOT_READABLE(web->conn->event.fde);
349         EVENT_FD_WRITEABLE(web->conn->event.fde);
350         web->output.output_pending = True;
351 }
352
353 /*
354   map a unix error code to a http error
355 */
356 void http_error_unix(struct websrv_context *web, const char *info)
357 {
358         int code = 500;
359         switch (errno) {
360         case ENOENT:
361         case EISDIR:
362                 code = 404;
363                 break;
364         case EACCES:
365                 code = 403;
366                 break;
367         }
368         info = talloc_asprintf(web, "%s<p>%s<p>\n", info, strerror(errno));
369         http_error(web, code, info);
370 }
371
372
373 /*
374   a simple file request
375 */
376 static void http_simple_request(struct websrv_context *web)
377 {
378         const char *url = web->input.url;
379         const char *path;
380         struct stat st;
381
382         path = http_local_path(web, url);
383         if (path == NULL) goto invalid;
384
385         /* looks ok */
386         web->output.fd = open(path, O_RDONLY);
387         if (web->output.fd == -1) {
388                 http_error_unix(web, path);
389                 return;
390         }
391
392         if (fstat(web->output.fd, &st) != 0 || !S_ISREG(st.st_mode)) {
393                 close(web->output.fd);
394                 goto invalid;
395         }
396
397         http_output_headers(web);
398         EVENT_FD_WRITEABLE(web->conn->event.fde);
399         web->output.output_pending = True;
400         return;
401
402 invalid:
403         http_error(web, 400, "Malformed URL");
404 }
405
406 /*
407   setup the standard ESP arrays
408 */
409 static void http_setup_arrays(struct esp_state *esp)
410 {
411         struct websrv_context *web = esp->web;
412         struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
413         struct EspRequest *req = esp->req;
414         char *p;
415
416 #define SETVAR(type, name, value) do { \
417                 const char *v = value; \
418                 if (v) espSetStringVar(req, type, name, v); \
419 } while (0)
420
421         SETVAR(ESP_REQUEST_OBJ, "CONTENT_LENGTH", 
422                talloc_asprintf(esp, "%u", web->input.content_length));
423         SETVAR(ESP_REQUEST_OBJ, "QUERY_STRING", web->input.query_string);
424         SETVAR(ESP_REQUEST_OBJ, "REQUEST_METHOD", web->input.post_request?"POST":"GET");
425         SETVAR(ESP_REQUEST_OBJ, "REQUEST_URI", web->input.url);
426         p = strrchr(web->input.url, '/');
427         SETVAR(ESP_REQUEST_OBJ, "SCRIPT_NAME", p+1);
428         SETVAR(ESP_REQUEST_OBJ, "SCRIPT_FILENAME", web->input.url);
429         p = socket_get_peer_name(web->conn->socket, esp);
430         SETVAR(ESP_REQUEST_OBJ, "REMOTE_HOST", p);
431         SETVAR(ESP_REQUEST_OBJ, "REMOTE_ADDR", p);
432         SETVAR(ESP_REQUEST_OBJ, "REMOTE_USER", "");
433         SETVAR(ESP_REQUEST_OBJ, "CONTENT_TYPE", web->input.content_type);
434         if (web->session) {
435                 SETVAR(ESP_REQUEST_OBJ, "SESSION_ID", web->session->id);
436         }
437
438         SETVAR(ESP_HEADERS_OBJ, "HTT_REFERER", web->input.referer);
439         SETVAR(ESP_HEADERS_OBJ, "HOST", web->input.host);
440         SETVAR(ESP_HEADERS_OBJ, "ACCEPT_ENCODING", web->input.accept_encoding);
441         SETVAR(ESP_HEADERS_OBJ, "ACCEPT_LANGUAGE", web->input.accept_language);
442         SETVAR(ESP_HEADERS_OBJ, "ACCEPT_CHARSET", web->input.accept_charset);
443         SETVAR(ESP_HEADERS_OBJ, "COOKIE", web->input.cookie);
444         SETVAR(ESP_HEADERS_OBJ, "USER_AGENT", web->input.user_agent);
445
446         SETVAR(ESP_SERVER_OBJ, "SERVER_ADDR", socket_get_my_addr(web->conn->socket, esp));
447         SETVAR(ESP_SERVER_OBJ, "SERVER_NAME", socket_get_my_addr(web->conn->socket, esp));
448         SETVAR(ESP_SERVER_OBJ, "SERVER_HOST", socket_get_my_addr(web->conn->socket, esp));
449         SETVAR(ESP_SERVER_OBJ, "DOCUMENT_ROOT", lp_swat_directory());
450         SETVAR(ESP_SERVER_OBJ, "SERVER_PORT", 
451                talloc_asprintf(esp, "%u", socket_get_my_port(web->conn->socket)));
452         SETVAR(ESP_SERVER_OBJ, "SERVER_PROTOCOL", web->tls_session?"https":"http");
453         SETVAR(ESP_SERVER_OBJ, "SERVER_SOFTWARE", "SWAT");
454         SETVAR(ESP_SERVER_OBJ, "GATEWAY_INTERFACE", "CGI/1.1");
455         SETVAR(ESP_SERVER_OBJ, "TLS_SUPPORT", edata->tls_data?"True":"False");
456 }
457
458 #if HAVE_SETJMP_H
459 /* the esp scripting lirary generates exceptions when
460    it hits a major error. We need to catch these and
461    report a internal server error via http
462 */
463 #include <setjmp.h>
464 static jmp_buf ejs_exception_buf;
465 static const char *exception_reason;
466
467 void ejs_exception(const char *reason)
468 {
469         exception_reason = reason;
470         DEBUG(0,("%s", reason));
471         longjmp(ejs_exception_buf, -1);
472 }
473 #else
474 void ejs_exception(const char *reason)
475 {
476         DEBUG(0,("%s", reason));
477         smb_panic(reason);
478 }
479 #endif
480
481 /*
482   process a esp request
483 */
484 static void esp_request(struct esp_state *esp)
485 {
486         struct websrv_context *web = esp->web;
487         const char *url = web->input.url;
488         size_t size;
489         int res;
490         char *emsg = NULL, *buf;
491
492         http_setup_arrays(esp);
493
494         if (http_readFile(web, &buf, &size, url) != 0) {
495                 http_error_unix(web, url);
496                 return;
497         }
498
499 #if HAVE_SETJMP_H
500         if (setjmp(ejs_exception_buf) != 0) {
501                 http_error(web, 500, exception_reason);
502                 return;
503         }
504 #endif
505         res = espProcessRequest(esp->req, url, buf, &emsg);
506         if (res != 0 && emsg) {
507                 http_writeBlock(web, emsg, strlen(emsg));
508         }
509         talloc_free(buf);
510         http_output_headers(web);
511         EVENT_FD_WRITEABLE(web->conn->event.fde);
512         web->output.output_pending = True;
513 }
514
515
516 /* 
517    handling of + and % escapes in http variables 
518 */
519 static const char *http_unescape(TALLOC_CTX *mem_ctx, const char *p)
520 {
521         char *s0 = talloc_strdup(mem_ctx, p);
522         char *s = s0;
523         if (s == NULL) return NULL;
524
525         while (*s) {
526                 unsigned v;
527                 if (*s == '+') *s = ' ';
528                 if (*s == '%' && sscanf(s+1, "%02x", &v) == 1) {
529                         *s = (char)v;
530                         memmove(s+1, s+3, strlen(s+3)+1);
531                 }
532                 s++;
533         }
534
535         return s0;
536 }
537
538 /*
539   set a form or GET variable
540 */
541 static void esp_putvar(struct esp_state *esp, const char *var, const char *value)
542 {
543         espSetStringVar(esp->req, ESP_FORM_OBJ, 
544                         http_unescape(esp, var),
545                         http_unescape(esp, value));
546 }
547
548
549 /*
550   parse the variables in a POST style request
551 */
552 static NTSTATUS http_parse_post(struct esp_state *esp)
553 {
554         DATA_BLOB b = esp->web->input.partial;
555
556         while (b.length) {
557                 char *p, *line;
558                 size_t len;
559
560                 p = memchr(b.data, '&', b.length);
561                 if (p == NULL) {
562                         len = b.length;
563                 } else {
564                         len = p - (char *)b.data;
565                 }
566                 line = talloc_strndup(esp, b.data, len);
567                 NT_STATUS_HAVE_NO_MEMORY(line);
568                                      
569                 p = strchr(line,'=');
570                 if (p) {
571                         *p = 0;
572                         esp_putvar(esp, line, p+1);
573                 }
574                 talloc_free(line);
575                 b.length -= len;
576                 b.data += len;
577                 if (b.length > 0) {
578                         b.length--;
579                         b.data++;
580                 }
581         }
582
583         return NT_STATUS_OK;
584 }
585
586 /*
587   parse the variables in a GET style request
588 */
589 static NTSTATUS http_parse_get(struct esp_state *esp)
590 {
591         struct websrv_context *web = esp->web;
592         char *p, *s, *tok;
593         char *pp;
594
595         p = strchr(web->input.url, '?');
596         web->input.query_string = p+1;
597         *p = 0;
598
599         s = talloc_strdup(esp, esp->web->input.query_string);
600         NT_STATUS_HAVE_NO_MEMORY(s);
601
602         for (tok=strtok_r(s,"&;", &pp);tok;tok=strtok_r(NULL,"&;", &pp)) {
603                 p = strchr(tok,'=');
604                 if (p) {
605                         *p = 0;
606                         esp_putvar(esp, tok, p+1);
607                 }
608         }
609         return NT_STATUS_OK;
610 }
611
612 /*
613   called when a session times out
614 */
615 static void session_timeout(struct event_context *ev, struct timed_event *te, 
616                             struct timeval t, void *private)
617 {
618         struct session_data *s = talloc_get_type(private, struct session_data);
619         talloc_free(s);
620 }
621
622 /*
623   destroy a session
624  */
625 static int session_destructor(void *ptr)
626 {
627         struct session_data *s = talloc_get_type(ptr, struct session_data);
628         DLIST_REMOVE(s->edata->sessions, s);
629         return 0;
630 }
631
632 /*
633   setup the session for this request
634 */
635 static void http_setup_session(struct esp_state *esp)
636 {
637         const char *session_key = SWAT_SESSION_KEY;
638         char *p;
639         const char *cookie = esp->web->input.cookie;
640         const char *key = NULL;
641         struct esp_data *edata = talloc_get_type(esp->web->task->private, struct esp_data);
642         struct session_data *s;
643
644         /* look for our session key */
645         if (cookie && (p = strstr(cookie, session_key)) && 
646             p[strlen(session_key)] == '=') {
647                 p += strlen(session_key)+1;
648                 key = talloc_strndup(esp, p, strcspn(p, ";"));
649         }
650
651         if (key == NULL) {
652                 key = generate_random_str_list(esp, 64, "0123456789");
653         }
654
655         /* try to find this session in the existing session list */
656         for (s=edata->sessions;s;s=s->next) {
657                 if (strcmp(key, s->id) == 0) break;
658         }
659
660         if (s == NULL) {
661                 /* create a new session */
662                 s = talloc_zero(edata, struct session_data);
663                 s->id = talloc_steal(s, key);
664                 s->data = NULL;
665                 s->te = NULL;
666                 s->edata = edata;
667                 s->lifetime = lp_parm_int(-1, "web", "sessiontimeout", 300);
668                 DLIST_ADD(edata->sessions, s);
669                 talloc_set_destructor(s, session_destructor);
670         }
671
672         http_setCookie(esp->web, session_key, key, s->lifetime, "/", 0);
673
674         if (s->data) {
675                 mprCopyVar(&esp->variables[ESP_SESSION_OBJ], s->data, MPR_DEEP_COPY);
676         }
677
678         esp->web->session = s;
679 }
680
681
682 /* callbacks for esp processing */
683 static const struct Esp esp_control = {
684         .maxScriptSize   = 60000,
685         .writeBlock      = http_writeBlock,
686         .setHeader       = http_setHeader,
687         .redirect        = http_redirect,
688         .setResponseCode = http_setResponseCode,
689         .readFile        = http_readFile,
690         .mapToStorage    = http_mapToStorage,
691         .setCookie       = http_setCookie,
692         .createSession   = http_createSession,
693         .destroySession  = http_destroySession,
694         .getSessionId    = http_getSessionId
695 };
696
697 /*
698   process a complete http request
699 */
700 void http_process_input(struct websrv_context *web)
701 {
702         NTSTATUS status;
703         struct esp_state *esp;
704         struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
705         char *p;
706         int i;
707         const char *file_type = NULL;
708         BOOL esp_enable = False;
709         const struct {
710                 const char *extension;
711                 const char *mime_type;
712                 BOOL esp_enable;
713         } mime_types[] = {
714                 {"gif",  "image/gif"},
715                 {"png",  "image/png"},
716                 {"jpg",  "image/jpeg"},
717                 {"txt",  "text/plain"},
718                 {"ico",  "image/x-icon"},
719                 {"esp",  "text/html", True}
720         };
721
722         esp = talloc_zero(web, struct esp_state);
723         if (esp == NULL) goto internal_error;
724
725         esp->web = web;
726
727         mprSetCtx(esp);
728
729         if (espOpen(&esp_control) != 0) goto internal_error;
730
731         for (i=0;i<ARRAY_SIZE(esp->variables);i++) {
732                 esp->variables[i] = mprCreateUndefinedVar();
733         }
734         esp->variables[ESP_HEADERS_OBJ]     = mprCreateObjVar("headers", ESP_HASH_SIZE);
735         esp->variables[ESP_FORM_OBJ]        = mprCreateObjVar("form", ESP_HASH_SIZE);
736         esp->variables[ESP_APPLICATION_OBJ] = mprCreateObjVar("application", ESP_HASH_SIZE);
737         esp->variables[ESP_COOKIES_OBJ]     = mprCreateObjVar("cookies", ESP_HASH_SIZE);
738         esp->variables[ESP_FILES_OBJ]       = mprCreateObjVar("files", ESP_HASH_SIZE);
739         esp->variables[ESP_REQUEST_OBJ]     = mprCreateObjVar("request", ESP_HASH_SIZE);
740         esp->variables[ESP_SERVER_OBJ]      = mprCreateObjVar("server", ESP_HASH_SIZE);
741         esp->variables[ESP_SESSION_OBJ]     = mprCreateObjVar("session", ESP_HASH_SIZE);
742
743         if (edata->application_data) {
744                 mprCopyVar(&esp->variables[ESP_APPLICATION_OBJ], 
745                            edata->application_data, MPR_DEEP_COPY);
746         }
747
748         http_setup_session(esp);
749
750         talloc_set_destructor(esp, esp_destructor);
751
752         smb_setup_ejs_functions();
753
754         esp->req = espCreateRequest(web, web->input.url, esp->variables);
755         if (esp->req == NULL) goto internal_error;
756
757         if (web->input.url == NULL) {
758                 http_error(web, 400, "You must specify a GET or POST request");
759                 return;
760         }
761         
762         /* parse any form or get variables */
763         if (web->input.post_request) {
764                 status = http_parse_post(esp);
765                 if (!NT_STATUS_IS_OK(status)) {
766                         http_error(web, 400, "Malformed POST data");
767                         return;
768                 }
769         } else if (strchr(web->input.url, '?')) {
770                 status = http_parse_get(esp);
771                 if (!NT_STATUS_IS_OK(status)) {
772                         http_error(web, 400, "Malformed GET data");
773                         return;
774                 }
775         }
776
777         /* work out the mime type */
778         p = strrchr(web->input.url, '.');
779         if (p == NULL) {
780                 esp_enable = True;
781         }
782         for (i=0;p && i<ARRAY_SIZE(mime_types);i++) {
783                 if (strcmp(mime_types[i].extension, p+1) == 0) {
784                         file_type = mime_types[i].mime_type;
785                         esp_enable = mime_types[i].esp_enable;
786                 }
787         }
788         if (file_type == NULL) {
789                 file_type = "text/html";
790         }
791
792         /* setup basic headers */
793         http_setResponseCode(web, 200);
794         http_setHeader(web, talloc_asprintf(esp, "Date: %s", 
795                                             http_timestring(esp, time(NULL))), 0);
796         http_setHeader(web, "Server: Samba", 0);
797         http_setHeader(web, "Connection: close", 0);
798         http_setHeader(web, talloc_asprintf(esp, "Content-Type: %s", file_type), 0);
799
800         if (esp_enable) {
801                 esp_request(esp);
802         } else {
803                 http_simple_request(web);
804         }
805
806         /* copy any application data to long term storage in edata */
807         talloc_free(edata->application_data);
808         edata->application_data = talloc_zero(edata, struct MprVar);
809         mprSetCtx(edata->application_data);
810         mprCopyVar(edata->application_data, &esp->variables[ESP_APPLICATION_OBJ], MPR_DEEP_COPY);
811
812         /* copy any session data */
813         if (web->session) {
814                 talloc_free(web->session->data);
815                 web->session->data = talloc_zero(web->session, struct MprVar);
816                 mprSetCtx(web->session->data);
817                 if (esp->variables[ESP_SESSION_OBJ].properties == NULL ||
818                     esp->variables[ESP_SESSION_OBJ].properties[0].numItems == 0) {
819                         talloc_free(web->session);
820                         web->session = NULL;
821                 } else {
822                         mprCopyVar(web->session->data, &esp->variables[ESP_SESSION_OBJ], 
823                                    MPR_DEEP_COPY);
824                         /* setup the timeout for the session data */
825                         talloc_free(web->session->te);
826                         web->session->te = event_add_timed(web->conn->event.ctx, web->session, 
827                                                            timeval_current_ofs(web->session->lifetime, 0), 
828                                                            session_timeout, web->session);
829                 }
830         }
831
832         talloc_free(esp);
833         return;
834         
835 internal_error:
836         talloc_free(esp);
837         http_error(web, 500, "Internal server error");
838 }
839
840
841 /*
842   parse one line of header input
843 */
844 NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
845 {
846         if (line[0] == 0) {
847                 web->input.end_of_headers = True;
848         } else if (strncasecmp(line,"GET ", 4)==0) {
849                 web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
850         } else if (strncasecmp(line,"POST ", 5)==0) {
851                 web->input.post_request = True;
852                 web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
853         } else if (strchr(line, ':') == NULL) {
854                 http_error(web, 400, "This server only accepts GET and POST requests");
855                 return NT_STATUS_INVALID_PARAMETER;
856         } else if (strncasecmp(line,"Content-Length: ", 16)==0) {
857                 web->input.content_length = strtoul(&line[16], NULL, 10);
858         } else {
859 #define PULL_HEADER(v, s) do { \
860         if (strncmp(line, s, strlen(s)) == 0) { \
861                 web->input.v = talloc_strdup(web, &line[strlen(s)]); \
862                 return NT_STATUS_OK; \
863         } \
864 } while (0)
865                 PULL_HEADER(content_type, "Content-Type: ");
866                 PULL_HEADER(user_agent, "User-Agent: ");
867                 PULL_HEADER(referer, "Referer: ");
868                 PULL_HEADER(host, "Host: ");
869                 PULL_HEADER(accept_encoding, "Accept-Encoding: ");
870                 PULL_HEADER(accept_language, "Accept-Language: ");
871                 PULL_HEADER(accept_charset, "Accept-Charset: ");
872                 PULL_HEADER(cookie, "Cookie: ");
873         }
874
875         /* ignore all other headers for now */
876         return NT_STATUS_OK;
877 }
878
879
880 /*
881   setup the esp processor - called at task initialisation
882 */
883 NTSTATUS http_setup_esp(struct task_server *task)
884 {
885         struct esp_data *edata;
886
887         edata = talloc_zero(task, struct esp_data);
888         NT_STATUS_HAVE_NO_MEMORY(edata);
889
890         task->private = edata;
891
892         return NT_STATUS_OK;
893 }