Merged revisions 47051 via svnmerge from
[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  * This program implements a tiny http server supporting the "get" method
25  * only and was inspired by micro-httpd by Jef Poskanzer 
26  * 
27  * \ref AstHTTP - AMI over the http protocol
28  */
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include <sys/types.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <time.h>
39 #include <string.h>
40 #include <netinet/in.h>
41 #include <sys/time.h>
42 #include <sys/socket.h>
43 #include <sys/stat.h>
44 #include <sys/signal.h>
45 #include <arpa/inet.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <pthread.h>
49
50 #include "asterisk/cli.h"
51 #include "asterisk/http.h"
52 #include "asterisk/utils.h"
53 #include "asterisk/strings.h"
54 #include "asterisk/options.h"
55 #include "asterisk/config.h"
56
57 #define MAX_PREFIX 80
58 #define DEFAULT_PREFIX "/asterisk"
59
60 /*!
61  * In order to have SSL support, we need the openssl libraries.
62  * Still we can decide whether or not to use them by commenting
63  * in or out the DO_SSL macro.
64  * We declare most of ssl support variables unconditionally,
65  * because their number is small and this simplifies the code.
66  */
67 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
68 #define DO_SSL  /* comment in/out if you want to support ssl */
69 #endif
70
71 #ifdef DO_SSL
72 #include <openssl/ssl.h>
73 #include <openssl/err.h>
74 SSL_CTX* ssl_ctx;
75 #endif /* DO_SSL */
76
77 /* SSL support */
78 #define AST_CERTFILE "asterisk.pem"
79 static int do_ssl;
80 static char *certfile;
81 static char *cipher;
82
83 /*!
84  * describes a server instance
85  */
86 struct server_instance {
87         FILE *f;        /* fopen/funopen result */
88         int fd;         /* the socket returned by accept() */
89         int is_ssl;     /* is this an ssl session ? */
90 #ifdef DO_SSL
91         SSL *ssl;       /* ssl state */
92 #endif
93         struct sockaddr_in requestor;
94         ast_http_callback callback;
95 };
96
97 /*!
98  * arguments for the accepting thread
99  */
100 struct server_args {
101         struct sockaddr_in sin;
102         struct sockaddr_in oldsin;
103         int is_ssl;             /* is this an SSL accept ? */
104         int accept_fd;
105         pthread_t master;
106 };
107
108 /*!
109  * we have up to two accepting threads, one for http, one for https
110  */
111 static struct server_args http_desc = {
112         .accept_fd = -1,
113         .master = AST_PTHREADT_NULL,
114         .is_ssl = 0,
115 };
116
117 static struct server_args https_desc = {
118         .accept_fd = -1,
119         .master = AST_PTHREADT_NULL,
120         .is_ssl = 1,
121 };
122
123 static struct ast_http_uri *uris;       /*!< list of supported handlers */
124
125 /* all valid URIs must be prepended by the string in prefix. */
126 static char prefix[MAX_PREFIX];
127 static int enablestatic=0;
128
129 /*! \brief Limit the kinds of files we're willing to serve up */
130 static struct {
131         char *ext;
132         char *mtype;
133 } mimetypes[] = {
134         { "png", "image/png" },
135         { "jpg", "image/jpeg" },
136         { "js", "application/x-javascript" },
137         { "wav", "audio/x-wav" },
138         { "mp3", "audio/mpeg" },
139 };
140
141 static char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
142 {
143         int x;
144         if (ftype) {
145                 for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) {
146                         if (!strcasecmp(ftype, mimetypes[x].ext))
147                                 return mimetypes[x].mtype;
148                 }
149         }
150         snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain");
151         return wkspace;
152 }
153
154 /* like ast_uri_decode, but replace '+' with ' ' */
155 static char *uri_decode(char *buf)
156 {
157         char *c;
158         ast_uri_decode(buf);
159         for (c = buf; *c; c++) {
160                 if (*c == '+')
161                         *c = ' ';
162         }
163         return buf;
164 }
165 static char *static_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
166 {
167         char result[4096];
168         char *c=result;
169         char *path;
170         char *ftype, *mtype;
171         char wkspace[80];
172         struct stat st;
173         int len;
174         int fd;
175         void *blob;
176
177         /* Yuck.  I'm not really sold on this, but if you don't deliver static content it makes your configuration 
178            substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
179         if (!enablestatic || ast_strlen_zero(uri))
180                 goto out403;
181         /* Disallow any funny filenames at all */
182         if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0]))
183                 goto out403;
184         if (strstr(uri, "/.."))
185                 goto out403;
186                 
187         if ((ftype = strrchr(uri, '.')))
188                 ftype++;
189         mtype=ftype2mtype(ftype, wkspace, sizeof(wkspace));
190         
191         /* Cap maximum length */
192         len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5;
193         if (len > 1024)
194                 goto out403;
195                 
196         path = alloca(len);
197         sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
198         if (stat(path, &st))
199                 goto out404;
200         if (S_ISDIR(st.st_mode))
201                 goto out404;
202         fd = open(path, O_RDONLY);
203         if (fd < 0)
204                 goto out403;
205         
206         len = st.st_size + strlen(mtype) + 40;
207         
208         blob = malloc(len);
209         if (blob) {
210                 c = blob;
211                 sprintf(c, "Content-type: %s\r\n\r\n", mtype);
212                 c += strlen(c);
213                 *contentlength = read(fd, c, st.st_size);
214                 if (*contentlength < 0) {
215                         close(fd);
216                         free(blob);
217                         goto out403;
218                 }
219         }
220         close(fd);
221         return blob;
222
223 out404:
224         *status = 404;
225         *title = strdup("Not Found");
226         return ast_http_error(404, "Not Found", NULL, "Nothing to see here.  Move along.");
227
228 out403:
229         *status = 403;
230         *title = strdup("Access Denied");
231         return ast_http_error(403, "Access Denied", NULL, "Sorry, I cannot let you do that, Dave.");
232 }
233
234
235 static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
236 {
237         char result[4096];
238         size_t reslen = sizeof(result);
239         char *c=result;
240         struct ast_variable *v;
241
242         ast_build_string(&c, &reslen,
243                 "\r\n"
244                 "<title>Asterisk HTTP Status</title>\r\n"
245                 "<body bgcolor=\"#ffffff\">\r\n"
246                 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
247                 "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
248
249         ast_build_string(&c, &reslen, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
250         ast_build_string(&c, &reslen, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
251                         ast_inet_ntoa(http_desc.oldsin.sin_addr));
252         ast_build_string(&c, &reslen, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
253                         ntohs(http_desc.oldsin.sin_port));
254         if (do_ssl)
255                 ast_build_string(&c, &reslen, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
256                         ntohs(https_desc.oldsin.sin_port));
257         ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
258         v = vars;
259         while(v) {
260                 if (strncasecmp(v->name, "cookie_", 7))
261                         ast_build_string(&c, &reslen, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
262                 v = v->next;
263         }
264         ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
265         v = vars;
266         while(v) {
267                 if (!strncasecmp(v->name, "cookie_", 7))
268                         ast_build_string(&c, &reslen, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
269                 v = v->next;
270         }
271         ast_build_string(&c, &reslen, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
272         return strdup(result);
273 }
274
275 static struct ast_http_uri statusuri = {
276         .callback = httpstatus_callback,
277         .description = "Asterisk HTTP General Status",
278         .uri = "httpstatus",
279         .has_subtree = 0,
280 };
281         
282 static struct ast_http_uri staticuri = {
283         .callback = static_callback,
284         .description = "Asterisk HTTP Static Delivery",
285         .uri = "static",
286         .has_subtree = 1,
287 };
288         
289 char *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
290 {
291         char *c = NULL;
292         asprintf(&c,
293                 "Content-type: text/html\r\n"
294                 "%s"
295                 "\r\n"
296                 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
297                 "<html><head>\r\n"
298                 "<title>%d %s</title>\r\n"
299                 "</head><body>\r\n"
300                 "<h1>%s</h1>\r\n"
301                 "<p>%s</p>\r\n"
302                 "<hr />\r\n"
303                 "<address>Asterisk Server</address>\r\n"
304                 "</body></html>\r\n",
305                         (extra_header ? extra_header : ""), status, title, title, text);
306         return c;
307 }
308
309 /*! \brief 
310  * Link the new uri into the list. They are sorted by length of
311  * the string, not alphabetically. Duplicate entries are not replaced,
312  * but the insertion order (using <= and not just <) makes sure that
313  * more recent insertions hide older ones.
314  * On a lookup, we just scan the list and stop at the first matching entry.
315  */
316 int ast_http_uri_link(struct ast_http_uri *urih)
317 {
318         struct ast_http_uri *prev=uris;
319         int len = strlen(urih->uri);
320
321         if (!uris || strlen(uris->uri) <= len ) {
322                 urih->next = uris;
323                 uris = urih;
324         } else {
325                 while (prev->next && strlen(prev->next->uri) > len)
326                         prev = prev->next;
327                 /* Insert it here */
328                 urih->next = prev->next;
329                 prev->next = urih;
330         }
331         return 0;
332 }       
333
334 void ast_http_uri_unlink(struct ast_http_uri *urih)
335 {
336         struct ast_http_uri *prev = uris;
337         if (!uris)
338                 return;
339         if (uris == urih) {
340                 uris = uris->next;
341         }
342         while(prev->next) {
343                 if (prev->next == urih) {
344                         prev->next = urih->next;
345                         break;
346                 }
347                 prev = prev->next;
348         }
349 }
350
351 static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies)
352 {
353         char *c;
354         char *params = uri;
355         struct ast_http_uri *urih=NULL;
356         int l;
357         struct ast_variable *vars=NULL, *v, *prev = NULL;
358
359         strsep(&params, "?");
360         /* Extract arguments from the request and store them in variables. */
361         if (params) {
362                 char *var, *val;
363
364                 while ((val = strsep(&params, "&"))) {
365                         var = strsep(&val, "=");
366                         if (val)
367                                 uri_decode(val);
368                         else 
369                                 val = "";
370                         ast_uri_decode(var);
371                         if ((v = ast_variable_new(var, val))) {
372                                 if (vars)
373                                         prev->next = v;
374                                 else
375                                         vars = v;
376                                 prev = v;
377                         }
378                 }
379         }
380         /*
381          * Append the cookies to the variables (the only reason to have them
382          * at the end is to avoid another pass of the cookies list to find
383          * the tail).
384          */
385         if (prev)
386                 prev->next = *cookies;
387         else
388                 vars = *cookies;
389         *cookies = NULL;
390         ast_uri_decode(uri);
391
392         /* We want requests to start with the prefix and '/' */
393         l = strlen(prefix);
394         if (l && !strncasecmp(uri, prefix, l) && uri[l] == '/') {
395                 uri += l + 1;
396                 /* scan registered uris to see if we match one. */
397                 for (urih = uris; urih; urih = urih->next) {
398                         l = strlen(urih->uri);
399                         c = uri + l;    /* candidate */
400                         if (strncasecmp(urih->uri, uri, l) /* no match */
401                             || (*c && *c != '/')) /* substring */
402                                 continue;
403                         if (*c == '/')
404                                 c++;
405                         if (!*c || urih->has_subtree) {
406                                 uri = c;
407                                 break;
408                         }
409                 }
410         }
411         if (urih) {
412                 c = urih->callback(sin, uri, vars, status, title, contentlength);
413         } else if (ast_strlen_zero(uri) && ast_strlen_zero(prefix)) {
414                 /* Special case: no prefix, no URI, send to /static/index.html */
415                 c = ast_http_error(302, "Moved Temporarily",
416                         "Location: /static/index.html\r\n",
417                         "This is not the page you are looking for...");
418                 *status = 302;
419                 *title = strdup("Moved Temporarily");
420         } else {
421                 c = ast_http_error(404, "Not Found", NULL,
422                         "The requested URL was not found on this server.");
423                 *status = 404;
424                 *title = strdup("Not Found");
425         }
426         ast_variables_destroy(vars);
427         return c;
428 }
429
430 #ifdef DO_SSL
431 #if defined(HAVE_FUNOPEN)
432 #define HOOK_T int
433 #define LEN_T int
434 #else
435 #define HOOK_T ssize_t
436 #define LEN_T size_t
437 #endif
438 /*!
439  * replacement read/write functions for SSL support.
440  * We use wrappers rather than SSL_read/SSL_write directly so
441  * we can put in some debugging.
442  */
443 static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
444 {
445         int i = SSL_read(cookie, buf, len-1);
446 #if 0
447         if (i >= 0)
448                 buf[i] = '\0';
449         ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
450 #endif
451         return i;
452 }
453
454 static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
455 {
456 #if 0
457         char *s = alloca(len+1);
458         strncpy(s, buf, len);
459         s[len] = '\0';
460         ast_verbose("ssl write size %d <%s>\n", (int)len, s);
461 #endif
462         return SSL_write(cookie, buf, len);
463 }
464
465 static int ssl_close(void *cookie)
466 {
467         close(SSL_get_fd(cookie));
468         SSL_shutdown(cookie);
469         SSL_free(cookie);
470         return 0;
471 }
472 #endif  /* DO_SSL */
473
474 static void *ast_httpd_helper_thread(void *data)
475 {
476         char buf[4096];
477         char cookie[4096];
478         struct server_instance *ser = data;
479         struct ast_variable *var, *prev=NULL, *vars=NULL;
480         char *uri, *c, *title=NULL;
481         int status = 200, contentlength = 0;
482
483         /*
484          * open a FILE * as appropriate.
485          */
486         if (!ser->is_ssl)
487                 ser->f = fdopen(ser->fd, "w+");
488 #ifdef DO_SSL
489         else if ( (ser->ssl = SSL_new(ssl_ctx)) ) {
490                 SSL_set_fd(ser->ssl, ser->fd);
491                 if (SSL_accept(ser->ssl) == 0)
492                         ast_verbose(" error setting up ssl connection");
493                 else {
494 #if defined(HAVE_FUNOPEN)       /* the BSD interface */
495                         ser->f = funopen(ser->ssl, ssl_read, ssl_write, NULL, ssl_close);
496
497 #elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */
498                         static const cookie_io_functions_t cookie_funcs = {
499                                 ssl_read, ssl_write, NULL, ssl_close
500                         };
501                         ser->f = fopencookie(ser->ssl, "w+", cookie_funcs);
502 #else
503                         /* could add other methods here */
504 #endif
505                 }
506                 if (!ser->f)    /* no success opening descriptor stacking */
507                         SSL_free(ser->ssl);
508         }
509 #endif /* DO_SSL */
510
511         if (!ser->f) {
512                 close(ser->fd);
513                 ast_log(LOG_WARNING, "FILE * open failed!\n");
514                 goto done;
515         }
516
517         if (!fgets(buf, sizeof(buf), ser->f))
518                 goto done;
519
520         uri = ast_skip_nonblanks(buf);  /* Skip method */
521         if (*uri)
522                 *uri++ = '\0';
523
524         uri = ast_skip_blanks(uri);     /* Skip white space */
525
526         if (*uri) {                     /* terminate at the first blank */
527                 c = ast_skip_nonblanks(uri);
528                 if (*c)
529                         *c = '\0';
530         }
531
532         /* process "Cookie: " lines */
533         while (fgets(cookie, sizeof(cookie), ser->f)) {
534                 char *vname, *vval;
535                 int l;
536
537                 /* Trim trailing characters */
538                 ast_trim_blanks(cookie);
539                 if (ast_strlen_zero(cookie))
540                         break;
541                 if (strncasecmp(cookie, "Cookie: ", 8))
542                         continue;
543
544                 /* TODO - The cookie parsing code below seems to work   
545                    in IE6 and FireFox 1.5.  However, it is not entirely 
546                    correct, and therefore may not work in all           
547                    circumstances.                                       
548                       For more details see RFC 2109 and RFC 2965        */
549         
550                 /* FireFox cookie strings look like:                    
551                      Cookie: mansession_id="********"                   
552                    InternetExplorer's look like:                        
553                      Cookie: $Version="1"; mansession_id="********"     */
554                 
555                 /* If we got a FireFox cookie string, the name's right  
556                     after "Cookie: "                                    */
557                 vname = ast_skip_blanks(cookie + 8);
558                         
559                 /* If we got an IE cookie string, we need to skip to    
560                     past the version to get to the name                 */
561                 if (*vname == '$') {
562                         strsep(&vname, ";");
563                         if (!vname)     /* no name ? */
564                                 continue;
565                         vname = ast_skip_blanks(vname);
566                 }
567                 vval = strchr(vname, '=');
568                 if (!vval)
569                         continue;
570                 /* Ditch the = and the quotes */
571                 *vval++ = '\0';
572                 if (*vval)
573                         vval++;
574                 if ( (l = strlen(vval)) )
575                         vval[l - 1] = '\0';     /* trim trailing quote */
576                 var = ast_variable_new(vname, vval);
577                 if (var) {
578                         if (prev)
579                                 prev->next = var;
580                         else
581                                 vars = var;
582                         prev = var;
583                 }
584         }
585
586         if (!*uri)
587                 c = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
588         else if (strcasecmp(buf, "get")) 
589                 c = ast_http_error(501, "Not Implemented", NULL,
590                         "Attempt to use unimplemented / unsupported method");
591         else    /* try to serve it */
592                 c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars);
593
594         /* If they aren't mopped up already, clean up the cookies */
595         if (vars)
596                 ast_variables_destroy(vars);
597
598         if (!c)
599                 c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error");
600         if (c) {
601                 time_t t = time(NULL);
602                 char timebuf[256];
603
604                 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
605                 fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
606                                 "Server: Asterisk\r\n"
607                                 "Date: %s\r\n"
608                                 "Connection: close\r\n",
609                         status, title ? title : "OK", timebuf);
610                 if (!contentlength) {   /* opaque body ? just dump it hoping it is properly formatted */
611                         fprintf(ser->f, "%s", c);
612                 } else {
613                         char *tmp = strstr(c, "\r\n\r\n");
614
615                         if (tmp) {
616                                 fprintf(ser->f, "Content-length: %d\r\n", contentlength);
617                                 /* first write the header, then the body */
618                                 fwrite(c, 1, (tmp + 4 - c), ser->f);
619                                 fwrite(tmp + 4, 1, contentlength, ser->f);
620                         }
621                 }
622                 free(c);
623         }
624         if (title)
625                 free(title);
626
627 done:
628         if (ser->f)
629                 fclose(ser->f);
630         free(ser);
631         return NULL;
632 }
633
634 static void *http_root(void *data)
635 {
636         struct server_args *desc = data;
637         int fd;
638         struct sockaddr_in sin;
639         socklen_t sinlen;
640         struct server_instance *ser;
641         pthread_t launched;
642         pthread_attr_t attr;
643         
644         for (;;) {
645                 int flags;
646
647                 ast_wait_for_input(desc->accept_fd, -1);
648                 sinlen = sizeof(sin);
649                 fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen);
650                 if (fd < 0) {
651                         if ((errno != EAGAIN) && (errno != EINTR))
652                                 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
653                         continue;
654                 }
655                 ser = ast_calloc(1, sizeof(*ser));
656                 if (!ser) {
657                         ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
658                         close(fd);
659                         continue;
660                 }
661                 flags = fcntl(fd, F_GETFL);
662                 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
663                 ser->fd = fd;
664                 ser->is_ssl = desc->is_ssl;
665                 memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
666
667                 pthread_attr_init(&attr);
668                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
669                         
670                 if (ast_pthread_create_background(&launched, &attr, ast_httpd_helper_thread, ser)) {
671                         ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
672                         close(ser->fd);
673                         free(ser);
674                 }
675         }
676         return NULL;
677 }
678
679 char *ast_http_setcookie(const char *var, const char *val, int expires, char *buf, size_t buflen)
680 {
681         char *c;
682         c = buf;
683         ast_build_string(&c, &buflen, "Set-Cookie: %s=\"%s\"; Version=\"1\"", var, val);
684         if (expires)
685                 ast_build_string(&c, &buflen, "; Max-Age=%d", expires);
686         ast_build_string(&c, &buflen, "\r\n");
687         return buf;
688 }
689
690 static int ssl_setup(void)
691 {
692 #ifndef DO_SSL
693         do_ssl = 0;
694         return 0;
695 #else
696         if (!do_ssl)
697                 return 0;
698         SSL_load_error_strings();
699         SSLeay_add_ssl_algorithms();
700         ssl_ctx = SSL_CTX_new( SSLv23_server_method() );
701         if (!ast_strlen_zero(certfile)) {
702                 if (SSL_CTX_use_certificate_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) == 0 ||
703                     SSL_CTX_use_PrivateKey_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) == 0 ||
704                     SSL_CTX_check_private_key(ssl_ctx) == 0 ) {
705                         ast_verbose("ssl cert error <%s>", certfile);
706                         sleep(2);
707                         do_ssl = 0;
708                         return 0;
709                 }
710         }
711         if (!ast_strlen_zero(cipher)) {
712                 if (SSL_CTX_set_cipher_list(ssl_ctx, cipher) == 0 ) {
713                         ast_verbose("ssl cipher error <%s>", cipher);
714                         sleep(2);
715                         do_ssl = 0;
716                         return 0;
717                 }
718         }
719         ast_verbose("ssl cert ok");
720         return 1;
721 #endif
722 }
723
724 static void http_server_start(struct server_args *desc)
725 {
726         int flags;
727         int x = 1;
728         
729         /* Do nothing if nothing has changed */
730         if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
731                 if (option_debug)
732                         ast_log(LOG_DEBUG, "Nothing changed in http\n");
733                 return;
734         }
735         
736         desc->oldsin = desc->sin;
737         
738         /* Shutdown a running server if there is one */
739         if (desc->master != AST_PTHREADT_NULL) {
740                 pthread_cancel(desc->master);
741                 pthread_kill(desc->master, SIGURG);
742                 pthread_join(desc->master, NULL);
743         }
744         
745         if (desc->accept_fd != -1)
746                 close(desc->accept_fd);
747
748         /* If there's no new server, stop here */
749         if (desc->sin.sin_family == 0)
750                 return;
751         
752         
753         desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0);
754         if (desc->accept_fd < 0) {
755                 ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
756                 return;
757         }
758         
759         setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
760         if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
761                 ast_log(LOG_NOTICE, "Unable to bind http server to %s:%d: %s\n",
762                         ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
763                         strerror(errno));
764                 goto error;
765         }
766         if (listen(desc->accept_fd, 10)) {
767                 ast_log(LOG_NOTICE, "Unable to listen!\n");
768                 close(desc->accept_fd);
769                 desc->accept_fd = -1;
770                 return;
771         }
772         flags = fcntl(desc->accept_fd, F_GETFL);
773         fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
774         if (ast_pthread_create_background(&desc->master, NULL, http_root, desc)) {
775                 ast_log(LOG_NOTICE, "Unable to launch http server on %s:%d: %s\n",
776                                 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
777                                 strerror(errno));
778                 goto error;
779         }
780         return;
781
782 error:
783         close(desc->accept_fd);
784         desc->accept_fd = -1;
785 }
786
787 static int __ast_http_load(int reload)
788 {
789         struct ast_config *cfg;
790         struct ast_variable *v;
791         int enabled=0;
792         int newenablestatic=0;
793         struct hostent *hp;
794         struct ast_hostent ahp;
795         char newprefix[MAX_PREFIX];
796
797         memset(&http_desc.sin, 0, sizeof(http_desc.sin));
798         http_desc.sin.sin_port = htons(8088);
799         memset(&https_desc.sin, 0, sizeof(https_desc.sin));
800         https_desc.sin.sin_port = htons(8089);
801         strcpy(newprefix, DEFAULT_PREFIX);
802         cfg = ast_config_load("http.conf");
803
804         do_ssl = 0;
805         if (certfile)
806                 free(certfile);
807         certfile = ast_strdup(AST_CERTFILE);
808         if (cipher)
809                 free(cipher);
810         cipher = ast_strdup("");
811
812         if (cfg) {
813                 v = ast_variable_browse(cfg, "general");
814                 while(v) {
815                         if (!strcasecmp(v->name, "enabled"))
816                                 enabled = ast_true(v->value);
817                         else if (!strcasecmp(v->name, "sslenable"))
818                                 do_ssl = ast_true(v->value);
819                         else if (!strcasecmp(v->name, "sslbindport"))
820                                 https_desc.sin.sin_port = htons(atoi(v->value));
821                         else if (!strcasecmp(v->name, "sslcert")) {
822                                 free(certfile);
823                                 certfile = ast_strdup(v->value);
824                         } else if (!strcasecmp(v->name, "sslcipher")) {
825                                 free(cipher);
826                                 cipher = ast_strdup(v->value);
827                         }
828                         else if (!strcasecmp(v->name, "enablestatic"))
829                                 newenablestatic = ast_true(v->value);
830                         else if (!strcasecmp(v->name, "bindport"))
831                                 http_desc.sin.sin_port = htons(atoi(v->value));
832                         else if (!strcasecmp(v->name, "bindaddr")) {
833                                 if ((hp = ast_gethostbyname(v->value, &ahp))) {
834                                         memcpy(&http_desc.sin.sin_addr, hp->h_addr, sizeof(http_desc.sin.sin_addr));
835                                         memcpy(&https_desc.sin.sin_addr, hp->h_addr, sizeof(https_desc.sin.sin_addr));
836                                 } else {
837                                         ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
838                                 }
839                         } else if (!strcasecmp(v->name, "prefix")) {
840                                 if (!ast_strlen_zero(v->value)) {
841                                         newprefix[0] = '/';
842                                         ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
843                                 } else {
844                                         newprefix[0] = '\0';
845                                 }
846                                         
847                         }
848                         v = v->next;
849                 }
850                 ast_config_destroy(cfg);
851         }
852         if (enabled)
853                 http_desc.sin.sin_family = https_desc.sin.sin_family = AF_INET;
854         if (strcmp(prefix, newprefix))
855                 ast_copy_string(prefix, newprefix, sizeof(prefix));
856         enablestatic = newenablestatic;
857         http_server_start(&http_desc);
858         if (ssl_setup())
859                 http_server_start(&https_desc);
860         return 0;
861 }
862
863 static int handle_show_http(int fd, int argc, char *argv[])
864 {
865         struct ast_http_uri *urih;
866         if (argc != 3)
867                 return RESULT_SHOWUSAGE;
868         ast_cli(fd, "HTTP Server Status:\n");
869         ast_cli(fd, "Prefix: %s\n", prefix);
870         if (!http_desc.oldsin.sin_family)
871                 ast_cli(fd, "Server Disabled\n\n");
872         else {
873                 ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n",
874                         ast_inet_ntoa(http_desc.oldsin.sin_addr),
875                         ntohs(http_desc.oldsin.sin_port));
876                 if (do_ssl)
877                         ast_cli(fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
878                                 ast_inet_ntoa(https_desc.oldsin.sin_addr),
879                                 ntohs(https_desc.oldsin.sin_port));
880         }
881         ast_cli(fd, "Enabled URI's:\n");
882         urih = uris;
883         while(urih){
884                 ast_cli(fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
885                 urih = urih->next;
886         }
887         if (!uris)
888                 ast_cli(fd, "None.\n");
889         return RESULT_SUCCESS;
890 }
891
892 int ast_http_reload(void)
893 {
894         return __ast_http_load(1);
895 }
896
897 static char show_http_help[] =
898 "Usage: http show status\n"
899 "       Lists status of internal HTTP engine\n";
900
901 static struct ast_cli_entry cli_http[] = {
902         { { "http", "show", "status", NULL },
903         handle_show_http, "Display HTTP server status",
904         show_http_help },
905 };
906
907 int ast_http_init(void)
908 {
909         ast_http_uri_link(&statusuri);
910         ast_http_uri_link(&staticuri);
911         ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
912         return __ast_http_load(0);
913 }