Asterisk HTTP response Content-type
[asterisk/asterisk.git] / main / http.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief http server for AMI access
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * This program implements a tiny http server
26  * and was inspired by micro-httpd by Jef Poskanzer
27  *
28  * \extref GMime http://spruce.sourceforge.net/gmime/
29  *
30  * \ref AstHTTP - AMI over the http protocol
31  */
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include <time.h>
38 #include <sys/time.h>
39 #include <sys/stat.h>
40 #include <sys/signal.h>
41 #include <fcntl.h>
42
43 #include "asterisk/paths.h"     /* use ast_config_AST_DATA_DIR */
44 #include "asterisk/cli.h"
45 #include "asterisk/tcptls.h"
46 #include "asterisk/http.h"
47 #include "asterisk/utils.h"
48 #include "asterisk/strings.h"
49 #include "asterisk/config.h"
50 #include "asterisk/stringfields.h"
51 #include "asterisk/ast_version.h"
52 #include "asterisk/manager.h"
53 #include "asterisk/_private.h"
54 #include "asterisk/astobj2.h"
55 #include "asterisk/netsock2.h"
56
57 #define MAX_PREFIX 80
58 #define DEFAULT_PORT 8088
59 #define DEFAULT_TLS_PORT 8089
60
61 /* See http.h for more information about the SSL implementation */
62 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
63 #define DO_SSL  /* comment in/out if you want to support ssl */
64 #endif
65
66 static struct ast_tls_config http_tls_cfg;
67
68 static void *httpd_helper_thread(void *arg);
69
70 /*!
71  * we have up to two accepting threads, one for http, one for https
72  */
73 static struct ast_tcptls_session_args http_desc = {
74         .accept_fd = -1,
75         .master = AST_PTHREADT_NULL,
76         .tls_cfg = NULL,
77         .poll_timeout = -1,
78         .name = "http server",
79         .accept_fn = ast_tcptls_server_root,
80         .worker_fn = httpd_helper_thread,
81 };
82
83 static struct ast_tcptls_session_args https_desc = {
84         .accept_fd = -1,
85         .master = AST_PTHREADT_NULL,
86         .tls_cfg = &http_tls_cfg,
87         .poll_timeout = -1,
88         .name = "https server",
89         .accept_fn = ast_tcptls_server_root,
90         .worker_fn = httpd_helper_thread,
91 };
92
93 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri);      /*!< list of supported handlers */
94
95 /* all valid URIs must be prepended by the string in prefix. */
96 static char prefix[MAX_PREFIX];
97 static int enablestatic;
98
99 /*! \brief Limit the kinds of files we're willing to serve up */
100 static struct {
101         const char *ext;
102         const char *mtype;
103 } mimetypes[] = {
104         { "png", "image/png" },
105         { "xml", "text/xml" },
106         { "jpg", "image/jpeg" },
107         { "js", "application/x-javascript" },
108         { "wav", "audio/x-wav" },
109         { "mp3", "audio/mpeg" },
110         { "svg", "image/svg+xml" },
111         { "svgz", "image/svg+xml" },
112         { "gif", "image/gif" },
113         { "html", "text/html" },
114         { "htm", "text/html" },
115         { "cnf", "text/plain" },
116         { "cfg", "text/plain" },
117         { "bin", "application/octet-stream" },
118         { "sbn", "application/octet-stream" },
119         { "ld", "application/octet-stream" },
120 };
121
122 struct http_uri_redirect {
123         AST_LIST_ENTRY(http_uri_redirect) entry;
124         char *dest;
125         char target[0];
126 };
127
128 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
129
130 static const struct ast_cfhttp_methods_text {
131         enum ast_http_method method;
132         const char text[];
133 } ast_http_methods_text[] = {
134         { AST_HTTP_UNKNOWN,     "UNKNOWN" },
135         { AST_HTTP_GET,         "GET" },
136         { AST_HTTP_POST,        "POST" },
137         { AST_HTTP_HEAD,        "HEAD" },
138         { AST_HTTP_PUT,         "PUT" },
139 };
140
141 const char *ast_get_http_method(enum ast_http_method method)
142 {
143         return ast_http_methods_text[method].text;
144 }
145
146 const char *ast_http_ftype2mtype(const char *ftype)
147 {
148         int x;
149
150         if (ftype) {
151                 for (x = 0; x < ARRAY_LEN(mimetypes); x++) {
152                         if (!strcasecmp(ftype, mimetypes[x].ext)) {
153                                 return mimetypes[x].mtype;
154                         }
155                 }
156         }
157         return NULL;
158 }
159
160 uint32_t ast_http_manid_from_vars(struct ast_variable *headers)
161 {
162         uint32_t mngid = 0;
163         struct ast_variable *v, *cookies;
164
165         cookies = ast_http_get_cookies(headers);
166         for (v = cookies; v; v = v->next) {
167                 if (!strcasecmp(v->name, "mansession_id")) {
168                         sscanf(v->value, "%30x", &mngid);
169                         break;
170                 }
171         }
172         if (cookies) {
173                 ast_variables_destroy(cookies);
174         }
175         return mngid;
176 }
177
178 void ast_http_prefix(char *buf, int len)
179 {
180         if (buf) {
181                 ast_copy_string(buf, prefix, len);
182         }
183 }
184
185 static int static_callback(struct ast_tcptls_session_instance *ser,
186         const struct ast_http_uri *urih, const char *uri,
187         enum ast_http_method method, struct ast_variable *get_vars,
188         struct ast_variable *headers)
189 {
190         char *path;
191         const char *ftype;
192         const char *mtype;
193         char wkspace[80];
194         struct stat st;
195         int len;
196         int fd;
197         struct ast_str *http_header;
198         struct timeval tv;
199         struct ast_tm tm;
200         char timebuf[80], etag[23];
201         struct ast_variable *v;
202         int not_modified = 0;
203
204         if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
205                 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
206                 return -1;
207         }
208
209         /* Yuck.  I'm not really sold on this, but if you don't deliver static content it makes your configuration
210            substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
211         if (!enablestatic || ast_strlen_zero(uri)) {
212                 goto out403;
213         }
214
215         /* Disallow any funny filenames at all */
216         if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0])) {
217                 goto out403;
218         }
219
220         if (strstr(uri, "/..")) {
221                 goto out403;
222         }
223
224         if ((ftype = strrchr(uri, '.'))) {
225                 ftype++;
226         }
227
228         if (!(mtype = ast_http_ftype2mtype(ftype))) {
229                 snprintf(wkspace, sizeof(wkspace), "text/%s", S_OR(ftype, "plain"));
230         }
231
232         /* Cap maximum length */
233         if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
234                 goto out403;
235         }
236
237         path = alloca(len);
238         sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
239         if (stat(path, &st)) {
240                 goto out404;
241         }
242
243         if (S_ISDIR(st.st_mode)) {
244                 goto out404;
245         }
246
247         fd = open(path, O_RDONLY);
248         if (fd < 0) {
249                 goto out403;
250         }
251
252         if (strstr(path, "/private/") && !astman_is_authed(ast_http_manid_from_vars(headers))) {
253                 goto out403;
254         }
255
256         /* make "Etag:" http header value */
257         snprintf(etag, sizeof(etag), "\"%ld\"", (long)st.st_mtime);
258
259         /* make "Last-Modified:" http header value */
260         tv.tv_sec = st.st_mtime;
261         tv.tv_usec = 0;
262         ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&tv, &tm, "GMT"));
263
264         /* check received "If-None-Match" request header and Etag value for file */
265         for (v = headers; v; v = v->next) {
266                 if (!strcasecmp(v->name, "If-None-Match")) {
267                         if (!strcasecmp(v->value, etag)) {
268                                 not_modified = 1;
269                         }
270                         break;
271                 }
272         }
273
274         if ( (http_header = ast_str_create(255)) == NULL) {
275                 return -1;
276         }
277
278         ast_str_set(&http_header, 0, "Content-type: %s\r\n"
279                 "ETag: %s\r\n"
280                 "Last-Modified: %s",
281                 mtype,
282                 etag,
283                 timebuf);
284
285         /* ast_http_send() frees http_header, so we don't need to do it before returning */
286         if (not_modified) {
287                 ast_http_send(ser, method, 304, "Not Modified", http_header, NULL, 0, 1);
288         } else {
289                 ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 1); /* static content flag is set */
290         }
291         close(fd);
292         return 0;
293
294 out404:
295         ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
296         return -1;
297
298 out403:
299         ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
300         return -1;
301 }
302
303 static int httpstatus_callback(struct ast_tcptls_session_instance *ser,
304         const struct ast_http_uri *urih, const char *uri,
305         enum ast_http_method method, struct ast_variable *get_vars,
306         struct ast_variable *headers)
307 {
308         struct ast_str *out;
309         struct ast_variable *v, *cookies = NULL;
310
311         if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
312                 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
313                 return -1;
314         }
315
316         if ( (out = ast_str_create(512)) == NULL) {
317                 return -1;
318         }
319
320         ast_str_append(&out, 0,
321                 "<title>Asterisk HTTP Status</title>\r\n"
322                 "<body bgcolor=\"#ffffff\">\r\n"
323                 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
324                 "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
325
326         ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
327         ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
328                        ast_sockaddr_stringify_addr(&http_desc.old_address));
329         ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%s</b></td></tr>\r\n",
330                        ast_sockaddr_stringify_port(&http_desc.old_address));
331         if (http_tls_cfg.enabled) {
332                 ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%s</b></td></tr>\r\n",
333                                ast_sockaddr_stringify_port(&https_desc.old_address));
334         }
335         ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
336         for (v = get_vars; v; v = v->next) {
337                 ast_str_append(&out, 0, "<tr><td><i>Submitted GET Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
338         }
339         ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
340
341         cookies = ast_http_get_cookies(headers);
342         for (v = cookies; v; v = v->next) {
343                 ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
344         }
345         ast_variables_destroy(cookies);
346
347         ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
348         ast_http_send(ser, method, 200, NULL, NULL, out, 0, 0);
349         return 0;
350 }
351
352 static struct ast_http_uri statusuri = {
353         .callback = httpstatus_callback,
354         .description = "Asterisk HTTP General Status",
355         .uri = "httpstatus",
356         .has_subtree = 0,
357         .data = NULL,
358         .key = __FILE__,
359 };
360
361 static struct ast_http_uri staticuri = {
362         .callback = static_callback,
363         .description = "Asterisk HTTP Static Delivery",
364         .uri = "static",
365         .has_subtree = 1,
366         .data = NULL,
367         .key= __FILE__,
368 };
369
370
371 /* send http/1.1 responce */
372 /* free content variable and close socket*/
373 void ast_http_send(struct ast_tcptls_session_instance *ser,
374         enum ast_http_method method, int status_code, const char *status_title,
375         struct ast_str *http_header, struct ast_str *out, const int fd,
376         unsigned int static_content)
377 {
378         struct timeval now = ast_tvnow();
379         struct ast_tm tm;
380         char timebuf[80];
381         int content_length = 0;
382
383         if (!ser || 0 == ser->f) {
384                 return;
385         }
386
387         ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&now, &tm, "GMT"));
388
389         /* calc conetnt length */
390         if (out) {
391                 content_length += strlen(ast_str_buffer(out));
392         }
393
394         if (fd) {
395                 content_length += lseek(fd, 0, SEEK_END);
396                 lseek(fd, 0, SEEK_SET);
397         }
398
399         /* send http header */
400         fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
401                 "Server: Asterisk/%s\r\n"
402                 "Date: %s\r\n"
403                 "Connection: close\r\n"
404                 "%s"
405                 "Content-Length: %d\r\n"
406                 "%s\r\n\r\n",
407                 status_code, status_title ? status_title : "OK",
408                 ast_get_version(),
409                 timebuf,
410                 static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
411                 content_length,
412                 http_header ? ast_str_buffer(http_header) : ""
413                 );
414
415         /* send content */
416         if (method != AST_HTTP_HEAD || status_code >= 400) {
417                 if (out) {
418                         fprintf(ser->f, "%s", ast_str_buffer(out));
419                 }
420
421                 if (fd) {
422                         char buf[256];
423                         int len;
424                         while ((len = read(fd, buf, sizeof(buf))) > 0) {
425                                 if (fwrite(buf, len, 1, ser->f) != 1) {
426                                         ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
427                                         break;
428                                 }
429                         }
430                 }
431         }
432
433         if (http_header) {
434                 ast_free(http_header);
435         }
436         if (out) {
437                 ast_free(out);
438         }
439
440         fclose(ser->f);
441         ser->f = 0;
442         return;
443 }
444
445 /* Send http "401 Unauthorized" responce and close socket*/
446 void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm,
447         const unsigned long nonce, const unsigned long opaque, int stale,
448         const char *text)
449 {
450         struct ast_str *http_headers = ast_str_create(128);
451         struct ast_str *out = ast_str_create(512);
452
453         if (!http_headers || !out) {
454                 ast_free(http_headers);
455                 ast_free(out);
456                 return;
457         }
458
459         ast_str_set(&http_headers, 0,
460                 "WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
461                 "Content-type: text/html",
462                 realm ? realm : "Asterisk",
463                 nonce,
464                 opaque,
465                 stale ? ", stale=true" : "");
466
467         ast_str_set(&out, 0,
468                 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
469                 "<html><head>\r\n"
470                 "<title>401 Unauthorized</title>\r\n"
471                 "</head><body>\r\n"
472                 "<h1>401 Unauthorized</h1>\r\n"
473                 "<p>%s</p>\r\n"
474                 "<hr />\r\n"
475                 "<address>Asterisk Server</address>\r\n"
476                 "</body></html>\r\n",
477                 text ? text : "");
478
479         ast_http_send(ser, AST_HTTP_UNKNOWN, 401, "Unauthorized", http_headers, out, 0, 0);
480         return;
481 }
482
483 /* send http error responce and close socket*/
484 void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code, const char *status_title, const char *text)
485 {
486         struct ast_str *http_headers = ast_str_create(40);
487         struct ast_str *out = ast_str_create(256);
488
489         if (!http_headers || !out) {
490                 ast_free(http_headers);
491                 ast_free(out);
492                 return;
493         }
494
495         ast_str_set(&http_headers, 0, "Content-type: text/html");
496
497         ast_str_set(&out, 0,
498                 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
499                 "<html><head>\r\n"
500                 "<title>%d %s</title>\r\n"
501                 "</head><body>\r\n"
502                 "<h1>%s</h1>\r\n"
503                 "<p>%s</p>\r\n"
504                 "<hr />\r\n"
505                 "<address>Asterisk Server</address>\r\n"
506                 "</body></html>\r\n",
507                         status_code, status_title, status_title, text);
508
509         ast_http_send(ser, AST_HTTP_UNKNOWN, status_code, status_title, http_headers, out, 0, 0);
510         return;
511 }
512
513 /*! \brief
514  * Link the new uri into the list.
515  *
516  * They are sorted by length of
517  * the string, not alphabetically. Duplicate entries are not replaced,
518  * but the insertion order (using <= and not just <) makes sure that
519  * more recent insertions hide older ones.
520  * On a lookup, we just scan the list and stop at the first matching entry.
521  */
522 int ast_http_uri_link(struct ast_http_uri *urih)
523 {
524         struct ast_http_uri *uri;
525         int len = strlen(urih->uri);
526
527         AST_RWLIST_WRLOCK(&uris);
528
529         if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
530                 AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
531                 AST_RWLIST_UNLOCK(&uris);
532                 return 0;
533         }
534
535         AST_RWLIST_TRAVERSE(&uris, uri, entry) {
536                 if (AST_RWLIST_NEXT(uri, entry) &&
537                         strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
538                         AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
539                         AST_RWLIST_UNLOCK(&uris);
540
541                         return 0;
542                 }
543         }
544
545         AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
546
547         AST_RWLIST_UNLOCK(&uris);
548
549         return 0;
550 }
551
552 void ast_http_uri_unlink(struct ast_http_uri *urih)
553 {
554         AST_RWLIST_WRLOCK(&uris);
555         AST_RWLIST_REMOVE(&uris, urih, entry);
556         AST_RWLIST_UNLOCK(&uris);
557 }
558
559 void ast_http_uri_unlink_all_with_key(const char *key)
560 {
561         struct ast_http_uri *urih;
562         AST_RWLIST_WRLOCK(&uris);
563         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&uris, urih, entry) {
564                 if (!strcmp(urih->key, key)) {
565                         AST_RWLIST_REMOVE_CURRENT(entry);
566                 }
567                 if (urih->dmallocd) {
568                         ast_free(urih->data);
569                 }
570                 if (urih->mallocd) {
571                         ast_free(urih);
572                 }
573         }
574         AST_RWLIST_TRAVERSE_SAFE_END;
575         AST_RWLIST_UNLOCK(&uris);
576 }
577
578 /*
579  * get post variables from client Request Entity-Body, if content type is
580  * application/x-www-form-urlencoded
581  */
582 struct ast_variable *ast_http_get_post_vars(
583         struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
584 {
585         int content_length = 0;
586         struct ast_variable *v, *post_vars=NULL, *prev = NULL;
587         char *buf, *var, *val;
588
589         for (v = headers; v; v = v->next) {
590                 if (!strcasecmp(v->name, "Content-Type")) {
591                         if (strcasecmp(v->value, "application/x-www-form-urlencoded")) {
592                                 return NULL;
593                         }
594                         break;
595                 }
596         }
597
598         for (v = headers; v; v = v->next) {
599                 if (!strcasecmp(v->name, "Content-Length")) {
600                         content_length = atoi(v->value) + 1;
601                         break;
602                 }
603         }
604
605         if (!content_length) {
606                 return NULL;
607         }
608
609         if (!(buf = alloca(content_length))) {
610                 return NULL;
611         }
612         if (!fgets(buf, content_length, ser->f)) {
613                 return NULL;
614         }
615
616         while ((val = strsep(&buf, "&"))) {
617                 var = strsep(&val, "=");
618                 if (val) {
619                         ast_uri_decode(val, ast_uri_http_legacy);
620                 } else  {
621                         val = "";
622                 }
623                 ast_uri_decode(var, ast_uri_http_legacy);
624                 if ((v = ast_variable_new(var, val, ""))) {
625                         if (post_vars) {
626                                 prev->next = v;
627                         } else {
628                                 post_vars = v;
629                         }
630                         prev = v;
631                 }
632         }
633         return post_vars;
634 }
635
636 static int handle_uri(struct ast_tcptls_session_instance *ser, char *uri,
637         enum ast_http_method method, struct ast_variable *headers)
638 {
639         char *c;
640         int res = -1;
641         char *params = uri;
642         struct ast_http_uri *urih = NULL;
643         int l;
644         struct ast_variable *get_vars = NULL, *v, *prev = NULL;
645         struct http_uri_redirect *redirect;
646
647         strsep(&params, "?");
648         /* Extract arguments from the request and store them in variables. */
649         if (params) {
650                 char *var, *val;
651
652                 while ((val = strsep(&params, "&"))) {
653                         var = strsep(&val, "=");
654                         if (val) {
655                                 ast_uri_decode(val, ast_uri_http_legacy);
656                         } else  {
657                                 val = "";
658                         }
659                         ast_uri_decode(var, ast_uri_http_legacy);
660                         if ((v = ast_variable_new(var, val, ""))) {
661                                 if (get_vars) {
662                                         prev->next = v;
663                                 } else {
664                                         get_vars = v;
665                                 }
666                                 prev = v;
667                         }
668                 }
669         }
670         ast_uri_decode(uri, ast_uri_http_legacy);
671
672         AST_RWLIST_RDLOCK(&uri_redirects);
673         AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
674                 if (!strcasecmp(uri, redirect->target)) {
675                         struct ast_str *http_header = ast_str_create(128);
676                         ast_str_set(&http_header, 0, "Location: %s\r\n", redirect->dest);
677                         ast_http_send(ser, method, 302, "Moved Temporarily", http_header, NULL, 0, 0);
678
679                         break;
680                 }
681         }
682         AST_RWLIST_UNLOCK(&uri_redirects);
683         if (redirect) {
684                 goto cleanup;
685         }
686
687         /* We want requests to start with the (optional) prefix and '/' */
688         l = strlen(prefix);
689         if (!strncasecmp(uri, prefix, l) && uri[l] == '/') {
690                 uri += l + 1;
691                 /* scan registered uris to see if we match one. */
692                 AST_RWLIST_RDLOCK(&uris);
693                 AST_RWLIST_TRAVERSE(&uris, urih, entry) {
694                         ast_debug(2, "match request [%s] with handler [%s] len %d\n", uri, urih->uri, l);
695                         l = strlen(urih->uri);
696                         c = uri + l;    /* candidate */
697                         if (strncasecmp(urih->uri, uri, l) /* no match */
698                             || (*c && *c != '/')) { /* substring */
699                                 continue;
700                         }
701                         if (*c == '/') {
702                                 c++;
703                         }
704                         if (!*c || urih->has_subtree) {
705                                 uri = c;
706                                 break;
707                         }
708                 }
709                 AST_RWLIST_UNLOCK(&uris);
710         }
711         if (urih) {
712                 res = urih->callback(ser, urih, uri, method, get_vars, headers);
713         } else {
714                 ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
715         }
716
717 cleanup:
718         ast_variables_destroy(get_vars);
719         return res;
720 }
721
722 #ifdef DO_SSL
723 #if defined(HAVE_FUNOPEN)
724 #define HOOK_T int
725 #define LEN_T int
726 #else
727 #define HOOK_T ssize_t
728 #define LEN_T size_t
729 #endif
730
731 /*!
732  * replacement read/write functions for SSL support.
733  * We use wrappers rather than SSL_read/SSL_write directly so
734  * we can put in some debugging.
735  */
736 /*static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
737 {
738         int i = SSL_read(cookie, buf, len-1);
739 #if 0
740         if (i >= 0)
741                 buf[i] = '\0';
742         ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
743 #endif
744         return i;
745 }
746
747 static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
748 {
749 #if 0
750         char *s = alloca(len+1);
751         strncpy(s, buf, len);
752         s[len] = '\0';
753         ast_verbose("ssl write size %d <%s>\n", (int)len, s);
754 #endif
755         return SSL_write(cookie, buf, len);
756 }
757
758 static int ssl_close(void *cookie)
759 {
760         close(SSL_get_fd(cookie));
761         SSL_shutdown(cookie);
762         SSL_free(cookie);
763         return 0;
764 }*/
765 #endif  /* DO_SSL */
766
767 static struct ast_variable *parse_cookies(char *cookies)
768 {
769         char *cur;
770         struct ast_variable *vars = NULL, *var;
771
772         while ((cur = strsep(&cookies, ";"))) {
773                 char *name, *val;
774
775                 name = val = cur;
776                 strsep(&val, "=");
777
778                 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
779                         continue;
780                 }
781
782                 name = ast_strip(name);
783                 val = ast_strip_quoted(val, "\"", "\"");
784
785                 if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
786                         continue;
787                 }
788
789                 if (option_debug) {
790                         ast_log(LOG_DEBUG, "mmm ... cookie!  Name: '%s'  Value: '%s'\n", name, val);
791                 }
792
793                 var = ast_variable_new(name, val, __FILE__);
794                 var->next = vars;
795                 vars = var;
796         }
797
798         return vars;
799 }
800
801 /* get cookie from Request headers */
802 struct ast_variable *ast_http_get_cookies(struct ast_variable *headers)
803 {
804         struct ast_variable *v, *cookies=NULL;
805
806         for (v = headers; v; v = v->next) {
807                 if (!strncasecmp(v->name, "Cookie", 6)) {
808                         char *tmp = ast_strdupa(v->value);
809                         if (cookies) {
810                                 ast_variables_destroy(cookies);
811                         }
812
813                         cookies = parse_cookies(tmp);
814                 }
815         }
816         return cookies;
817 }
818
819
820 static void *httpd_helper_thread(void *data)
821 {
822         char buf[4096];
823         char header_line[4096];
824         struct ast_tcptls_session_instance *ser = data;
825         struct ast_variable *headers = NULL;
826         struct ast_variable *tail = headers;
827         char *uri, *method;
828         enum ast_http_method http_method = AST_HTTP_UNKNOWN;
829
830         if (!fgets(buf, sizeof(buf), ser->f)) {
831                 goto done;
832         }
833
834         /* Get method */
835         method = ast_skip_blanks(buf);
836         uri = ast_skip_nonblanks(method);
837         if (*uri) {
838                 *uri++ = '\0';
839         }
840
841         if (!strcasecmp(method,"GET")) {
842                 http_method = AST_HTTP_GET;
843         } else if (!strcasecmp(method,"POST")) {
844                 http_method = AST_HTTP_POST;
845         } else if (!strcasecmp(method,"HEAD")) {
846                 http_method = AST_HTTP_HEAD;
847         } else if (!strcasecmp(method,"PUT")) {
848                 http_method = AST_HTTP_PUT;
849         }
850
851         uri = ast_skip_blanks(uri);     /* Skip white space */
852
853         if (*uri) {                     /* terminate at the first blank */
854                 char *c = ast_skip_nonblanks(uri);
855
856                 if (*c) {
857                         *c = '\0';
858                 }
859         }
860
861         /* process "Request Headers" lines */
862         while (fgets(header_line, sizeof(header_line), ser->f)) {
863                 char *name, *value;
864
865                 /* Trim trailing characters */
866                 ast_trim_blanks(header_line);
867                 if (ast_strlen_zero(header_line)) {
868                         break;
869                 }
870
871                 value = header_line;
872                 name = strsep(&value, ":");
873                 if (!value) {
874                         continue;
875                 }
876
877                 value = ast_skip_blanks(value);
878                 if (ast_strlen_zero(value) || ast_strlen_zero(name)) {
879                         continue;
880                 }
881
882                 ast_trim_blanks(name);
883
884                 if (!headers) {
885                         headers = ast_variable_new(name, value, __FILE__);
886                         tail = headers;
887                 } else {
888                         tail->next = ast_variable_new(name, value, __FILE__);
889                         tail = tail->next;
890                 }
891         }
892
893         if (!*uri) {
894                 ast_http_error(ser, 400, "Bad Request", "Invalid Request");
895                 return NULL;
896         }
897
898         handle_uri(ser, uri, http_method, headers);
899
900         /* Clean up all the header information pulled as well */
901         if (headers) {
902                 ast_variables_destroy(headers);
903         }
904
905 done:
906         if (ser->f) {
907                 fclose(ser->f);
908         }
909         ao2_ref(ser, -1);
910         ser = NULL;
911         return NULL;
912 }
913
914 /*!
915  * \brief Add a new URI redirect
916  * The entries in the redirect list are sorted by length, just like the list
917  * of URI handlers.
918  */
919 static void add_redirect(const char *value)
920 {
921         char *target, *dest;
922         struct http_uri_redirect *redirect, *cur;
923         unsigned int target_len;
924         unsigned int total_len;
925
926         dest = ast_strdupa(value);
927         dest = ast_skip_blanks(dest);
928         target = strsep(&dest, " ");
929         target = ast_skip_blanks(target);
930         target = strsep(&target, " "); /* trim trailing whitespace */
931
932         if (!dest) {
933                 ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
934                 return;
935         }
936
937         target_len = strlen(target) + 1;
938         total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
939
940         if (!(redirect = ast_calloc(1, total_len))) {
941                 return;
942         }
943         redirect->dest = redirect->target + target_len;
944         strcpy(redirect->target, target);
945         strcpy(redirect->dest, dest);
946
947         AST_RWLIST_WRLOCK(&uri_redirects);
948
949         target_len--; /* So we can compare directly with strlen() */
950         if (AST_RWLIST_EMPTY(&uri_redirects)
951                 || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
952                 AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
953                 AST_RWLIST_UNLOCK(&uri_redirects);
954
955                 return;
956         }
957
958         AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
959                 if (AST_RWLIST_NEXT(cur, entry)
960                         && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
961                         AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
962                         AST_RWLIST_UNLOCK(&uri_redirects);
963                         return;
964                 }
965         }
966
967         AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
968
969         AST_RWLIST_UNLOCK(&uri_redirects);
970 }
971
972 static int __ast_http_load(int reload)
973 {
974         struct ast_config *cfg;
975         struct ast_variable *v;
976         int enabled=0;
977         int newenablestatic=0;
978         char newprefix[MAX_PREFIX] = "";
979         struct http_uri_redirect *redirect;
980         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
981         uint32_t bindport = DEFAULT_PORT;
982         struct ast_sockaddr *addrs = NULL;
983         int num_addrs = 0;
984
985         cfg = ast_config_load2("http.conf", "http", config_flags);
986         if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
987                 return 0;
988         }
989
990         /* default values */
991         http_tls_cfg.enabled = 0;
992         if (http_tls_cfg.certfile) {
993                 ast_free(http_tls_cfg.certfile);
994         }
995         http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
996
997         if (http_tls_cfg.pvtfile) {
998                 ast_free(http_tls_cfg.pvtfile);
999         }
1000         http_tls_cfg.pvtfile = ast_strdup("");
1001
1002         if (http_tls_cfg.cipher) {
1003                 ast_free(http_tls_cfg.cipher);
1004         }
1005         http_tls_cfg.cipher = ast_strdup("");
1006
1007         AST_RWLIST_WRLOCK(&uri_redirects);
1008         while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
1009                 ast_free(redirect);
1010         }
1011         AST_RWLIST_UNLOCK(&uri_redirects);
1012
1013         if (cfg) {
1014                 v = ast_variable_browse(cfg, "general");
1015                 for (; v; v = v->next) {
1016
1017                         /* handle tls conf */
1018                         if (!ast_tls_read_conf(&http_tls_cfg, &https_desc, v->name, v->value)) {
1019                                 continue;
1020                         }
1021
1022                         if (!strcasecmp(v->name, "enabled")) {
1023                                 enabled = ast_true(v->value);
1024                         } else if (!strcasecmp(v->name, "enablestatic")) {
1025                                 newenablestatic = ast_true(v->value);
1026                         } else if (!strcasecmp(v->name, "bindport")) {
1027                                 if (ast_parse_arg(v->value, PARSE_UINT32 | PARSE_IN_RANGE | PARSE_DEFAULT, &bindport, DEFAULT_PORT, 0, 65535)) {
1028                                         ast_log(LOG_WARNING, "Invalid port %s specified. Using default port %"PRId32, v->value, DEFAULT_PORT);
1029                                 }
1030                         } else if (!strcasecmp(v->name, "bindaddr")) {
1031                                 if (!(num_addrs = ast_sockaddr_resolve(&addrs, v->value, 0, AST_AF_UNSPEC))) {
1032                                         ast_log(LOG_WARNING, "Invalid bind address %s\n", v->value);
1033                                 } else {
1034                                         ast_log(LOG_WARNING, "Got %d addresses\n", num_addrs);
1035                                 }
1036                         } else if (!strcasecmp(v->name, "prefix")) {
1037                                 if (!ast_strlen_zero(v->value)) {
1038                                         newprefix[0] = '/';
1039                                         ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
1040                                 } else {
1041                                         newprefix[0] = '\0';
1042                                 }
1043                         } else if (!strcasecmp(v->name, "redirect")) {
1044                                 add_redirect(v->value);
1045                         } else {
1046                                 ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
1047                         }
1048                 }
1049
1050                 ast_config_destroy(cfg);
1051         }
1052
1053         if (strcmp(prefix, newprefix)) {
1054                 ast_copy_string(prefix, newprefix, sizeof(prefix));
1055         }
1056         enablestatic = newenablestatic;
1057
1058         if (num_addrs && enabled) {
1059                 int i;
1060                 for (i = 0; i < num_addrs; ++i) {
1061                         ast_sockaddr_copy(&http_desc.local_address, &addrs[i]);
1062                         if (!ast_sockaddr_port(&http_desc.local_address)) {
1063                                 ast_sockaddr_set_port(&http_desc.local_address, bindport);
1064                         }
1065                         ast_tcptls_server_start(&http_desc);
1066                         if (http_desc.accept_fd == -1) {
1067                                 ast_log(LOG_WARNING, "Failed to start HTTP server for address %s\n", ast_sockaddr_stringify(&addrs[i]));
1068                                 ast_sockaddr_setnull(&http_desc.local_address);
1069                         } else {
1070                                 ast_verb(1, "Bound HTTP server to address %s\n", ast_sockaddr_stringify(&addrs[i]));
1071                                 break;
1072                         }
1073                 }
1074                 /* When no specific TLS bindaddr is specified, we just use
1075                  * the non-TLS bindaddress here.
1076                  */
1077                 if (ast_sockaddr_isnull(&https_desc.local_address) && http_desc.accept_fd != -1) {
1078                         ast_sockaddr_copy(&https_desc.local_address, &https_desc.local_address);
1079                         /* Of course, we can't use the same port though.
1080                          * Since no bind address was specified, we just use the
1081                          * default TLS port
1082                          */
1083                         ast_sockaddr_set_port(&https_desc.local_address, DEFAULT_TLS_PORT);
1084                 }
1085         }
1086
1087         if (enabled && !ast_sockaddr_isnull(&https_desc.local_address)) {
1088                 /* We can get here either because a TLS-specific address was specified
1089                  * or because we copied the non-TLS address here. In the case where
1090                  * we read an explicit address from the config, there may have been
1091                  * no port specified, so we'll just use the default TLS port.
1092                  */
1093                 if (!ast_sockaddr_port(&https_desc.local_address)) {
1094                         ast_sockaddr_set_port(&https_desc.local_address, DEFAULT_TLS_PORT);
1095                 }
1096                 if (ast_ssl_setup(https_desc.tls_cfg)) {
1097                         ast_tcptls_server_start(&https_desc);
1098                 }
1099         }
1100
1101         return 0;
1102 }
1103
1104 static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1105 {
1106         struct ast_http_uri *urih;
1107         struct http_uri_redirect *redirect;
1108
1109         switch (cmd) {
1110         case CLI_INIT:
1111                 e->command = "http show status";
1112                 e->usage =
1113                         "Usage: http show status\n"
1114                         "       Lists status of internal HTTP engine\n";
1115                 return NULL;
1116         case CLI_GENERATE:
1117                 return NULL;
1118         }
1119
1120         if (a->argc != 3) {
1121                 return CLI_SHOWUSAGE;
1122         }
1123         ast_cli(a->fd, "HTTP Server Status:\n");
1124         ast_cli(a->fd, "Prefix: %s\n", prefix);
1125         if (ast_sockaddr_isnull(&http_desc.old_address)) {
1126                 ast_cli(a->fd, "Server Disabled\n\n");
1127         } else {
1128                 ast_cli(a->fd, "Server Enabled and Bound to %s\n\n",
1129                         ast_sockaddr_stringify(&http_desc.old_address));
1130                 if (http_tls_cfg.enabled) {
1131                         ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s\n\n",
1132                                 ast_sockaddr_stringify(&https_desc.old_address));
1133                 }
1134         }
1135
1136         ast_cli(a->fd, "Enabled URI's:\n");
1137         AST_RWLIST_RDLOCK(&uris);
1138         if (AST_RWLIST_EMPTY(&uris)) {
1139                 ast_cli(a->fd, "None.\n");
1140         } else {
1141                 AST_RWLIST_TRAVERSE(&uris, urih, entry)
1142                         ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
1143         }
1144         AST_RWLIST_UNLOCK(&uris);
1145
1146         ast_cli(a->fd, "\nEnabled Redirects:\n");
1147         AST_RWLIST_RDLOCK(&uri_redirects);
1148         AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
1149                 ast_cli(a->fd, "  %s => %s\n", redirect->target, redirect->dest);
1150         if (AST_RWLIST_EMPTY(&uri_redirects)) {
1151                 ast_cli(a->fd, "  None.\n");
1152         }
1153         AST_RWLIST_UNLOCK(&uri_redirects);
1154
1155         return CLI_SUCCESS;
1156 }
1157
1158 int ast_http_reload(void)
1159 {
1160         return __ast_http_load(1);
1161 }
1162
1163 static struct ast_cli_entry cli_http[] = {
1164         AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
1165 };
1166
1167 int ast_http_init(void)
1168 {
1169         ast_http_uri_link(&statusuri);
1170         ast_http_uri_link(&staticuri);
1171         ast_cli_register_multiple(cli_http, ARRAY_LEN(cli_http));
1172
1173         return __ast_http_load(0);
1174 }