aboutsummaryrefslogtreecommitdiffstats
path: root/main/http.c
diff options
context:
space:
mode:
authorrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2007-04-06 21:16:38 +0000
committerrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2007-04-06 21:16:38 +0000
commitfe453b5ef2007fde913e3ebd31e401f0cdb658eb (patch)
treed1a8725b9d1a7d8508205ad650f4a6ed1de11339 /main/http.c
parentfb1d21c4f24ce15bb1a102b6a650fea83e77ecd5 (diff)
Merged revisions 60603 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r60603 | russell | 2007-04-06 15:58:43 -0500 (Fri, 06 Apr 2007) | 13 lines To be able to achieve the things that we would like to achieve with the Asterisk GUI project, we need a fully functional HTTP interface with access to the Asterisk manager interface. One of the things that was intended to be a part of this system, but was never actually implemented, was the ability for the GUI to be able to upload files to Asterisk. So, this commit adds this in the most minimally invasive way that we could come up with. A lot of work on minimime was done by Steve Murphy. He fixed a lot of bugs in the parser, and updated it to be thread-safe. The ability to check permissions of active manager sessions was added by Dwayne Hubbard. Then, hacking this all together and do doing the modifications necessary to the HTTP interface was done by me. ........ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@60604 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main/http.c')
-rw-r--r--main/http.c318
1 files changed, 314 insertions, 4 deletions
diff --git a/main/http.c b/main/http.c
index 4fbe9bded..0dee18faa 100644
--- a/main/http.c
+++ b/main/http.c
@@ -21,8 +21,9 @@
* \brief http server for AMI access
*
* \author Mark Spencer <markster@digium.com>
- * This program implements a tiny http server supporting the "get" method
- * only and was inspired by micro-httpd by Jef Poskanzer
+ *
+ * This program implements a tiny http server
+ * and was inspired by micro-httpd by Jef Poskanzer
*
* \ref AstHTTP - AMI over the http protocol
*/
@@ -47,6 +48,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <fcntl.h>
#include <pthread.h>
+#include "minimime/mm.h"
+
#include "asterisk/cli.h"
#include "asterisk/http.h"
#include "asterisk/utils.h"
@@ -55,6 +58,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/config.h"
#include "asterisk/stringfields.h"
#include "asterisk/version.h"
+#include "asterisk/manager.h"
#define MAX_PREFIX 80
#define DEFAULT_PREFIX "/asterisk"
@@ -93,6 +97,14 @@ static struct server_args https_desc = {
static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri); /*!< list of supported handlers */
+struct ast_http_post_mapping {
+ AST_RWLIST_ENTRY(ast_http_post_mapping) entry;
+ char *from;
+ char *to;
+};
+
+static AST_RWLIST_HEAD_STATIC(post_mappings, ast_http_post_mapping);
+
/* all valid URIs must be prepended by the string in prefix. */
static char prefix[MAX_PREFIX];
static int enablestatic;
@@ -329,6 +341,225 @@ void ast_http_uri_unlink(struct ast_http_uri *urih)
AST_RWLIST_UNLOCK(&uris);
}
+/*! \note This assumes that the post_mappings list is locked */
+static struct ast_http_post_mapping *find_post_mapping(const char *uri)
+{
+ struct ast_http_post_mapping *post_map;
+
+ if (!ast_strlen_zero(prefix) && strncmp(prefix, uri, strlen(prefix))) {
+ ast_log(LOG_DEBUG, "URI %s does not have prefix %s\n", uri, prefix);
+ return NULL;
+ }
+
+ uri += strlen(prefix);
+ if (*uri == '/')
+ uri++;
+
+ AST_RWLIST_TRAVERSE(&post_mappings, post_map, entry) {
+ if (!strcmp(uri, post_map->from))
+ return post_map;
+ }
+
+ return NULL;
+}
+
+static int get_filename(struct mm_mimepart *part, char *fn, size_t fn_len)
+{
+ const char *filename;
+
+ filename = mm_content_getdispositionparambyname(part->type, "filename");
+
+ if (ast_strlen_zero(filename))
+ return -1;
+
+ ast_copy_string(fn, filename, fn_len);
+
+ return 0;
+}
+
+static void post_raw(struct mm_mimepart *part, const char *post_dir, const char *fn)
+{
+ char filename[PATH_MAX];
+ FILE *f;
+ const char *body;
+ size_t body_len;
+
+ snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Posting raw data to %s\n", filename);
+
+ if (!(f = fopen(filename, "w"))) {
+ ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);
+ return;
+ }
+
+ if (!(body = mm_mimepart_getbody(part, 0))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Couldn't get the mimepart body\n");
+ fclose(f);
+ return;
+ }
+ body_len = mm_mimepart_getlength(part);
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Body length is %ld\n", body_len);
+
+ fwrite(body, 1, body_len, f);
+
+ fclose(f);
+}
+
+static struct ast_str *handle_post(struct server_instance *ser, char *uri,
+ int *status, char **title, int *contentlength, struct ast_variable *headers,
+ struct ast_variable *cookies)
+{
+ char buf;
+ FILE *f;
+ size_t res;
+ struct ast_variable *var;
+ int content_len = 0;
+ MM_CTX *ctx;
+ int mm_res, i;
+ struct ast_http_post_mapping *post_map;
+ const char *post_dir;
+ unsigned long ident = 0;
+
+ for (var = cookies; var; var = var->next) {
+ if (strcasecmp(var->name, "mansession_id"))
+ continue;
+
+ if (sscanf(var->value, "%lx", &ident) != 1) {
+ *status = 400;
+ *title = ast_strdup("Bad Request");
+ return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+ }
+
+ if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
+ *status = 401;
+ *title = ast_strdup("Unauthorized");
+ return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
+ }
+
+ break;
+ }
+ if (!var) {
+ *status = 401;
+ *title = ast_strdup("Unauthorized");
+ return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
+ }
+
+ if (!(f = tmpfile()))
+ return NULL;
+
+ for (var = headers; var; var = var->next) {
+ if (!strcasecmp(var->name, "Content-Length")) {
+ if ((sscanf(var->value, "%u", &content_len)) != 1) {
+ ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
+ fclose(f);
+ return NULL;
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Got a Content-Length of %d\n", content_len);
+ } else if (!strcasecmp(var->name, "Content-Type"))
+ fprintf(f, "Content-Type: %s\r\n\r\n", var->value);
+ }
+
+ while ((res = fread(&buf, 1, 1, ser->f))) {
+ fwrite(&buf, 1, 1, f);
+ content_len--;
+ if (!content_len)
+ break;
+ }
+
+ if (fseek(f, SEEK_SET, 0)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Failed to seek temp file back to beginning.\n");
+ fclose(f);
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&post_mappings);
+ if (!(post_map = find_post_mapping(uri))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "%s is not a valid URI for POST\n", uri);
+ AST_RWLIST_UNLOCK(&post_mappings);
+ fclose(f);
+ *status = 404;
+ *title = ast_strdup("Not Found");
+ return ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
+ }
+ post_dir = ast_strdupa(post_map->to);
+ post_map = NULL;
+ AST_RWLIST_UNLOCK(&post_mappings);
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Going to post files to dir %s\n", post_dir);
+
+ if (!(ctx = mm_context_new())) {
+ fclose(f);
+ return NULL;
+ }
+
+ mm_res = mm_parse_fileptr(ctx, f, MM_PARSE_LOOSE, 0);
+ fclose(f);
+ if (mm_res == -1) {
+ ast_log(LOG_ERROR, "Error parsing MIME data\n");
+ mm_context_free(ctx);
+ *status = 400;
+ *title = ast_strdup("Bad Request");
+ return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+ }
+
+ mm_res = mm_context_countparts(ctx);
+ if (!mm_res) {
+ ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
+ mm_context_free(ctx);
+ *status = 400;
+ *title = ast_strdup("Bad Request");
+ return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+ }
+
+ if (option_debug) {
+ if (mm_context_iscomposite(ctx))
+ ast_log(LOG_DEBUG, "Found %d MIME parts\n", mm_res - 1);
+ else
+ ast_log(LOG_DEBUG, "We have a flat (not multi-part) message\n");
+ }
+
+ for (i = 1; i < mm_res; i++) {
+ struct mm_mimepart *part;
+ char fn[PATH_MAX];
+
+ if (!(part = mm_context_getpart(ctx, i))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Failed to get mime part num %d\n", i);
+ continue;
+ }
+
+ if (get_filename(part, fn, sizeof(fn))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Failed to retrieve a filename for part num %d\n", i);
+ continue;
+ }
+
+ if (!part->type) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "This part has no content struct?\n");
+ continue;
+ }
+
+ /* XXX This assumes the MIME part body is not encoded! */
+ post_raw(part, post_dir, fn);
+ }
+
+ mm_context_free(ctx);
+
+ *status = 200;
+ *title = ast_strdup("OK");
+ return ast_http_error(200, "OK", NULL, "File successfully uploaded.");
+}
+
static struct ast_str *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies)
{
char *c;
@@ -520,7 +751,7 @@ static void *httpd_helper_thread(void *data)
char buf[4096];
char cookie[4096];
struct server_instance *ser = data;
- struct ast_variable *var, *prev=NULL, *vars=NULL;
+ struct ast_variable *var, *prev=NULL, *vars=NULL, *headers = NULL;
char *uri, *title=NULL;
int status = 200, contentlength = 0;
struct ast_str *out = NULL;
@@ -549,8 +780,23 @@ static void *httpd_helper_thread(void *data)
ast_trim_blanks(cookie);
if (ast_strlen_zero(cookie))
break;
- if (strncasecmp(cookie, "Cookie: ", 8))
+ if (strncasecmp(cookie, "Cookie: ", 8)) {
+ char *name, *value;
+
+ value = ast_strdupa(cookie);
+ name = strsep(&value, ":");
+ if (!value)
+ continue;
+ value = ast_skip_blanks(value);
+ if (ast_strlen_zero(value))
+ continue;
+ var = ast_variable_new(name, value);
+ if (!var)
+ continue;
+ var->next = headers;
+ headers = var;
continue;
+ }
/* TODO - The cookie parsing code below seems to work
in IE6 and FireFox 1.5. However, it is not entirely
@@ -596,6 +842,8 @@ static void *httpd_helper_thread(void *data)
if (!*uri)
out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
+ else if (!strcasecmp(buf, "post"))
+ out = handle_post(ser, uri, &status, &title, &contentlength, headers, vars);
else if (strcasecmp(buf, "get"))
out = ast_http_error(501, "Not Implemented", NULL,
"Attempt to use unimplemented / unsupported method");
@@ -851,6 +1099,47 @@ static void add_redirect(const char *value)
AST_RWLIST_UNLOCK(&uri_redirects);
}
+static void destroy_post_mapping(struct ast_http_post_mapping *post_map)
+{
+ if (post_map->from)
+ free(post_map->from);
+ if (post_map->to)
+ free(post_map->to);
+ free(post_map);
+}
+
+static void destroy_post_mappings(void)
+{
+ struct ast_http_post_mapping *post_map;
+
+ AST_RWLIST_WRLOCK(&post_mappings);
+ while ((post_map = AST_RWLIST_REMOVE_HEAD(&post_mappings, entry)))
+ destroy_post_mapping(post_map);
+ AST_RWLIST_UNLOCK(&post_mappings);
+}
+
+static void add_post_mapping(const char *from, const char *to)
+{
+ struct ast_http_post_mapping *post_map;
+
+ if (!(post_map = ast_calloc(1, sizeof(*post_map))))
+ return;
+
+ if (!(post_map->from = ast_strdup(from))) {
+ destroy_post_mapping(post_map);
+ return;
+ }
+
+ if (!(post_map->to = ast_strdup(to))) {
+ destroy_post_mapping(post_map);
+ return;
+ }
+
+ AST_RWLIST_WRLOCK(&post_mappings);
+ AST_RWLIST_INSERT_TAIL(&post_mappings, post_map, entry);
+ AST_RWLIST_UNLOCK(&post_mappings);
+}
+
static int __ast_http_load(int reload)
{
struct ast_config *cfg;
@@ -869,6 +1158,7 @@ static int __ast_http_load(int reload)
memset(&https_desc.sin, 0, sizeof(https_desc.sin));
https_desc.sin.sin_port = htons(8089);
+
strcpy(newprefix, DEFAULT_PREFIX);
http_tls_cfg.enabled = 0;
@@ -884,6 +1174,8 @@ static int __ast_http_load(int reload)
free(redirect);
AST_RWLIST_UNLOCK(&uri_redirects);
+ destroy_post_mappings();
+
cfg = ast_config_load("http.conf");
if (cfg) {
v = ast_variable_browse(cfg, "general");
@@ -931,6 +1223,10 @@ static int __ast_http_load(int reload)
ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
}
}
+
+ for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next)
+ add_post_mapping(v->name, v->value);
+
ast_config_destroy(cfg);
}
if (!have_sslbindaddr)
@@ -943,6 +1239,7 @@ static int __ast_http_load(int reload)
server_start(&http_desc);
if (ssl_setup(https_desc.tls_cfg))
server_start(&https_desc);
+
return 0;
}
@@ -950,6 +1247,7 @@ static int handle_show_http(int fd, int argc, char *argv[])
{
struct ast_http_uri *urih;
struct http_uri_redirect *redirect;
+ struct ast_http_post_mapping *post_map;
if (argc != 3)
return RESULT_SHOWUSAGE;
@@ -986,6 +1284,14 @@ static int handle_show_http(int fd, int argc, char *argv[])
ast_cli(fd, " None.\n");
AST_RWLIST_UNLOCK(&uri_redirects);
+
+ ast_cli(fd, "\nPOST mappings:\n");
+ AST_RWLIST_RDLOCK(&post_mappings);
+ AST_LIST_TRAVERSE(&post_mappings, post_map, entry)
+ ast_cli(fd, "%s/%s => %s\n", prefix, post_map->from, post_map->to);
+ ast_cli(fd, "%s\n", AST_LIST_EMPTY(&post_mappings) ? "None.\n" : "");
+ AST_RWLIST_UNLOCK(&post_mappings);
+
return RESULT_SUCCESS;
}
@@ -1006,8 +1312,12 @@ static struct ast_cli_entry cli_http[] = {
int ast_http_init(void)
{
+ mm_library_init();
+ mm_codec_registerdefaultcodecs();
+
ast_http_uri_link(&statusuri);
ast_http_uri_link(&staticuri);
ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
+
return __ast_http_load(0);
}