Sat Jul 12 2014 17:18:33

Asterisk developer's documentation


http.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \brief http server for AMI access
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * This program implements a tiny http server
00026  * and was inspired by micro-httpd by Jef Poskanzer
00027  *
00028  * \extref GMime http://spruce.sourceforge.net/gmime/
00029  *
00030  * \ref AstHTTP - AMI over the http protocol
00031  */
00032 
00033 /*** MODULEINFO
00034    <support_level>core</support_level>
00035  ***/
00036 
00037 #include "asterisk.h"
00038 
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416067 $")
00040 
00041 #include <time.h>
00042 #include <sys/time.h>
00043 #include <sys/stat.h>
00044 #include <sys/signal.h>
00045 #include <fcntl.h>
00046 
00047 #include "asterisk/paths.h"   /* use ast_config_AST_DATA_DIR */
00048 #include "asterisk/cli.h"
00049 #include "asterisk/tcptls.h"
00050 #include "asterisk/http.h"
00051 #include "asterisk/utils.h"
00052 #include "asterisk/strings.h"
00053 #include "asterisk/config.h"
00054 #include "asterisk/stringfields.h"
00055 #include "asterisk/ast_version.h"
00056 #include "asterisk/manager.h"
00057 #include "asterisk/_private.h"
00058 #include "asterisk/astobj2.h"
00059 #include "asterisk/netsock2.h"
00060 
00061 #define MAX_PREFIX 80
00062 #define DEFAULT_PORT 8088
00063 #define DEFAULT_TLS_PORT 8089
00064 #define DEFAULT_SESSION_LIMIT 100
00065 #define DEFAULT_SESSION_INACTIVITY 30000  /* (ms) Idle time waiting for data. */
00066 
00067 /* See http.h for more information about the SSL implementation */
00068 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
00069 #define  DO_SSL   /* comment in/out if you want to support ssl */
00070 #endif
00071 
00072 static int session_limit = DEFAULT_SESSION_LIMIT;
00073 static int session_inactivity = DEFAULT_SESSION_INACTIVITY;
00074 static int session_count = 0;
00075 
00076 static struct ast_tls_config http_tls_cfg;
00077 
00078 static void *httpd_helper_thread(void *arg);
00079 
00080 /*!
00081  * we have up to two accepting threads, one for http, one for https
00082  */
00083 static struct ast_tcptls_session_args http_desc = {
00084    .accept_fd = -1,
00085    .master = AST_PTHREADT_NULL,
00086    .tls_cfg = NULL,
00087    .poll_timeout = -1,
00088    .name = "http server",
00089    .accept_fn = ast_tcptls_server_root,
00090    .worker_fn = httpd_helper_thread,
00091 };
00092 
00093 static struct ast_tcptls_session_args https_desc = {
00094    .accept_fd = -1,
00095    .master = AST_PTHREADT_NULL,
00096    .tls_cfg = &http_tls_cfg,
00097    .poll_timeout = -1,
00098    .name = "https server",
00099    .accept_fn = ast_tcptls_server_root,
00100    .worker_fn = httpd_helper_thread,
00101 };
00102 
00103 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri); /*!< list of supported handlers */
00104 
00105 /* all valid URIs must be prepended by the string in prefix. */
00106 static char prefix[MAX_PREFIX];
00107 static int enablestatic;
00108 
00109 /*! \brief Limit the kinds of files we're willing to serve up */
00110 static struct {
00111    const char *ext;
00112    const char *mtype;
00113 } mimetypes[] = {
00114    { "png", "image/png" },
00115    { "xml", "text/xml" },
00116    { "jpg", "image/jpeg" },
00117    { "js", "application/x-javascript" },
00118    { "wav", "audio/x-wav" },
00119    { "mp3", "audio/mpeg" },
00120    { "svg", "image/svg+xml" },
00121    { "svgz", "image/svg+xml" },
00122    { "gif", "image/gif" },
00123    { "html", "text/html" },
00124    { "htm", "text/html" },
00125    { "css", "text/css" },
00126    { "cnf", "text/plain" },
00127    { "cfg", "text/plain" },
00128    { "bin", "application/octet-stream" },
00129    { "sbn", "application/octet-stream" },
00130    { "ld", "application/octet-stream" },
00131 };
00132 
00133 struct http_uri_redirect {
00134    AST_LIST_ENTRY(http_uri_redirect) entry;
00135    char *dest;
00136    char target[0];
00137 };
00138 
00139 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
00140 
00141 static const struct ast_cfhttp_methods_text {
00142    enum ast_http_method method;
00143    const char *text;
00144 } ast_http_methods_text[] = {
00145    { AST_HTTP_UNKNOWN,     "UNKNOWN" },
00146    { AST_HTTP_GET,         "GET" },
00147    { AST_HTTP_POST,        "POST" },
00148    { AST_HTTP_HEAD,        "HEAD" },
00149    { AST_HTTP_PUT,         "PUT" },
00150 };
00151 
00152 const char *ast_get_http_method(enum ast_http_method method)
00153 {
00154    int x;
00155 
00156    for (x = 0; x < ARRAY_LEN(ast_http_methods_text); x++) {
00157       if (ast_http_methods_text[x].method == method) {
00158          return ast_http_methods_text[x].text;
00159       }
00160    }
00161 
00162    return NULL;
00163 }
00164 
00165 const char *ast_http_ftype2mtype(const char *ftype)
00166 {
00167    int x;
00168 
00169    if (ftype) {
00170       for (x = 0; x < ARRAY_LEN(mimetypes); x++) {
00171          if (!strcasecmp(ftype, mimetypes[x].ext)) {
00172             return mimetypes[x].mtype;
00173          }
00174       }
00175    }
00176    return NULL;
00177 }
00178 
00179 uint32_t ast_http_manid_from_vars(struct ast_variable *headers)
00180 {
00181    uint32_t mngid = 0;
00182    struct ast_variable *v, *cookies;
00183 
00184    cookies = ast_http_get_cookies(headers);
00185    for (v = cookies; v; v = v->next) {
00186       if (!strcasecmp(v->name, "mansession_id")) {
00187          sscanf(v->value, "%30x", &mngid);
00188          break;
00189       }
00190    }
00191    ast_variables_destroy(cookies);
00192    return mngid;
00193 }
00194 
00195 void ast_http_prefix(char *buf, int len)
00196 {
00197    if (buf) {
00198       ast_copy_string(buf, prefix, len);
00199    }
00200 }
00201 
00202 static int static_callback(struct ast_tcptls_session_instance *ser,
00203    const struct ast_http_uri *urih, const char *uri,
00204    enum ast_http_method method, struct ast_variable *get_vars,
00205    struct ast_variable *headers)
00206 {
00207    char *path;
00208    const char *ftype;
00209    const char *mtype;
00210    char wkspace[80];
00211    struct stat st;
00212    int len;
00213    int fd;
00214    struct ast_str *http_header;
00215    struct timeval tv;
00216    struct ast_tm tm;
00217    char timebuf[80], etag[23];
00218    struct ast_variable *v;
00219    int not_modified = 0;
00220 
00221    if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
00222       ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
00223       return -1;
00224    }
00225 
00226    /* Yuck.  I'm not really sold on this, but if you don't deliver static content it makes your configuration
00227       substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
00228    if (!enablestatic || ast_strlen_zero(uri)) {
00229       goto out403;
00230    }
00231 
00232    /* Disallow any funny filenames at all (checking first character only??) */
00233    if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0])) {
00234       goto out403;
00235    }
00236 
00237    if (strstr(uri, "/..")) {
00238       goto out403;
00239    }
00240 
00241    if ((ftype = strrchr(uri, '.'))) {
00242       ftype++;
00243    }
00244 
00245    if (!(mtype = ast_http_ftype2mtype(ftype))) {
00246       snprintf(wkspace, sizeof(wkspace), "text/%s", S_OR(ftype, "plain"));
00247       mtype = wkspace;
00248    }
00249 
00250    /* Cap maximum length */
00251    if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
00252       goto out403;
00253    }
00254 
00255    path = ast_alloca(len);
00256    sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
00257    if (stat(path, &st)) {
00258       goto out404;
00259    }
00260 
00261    if (S_ISDIR(st.st_mode)) {
00262       goto out404;
00263    }
00264 
00265    if (strstr(path, "/private/") && !astman_is_authed(ast_http_manid_from_vars(headers))) {
00266       goto out403;
00267    }
00268 
00269    fd = open(path, O_RDONLY);
00270    if (fd < 0) {
00271       goto out403;
00272    }
00273 
00274    /* make "Etag:" http header value */
00275    snprintf(etag, sizeof(etag), "\"%ld\"", (long)st.st_mtime);
00276 
00277    /* make "Last-Modified:" http header value */
00278    tv.tv_sec = st.st_mtime;
00279    tv.tv_usec = 0;
00280    ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&tv, &tm, "GMT"));
00281 
00282    /* check received "If-None-Match" request header and Etag value for file */
00283    for (v = headers; v; v = v->next) {
00284       if (!strcasecmp(v->name, "If-None-Match")) {
00285          if (!strcasecmp(v->value, etag)) {
00286             not_modified = 1;
00287          }
00288          break;
00289       }
00290    }
00291 
00292    if ( (http_header = ast_str_create(255)) == NULL) {
00293       close(fd);
00294       return -1;
00295    }
00296 
00297    ast_str_set(&http_header, 0, "Content-type: %s\r\n"
00298       "ETag: %s\r\n"
00299       "Last-Modified: %s\r\n",
00300       mtype,
00301       etag,
00302       timebuf);
00303 
00304    /* ast_http_send() frees http_header, so we don't need to do it before returning */
00305    if (not_modified) {
00306       ast_http_send(ser, method, 304, "Not Modified", http_header, NULL, 0, 1);
00307    } else {
00308       ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 1); /* static content flag is set */
00309    }
00310    close(fd);
00311    return 0;
00312 
00313 out404:
00314    ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
00315    return -1;
00316 
00317 out403:
00318    ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
00319    return -1;
00320 }
00321 
00322 static int httpstatus_callback(struct ast_tcptls_session_instance *ser,
00323    const struct ast_http_uri *urih, const char *uri,
00324    enum ast_http_method method, struct ast_variable *get_vars,
00325    struct ast_variable *headers)
00326 {
00327    struct ast_str *out;
00328    struct ast_variable *v, *cookies = NULL;
00329 
00330    if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
00331       ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
00332       return -1;
00333    }
00334 
00335    if ( (out = ast_str_create(512)) == NULL) {
00336       return -1;
00337    }
00338 
00339    ast_str_append(&out, 0,
00340       "<title>Asterisk HTTP Status</title>\r\n"
00341       "<body bgcolor=\"#ffffff\">\r\n"
00342       "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
00343       "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
00344 
00345    ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
00346    ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
00347              ast_sockaddr_stringify_addr(&http_desc.old_address));
00348    ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%s</b></td></tr>\r\n",
00349              ast_sockaddr_stringify_port(&http_desc.old_address));
00350    if (http_tls_cfg.enabled) {
00351       ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%s</b></td></tr>\r\n",
00352                 ast_sockaddr_stringify_port(&https_desc.old_address));
00353    }
00354    ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00355    for (v = get_vars; v; v = v->next) {
00356       ast_str_append(&out, 0, "<tr><td><i>Submitted GET Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
00357    }
00358    ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00359 
00360    cookies = ast_http_get_cookies(headers);
00361    for (v = cookies; v; v = v->next) {
00362       ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
00363    }
00364    ast_variables_destroy(cookies);
00365 
00366    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");
00367    ast_http_send(ser, method, 200, NULL, NULL, out, 0, 0);
00368    return 0;
00369 }
00370 
00371 static struct ast_http_uri statusuri = {
00372    .callback = httpstatus_callback,
00373    .description = "Asterisk HTTP General Status",
00374    .uri = "httpstatus",
00375    .has_subtree = 0,
00376    .data = NULL,
00377    .key = __FILE__,
00378 };
00379 
00380 static struct ast_http_uri staticuri = {
00381    .callback = static_callback,
00382    .description = "Asterisk HTTP Static Delivery",
00383    .uri = "static",
00384    .has_subtree = 1,
00385    .data = NULL,
00386    .key= __FILE__,
00387 };
00388 
00389 
00390 /* send http/1.1 response */
00391 /* free content variable and close socket*/
00392 void ast_http_send(struct ast_tcptls_session_instance *ser,
00393    enum ast_http_method method, int status_code, const char *status_title,
00394    struct ast_str *http_header, struct ast_str *out, const int fd,
00395    unsigned int static_content)
00396 {
00397    struct timeval now = ast_tvnow();
00398    struct ast_tm tm;
00399    char timebuf[80];
00400    int content_length = 0;
00401 
00402    if (!ser || 0 == ser->f) {
00403       return;
00404    }
00405 
00406    ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&now, &tm, "GMT"));
00407 
00408    /* calc content length */
00409    if (out) {
00410       content_length += ast_str_strlen(out);
00411    }
00412 
00413    if (fd) {
00414       content_length += lseek(fd, 0, SEEK_END);
00415       lseek(fd, 0, SEEK_SET);
00416    }
00417 
00418    /* send http header */
00419    fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
00420       "Server: Asterisk/%s\r\n"
00421       "Date: %s\r\n"
00422       "Connection: close\r\n"
00423       "%s"
00424       "Content-Length: %d\r\n"
00425       "%s"
00426       "\r\n",
00427       status_code, status_title ? status_title : "OK",
00428       ast_get_version(),
00429       timebuf,
00430       static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
00431       content_length,
00432       http_header ? ast_str_buffer(http_header) : ""
00433       );
00434 
00435    /* send content */
00436    if (method != AST_HTTP_HEAD || status_code >= 400) {
00437       if (out && ast_str_strlen(out)) {
00438          if (fwrite(ast_str_buffer(out), ast_str_strlen(out), 1, ser->f) != 1) {
00439             ast_log(LOG_ERROR, "fwrite() failed: %s\n", strerror(errno));
00440          }
00441       }
00442 
00443       if (fd) {
00444          char buf[256];
00445          int len;
00446          while ((len = read(fd, buf, sizeof(buf))) > 0) {
00447             if (fwrite(buf, len, 1, ser->f) != 1) {
00448                ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00449                break;
00450             }
00451          }
00452       }
00453    }
00454 
00455    if (http_header) {
00456       ast_free(http_header);
00457    }
00458    if (out) {
00459       ast_free(out);
00460    }
00461 
00462    ast_tcptls_close_session_file(ser);
00463    return;
00464 }
00465 
00466 /* Send http "401 Unauthorized" responce and close socket*/
00467 void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm,
00468    const unsigned long nonce, const unsigned long opaque, int stale,
00469    const char *text)
00470 {
00471    struct ast_str *http_headers = ast_str_create(128);
00472    struct ast_str *out = ast_str_create(512);
00473 
00474    if (!http_headers || !out) {
00475       ast_free(http_headers);
00476       ast_free(out);
00477       return;
00478    }
00479 
00480    ast_str_set(&http_headers, 0,
00481       "WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
00482       "Content-type: text/html\r\n",
00483       realm ? realm : "Asterisk",
00484       nonce,
00485       opaque,
00486       stale ? ", stale=true" : "");
00487 
00488    ast_str_set(&out, 0,
00489       "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
00490       "<html><head>\r\n"
00491       "<title>401 Unauthorized</title>\r\n"
00492       "</head><body>\r\n"
00493       "<h1>401 Unauthorized</h1>\r\n"
00494       "<p>%s</p>\r\n"
00495       "<hr />\r\n"
00496       "<address>Asterisk Server</address>\r\n"
00497       "</body></html>\r\n",
00498       text ? text : "");
00499 
00500    ast_http_send(ser, AST_HTTP_UNKNOWN, 401, "Unauthorized", http_headers, out, 0, 0);
00501    return;
00502 }
00503 
00504 /* send http error response and close socket*/
00505 void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code, const char *status_title, const char *text)
00506 {
00507    struct ast_str *http_headers = ast_str_create(40);
00508    struct ast_str *out = ast_str_create(256);
00509 
00510    if (!http_headers || !out) {
00511       ast_free(http_headers);
00512       ast_free(out);
00513       return;
00514    }
00515 
00516    ast_str_set(&http_headers, 0, "Content-type: text/html\r\n");
00517 
00518    ast_str_set(&out, 0,
00519       "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
00520       "<html><head>\r\n"
00521       "<title>%d %s</title>\r\n"
00522       "</head><body>\r\n"
00523       "<h1>%s</h1>\r\n"
00524       "<p>%s</p>\r\n"
00525       "<hr />\r\n"
00526       "<address>Asterisk Server</address>\r\n"
00527       "</body></html>\r\n",
00528          status_code, status_title, status_title, text);
00529 
00530    ast_http_send(ser, AST_HTTP_UNKNOWN, status_code, status_title, http_headers, out, 0, 0);
00531    return;
00532 }
00533 
00534 /*! \brief
00535  * Link the new uri into the list.
00536  *
00537  * They are sorted by length of
00538  * the string, not alphabetically. Duplicate entries are not replaced,
00539  * but the insertion order (using <= and not just <) makes sure that
00540  * more recent insertions hide older ones.
00541  * On a lookup, we just scan the list and stop at the first matching entry.
00542  */
00543 int ast_http_uri_link(struct ast_http_uri *urih)
00544 {
00545    struct ast_http_uri *uri;
00546    int len = strlen(urih->uri);
00547 
00548    AST_RWLIST_WRLOCK(&uris);
00549 
00550    if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
00551       AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
00552       AST_RWLIST_UNLOCK(&uris);
00553       return 0;
00554    }
00555 
00556    AST_RWLIST_TRAVERSE(&uris, uri, entry) {
00557       if (AST_RWLIST_NEXT(uri, entry) &&
00558          strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
00559          AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
00560          AST_RWLIST_UNLOCK(&uris);
00561 
00562          return 0;
00563       }
00564    }
00565 
00566    AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
00567 
00568    AST_RWLIST_UNLOCK(&uris);
00569 
00570    return 0;
00571 }
00572 
00573 void ast_http_uri_unlink(struct ast_http_uri *urih)
00574 {
00575    AST_RWLIST_WRLOCK(&uris);
00576    AST_RWLIST_REMOVE(&uris, urih, entry);
00577    AST_RWLIST_UNLOCK(&uris);
00578 }
00579 
00580 void ast_http_uri_unlink_all_with_key(const char *key)
00581 {
00582    struct ast_http_uri *urih;
00583    AST_RWLIST_WRLOCK(&uris);
00584    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&uris, urih, entry) {
00585       if (!strcmp(urih->key, key)) {
00586          AST_RWLIST_REMOVE_CURRENT(entry);
00587          if (urih->dmallocd) {
00588             ast_free(urih->data);
00589          }
00590          if (urih->mallocd) {
00591             ast_free(urih);
00592          }
00593       }
00594    }
00595    AST_RWLIST_TRAVERSE_SAFE_END;
00596    AST_RWLIST_UNLOCK(&uris);
00597 }
00598 
00599 #define MAX_POST_CONTENT 1025
00600 
00601 /*
00602  * get post variables from client Request Entity-Body, if content type is
00603  * application/x-www-form-urlencoded
00604  */
00605 struct ast_variable *ast_http_get_post_vars(
00606    struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
00607 {
00608    int content_length = 0;
00609    struct ast_variable *v, *post_vars=NULL, *prev = NULL;
00610    char *buf, *var, *val;
00611    int res;
00612 
00613    for (v = headers; v; v = v->next) {
00614       if (!strcasecmp(v->name, "Content-Type")) {
00615          if (strcasecmp(v->value, "application/x-www-form-urlencoded")) {
00616             return NULL;
00617          }
00618          break;
00619       }
00620    }
00621 
00622    for (v = headers; v; v = v->next) {
00623       if (!strcasecmp(v->name, "Content-Length")) {
00624          content_length = atoi(v->value);
00625          break;
00626       }
00627    }
00628 
00629    if (content_length <= 0) {
00630       return NULL;
00631    }
00632 
00633    if (content_length > MAX_POST_CONTENT - 1) {
00634       ast_log(LOG_WARNING, "Excessively long HTTP content. %d is greater than our max of %d\n",
00635             content_length, MAX_POST_CONTENT);
00636       ast_http_send(ser, AST_HTTP_POST, 413, "Request Entity Too Large", NULL, NULL, 0, 0);
00637       return NULL;
00638    }
00639 
00640    buf = ast_malloc(content_length + 1);
00641    if (!buf) {
00642       return NULL;
00643    }
00644 
00645    res = fread(buf, 1, content_length, ser->f);
00646    if (res < content_length) {
00647       /* Error, distinguishable by ferror() or feof(), but neither
00648        * is good. */
00649       goto done;
00650    }
00651    buf[content_length] = '\0';
00652 
00653    while ((val = strsep(&buf, "&"))) {
00654       var = strsep(&val, "=");
00655       if (val) {
00656          ast_uri_decode(val, ast_uri_http_legacy);
00657       } else  {
00658          val = "";
00659       }
00660       ast_uri_decode(var, ast_uri_http_legacy);
00661       if ((v = ast_variable_new(var, val, ""))) {
00662          if (post_vars) {
00663             prev->next = v;
00664          } else {
00665             post_vars = v;
00666          }
00667          prev = v;
00668       }
00669    }
00670 
00671 done:
00672    ast_free(buf);
00673    return post_vars;
00674 }
00675 
00676 static int handle_uri(struct ast_tcptls_session_instance *ser, char *uri,
00677    enum ast_http_method method, struct ast_variable *headers)
00678 {
00679    char *c;
00680    int res = -1;
00681    char *params = uri;
00682    struct ast_http_uri *urih = NULL;
00683    int l;
00684    struct ast_variable *get_vars = NULL, *v, *prev = NULL;
00685    struct http_uri_redirect *redirect;
00686 
00687    ast_debug(2, "HTTP Request URI is %s \n", uri);
00688 
00689    strsep(&params, "?");
00690    /* Extract arguments from the request and store them in variables. */
00691    if (params) {
00692       char *var, *val;
00693 
00694       while ((val = strsep(&params, "&"))) {
00695          var = strsep(&val, "=");
00696          if (val) {
00697             ast_uri_decode(val, ast_uri_http_legacy);
00698          } else  {
00699             val = "";
00700          }
00701          ast_uri_decode(var, ast_uri_http_legacy);
00702          if ((v = ast_variable_new(var, val, ""))) {
00703             if (get_vars) {
00704                prev->next = v;
00705             } else {
00706                get_vars = v;
00707             }
00708             prev = v;
00709          }
00710       }
00711    }
00712    ast_uri_decode(uri, ast_uri_http_legacy);
00713 
00714    AST_RWLIST_RDLOCK(&uri_redirects);
00715    AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
00716       if (!strcasecmp(uri, redirect->target)) {
00717          struct ast_str *http_header = ast_str_create(128);
00718          ast_str_set(&http_header, 0, "Location: %s\r\n", redirect->dest);
00719          ast_http_send(ser, method, 302, "Moved Temporarily", http_header, NULL, 0, 0);
00720 
00721          break;
00722       }
00723    }
00724    AST_RWLIST_UNLOCK(&uri_redirects);
00725    if (redirect) {
00726       goto cleanup;
00727    }
00728 
00729    /* We want requests to start with the (optional) prefix and '/' */
00730    l = strlen(prefix);
00731    if (!strncasecmp(uri, prefix, l) && uri[l] == '/') {
00732       uri += l + 1;
00733       /* scan registered uris to see if we match one. */
00734       AST_RWLIST_RDLOCK(&uris);
00735       AST_RWLIST_TRAVERSE(&uris, urih, entry) {
00736          ast_debug(2, "match request [%s] with handler [%s] len %d\n", uri, urih->uri, l);
00737          l = strlen(urih->uri);
00738          c = uri + l;   /* candidate */
00739          if (strncasecmp(urih->uri, uri, l) /* no match */
00740              || (*c && *c != '/')) { /* substring */
00741             continue;
00742          }
00743          if (*c == '/') {
00744             c++;
00745          }
00746          if (!*c || urih->has_subtree) {
00747             uri = c;
00748             break;
00749          }
00750       }
00751       AST_RWLIST_UNLOCK(&uris);
00752    }
00753    if (urih) {
00754       res = urih->callback(ser, urih, uri, method, get_vars, headers);
00755    } else {
00756       ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
00757    }
00758 
00759 cleanup:
00760    ast_variables_destroy(get_vars);
00761    return res;
00762 }
00763 
00764 #ifdef DO_SSL
00765 #if defined(HAVE_FUNOPEN)
00766 #define HOOK_T int
00767 #define LEN_T int
00768 #else
00769 #define HOOK_T ssize_t
00770 #define LEN_T size_t
00771 #endif
00772 
00773 /*!
00774  * replacement read/write functions for SSL support.
00775  * We use wrappers rather than SSL_read/SSL_write directly so
00776  * we can put in some debugging.
00777  */
00778 /*static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
00779 {
00780    int i = SSL_read(cookie, buf, len-1);
00781 #if 0
00782    if (i >= 0)
00783       buf[i] = '\0';
00784    ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
00785 #endif
00786    return i;
00787 }
00788 
00789 static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
00790 {
00791 #if 0
00792    char *s = ast_alloca(len+1);
00793    strncpy(s, buf, len);
00794    s[len] = '\0';
00795    ast_verbose("ssl write size %d <%s>\n", (int)len, s);
00796 #endif
00797    return SSL_write(cookie, buf, len);
00798 }
00799 
00800 static int ssl_close(void *cookie)
00801 {
00802    close(SSL_get_fd(cookie));
00803    SSL_shutdown(cookie);
00804    SSL_free(cookie);
00805    return 0;
00806 }*/
00807 #endif   /* DO_SSL */
00808 
00809 static struct ast_variable *parse_cookies(const char *cookies)
00810 {
00811    char *parse = ast_strdupa(cookies);
00812    char *cur;
00813    struct ast_variable *vars = NULL, *var;
00814 
00815    while ((cur = strsep(&parse, ";"))) {
00816       char *name, *val;
00817 
00818       name = val = cur;
00819       strsep(&val, "=");
00820 
00821       if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00822          continue;
00823       }
00824 
00825       name = ast_strip(name);
00826       val = ast_strip_quoted(val, "\"", "\"");
00827 
00828       if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00829          continue;
00830       }
00831 
00832       ast_debug(1, "HTTP Cookie, Name: '%s'  Value: '%s'\n", name, val);
00833 
00834       var = ast_variable_new(name, val, __FILE__);
00835       var->next = vars;
00836       vars = var;
00837    }
00838 
00839    return vars;
00840 }
00841 
00842 /* get cookie from Request headers */
00843 struct ast_variable *ast_http_get_cookies(struct ast_variable *headers)
00844 {
00845    struct ast_variable *v, *cookies = NULL;
00846 
00847    for (v = headers; v; v = v->next) {
00848       if (!strcasecmp(v->name, "Cookie")) {
00849          ast_variables_destroy(cookies);
00850          cookies = parse_cookies(v->value);
00851       }
00852    }
00853    return cookies;
00854 }
00855 
00856 /*! Limit the number of request headers in case the sender is being ridiculous. */
00857 #define MAX_HTTP_REQUEST_HEADERS 100
00858 
00859 static void *httpd_helper_thread(void *data)
00860 {
00861    char buf[4096];
00862    char header_line[4096];
00863    struct ast_tcptls_session_instance *ser = data;
00864    struct ast_variable *headers = NULL;
00865    struct ast_variable *tail = headers;
00866    char *uri, *method;
00867    enum ast_http_method http_method = AST_HTTP_UNKNOWN;
00868    int remaining_headers;
00869    int flags;
00870    struct protoent *p;
00871 
00872    if (ast_atomic_fetchadd_int(&session_count, +1) >= session_limit) {
00873       goto done;
00874    }
00875 
00876    /* here we set TCP_NODELAY on the socket to disable Nagle's algorithm.
00877     * This is necessary to prevent delays (caused by buffering) as we
00878     * write to the socket in bits and pieces. */
00879    p = getprotobyname("tcp");
00880    if (p) {
00881       int arg = 1;
00882       if( setsockopt(ser->fd, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
00883          ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection: %s\n", strerror(errno));
00884          ast_log(LOG_WARNING, "Some HTTP requests may be slow to respond.\n");
00885       }
00886    } else {
00887       ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection, getprotobyname(\"tcp\") failed\n");
00888       ast_log(LOG_WARNING, "Some HTTP requests may be slow to respond.\n");
00889    }
00890 
00891    /* make sure socket is non-blocking */
00892    flags = fcntl(ser->fd, F_GETFL);
00893    flags |= O_NONBLOCK;
00894    fcntl(ser->fd, F_SETFL, flags);
00895 
00896    /* We can let the stream wait for data to arrive. */
00897    ast_tcptls_stream_set_exclusive_input(ser->stream_cookie, 1);
00898 
00899    ast_tcptls_stream_set_timeout_inactivity(ser->stream_cookie, session_inactivity);
00900 
00901    if (!fgets(buf, sizeof(buf), ser->f) || feof(ser->f)) {
00902       goto done;
00903    }
00904 
00905    /* Get method */
00906    method = ast_skip_blanks(buf);
00907    uri = ast_skip_nonblanks(method);
00908    if (*uri) {
00909       *uri++ = '\0';
00910    }
00911 
00912    if (!strcasecmp(method,"GET")) {
00913       http_method = AST_HTTP_GET;
00914    } else if (!strcasecmp(method,"POST")) {
00915       http_method = AST_HTTP_POST;
00916    } else if (!strcasecmp(method,"HEAD")) {
00917       http_method = AST_HTTP_HEAD;
00918    } else if (!strcasecmp(method,"PUT")) {
00919       http_method = AST_HTTP_PUT;
00920    }
00921 
00922    uri = ast_skip_blanks(uri);   /* Skip white space */
00923 
00924    if (*uri) {       /* terminate at the first blank */
00925       char *c = ast_skip_nonblanks(uri);
00926 
00927       if (*c) {
00928          *c = '\0';
00929       }
00930    } else {
00931       ast_http_error(ser, 400, "Bad Request", "Invalid Request");
00932       goto done;
00933    }
00934 
00935    /* process "Request Headers" lines */
00936    remaining_headers = MAX_HTTP_REQUEST_HEADERS;
00937    for (;;) {
00938       char *name;
00939       char *value;
00940 
00941       if (!fgets(header_line, sizeof(header_line), ser->f) || feof(ser->f)) {
00942          ast_http_error(ser, 400, "Bad Request", "Timeout");
00943          goto done;
00944       }
00945 
00946       /* Trim trailing characters */
00947       ast_trim_blanks(header_line);
00948       if (ast_strlen_zero(header_line)) {
00949          /* A blank line ends the request header section. */
00950          break;
00951       }
00952 
00953       value = header_line;
00954       name = strsep(&value, ":");
00955       if (!value) {
00956          continue;
00957       }
00958 
00959       value = ast_skip_blanks(value);
00960       if (ast_strlen_zero(value) || ast_strlen_zero(name)) {
00961          continue;
00962       }
00963 
00964       ast_trim_blanks(name);
00965 
00966       if (!remaining_headers--) {
00967          /* Too many headers. */
00968          ast_http_error(ser, 413, "Request Entity Too Large", "Too many headers");
00969          goto done;
00970       }
00971       if (!headers) {
00972          headers = ast_variable_new(name, value, __FILE__);
00973          tail = headers;
00974       } else {
00975          tail->next = ast_variable_new(name, value, __FILE__);
00976          tail = tail->next;
00977       }
00978       if (!tail) {
00979          /*
00980           * Variable allocation failure.
00981           * Try to make some room.
00982           */
00983          ast_variables_destroy(headers);
00984          headers = NULL;
00985 
00986          ast_http_error(ser, 500, "Server Error", "Out of memory");
00987          goto done;
00988       }
00989    }
00990 
00991    handle_uri(ser, uri, http_method, headers);
00992 
00993 done:
00994    ast_atomic_fetchadd_int(&session_count, -1);
00995 
00996    /* clean up all the header information */
00997    ast_variables_destroy(headers);
00998 
00999    if (ser->f) {
01000       ast_tcptls_close_session_file(ser);
01001    }
01002    ao2_ref(ser, -1);
01003    ser = NULL;
01004    return NULL;
01005 }
01006 
01007 /*!
01008  * \brief Add a new URI redirect
01009  * The entries in the redirect list are sorted by length, just like the list
01010  * of URI handlers.
01011  */
01012 static void add_redirect(const char *value)
01013 {
01014    char *target, *dest;
01015    struct http_uri_redirect *redirect, *cur;
01016    unsigned int target_len;
01017    unsigned int total_len;
01018 
01019    dest = ast_strdupa(value);
01020    dest = ast_skip_blanks(dest);
01021    target = strsep(&dest, " ");
01022    target = ast_skip_blanks(target);
01023    target = strsep(&target, " "); /* trim trailing whitespace */
01024 
01025    if (!dest) {
01026       ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
01027       return;
01028    }
01029 
01030    target_len = strlen(target) + 1;
01031    total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
01032 
01033    if (!(redirect = ast_calloc(1, total_len))) {
01034       return;
01035    }
01036    redirect->dest = redirect->target + target_len;
01037    strcpy(redirect->target, target);
01038    strcpy(redirect->dest, dest);
01039 
01040    AST_RWLIST_WRLOCK(&uri_redirects);
01041 
01042    target_len--; /* So we can compare directly with strlen() */
01043    if (AST_RWLIST_EMPTY(&uri_redirects)
01044       || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
01045       AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
01046       AST_RWLIST_UNLOCK(&uri_redirects);
01047 
01048       return;
01049    }
01050 
01051    AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
01052       if (AST_RWLIST_NEXT(cur, entry)
01053          && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
01054          AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
01055          AST_RWLIST_UNLOCK(&uri_redirects);
01056          return;
01057       }
01058    }
01059 
01060    AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
01061 
01062    AST_RWLIST_UNLOCK(&uri_redirects);
01063 }
01064 
01065 static int __ast_http_load(int reload)
01066 {
01067    struct ast_config *cfg;
01068    struct ast_variable *v;
01069    int enabled=0;
01070    int newenablestatic=0;
01071    char newprefix[MAX_PREFIX] = "";
01072    struct http_uri_redirect *redirect;
01073    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01074    uint32_t bindport = DEFAULT_PORT;
01075    RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);
01076    int num_addrs = 0;
01077    int http_tls_was_enabled = 0;
01078 
01079    cfg = ast_config_load2("http.conf", "http", config_flags);
01080    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01081       return 0;
01082    }
01083 
01084    http_tls_was_enabled = (reload && http_tls_cfg.enabled);
01085 
01086    http_tls_cfg.enabled = 0;
01087    if (http_tls_cfg.certfile) {
01088       ast_free(http_tls_cfg.certfile);
01089    }
01090    http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
01091 
01092    if (http_tls_cfg.pvtfile) {
01093       ast_free(http_tls_cfg.pvtfile);
01094    }
01095    http_tls_cfg.pvtfile = ast_strdup("");
01096 
01097    if (http_tls_cfg.cipher) {
01098       ast_free(http_tls_cfg.cipher);
01099    }
01100    http_tls_cfg.cipher = ast_strdup("");
01101 
01102    AST_RWLIST_WRLOCK(&uri_redirects);
01103    while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
01104       ast_free(redirect);
01105    }
01106    AST_RWLIST_UNLOCK(&uri_redirects);
01107 
01108    ast_sockaddr_setnull(&https_desc.local_address);
01109 
01110    session_limit = DEFAULT_SESSION_LIMIT;
01111    session_inactivity = DEFAULT_SESSION_INACTIVITY;
01112 
01113    if (cfg) {
01114       v = ast_variable_browse(cfg, "general");
01115       for (; v; v = v->next) {
01116 
01117          /* read tls config options while preventing unsupported options from being set */
01118          if (strcasecmp(v->name, "tlscafile")
01119             && strcasecmp(v->name, "tlscapath")
01120             && strcasecmp(v->name, "tlscadir")
01121             && strcasecmp(v->name, "tlsverifyclient")
01122             && strcasecmp(v->name, "tlsdontverifyserver")
01123             && strcasecmp(v->name, "tlsclientmethod")
01124             && strcasecmp(v->name, "sslclientmethod")
01125             && strcasecmp(v->name, "tlscipher")
01126             && strcasecmp(v->name, "sslcipher")
01127             && !ast_tls_read_conf(&http_tls_cfg, &https_desc, v->name, v->value)) {
01128             continue;
01129          }
01130 
01131          if (!strcasecmp(v->name, "enabled")) {
01132             enabled = ast_true(v->value);
01133          } else if (!strcasecmp(v->name, "enablestatic")) {
01134             newenablestatic = ast_true(v->value);
01135          } else if (!strcasecmp(v->name, "bindport")) {
01136             if (ast_parse_arg(v->value, PARSE_UINT32 | PARSE_IN_RANGE | PARSE_DEFAULT, &bindport, DEFAULT_PORT, 0, 65535)) {
01137                ast_log(LOG_WARNING, "Invalid port %s specified. Using default port %"PRId32, v->value, DEFAULT_PORT);
01138             }
01139          } else if (!strcasecmp(v->name, "bindaddr")) {
01140             if (!(num_addrs = ast_sockaddr_resolve(&addrs, v->value, 0, AST_AF_UNSPEC))) {
01141                ast_log(LOG_WARNING, "Invalid bind address %s\n", v->value);
01142             }
01143          } else if (!strcasecmp(v->name, "prefix")) {
01144             if (!ast_strlen_zero(v->value)) {
01145                newprefix[0] = '/';
01146                ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
01147             } else {
01148                newprefix[0] = '\0';
01149             }
01150          } else if (!strcasecmp(v->name, "redirect")) {
01151             add_redirect(v->value);
01152          } else if (!strcasecmp(v->name, "sessionlimit")) {
01153             if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
01154                      &session_limit, DEFAULT_SESSION_LIMIT, 1, INT_MAX)) {
01155                ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
01156                      v->name, v->value, v->lineno);
01157             }
01158          } else if (!strcasecmp(v->name, "session_inactivity")) {
01159             if (ast_parse_arg(v->value, PARSE_INT32 |PARSE_DEFAULT | PARSE_IN_RANGE,
01160                &session_inactivity, DEFAULT_SESSION_INACTIVITY, 1, INT_MAX)) {
01161                ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
01162                   v->name, v->value, v->lineno);
01163             }
01164          } else {
01165             ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
01166          }
01167       }
01168 
01169       ast_config_destroy(cfg);
01170    }
01171 
01172    if (strcmp(prefix, newprefix)) {
01173       ast_copy_string(prefix, newprefix, sizeof(prefix));
01174    }
01175    enablestatic = newenablestatic;
01176 
01177    if (num_addrs && enabled) {
01178       int i;
01179       for (i = 0; i < num_addrs; ++i) {
01180          ast_sockaddr_copy(&http_desc.local_address, &addrs[i]);
01181          if (!ast_sockaddr_port(&http_desc.local_address)) {
01182             ast_sockaddr_set_port(&http_desc.local_address, bindport);
01183          }
01184          ast_tcptls_server_start(&http_desc);
01185          if (http_desc.accept_fd == -1) {
01186             ast_log(LOG_WARNING, "Failed to start HTTP server for address %s\n", ast_sockaddr_stringify(&addrs[i]));
01187             ast_sockaddr_setnull(&http_desc.local_address);
01188          } else {
01189             ast_verb(1, "Bound HTTP server to address %s\n", ast_sockaddr_stringify(&addrs[i]));
01190             break;
01191          }
01192       }
01193       /* When no specific TLS bindaddr is specified, we just use
01194        * the non-TLS bindaddress here.
01195        */
01196       if (ast_sockaddr_isnull(&https_desc.local_address) && http_desc.accept_fd != -1) {
01197          ast_sockaddr_copy(&https_desc.local_address, &https_desc.local_address);
01198          /* Of course, we can't use the same port though.
01199           * Since no bind address was specified, we just use the
01200           * default TLS port
01201           */
01202          ast_sockaddr_set_port(&https_desc.local_address, DEFAULT_TLS_PORT);
01203       }
01204    }
01205    if (http_tls_was_enabled && !http_tls_cfg.enabled) {
01206       ast_tcptls_server_stop(&https_desc);
01207    } else if (http_tls_cfg.enabled && !ast_sockaddr_isnull(&https_desc.local_address)) {
01208       /* We can get here either because a TLS-specific address was specified
01209        * or because we copied the non-TLS address here. In the case where
01210        * we read an explicit address from the config, there may have been
01211        * no port specified, so we'll just use the default TLS port.
01212        */
01213       if (!ast_sockaddr_port(&https_desc.local_address)) {
01214          ast_sockaddr_set_port(&https_desc.local_address, DEFAULT_TLS_PORT);
01215       }
01216       if (ast_ssl_setup(https_desc.tls_cfg)) {
01217          ast_tcptls_server_start(&https_desc);
01218       }
01219    }
01220 
01221    return 0;
01222 }
01223 
01224 static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01225 {
01226    struct ast_http_uri *urih;
01227    struct http_uri_redirect *redirect;
01228 
01229    switch (cmd) {
01230    case CLI_INIT:
01231       e->command = "http show status";
01232       e->usage =
01233          "Usage: http show status\n"
01234          "       Lists status of internal HTTP engine\n";
01235       return NULL;
01236    case CLI_GENERATE:
01237       return NULL;
01238    }
01239 
01240    if (a->argc != 3) {
01241       return CLI_SHOWUSAGE;
01242    }
01243    ast_cli(a->fd, "HTTP Server Status:\n");
01244    ast_cli(a->fd, "Prefix: %s\n", prefix);
01245    if (ast_sockaddr_isnull(&http_desc.old_address)) {
01246       ast_cli(a->fd, "Server Disabled\n\n");
01247    } else {
01248       ast_cli(a->fd, "Server Enabled and Bound to %s\n\n",
01249          ast_sockaddr_stringify(&http_desc.old_address));
01250       if (http_tls_cfg.enabled) {
01251          ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s\n\n",
01252             ast_sockaddr_stringify(&https_desc.old_address));
01253       }
01254    }
01255 
01256    ast_cli(a->fd, "Enabled URI's:\n");
01257    AST_RWLIST_RDLOCK(&uris);
01258    if (AST_RWLIST_EMPTY(&uris)) {
01259       ast_cli(a->fd, "None.\n");
01260    } else {
01261       AST_RWLIST_TRAVERSE(&uris, urih, entry)
01262          ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
01263    }
01264    AST_RWLIST_UNLOCK(&uris);
01265 
01266    ast_cli(a->fd, "\nEnabled Redirects:\n");
01267    AST_RWLIST_RDLOCK(&uri_redirects);
01268    AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
01269       ast_cli(a->fd, "  %s => %s\n", redirect->target, redirect->dest);
01270    if (AST_RWLIST_EMPTY(&uri_redirects)) {
01271       ast_cli(a->fd, "  None.\n");
01272    }
01273    AST_RWLIST_UNLOCK(&uri_redirects);
01274 
01275    return CLI_SUCCESS;
01276 }
01277 
01278 int ast_http_reload(void)
01279 {
01280    return __ast_http_load(1);
01281 }
01282 
01283 static struct ast_cli_entry cli_http[] = {
01284    AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
01285 };
01286 
01287 static void http_shutdown(void)
01288 {
01289    struct http_uri_redirect *redirect;
01290    ast_cli_unregister_multiple(cli_http, ARRAY_LEN(cli_http));
01291 
01292    ast_tcptls_server_stop(&http_desc);
01293    if (http_tls_cfg.enabled) {
01294       ast_tcptls_server_stop(&https_desc);
01295    }
01296    ast_free(http_tls_cfg.certfile);
01297    ast_free(http_tls_cfg.pvtfile);
01298    ast_free(http_tls_cfg.cipher);
01299 
01300    ast_http_uri_unlink(&statusuri);
01301    ast_http_uri_unlink(&staticuri);
01302 
01303    AST_RWLIST_WRLOCK(&uri_redirects);
01304    while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
01305       ast_free(redirect);
01306    }
01307    AST_RWLIST_UNLOCK(&uri_redirects);
01308 }
01309 
01310 int ast_http_init(void)
01311 {
01312    ast_http_uri_link(&statusuri);
01313    ast_http_uri_link(&staticuri);
01314    ast_cli_register_multiple(cli_http, ARRAY_LEN(cli_http));
01315    ast_register_atexit(http_shutdown);
01316 
01317    return __ast_http_load(0);
01318 }