Wed Oct 28 11:51:04 2009

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  * \ref AstHTTP - AMI over the http protocol
00029  */
00030 
00031 #include "asterisk.h"
00032 
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211569 $")
00034 
00035 #include <time.h>
00036 #include <sys/time.h>
00037 #include <sys/stat.h>
00038 #include <sys/signal.h>
00039 #include <fcntl.h>
00040 
00041 #include "asterisk/paths.h"   /* use ast_config_AST_DATA_DIR */
00042 #include "asterisk/network.h"
00043 #include "asterisk/cli.h"
00044 #include "asterisk/tcptls.h"
00045 #include "asterisk/http.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/strings.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/stringfields.h"
00050 #include "asterisk/ast_version.h"
00051 #include "asterisk/manager.h"
00052 #include "asterisk/_private.h"
00053 #include "asterisk/astobj2.h"
00054 
00055 #define MAX_PREFIX 80
00056 
00057 /* See http.h for more information about the SSL implementation */
00058 #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
00059 #define  DO_SSL   /* comment in/out if you want to support ssl */
00060 #endif
00061 
00062 static struct ast_tls_config http_tls_cfg;
00063 
00064 static void *httpd_helper_thread(void *arg);
00065 
00066 /*!
00067  * we have up to two accepting threads, one for http, one for https
00068  */
00069 static struct ast_tcptls_session_args http_desc = {
00070    .accept_fd = -1,
00071    .master = AST_PTHREADT_NULL,
00072    .tls_cfg = NULL,
00073    .poll_timeout = -1,
00074    .name = "http server",
00075    .accept_fn = ast_tcptls_server_root,
00076    .worker_fn = httpd_helper_thread,
00077 };
00078 
00079 static struct ast_tcptls_session_args https_desc = {
00080    .accept_fd = -1,
00081    .master = AST_PTHREADT_NULL,
00082    .tls_cfg = &http_tls_cfg,
00083    .poll_timeout = -1,
00084    .name = "https server",
00085    .accept_fn = ast_tcptls_server_root,
00086    .worker_fn = httpd_helper_thread,
00087 };
00088 
00089 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri); /*!< list of supported handlers */
00090 
00091 /* all valid URIs must be prepended by the string in prefix. */
00092 static char prefix[MAX_PREFIX];
00093 static int enablestatic;
00094 
00095 /*! \brief Limit the kinds of files we're willing to serve up */
00096 static struct {
00097    const char *ext;
00098    const char *mtype;
00099 } mimetypes[] = {
00100    { "png", "image/png" },
00101    { "jpg", "image/jpeg" },
00102    { "js", "application/x-javascript" },
00103    { "wav", "audio/x-wav" },
00104    { "mp3", "audio/mpeg" },
00105    { "svg", "image/svg+xml" },
00106    { "svgz", "image/svg+xml" },
00107    { "gif", "image/gif" },
00108 };
00109 
00110 struct http_uri_redirect {
00111    AST_LIST_ENTRY(http_uri_redirect) entry;
00112    char *dest;
00113    char target[0];
00114 };
00115 
00116 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
00117 
00118 static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
00119 {
00120    int x;
00121 
00122    if (ftype) {
00123       for (x = 0; x < ARRAY_LEN(mimetypes); x++) {
00124          if (!strcasecmp(ftype, mimetypes[x].ext)) {
00125             return mimetypes[x].mtype;
00126          }
00127       }
00128    }
00129 
00130    snprintf(wkspace, wkspacelen, "text/%s", S_OR(ftype, "plain"));
00131 
00132    return wkspace;
00133 }
00134 
00135 static uint32_t manid_from_vars(struct ast_variable *sid) {
00136    uint32_t mngid;
00137 
00138    while (sid && strcmp(sid->name, "mansession_id"))
00139       sid = sid->next;
00140 
00141    if (!sid || sscanf(sid->value, "%30x", &mngid) != 1)
00142       return 0;
00143 
00144    return mngid;
00145 }
00146 
00147 void ast_http_prefix(char *buf, int len)
00148 {
00149    if (buf) {
00150       ast_copy_string(buf, prefix, len);
00151    }
00152 }
00153 
00154 static struct ast_str *static_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
00155 {
00156    char *path;
00157    char *ftype;
00158    const char *mtype;
00159    char wkspace[80];
00160    struct stat st;
00161    int len;
00162    int fd;
00163    struct timeval now = ast_tvnow();
00164    char buf[256];
00165    struct ast_tm tm;
00166 
00167    /* Yuck.  I'm not really sold on this, but if you don't deliver static content it makes your configuration 
00168       substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
00169    if (!enablestatic || ast_strlen_zero(uri)) {
00170       goto out403;
00171    }
00172 
00173    /* Disallow any funny filenames at all */
00174    if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0])) {
00175       goto out403;
00176    }
00177 
00178    if (strstr(uri, "/..")) {
00179       goto out403;
00180    }
00181       
00182    if ((ftype = strrchr(uri, '.'))) {
00183       ftype++;
00184    }
00185 
00186    mtype = ftype2mtype(ftype, wkspace, sizeof(wkspace));
00187    
00188    /* Cap maximum length */
00189    if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
00190       goto out403;
00191    }
00192       
00193    path = alloca(len);
00194    sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
00195    if (stat(path, &st)) {
00196       goto out404;
00197    }
00198 
00199    if (S_ISDIR(st.st_mode)) {
00200       goto out404;
00201    }  
00202 
00203    if ((fd = open(path, O_RDONLY)) < 0) {
00204       goto out403;
00205    }
00206 
00207    if (strstr(path, "/private/") && !astman_is_authed(manid_from_vars(vars))) {
00208       goto out403;
00209    }
00210 
00211    ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
00212    fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
00213       "Server: Asterisk/%s\r\n"
00214       "Date: %s\r\n"
00215       "Connection: close\r\n"
00216       "Cache-Control: private\r\n"
00217       "Content-Length: %d\r\n"
00218       "Content-type: %s\r\n\r\n",
00219       ast_get_version(), buf, (int) st.st_size, mtype);
00220 
00221    while ((len = read(fd, buf, sizeof(buf))) > 0) {
00222       if (fwrite(buf, 1, len, ser->f) != len) {
00223          ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00224       }
00225    }
00226 
00227    close(fd);
00228 
00229    return NULL;
00230 
00231 out404:
00232    return ast_http_error((*status = 404),
00233                (*title = ast_strdup("Not Found")),
00234                 NULL, "The requested URL was not found on this server.");
00235 
00236 out403:
00237    return ast_http_error((*status = 403),
00238                (*title = ast_strdup("Access Denied")),
00239                NULL, "You do not have permission to access the requested URL.");
00240 }
00241 
00242 
00243 static struct ast_str *httpstatus_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
00244 {
00245    struct ast_str *out = ast_str_create(512);
00246    struct ast_variable *v;
00247 
00248    if (out == NULL) {
00249       return out;
00250    }
00251 
00252    ast_str_append(&out, 0,
00253              "\r\n"
00254              "<title>Asterisk HTTP Status</title>\r\n"
00255              "<body bgcolor=\"#ffffff\">\r\n"
00256              "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
00257              "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
00258    ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
00259    ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
00260              ast_inet_ntoa(http_desc.old_address.sin_addr));
00261    ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
00262              ntohs(http_desc.old_address.sin_port));
00263 
00264    if (http_tls_cfg.enabled) {
00265       ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
00266                 ntohs(https_desc.old_address.sin_port));
00267    }
00268 
00269    ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00270 
00271    for (v = vars; v; v = v->next) {
00272       if (strncasecmp(v->name, "cookie_", 7)) {
00273          ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
00274       }
00275    }
00276 
00277    ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00278 
00279    for (v = vars; v; v = v->next) {
00280       if (!strncasecmp(v->name, "cookie_", 7)) {
00281          ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
00282       }
00283    }
00284 
00285    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");
00286    return out;
00287 }
00288 
00289 static struct ast_http_uri statusuri = {
00290    .callback = httpstatus_callback,
00291    .description = "Asterisk HTTP General Status",
00292    .uri = "httpstatus",
00293    .supports_get = 1,
00294    .data = NULL,
00295    .key = __FILE__,
00296 };
00297    
00298 static struct ast_http_uri staticuri = {
00299    .callback = static_callback,
00300    .description = "Asterisk HTTP Static Delivery",
00301    .uri = "static",
00302    .has_subtree = 1,
00303    .static_content = 1,
00304    .supports_get = 1,
00305    .data = NULL,
00306    .key= __FILE__,
00307 };
00308    
00309 struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
00310 {
00311    struct ast_str *out = ast_str_create(512);
00312 
00313    if (out == NULL) {
00314       return out;
00315    }
00316 
00317    ast_str_set(&out, 0,
00318           "Content-type: text/html\r\n"
00319           "%s"
00320           "\r\n"
00321           "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
00322           "<html><head>\r\n"
00323           "<title>%d %s</title>\r\n"
00324           "</head><body>\r\n"
00325           "<h1>%s</h1>\r\n"
00326           "<p>%s</p>\r\n"
00327           "<hr />\r\n"
00328           "<address>Asterisk Server</address>\r\n"
00329           "</body></html>\r\n",
00330           (extra_header ? extra_header : ""), status, title, title, text);
00331 
00332    return out;
00333 }
00334 
00335 /*! \brief 
00336  * Link the new uri into the list. 
00337  *
00338  * They are sorted by length of
00339  * the string, not alphabetically. Duplicate entries are not replaced,
00340  * but the insertion order (using <= and not just <) makes sure that
00341  * more recent insertions hide older ones.
00342  * On a lookup, we just scan the list and stop at the first matching entry.
00343  */
00344 int ast_http_uri_link(struct ast_http_uri *urih)
00345 {
00346    struct ast_http_uri *uri;
00347    int len = strlen(urih->uri);
00348 
00349    if (!(urih->supports_get || urih->supports_post)) {
00350       ast_log(LOG_WARNING, "URI handler does not provide either GET or POST method: %s (%s)\n", urih->uri, urih->description);
00351       return -1;
00352    }
00353 
00354    AST_RWLIST_WRLOCK(&uris);
00355 
00356    if (AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len) {
00357       AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
00358       AST_RWLIST_UNLOCK(&uris);
00359 
00360       return 0;
00361    }
00362 
00363    AST_RWLIST_TRAVERSE(&uris, uri, entry) {
00364       if (AST_RWLIST_NEXT(uri, entry) &&
00365           strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
00366          AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
00367          AST_RWLIST_UNLOCK(&uris); 
00368 
00369          return 0;
00370       }
00371    }
00372 
00373    AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
00374 
00375    AST_RWLIST_UNLOCK(&uris);
00376    
00377    return 0;
00378 }  
00379 
00380 void ast_http_uri_unlink(struct ast_http_uri *urih)
00381 {
00382    AST_RWLIST_WRLOCK(&uris);
00383    AST_RWLIST_REMOVE(&uris, urih, entry);
00384    AST_RWLIST_UNLOCK(&uris);
00385 }
00386 
00387 void ast_http_uri_unlink_all_with_key(const char *key)
00388 {
00389    struct ast_http_uri *urih;
00390    AST_RWLIST_WRLOCK(&uris);
00391    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&uris, urih, entry) {
00392       if (!strcmp(urih->key, key)) {
00393          AST_RWLIST_REMOVE_CURRENT(entry);
00394       }
00395       if (urih->dmallocd) {
00396          ast_free(urih->data);
00397       }
00398       if (urih->mallocd) {
00399          ast_free(urih);
00400       }
00401    }
00402    AST_RWLIST_TRAVERSE_SAFE_END;
00403    AST_RWLIST_UNLOCK(&uris);
00404 }
00405 
00406 /*
00407  * Decode special characters in http uri.
00408  * We have ast_uri_decode to handle %XX sequences, but spaces
00409  * are encoded as a '+' so we need to replace them beforehand.
00410  */
00411 static void http_decode(char *s)
00412 {
00413    char *t;
00414    
00415    for (t = s; *t; t++) {
00416       if (*t == '+')
00417          *t = ' ';
00418    }
00419 
00420    ast_uri_decode(s);
00421 }
00422 
00423 static struct ast_str *handle_uri(struct ast_tcptls_session_instance *ser, char *uri, enum ast_http_method method,
00424               int *status, char **title, int *contentlength, struct ast_variable **cookies, struct ast_variable *headers, 
00425               unsigned int *static_content)
00426 {
00427    char *c;
00428    struct ast_str *out = NULL;
00429    char *params = uri;
00430    struct ast_http_uri *urih = NULL;
00431    int l;
00432    struct ast_variable *vars = NULL, *v, *prev = NULL;
00433    struct http_uri_redirect *redirect;
00434    int saw_method = 0;
00435 
00436    /* preserve previous behavior of only support URI parameters on GET requests */
00437    if (method == AST_HTTP_GET) {
00438       strsep(&params, "?");
00439       
00440       /* Extract arguments from the request and store them in variables.
00441        * Note that a request can have multiple arguments with the same
00442        * name, and we store them all in the list of variables.
00443        * It is up to the application to handle multiple values.
00444        */
00445       if (params) {
00446          char *var, *val;
00447          
00448          while ((val = strsep(&params, "&"))) {
00449             var = strsep(&val, "=");
00450             if (val) {
00451                http_decode(val);
00452             } else {
00453                val = "";
00454             }
00455             http_decode(var);
00456             if ((v = ast_variable_new(var, val, ""))) {
00457                if (vars) {
00458                   prev->next = v;
00459                } else {
00460                   vars = v;
00461                }
00462                prev = v;
00463             }
00464          }
00465       }
00466    }
00467 
00468    /*
00469     * Append the cookies to the list of variables.
00470     * This saves a pass in the cookies list, but has the side effect
00471     * that a variable might mask a cookie with the same name if the
00472     * application stops at the first match.
00473     * Note that this is the same behaviour as $_REQUEST variables in PHP.
00474     */
00475    if (prev) {
00476       prev->next = *cookies;
00477    } else {
00478       vars = *cookies;
00479    }
00480    *cookies = NULL;
00481 
00482    http_decode(uri);
00483 
00484    AST_RWLIST_RDLOCK(&uri_redirects);
00485    AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
00486       if (!strcasecmp(uri, redirect->target)) {
00487          char buf[512];
00488 
00489          snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
00490          out = ast_http_error((*status = 302),
00491                     (*title = ast_strdup("Moved Temporarily")),
00492                     buf, "Redirecting...");
00493 
00494          break;
00495       }
00496    }
00497    AST_RWLIST_UNLOCK(&uri_redirects);
00498 
00499    if (redirect) {
00500       goto cleanup;
00501    }
00502 
00503    /* We want requests to start with the (optional) prefix and '/' */
00504    l = strlen(prefix);
00505    if (!strncasecmp(uri, prefix, l) && uri[l] == '/') {
00506       uri += l + 1;
00507       /* scan registered uris to see if we match one. */
00508       AST_RWLIST_RDLOCK(&uris);
00509       AST_RWLIST_TRAVERSE(&uris, urih, entry) {
00510          ast_debug(2, "match request [%s] with handler [%s] len %d\n", uri, urih->uri, l);
00511          if (!saw_method) {
00512             switch (method) {
00513             case AST_HTTP_GET:
00514                if (urih->supports_get) {
00515                   saw_method = 1;
00516                }
00517                break;
00518             case AST_HTTP_POST:
00519                if (urih->supports_post) {
00520                   saw_method = 1;
00521                }
00522                break;
00523             }
00524          }
00525 
00526          l = strlen(urih->uri);
00527          c = uri + l;   /* candidate */
00528 
00529          if (strncasecmp(urih->uri, uri, l) || /* no match */
00530              (*c && *c != '/')) { /* substring */
00531             continue;
00532          }
00533 
00534          if (*c == '/') {
00535             c++;
00536          }
00537 
00538          if (!*c || urih->has_subtree) {
00539             if (((method == AST_HTTP_GET) && urih->supports_get) ||
00540                 ((method == AST_HTTP_POST) && urih->supports_post)) {
00541                uri = c;
00542 
00543                break;
00544             }
00545          }
00546       }
00547 
00548       if (!urih) {
00549          AST_RWLIST_UNLOCK(&uris);
00550       }
00551    }
00552 
00553    if (method == AST_HTTP_POST && !astman_is_authed(manid_from_vars(vars))) {
00554       out = ast_http_error((*status = 403),
00555                (*title = ast_strdup("Access Denied")),
00556                NULL, "You do not have permission to access the requested URL.");
00557    } else if (urih) {
00558       *static_content = urih->static_content;
00559       out = urih->callback(ser, urih, uri, method, vars, headers, status, title, contentlength);
00560       AST_RWLIST_UNLOCK(&uris);
00561    } else if (saw_method) {
00562       out = ast_http_error((*status = 404),
00563                  (*title = ast_strdup("Not Found")), NULL,
00564                  "The requested URL was not found on this server.");
00565    } else {
00566       out = ast_http_error((*status = 501),
00567                  (*title = ast_strdup("Not Implemented")), NULL,
00568                  "Attempt to use unimplemented / unsupported method");
00569    }
00570 
00571 cleanup:
00572    ast_variables_destroy(vars);
00573 
00574    return out;
00575 }
00576 
00577 #ifdef DO_SSL
00578 #if defined(HAVE_FUNOPEN)
00579 #define HOOK_T int
00580 #define LEN_T int
00581 #else
00582 #define HOOK_T ssize_t
00583 #define LEN_T size_t
00584 #endif
00585 
00586 /*!
00587  * replacement read/write functions for SSL support.
00588  * We use wrappers rather than SSL_read/SSL_write directly so
00589  * we can put in some debugging.
00590  */
00591 /*static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
00592 {
00593    int i = SSL_read(cookie, buf, len-1);
00594 #if 0
00595    if (i >= 0)
00596       buf[i] = '\0';
00597    ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
00598 #endif
00599    return i;
00600 }
00601 
00602 static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
00603 {
00604 #if 0
00605    char *s = alloca(len+1);
00606    strncpy(s, buf, len);
00607    s[len] = '\0';
00608    ast_verbose("ssl write size %d <%s>\n", (int)len, s);
00609 #endif
00610    return SSL_write(cookie, buf, len);
00611 }
00612 
00613 static int ssl_close(void *cookie)
00614 {
00615    close(SSL_get_fd(cookie));
00616    SSL_shutdown(cookie);
00617    SSL_free(cookie);
00618    return 0;
00619 }*/
00620 #endif   /* DO_SSL */
00621 
00622 static struct ast_variable *parse_cookies(char *cookies)
00623 {
00624    char *cur;
00625    struct ast_variable *vars = NULL, *var;
00626 
00627    /* Skip Cookie: */
00628    cookies += 8;
00629 
00630    while ((cur = strsep(&cookies, ";"))) {
00631       char *name, *val;
00632       
00633       name = val = cur;
00634       strsep(&val, "=");
00635 
00636       if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00637          continue;
00638       }
00639 
00640       name = ast_strip(name);
00641       val = ast_strip_quoted(val, "\"", "\"");
00642 
00643       if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00644          continue;
00645       }
00646 
00647       if (option_debug) {
00648          ast_log(LOG_DEBUG, "mmm ... cookie!  Name: '%s'  Value: '%s'\n", name, val);
00649       }
00650 
00651       var = ast_variable_new(name, val, __FILE__);
00652       var->next = vars;
00653       vars = var;
00654    }
00655 
00656    return vars;
00657 }
00658 
00659 static void *httpd_helper_thread(void *data)
00660 {
00661    char buf[4096];
00662    char cookie[4096];
00663    struct ast_tcptls_session_instance *ser = data;
00664    struct ast_variable *vars=NULL, *headers = NULL;
00665    char *uri, *title=NULL;
00666    int status = 200, contentlength = 0;
00667    struct ast_str *out = NULL;
00668    unsigned int static_content = 0;
00669    struct ast_variable *tail = headers;
00670 
00671    if (!fgets(buf, sizeof(buf), ser->f)) {
00672       goto done;
00673    }
00674 
00675    uri = ast_skip_nonblanks(buf);   /* Skip method */
00676    if (*uri) {
00677       *uri++ = '\0';
00678    }
00679 
00680    uri = ast_skip_blanks(uri);   /* Skip white space */
00681 
00682    if (*uri) {       /* terminate at the first blank */
00683       char *c = ast_skip_nonblanks(uri);
00684 
00685       if (*c) {
00686          *c = '\0';
00687       }
00688    }
00689 
00690    /* process "Cookie: " lines */
00691    while (fgets(cookie, sizeof(cookie), ser->f)) {
00692       /* Trim trailing characters */
00693       ast_trim_blanks(cookie);
00694       if (ast_strlen_zero(cookie)) {
00695          break;
00696       }
00697       if (!strncasecmp(cookie, "Cookie: ", 8)) {
00698          vars = parse_cookies(cookie);
00699       } else {
00700          char *name, *val;
00701 
00702          val = cookie;
00703          name = strsep(&val, ":");
00704          if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
00705             continue;
00706          }
00707          ast_trim_blanks(name);
00708          val = ast_skip_blanks(val);
00709 
00710          if (!headers) {
00711             headers = ast_variable_new(name, val, __FILE__);
00712             tail = headers;
00713          } else {
00714             tail->next = ast_variable_new(name, val, __FILE__);
00715             tail = tail->next;
00716          }
00717       }
00718    }
00719 
00720    if (!*uri) {
00721       out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
00722    } else if (strcasecmp(buf, "post") && strcasecmp(buf, "get")) {
00723       out = ast_http_error(501, "Not Implemented", NULL,
00724                  "Attempt to use unimplemented / unsupported method");
00725    } else { /* try to serve it */
00726       out = handle_uri(ser, uri, (!strcasecmp(buf, "get")) ? AST_HTTP_GET : AST_HTTP_POST,
00727              &status, &title, &contentlength, &vars, headers, &static_content);
00728    }
00729 
00730    /* If they aren't mopped up already, clean up the cookies */
00731    if (vars) {
00732       ast_variables_destroy(vars);
00733    }
00734    /* Clean up all the header information pulled as well */
00735    if (headers) {
00736       ast_variables_destroy(headers);
00737    }
00738 
00739    if (out) {
00740       struct timeval now = ast_tvnow();
00741       char timebuf[256];
00742       struct ast_tm tm;
00743 
00744       ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
00745       fprintf(ser->f,
00746          "HTTP/1.1 %d %s\r\n"
00747          "Server: Asterisk/%s\r\n"
00748          "Date: %s\r\n"
00749          "Connection: close\r\n"
00750          "%s",
00751          status, title ? title : "OK", ast_get_version(), timebuf,
00752          static_content ? "" : "Cache-Control: no-cache, no-store\r\n");
00753          /* We set the no-cache headers only for dynamic content.
00754          * If you want to make sure the static file you requested is not from cache,
00755          * append a random variable to your GET request.  Ex: 'something.html?r=109987734'
00756          */
00757       if (!contentlength) {   /* opaque body ? just dump it hoping it is properly formatted */
00758          fprintf(ser->f, "%s", out->str);
00759       } else {
00760          char *tmp = strstr(out->str, "\r\n\r\n");
00761 
00762          if (tmp) {
00763             fprintf(ser->f, "Content-length: %d\r\n", contentlength);
00764             /* first write the header, then the body */
00765             if (fwrite(out->str, 1, (tmp + 4 - out->str), ser->f) != tmp + 4 - out->str) {
00766                ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00767             }
00768             if (fwrite(tmp + 4, 1, contentlength, ser->f) != contentlength ) {
00769                ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00770             }
00771          }
00772       }
00773       ast_free(out);
00774    }
00775 
00776    if (title) {
00777       ast_free(title);
00778    }
00779 
00780 done:
00781    fclose(ser->f);
00782    ao2_ref(ser, -1);
00783    ser = NULL;
00784 
00785    return NULL;
00786 }
00787 
00788 /*!
00789  * \brief Add a new URI redirect
00790  * The entries in the redirect list are sorted by length, just like the list
00791  * of URI handlers.
00792  */
00793 static void add_redirect(const char *value)
00794 {
00795    char *target, *dest;
00796    struct http_uri_redirect *redirect, *cur;
00797    unsigned int target_len;
00798    unsigned int total_len;
00799 
00800    dest = ast_strdupa(value);
00801    dest = ast_skip_blanks(dest);
00802    target = strsep(&dest, " ");
00803    target = ast_skip_blanks(target);
00804    target = strsep(&target, " "); /* trim trailing whitespace */
00805 
00806    if (!dest) {
00807       ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
00808       return;
00809    }
00810 
00811    target_len = strlen(target) + 1;
00812    total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
00813 
00814    if (!(redirect = ast_calloc(1, total_len))) {
00815       return;
00816    }
00817 
00818    redirect->dest = redirect->target + target_len;
00819    strcpy(redirect->target, target);
00820    strcpy(redirect->dest, dest);
00821 
00822    AST_RWLIST_WRLOCK(&uri_redirects);
00823 
00824    target_len--; /* So we can compare directly with strlen() */
00825    if (AST_RWLIST_EMPTY(&uri_redirects) 
00826        || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len) {
00827       AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
00828       AST_RWLIST_UNLOCK(&uri_redirects);
00829 
00830       return;
00831    }
00832 
00833    AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
00834       if (AST_RWLIST_NEXT(cur, entry) 
00835           && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len) {
00836          AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
00837          AST_RWLIST_UNLOCK(&uri_redirects); 
00838 
00839          return;
00840       }
00841    }
00842 
00843    AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
00844 
00845    AST_RWLIST_UNLOCK(&uri_redirects);
00846 }
00847 
00848 static int __ast_http_load(int reload)
00849 {
00850    struct ast_config *cfg;
00851    struct ast_variable *v;
00852    int enabled=0;
00853    int newenablestatic=0;
00854    struct hostent *hp;
00855    struct ast_hostent ahp;
00856    char newprefix[MAX_PREFIX] = "";
00857    int have_sslbindaddr = 0;
00858    struct http_uri_redirect *redirect;
00859    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00860 
00861    if ((cfg = ast_config_load2("http.conf", "http", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
00862       return 0;
00863    }
00864 
00865    /* default values */
00866    memset(&http_desc.local_address, 0, sizeof(http_desc.local_address));
00867    http_desc.local_address.sin_port = htons(8088);
00868 
00869    memset(&https_desc.local_address, 0, sizeof(https_desc.local_address));
00870    https_desc.local_address.sin_port = htons(8089);
00871 
00872    http_tls_cfg.enabled = 0;
00873    if (http_tls_cfg.certfile) {
00874       ast_free(http_tls_cfg.certfile);
00875    }
00876    http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
00877    if (http_tls_cfg.cipher) {
00878       ast_free(http_tls_cfg.cipher);
00879    }
00880    http_tls_cfg.cipher = ast_strdup("");
00881 
00882    AST_RWLIST_WRLOCK(&uri_redirects);
00883    while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
00884       ast_free(redirect);
00885    }
00886    AST_RWLIST_UNLOCK(&uri_redirects);
00887 
00888    if (cfg) {
00889       v = ast_variable_browse(cfg, "general");
00890       for (; v; v = v->next) {
00891          if (!strcasecmp(v->name, "enabled")) {
00892             enabled = ast_true(v->value);
00893          } else if (!strcasecmp(v->name, "sslenable")) {
00894             http_tls_cfg.enabled = ast_true(v->value);
00895          } else if (!strcasecmp(v->name, "sslbindport")) {
00896             https_desc.local_address.sin_port = htons(atoi(v->value));
00897          } else if (!strcasecmp(v->name, "sslcert")) {
00898             ast_free(http_tls_cfg.certfile);
00899             http_tls_cfg.certfile = ast_strdup(v->value);
00900          } else if (!strcasecmp(v->name, "sslcipher")) {
00901             ast_free(http_tls_cfg.cipher);
00902             http_tls_cfg.cipher = ast_strdup(v->value);
00903          } else if (!strcasecmp(v->name, "enablestatic")) {
00904             newenablestatic = ast_true(v->value);
00905          } else if (!strcasecmp(v->name, "bindport")) {
00906             http_desc.local_address.sin_port = htons(atoi(v->value));
00907          } else if (!strcasecmp(v->name, "sslbindaddr")) {
00908             if ((hp = ast_gethostbyname(v->value, &ahp))) {
00909                memcpy(&https_desc.local_address.sin_addr, hp->h_addr, sizeof(https_desc.local_address.sin_addr));
00910                have_sslbindaddr = 1;
00911             } else {
00912                ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
00913             }
00914          } else if (!strcasecmp(v->name, "bindaddr")) {
00915             if ((hp = ast_gethostbyname(v->value, &ahp))) {
00916                memcpy(&http_desc.local_address.sin_addr, hp->h_addr, sizeof(http_desc.local_address.sin_addr));
00917             } else {
00918                ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
00919             }
00920          } else if (!strcasecmp(v->name, "prefix")) {
00921             if (!ast_strlen_zero(v->value)) {
00922                newprefix[0] = '/';
00923                ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
00924             } else {
00925                newprefix[0] = '\0';
00926             }
00927          } else if (!strcasecmp(v->name, "redirect")) {
00928             add_redirect(v->value);
00929          } else {
00930             ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
00931          }
00932       }
00933 
00934       ast_config_destroy(cfg);
00935    }
00936 
00937    if (!have_sslbindaddr) {
00938       https_desc.local_address.sin_addr = http_desc.local_address.sin_addr;
00939    }
00940    if (enabled) {
00941       http_desc.local_address.sin_family = https_desc.local_address.sin_family = AF_INET;
00942    }
00943    if (strcmp(prefix, newprefix)) {
00944       ast_copy_string(prefix, newprefix, sizeof(prefix));
00945    }
00946    enablestatic = newenablestatic;
00947    ast_tcptls_server_start(&http_desc);
00948    if (ast_ssl_setup(https_desc.tls_cfg)) {
00949       ast_tcptls_server_start(&https_desc);
00950    }
00951 
00952    return 0;
00953 }
00954 
00955 static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00956 {
00957    struct ast_http_uri *urih;
00958    struct http_uri_redirect *redirect;
00959 
00960    switch (cmd) {
00961    case CLI_INIT:
00962       e->command = "http show status";
00963       e->usage = 
00964          "Usage: http show status\n"
00965          "       Lists status of internal HTTP engine\n";
00966       return NULL;
00967    case CLI_GENERATE:
00968       return NULL;
00969    }
00970    
00971    if (a->argc != 3) {
00972       return CLI_SHOWUSAGE;
00973    }
00974    ast_cli(a->fd, "HTTP Server Status:\n");
00975    ast_cli(a->fd, "Prefix: %s\n", prefix);
00976    if (!http_desc.old_address.sin_family) {
00977       ast_cli(a->fd, "Server Disabled\n\n");
00978    } else {
00979       ast_cli(a->fd, "Server Enabled and Bound to %s:%d\n\n",
00980          ast_inet_ntoa(http_desc.old_address.sin_addr),
00981          ntohs(http_desc.old_address.sin_port));
00982       if (http_tls_cfg.enabled) {
00983          ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
00984             ast_inet_ntoa(https_desc.old_address.sin_addr),
00985             ntohs(https_desc.old_address.sin_port));
00986       }
00987    }
00988 
00989    ast_cli(a->fd, "Enabled URI's:\n");
00990    AST_RWLIST_RDLOCK(&uris);
00991    if (AST_RWLIST_EMPTY(&uris)) {
00992       ast_cli(a->fd, "None.\n");
00993    } else {
00994       AST_RWLIST_TRAVERSE(&uris, urih, entry) {
00995          ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : ""), urih->description);
00996       }
00997    }
00998    AST_RWLIST_UNLOCK(&uris);
00999 
01000    ast_cli(a->fd, "\nEnabled Redirects:\n");
01001    AST_RWLIST_RDLOCK(&uri_redirects);
01002    AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
01003       ast_cli(a->fd, "  %s => %s\n", redirect->target, redirect->dest);
01004    }
01005    if (AST_RWLIST_EMPTY(&uri_redirects)) {
01006       ast_cli(a->fd, "  None.\n");
01007    }
01008    AST_RWLIST_UNLOCK(&uri_redirects);
01009 
01010    return CLI_SUCCESS;
01011 }
01012 
01013 int ast_http_reload(void)
01014 {
01015    return __ast_http_load(1);
01016 }
01017 
01018 static struct ast_cli_entry cli_http[] = {
01019    AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
01020 };
01021 
01022 int ast_http_init(void)
01023 {
01024    ast_http_uri_link(&statusuri);
01025    ast_http_uri_link(&staticuri);
01026    ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
01027 
01028    return __ast_http_load(0);
01029 }

Generated on Wed Oct 28 11:51:04 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6