aboutsummaryrefslogtreecommitdiffstats
path: root/main/http.c
diff options
context:
space:
mode:
authorrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2007-04-06 20:58:43 +0000
committerrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2007-04-06 20:58:43 +0000
commit52502ee152be35747e2c65ea19765fd3ef618d91 (patch)
tree59a9f7f65b11753182e16b0e635788e2caf96da5 /main/http.c
parentbef2aae366c4db003ad5758a02378bbf1f176182 (diff)
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/branches/1.4@60603 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main/http.c')
-rw-r--r--main/http.c321
1 files changed, 318 insertions, 3 deletions
diff --git a/main/http.c b/main/http.c
index bd140279f..014940b5a 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"
@@ -54,6 +57,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/options.h"
#include "asterisk/config.h"
#include "asterisk/version.h"
+#include "asterisk/manager.h"
#define MAX_PREFIX 80
#define DEFAULT_PREFIX "/asterisk"
@@ -68,6 +72,14 @@ struct ast_http_server_instance {
AST_RWLOCK_DEFINE_STATIC(uris_lock);
static struct ast_http_uri *uris;
+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);
+
static int httpfd = -1;
static pthread_t master = AST_PTHREADT_NULL;
static char prefix[MAX_PREFIX];
@@ -287,6 +299,225 @@ void ast_http_uri_unlink(struct ast_http_uri *urih)
ast_rwlock_unlock(&uris_lock);
}
+/*! \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 char *handle_post(struct ast_http_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_strdup("");
+}
+
static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies)
{
char *c;
@@ -376,7 +607,7 @@ static void *ast_httpd_helper_thread(void *data)
char cookie[4096];
char timebuf[256];
struct ast_http_server_instance *ser = data;
- struct ast_variable *var, *prev=NULL, *vars=NULL;
+ struct ast_variable *var, *prev=NULL, *vars=NULL, *headers = NULL;
char *uri, *c, *title=NULL;
char *vname, *vval;
int status = 200, contentlength = 0;
@@ -459,12 +690,29 @@ static void *ast_httpd_helper_thread(void *data)
}
}
}
+ } else {
+ 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;
}
}
if (*uri) {
if (!strcasecmp(buf, "get"))
c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars);
+ else if (!strcasecmp(buf, "post"))
+ c = handle_post(ser, uri, &status, &title, &contentlength, headers, vars);
else
c = ast_http_error(501, "Not Implemented", NULL, "Attempt to use unimplemented / unsupported method");\
} else
@@ -624,6 +872,47 @@ static void http_server_start(struct sockaddr_in *sin)
}
}
+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;
@@ -637,7 +926,11 @@ static int __ast_http_load(int reload)
memset(&sin, 0, sizeof(sin));
sin.sin_port = htons(8088);
+
strcpy(newprefix, DEFAULT_PREFIX);
+
+ destroy_post_mappings();
+
cfg = ast_config_load("http.conf");
if (cfg) {
v = ast_variable_browse(cfg, "general");
@@ -665,6 +958,10 @@ static int __ast_http_load(int reload)
}
v = v->next;
}
+
+ for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next)
+ add_post_mapping(v->name, v->value);
+
ast_config_destroy(cfg);
}
if (enabled)
@@ -674,15 +971,21 @@ static int __ast_http_load(int reload)
prefix_len = strlen(prefix);
}
enablestatic = newenablestatic;
+
http_server_start(&sin);
+
+
return 0;
}
static int handle_show_http(int fd, int argc, char *argv[])
{
struct ast_http_uri *urih;
+ struct ast_http_post_mapping *post_map;
+
if (argc != 3)
return RESULT_SHOWUSAGE;
+
ast_cli(fd, "HTTP Server Status:\n");
ast_cli(fd, "Prefix: %s\n", prefix);
if (oldsin.sin_family)
@@ -701,6 +1004,14 @@ static int handle_show_http(int fd, int argc, char *argv[])
if (!uris)
ast_cli(fd, "None.\n");
ast_rwlock_unlock(&uris_lock);
+
+ 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;
}
@@ -721,8 +1032,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);
}