Implement https support.
[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 #ifdef HAVE_OPENSSL
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 /*!
432  * replacement read/write functions for SSL support.
433  * We use wrappers rather than SSL_read/SSL_write directly so
434  * we can put in some debugging.
435  */
436 static int ssl_read(void *cookie, char *buf, int len)
437 {
438         int i;
439         i = SSL_read(cookie, buf, len-1);
440 #if 0
441         if (i >= 0)
442                 buf[i] = '\0';
443         ast_verbose("ssl read size %d returns %d <%s>\n", len, i, buf);
444 #endif
445         return i;
446 }
447
448 static int ssl_write(void *cookie, const char *buf, int len)
449 {
450 #if 0
451         char *s = alloca(len+1);
452         strncpy(s, buf, len);
453         s[len] = '\0';
454         ast_verbose("ssl write size %d <%s>\n", len, s);
455 #endif
456         return SSL_write(cookie, buf, len);
457 }
458
459 static int ssl_close(void *cookie)
460 {
461         close(SSL_get_fd(cookie));
462         SSL_shutdown(cookie);
463         SSL_free(cookie);
464         return 0;
465 }
466 #endif
467
468 static void *ast_httpd_helper_thread(void *data)
469 {
470         char buf[4096];
471         char cookie[4096];
472         struct server_instance *ser = data;
473         struct ast_variable *var, *prev=NULL, *vars=NULL;
474         char *uri, *c, *title=NULL;
475         int status = 200, contentlength = 0;
476
477 #ifdef DO_SSL
478         if (ser->is_ssl) {
479                 ser->ssl = SSL_new(ssl_ctx);
480                 SSL_set_fd(ser->ssl, ser->fd);
481                 if (SSL_accept(ser->ssl) == 0) {
482                         ast_verbose(" error setting up ssl connection");
483                         goto done;
484                 }
485                 ser->f = funopen(ser->ssl, ssl_read, ssl_write, NULL, ssl_close);
486         } else
487 #endif
488         ser->f = fdopen(ser->fd, "w+");
489
490         if (!ser->f) {
491                 ast_log(LOG_WARNING, "fdopen/funopen failed!\n");
492                 close(ser->fd);
493                 free(ser);
494                 return NULL;
495         }
496
497         if (!fgets(buf, sizeof(buf), ser->f))
498                 goto done;
499
500         uri = ast_skip_nonblanks(buf);  /* Skip method */
501         if (*uri)
502                 *uri++ = '\0';
503
504         uri = ast_skip_blanks(uri);     /* Skip white space */
505
506         if (*uri) {                     /* terminate at the first blank */
507                 c = ast_skip_nonblanks(uri);
508                 if (*c)
509                         *c = '\0';
510         }
511
512         /* process "Cookie: " lines */
513         while (fgets(cookie, sizeof(cookie), ser->f)) {
514                 char *vname, *vval;
515                 int l;
516
517                 /* Trim trailing characters */
518                 ast_trim_blanks(cookie);
519                 if (ast_strlen_zero(cookie))
520                         break;
521                 if (strncasecmp(cookie, "Cookie: ", 8))
522                         continue;
523
524                 /* TODO - The cookie parsing code below seems to work   
525                    in IE6 and FireFox 1.5.  However, it is not entirely 
526                    correct, and therefore may not work in all           
527                    circumstances.                                       
528                       For more details see RFC 2109 and RFC 2965        */
529         
530                 /* FireFox cookie strings look like:                    
531                      Cookie: mansession_id="********"                   
532                    InternetExplorer's look like:                        
533                      Cookie: $Version="1"; mansession_id="********"     */
534                 
535                 /* If we got a FireFox cookie string, the name's right  
536                     after "Cookie: "                                    */
537                 vname = ast_skip_blanks(cookie + 8);
538                         
539                 /* If we got an IE cookie string, we need to skip to    
540                     past the version to get to the name                 */
541                 if (*vname == '$') {
542                         strsep(&vname, ";");
543                         if (!vname)     /* no name ? */
544                                 continue;
545                         vname = ast_skip_blanks(vname);
546                 }
547                 vval = strchr(vname, '=');
548                 if (!vval)
549                         continue;
550                 /* Ditch the = and the quotes */
551                 *vval++ = '\0';
552                 if (*vval)
553                         vval++;
554                 if ( (l = strlen(vval)) )
555                         vval[l - 1] = '\0';     /* trim trailing quote */
556                 var = ast_variable_new(vname, vval);
557                 if (var) {
558                         if (prev)
559                                 prev->next = var;
560                         else
561                                 vars = var;
562                         prev = var;
563                 }
564         }
565
566         if (!*uri)
567                 c = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
568         else if (strcasecmp(buf, "get")) 
569                 c = ast_http_error(501, "Not Implemented", NULL,
570                         "Attempt to use unimplemented / unsupported method");
571         else    /* try to serve it */
572                 c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars);
573
574         /* If they aren't mopped up already, clean up the cookies */
575         if (vars)
576                 ast_variables_destroy(vars);
577
578         if (!c)
579                 c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error");
580         if (c) {
581                 time_t t = time(NULL);
582                 char timebuf[256];
583
584                 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
585                 fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
586                                 "Server: Asterisk\r\n"
587                                 "Date: %s\r\n"
588                                 "Connection: close\r\n",
589                         status, title ? title : "OK", timebuf);
590                 if (!contentlength) {   /* opaque body ? just dump it hoping it is properly formatted */
591                         fprintf(ser->f, "%s", c);
592                 } else {
593                         char *tmp = strstr(c, "\r\n\r\n");
594
595                         if (tmp) {
596                                 fprintf(ser->f, "Content-length: %d\r\n", contentlength);
597                                 /* first write the header, then the body */
598                                 fwrite(c, 1, (tmp + 4 - c), ser->f);
599                                 fwrite(tmp + 4, 1, contentlength, ser->f);
600                         }
601                 }
602                 free(c);
603         }
604         if (title)
605                 free(title);
606
607 done:
608         fclose(ser->f);
609         free(ser);
610         return NULL;
611 }
612
613 static void *http_root(void *data)
614 {
615         struct server_args *desc = data;
616         int fd;
617         struct sockaddr_in sin;
618         socklen_t sinlen;
619         struct server_instance *ser;
620         pthread_t launched;
621         pthread_attr_t attr;
622         
623         for (;;) {
624                 int flags;
625
626                 ast_wait_for_input(desc->accept_fd, -1);
627                 sinlen = sizeof(sin);
628                 fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen);
629                 if (fd < 0) {
630                         if ((errno != EAGAIN) && (errno != EINTR))
631                                 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
632                         continue;
633                 }
634                 ser = ast_calloc(1, sizeof(*ser));
635                 if (!ser) {
636                         ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
637                         close(fd);
638                         continue;
639                 }
640                 flags = fcntl(fd, F_GETFL);
641                 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
642                 ser->fd = fd;
643                 ser->is_ssl = desc->is_ssl;
644                 memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
645
646                 pthread_attr_init(&attr);
647                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
648                         
649                 if (ast_pthread_create_background(&launched, &attr, ast_httpd_helper_thread, ser)) {
650                         ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
651                         close(ser->fd);
652                         free(ser);
653                 }
654         }
655         return NULL;
656 }
657
658 char *ast_http_setcookie(const char *var, const char *val, int expires, char *buf, size_t buflen)
659 {
660         char *c;
661         c = buf;
662         ast_build_string(&c, &buflen, "Set-Cookie: %s=\"%s\"; Version=\"1\"", var, val);
663         if (expires)
664                 ast_build_string(&c, &buflen, "; Max-Age=%d", expires);
665         ast_build_string(&c, &buflen, "\r\n");
666         return buf;
667 }
668
669 static int ssl_setup(void)
670 {
671 #ifndef DO_SSL
672         do_ssl = 0;
673         return 0;
674 #else
675         if (!do_ssl)
676                 return 0;
677         SSL_load_error_strings();
678         SSLeay_add_ssl_algorithms();
679         ssl_ctx = SSL_CTX_new( SSLv23_server_method() );
680         if (!ast_strlen_zero(certfile)) {
681                 if (SSL_CTX_use_certificate_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) == 0 ||
682                     SSL_CTX_use_PrivateKey_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) == 0 ||
683                     SSL_CTX_check_private_key(ssl_ctx) == 0 ) {
684                         ast_verbose("ssl cert error <%s>", certfile);
685                         sleep(2);
686                         do_ssl = 0;
687                         return 0;
688                 }
689         }
690         if (!ast_strlen_zero(cipher)) {
691                 if (SSL_CTX_set_cipher_list(ssl_ctx, cipher) == 0 ) {
692                         ast_verbose("ssl cipher error <%s>", cipher);
693                         sleep(2);
694                         do_ssl = 0;
695                         return 0;
696                 }
697         }
698         ast_verbose("ssl cert ok");
699         return 1;
700 #endif
701 }
702
703 static void http_server_start(struct server_args *desc)
704 {
705         int flags;
706         int x = 1;
707         
708         /* Do nothing if nothing has changed */
709         if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
710                 if (option_debug)
711                         ast_log(LOG_DEBUG, "Nothing changed in http\n");
712                 return;
713         }
714         
715         desc->oldsin = desc->sin;
716         
717         /* Shutdown a running server if there is one */
718         if (desc->master != AST_PTHREADT_NULL) {
719                 pthread_cancel(desc->master);
720                 pthread_kill(desc->master, SIGURG);
721                 pthread_join(desc->master, NULL);
722         }
723         
724         if (desc->accept_fd != -1)
725                 close(desc->accept_fd);
726
727         /* If there's no new server, stop here */
728         if (desc->sin.sin_family == 0)
729                 return;
730         
731         
732         desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0);
733         if (desc->accept_fd < 0) {
734                 ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
735                 return;
736         }
737         
738         setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
739         if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
740                 ast_log(LOG_NOTICE, "Unable to bind http server to %s:%d: %s\n",
741                         ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
742                         strerror(errno));
743                 goto error;
744         }
745         if (listen(desc->accept_fd, 10)) {
746                 ast_log(LOG_NOTICE, "Unable to listen!\n");
747                 close(desc->accept_fd);
748                 desc->accept_fd = -1;
749                 return;
750         }
751         flags = fcntl(desc->accept_fd, F_GETFL);
752         fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
753         if (ast_pthread_create_background(&desc->master, NULL, http_root, desc)) {
754                 ast_log(LOG_NOTICE, "Unable to launch http server on %s:%d: %s\n",
755                                 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
756                                 strerror(errno));
757                 goto error;
758         }
759         return;
760
761 error:
762         close(desc->accept_fd);
763         desc->accept_fd = -1;
764 }
765
766 static int __ast_http_load(int reload)
767 {
768         struct ast_config *cfg;
769         struct ast_variable *v;
770         int enabled=0;
771         int newenablestatic=0;
772         struct hostent *hp;
773         struct ast_hostent ahp;
774         char newprefix[MAX_PREFIX];
775
776         memset(&http_desc.sin, 0, sizeof(http_desc.sin));
777         http_desc.sin.sin_port = htons(8088);
778         memset(&https_desc.sin, 0, sizeof(https_desc.sin));
779         https_desc.sin.sin_port = htons(8089);
780         strcpy(newprefix, DEFAULT_PREFIX);
781         cfg = ast_config_load("http.conf");
782
783         do_ssl = 0;
784         if (certfile)
785                 free(certfile);
786         certfile = ast_strdup(AST_CERTFILE);
787         if (cipher)
788                 free(cipher);
789         cipher = ast_strdup("");
790
791         if (cfg) {
792                 v = ast_variable_browse(cfg, "general");
793                 while(v) {
794                         if (!strcasecmp(v->name, "enabled"))
795                                 enabled = ast_true(v->value);
796                         else if (!strcasecmp(v->name, "sslenable"))
797                                 do_ssl = ast_true(v->value);
798                         else if (!strcasecmp(v->name, "sslbindport"))
799                                 https_desc.sin.sin_port = htons(atoi(v->value));
800                         else if (!strcasecmp(v->name, "sslcert")) {
801                                 free(certfile);
802                                 certfile = ast_strdup(v->value);
803                         } else if (!strcasecmp(v->name, "sslcipher")) {
804                                 free(cipher);
805                                 cipher = ast_strdup(v->value);
806                         }
807                         else if (!strcasecmp(v->name, "enablestatic"))
808                                 newenablestatic = ast_true(v->value);
809                         else if (!strcasecmp(v->name, "bindport"))
810                                 http_desc.sin.sin_port = htons(atoi(v->value));
811                         else if (!strcasecmp(v->name, "bindaddr")) {
812                                 if ((hp = ast_gethostbyname(v->value, &ahp))) {
813                                         memcpy(&http_desc.sin.sin_addr, hp->h_addr, sizeof(http_desc.sin.sin_addr));
814                                         memcpy(&https_desc.sin.sin_addr, hp->h_addr, sizeof(https_desc.sin.sin_addr));
815                                 } else {
816                                         ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
817                                 }
818                         } else if (!strcasecmp(v->name, "prefix")) {
819                                 if (!ast_strlen_zero(v->value)) {
820                                         newprefix[0] = '/';
821                                         ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
822                                 } else {
823                                         newprefix[0] = '\0';
824                                 }
825                                         
826                         }
827                         v = v->next;
828                 }
829                 ast_config_destroy(cfg);
830         }
831         if (enabled)
832                 http_desc.sin.sin_family = https_desc.sin.sin_family = AF_INET;
833         if (strcmp(prefix, newprefix))
834                 ast_copy_string(prefix, newprefix, sizeof(prefix));
835         enablestatic = newenablestatic;
836         http_server_start(&http_desc);
837         if (ssl_setup())
838                 http_server_start(&https_desc);
839         return 0;
840 }
841
842 static int handle_show_http(int fd, int argc, char *argv[])
843 {
844         struct ast_http_uri *urih;
845         if (argc != 3)
846                 return RESULT_SHOWUSAGE;
847         ast_cli(fd, "HTTP Server Status:\n");
848         ast_cli(fd, "Prefix: %s\n", prefix);
849         if (!http_desc.oldsin.sin_family)
850                 ast_cli(fd, "Server Disabled\n\n");
851         else {
852                 ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n",
853                         ast_inet_ntoa(http_desc.oldsin.sin_addr),
854                         ntohs(http_desc.oldsin.sin_port));
855                 if (do_ssl)
856                         ast_cli(fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
857                                 ast_inet_ntoa(https_desc.oldsin.sin_addr),
858                                 ntohs(https_desc.oldsin.sin_port));
859         }
860         ast_cli(fd, "Enabled URI's:\n");
861         urih = uris;
862         while(urih){
863                 ast_cli(fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
864                 urih = urih->next;
865         }
866         if (!uris)
867                 ast_cli(fd, "None.\n");
868         return RESULT_SUCCESS;
869 }
870
871 int ast_http_reload(void)
872 {
873         return __ast_http_load(1);
874 }
875
876 static char show_http_help[] =
877 "Usage: http list status\n"
878 "       Lists status of internal HTTP engine\n";
879
880 static struct ast_cli_entry cli_http[] = {
881         { { "http", "list", "status", NULL },
882         handle_show_http, "Display HTTP server status",
883         show_http_help },
884 };
885
886 int ast_http_init(void)
887 {
888         ast_http_uri_link(&statusuri);
889         ast_http_uri_link(&staticuri);
890         ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
891         return __ast_http_load(0);
892 }