2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief http server for AMI access
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
27 * \ref AstHTTP - AMI over the http protocol
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34 #include <sys/types.h>
40 #include <netinet/in.h>
42 #include <sys/socket.h>
44 #include <sys/signal.h>
45 #include <arpa/inet.h>
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 #include "asterisk/stringfields.h"
57 #include "asterisk/version.h"
60 #define DEFAULT_PREFIX "/asterisk"
62 /* See http.h for more information about the SSL implementation */
63 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
64 #define DO_SSL /* comment in/out if you want to support ssl */
67 static struct tls_config http_tls_cfg;
69 static void *httpd_helper_thread(void *arg);
72 * we have up to two accepting threads, one for http, one for https
74 static struct server_args http_desc = {
76 .master = AST_PTHREADT_NULL,
79 .name = "http server",
80 .accept_fn = server_root,
81 .worker_fn = httpd_helper_thread,
84 static struct server_args https_desc = {
86 .master = AST_PTHREADT_NULL,
87 .tls_cfg = &http_tls_cfg,
89 .name = "https server",
90 .accept_fn = server_root,
91 .worker_fn = httpd_helper_thread,
94 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri); /*!< list of supported handlers */
96 /* all valid URIs must be prepended by the string in prefix. */
97 static char prefix[MAX_PREFIX];
98 static int enablestatic;
100 /*! \brief Limit the kinds of files we're willing to serve up */
105 { "png", "image/png" },
106 { "jpg", "image/jpeg" },
107 { "js", "application/x-javascript" },
108 { "wav", "audio/x-wav" },
109 { "mp3", "audio/mpeg" },
112 struct http_uri_redirect {
113 AST_LIST_ENTRY(http_uri_redirect) entry;
118 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
120 static char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
124 for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) {
125 if (!strcasecmp(ftype, mimetypes[x].ext))
126 return mimetypes[x].mtype;
129 snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain");
133 /* like ast_uri_decode, but replace '+' with ' ' */
134 static char *uri_decode(char *buf)
138 for (c = buf; *c; c++) {
145 static struct ast_str *static_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
147 struct ast_str *result;
155 /* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration
156 substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
157 if (!enablestatic || ast_strlen_zero(uri))
159 /* Disallow any funny filenames at all */
160 if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0]))
162 if (strstr(uri, "/.."))
165 if ((ftype = strrchr(uri, '.')))
167 mtype=ftype2mtype(ftype, wkspace, sizeof(wkspace));
169 /* Cap maximum length */
170 len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5;
175 sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
178 if (S_ISDIR(st.st_mode))
180 fd = open(path, O_RDONLY);
184 len = st.st_size + strlen(mtype) + 40;
185 result = ast_str_create(len);
186 if (result == NULL) /* XXX not really but... */
189 ast_str_append(&result, 0, "Content-type: %s\r\n\r\n", mtype);
190 *contentlength = read(fd, result->str + result->used, st.st_size);
191 if (*contentlength < 0) {
196 result->used += *contentlength;
202 *title = strdup("Not Found");
203 return ast_http_error(404, "Not Found", NULL, "Nothing to see here. Move along.");
207 *title = strdup("Access Denied");
208 return ast_http_error(403, "Access Denied", NULL, "Sorry, I cannot let you do that, Dave.");
212 static struct ast_str *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
214 struct ast_str *out = ast_str_create(512);
215 struct ast_variable *v;
220 ast_str_append(&out, 0,
222 "<title>Asterisk HTTP Status</title>\r\n"
223 "<body bgcolor=\"#ffffff\">\r\n"
224 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
225 "<h2> Asterisk™ HTTP Status</h2></td></tr>\r\n");
227 ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
228 ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
229 ast_inet_ntoa(http_desc.oldsin.sin_addr));
230 ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
231 ntohs(http_desc.oldsin.sin_port));
232 if (http_tls_cfg.enabled)
233 ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
234 ntohs(https_desc.oldsin.sin_port));
235 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
236 for (v = vars; v; v = v->next) {
237 if (strncasecmp(v->name, "cookie_", 7))
238 ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
240 ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
241 for (v = vars; v; v = v->next) {
242 if (!strncasecmp(v->name, "cookie_", 7))
243 ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
245 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");
249 static struct ast_http_uri statusuri = {
250 .callback = httpstatus_callback,
251 .description = "Asterisk HTTP General Status",
256 static struct ast_http_uri staticuri = {
257 .callback = static_callback,
258 .description = "Asterisk HTTP Static Delivery",
263 struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
265 struct ast_str *out = ast_str_create(512);
269 "Content-type: text/html\r\n"
272 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
274 "<title>%d %s</title>\r\n"
279 "<address>Asterisk Server</address>\r\n"
280 "</body></html>\r\n",
281 (extra_header ? extra_header : ""), status, title, title, text);
286 * Link the new uri into the list.
288 * They are sorted by length of
289 * the string, not alphabetically. Duplicate entries are not replaced,
290 * but the insertion order (using <= and not just <) makes sure that
291 * more recent insertions hide older ones.
292 * On a lookup, we just scan the list and stop at the first matching entry.
294 int ast_http_uri_link(struct ast_http_uri *urih)
296 struct ast_http_uri *uri;
297 int len = strlen(urih->uri);
299 AST_RWLIST_WRLOCK(&uris);
301 if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
302 AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
303 AST_RWLIST_UNLOCK(&uris);
307 AST_RWLIST_TRAVERSE(&uris, uri, entry) {
308 if ( AST_RWLIST_NEXT(uri, entry)
309 && strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len ) {
310 AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
311 AST_RWLIST_UNLOCK(&uris);
316 AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
318 AST_RWLIST_UNLOCK(&uris);
323 void ast_http_uri_unlink(struct ast_http_uri *urih)
325 AST_RWLIST_WRLOCK(&uris);
326 AST_RWLIST_REMOVE(&uris, urih, entry);
327 AST_RWLIST_UNLOCK(&uris);
330 static struct ast_str *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies)
333 struct ast_str *out = NULL;
335 struct ast_http_uri *urih=NULL;
337 struct ast_variable *vars=NULL, *v, *prev = NULL;
338 struct http_uri_redirect *redirect;
340 strsep(¶ms, "?");
341 /* Extract arguments from the request and store them in variables. */
345 while ((val = strsep(¶ms, "&"))) {
346 var = strsep(&val, "=");
352 if ((v = ast_variable_new(var, val))) {
362 * Append the cookies to the variables (the only reason to have them
363 * at the end is to avoid another pass of the cookies list to find
367 prev->next = *cookies;
373 AST_RWLIST_RDLOCK(&uri_redirects);
374 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
375 if (!strcasecmp(uri, redirect->target)) {
377 snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
378 out = ast_http_error(302, "Moved Temporarily", buf,
379 "There is no spoon...");
381 *title = strdup("Moved Temporarily");
385 AST_RWLIST_UNLOCK(&uri_redirects);
389 /* We want requests to start with the prefix and '/' */
391 if (l && !strncasecmp(uri, prefix, l) && uri[l] == '/') {
393 /* scan registered uris to see if we match one. */
394 AST_RWLIST_RDLOCK(&uris);
395 AST_RWLIST_TRAVERSE(&uris, urih, entry) {
396 l = strlen(urih->uri);
397 c = uri + l; /* candidate */
398 if (strncasecmp(urih->uri, uri, l) /* no match */
399 || (*c && *c != '/')) /* substring */
403 if (!*c || urih->has_subtree) {
409 AST_RWLIST_UNLOCK(&uris);
412 out = urih->callback(sin, uri, vars, status, title, contentlength);
413 AST_RWLIST_UNLOCK(&uris);
415 out = ast_http_error(404, "Not Found", NULL,
416 "The requested URL was not found on this server.");
418 *title = strdup("Not Found");
422 ast_variables_destroy(vars);
427 #if defined(HAVE_FUNOPEN)
431 #define HOOK_T ssize_t
435 * replacement read/write functions for SSL support.
436 * We use wrappers rather than SSL_read/SSL_write directly so
437 * we can put in some debugging.
439 static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
441 int i = SSL_read(cookie, buf, len-1);
445 ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
450 static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
453 char *s = alloca(len+1);
454 strncpy(s, buf, len);
456 ast_verbose("ssl write size %d <%s>\n", (int)len, s);
458 return SSL_write(cookie, buf, len);
461 static int ssl_close(void *cookie)
463 close(SSL_get_fd(cookie));
464 SSL_shutdown(cookie);
471 * creates a FILE * from the fd passed by the accept thread.
472 * This operation is potentially expensive (certificate verification),
473 * so we do it in the child thread context.
475 static void *make_file_from_fd(void *data)
477 struct server_instance *ser = data;
480 * open a FILE * as appropriate.
482 if (!ser->parent->tls_cfg)
483 ser->f = fdopen(ser->fd, "w+");
485 else if ( (ser->ssl = SSL_new(ser->parent->tls_cfg->ssl_ctx)) ) {
486 SSL_set_fd(ser->ssl, ser->fd);
487 if (SSL_accept(ser->ssl) == 0)
488 ast_verbose(" error setting up ssl connection");
490 #if defined(HAVE_FUNOPEN) /* the BSD interface */
491 ser->f = funopen(ser->ssl, ssl_read, ssl_write, NULL, ssl_close);
493 #elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */
494 static const cookie_io_functions_t cookie_funcs = {
495 ssl_read, ssl_write, NULL, ssl_close
497 ser->f = fopencookie(ser->ssl, "w+", cookie_funcs);
499 /* could add other methods here */
502 if (!ser->f) /* no success opening descriptor stacking */
509 ast_log(LOG_WARNING, "FILE * open failed!\n");
513 return ser->parent->worker_fn(ser);
516 static void *httpd_helper_thread(void *data)
520 struct server_instance *ser = data;
521 struct ast_variable *var, *prev=NULL, *vars=NULL;
522 char *uri, *title=NULL;
523 int status = 200, contentlength = 0;
524 struct ast_str *out = NULL;
526 if (!fgets(buf, sizeof(buf), ser->f))
529 uri = ast_skip_nonblanks(buf); /* Skip method */
533 uri = ast_skip_blanks(uri); /* Skip white space */
535 if (*uri) { /* terminate at the first blank */
536 char *c = ast_skip_nonblanks(uri);
541 /* process "Cookie: " lines */
542 while (fgets(cookie, sizeof(cookie), ser->f)) {
546 /* Trim trailing characters */
547 ast_trim_blanks(cookie);
548 if (ast_strlen_zero(cookie))
550 if (strncasecmp(cookie, "Cookie: ", 8))
553 /* TODO - The cookie parsing code below seems to work
554 in IE6 and FireFox 1.5. However, it is not entirely
555 correct, and therefore may not work in all
557 For more details see RFC 2109 and RFC 2965 */
559 /* FireFox cookie strings look like:
560 Cookie: mansession_id="********"
561 InternetExplorer's look like:
562 Cookie: $Version="1"; mansession_id="********" */
564 /* If we got a FireFox cookie string, the name's right
566 vname = ast_skip_blanks(cookie + 8);
568 /* If we got an IE cookie string, we need to skip to
569 past the version to get to the name */
572 if (!vname) /* no name ? */
574 vname = ast_skip_blanks(vname);
576 vval = strchr(vname, '=');
579 /* Ditch the = and the quotes */
583 if ( (l = strlen(vval)) )
584 vval[l - 1] = '\0'; /* trim trailing quote */
585 var = ast_variable_new(vname, vval);
596 out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
597 else if (strcasecmp(buf, "get"))
598 out = ast_http_error(501, "Not Implemented", NULL,
599 "Attempt to use unimplemented / unsupported method");
600 else /* try to serve it */
601 out = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars);
603 /* If they aren't mopped up already, clean up the cookies */
605 ast_variables_destroy(vars);
608 out = ast_http_error(500, "Internal Error", NULL, "Internal Server Error");
610 time_t t = time(NULL);
613 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
614 fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
615 "Server: Asterisk/%s\r\n"
617 "Connection: close\r\n",
618 status, title ? title : "OK", ASTERISK_VERSION, timebuf);
619 if (!contentlength) { /* opaque body ? just dump it hoping it is properly formatted */
620 fprintf(ser->f, "%s", out->str);
622 char *tmp = strstr(out->str, "\r\n\r\n");
625 fprintf(ser->f, "Content-length: %d\r\n", contentlength);
626 /* first write the header, then the body */
627 fwrite(out->str, 1, (tmp + 4 - out->str), ser->f);
628 fwrite(tmp + 4, 1, contentlength, ser->f);
643 void *server_root(void *data)
645 struct server_args *desc = data;
647 struct sockaddr_in sin;
649 struct server_instance *ser;
656 if (desc->periodic_fn)
657 desc->periodic_fn(desc);
658 i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
661 sinlen = sizeof(sin);
662 fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen);
664 if ((errno != EAGAIN) && (errno != EINTR))
665 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
668 ser = ast_calloc(1, sizeof(*ser));
670 ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
674 flags = fcntl(fd, F_GETFL);
675 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
678 memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
680 pthread_attr_init(&attr);
681 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
683 if (ast_pthread_create_background(&launched, &attr, make_file_from_fd, ser)) {
684 ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
689 pthread_attr_destroy(&attr);
694 int ssl_setup(struct tls_config *cfg)
702 SSL_load_error_strings();
703 SSLeay_add_ssl_algorithms();
704 cfg->ssl_ctx = SSL_CTX_new( SSLv23_server_method() );
705 if (!ast_strlen_zero(cfg->certfile)) {
706 if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
707 SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
708 SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) {
709 ast_verbose("ssl cert error <%s>", cfg->certfile);
715 if (!ast_strlen_zero(cfg->cipher)) {
716 if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
717 ast_verbose("ssl cipher error <%s>", cfg->cipher);
723 ast_verbose("ssl cert ok");
729 * This is a generic (re)start routine for a TCP server,
730 * which does the socket/bind/listen and starts a thread for handling
733 void server_start(struct server_args *desc)
738 /* Do nothing if nothing has changed */
739 if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
741 ast_log(LOG_DEBUG, "Nothing changed in %s\n", desc->name);
745 desc->oldsin = desc->sin;
747 /* Shutdown a running server if there is one */
748 if (desc->master != AST_PTHREADT_NULL) {
749 pthread_cancel(desc->master);
750 pthread_kill(desc->master, SIGURG);
751 pthread_join(desc->master, NULL);
754 if (desc->accept_fd != -1)
755 close(desc->accept_fd);
757 /* If there's no new server, stop here */
758 if (desc->sin.sin_family == 0)
761 desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0);
762 if (desc->accept_fd < 0) {
763 ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
764 desc->name, strerror(errno));
768 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
769 if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
770 ast_log(LOG_NOTICE, "Unable to bind %s to %s:%d: %s\n",
772 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
776 if (listen(desc->accept_fd, 10)) {
777 ast_log(LOG_NOTICE, "Unable to listen for %s!\n", desc->name);
780 flags = fcntl(desc->accept_fd, F_GETFL);
781 fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
782 if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
783 ast_log(LOG_NOTICE, "Unable to launch %s on %s:%d: %s\n",
785 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
792 close(desc->accept_fd);
793 desc->accept_fd = -1;
797 * \brief Add a new URI redirect
798 * The entries in the redirect list are sorted by length, just like the list
801 static void add_redirect(const char *value)
804 struct http_uri_redirect *redirect, *cur;
805 unsigned int target_len;
806 unsigned int total_len;
808 dest = ast_strdupa(value);
809 dest = ast_skip_blanks(dest);
810 target = strsep(&dest, " ");
811 target = ast_skip_blanks(target);
812 target = strsep(&target, " "); /* trim trailing whitespace */
815 ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
819 target_len = strlen(target) + 1;
820 total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
822 if (!(redirect = ast_calloc(1, total_len)))
825 redirect->dest = redirect->target + target_len;
826 strcpy(redirect->target, target);
827 strcpy(redirect->dest, dest);
829 AST_RWLIST_WRLOCK(&uri_redirects);
831 target_len--; /* So we can compare directly with strlen() */
832 if ( AST_RWLIST_EMPTY(&uri_redirects)
833 || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
834 AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
835 AST_RWLIST_UNLOCK(&uri_redirects);
839 AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
840 if ( AST_RWLIST_NEXT(cur, entry)
841 && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
842 AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
843 AST_RWLIST_UNLOCK(&uri_redirects);
848 AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
850 AST_RWLIST_UNLOCK(&uri_redirects);
853 static int __ast_http_load(int reload)
855 struct ast_config *cfg;
856 struct ast_variable *v;
858 int newenablestatic=0;
860 struct ast_hostent ahp;
861 char newprefix[MAX_PREFIX];
862 int have_sslbindaddr = 0;
863 struct http_uri_redirect *redirect;
866 memset(&http_desc.sin, 0, sizeof(http_desc.sin));
867 http_desc.sin.sin_port = htons(8088);
869 memset(&https_desc.sin, 0, sizeof(https_desc.sin));
870 https_desc.sin.sin_port = htons(8089);
871 strcpy(newprefix, DEFAULT_PREFIX);
873 http_tls_cfg.enabled = 0;
874 if (http_tls_cfg.certfile)
875 free(http_tls_cfg.certfile);
876 http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
877 if (http_tls_cfg.cipher)
878 free(http_tls_cfg.cipher);
879 http_tls_cfg.cipher = ast_strdup("");
881 AST_RWLIST_WRLOCK(&uri_redirects);
882 while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry)))
884 AST_RWLIST_UNLOCK(&uri_redirects);
886 cfg = ast_config_load("http.conf");
888 v = ast_variable_browse(cfg, "general");
889 for (; v; v = v->next) {
890 if (!strcasecmp(v->name, "enabled"))
891 enabled = ast_true(v->value);
892 else if (!strcasecmp(v->name, "sslenable"))
893 http_tls_cfg.enabled = ast_true(v->value);
894 else if (!strcasecmp(v->name, "sslbindport"))
895 https_desc.sin.sin_port = htons(atoi(v->value));
896 else if (!strcasecmp(v->name, "sslcert")) {
897 free(http_tls_cfg.certfile);
898 http_tls_cfg.certfile = ast_strdup(v->value);
899 } else if (!strcasecmp(v->name, "sslcipher")) {
900 free(http_tls_cfg.cipher);
901 http_tls_cfg.cipher = ast_strdup(v->value);
903 else if (!strcasecmp(v->name, "enablestatic"))
904 newenablestatic = ast_true(v->value);
905 else if (!strcasecmp(v->name, "bindport"))
906 http_desc.sin.sin_port = htons(atoi(v->value));
907 else if (!strcasecmp(v->name, "sslbindaddr")) {
908 if ((hp = ast_gethostbyname(v->value, &ahp))) {
909 memcpy(&https_desc.sin.sin_addr, hp->h_addr, sizeof(https_desc.sin.sin_addr));
910 have_sslbindaddr = 1;
912 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
914 } else if (!strcasecmp(v->name, "bindaddr")) {
915 if ((hp = ast_gethostbyname(v->value, &ahp))) {
916 memcpy(&http_desc.sin.sin_addr, hp->h_addr, sizeof(http_desc.sin.sin_addr));
918 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
920 } else if (!strcasecmp(v->name, "prefix")) {
921 if (!ast_strlen_zero(v->value)) {
923 ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
927 } else if (!strcasecmp(v->name, "redirect")) {
928 add_redirect(v->value);
930 ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
933 ast_config_destroy(cfg);
935 if (!have_sslbindaddr)
936 https_desc.sin.sin_addr = http_desc.sin.sin_addr;
938 http_desc.sin.sin_family = https_desc.sin.sin_family = AF_INET;
939 if (strcmp(prefix, newprefix))
940 ast_copy_string(prefix, newprefix, sizeof(prefix));
941 enablestatic = newenablestatic;
942 server_start(&http_desc);
943 if (ssl_setup(https_desc.tls_cfg))
944 server_start(&https_desc);
948 static int handle_show_http(int fd, int argc, char *argv[])
950 struct ast_http_uri *urih;
951 struct http_uri_redirect *redirect;
954 return RESULT_SHOWUSAGE;
956 ast_cli(fd, "HTTP Server Status:\n");
957 ast_cli(fd, "Prefix: %s\n", prefix);
958 if (!http_desc.oldsin.sin_family)
959 ast_cli(fd, "Server Disabled\n\n");
961 ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n",
962 ast_inet_ntoa(http_desc.oldsin.sin_addr),
963 ntohs(http_desc.oldsin.sin_port));
964 if (http_tls_cfg.enabled)
965 ast_cli(fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
966 ast_inet_ntoa(https_desc.oldsin.sin_addr),
967 ntohs(https_desc.oldsin.sin_port));
970 ast_cli(fd, "Enabled URI's:\n");
971 AST_RWLIST_RDLOCK(&uris);
972 if (AST_RWLIST_EMPTY(&uris)) {
973 ast_cli(fd, "None.\n");
975 AST_RWLIST_TRAVERSE(&uris, urih, entry)
976 ast_cli(fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
978 AST_RWLIST_UNLOCK(&uris);
980 ast_cli(fd, "\nEnabled Redirects:\n");
981 AST_RWLIST_RDLOCK(&uri_redirects);
982 AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
983 ast_cli(fd, " %s => %s\n", redirect->target, redirect->dest);
984 if (AST_RWLIST_EMPTY(&uri_redirects))
985 ast_cli(fd, " None.\n");
986 AST_RWLIST_UNLOCK(&uri_redirects);
988 return RESULT_SUCCESS;
991 int ast_http_reload(void)
993 return __ast_http_load(1);
996 static char show_http_help[] =
997 "Usage: http show status\n"
998 " Lists status of internal HTTP engine\n";
1000 static struct ast_cli_entry cli_http[] = {
1001 { { "http", "show", "status", NULL },
1002 handle_show_http, "Display HTTP server status",
1006 int ast_http_init(void)
1008 ast_http_uri_link(&statusuri);
1009 ast_http_uri_link(&staticuri);
1010 ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
1011 return __ast_http_load(0);