aboutsummaryrefslogtreecommitdiffstats
path: root/main
diff options
context:
space:
mode:
authortilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2009-04-23 20:36:35 +0000
committertilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2009-04-23 20:36:35 +0000
commit23d5f93d74c885df5a8feee25229d68576b394d3 (patch)
treea9289da90accc4137203d99a5205aae4d8ed7edb /main
parentc052997dcc8db29dd063ab6eeb2bfa3be1f4ae96 (diff)
Support HTTP digest authentication for the http manager interface.
(closes issue #10961) Reported by: ys Patches: digest_auth_r148468_v5.diff uploaded by ys (license 281) SVN branch http://svn.digium.com/svn/asterisk/team/group/manager_http_auth Tested by: ys, twilson, tilghman Review: http://reviewboard.digium.com/r/223/ Reviewed by: tilghman,russellb,mmichelson git-svn-id: http://svn.digium.com/svn/asterisk/trunk@190349 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main')
-rw-r--r--main/astobj2.c14
-rw-r--r--main/http.c757
-rw-r--r--main/manager.c1570
-rw-r--r--main/utils.c120
4 files changed, 1688 insertions, 773 deletions
diff --git a/main/astobj2.c b/main/astobj2.c
index 36d943843..8bd57d87c 100644
--- a/main/astobj2.c
+++ b/main/astobj2.c
@@ -236,7 +236,7 @@ int __ao2_ref_debug(void *user_data, const int delta, char *tag, char *file, int
if (delta != 0) {
FILE *refo = fopen(REF_FILE,"a");
- fprintf(refo, "%p %s%d %s:%d:%s (%s) [@%d]\n", user_data, (delta<0? "":"+"), delta, file, line, funcname, tag, obj->priv_data.ref_counter);
+ fprintf(refo, "%p %s%d %s:%d:%s (%s) [@%d]\n", user_data, (delta<0? "":"+"), delta, file, line, funcname, tag, obj ? obj->priv_data.ref_counter : -1);
fclose(refo);
}
if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */
@@ -428,7 +428,7 @@ static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *
return NULL;
c->version = 1; /* 0 is a reserved value here */
- c->n_buckets = n_buckets;
+ c->n_buckets = hash_fn ? n_buckets : 1;
c->hash_fn = hash_fn ? hash_fn : hash_zero;
c->cmp_fn = cmp_fn;
@@ -444,10 +444,11 @@ struct ao2_container *__ao2_container_alloc_debug(const unsigned int n_buckets,
{
/* XXX maybe consistency check on arguments ? */
/* compute the container size */
- size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
+ const unsigned int num_buckets = hash_fn ? n_buckets : 1;
+ size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
struct ao2_container *c = __ao2_alloc_debug(container_size, container_destruct_debug, tag, file, line, funcname);
- return internal_ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn);
+ return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
}
struct ao2_container *__ao2_container_alloc(const unsigned int n_buckets, ao2_hash_fn *hash_fn,
@@ -456,10 +457,11 @@ struct ao2_container *__ao2_container_alloc(const unsigned int n_buckets, ao2_ha
/* XXX maybe consistency check on arguments ? */
/* compute the container size */
- size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
+ const unsigned int num_buckets = hash_fn ? n_buckets : 1;
+ size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
struct ao2_container *c = __ao2_alloc(container_size, container_destruct);
- return internal_ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn);
+ return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
}
/*!
diff --git a/main/http.c b/main/http.c
index 011c0893d..399e5140c 100644
--- a/main/http.c
+++ b/main/http.c
@@ -17,14 +17,14 @@
*/
/*!
- * \file
+ * \file
* \brief http server for AMI access
*
* \author Mark Spencer <markster@digium.com>
*
* This program implements a tiny http server
- * and was inspired by micro-httpd by Jef Poskanzer
- *
+ * and was inspired by micro-httpd by Jef Poskanzer
+ *
* \ref AstHTTP - AMI over the http protocol
*/
@@ -98,6 +98,7 @@ static struct {
const char *mtype;
} mimetypes[] = {
{ "png", "image/png" },
+ { "xml", "text/xml" },
{ "jpg", "image/jpeg" },
{ "js", "application/x-javascript" },
{ "wav", "audio/x-wav" },
@@ -115,7 +116,23 @@ struct http_uri_redirect {
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;
+ const char 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" },
+};
+
+const char *ast_get_http_method(enum ast_http_method method)
+{
+ return ast_http_methods_text[method].text;
+}
+
+const char *ast_http_ftype2mtype(const char *ftype)
{
int x;
@@ -126,21 +143,24 @@ static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
}
}
}
-
- snprintf(wkspace, wkspacelen, "text/%s", S_OR(ftype, "plain"));
-
- return wkspace;
+ return NULL;
}
-static uint32_t manid_from_vars(struct ast_variable *sid) {
- uint32_t mngid;
+uint32_t ast_http_manid_from_vars(struct ast_variable *headers)
+{
+ uint32_t mngid = 0;
+ struct ast_variable *v, *cookies;
- while (sid && strcmp(sid->name, "mansession_id"))
- sid = sid->next;
-
- if (!sid || sscanf(sid->value, "%x", &mngid) != 1)
- return 0;
-
+ 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,20 +171,31 @@ void ast_http_prefix(char *buf, int len)
}
}
-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 *ftype;
const 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[23];
+ struct ast_variable *v;
+ int not_modified = 0;
- /* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration
+ 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;
@@ -178,18 +209,20 @@ static struct ast_str *static_callback(struct ast_tcptls_session_instance *ser,
if (strstr(uri, "/..")) {
goto out403;
}
-
+
if ((ftype = strrchr(uri, '.'))) {
ftype++;
}
- mtype = ftype2mtype(ftype, wkspace, sizeof(wkspace));
-
+ if (!(mtype = ast_http_ftype2mtype(ftype))) {
+ snprintf(wkspace, sizeof(wkspace), "text/%s", S_OR(ftype, "plain"));
+ }
+
/* Cap maximum length */
if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
goto out403;
}
-
+
path = alloca(len);
sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
if (stat(path, &st)) {
@@ -198,142 +231,275 @@ static struct ast_str *static_callback(struct ast_tcptls_session_instance *ser,
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(ast_http_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: private\r\n"
- "Content-Length: %d\r\n"
- "Content-type: %s\r\n\r\n",
- ast_get_version(), buf, (int) st.st_size, mtype);
+ /* make "Etag:" http header value */
+ snprintf(etag, sizeof(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"));
- 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));
+ /* 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;
}
}
- close(fd);
+ if ( (http_header = ast_str_create(255)) == NULL) {
+ return -1;
+ }
- return NULL;
+ ast_str_set(&http_header, 0, "Content-type: %s\r\n"
+ "ETag: %s\r\n"
+ "Last-Modified: %s",
+ mtype,
+ etag,
+ timebuf);
+
+ /* ast_http_send() frees http_header, so we don't need to do it before returning */
+ 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 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.");
+ ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
+ return -1;
}
-
-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)
+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 = ast_str_create(512);
- struct ast_variable *v;
+ struct ast_str *out;
+ struct ast_variable *v, *cookies = NULL;
- if (out == NULL) {
- return out;
+ 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>&nbsp;&nbsp;Asterisk&trade; 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>&nbsp;&nbsp;Asterisk&trade; 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_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_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_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__,
};
-
+
static struct ast_http_uri staticuri = {
.callback = static_callback,
.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(ast_str_buffer(out));
+ }
+
+ 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 ? ast_str_buffer(http_header) : ""
+ );
+
+ /* send content */
+ if (method != AST_HTTP_HEAD || status_code >= 400) {
+ if (out) {
+ fprintf(ser->f, "%s", ast_str_buffer(out));
+ }
+
+ 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 || !out) {
+ ast_free(http_headers);
+ ast_free(out);
+ 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;
}
-/*! \brief
- * Link the new uri into the list.
+/* 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 || !out) {
+ ast_free(http_headers);
+ ast_free(out);
+ 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
+ * Link the new uri into the list.
*
* They are sorted by length of
* the string, not alphabetically. Duplicate entries are not replaced,
@@ -346,25 +512,19 @@ int ast_http_uri_link(struct ast_http_uri *urih)
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) {
+ strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
- AST_RWLIST_UNLOCK(&uris);
+ AST_RWLIST_UNLOCK(&uris);
return 0;
}
@@ -373,9 +533,9 @@ int ast_http_uri_link(struct ast_http_uri *urih)
AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
AST_RWLIST_UNLOCK(&uris);
-
+
return 0;
-}
+}
void ast_http_uri_unlink(struct ast_http_uri *urih)
{
@@ -411,91 +571,121 @@ void ast_http_uri_unlink_all_with_key(const char *key)
static void http_decode(char *s)
{
char *t;
-
+
for (t = s; *t; t++) {
- if (*t == '+')
+ if (*t == '+') {
*t = ' ';
+ }
}
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 *ast_http_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;
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(&params, "?");
-
- /* 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(&params, "&"))) {
- var = strsep(&val, "=");
- if (val) {
- http_decode(val);
+
+ strsep(&params, "?");
+ /* Extract arguments from the request and store them in variables. */
+ if (params) {
+ char *var, *val;
+
+ while ((val = strsep(&params, "&"))) {
+ 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 = "";
- }
- http_decode(var);
- if ((v = ast_variable_new(var, val, ""))) {
- if (vars) {
- prev->next = v;
- } else {
- vars = v;
- }
- prev = v;
+ get_vars = 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 +698,31 @@ static struct ast_str *handle_uri(struct ast_tcptls_session_instance *ser, char
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
@@ -624,12 +775,9 @@ static struct ast_variable *parse_cookies(char *cookies)
char *cur;
struct ast_variable *vars = NULL, *var;
- /* Skip Cookie: */
- cookies += 8;
-
while ((cur = strsep(&cookies, ";"))) {
char *name, *val;
-
+
name = val = cur;
strsep(&val, "=");
@@ -656,27 +804,55 @@ static struct ast_variable *parse_cookies(char *cookies)
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 */
@@ -687,101 +863,56 @@ static void *httpd_helper_thread(void *data)
}
}
- /* 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);
- } 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);
+ value = header_line;
+ name = strsep(&value, ":");
+ if (!value) {
+ continue;
+ }
- if (!headers) {
- headers = ast_variable_new(name, val, __FILE__);
- tail = headers;
- } else {
- tail->next = ast_variable_new(name, val, __FILE__);
- tail = tail->next;
- }
+ 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 {
+ 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);
+ ast_http_error(ser, 400, "Bad Request", "Invalid Request");
+ return NULL;
}
- /* If they aren't mopped up already, clean up the cookies */
- if (vars) {
- ast_variables_destroy(vars);
- }
+ 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", ast_str_buffer(out));
- } else {
- char *tmp = strstr(ast_str_buffer(out), "\r\n\r\n");
-
- if (tmp) {
- fprintf(ser->f, "Content-length: %d\r\n", contentlength);
- /* first write the header, then the body */
- if (fwrite(ast_str_buffer(out), 1, (tmp + 4 - ast_str_buffer(out)), ser->f) != tmp + 4 - ast_str_buffer(out)) {
- 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 +945,6 @@ static void add_redirect(const char *value)
if (!(redirect = ast_calloc(1, total_len))) {
return;
}
-
redirect->dest = redirect->target + target_len;
strcpy(redirect->target, target);
strcpy(redirect->dest, dest);
@@ -822,8 +952,8 @@ static void add_redirect(const char *value)
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);
@@ -831,11 +961,10 @@ static void add_redirect(const char *value)
}
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);
-
+ AST_RWLIST_UNLOCK(&uri_redirects);
return;
}
}
@@ -961,14 +1090,14 @@ static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_a
switch (cmd) {
case CLI_INIT:
e->command = "http show status";
- e->usage =
+ e->usage =
"Usage: http show status\n"
" Lists status of internal HTTP engine\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
-
+
if (a->argc != 3) {
return CLI_SHOWUSAGE;
}
@@ -992,17 +1121,15 @@ static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_a
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");
}
diff --git a/main/manager.c b/main/manager.c
index 8d5511669..5989e4786 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -22,7 +22,7 @@
*
* \author Mark Spencer <markster@digium.com>
*
- * \extref OpenSSL http://www.openssl.org - for AMI/SSL
+ * \extref OpenSSL http://www.openssl.org - for AMI/SSL
*
* At the moment this file contains a number of functions, namely:
*
@@ -126,8 +126,10 @@ static int httptimeout = 60;
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;
-static int num_sessions;
static int manager_debug; /*!< enable some debugging code in the manager */
@@ -138,7 +140,8 @@ static int manager_debug; /*!< enable some debugging code in the manager */
* 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 {
@@ -183,7 +186,6 @@ static struct {
*/
struct mansession_session {
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 */
@@ -206,6 +208,9 @@ struct mansession_session {
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 */
AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
AST_LIST_ENTRY(mansession_session) list;
};
@@ -221,9 +226,9 @@ struct mansession {
int fd;
};
-#define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next))
+static struct ao2_container *sessions = NULL;
-static AST_LIST_HEAD_STATIC(sessions, mansession_session);
+#define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next))
/*! \brief user descriptor, as read from the config file.
*
@@ -238,8 +243,9 @@ struct ast_manager_user {
int readperm; /*! Authorization for reading */
int writeperm; /*! Authorization for writing */
int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
- int displayconnects; /*!< XXX unused */
- int keep; /*!< mark entries created on a reload */
+ int displayconnects; /*!< XXX unused */
+ int keep; /*!< mark entries created on a reload */
+ char *a1_hash; /*!< precalculated A1 for Digest auth */
AST_RWLIST_ENTRY(ast_manager_user) list;
};
@@ -252,6 +258,9 @@ static AST_RWLIST_HEAD_STATIC(actions, manager_action);
/*! \brief list of hooks registered */
static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
+static struct eventqent *unref_event(struct eventqent *e);
+static void ref_event(struct eventqent *e);
+
/*! \brief Add a custom hook to be called when an event is fired */
void ast_manager_register_hook(struct manager_custom_hook *hook)
{
@@ -274,7 +283,7 @@ void ast_manager_unregister_hook(struct manager_custom_hook *hook)
* Event list management functions.
* We assume that the event list always has at least one element,
* and the delete code will not remove the last entry even if the
- *
+ *
*/
#if 0
static time_t __deb(time_t start, const char *msg)
@@ -336,8 +345,9 @@ static struct eventqent *grab_last(void)
/* the list is never empty now, but may become so when
* we optimize it in the future, so be prepared.
*/
- if (ret)
+ if (ret) {
ast_atomic_fetchadd_int(&ret->usecount, 1);
+ }
AST_LIST_UNLOCK(&all_events);
return ret;
}
@@ -416,12 +426,14 @@ static int ast_instring(const char *bigstr, const char *smallstr, const char del
do {
if ((next = strchr(val, delim))) {
- if (!strncmp(val, smallstr, (next - val)))
+ if (!strncmp(val, smallstr, (next - val))) {
return 1;
- else
+ } else {
continue;
- } else
+ }
+ } else {
return !strcmp(smallstr, val);
+ }
} while (*(val = (next + 1)));
return 0;
@@ -431,12 +443,14 @@ static int get_perm(const char *instr)
{
int x = 0, ret = 0;
- if (!instr)
+ if (!instr) {
return 0;
+ }
for (x = 0; x < ARRAY_LEN(perms); x++) {
- if (ast_instring(instr, perms[x].label, ','))
+ if (ast_instring(instr, perms[x].label, ',')) {
ret |= perms[x].num;
+ }
}
return ret;
@@ -450,36 +464,93 @@ static int strings_to_mask(const char *string)
{
const char *p;
- if (ast_strlen_zero(string))
+ if (ast_strlen_zero(string)) {
return -1;
+ }
- for (p = string; *p; p++)
- if (*p < '0' || *p > '9')
+ for (p = string; *p; p++) {
+ if (*p < '0' || *p > '9') {
break;
- if (!p) /* all digits */
+ }
+ }
+ if (!p) { /* all digits */
return atoi(string);
- if (ast_false(string))
+ }
+ if (ast_false(string)) {
return 0;
+ }
if (ast_true(string)) { /* all permissions */
int x, ret = 0;
- for (x = 0; x < ARRAY_LEN(perms); x++)
+ for (x = 0; x < ARRAY_LEN(perms); x++) {
ret |= perms[x].num;
+ }
return ret;
}
return get_perm(string);
}
-static int check_manager_session_inuse(const char *name)
+/*! \brief Unreference manager session object.
+ If no more references, then go ahead and delete it */
+static struct mansession_session *unref_mansession(struct mansession_session *s)
{
- struct mansession_session *session = NULL;
+ int refcount = ao2_ref(s, -1);
+ if (manager_debug) {
+ ast_log(LOG_DEBUG, "Mansession: %p refcount now %d\n", s, refcount - 1);
+ }
+ return s;
+}
- AST_LIST_LOCK(&sessions);
- AST_LIST_TRAVERSE(&sessions, session, list) {
- if (!strcasecmp(session->username, name))
- break;
+static void session_destructor(void *obj)
+{
+ struct mansession_session *session = obj;
+ struct eventqent *eqe = session->last_ev;
+
+ if (session->f != NULL) {
+ fclose(session->f);
}
- AST_LIST_UNLOCK(&sessions);
+ unref_event(eqe);
+}
+
+/*! \brief Allocate manager session structure and add it to the list of sessions */
+static struct mansession_session *build_mansession(struct sockaddr_in sin)
+{
+ struct mansession_session *newsession;
+
+ if (!(newsession = ao2_alloc(sizeof(*newsession), session_destructor))) {
+ return NULL;
+ }
+ memset(newsession, 0, sizeof(*newsession));
+ newsession->fd = -1;
+ newsession->waiting_thread = AST_PTHREADT_NULL;
+ newsession->writetimeout = 100;
+ newsession->send_events = -1;
+ newsession->sin = sin;
+
+ ao2_link(sessions, newsession);
+
+ return unref_mansession(newsession);
+}
+
+static int mansession_cmp_fn(void *obj, void *arg, int flags)
+{
+ struct mansession_session *s = obj;
+ char *str = arg;
+ return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
+}
+
+static void session_destroy(struct mansession_session *s)
+{
+ ao2_unlink(sessions, s);
+}
+
+
+static int check_manager_session_inuse(const char *name)
+{
+ struct mansession_session *session = ao2_find(sessions, (char*) name, OBJ_POINTER);
+ if (session) {
+ unref_mansession(session);
+ }
return session ? 1 : 0;
}
@@ -493,8 +564,9 @@ static struct ast_manager_user *get_manager_by_name_locked(const char *name)
struct ast_manager_user *user = NULL;
AST_RWLIST_TRAVERSE(&users, user, list)
- if (!strcasecmp(user->username, name))
+ if (!strcasecmp(user->username, name)) {
break;
+ }
return user;
}
@@ -508,10 +580,11 @@ static int manager_displayconnects (struct mansession_session *session)
int ret = 0;
AST_RWLIST_RDLOCK(&users);
- if ((user = get_manager_by_name_locked (session->username)))
+ if ((user = get_manager_by_name_locked (session->username))) {
ret = user->displayconnects;
+ }
AST_RWLIST_UNLOCK(&users);
-
+
return ret;
}
@@ -524,7 +597,7 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
switch (cmd) {
case CLI_INIT:
e->command = "manager show command";
- e->usage =
+ e->usage =
"Usage: manager show command <actionname>\n"
" Shows the detailed description for a specific Asterisk manager interface command.\n";
return NULL;
@@ -542,8 +615,9 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
return ret;
}
authority = ast_str_alloca(80);
- if (a->argc != 4)
+ if (a->argc != 4) {
return CLI_SHOWUSAGE;
+ }
AST_RWLIST_RDLOCK(&actions);
AST_RWLIST_TRAVERSE(&actions, cur, list) {
@@ -569,17 +643,19 @@ static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
return NULL;
case CLI_GENERATE:
- return NULL;
+ return NULL;
}
- if (a->argc == 3)
+
+ if (a->argc == 3) {
ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
- else if (a->argc == 4) {
- if (!strcasecmp(a->argv[3], "on"))
+ } else if (a->argc == 4) {
+ if (!strcasecmp(a->argv[3], "on")) {
manager_debug = 1;
- else if (!strcasecmp(a->argv[3], "off"))
+ } else if (!strcasecmp(a->argv[3], "off")) {
manager_debug = 0;
- else
+ } else {
return CLI_SHOWUSAGE;
+ }
}
return CLI_SUCCESS;
}
@@ -595,15 +671,16 @@ static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli
switch (cmd) {
case CLI_INIT:
e->command = "manager show user";
- e->usage =
+ e->usage =
" Usage: manager show user <user>\n"
" Display all information related to the manager user specified.\n";
return NULL;
case CLI_GENERATE:
l = strlen(a->word);
which = 0;
- if (a->pos != 3)
+ if (a->pos != 3) {
return NULL;
+ }
AST_RWLIST_RDLOCK(&users);
AST_RWLIST_TRAVERSE(&users, user, list) {
if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
@@ -615,8 +692,9 @@ static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli
return ret;
}
- if (a->argc != 4)
+ if (a->argc != 4) {
return CLI_SHOWUSAGE;
+ }
AST_RWLIST_RDLOCK(&users);
@@ -654,7 +732,7 @@ static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cl
switch (cmd) {
case CLI_INIT:
e->command = "manager show users";
- e->usage =
+ e->usage =
"Usage: manager show users\n"
" Prints a listing of all managers that are currently configured on that\n"
" system.\n";
@@ -662,8 +740,9 @@ static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cl
case CLI_GENERATE:
return NULL;
}
- if (a->argc != 3)
+ if (a->argc != 3) {
return CLI_SHOWUSAGE;
+ }
AST_RWLIST_RDLOCK(&users);
@@ -683,9 +762,8 @@ static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cl
AST_RWLIST_UNLOCK(&users);
- ast_cli(a->fd, "-------------------\n");
- ast_cli(a->fd, "%d manager users configured.\n", count_amu);
-
+ ast_cli(a->fd,"-------------------\n"
+ "%d manager users configured.\n", count_amu);
return CLI_SUCCESS;
}
@@ -699,13 +777,13 @@ static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli
switch (cmd) {
case CLI_INIT:
e->command = "manager show commands";
- e->usage =
+ e->usage =
"Usage: manager show commands\n"
" Prints a listing of all the available Asterisk manager interface commands.\n";
return NULL;
case CLI_GENERATE:
- return NULL;
- }
+ return NULL;
+ }
authority = ast_str_alloca(80);
ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
@@ -726,26 +804,30 @@ static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli
#define HSMCONN_FORMAT1 " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
#define HSMCONN_FORMAT2 " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
int count = 0;
+ struct ao2_iterator i;
+
switch (cmd) {
case CLI_INIT:
e->command = "manager show connected";
- e->usage =
+ e->usage =
"Usage: manager show connected\n"
" Prints a listing of the users that are currently connected to the\n"
"Asterisk manager interface.\n";
return NULL;
case CLI_GENERATE:
- return NULL;
+ return NULL;
}
ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
- AST_LIST_LOCK(&sessions);
- AST_LIST_TRAVERSE(&sessions, session, list) {
+ i = ao2_iterator_init(sessions, 0);
+ while ((session = ao2_iterator_next(&i))) {
+ ao2_lock(session);
ast_cli(a->fd, HSMCONN_FORMAT2, session->username, ast_inet_ntoa(session->sin.sin_addr), (int)(session->sessionstart), (int)(now - session->sessionstart), session->fd, session->inuse, session->readperm, session->writeperm);
count++;
+ ao2_unlock(session);
+ unref_mansession(session);
}
- AST_LIST_UNLOCK(&sessions);
ast_cli(a->fd, "%d users connected.\n", count);
@@ -760,7 +842,7 @@ static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_c
switch (cmd) {
case CLI_INIT:
e->command = "manager show eventq";
- e->usage =
+ e->usage =
"Usage: manager show eventq\n"
" Prints a listing of all events pending in the Asterisk manger\n"
"event queue.\n";
@@ -792,8 +874,9 @@ static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_
case CLI_GENERATE:
return NULL;
}
- if (a->argc > 2)
+ if (a->argc > 2) {
return CLI_SHOWUSAGE;
+ }
reload_manager();
return CLI_SUCCESS;
}
@@ -810,12 +893,6 @@ static struct ast_cli_entry cli_manager[] = {
AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
};
-/*
- * Decrement the usecount for the event; if it goes to zero,
- * (why check for e->next ?) wakeup the
- * main thread, which is in charge of freeing the record.
- * Returns the next record.
- */
static struct eventqent *unref_event(struct eventqent *e)
{
ast_atomic_fetchadd_int(&e->usecount, -1);
@@ -843,18 +920,14 @@ static void free_session(struct mansession_session *session)
if (session->f != NULL)
fclose(session->f);
- ast_mutex_destroy(&session->__lock);
ast_free(session);
unref_event(eqe);
}
static void destroy_session(struct mansession_session *session)
{
- AST_LIST_LOCK(&sessions);
- AST_LIST_REMOVE(&sessions, session, list);
- ast_atomic_fetchadd_int(&num_sessions, -1);
+ ao2_unlink(sessions, session);
free_session(session);
- AST_LIST_UNLOCK(&sessions);
}
/*
@@ -913,20 +986,24 @@ struct ast_variable *astman_get_variables(const struct message *m)
for (x = 0; x < m->hdrcount; x++) {
char *parse, *var, *val;
- if (strncasecmp("Variable: ", m->headers[x], varlen))
+ if (strncasecmp("Variable: ", m->headers[x], varlen)) {
continue;
+ }
parse = ast_strdupa(m->headers[x] + varlen);
AST_STANDARD_APP_ARGS(args, parse);
- if (!args.argc)
+ if (!args.argc) {
continue;
+ }
for (y = 0; y < args.argc; y++) {
- if (!args.vars[y])
+ if (!args.vars[y]) {
continue;
+ }
var = val = ast_strdupa(args.vars[y]);
strsep(&val, "=");
- if (!val || ast_strlen_zero(var))
+ if (!val || ast_strlen_zero(var)) {
continue;
+ }
cur = ast_variable_new(var, val, "");
cur->next = head;
head = cur;
@@ -970,8 +1047,9 @@ void astman_append(struct mansession *s, const char *fmt, ...)
va_list ap;
struct ast_str *buf;
- if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
+ if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
return;
+ }
va_start(ap, fmt);
ast_str_set_va(&buf, 0, fmt, ap);
@@ -1006,16 +1084,19 @@ static void astman_send_response_full(struct mansession *s, const struct message
const char *id = astman_get_header(m, "ActionID");
astman_append(s, "Response: %s\r\n", resp);
- if (!ast_strlen_zero(id))
+ if (!ast_strlen_zero(id)) {
astman_append(s, "ActionID: %s\r\n", id);
- if (listflag)
+ }
+ if (listflag) {
astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
- if (msg == MSG_MOREDATA)
+ }
+ if (msg == MSG_MOREDATA) {
return;
- else if (msg)
+ } else if (msg) {
astman_append(s, "Message: %s\r\n\r\n", msg);
- else
+ } else {
astman_append(s, "\r\n");
+ }
}
void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
@@ -1052,10 +1133,11 @@ static int set_eventmask(struct mansession *s, const char *eventmask)
{
int maskint = strings_to_mask(eventmask);
- ast_mutex_lock(&s->session->__lock);
- if (maskint >= 0)
+ ao2_lock(s);
+ if (maskint >= 0) {
s->session->send_events = maskint;
- ast_mutex_unlock(&s->session->__lock);
+ }
+ ao2_unlock(s);
return maskint;
}
@@ -1074,8 +1156,9 @@ static int authenticate(struct mansession *s, const struct message *m)
int error = -1;
struct ast_manager_user *user = NULL;
- if (ast_strlen_zero(username)) /* missing username */
+ if (ast_strlen_zero(username)) { /* missing username */
return -1;
+ }
/* locate user in locked state */
AST_RWLIST_WRLOCK(&users);
@@ -1099,14 +1182,16 @@ static int authenticate(struct mansession *s, const struct message *m)
MD5Final(digest, &md5);
for (x = 0; x < 16; x++)
len += sprintf(md5key + len, "%2.2x", digest[x]);
- if (!strcmp(md5key, key))
+ if (!strcmp(md5key, key)) {
error = 0;
+ }
} else {
- ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
+ ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
S_OR(s->session->challenge, ""));
}
- } else if (password && user->secret && !strcmp(password, user->secret))
+ } else if (password && user->secret && !strcmp(password, user->secret)) {
error = 0;
+ }
if (error) {
ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
@@ -1115,14 +1200,14 @@ static int authenticate(struct mansession *s, const struct message *m)
}
/* auth complete */
-
+
ast_copy_string(s->session->username, username, sizeof(s->session->username));
s->session->readperm = user->readperm;
s->session->writeperm = user->writeperm;
s->session->writetimeout = user->writetimeout;
s->session->sessionstart = time(NULL);
set_eventmask(s, astman_get_header(m, "Events"));
-
+
AST_RWLIST_UNLOCK(&users);
return 0;
}
@@ -1178,13 +1263,15 @@ static int action_getconfig(struct mansession *s, const struct message *m)
if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
lineno = 0;
astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
- for (v = ast_variable_browse(cfg, cur_category); v; v = v->next)
+ for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) {
astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
+ }
catcount++;
}
}
- if (!ast_strlen_zero(category) && catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
+ if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
astman_append(s, "No categories found\r\n");
+ }
ast_config_destroy(cfg);
astman_append(s, "\r\n");
@@ -1218,8 +1305,9 @@ static int action_listcategories(struct mansession *s, const struct message *m)
astman_append(s, "Category-%06d: %s\r\n", catcount, category);
catcount++;
}
- if (catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
+ if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
astman_append(s, "Error: no categories found\r\n");
+ }
ast_config_destroy(cfg);
astman_append(s, "\r\n");
@@ -1227,14 +1315,15 @@ static int action_listcategories(struct mansession *s, const struct message *m)
}
-
+
/*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
static void json_escape(char *out, const char *in)
{
for (; *in; in++) {
- if (*in == '\\' || *in == '\"')
+ if (*in == '\\' || *in == '\"') {
*out++ = '\\';
+ }
*out++ = *in;
}
*out = '\0';
@@ -1281,11 +1370,13 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
}
json_escape(buf, category);
astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
- if (!comma1)
+ if (!comma1) {
comma1 = 1;
+ }
for (v = ast_variable_browse(cfg, category); v; v = v->next) {
- if (comma2)
+ if (comma2) {
astman_append(s, ",");
+ }
if (buf_len < 2 * strlen(v->name) + 1) {
buf_len *= 2;
buf = alloca(buf_len);
@@ -1298,8 +1389,9 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
}
json_escape(buf, v->value);
astman_append(s, "%s\"", buf);
- if (!comma2)
+ if (!comma2) {
comma2 = 1;
+ }
}
astman_append(s, "]");
}
@@ -1346,7 +1438,7 @@ static enum error_type handle_updates(struct mansession *s, const struct message
object = 1;
value++;
}
-
+
snprintf(hdr, sizeof(hdr), "Match-%06d", x);
match = astman_get_header(m, hdr);
@@ -1364,8 +1456,9 @@ static enum error_type handle_updates(struct mansession *s, const struct message
}
if (ast_strlen_zero(match)) {
ast_category_append(cfg, category);
- } else
+ } else {
ast_category_insert(cfg, category, match);
+ }
} else if (!strcasecmp(action, "renamecat")) {
if (ast_strlen_zero(value)) {
result = UNSPECIFIED_ARGUMENT;
@@ -1418,15 +1511,16 @@ static enum error_type handle_updates(struct mansession *s, const struct message
break;
}
if (!(category = ast_category_get(cfg, cat))) {
- result = UNKNOWN_CATEGORY;
+ result = UNKNOWN_CATEGORY;
break;
}
if (!(v = ast_variable_new(var, value, dfn))) {
result = FAILURE_ALLOCATION;
break;
}
- if (object || (match && !strcasecmp(match, "object")))
+ if (object || (match && !strcasecmp(match, "object"))) {
v->object = 1;
+ }
ast_variable_append(category, v);
} else if (!strcasecmp(action, "insert")) {
if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
@@ -1497,8 +1591,9 @@ static int action_updateconfig(struct mansession *s, const struct message *m)
}
astman_send_ack(s, m, NULL);
if (!ast_strlen_zero(rld)) {
- if (ast_true(rld))
+ if (ast_true(rld)) {
rld = NULL;
+ }
ast_module_reload(rld);
}
} else {
@@ -1584,21 +1679,24 @@ static int action_waitevent(struct mansession *s, const struct message *m)
const char *id = astman_get_header(m, "ActionID");
char idText[256];
- if (!ast_strlen_zero(id))
+ if (!ast_strlen_zero(id)) {
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
- else
+ } else {
idText[0] = '\0';
+ }
if (!ast_strlen_zero(timeouts)) {
sscanf(timeouts, "%i", &timeout);
- if (timeout < -1)
+ if (timeout < -1) {
timeout = -1;
+ }
/* XXX maybe put an upper bound, or prevent the use of 0 ? */
}
- ast_mutex_lock(&s->session->__lock);
- if (s->session->waiting_thread != AST_PTHREADT_NULL)
+ ao2_lock(s);
+ if (s->session->waiting_thread != AST_PTHREADT_NULL) {
pthread_kill(s->session->waiting_thread, SIGURG);
+ }
if (s->session->managerid) { /* AMI-over-HTTP session */
/*
@@ -1609,43 +1707,52 @@ static int action_waitevent(struct mansession *s, const struct message *m)
time_t now = time(NULL);
int max = s->session->sessiontimeout - now - 10;
- if (max < 0) /* We are already late. Strange but possible. */
+ if (max < 0) { /* We are already late. Strange but possible. */
max = 0;
- if (timeout < 0 || timeout > max)
+ }
+ if (timeout < 0 || timeout > max) {
timeout = max;
- if (!s->session->send_events) /* make sure we record events */
+ }
+ if (!s->session->send_events) { /* make sure we record events */
s->session->send_events = -1;
+ }
}
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
/* XXX should this go inside the lock ? */
s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */
ast_debug(1, "Starting waiting for an event!\n");
for (x = 0; x < timeout || timeout < 0; x++) {
- ast_mutex_lock(&s->session->__lock);
- if (NEW_EVENT(s))
+ ao2_lock(s);
+ if (NEW_EVENT(s)) {
needexit = 1;
+ }
/* We can have multiple HTTP session point to the same mansession entry.
* The way we deal with it is not very nice: newcomers kick out the previous
* HTTP session. XXX this needs to be improved.
*/
- if (s->session->waiting_thread != pthread_self())
+ if (s->session->waiting_thread != pthread_self()) {
needexit = 1;
- if (s->session->needdestroy)
+ }
+ if (s->session->needdestroy) {
needexit = 1;
- ast_mutex_unlock(&s->session->__lock);
- if (needexit)
+ }
+ ao2_unlock(s);
+ if (needexit) {
break;
+ }
if (s->session->managerid == 0) { /* AMI session */
- if (ast_wait_for_input(s->session->fd, 1000))
+ if (ast_wait_for_input(s->session->fd, 1000)) {
break;
+ }
} else { /* HTTP session */
sleep(1);
}
}
ast_debug(1, "Finished waiting for an event!\n");
- ast_mutex_lock(&s->session->__lock);
+
+ ao2_lock(s);
if (s->session->waiting_thread == pthread_self()) {
struct eventqent *eqe;
astman_send_response(s, m, "Success", "Waiting for Event completed.");
@@ -1665,7 +1772,7 @@ static int action_waitevent(struct mansession *s, const struct message *m)
} else {
ast_debug(1, "Abandoning event request!\n");
}
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
return 0;
}
@@ -1682,9 +1789,10 @@ static int action_listcommands(struct mansession *s, const struct message *m)
astman_start_ack(s, m);
AST_RWLIST_TRAVERSE(&actions, cur, list) {
- if (s->session->writeperm & cur->authority || cur->authority == 0)
+ if (s->session->writeperm & cur->authority || cur->authority == 0) {
astman_append(s, "%s: %s (Priv: %s)\r\n",
cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
+ }
}
astman_append(s, "\r\n");
@@ -1726,14 +1834,22 @@ static int action_logoff(struct mansession *s, const struct message *m)
static int action_login(struct mansession *s, const struct message *m)
{
+
+ /* still authenticated - don't process again */
+ if (s->session->authenticated) {
+ astman_send_ack(s, m, "Already authenticated");
+ return 0;
+ }
+
if (authenticate(s, m)) {
sleep(1);
astman_send_error(s, m, "Authentication failed");
return -1;
}
s->session->authenticated = 1;
- if (manager_displayconnects(s->session))
+ if (manager_displayconnects(s->session)) {
ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
+ }
ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
astman_send_ack(s, m, "Authentication accepted");
return 0;
@@ -1744,12 +1860,13 @@ static int action_challenge(struct mansession *s, const struct message *m)
const char *authtype = astman_get_header(m, "AuthType");
if (!strcasecmp(authtype, "MD5")) {
- if (ast_strlen_zero(s->session->challenge))
+ if (ast_strlen_zero(s->session->challenge)) {
snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
- ast_mutex_lock(&s->session->__lock);
+ }
+ ao2_lock(s);
astman_start_ack(s, m);
astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
} else {
astman_send_error(s, m, "Must specify AuthType");
}
@@ -1826,8 +1943,9 @@ static int action_setvar(struct mansession *s, const struct message *m)
pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
- if (c)
+ if (c) {
ast_channel_unlock(c);
+ }
astman_send_ack(s, m, "Variable Set");
@@ -1877,15 +1995,16 @@ static int action_getvar(struct mansession *s, const struct message *m)
pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
}
- if (c)
+ if (c) {
ast_channel_unlock(c);
+ }
astman_start_ack(s, m);
astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
return 0;
}
-static char mandescr_status[] =
+static char mandescr_status[] =
"Description: Lists channel status along with requested channel vars.\n"
"Variables: (Names marked with * are required)\n"
" *Channel: Name of the channel to query for status\n"
@@ -1893,7 +2012,7 @@ static char mandescr_status[] =
" ActionID: Optional ID for this transaction\n"
"Will return the status information of each channel along with the\n"
"value for the specified channel variables.\n";
-
+
/*! \brief Manager "status" command to show channels */
/* Needs documentation... */
@@ -1915,14 +2034,15 @@ static int action_status(struct mansession *s, const struct message *m)
);
struct ast_str *str = ast_str_create(1000);
- if (!ast_strlen_zero(id))
+ if (!ast_strlen_zero(id)) {
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
- else
+ } else {
idText[0] = '\0';
+ }
- if (all)
+ if (all) {
c = ast_channel_walk_locked(NULL);
- else {
+ } else {
c = ast_get_channel_by_name_locked(name);
if (!c) {
astman_send_error(s, m, "No such channel");
@@ -1958,10 +2078,11 @@ static int action_status(struct mansession *s, const struct message *m)
}
channels++;
- if (c->_bridge)
+ if (c->_bridge) {
snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
- else
+ } else {
bridge[0] = '\0';
+ }
if (c->pbx) {
if (c->cdr) {
elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
@@ -2012,8 +2133,9 @@ static int action_status(struct mansession *s, const struct message *m)
ast_state2str(c->_state), bridge, c->uniqueid, ast_str_buffer(str), idText);
}
ast_channel_unlock(c);
- if (!all)
+ if (!all) {
break;
+ }
c = ast_channel_walk_locked(c);
}
astman_append(s,
@@ -2057,12 +2179,13 @@ static int action_sendtext(struct mansession *s, const struct message *m)
res = ast_sendtext(c, textmsg);
ast_channel_unlock(c);
-
- if (res > 0)
+
+ if (res > 0) {
astman_send_ack(s, m, "Success");
- else
+ } else {
astman_send_error(s, m, "Failure");
-
+ }
+
return res;
}
@@ -2137,18 +2260,23 @@ static int action_redirect(struct mansession *s, const struct message *m)
} else {
res = -1;
}
- if (!res)
+ if (!res) {
astman_send_ack(s, m, "Dual Redirect successful");
- else
+ } else {
astman_send_error(s, m, "Secondary redirect failed");
- } else
+ }
+ } else {
astman_send_ack(s, m, "Redirect successful");
- } else
+ }
+ } else {
astman_send_error(s, m, "Redirect failed");
- if (chan)
+ }
+ if (chan) {
ast_channel_unlock(chan);
- if (chan2)
+ }
+ if (chan2) {
ast_channel_unlock(chan2);
+ }
return 0;
}
@@ -2170,7 +2298,7 @@ static int action_atxfer(struct mansession *s, const struct message *m)
struct ast_call_feature *atxfer_feature = NULL;
char *feature_code = NULL;
- if (ast_strlen_zero(name)) {
+ if (ast_strlen_zero(name)) {
astman_send_error(s, m, "No channel specified");
return 0;
}
@@ -2271,8 +2399,9 @@ static int action_command(struct mansession *s, const struct message *m)
}
astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
- if (!ast_strlen_zero(id))
+ if (!ast_strlen_zero(id)) {
astman_append(s, "ActionID: %s\r\n", id);
+ }
/* FIXME: Wedge a ActionID response in here, waiting for later changes */
ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
l = lseek(fd, 0, SEEK_END); /* how many chars available */
@@ -2296,8 +2425,9 @@ static int action_command(struct mansession *s, const struct message *m)
close(fd);
unlink(template);
astman_append(s, "--END COMMAND--\r\n\r\n");
- if (final_buf)
+ if (final_buf) {
ast_free(final_buf);
+ }
return 0;
}
@@ -2340,8 +2470,9 @@ static void *fast_originate(void *data)
in->vars, in->account, &chan);
}
- if (!chan)
- snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
+ if (!chan) {
+ snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
+ }
/* Tell the manager what happened with the channel */
manager_event(EVENT_FLAG_CALL, "OriginateResponse",
"%s%s"
@@ -2353,16 +2484,17 @@ static void *fast_originate(void *data)
"Uniqueid: %s\r\n"
"CallerIDNum: %s\r\n"
"CallerIDName: %s\r\n",
- in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success",
- chan ? chan->name : requested_channel, in->context, in->exten, reason,
+ in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success",
+ chan ? chan->name : requested_channel, in->context, in->exten, reason,
chan ? chan->uniqueid : "<null>",
S_OR(in->cid_num, "<unknown>"),
S_OR(in->cid_name, "<unknown>")
);
/* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
- if (chan)
+ if (chan) {
ast_channel_unlock(chan);
+ }
ast_free(in);
return NULL;
}
@@ -2434,13 +2566,15 @@ static int action_originate(struct mansession *s, const struct message *m)
ast_copy_string(tmp2, callerid, sizeof(tmp2));
ast_callerid_parse(tmp2, &n, &l);
if (n) {
- if (ast_strlen_zero(n))
+ if (ast_strlen_zero(n)) {
n = NULL;
+ }
}
if (l) {
ast_shrink_phone_number(l);
- if (ast_strlen_zero(l))
+ if (ast_strlen_zero(l)) {
l = NULL;
+ }
}
if (!ast_strlen_zero(codecs)) {
format = 0;
@@ -2454,13 +2588,15 @@ static int action_originate(struct mansession *s, const struct message *m)
if (!ast_strlen_zero(id))
snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id);
ast_copy_string(fast->tech, tech, sizeof(fast->tech));
- ast_copy_string(fast->data, data, sizeof(fast->data));
+ ast_copy_string(fast->data, data, sizeof(fast->data));
ast_copy_string(fast->app, app, sizeof(fast->app));
ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
- if (l)
+ if (l) {
ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
- if (n)
+ }
+ if (n) {
ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
+ }
fast->vars = vars;
ast_copy_string(fast->context, context, sizeof(fast->context));
ast_copy_string(fast->exten, exten, sizeof(fast->exten));
@@ -2493,17 +2629,18 @@ static int action_originate(struct mansession *s, const struct message *m)
}
res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
} else {
- if (exten && context && pi)
+ if (exten && context && pi) {
res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
- else {
+ } else {
astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
return 0;
}
}
- if (!res)
+ if (!res) {
astman_send_ack(s, m, "Originate successfully queued");
- else
+ } else {
astman_send_error(s, m, "Originate failed");
+ }
return 0;
}
@@ -2591,8 +2728,9 @@ static int action_extensionstate(struct mansession *s, const struct message *m)
astman_send_error(s, m, "Extension not specified");
return 0;
}
- if (ast_strlen_zero(context))
+ if (ast_strlen_zero(context)) {
context = "default";
+ }
status = ast_extension_state(NULL, context, exten);
ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
astman_start_ack(s, m);
@@ -2649,7 +2787,7 @@ static int process_events(struct mansession *s)
{
int ret = 0;
- ast_mutex_lock(&s->session->__lock);
+ ao2_lock(s->session);
if (s->session->f != NULL) {
struct eventqent *eqe;
@@ -2664,7 +2802,7 @@ static int process_events(struct mansession *s)
s->session->last_ev = unref_event(s->session->last_ev);
}
}
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s->session);
return ret;
}
@@ -2704,10 +2842,11 @@ static int action_coresettings(struct mansession *s, const struct message *m)
const char *actionid = astman_get_header(m, "ActionID");
char idText[150];
- if (!ast_strlen_zero(actionid))
+ if (!ast_strlen_zero(actionid)) {
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
- else
+ } else {
idText[0] = '\0';
+ }
astman_append(s, "Response: Success\r\n"
"%s"
@@ -2718,14 +2857,14 @@ static int action_coresettings(struct mansession *s, const struct message *m)
"CoreMaxLoadAvg: %f\r\n"
"CoreRunUser: %s\r\n"
"CoreRunGroup: %s\r\n"
- "CoreMaxFilehandles: %d\r\n"
+ "CoreMaxFilehandles: %d\r\n"
"CoreRealTimeEnabled: %s\r\n"
"CoreCDRenabled: %s\r\n"
"CoreHTTPenabled: %s\r\n"
"\r\n",
idText,
AMI_VERSION,
- ast_get_version(),
+ ast_get_version(),
ast_config_AST_SYSTEM_NAME,
option_maxcalls,
option_maxload,
@@ -2753,10 +2892,11 @@ static int action_corestatus(struct mansession *s, const struct message *m)
char reloadtime[150];
struct ast_tm tm;
- if (!ast_strlen_zero(actionid))
+ if (!ast_strlen_zero(actionid)) {
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
- else
+ } else {
idText[0] = '\0';
+ }
ast_localtime(&ast_startuptime, &tm, NULL);
ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
@@ -2789,10 +2929,11 @@ static int action_reload(struct mansession *s, const struct message *m)
const char *module = astman_get_header(m, "Module");
int res = ast_module_reload(S_OR(module, NULL));
- if (res == 2)
+ if (res == 2) {
astman_send_ack(s, m, "Module Reloaded");
- else
+ } else {
astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
+ }
return 0;
}
@@ -2802,7 +2943,7 @@ static char mandescr_coreshowchannels[] =
"Variables:\n"
" ActionID: Optional Action id for message matching.\n";
-/*! \brief Manager command "CoreShowChannels" - List currently defined channels
+/*! \brief Manager command "CoreShowChannels" - List currently defined channels
* and some information about them. */
static int action_coreshowchannels(struct mansession *s, const struct message *m)
{
@@ -2812,12 +2953,13 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
int numchans = 0;
int duration, durh, durm, durs;
- if (!ast_strlen_zero(actionid))
+ if (!ast_strlen_zero(actionid)) {
snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
- else
+ } else {
actionidtext[0] = '\0';
+ }
- astman_send_listack(s, m, "Channels will follow", "start");
+ astman_send_listack(s, m, "Channels will follow", "start");
while ((c = ast_channel_walk_locked(c)) != NULL) {
struct ast_channel *bc = ast_bridged_channel(c);
@@ -2864,7 +3006,7 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
return 0;
}
-static char mandescr_modulecheck[] =
+static char mandescr_modulecheck[] =
"Description: Checks if Asterisk module is loaded\n"
"Variables: \n"
" ActionID: <id> Action ID for this transaction. Will be returned.\n"
@@ -2905,10 +3047,11 @@ static int manager_modulecheck(struct mansession *s, const struct message *m)
version = ast_file_version_find(filename);
#endif
- if (!ast_strlen_zero(id))
+ if (!ast_strlen_zero(id)) {
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
- else
+ } else {
idText[0] = '\0';
+ }
astman_append(s, "Response: Success\r\n%s", idText);
#if !defined(LOW_MEMORY)
astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
@@ -2916,7 +3059,7 @@ static int manager_modulecheck(struct mansession *s, const struct message *m)
return 0;
}
-static char mandescr_moduleload[] =
+static char mandescr_moduleload[] =
"Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
"Variables: \n"
" ActionID: <id> Action ID for this transaction. Will be returned.\n"
@@ -2933,37 +3076,42 @@ static int manager_moduleload(struct mansession *s, const struct message *m)
const char *module = astman_get_header(m, "Module");
const char *loadtype = astman_get_header(m, "LoadType");
- if (!loadtype || strlen(loadtype) == 0)
+ if (!loadtype || strlen(loadtype) == 0) {
astman_send_error(s, m, "Incomplete ModuleLoad action.");
- if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
+ }
+ if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0) {
astman_send_error(s, m, "Need module name");
+ }
if (!strcasecmp(loadtype, "load")) {
res = ast_load_resource(module);
- if (res)
+ if (res) {
astman_send_error(s, m, "Could not load module.");
- else
+ } else {
astman_send_ack(s, m, "Module loaded.");
+ }
} else if (!strcasecmp(loadtype, "unload")) {
res = ast_unload_resource(module, AST_FORCE_SOFT);
- if (res)
+ if (res) {
astman_send_error(s, m, "Could not unload module.");
- else
+ } else {
astman_send_ack(s, m, "Module unloaded.");
+ }
} else if (!strcasecmp(loadtype, "reload")) {
if (module != NULL) {
res = ast_module_reload(module);
- if (res == 0)
+ if (res == 0) {
astman_send_error(s, m, "No such module.");
- else if (res == 1)
+ } else if (res == 1) {
astman_send_error(s, m, "Module does not support reload action.");
- else
+ } else {
astman_send_ack(s, m, "Module reloaded.");
+ }
} else {
ast_module_reload(NULL); /* Reload all modules */
astman_send_ack(s, m, "All modules reloaded");
}
- } else
+ } else
astman_send_error(s, m, "Incomplete ModuleLoad action.");
return 0;
}
@@ -2987,21 +3135,21 @@ static int process_message(struct mansession *s, const struct message *m)
int ret = 0;
struct manager_action *tmp;
const char *user = astman_get_header(m, "Username");
+ int (*call_func)(struct mansession *s, const struct message *m) = NULL;
ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
- ast_debug(1, "Manager received command '%s'\n", action);
if (ast_strlen_zero(action)) {
- ast_mutex_lock(&s->session->__lock);
+ ao2_lock(s);
astman_send_error(s, m, "Missing action in request");
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
return 0;
}
if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
- ast_mutex_lock(&s->session->__lock);
+ ao2_lock(s);
astman_send_error(s, m, "Permission denied");
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
return 0;
}
@@ -3009,34 +3157,42 @@ static int process_message(struct mansession *s, const struct message *m)
(!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
if (check_manager_session_inuse(user)) {
sleep(1);
- ast_mutex_lock(&s->session->__lock);
+ ao2_lock(s);
astman_send_error(s, m, "Login Already In Use");
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
return -1;
}
}
AST_RWLIST_RDLOCK(&actions);
AST_RWLIST_TRAVERSE(&actions, tmp, list) {
- if (strcasecmp(action, tmp->action))
+ if (strcasecmp(action, tmp->action)) {
continue;
- if (s->session->writeperm & tmp->authority || tmp->authority == 0)
- ret = tmp->func(s, m);
- else
+ }
+ if (s->session->writeperm & tmp->authority || tmp->authority == 0) {
+ call_func = tmp->func;
+ } else {
astman_send_error(s, m, "Permission denied");
+ tmp = NULL;
+ }
break;
}
AST_RWLIST_UNLOCK(&actions);
- if (!tmp) {
+ if (tmp && call_func) {
+ /* call AMI function after actions list are unlocked */
+ ast_debug(1, "Running action '%s'\n", tmp->action);
+ ret = call_func(s, m);
+ } else {
char buf[512];
snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
- ast_mutex_lock(&s->session->__lock);
+ ao2_lock(s);
astman_send_error(s, m, buf);
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
}
- if (ret)
+ if (ret) {
return ret;
+ }
/* Once done with our message, deliver any pending events unless the
requester doesn't want them as part of this response.
*/
@@ -3068,12 +3224,13 @@ static int get_input(struct mansession *s, char *output)
*/
for (x = 0; x < s->session->inlen; x++) {
int cr; /* set if we have \r */
- if (src[x] == '\r' && x+1 < s->session->inlen && src[x+1] == '\n')
+ if (src[x] == '\r' && x+1 < s->session->inlen && src[x + 1] == '\n') {
cr = 2; /* Found. Update length to include \r\n */
- else if (src[x] == '\n')
+ } else if (src[x] == '\n') {
cr = 1; /* also accept \n only */
- else
+ } else {
continue;
+ }
memmove(output, src, x); /*... but trim \r\n */
output[x] = '\0'; /* terminate the string */
x += cr; /* number of bytes used */
@@ -3089,40 +3246,42 @@ static int get_input(struct mansession *s, char *output)
res = 0;
while (res == 0) {
/* XXX do we really need this locking ? */
- ast_mutex_lock(&s->session->__lock);
+ ao2_lock(s);
if (s->session->pending_event) {
s->session->pending_event = 0;
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
return 0;
}
s->session->waiting_thread = pthread_self();
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
res = ast_wait_for_input(s->session->fd, -1); /* return 0 on timeout ? */
- ast_mutex_lock(&s->session->__lock);
+ ao2_lock(s);
s->session->waiting_thread = AST_PTHREADT_NULL;
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
}
if (res < 0) {
/* If we get a signal from some other thread (typically because
* there are new events queued), return 0 to notify the caller.
*/
- if (errno == EINTR || errno == EAGAIN)
+ if (errno == EINTR || errno == EAGAIN) {
return 0;
+ }
ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
return -1;
}
- ast_mutex_lock(&s->session->__lock);
+
+ ao2_lock(s);
res = fread(src + s->session->inlen, 1, maxlen - s->session->inlen, s->session->f);
- if (res < 1)
+ if (res < 1) {
res = -1; /* error return */
- else {
+ } else {
s->session->inlen += res;
src[s->session->inlen] = '\0';
res = 0;
}
- ast_mutex_unlock(&s->session->__lock);
+ ao2_unlock(s);
return res;
}
@@ -3134,16 +3293,18 @@ static int do_message(struct mansession *s)
for (;;) {
/* Check if any events are pending and do them if needed */
- if (process_events(s))
+ if (process_events(s)) {
return -1;
+ }
res = get_input(s, header_buf);
if (res == 0) {
continue;
} else if (res > 0) {
- if (ast_strlen_zero(header_buf))
+ if (ast_strlen_zero(header_buf)) {
return process_message(s, &m) ? -1 : 0;
- else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
+ } else if (m.hdrcount < (AST_MAX_MANHEADERS - 1)) {
m.headers[m.hdrcount++] = ast_strdupa(header_buf);
+ }
} else {
return res;
}
@@ -3161,55 +3322,52 @@ static int do_message(struct mansession *s)
static void *session_do(void *data)
{
struct ast_tcptls_session_instance *ser = data;
- struct mansession_session *session = ast_calloc(1, sizeof(*session));
- struct mansession s = {.session = NULL, };
+ struct mansession_session *session = build_mansession(ser->remote_address);
+ struct mansession s = { NULL, };
int flags;
int res;
- if (session == NULL)
+ if (session == NULL) {
goto done;
-
- session->writetimeout = 100;
- session->waiting_thread = AST_PTHREADT_NULL;
+ }
+ ao2_lock(sessions);
flags = fcntl(ser->fd, F_GETFL);
- if (!block_sockets) /* make sure socket is non-blocking */
+ if (!block_sockets) { /* make sure socket is non-blocking */
flags |= O_NONBLOCK;
- else
+ } else {
flags &= ~O_NONBLOCK;
+ }
fcntl(ser->fd, F_SETFL, flags);
- ast_mutex_init(&session->__lock);
- session->send_events = -1;
/* Hook to the tail of the event queue */
session->last_ev = grab_last();
/* these fields duplicate those in the 'ser' structure */
- session->fd = ser->fd;
- session->f = ser->f;
+ session->fd = s.fd = ser->fd;
+ session->f = s.f = ser->f;
session->sin = ser->remote_address;
s.session = session;
AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
- AST_LIST_LOCK(&sessions);
- AST_LIST_INSERT_HEAD(&sessions, session, list);
- ast_atomic_fetchadd_int(&num_sessions, 1);
- AST_LIST_UNLOCK(&sessions);
-
+ ao2_unlock(sessions);
astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
for (;;) {
- if ((res = do_message(&s)) < 0)
+ if ((res = do_message(&s)) < 0) {
break;
+ }
}
/* session is over, explain why and terminate */
if (session->authenticated) {
- if (manager_displayconnects(session))
+ if (manager_displayconnects(session)) {
ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
+ }
ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
} else {
- if (displayconnects)
+ if (displayconnects) {
ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
+ }
ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
}
@@ -3239,23 +3397,24 @@ static void purge_sessions(int n_max)
{
struct mansession_session *session;
time_t now = time(NULL);
+ struct ao2_iterator i;
- AST_LIST_LOCK(&sessions);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, session, list) {
+ i = ao2_iterator_init(sessions, 0);
+ while ((session = ao2_iterator_next(&i)) && n_max > 0) {
+ unref_mansession(session);
+ ao2_lock(session);
if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
- AST_LIST_REMOVE_CURRENT(list);
- ast_atomic_fetchadd_int(&num_sessions, -1);
if (session->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(session)) {
ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
session->username, ast_inet_ntoa(session->sin.sin_addr));
}
- free_session(session); /* XXX outside ? */
- if (--n_max <= 0)
- break;
+ ao2_unlock(session);
+ session_destroy(session);
+ n_max--;
+ } else {
+ ao2_unlock(session);
}
}
- AST_LIST_TRAVERSE_SAFE_END;
- AST_LIST_UNLOCK(&sessions);
}
/*
@@ -3267,8 +3426,9 @@ static int append_event(const char *str, int category)
struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
static int seq; /* sequence number */
- if (!tmp)
+ if (!tmp) {
return -1;
+ }
/* need to init all fields, because ast_malloc() does not */
tmp->usecount = 0;
@@ -3299,13 +3459,11 @@ int __manager_event(int category, const char *event,
va_list ap;
struct timeval now;
struct ast_str *buf;
+ struct ao2_iterator i;
- /* Abort if there aren't any manager sessions */
- if (!num_sessions)
- return 0;
-
- if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
+ if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE))) {
return -1;
+ }
cat_str = authority_to_str(category, &auth);
ast_str_set(&buf, 0,
@@ -3336,21 +3494,22 @@ int __manager_event(int category, const char *event,
append_event(ast_str_buffer(buf), category);
/* Wake up any sleeping sessions */
- AST_LIST_LOCK(&sessions);
- AST_LIST_TRAVERSE(&sessions, session, list) {
- ast_mutex_lock(&session->__lock);
- if (session->waiting_thread != AST_PTHREADT_NULL)
+ i = ao2_iterator_init(sessions, 0);
+ while ((session = ao2_iterator_next(&i))) {
+ ao2_lock(session);
+ if (session->waiting_thread != AST_PTHREADT_NULL) {
pthread_kill(session->waiting_thread, SIGURG);
- else
+ } else {
/* We have an event to process, but the mansession is
* not waiting for it. We still need to indicate that there
* is an event waiting so that get_input processes the pending
* event instead of polling.
*/
session->pending_event = 1;
- ast_mutex_unlock(&session->__lock);
+ }
+ ao2_unlock(session);
+ unref_mansession(session);
}
- AST_LIST_UNLOCK(&sessions);
AST_RWLIST_RDLOCK(&manager_hooks);
AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
@@ -3419,10 +3578,11 @@ static int ast_manager_register_struct(struct manager_action *act)
}
}
- if (prev)
+ if (prev) {
AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
- else
+ } else {
AST_RWLIST_INSERT_HEAD(&actions, act, list);
+ }
ast_verb(2, "Manager registered action %s\n", act->action);
@@ -3437,8 +3597,9 @@ int ast_manager_register2(const char *action, int auth, int (*func)(struct manse
{
struct manager_action *cur = NULL;
- if (!(cur = ast_calloc(1, sizeof(*cur))))
+ if (!(cur = ast_calloc(1, sizeof(*cur)))) {
return -1;
+ }
cur->action = action;
cur->authority = auth;
@@ -3488,25 +3649,64 @@ static char *contenttype[] = {
static struct mansession_session *find_session(uint32_t ident, int incinuse)
{
struct mansession_session *session;
+ struct ao2_iterator i;
- if (ident == 0)
+ if (ident == 0) {
return NULL;
+ }
- AST_LIST_LOCK(&sessions);
- AST_LIST_TRAVERSE(&sessions, session, list) {
- ast_mutex_lock(&session->__lock);
+ i = ao2_iterator_init(sessions, 0);
+ while ((session = ao2_iterator_next(&i))) {
+ ao2_lock(session);
if (session->managerid == ident && !session->needdestroy) {
ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
+ unref_mansession(session);
break;
}
- ast_mutex_unlock(&session->__lock);
+ ao2_unlock(session);
+ unref_mansession(session);
}
- AST_LIST_UNLOCK(&sessions);
return session;
}
-int astman_is_authed(uint32_t ident)
+/*!
+ * locate an http session in the list.
+ * The search keys (nonce) and (username) is value from received
+ * "Authorization" http header.
+ * As well as in find_session() function, the value of the nonce can't be zero.
+ * (0 meansi, that the session used for AMI socket connection).
+ * Flag (stale) is set, if client used valid, but old, nonce value.
+ *
+ */
+static struct mansession_session *find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
+{
+ struct mansession_session *session;
+ struct ao2_iterator i;
+
+ if (nonce == 0 || username == NULL || stale == NULL) {
+ return NULL;
+ }
+
+ i = ao2_iterator_init(sessions, 0);
+ while ((session = ao2_iterator_next(&i))) {
+ ao2_lock(session);
+ if (!strcasecmp(session->username, username) && session->managerid == nonce) {
+ *stale = 0;
+ unref_mansession(session);
+ break;
+ } else if (!strcasecmp(session->username, username) && session->oldnonce == nonce) {
+ *stale = 1;
+ unref_mansession(session);
+ break;
+ }
+ ao2_unlock(session);
+ unref_mansession(session);
+ }
+ return session;
+}
+
+int astman_is_authed(uint32_t ident)
{
int authed;
struct mansession_session *session;
@@ -3516,7 +3716,7 @@ int astman_is_authed(uint32_t ident)
authed = (session->authenticated != 0);
- ast_mutex_unlock(&session->__lock);
+ ao2_unlock(session);
return authed;
}
@@ -3525,18 +3725,24 @@ int astman_verify_session_readpermissions(uint32_t ident, int perm)
{
int result = 0;
struct mansession_session *session;
+ struct ao2_iterator i;
- AST_LIST_LOCK(&sessions);
- AST_LIST_TRAVERSE(&sessions, session, list) {
- ast_mutex_lock(&session->__lock);
+ if (ident == 0) {
+ return 0;
+ }
+
+ i = ao2_iterator_init(sessions, 0);
+ while ((session = ao2_iterator_next(&i))) {
+ ao2_lock(session);
if ((session->managerid == ident) && (session->readperm & perm)) {
result = 1;
- ast_mutex_unlock(&session->__lock);
+ ao2_unlock(session);
+ unref_mansession(session);
break;
}
- ast_mutex_unlock(&session->__lock);
+ ao2_unlock(session);
+ unref_mansession(session);
}
- AST_LIST_UNLOCK(&sessions);
return result;
}
@@ -3544,18 +3750,24 @@ int astman_verify_session_writepermissions(uint32_t ident, int perm)
{
int result = 0;
struct mansession_session *session;
+ struct ao2_iterator i;
- AST_LIST_LOCK(&sessions);
- AST_LIST_TRAVERSE(&sessions, session, list) {
- ast_mutex_lock(&session->__lock);
+ if (ident == 0) {
+ return 0;
+ }
+
+ i = ao2_iterator_init(sessions, 0);
+ while ((session = ao2_iterator_next(&i))) {
+ ao2_lock(session);
if ((session->managerid == ident) && (session->writeperm & perm)) {
result = 1;
- ast_mutex_unlock(&session->__lock);
+ ao2_unlock(session);
+ unref_mansession(session);
break;
}
- ast_mutex_unlock(&session->__lock);
+ ao2_unlock(session);
+ unref_mansession(session);
}
- AST_LIST_UNLOCK(&sessions);
return result;
}
@@ -3577,10 +3789,11 @@ static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
ast_str_append(out, 0, "%s", buf);
dst = buf;
space = sizeof(buf);
- if (*src == '\0')
+ if (*src == '\0') {
break;
+ }
}
-
+
if ( (mode & 2) && !isalnum(*src)) {
*dst++ = '_';
space--;
@@ -3625,29 +3838,11 @@ struct variable_count {
int count;
};
-static int compress_char(char c)
-{
- c &= 0x7f;
- if (c < 32)
- return 0;
- else if (c >= 'a' && c <= 'z')
- return c - 64;
- else if (c > 'z')
- return '_';
- else
- return c - 32;
-}
-
static int variable_count_hash_fn(const void *vvc, const int flags)
{
const struct variable_count *vc = vvc;
- int res = 0, i;
- for (i = 0; i < 5; i++) {
- if (vc->varname[i] == '\0')
- break;
- res += compress_char(vc->varname[i]) << (i * 6);
- }
- return res;
+
+ return ast_str_hash(vc->varname);
}
static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
@@ -3676,7 +3871,7 @@ static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
*
* General: the unformatted text is used as a value of
* XML output: to be completed
- *
+ *
* \verbatim
* Each section is within <response type="object" id="xxx">
* where xxx is taken from ajaxdest variable or defaults to unknown
@@ -3689,7 +3884,7 @@ static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
* Sections (blank lines in the input) are separated by a <HR>
*
*/
-static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
+static void xml_translate(struct ast_str **out, char *in, struct ast_variable *get_vars, enum output_format format)
{
struct ast_variable *v;
const char *dest = NULL;
@@ -3701,30 +3896,41 @@ static void xml_translate(struct ast_str **out, char *in, struct ast_variable *v
struct variable_count *vc = NULL;
struct ao2_container *vco = NULL;
- for (v = vars; v; v = v->next) {
- if (!dest && !strcasecmp(v->name, "ajaxdest"))
- dest = v->value;
- else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
- objtype = v->value;
+ if (xml) {
+ /* dest and objtype need only for XML format */
+ for (v = get_vars; v; v = v->next) {
+ if (!strcasecmp(v->name, "ajaxdest")) {
+ dest = v->value;
+ } else if (!strcasecmp(v->name, "ajaxobjtype")) {
+ objtype = v->value;
+ }
+ }
+ if (ast_strlen_zero(dest)) {
+ dest = "unknown";
+ }
+ if (ast_strlen_zero(objtype)) {
+ objtype = "generic";
+ }
}
- if (!dest)
- dest = "unknown";
- if (!objtype)
- objtype = "generic";
/* we want to stop when we find an empty line */
while (in && *in) {
val = strsep(&in, "\r\n"); /* mark start and end of line */
- if (in && *in == '\n') /* remove trailing \n if any */
+ if (in && *in == '\n') { /* remove trailing \n if any */
in++;
+ }
ast_trim_blanks(val);
ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
if (ast_strlen_zero(val)) {
- if (in_data) { /* close data */
+ /* empty line */
+ if (in_data) {
+ /* close data in Opaque mode */
ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
in_data = 0;
}
+
if (inobj) {
+ /* close block */
ast_str_append(out, 0, xml ? " /></response>\n" :
"<tr><td colspan=\"2\"><hr></td></tr>\r\n");
inobj = 0;
@@ -3734,54 +3940,57 @@ static void xml_translate(struct ast_str **out, char *in, struct ast_variable *v
continue;
}
- /* we expect Name: value lines */
- if (in_data) {
- var = NULL;
- } else {
- var = strsep(&val, ":");
- if (val) { /* found the field name */
- val = ast_skip_blanks(val);
- ast_trim_blanks(var);
- } else { /* field name not found, move to opaque mode */
- val = var;
- var = "Opaque-data";
- }
- }
-
if (!inobj) {
- if (xml)
+ /* start new block */
+ if (xml) {
ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
- else
- ast_str_append(out, 0, "<body>\n");
+ }
vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
inobj = 1;
}
- if (!in_data) { /* build appropriate line start */
- ast_str_append(out, 0, xml ? " " : "<tr><td>");
- if ((vc = ao2_find(vco, var, 0)))
- vc->count++;
- else {
- /* Create a new entry for this one */
- vc = ao2_alloc(sizeof(*vc), NULL);
- vc->varname = var;
- vc->count = 1;
- ao2_link(vco, vc);
- }
- xml_copy_escape(out, var, xml ? 1 | 2 : 0);
- if (vc->count > 1)
- ast_str_append(out, 0, "-%d", vc->count);
- ao2_ref(vc, -1);
- ast_str_append(out, 0, xml ? "='" : "</td><td>");
- if (!strcmp(var, "Opaque-data"))
- in_data = 1;
+ if (in_data) {
+ /* Process data field in Opaque mode */
+ xml_copy_escape(out, val, 0); /* data field */
+ ast_str_append(out, 0, xml ? "\n" : "<br>\n");
+ continue;
}
+
+ /* We expect "Name: value" line here */
+ var = strsep(&val, ":");
+ if (val) {
+ /* found the field name */
+ val = ast_skip_blanks(val);
+ ast_trim_blanks(var);
+ } else {
+ /* field name not found, switch to opaque mode */
+ val = var;
+ var = "Opaque-data";
+ in_data = 1;
+ }
+
+
+ ast_str_append(out, 0, xml ? " " : "<tr><td>");
+ if ((vc = ao2_find(vco, var, 0))) {
+ vc->count++;
+ } else {
+ /* Create a new entry for this one */
+ vc = ao2_alloc(sizeof(*vc), NULL);
+ vc->varname = var;
+ vc->count = 1;
+ ao2_link(vco, vc);
+ }
+
+ xml_copy_escape(out, var, xml ? 1 | 2 : 0); /* data name */
+ if (vc->count > 1) {
+ ast_str_append(out, 0, "-%d", vc->count);
+ }
+ ao2_ref(vc, -1);
+ ast_str_append(out, 0, xml ? "='" : "</td><td>");
xml_copy_escape(out, val, 0); /* data field */
- if (!in_data)
- ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
- else
- ast_str_append(out, 0, xml ? "\n" : "<br>\n");
+ ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
}
+
if (inobj) {
ast_str_append(out, 0, xml ? " /></response>\n" :
"<tr><td colspan=\"2\"><hr></td></tr>\r\n");
@@ -3789,43 +3998,55 @@ static void xml_translate(struct ast_str **out, char *in, struct ast_variable *v
}
}
-static struct ast_str *generic_http_callback(enum output_format format,
- struct sockaddr_in *remote_address, const char *uri, enum ast_http_method method,
- struct ast_variable *params, int *status,
- char **title, int *contentlength)
+static int generic_http_callback(struct ast_tcptls_session_instance *ser,
+ enum ast_http_method method,
+ enum output_format format,
+ struct sockaddr_in *remote_address, const char *uri,
+ struct ast_variable *get_params,
+ struct ast_variable *headers)
{
struct mansession s = {.session = NULL, };
struct mansession_session *session = NULL;
uint32_t ident = 0;
int blastaway = 0;
- struct ast_variable *v;
+ struct ast_variable *v, *cookies, *params = get_params;
char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
- struct ast_str *out = NULL;
+ struct ast_str *http_header = NULL, *out = NULL;
struct message m = { 0 };
unsigned int x;
size_t hdrlen;
- for (v = params; v; v = v->next) {
+ if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
+ ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
+ return -1;
+ }
+
+ cookies = ast_http_get_cookies(headers);
+ for (v = cookies; v; v = v->next) {
if (!strcasecmp(v->name, "mansession_id")) {
sscanf(v->value, "%x", &ident);
break;
}
}
+ if (cookies) {
+ ast_variables_destroy(cookies);
+ }
if (!(session = find_session(ident, 1))) {
+
+ /**/
/* Create new session.
* While it is not in the list we don't need any locking
*/
- if (!(session = ast_calloc(1, sizeof(*session)))) {
- *status = 500;
- goto generic_callback_out;
+ if (!(session = build_mansession(*remote_address))) {
+ ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
+ return -1;
}
+ ao2_lock(session);
session->sin = *remote_address;
session->fd = -1;
session->waiting_thread = AST_PTHREADT_NULL;
session->send_events = 0;
- ast_mutex_init(&session->__lock);
- ast_mutex_lock(&session->__lock);
session->inuse = 1;
/*!\note There is approximately a 1 in 1.8E19 chance that the following
* calculation will produce 0, which is an invalid ID, but due to the
@@ -3835,24 +4056,35 @@ static struct ast_str *generic_http_callback(enum output_format format,
while ((session->managerid = ast_random() ^ (unsigned long) session) == 0);
session->last_ev = grab_last();
AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
- AST_LIST_LOCK(&sessions);
- AST_LIST_INSERT_HEAD(&sessions, session, list);
- ast_atomic_fetchadd_int(&num_sessions, 1);
- AST_LIST_UNLOCK(&sessions);
}
+ ao2_unlock(session);
- s.session = session;
+ http_header = ast_str_create(128);
+ out = ast_str_create(2048);
- ast_mutex_unlock(&session->__lock);
-
- if (!(out = ast_str_create(1024))) {
- *status = 500;
+ if (http_header == NULL || out == NULL) {
+ ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n");
goto generic_callback_out;
}
+ s.session = session;
s.fd = mkstemp(template); /* create a temporary file for command output */
unlink(template);
+ if (s.fd <= -1) {
+ ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)\n");
+ goto generic_callback_out;
+ }
s.f = fdopen(s.fd, "w+");
+ if (!s.f) {
+ ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
+ ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)\n");
+ close(s.fd);
+ goto generic_callback_out;
+ }
+
+ if (method == AST_HTTP_POST) {
+ params = ast_http_get_post_vars(ser, headers);
+ }
for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
hdrlen = strlen(v->name) + strlen(v->value) + 3;
@@ -3877,14 +4109,13 @@ static struct ast_str *generic_http_callback(enum output_format format,
session->needdestroy = 1;
}
- ast_str_append(&out, 0,
- "Content-type: text/%s\r\n"
- "Cache-Control: no-cache;\r\n"
- "Set-Cookie: mansession_id=\"%08x\"; Version=\"1\"; Max-Age=%d\r\n"
- "Pragma: SuppressEvents\r\n"
- "\r\n",
- contenttype[format],
- session->managerid, httptimeout);
+ ast_str_append(&http_header, 0,
+ "Content-type: text/%s\r\n"
+ "Cache-Control: no-cache;\r\n"
+ "Set-Cookie: mansession_id=\"%08x\"; Version=\"1\"; Max-Age=%d"
+ "Pragma: SuppressEvents\r\n",
+ contenttype[format],
+ session->managerid, httptimeout);
if (format == FORMAT_XML) {
ast_str_append(&out, 0, "<ajax-response>\n");
@@ -3897,7 +4128,7 @@ static struct ast_str *generic_http_callback(enum output_format format,
#define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
#define TEST_STRING \
- "<form action=\"manager\">\n\
+ "<form action=\"manager\" method=\"post\">\n\
Action: <select name=\"action\">\n\
<option value=\"\">-----&gt;</option>\n\
<option value=\"login\">login</option>\n\
@@ -3919,15 +4150,16 @@ static struct ast_str *generic_http_callback(enum output_format format,
if (s.f != NULL) { /* have temporary output */
char *buf;
size_t l = ftell(s.f);
-
+
if (l) {
if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, s.fd, 0))) {
ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n");
} else {
- if (format == FORMAT_XML || format == FORMAT_HTML)
+ if (format == FORMAT_XML || format == FORMAT_HTML) {
xml_translate(&out, buf, params, format);
- else
+ } else {
ast_str_append(&out, 0, "%s", buf);
+ }
munmap(buf, l);
}
} else if (format == FORMAT_XML || format == FORMAT_HTML) {
@@ -3940,10 +4172,11 @@ static struct ast_str *generic_http_callback(enum output_format format,
if (format == FORMAT_XML) {
ast_str_append(&out, 0, "</ajax-response>\n");
- } else if (format == FORMAT_HTML)
+ } else if (format == FORMAT_HTML) {
ast_str_append(&out, 0, "</table></body>\r\n");
+ }
- ast_mutex_lock(&session->__lock);
+ ao2_lock(session);
/* Reset HTTP timeout. If we're not authenticated, keep it extremely short */
session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);
@@ -3953,42 +4186,381 @@ static struct ast_str *generic_http_callback(enum output_format format,
blastaway = 1;
} else {
ast_debug(1, "Need destroy, but can't do it yet!\n");
- if (session->waiting_thread != AST_PTHREADT_NULL)
+ if (session->waiting_thread != AST_PTHREADT_NULL) {
pthread_kill(session->waiting_thread, SIGURG);
+ }
session->inuse--;
}
- } else
+ } else {
session->inuse--;
- ast_mutex_unlock(&session->__lock);
+ }
+ ao2_unlock(session);
+
+ ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
+ http_header = out = NULL;
- if (blastaway)
- destroy_session(session);
generic_callback_out:
- if (*status != 200)
- return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
- return out;
+ /* Clear resource */
+
+ if (method == AST_HTTP_POST && params) {
+ ast_variables_destroy(params);
+ }
+ if (http_header) {
+ ast_free(http_header);
+ }
+ if (out) {
+ ast_free(out);
+ }
+ if (session->f) {
+ fclose(session->f);
+ }
+
+ if (session != NULL && blastaway) {
+ session_destroy(session);
+ }
+ return 0;
}
-static struct ast_str *manager_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)
+static int auth_http_callback(struct ast_tcptls_session_instance *ser,
+ enum ast_http_method method,
+ enum output_format format,
+ struct sockaddr_in *remote_address, const char *uri,
+ struct ast_variable *get_params,
+ struct ast_variable *headers)
{
- return generic_http_callback(FORMAT_HTML, &ser->remote_address, uri, method, params, status, title, contentlength);
+ struct mansession_session *session = NULL;
+ struct mansession s = { NULL, };
+ struct ast_variable *v, *params = get_params;
+ char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
+ struct ast_str *http_header = NULL, *out = NULL;
+ size_t result_size = 512;
+ struct message m = { 0 };
+ unsigned int x;
+ size_t hdrlen;
+
+ time_t time_now = time(NULL);
+ unsigned long nonce = 0, nc;
+ struct ast_http_digest d = { NULL, };
+ struct ast_manager_user *user = NULL;
+ int stale = 0;
+ char resp_hash[256]="";
+ /* Cache for user data */
+ char u_username[80];
+ int u_readperm;
+ int u_writeperm;
+ int u_writetimeout;
+ int u_displayconnects;
+
+ if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
+ ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
+ return -1;
+ }
+
+ /* Find "Authorization: " header */
+ for (v = headers; v; v = v->next) {
+ if (!strcasecmp(v->name, "Authorization")) {
+ break;
+ }
+ }
+
+ if (!v || ast_strlen_zero(v->value)) {
+ goto out_401; /* Authorization Header not present - send auth request */
+ }
+
+ /* Digest found - parse */
+ if (ast_string_field_init(&d, 128)) {
+ ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
+ return -1;
+ }
+
+ if (ast_parse_digest(v->value, &d, 0, 1)) {
+ /* Error in Digest - send new one */
+ nonce = 0;
+ goto out_401;
+ }
+ if (sscanf(d.nonce, "%lx", &nonce) != 1) {
+ ast_log(LOG_WARNING, "Received incorrect nonce in Digest <%s>\n", d.nonce);
+ nonce = 0;
+ goto out_401;
+ }
+
+ AST_RWLIST_WRLOCK(&users);
+ user = get_manager_by_name_locked(d.username);
+ if(!user) {
+ AST_RWLIST_UNLOCK(&users);
+ ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username);
+ nonce = 0;
+ goto out_401;
+ }
+
+ /* --- We have User for this auth, now check ACL */
+ if (user->ha && !ast_apply_ha(user->ha, remote_address)) {
+ AST_RWLIST_UNLOCK(&users);
+ ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username);
+ ast_http_error(ser, 403, "Permission denied", "Permission denied\n");
+ return -1;
+ }
+
+ /* --- We have auth, so check it */
+
+ /* compute the expected response to compare with what we received */
+ {
+ char a2[256];
+ char a2_hash[256];
+ char resp[256];
+
+ /* XXX Now request method are hardcoded in A2 */
+ snprintf(a2, sizeof(a2), "%s:%s", ast_get_http_method(method), d.uri);
+ ast_md5_hash(a2_hash, a2);
+
+ if (d.qop) {
+ /* RFC 2617 */
+ snprintf(resp, sizeof(resp), "%s:%08lx:%s:%s:auth:%s", user->a1_hash, nonce, d.nc, d.cnonce, a2_hash);
+ } else {
+ /* RFC 2069 */
+ snprintf(resp, sizeof(resp), "%s:%08lx:%s", user->a1_hash, nonce, a2_hash);
+ }
+ ast_md5_hash(resp_hash, resp);
+ }
+
+ if (!d.nonce || strncasecmp(d.response, resp_hash, strlen(resp_hash))) {
+ /* Something was wrong, so give the client to try with a new challenge */
+ AST_RWLIST_UNLOCK(&users);
+ nonce = 0;
+ goto out_401;
+ }
+
+ /*
+ * User are pass Digest authentication.
+ * Now, cache the user data and unlock user list.
+ */
+ ast_copy_string(u_username, user->username, sizeof(u_username));
+ u_readperm = user->readperm;
+ u_writeperm = user->writeperm;
+ u_displayconnects = user->displayconnects;
+ u_writetimeout = user->writetimeout;
+ AST_RWLIST_UNLOCK(&users);
+
+ if (!(session = find_session_by_nonce(d.username, nonce, &stale))) {
+ /*
+ * Create new session.
+ * While it is not in the list we don't need any locking
+ */
+ if (!(session = build_mansession(*remote_address))) {
+ ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
+ return -1;
+ }
+ ao2_lock(session);
+
+ ast_copy_string(session->username, u_username, sizeof(session->username));
+ session->managerid = nonce;
+ session->last_ev = grab_last();
+ AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
+
+ session->readperm = u_readperm;
+ session->writeperm = u_writeperm;
+ session->writetimeout = u_writetimeout;
+
+ if (u_displayconnects) {
+ ast_verb(2, "HTTP Manager '%s' logged in from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
+ }
+ session->noncetime = session->sessionstart = time_now;
+ session->authenticated = 1;
+
+ ast_log(LOG_EVENT, "HTTP Manager '%s' logged in from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
+
+ } else if (stale) {
+ /*
+ * Session found, but nonce is stale.
+ *
+ * This could be because an old request (w/old nonce) arrived.
+ *
+ * This may be as the result of http proxy usage (separate delay or
+ * multipath) or in a situation where a page was refreshed too quickly
+ * (seen in Firefox).
+ *
+ * In this situation, we repeat the 401 auth with the current nonce
+ * value.
+ */
+ nonce = session->managerid;
+ ao2_unlock(session);
+ stale = 1;
+ goto out_401;
+ } else {
+ sscanf(d.nc, "%lx", &nc);
+ if (session->nc >= nc || ((time_now - session->noncetime) > 62) ) {
+ /*
+ * Nonce time expired (> 2 minutes) or something wrong with nonce
+ * counter.
+ *
+ * Create new nonce key and resend Digest auth request. Old nonce
+ * is saved for stale checking...
+ */
+ session->nc = 0; /* Reset nonce counter */
+ session->oldnonce = session->managerid;
+ nonce = session->managerid = ast_random();
+ session->noncetime = time_now;
+ ao2_unlock(session);
+ stale = 1;
+ goto out_401;
+ } else {
+ session->nc = nc; /* All OK, save nonce counter */
+ }
+ }
+
+
+ /* Reset session timeout. */
+ session->sessiontimeout = time(NULL) + (httptimeout > 5 ? httptimeout : 5);
+ ao2_unlock(session);
+
+ s.session = session;
+ s.fd = mkstemp(template); /* create a temporary file for command output */
+ unlink(template);
+ if (s.fd <= -1) {
+ ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)\n");
+ goto auth_callback_out;
+ }
+ s.f = fdopen(s.fd, "w+");
+ if (!s.f) {
+ ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
+ ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)\n");
+ close(s.fd);
+ goto auth_callback_out;
+ }
+
+ if (method == AST_HTTP_POST) {
+ params = ast_http_get_post_vars(ser, headers);
+ }
+
+ for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
+ hdrlen = strlen(v->name) + strlen(v->value) + 3;
+ m.headers[m.hdrcount] = alloca(hdrlen);
+ snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
+ ast_verb(4, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
+ m.hdrcount = x + 1;
+ }
+
+ if (process_message(&s, &m)) {
+ if (u_displayconnects) {
+ ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
+ }
+ ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
+
+ session->needdestroy = 1;
+ }
+
+ if (s.f) {
+ result_size = ftell(s.f); /* Calculate approx. size of result */
+ }
+
+ http_header = ast_str_create(80);
+ out = ast_str_create(result_size * 2 + 512);
+
+ if (http_header == NULL || out == NULL) {
+ ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n");
+ goto auth_callback_out;
+ }
+
+ ast_str_append(&http_header, 0, "Content-type: text/%s", contenttype[format]);
+
+ if (format == FORMAT_XML) {
+ ast_str_append(&out, 0, "<ajax-response>\n");
+ } else if (format == FORMAT_HTML) {
+ ast_str_append(&out, 0,
+ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+ "<html><head>\r\n"
+ "<title>Asterisk&trade; Manager Interface</title>\r\n"
+ "</head><body style=\"background-color: #ffffff;\">\r\n"
+ "<form method=\"POST\">\r\n"
+ "<table align=\"center\" style=\"background-color: #f1f1f1;\" width=\"500\">\r\n"
+ "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\"><h1>Manager Tester</h1></th></tr>\r\n"
+ "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\">Action: <input name=\"action\" /> Cmd: <input name=\"command\" /><br>"
+ "<input type=\"submit\" value=\"Send request\" /></th></tr>\r\n");
+ }
+
+ if (s.f != NULL) { /* have temporary output */
+ char *buf;
+ size_t l = ftell(s.f);
+
+ if (l) {
+ if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s.fd, 0))) {
+ if (format == FORMAT_XML || format == FORMAT_HTML) {
+ xml_translate(&out, buf, params, format);
+ } else {
+ ast_str_append(&out, 0, "%s", buf);
+ }
+ munmap(buf, l);
+ }
+ } else if (format == FORMAT_XML || format == FORMAT_HTML) {
+ xml_translate(&out, "", params, format);
+ }
+ fclose(s.f);
+ s.f = NULL;
+ s.fd = -1;
+ }
+
+ if (format == FORMAT_XML) {
+ ast_str_append(&out, 0, "</ajax-response>\n");
+ } else if (format == FORMAT_HTML) {
+ ast_str_append(&out, 0, "</table></form></body></html>\r\n");
+ }
+
+ ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
+ http_header = out = NULL;
+
+auth_callback_out:
+ /* Clear resources and unlock manager session */
+ if (method == AST_HTTP_POST && params) {
+ ast_variables_destroy(params);
+ }
+
+ ast_free(http_header);
+ ast_free(out);
+
+ ao2_lock(session);
+ if (session->f) {
+ fclose(session->f);
+ }
+ session->f = NULL;
+ session->fd = -1;
+ ao2_unlock(session);
+
+ if (session->needdestroy) {
+ ast_debug(1, "Need destroy, doing it now!\n");
+ session_destroy(session);
+ }
+ ast_string_field_free_memory(&d);
+ return 0;
+
+out_401:
+ if (!nonce) {
+ nonce = ast_random();
+ }
+
+ ast_http_auth(ser, global_realm, nonce, nonce, stale, NULL);
+ ast_string_field_free_memory(&d);
+ return 0;
}
-static struct ast_str *mxml_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)
+static int manager_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)
{
- return generic_http_callback(FORMAT_XML, &ser->remote_address, uri, method, params, status, title, contentlength);
+ return generic_http_callback(ser, method, FORMAT_HTML, &ser->remote_address, uri, get_params, headers);
}
-static struct ast_str *rawman_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)
+static int mxml_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)
{
- return generic_http_callback(FORMAT_RAW, &ser->remote_address, uri, method, params, status, title, contentlength);
+ return generic_http_callback(ser, method, FORMAT_XML, &ser->remote_address, uri, get_params, headers);
+}
+
+static int rawman_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)
+{
+ return generic_http_callback(ser, method, FORMAT_RAW, &ser->remote_address, uri, get_params, headers);
}
struct ast_http_uri rawmanuri = {
.description = "Raw HTTP Manager Event Interface",
.uri = "rawman",
.callback = rawman_http_callback,
- .supports_get = 1,
.data = NULL,
.key = __FILE__,
};
@@ -3997,7 +4569,6 @@ struct ast_http_uri manageruri = {
.description = "HTML Manager Event Interface",
.uri = "manager",
.callback = manager_http_callback,
- .supports_get = 1,
.data = NULL,
.key = __FILE__,
};
@@ -4006,7 +4577,50 @@ struct ast_http_uri managerxmluri = {
.description = "XML Manager Event Interface",
.uri = "mxml",
.callback = mxml_http_callback,
- .supports_get = 1,
+ .data = NULL,
+ .key = __FILE__,
+};
+
+
+/* Callback with Digest authentication */
+static int auth_manager_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)
+{
+ return auth_http_callback(ser, method, FORMAT_HTML, &ser->remote_address, uri, get_params, headers);
+}
+
+static int auth_mxml_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)
+{
+ return auth_http_callback(ser, method, FORMAT_XML, &ser->remote_address, uri, get_params, headers);
+}
+
+static int auth_rawman_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)
+{
+ return auth_http_callback(ser, method, FORMAT_RAW, &ser->remote_address, uri, get_params, headers);
+}
+
+struct ast_http_uri arawmanuri = {
+ .description = "Raw HTTP Manager Event Interface w/Digest authentication",
+ .uri = "arawman",
+ .has_subtree = 0,
+ .callback = auth_rawman_http_callback,
+ .data = NULL,
+ .key = __FILE__,
+};
+
+struct ast_http_uri amanageruri = {
+ .description = "HTML Manager Event Interface w/Digest authentication",
+ .uri = "amanager",
+ .has_subtree = 0,
+ .callback = auth_manager_http_callback,
+ .data = NULL,
+ .key = __FILE__,
+};
+
+struct ast_http_uri amanagerxmluri = {
+ .description = "XML Manager Event Interface w/Digest authentication",
+ .uri = "amxml",
+ .has_subtree = 0,
+ .callback = auth_mxml_http_callback,
.data = NULL,
.key = __FILE__,
};
@@ -4027,7 +4641,7 @@ struct ast_tls_config ami_tls_cfg;
static struct ast_tcptls_session_args ami_desc = {
.accept_fd = -1,
.master = AST_PTHREADT_NULL,
- .tls_cfg = NULL,
+ .tls_cfg = NULL,
.poll_timeout = 5000, /* wake up every 5 seconds */
.periodic_fn = purge_old_stuff,
.name = "AMI server",
@@ -4038,7 +4652,7 @@ static struct ast_tcptls_session_args ami_desc = {
static struct ast_tcptls_session_args amis_desc = {
.accept_fd = -1,
.master = AST_PTHREADT_NULL,
- .tls_cfg = &ami_tls_cfg,
+ .tls_cfg = &ami_tls_cfg,
.poll_timeout = -1, /* the other does the periodic cleanup */
.name = "AMI TLS server",
.accept_fn = ast_tcptls_server_root, /* thread doing the accept() */
@@ -4057,6 +4671,8 @@ static int __init_manager(int reload)
struct ast_manager_user *user = NULL;
struct ast_variable *var;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ char a1[256];
+ char a1_hash[256];
manager_enabled = 0;
@@ -4101,8 +4717,9 @@ static int __init_manager(int reload)
/* Append placeholder event so master_eventq never runs dry */
append_event("Event: Placeholder\r\n\r\n", 0);
}
- if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
return 0;
+ }
displayconnects = 1;
if (!cfg) {
@@ -4111,26 +4728,29 @@ static int __init_manager(int reload)
}
/* default values */
+ ast_copy_string(global_realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(global_realm));
memset(&ami_desc.local_address, 0, sizeof(struct sockaddr_in));
memset(&amis_desc.local_address, 0, sizeof(amis_desc.local_address));
amis_desc.local_address.sin_port = htons(5039);
ami_desc.local_address.sin_port = htons(DEFAULT_MANAGER_PORT);
ami_tls_cfg.enabled = 0;
- if (ami_tls_cfg.certfile)
+ if (ami_tls_cfg.certfile) {
ast_free(ami_tls_cfg.certfile);
+ }
ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
- if (ami_tls_cfg.cipher)
+ if (ami_tls_cfg.cipher) {
ast_free(ami_tls_cfg.cipher);
+ }
ami_tls_cfg.cipher = ast_strdup("");
for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
val = var->value;
- if (!strcasecmp(var->name, "sslenable"))
+ if (!strcasecmp(var->name, "sslenable")) {
ami_tls_cfg.enabled = ast_true(val);
- else if (!strcasecmp(var->name, "sslbindport"))
+ } else if (!strcasecmp(var->name, "sslbindport")) {
amis_desc.local_address.sin_port = htons(atoi(val));
- else if (!strcasecmp(var->name, "sslbindaddr")) {
+ } else if (!strcasecmp(var->name, "sslbindaddr")) {
if ((hp = ast_gethostbyname(val, &ahp))) {
memcpy(&amis_desc.local_address.sin_addr, hp->h_addr, sizeof(amis_desc.local_address.sin_addr));
have_sslbindaddr = 1;
@@ -4156,7 +4776,7 @@ static int __init_manager(int reload)
ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
memset(&ami_desc.local_address.sin_addr, 0, sizeof(ami_desc.local_address.sin_addr));
}
- } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
+ } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
allowmultiplelogin = ast_true(val);
} else if (!strcasecmp(var->name, "displayconnects")) {
displayconnects = ast_true(val);
@@ -4169,17 +4789,19 @@ static int __init_manager(int reload)
} else {
ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
var->name, val);
- }
+ }
}
- if (manager_enabled)
+ if (manager_enabled) {
ami_desc.local_address.sin_family = AF_INET;
- if (!have_sslbindaddr)
+ }
+ if (!have_sslbindaddr) {
amis_desc.local_address.sin_addr = ami_desc.local_address.sin_addr;
- if (ami_tls_cfg.enabled)
+ }
+ if (ami_tls_cfg.enabled) {
amis_desc.local_address.sin_family = AF_INET;
+ }
-
AST_RWLIST_WRLOCK(&users);
/* First, get users from users.conf */
@@ -4189,9 +4811,10 @@ static int __init_manager(int reload)
int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
while ((cat = ast_category_browse(ucfg, cat))) {
- if (!strcasecmp(cat, "general"))
+ if (!strcasecmp(cat, "general")) {
continue;
-
+ }
+
hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
@@ -4199,13 +4822,14 @@ static int __init_manager(int reload)
const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
-
+
/* Look for an existing entry,
* if none found - create one and add it to the list
*/
if (!(user = get_manager_by_name_locked(cat))) {
- if (!(user = ast_calloc(1, sizeof(*user))))
+ if (!(user = ast_calloc(1, sizeof(*user)))) {
break;
+ }
/* Copy name over */
ast_copy_string(user->username, cat, sizeof(user->username));
@@ -4220,36 +4844,45 @@ static int __init_manager(int reload)
user->writetimeout = 100;
}
- if (!user_secret)
+ if (!user_secret) {
user_secret = ast_variable_retrieve(ucfg, "general", "secret");
- if (!user_read)
+ }
+ if (!user_read) {
user_read = ast_variable_retrieve(ucfg, "general", "read");
- if (!user_write)
+ }
+ if (!user_write) {
user_write = ast_variable_retrieve(ucfg, "general", "write");
- if (!user_displayconnects)
+ }
+ if (!user_displayconnects) {
user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
- if (!user_writetimeout)
+ }
+ if (!user_writetimeout) {
user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
+ }
if (!ast_strlen_zero(user_secret)) {
- if (user->secret)
+ if (user->secret) {
ast_free(user->secret);
+ }
user->secret = ast_strdup(user_secret);
}
- if (user_read)
+ if (user_read) {
user->readperm = get_perm(user_read);
- if (user_write)
+ }
+ if (user_write) {
user->writeperm = get_perm(user_write);
- if (user_displayconnects)
+ }
+ if (user_displayconnects) {
user->displayconnects = ast_true(user_displayconnects);
-
+ }
if (user_writetimeout) {
int value = atoi(user_writetimeout);
- if (value < 100)
+ if (value < 100) {
ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno);
- else
+ } else {
user->writetimeout = value;
+ }
}
}
}
@@ -4261,13 +4894,15 @@ static int __init_manager(int reload)
while ((cat = ast_category_browse(cfg, cat))) {
struct ast_ha *oldha;
- if (!strcasecmp(cat, "general"))
+ if (!strcasecmp(cat, "general")) {
continue;
+ }
/* Look for an existing entry, if none found - create one and add it to the list */
if (!(user = get_manager_by_name_locked(cat))) {
- if (!(user = ast_calloc(1, sizeof(*user))))
+ if (!(user = ast_calloc(1, sizeof(*user)))) {
break;
+ }
/* Copy name over */
ast_copy_string(user->username, cat, sizeof(user->username));
@@ -4290,8 +4925,9 @@ static int __init_manager(int reload)
var = ast_variable_browse(cfg, cat);
for (; var; var = var->next) {
if (!strcasecmp(var->name, "secret")) {
- if (user->secret)
+ if (user->secret) {
ast_free(user->secret);
+ }
user->secret = ast_strdup(var->value);
} else if (!strcasecmp(var->name, "deny") ||
!strcasecmp(var->name, "permit")) {
@@ -4304,12 +4940,14 @@ static int __init_manager(int reload)
user->displayconnects = ast_true(var->value);
} else if (!strcasecmp(var->name, "writetimeout")) {
int value = atoi(var->value);
- if (value < 100)
+ if (value < 100) {
ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
- else
+ } else {
user->writetimeout = value;
- } else
+ }
+ } else {
ast_debug(1, "%s is an unknown option.\n", var->name);
+ }
}
ast_free_ha(oldha);
}
@@ -4319,13 +4957,25 @@ static int __init_manager(int reload)
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
if (user->keep) { /* valid record. clear flag for the next round */
user->keep = 0;
+
+ /* Calculate A1 for Digest auth */
+ snprintf(a1, sizeof(a1), "%s:%s:%s", user->username, global_realm, user->secret);
+ ast_md5_hash(a1_hash,a1);
+ if (user->a1_hash) {
+ ast_free(user->a1_hash);
+ }
+ user->a1_hash = ast_strdup(a1_hash);
continue;
}
/* We do not need to keep this user so take them out of the list */
AST_RWLIST_REMOVE_CURRENT(list);
/* Free their memory now */
- if (user->secret)
+ if (user->a1_hash) {
+ ast_free(user->a1_hash);
+ }
+ if (user->secret) {
ast_free(user->secret);
+ }
ast_free_ha(user->ha);
ast_free(user);
}
@@ -4333,11 +4983,21 @@ static int __init_manager(int reload)
AST_RWLIST_UNLOCK(&users);
+ if (!reload) {
+ /* If you have a NULL hash fn, you only need a single bucket */
+ sessions = ao2_container_alloc(1, NULL, mansession_cmp_fn);
+ }
+
if (webmanager_enabled && manager_enabled) {
if (!webregged) {
+
ast_http_uri_link(&rawmanuri);
ast_http_uri_link(&manageruri);
ast_http_uri_link(&managerxmluri);
+
+ ast_http_uri_link(&arawmanuri);
+ ast_http_uri_link(&amanageruri);
+ ast_http_uri_link(&amanagerxmluri);
webregged = 1;
}
} else {
@@ -4345,18 +5005,24 @@ static int __init_manager(int reload)
ast_http_uri_unlink(&rawmanuri);
ast_http_uri_unlink(&manageruri);
ast_http_uri_unlink(&managerxmluri);
+
+ ast_http_uri_unlink(&arawmanuri);
+ ast_http_uri_unlink(&amanageruri);
+ ast_http_uri_unlink(&amanagerxmluri);
webregged = 0;
}
}
- if (newhttptimeout > 0)
+ if (newhttptimeout > 0) {
httptimeout = newhttptimeout;
+ }
manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");
ast_tcptls_server_start(&ami_desc);
- if (ast_ssl_setup(amis_desc.tls_cfg))
+ if (ast_ssl_setup(amis_desc.tls_cfg)) {
ast_tcptls_server_start(&amis_desc);
+ }
return 0;
}
@@ -4385,7 +5051,7 @@ int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastor
struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
{
struct ast_datastore *datastore = NULL;
-
+
if (info == NULL)
return NULL;
diff --git a/main/utils.c b/main/utils.c
index 60da56250..57ea0566d 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -1839,6 +1839,126 @@ int ast_utils_init(void)
return 0;
}
+
+/*!
+ *\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) {
+ int i;
+ char *c, key[512], val[512], tmp[512];
+ struct ast_str *str = ast_str_create(16);
+
+ if (ast_strlen_zero(digest) || !d || !str) {
+ ast_free(str);
+ return -1;
+ }
+
+ ast_str_set(&str, 0, "%s", digest);
+
+ c = ast_skip_blanks(ast_str_buffer(str));
+
+ if (strncasecmp(tmp, "Digest ", strlen("Digest "))) {
+ ast_log(LOG_WARNING, "Missing Digest.\n");
+ ast_free(str);
+ return -1;
+ }
+ c += strlen("Digest ");
+
+ /* lookup for keys/value pair */
+ while (*c && *(c = ast_skip_blanks(c))) {
+ /* find key */
+ i = 0;
+ while (*c && *c != '=' && *c != ',' && !isspace(*c)) {
+ key[i++] = *c++;
+ }
+ key[i] = '\0';
+ c = ast_skip_blanks(c);
+ if (*c == '=') {
+ c = ast_skip_blanks(++c);
+ i = 0;
+ if (*c == '\"') {
+ /* in quotes. Skip first and look for last */
+ c++;
+ while (*c && *c != '\"') {
+ if (*c == '\\' && c[1] != '\0') { /* unescape chars */
+ c++;
+ }
+ val[i++] = *c++;
+ }
+ } else {
+ /* token */
+ while (*c && *c != ',' && !isspace(*c)) {
+ val[i++] = *c++;
+ }
+ }
+ val[i] = '\0';
+ }
+
+ while (*c && *c != ',') {
+ c++;
+ }
+ if (*c) {
+ c++;
+ }
+
+ if (!strcasecmp(key, "username")) {
+ ast_string_field_set(d, username, val);
+ } else if (!strcasecmp(key, "realm")) {
+ ast_string_field_set(d, realm, val);
+ } else if (!strcasecmp(key, "nonce")) {
+ ast_string_field_set(d, nonce, val);
+ } else if (!strcasecmp(key, "uri")) {
+ ast_string_field_set(d, uri, val);
+ } else if (!strcasecmp(key, "domain")) {
+ ast_string_field_set(d, domain, val);
+ } else if (!strcasecmp(key, "response")) {
+ ast_string_field_set(d, response, val);
+ } else if (!strcasecmp(key, "algorithm")) {
+ if (strcasecmp(val, "MD5")) {
+ ast_log(LOG_WARNING, "Digest algorithm: \"%s\" not supported.\n", val);
+ return -1;
+ }
+ } else if (!strcasecmp(key, "cnonce")) {
+ ast_string_field_set(d, cnonce, val);
+ } else if (!strcasecmp(key, "opaque")) {
+ ast_string_field_set(d, opaque, val);
+ } else if (!strcasecmp(key, "qop") && !strcasecmp(val, "auth")) {
+ d->qop = 1;
+ } else if (!strcasecmp(key, "nc")) {
+ unsigned long u;
+ if (sscanf(val, "%lx", &u) != 1) {
+ ast_log(LOG_WARNING, "Incorrect Digest nc value: \"%s\".\n", val);
+ return -1;
+ }
+ ast_string_field_set(d, nc, val);
+ }
+ }
+ ast_free(str);
+
+ /* Digest checkout */
+ if (ast_strlen_zero(d->realm) || ast_strlen_zero(d->nonce)) {
+ /* "realm" and "nonce" MUST be always exist */
+ return -1;
+ }
+
+ if (!request) {
+ /* Additional check for Digest response */
+ if (ast_strlen_zero(d->username) || ast_strlen_zero(d->uri) || ast_strlen_zero(d->response)) {
+ return -1;
+ }
+
+ if (pedantic && d->qop && (ast_strlen_zero(d->cnonce) || ast_strlen_zero(d->nc))) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
#ifndef __AST_DEBUG_MALLOC
int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...)
{