aboutsummaryrefslogtreecommitdiffstats
path: root/main/http.c
diff options
context:
space:
mode:
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);
}