[asterisk-commits] twilson: branch group/manager_http_auth r161982 - in /team/group/manager_http...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Dec 9 09:18:44 CST 2008
Author: twilson
Date: Tue Dec 9 09:18:43 2008
New Revision: 161982
URL: http://svn.digium.com/view/asterisk?view=rev&rev=161982
Log:
Initial import
Modified:
team/group/manager_http_auth/include/asterisk/http.h
team/group/manager_http_auth/include/asterisk/utils.h
team/group/manager_http_auth/main/http.c
team/group/manager_http_auth/main/manager.c
team/group/manager_http_auth/main/utils.c
team/group/manager_http_auth/res/res_http_post.c
team/group/manager_http_auth/res/res_phoneprov.c
Modified: team/group/manager_http_auth/include/asterisk/http.h
URL: http://svn.digium.com/view/asterisk/team/group/manager_http_auth/include/asterisk/http.h?view=diff&rev=161982&r1=161981&r2=161982
==============================================================================
--- team/group/manager_http_auth/include/asterisk/http.h (original)
+++ team/group/manager_http_auth/include/asterisk/http.h Tue Dec 9 09:18:43 2008
@@ -52,27 +52,39 @@
* be run earlier in the startup process so modules have it available.
*/
+/*! \brief HTTP Request methods known by Asterisk */
+enum ast_http_method {
+ AST_HTTP_UNKNOWN = 0, /*!< Unknown response */
+ AST_HTTP_GET,
+ AST_HTTP_POST,
+ AST_HTTP_HEAD,
+ AST_HTTP_PUT /*!< Not supported in Asterisk */
+};
+
+struct ast_http_uri;
/*! \brief HTTP Callbacks take the socket
- \note The method and the path as arguments and should
- return the content, allocated with malloc(). Status should be changed to reflect
- the status of the request if it isn't 200 and title may be set to a malloc()'d string
- to an appropriate title for non-200 responses. Content length may also be specified.
-\verbatim
- The return value may include additional headers at the front and MUST include a blank
- line with \r\n to provide separation between user headers and content (even if no
- content is specified)
+ \note The callback function get server instance, uri, http method,
+ get method variable (if present in uri) and http headers as arguments
+ and should ast_http_send() function for sending content allocated
+ with malloc() and/or content from opeened file descriptor.
+
+ Status and status text should be put as argument in ast_http_send()
+ function to reflect the status of the request. (200 or 304, for example).
+ Content length calculated by ast_http_send() function internality.
+
+ Static content indicated in argument passed to ast_http_send() function.
+
+\verbatim
+ All addition http response headers must be send as separate malloc'ated
+ string, and must be not existed in responce content! No addition line
+ with \r\n are required.
\endverbatim
+
+ For error responce can be used ast_http_error() function.
*/
-
-enum ast_http_method {
- AST_HTTP_GET = 0,
- AST_HTTP_POST,
-};
-struct ast_http_uri;
-
-typedef struct ast_str *(*ast_http_callback)(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength);
+typedef int (*ast_http_callback)(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers);
/*! \brief Definition of a URI handler */
struct ast_http_uri {
@@ -81,12 +93,6 @@
const char *uri;
ast_http_callback callback;
unsigned int has_subtree:1;
- /*! This handler serves static content */
- unsigned int static_content:1;
- /*! This handler accepts GET requests */
- unsigned int supports_get:1;
- /*! This handler accepts POST requests */
- unsigned int supports_post:1;
/*! Structure is malloc'd */
unsigned int mallocd:1;
/*! Data structure is malloc'd */
@@ -97,6 +103,9 @@
const char *key;
};
+/* Get cookie from Request headers */
+struct ast_variable *ast_http_get_cookies(struct ast_variable *headers);
+
/*! \brief Register a URI handler */
int ast_http_uri_link(struct ast_http_uri *urihandler);
@@ -106,10 +115,48 @@
/*! \brief Unregister all handlers with matching key */
void ast_http_uri_unlink_all_with_key(const char *key);
-/*! \brief Return an ast_str malloc()'d string containing an HTTP error message */
-struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text);
+/*! \brief Return http method name string */
+char *ast_get_http_method(enum ast_http_method method);
+
+/*! \brief Return mime type based on extension */
+char *ast_ftype2mtype(const char *ftype);
+
+/*! \brief Return manager id, if exist, from request headers */
+uint32_t manid_from_vars(struct ast_variable *headers);
+
+/*! \brief Generic function for sending http/1.1 responce.
+ \note Function make http responce header from status_code,
+ status_header and http_header.
+
+ Extra http header MUST BE only present in http_header argument.
+ Argument "out" contain only content of response (no headers!).
+
+ Http content can be constructed from mallocated argument "out", if
+ it is not NULL.
+ And, if fd is not null, from opened (for reading) file.
+
+ This function calculate content-lenght http header itself.
+
+ if http_header or out argument have not NULL value, this function
+ free memory, mallocated by caller and close http socket before exit.
+
+ This function don't close file, identifide in fd variable.
+ */
+void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, const int fd, unsigned int static_content);
+
+/* \brief Send http "401 Unauthorized" responce and close socket*/
+void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm, const unsigned long nonce, const unsigned long opaque, int stale, const char *text);
+
+/* \brief Send HTTP error message and close socket */
+void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text);
/*! \brief Return the current prefix */
void ast_http_prefix(char *buf, int len);
+
+/* \brief get post variables from client Request Entity-Body, if content type
+ is application/x-www-form-urlencoded. */
+struct ast_variable *get_post_vars(struct ast_tcptls_session_instance *ser, struct ast_variable *headers);
+
+
#endif /* _ASTERISK_SRV_H */
Modified: team/group/manager_http_auth/include/asterisk/utils.h
URL: http://svn.digium.com/view/asterisk/team/group/manager_http_auth/include/asterisk/utils.h?view=diff&rev=161982&r1=161981&r2=161982
==============================================================================
--- team/group/manager_http_auth/include/asterisk/utils.h (original)
+++ team/group/manager_http_auth/include/asterisk/utils.h Tue Dec 9 09:18:43 2008
@@ -641,6 +641,31 @@
int ast_mkdir(const char *path, int mode);
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* Definition for Digest authorization */
+struct ast_http_digest {
+ char username[80];
+ char nonce[32];
+ char uri[256];
+ char realm[256];
+ char domain[256];
+ char response[256];
+ int qop; /* Flag set to 1, if we send/recv qop="quth" */
+ char cnonce[256];
+ char opaque[32];
+ char nc[32];
+};
+
+/*!
+ *\brief Parse digest authorization header.
+ *\return Returns -1 if we have no auth or something wrong with digest.
+ *\note This function may be used for Digest request and responce header.
+ * request arg is set to nonzero, if we parse Digest Request.
+ * pedantic arg can be set to nonzero if we need to do addition Digest check.
+ */
+int ast_parse_digest(const char *digest, struct ast_http_digest *d, int request, int pedantic);
+
#ifdef AST_DEVMODE
#define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
Modified: team/group/manager_http_auth/main/http.c
URL: http://svn.digium.com/view/asterisk/team/group/manager_http_auth/main/http.c?view=diff&rev=161982&r1=161981&r2=161982
==============================================================================
--- team/group/manager_http_auth/main/http.c (original)
+++ team/group/manager_http_auth/main/http.c Tue Dec 9 09:18:43 2008
@@ -94,10 +94,11 @@
/*! \brief Limit the kinds of files we're willing to serve up */
static struct {
- const char *ext;
- const char *mtype;
+ char *ext;
+ char *mtype;
} mimetypes[] = {
{ "png", "image/png" },
+ { "xml", "text/xml" },
{ "jpg", "image/jpeg" },
{ "js", "application/x-javascript" },
{ "wav", "audio/x-wav" },
@@ -115,32 +116,52 @@
static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
-static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
+const struct ast_cfhttp_methods_text {
+ enum ast_http_method method;
+ char * const text;
+} ast_http_methods_text[] = {
+ { AST_HTTP_UNKNOWN, "UNKNOWN" },
+ { AST_HTTP_GET, "GET" },
+ { AST_HTTP_POST, "POST" },
+ { AST_HTTP_HEAD, "HEAD" },
+ { AST_HTTP_PUT, "PUT" }
+};
+
+/*! \brief Return http method name string */
+char *ast_get_http_method(enum ast_http_method method) {
+ return ast_http_methods_text[method].text;
+}
+
+
+/*! \brief Return mime type based on extension */
+char *ast_ftype2mtype(const char *ftype)
{
int x;
if (ftype) {
- for (x = 0; x < ARRAY_LEN(mimetypes); x++) {
+ for (x = 0;x < ARRAY_LEN(mimetypes); x++) {
if (!strcasecmp(ftype, mimetypes[x].ext)) {
return mimetypes[x].mtype;
}
}
}
-
- snprintf(wkspace, wkspacelen, "text/%s", S_OR(ftype, "plain"));
-
- return wkspace;
-}
-
-static uint32_t manid_from_vars(struct ast_variable *sid) {
- uint32_t mngid;
-
- while (sid && strcmp(sid->name, "mansession_id"))
- sid = sid->next;
-
- if (!sid || sscanf(sid->value, "%x", &mngid) != 1)
- return 0;
-
+ return NULL;
+}
+
+uint32_t manid_from_vars(struct ast_variable *headers) {
+ uint32_t mngid = 0;
+ struct ast_variable *v, *cookies;
+
+ cookies = ast_http_get_cookies(headers);
+ for (v = cookies; v; v = v->next) {
+ if (!strcasecmp(v->name, "mansession_id")) {
+ sscanf(v->value, "%x", &mngid);
+ break;
+ }
+ }
+ if (cookies) {
+ ast_variables_destroy(cookies);
+ }
return mngid;
}
@@ -151,30 +172,36 @@
}
}
-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)
+static int 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 *get_vars, struct ast_variable *headers)
{
char *path;
char *ftype;
- const char *mtype;
+ char *mtype;
char wkspace[80];
struct stat st;
int len;
int fd;
- struct timeval now = ast_tvnow();
- char buf[256];
+ struct ast_str *http_header;
+ struct timeval tv;
struct ast_tm tm;
+ char timebuf[80], etag[20];
+ struct ast_variable *v;
+ int not_modified = 0;
+
+ if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
+ ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
+ return -1;
+ }
/* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration
substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
if (!enablestatic || ast_strlen_zero(uri)) {
goto out403;
}
-
/* Disallow any funny filenames at all */
if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0])) {
goto out403;
}
-
if (strstr(uri, "/..")) {
goto out403;
}
@@ -182,8 +209,10 @@
if ((ftype = strrchr(uri, '.'))) {
ftype++;
}
-
- mtype = ftype2mtype(ftype, wkspace, sizeof(wkspace));
+ mtype = ast_ftype2mtype(ftype);
+ if (!mtype) {
+ snprintf(wkspace, sizeof(wkspace), "text/%s", ftype ? ftype : "plain");
+ }
/* Cap maximum length */
if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
@@ -195,102 +224,115 @@
if (stat(path, &st)) {
goto out404;
}
-
if (S_ISDIR(st.st_mode)) {
goto out404;
}
-
- if ((fd = open(path, O_RDONLY)) < 0) {
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
goto out403;
}
- if (strstr(path, "/private/") && !astman_is_authed(manid_from_vars(vars))) {
+ if (strstr(path, "/private/") && !astman_is_authed(manid_from_vars(headers))) {
goto out403;
}
- ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
- fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
- "Server: Asterisk/%s\r\n"
- "Date: %s\r\n"
- "Connection: close\r\n"
- "Cache-Control: no-cache, no-store\r\n"
- "Content-Length: %d\r\n"
- "Content-type: %s\r\n\r\n",
- ast_get_version(), buf, (int) st.st_size, mtype);
-
- while ((len = read(fd, buf, sizeof(buf))) > 0) {
- if (fwrite(buf, 1, len, ser->f) != len) {
- ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
- }
- }
-
+ /* make "Etag:" http header value */
+ sprintf(etag, "\"%ld\"", (long)st.st_mtime);
+
+ /* make "Last-Modified:" http header value */
+ tv.tv_sec = st.st_mtime;
+ tv.tv_usec = 0;
+ ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&tv, &tm, "GMT"));
+
+ /* check received "If-None-Match" request header and Etag value for file */
+ for (v = headers; v; v = v->next) {
+ if (!strcasecmp(v->name, "If-None-Match")) {
+ if (!strcasecmp(v->value, etag)) {
+ not_modified = 1;
+ }
+ break;
+ }
+ }
+
+ if ( (http_header = ast_str_create(255)) == NULL) {
+ return -1;
+ }
+
+ ast_str_set(&http_header, 0, "Content-type: %s\r\n"
+ "ETag: %s\r\n"
+ "Last-Modified: %s",
+ mtype,
+ etag,
+ timebuf);
+
+ if (not_modified) {
+ ast_http_send(ser, method, 304, "Not Modified", http_header, NULL, 0, 1);
+ } else {
+ ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 1); /* static content flag is set */
+ }
close(fd);
-
- return NULL;
+ return 0;
out404:
- return ast_http_error((*status = 404),
- (*title = ast_strdup("Not Found")),
- NULL, "The requested URL was not found on this server.");
+ ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
+ return -1;
out403:
- return ast_http_error((*status = 403),
- (*title = ast_strdup("Access Denied")),
- NULL, "You do not have permission to access the requested URL.");
-}
-
-
-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)
-{
- struct ast_str *out = ast_str_create(512);
- struct ast_variable *v;
-
- if (out == NULL) {
- return out;
+ ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
+ return -1;
+}
+
+static int 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 *get_vars, struct ast_variable *headers)
+{
+ struct ast_str *out;
+ struct ast_variable *v, *cookies = NULL;
+
+ if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
+ ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
+ return -1;
+ }
+
+ if ( (out = ast_str_create(512)) == NULL) {
+ return -1;
}
ast_str_append(&out, 0,
- "\r\n"
- "<title>Asterisk HTTP Status</title>\r\n"
- "<body bgcolor=\"#ffffff\">\r\n"
- "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
- "<h2> Asterisk™ HTTP Status</h2></td></tr>\r\n");
+ "<title>Asterisk HTTP Status</title>\r\n"
+ "<body bgcolor=\"#ffffff\">\r\n"
+ "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
+ "<h2> Asterisk™ HTTP Status</h2></td></tr>\r\n");
+
ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
ast_inet_ntoa(http_desc.old_local_address.sin_addr));
ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
ntohs(http_desc.old_local_address.sin_port));
-
if (http_tls_cfg.enabled) {
ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
ntohs(https_desc.old_local_address.sin_port));
}
-
ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
-
- for (v = vars; v; v = v->next) {
- if (strncasecmp(v->name, "cookie_", 7)) {
- ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
- }
- }
-
+ for (v = get_vars; v; v = v->next) {
+ ast_str_append(&out, 0, "<tr><td><i>Submitted GET Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
+ }
ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
- for (v = vars; v; v = v->next) {
- if (!strncasecmp(v->name, "cookie_", 7)) {
- ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
- }
- }
+ cookies = ast_http_get_cookies(headers);
+ for (v = cookies; v; v = v->next) {
+ ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
+ }
+ ast_variables_destroy(cookies);
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");
- return out;
+ ast_http_send(ser, method, 200, NULL, NULL, out, 0, 0);
+ return 0;
}
static struct ast_http_uri statusuri = {
.callback = httpstatus_callback,
.description = "Asterisk HTTP General Status",
.uri = "httpstatus",
- .supports_get = 1,
+ .has_subtree = 0,
.data = NULL,
.key = __FILE__,
};
@@ -300,36 +342,141 @@
.description = "Asterisk HTTP Static Delivery",
.uri = "static",
.has_subtree = 1,
- .static_content = 1,
- .supports_get = 1,
.data = NULL,
.key= __FILE__,
};
-
-struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
-{
+
+
+/* send http/1.1 responce */
+/* free content variable and close socket*/
+void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, const int fd, unsigned int static_content)
+{
+ struct timeval now = ast_tvnow();
+ struct ast_tm tm;
+ char timebuf[80];
+ int content_length = 0;
+
+ if (!ser || 0 == ser->f) {
+ return;
+ }
+
+ ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&now, &tm, "GMT"));
+
+ /* calc conetnt length */
+ if (out) {
+ content_length += strlen(out->str);
+ }
+
+ if (fd) {
+ content_length += lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ /* send http header */
+ fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
+ "Server: Asterisk/%s\r\n"
+ "Date: %s\r\n"
+ "Connection: close\r\n"
+ "%s"
+ "Content-Length: %d\r\n"
+ "%s\r\n\r\n",
+ status_code, status_title ? status_title : "OK",
+ ast_get_version(),
+ timebuf,
+ static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
+ content_length,
+ http_header ? http_header->str : ""
+ );
+
+ /* send content */
+ if (method != AST_HTTP_HEAD || status_code >= 400) {
+ if (out) {
+ fprintf(ser->f, "%s", out->str);
+ }
+
+ if (fd) {
+ char buf[256];
+ int len;
+ while ((len = read(fd, buf, sizeof(buf))) > 0) {
+ if (fwrite(buf, len, 1, ser->f) != len) {
+ ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
+ }
+ }
+ }
+ }
+
+ if (http_header) {
+ ast_free(http_header);
+ }
+ if (out) {
+ ast_free(out);
+ }
+
+ fclose(ser->f);
+ ser->f = 0;
+ return;
+}
+
+/* Send http "401 Unauthorized" responce and close socket*/
+void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm, const unsigned long nonce, const unsigned long opaque, int stale, const char *text)
+{
+ struct ast_str *http_headers = ast_str_create(128);
struct ast_str *out = ast_str_create(512);
-
- if (out == NULL) {
- return out;
- }
+
+ if (http_headers == NULL || out == NULL) {
+ return;
+ }
+
+ ast_str_set(&http_headers, 0,
+ "WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
+ "Content-type: text/html",
+ realm ? realm : "Asterisk",
+ nonce,
+ opaque,
+ stale ? ", stale=true" : "");
ast_str_set(&out, 0,
- "Content-type: text/html\r\n"
- "%s"
- "\r\n"
- "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
- "<html><head>\r\n"
- "<title>%d %s</title>\r\n"
- "</head><body>\r\n"
- "<h1>%s</h1>\r\n"
- "<p>%s</p>\r\n"
- "<hr />\r\n"
- "<address>Asterisk Server</address>\r\n"
- "</body></html>\r\n",
- (extra_header ? extra_header : ""), status, title, title, text);
-
- return out;
+ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+ "<html><head>\r\n"
+ "<title>401 Unauthorized</title>\r\n"
+ "</head><body>\r\n"
+ "<h1>401 Unauthorized</h1>\r\n"
+ "<p>%s</p>\r\n"
+ "<hr />\r\n"
+ "<address>Asterisk Server</address>\r\n"
+ "</body></html>\r\n",
+ text ? text : "");
+
+ ast_http_send(ser, AST_HTTP_UNKNOWN, 401, "Unauthorized", http_headers, out, 0, 0);
+ return;
+}
+
+/* send http error responce and close socket*/
+void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code, const char *status_title, const char *text)
+{
+ struct ast_str *http_headers = ast_str_create(40);
+ struct ast_str *out = ast_str_create(256);
+
+ if (http_headers == NULL && out == NULL) {
+ return;
+ }
+
+ ast_str_set(&http_headers, 0, "Content-type: text/html");
+
+ ast_str_set(&out, 0,
+ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+ "<html><head>\r\n"
+ "<title>%d %s</title>\r\n"
+ "</head><body>\r\n"
+ "<h1>%s</h1>\r\n"
+ "<p>%s</p>\r\n"
+ "<hr />\r\n"
+ "<address>Asterisk Server</address>\r\n"
+ "</body></html>\r\n",
+ status_code, status_title, status_title, text);
+
+ ast_http_send(ser, AST_HTTP_UNKNOWN, status_code, status_title, http_headers, out, 0, 0);
+ return;
}
/*! \brief
@@ -345,27 +492,20 @@
{
struct ast_http_uri *uri;
int len = strlen(urih->uri);
-
- if (!(urih->supports_get || urih->supports_post)) {
- ast_log(LOG_WARNING, "URI handler does not provide either GET or POST method: %s (%s)\n", urih->uri, urih->description);
- return -1;
- }
-
+
AST_RWLIST_WRLOCK(&uris);
- if (AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len) {
+ if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
AST_RWLIST_UNLOCK(&uris);
-
return 0;
}
AST_RWLIST_TRAVERSE(&uris, uri, entry) {
- if (AST_RWLIST_NEXT(uri, entry) &&
- strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
+ if ( AST_RWLIST_NEXT(uri, entry)
+ && strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len ) {
AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
AST_RWLIST_UNLOCK(&uris);
-
return 0;
}
}
@@ -420,82 +560,109 @@
ast_uri_decode(s);
}
-static struct ast_str *handle_uri(struct ast_tcptls_session_instance *ser, char *uri, enum ast_http_method method,
- int *status, char **title, int *contentlength, struct ast_variable **cookies, struct ast_variable *headers,
- unsigned int *static_content)
+/*
+ * get post variables from client Request Entity-Body, if content type is
+ * application/x-www-form-urlencoded
+ */
+struct ast_variable *get_post_vars(struct ast_tcptls_session_instance *ser, struct ast_variable *headers) {
+ int content_length = 0;
+ struct ast_variable *v, *post_vars=NULL, *prev = NULL;
+ char *buf, *var, *val;
+
+ for (v = headers; v; v = v->next) {
+ if (!strcasecmp(v->name, "Content-Type")) {
+ if (strcasecmp(v->value, "application/x-www-form-urlencoded")) {
+ return NULL;
+ }
+ break;
+ }
+ }
+
+ for (v = headers; v; v = v->next) {
+ if (!strcasecmp(v->name, "Content-Length")) {
+ content_length = atoi(v->value) + 1;
+ break;
+ }
+ }
+
+ if (!content_length) {
+ return NULL;
+ }
+
+ if (!(buf = alloca(content_length))) {
+ return NULL;
+ }
+ if (!fgets(buf, content_length, ser->f)) {
+ return NULL;
+ }
+
+ while ((val = strsep(&buf, "&"))) {
+ var = strsep(&val, "=");
+ if (val) {
+ http_decode(val);
+ } else {
+ val = "";
+ }
+ http_decode(var);
+ if ((v = ast_variable_new(var, val, ""))) {
+ if (post_vars) {
+ prev->next = v;
+ } else {
+ post_vars = v;
+ }
+ prev = v;
+ }
+ }
+ return post_vars;
+}
+
+static int handle_uri(struct ast_tcptls_session_instance *ser, char *uri, enum ast_http_method method,
+ struct ast_variable *headers)
{
char *c;
- struct ast_str *out = NULL;
+ int res = -1;
char *params = uri;
- struct ast_http_uri *urih = NULL;
+ struct ast_http_uri *urih=NULL;
int l;
- struct ast_variable *vars = NULL, *v, *prev = NULL;
+ struct ast_variable *get_vars=NULL, *v, *prev = NULL;
struct http_uri_redirect *redirect;
- int saw_method = 0;
-
- /* preserve previous behavior of only support URI parameters on GET requests */
- if (method == AST_HTTP_GET) {
- strsep(¶ms, "?");
-
- /* Extract arguments from the request and store them in variables.
- * Note that a request can have multiple arguments with the same
- * name, and we store them all in the list of variables.
- * It is up to the application to handle multiple values.
- */
- if (params) {
- char *var, *val;
-
- while ((val = strsep(¶ms, "&"))) {
- var = strsep(&val, "=");
- if (val) {
- http_decode(val);
+
+ strsep(¶ms, "?");
+ /* Extract arguments from the request and store them in variables. */
+ if (params) {
+ char *var, *val;
+
+ while ((val = strsep(¶ms, "&"))) {
+ var = strsep(&val, "=");
+ if (val) {
+ http_decode(val);
+ } else {
+ val = "";
+ }
+ http_decode(var);
+ if ((v = ast_variable_new(var, val, ""))) {
+ if (get_vars) {
+ prev->next = v;
} else {
- val = "";
+ get_vars = v;
}
- http_decode(var);
- if ((v = ast_variable_new(var, val, ""))) {
- if (vars) {
- prev->next = v;
- } else {
- vars = v;
- }
- prev = v;
- }
+ prev = v;
}
}
}
-
- /*
- * Append the cookies to the list of variables.
- * This saves a pass in the cookies list, but has the side effect
- * that a variable might mask a cookie with the same name if the
- * application stops at the first match.
- * Note that this is the same behaviour as $_REQUEST variables in PHP.
- */
- if (prev) {
- prev->next = *cookies;
- } else {
- vars = *cookies;
- }
- *cookies = NULL;
-
http_decode(uri);
AST_RWLIST_RDLOCK(&uri_redirects);
AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
if (!strcasecmp(uri, redirect->target)) {
- char buf[512];
-
- snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
- out = ast_http_error((*status = 302),
- (*title = ast_strdup("Moved Temporarily")),
- buf, "Redirecting...");
+ struct ast_str *http_header = ast_str_create(128);
+ ast_str_set(&http_header, 0, "Location: %s\r\n", redirect->dest);
+ ast_http_send(ser, method, 302, "Moved Temporarily", http_header, NULL, 0, 0);
break;
}
}
AST_RWLIST_UNLOCK(&uri_redirects);
-
if (redirect) {
goto cleanup;
}
@@ -508,70 +675,31 @@
AST_RWLIST_RDLOCK(&uris);
AST_RWLIST_TRAVERSE(&uris, urih, entry) {
ast_debug(2, "match request [%s] with handler [%s] len %d\n", uri, urih->uri, l);
- if (!saw_method) {
- switch (method) {
- case AST_HTTP_GET:
- if (urih->supports_get) {
- saw_method = 1;
- }
- break;
- case AST_HTTP_POST:
- if (urih->supports_post) {
- saw_method = 1;
- }
- break;
- }
- }
-
l = strlen(urih->uri);
c = uri + l; /* candidate */
-
- if (strncasecmp(urih->uri, uri, l) || /* no match */
- (*c && *c != '/')) { /* substring */
+ if (strncasecmp(urih->uri, uri, l) /* no match */
+ || (*c && *c != '/')) { /* substring */
continue;
}
-
if (*c == '/') {
c++;
}
-
if (!*c || urih->has_subtree) {
- if (((method == AST_HTTP_GET) && urih->supports_get) ||
- ((method == AST_HTTP_POST) && urih->supports_post)) {
- uri = c;
-
- break;
- }
+ uri = c;
+ break;
}
}
-
- if (!urih) {
- AST_RWLIST_UNLOCK(&uris);
- }
- }
-
- if (method == AST_HTTP_POST && !astman_is_authed(manid_from_vars(vars))) {
- out = ast_http_error((*status = 403),
- (*title = ast_strdup("Access Denied")),
- NULL, "You do not have permission to access the requested URL.");
- } else if (urih) {
- *static_content = urih->static_content;
- out = urih->callback(ser, urih, uri, method, vars, headers, status, title, contentlength);
AST_RWLIST_UNLOCK(&uris);
- } else if (saw_method) {
- out = ast_http_error((*status = 404),
- (*title = ast_strdup("Not Found")), NULL,
- "The requested URL was not found on this server.");
+ }
+ if (urih) {
+ res = urih->callback(ser, urih, uri, method, get_vars, headers);
} else {
- out = ast_http_error((*status = 501),
- (*title = ast_strdup("Not Implemented")), NULL,
- "Attempt to use unimplemented / unsupported method");
+ ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
}
cleanup:
- ast_variables_destroy(vars);
-
- return out;
+ ast_variables_destroy(get_vars);
+ return res;
}
#ifdef DO_SSL
@@ -582,7 +710,6 @@
#define HOOK_T ssize_t
#define LEN_T size_t
#endif
-
/*!
* replacement read/write functions for SSL support.
* We use wrappers rather than SSL_read/SSL_write directly so
@@ -624,9 +751,6 @@
char *cur;
struct ast_variable *vars = NULL, *var;
- /* Skip Cookie: */
- cookies += 8;
-
while ((cur = strsep(&cookies, ";"))) {
char *name, *val;
@@ -656,132 +780,117 @@
return vars;
}
+/* get cookie from Request headers */
+struct ast_variable *ast_http_get_cookies(struct ast_variable *headers)
+{
+ struct ast_variable *v, *cookies=NULL;
+
+ for (v = headers; v; v = v->next) {
+ if (!strncasecmp(v->name, "Cookie", 6)) {
+ if (cookies) {
+ ast_variables_destroy(cookies);
+ }
+
+ cookies = parse_cookies((char *)v->value);
+ }
+ }
+ return cookies;
+}
+
+
static void *httpd_helper_thread(void *data)
{
char buf[4096];
- char cookie[4096];
+ char header_line[4096];
struct ast_tcptls_session_instance *ser = data;
- struct ast_variable *vars=NULL, *headers = NULL;
- char *uri, *title=NULL;
- int status = 200, contentlength = 0;
- struct ast_str *out = NULL;
- unsigned int static_content = 0;
+ struct ast_variable *headers = NULL;
struct ast_variable *tail = headers;
+ char *uri, *method;
+ enum ast_http_method http_method = AST_HTTP_UNKNOWN;
if (!fgets(buf, sizeof(buf), ser->f)) {
goto done;
}
- uri = ast_skip_nonblanks(buf); /* Skip method */
+ /* Get method */
+ method = ast_skip_blanks(buf);
+ uri = ast_skip_nonblanks(method);
if (*uri) {
*uri++ = '\0';
}
+ if (!strcasecmp(method,"GET")) {
+ http_method = AST_HTTP_GET;
+ } else if (!strcasecmp(method,"POST")) {
+ http_method = AST_HTTP_POST;
+ } else if (!strcasecmp(method,"HEAD")) {
+ http_method = AST_HTTP_HEAD;
+ } else if (!strcasecmp(method,"PUT")) {
+ http_method = AST_HTTP_PUT;
+ }
+
uri = ast_skip_blanks(uri); /* Skip white space */
if (*uri) { /* terminate at the first blank */
char *c = ast_skip_nonblanks(uri);
-
if (*c) {
*c = '\0';
}
}
- /* process "Cookie: " lines */
- while (fgets(cookie, sizeof(cookie), ser->f)) {
+ /* process "Request Headers" lines */
+ while (fgets(header_line, sizeof(header_line), ser->f)) {
+ char *name, *value;
+
/* Trim trailing characters */
- ast_trim_blanks(cookie);
- if (ast_strlen_zero(cookie)) {
+ ast_trim_blanks(header_line);
+ if (ast_strlen_zero(header_line)) {
break;
}
- if (!strncasecmp(cookie, "Cookie: ", 8)) {
- vars = parse_cookies(cookie);
+
+ /* Are ast_strdupa is needed here ? */
+ /* ast_variable_new allocate memory himself */
+ /* value = ast_strdupa(header_line); */
+ value = header_line;
+ name = strsep(&value, ":");
+ if (!value) {
+ continue;
+ }
+
+ value = ast_skip_blanks(value);
+ if (ast_strlen_zero(value) || ast_strlen_zero(name)) {
+ continue;
+ }
+
+ ast_trim_blanks(name);
+
+ if (!headers) {
+ headers = ast_variable_new(name, value, __FILE__);
+ tail = headers;
} else {
- char *name, *val;
-
- val = cookie;
- name = strsep(&val, ":");
- if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
- continue;
- }
- ast_trim_blanks(name);
- val = ast_skip_blanks(val);
-
- if (!headers) {
- headers = ast_variable_new(name, val, __FILE__);
- tail = headers;
- } else {
- tail->next = ast_variable_new(name, val, __FILE__);
- tail = tail->next;
- }
+ tail->next = ast_variable_new(name, value, __FILE__);
+ tail = tail->next;
}
}
if (!*uri) {
- out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
- } else if (strcasecmp(buf, "post") && strcasecmp(buf, "get")) {
- out = ast_http_error(501, "Not Implemented", NULL,
- "Attempt to use unimplemented / unsupported method");
- } else { /* try to serve it */
- out = handle_uri(ser, uri, (!strcasecmp(buf, "get")) ? AST_HTTP_GET : AST_HTTP_POST,
- &status, &title, &contentlength, &vars, headers, &static_content);
- }
-
- /* If they aren't mopped up already, clean up the cookies */
- if (vars) {
- ast_variables_destroy(vars);
- }
+ ast_http_error(ser, 400, "Bad Request", "Invalid Request");
+ return NULL;
+ }
+
+ handle_uri(ser, uri, http_method, headers);
+
/* Clean up all the header information pulled as well */
if (headers) {
ast_variables_destroy(headers);
}
- if (out) {
- struct timeval now = ast_tvnow();
- char timebuf[256];
- struct ast_tm tm;
-
- ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
- fprintf(ser->f,
- "HTTP/1.1 %d %s\r\n"
- "Server: Asterisk/%s\r\n"
- "Date: %s\r\n"
- "Connection: close\r\n"
- "%s",
- status, title ? title : "OK", ast_get_version(), timebuf,
- static_content ? "" : "Cache-Control: no-cache, no-store\r\n");
- /* We set the no-cache headers only for dynamic content.
- * If you want to make sure the static file you requested is not from cache,
- * append a random variable to your GET request. Ex: 'something.html?r=109987734'
- */
- if (!contentlength) { /* opaque body ? just dump it hoping it is properly formatted */
- fprintf(ser->f, "%s", out->str);
- } else {
- char *tmp = strstr(out->str, "\r\n\r\n");
-
- if (tmp) {
- fprintf(ser->f, "Content-length: %d\r\n", contentlength);
- /* first write the header, then the body */
- if (fwrite(out->str, 1, (tmp + 4 - out->str), ser->f) != tmp + 4 - out->str) {
- ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
- }
- if (fwrite(tmp + 4, 1, contentlength, ser->f) != contentlength ) {
- ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
- }
- }
- }
- ast_free(out);
- }
-
- if (title) {
- ast_free(title);
- }
-
done:
- fclose(ser->f);
+ if (ser->f) {
+ fclose(ser->f);
+ }
ao2_ref(ser, -1);
ser = NULL;
-
return NULL;
}
@@ -814,7 +923,6 @@
if (!(redirect = ast_calloc(1, total_len))) {
return;
}
-
redirect->dest = redirect->target + target_len;
strcpy(redirect->target, target);
strcpy(redirect->dest, dest);
@@ -822,20 +930,18 @@
AST_RWLIST_WRLOCK(&uri_redirects);
target_len--; /* So we can compare directly with strlen() */
- if (AST_RWLIST_EMPTY(&uri_redirects)
- || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len) {
+ if ( AST_RWLIST_EMPTY(&uri_redirects)
+ || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
AST_RWLIST_UNLOCK(&uri_redirects);
-
return;
}
AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
- if (AST_RWLIST_NEXT(cur, entry)
- && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len) {
+ if ( AST_RWLIST_NEXT(cur, entry)
+ && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
AST_RWLIST_UNLOCK(&uri_redirects);
-
return;
}
}
@@ -934,7 +1040,6 @@
ast_config_destroy(cfg);
}
-
if (!have_sslbindaddr) {
https_desc.local_address.sin_addr = http_desc.local_address.sin_addr;
}
@@ -949,7 +1054,7 @@
if (ast_ssl_setup(https_desc.tls_cfg)) {
ast_tcptls_server_start(&https_desc);
}
-
+
return 0;
}
@@ -992,17 +1097,15 @@
if (AST_RWLIST_EMPTY(&uris)) {
ast_cli(a->fd, "None.\n");
} else {
- AST_RWLIST_TRAVERSE(&uris, urih, entry) {
- ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : ""), urih->description);
- }
+ AST_RWLIST_TRAVERSE(&uris, urih, entry)
+ ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
}
AST_RWLIST_UNLOCK(&uris);
ast_cli(a->fd, "\nEnabled Redirects:\n");
AST_RWLIST_RDLOCK(&uri_redirects);
- AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
+ AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
ast_cli(a->fd, " %s => %s\n", redirect->target, redirect->dest);
- }
if (AST_RWLIST_EMPTY(&uri_redirects)) {
ast_cli(a->fd, " None.\n");
}
Modified: team/group/manager_http_auth/main/manager.c
URL: http://svn.digium.com/view/asterisk/team/group/manager_http_auth/main/manager.c?view=diff&rev=161982&r1=161981&r2=161982
==============================================================================
--- team/group/manager_http_auth/main/manager.c (original)
+++ team/group/manager_http_auth/main/manager.c Tue Dec 9 09:18:43 2008
@@ -126,7 +126,11 @@
static int manager_enabled = 0;
static int webmanager_enabled = 0;
+#define DEFAULT_REALM "asterisk"
+static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
+
static int block_sockets;
+#define MAX_SESSIONS 256 /*!< Maximum active sessions */
static int num_sessions;
static int manager_debug; /*!< enable some debugging code in the manager */
@@ -138,7 +142,8 @@
* AMI session have managerid == 0; the entry is created upon a connect,
* and destroyed with the socket.
* HTTP sessions have managerid != 0, the value is used as a search key
- * to lookup sessions (using the mansession_id cookie).
+ * to lookup sessions (using the mansession_id cookie, or nonce key from
+ * Digest Authentication http header).
*/
#define MAX_BLACKLIST_CMD_LEN 2
static struct {
@@ -150,7 +155,6 @@
struct mansession {
pthread_t ms_t; /*!< Execution thread, basically useless */
- ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
/* XXX need to document which fields it is protecting */
struct sockaddr_in sin; /*!< address we are connecting from */
FILE *f; /*!< fdopen() on the underlying fd */
@@ -173,13 +177,17 @@
struct eventqent *last_ev; /*!< last event processed. */
int writetimeout; /*!< Timeout for ast_carefulwrite() */
int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
+ time_t noncetime; /*!< Timer for nonce value expiration */
+ unsigned long oldnonce; /*!< Stale nonce value */
+ unsigned long nc; /*!< incremental nonce counter */
[... 3300 lines stripped ...]
More information about the asterisk-commits
mailing list