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