Re-add HTTP post support by moving to res_http_post.c
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@112426 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
parent
b95d24ea47
commit
1eb31edde2
|
@ -1,10 +1,6 @@
|
|||
<category name="MENUSELECT_CFLAGS" displayname="Compiler Flags" positive_output="yes" remove_on_change=".lastclean">
|
||||
<member name="DONT_OPTIMIZE" displayname="Disable Optimizations by the Compiler">
|
||||
</member>
|
||||
<member name="ENABLE_UPLOADS" displayname="Enable HTTP uploads">
|
||||
<defaultenabled>yes</defaultenabled>
|
||||
<depend>gmime</depend>
|
||||
</member>
|
||||
<member name="DEBUG_THREADS" displayname="Enable Thread Debugging">
|
||||
</member>
|
||||
<member name="STATIC_BUILD" displayname="Build static binaries">
|
||||
|
|
|
@ -70,9 +70,9 @@ enum ast_http_method {
|
|||
AST_HTTP_GET = 0,
|
||||
AST_HTTP_POST,
|
||||
};
|
||||
struct ast_http_uri;
|
||||
|
||||
typedef struct ast_str *(*ast_http_callback)(struct ast_tcptls_session_instance *ser, const char *uri, enum ast_http_method method,
|
||||
struct ast_variable *params, int *status, char **title, int *contentlength);
|
||||
typedef struct ast_str *(*ast_http_callback)(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength);
|
||||
|
||||
/*! \brief Definition of a URI handler */
|
||||
struct ast_http_uri {
|
||||
|
@ -87,6 +87,10 @@ struct ast_http_uri {
|
|||
unsigned int supports_get:1;
|
||||
/*! This handler accepts POST requests */
|
||||
unsigned int supports_post:1;
|
||||
/*! Data to bind to the uri if needed */
|
||||
void *data;
|
||||
/*! Key to be used for unlinking if multipile URIs registerd */
|
||||
const char *key;
|
||||
};
|
||||
|
||||
/*! \brief Register a URI handler */
|
||||
|
@ -95,6 +99,9 @@ int ast_http_uri_link(struct ast_http_uri *urihandler);
|
|||
/*! \brief Unregister a URI handler */
|
||||
void ast_http_uri_unlink(struct ast_http_uri *urihandler);
|
||||
|
||||
/*! \brief Unregister all handlers with matching key */
|
||||
void ast_http_uri_unlink_all_with_key(const char *key);
|
||||
|
||||
/*! \brief Return an ast_str malloc()'d string containing an HTTP error message */
|
||||
struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text);
|
||||
|
||||
|
|
349
main/http.c
349
main/http.c
|
@ -38,10 +38,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include <sys/signal.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef ENABLE_UPLOADS
|
||||
#include <gmime/gmime.h>
|
||||
#endif /* ENABLE_UPLOADS */
|
||||
|
||||
#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
|
||||
#include "asterisk/network.h"
|
||||
#include "asterisk/cli.h"
|
||||
|
@ -91,21 +87,6 @@ static struct server_args https_desc = {
|
|||
|
||||
static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri); /*!< list of supported handlers */
|
||||
|
||||
#ifdef ENABLE_UPLOADS
|
||||
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);
|
||||
|
||||
struct mime_cbinfo {
|
||||
int count;
|
||||
const char *post_dir;
|
||||
};
|
||||
#endif /* ENABLE_UPLOADS */
|
||||
|
||||
/* all valid URIs must be prepended by the string in prefix. */
|
||||
static char prefix[MAX_PREFIX];
|
||||
static int enablestatic;
|
||||
|
@ -150,8 +131,7 @@ static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
|
|||
return wkspace;
|
||||
}
|
||||
|
||||
static struct ast_str *static_callback(struct ast_tcptls_session_instance *ser, const char *uri, enum ast_http_method method,
|
||||
struct ast_variable *vars, int *status, char **title, int *contentlength)
|
||||
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)
|
||||
{
|
||||
char *path;
|
||||
char *ftype;
|
||||
|
@ -234,8 +214,7 @@ out403:
|
|||
}
|
||||
|
||||
|
||||
static struct ast_str *httpstatus_callback(struct ast_tcptls_session_instance *ser, const char *uri, enum ast_http_method method,
|
||||
struct ast_variable *vars, int *status, char **title, int *contentlength)
|
||||
static struct ast_str *httpstatus_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
|
||||
{
|
||||
struct ast_str *out = ast_str_create(512);
|
||||
struct ast_variable *v;
|
||||
|
@ -286,6 +265,8 @@ static struct ast_http_uri statusuri = {
|
|||
.description = "Asterisk HTTP General Status",
|
||||
.uri = "httpstatus",
|
||||
.supports_get = 1,
|
||||
.data = NULL,
|
||||
.key = __FILE__,
|
||||
};
|
||||
|
||||
static struct ast_http_uri staticuri = {
|
||||
|
@ -295,6 +276,8 @@ static struct ast_http_uri staticuri = {
|
|||
.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)
|
||||
|
@ -375,242 +358,20 @@ void ast_http_uri_unlink(struct ast_http_uri *urih)
|
|||
AST_RWLIST_UNLOCK(&uris);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_UPLOADS
|
||||
/*! \note This assumes that the post_mappings list is locked */
|
||||
static struct ast_http_post_mapping *find_post_mapping(const char *uri)
|
||||
void ast_http_uri_unlink_all_with_key(const char *key)
|
||||
{
|
||||
struct ast_http_post_mapping *post_map;
|
||||
|
||||
if (!ast_strlen_zero(prefix) && strncmp(prefix, uri, strlen(prefix))) {
|
||||
ast_debug(1, "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;
|
||||
struct ast_http_uri *urih;
|
||||
AST_RWLIST_WRLOCK(&uris);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&uris, urih, entry) {
|
||||
if (!strcmp(urih->key, key)) {
|
||||
AST_RWLIST_REMOVE_CURRENT(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
AST_RWLIST_TRAVERSE_SAFE_END
|
||||
}
|
||||
|
||||
static void post_raw(GMimePart *part, const char *post_dir, const char *fn)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
GMimeDataWrapper *content;
|
||||
GMimeStream *stream;
|
||||
int fd;
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);
|
||||
|
||||
ast_debug(1, "Posting raw data to %s\n", filename);
|
||||
|
||||
if ((fd = open(filename, O_CREAT | O_WRONLY, 0666)) == -1) {
|
||||
ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
stream = g_mime_stream_fs_new(fd);
|
||||
|
||||
content = g_mime_part_get_content_object(part);
|
||||
g_mime_data_wrapper_write_to_stream(content, stream);
|
||||
g_mime_stream_flush(stream);
|
||||
|
||||
g_object_unref(content);
|
||||
g_object_unref(stream);
|
||||
}
|
||||
|
||||
static GMimeMessage *parse_message(FILE *f)
|
||||
{
|
||||
GMimeMessage *message;
|
||||
GMimeParser *parser;
|
||||
GMimeStream *stream;
|
||||
|
||||
stream = g_mime_stream_file_new(f);
|
||||
|
||||
parser = g_mime_parser_new_with_stream(stream);
|
||||
g_mime_parser_set_respect_content_length(parser, 1);
|
||||
|
||||
g_object_unref(stream);
|
||||
|
||||
message = g_mime_parser_construct_message(parser);
|
||||
|
||||
g_object_unref(parser);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
static void process_message_callback(GMimeObject *part, gpointer user_data)
|
||||
{
|
||||
struct mime_cbinfo *cbinfo = user_data;
|
||||
|
||||
cbinfo->count++;
|
||||
|
||||
/* We strip off the headers before we get here, so should only see GMIME_IS_PART */
|
||||
if (GMIME_IS_MESSAGE_PART(part)) {
|
||||
ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PART\n");
|
||||
return;
|
||||
} else if (GMIME_IS_MESSAGE_PARTIAL(part)) {
|
||||
ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PARTIAL\n");
|
||||
return;
|
||||
} else if (GMIME_IS_MULTIPART(part)) {
|
||||
GList *l;
|
||||
|
||||
ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MULTIPART, trying to process subparts\n");
|
||||
l = GMIME_MULTIPART(part)->subparts;
|
||||
while (l) {
|
||||
process_message_callback(l->data, cbinfo);
|
||||
l = l->next;
|
||||
}
|
||||
} else if (GMIME_IS_PART(part)) {
|
||||
const char *filename;
|
||||
|
||||
if (ast_strlen_zero(filename = g_mime_part_get_filename(GMIME_PART(part)))) {
|
||||
ast_debug(1, "Skipping part with no filename\n");
|
||||
return;
|
||||
}
|
||||
|
||||
post_raw(GMIME_PART(part), cbinfo->post_dir, filename);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Encountered unknown MIME part. This should never happen!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int process_message(GMimeMessage *message, const char *post_dir)
|
||||
{
|
||||
struct mime_cbinfo cbinfo = {
|
||||
.count = 0,
|
||||
.post_dir = post_dir,
|
||||
};
|
||||
|
||||
g_mime_message_foreach_part(message, process_message_callback, &cbinfo);
|
||||
|
||||
return cbinfo.count;
|
||||
}
|
||||
|
||||
static struct ast_str *handle_post(struct ast_tcptls_session_instance *ser, char *uri,
|
||||
int *status, char **title, int *contentlength, struct ast_variable *headers,
|
||||
struct ast_variable *cookies)
|
||||
{
|
||||
char buf[4096];
|
||||
FILE *f;
|
||||
size_t res;
|
||||
struct ast_variable *var;
|
||||
int content_len = 0;
|
||||
struct ast_http_post_mapping *post_map;
|
||||
const char *post_dir;
|
||||
unsigned long ident = 0;
|
||||
GMimeMessage *message;
|
||||
int message_count = 0;
|
||||
|
||||
for (var = cookies; var; var = var->next) {
|
||||
if (strcasecmp(var->name, "mansession_id")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sscanf(var->value, "%lx", &ident) != 1) {
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Bad Request")),
|
||||
NULL, "The was an error parsing the request.");
|
||||
}
|
||||
|
||||
if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
|
||||
return ast_http_error((*status = 401),
|
||||
(*title = ast_strdup("Unauthorized")),
|
||||
NULL, "You are not authorized to make this request.");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
if (!var) {
|
||||
return ast_http_error((*status = 401),
|
||||
(*title = ast_strdup("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;
|
||||
}
|
||||
ast_debug(1, "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);
|
||||
}
|
||||
}
|
||||
|
||||
for (res = sizeof(buf); content_len; content_len -= res) {
|
||||
if (content_len < res) {
|
||||
res = content_len;
|
||||
}
|
||||
fread(buf, 1, res, ser->f);
|
||||
fwrite(buf, 1, res, f);
|
||||
}
|
||||
|
||||
if (fseek(f, SEEK_SET, 0)) {
|
||||
ast_debug(1, "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))) {
|
||||
ast_debug(1, "%s is not a valid URI for POST\n", uri);
|
||||
AST_RWLIST_UNLOCK(&post_mappings);
|
||||
|
||||
return ast_http_error((*status = 404),
|
||||
(*title = ast_strdup("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);
|
||||
|
||||
ast_debug(1, "Going to post files to dir %s\n", post_dir);
|
||||
|
||||
message = parse_message(f); /* Takes ownership and will close f */
|
||||
|
||||
if (!message) {
|
||||
ast_log(LOG_ERROR, "Error parsing MIME data\n");
|
||||
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Bad Request")),
|
||||
NULL, "The was an error parsing the request.");
|
||||
}
|
||||
|
||||
if (!(message_count = process_message(message, post_dir))) {
|
||||
ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
|
||||
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Bad Request")),
|
||||
NULL, "The was an error parsing the request.");
|
||||
}
|
||||
|
||||
return ast_http_error((*status = 200),
|
||||
(*title = ast_strdup("OK")),
|
||||
NULL, "File successfully uploaded.");
|
||||
}
|
||||
#endif /* ENABLE_UPLOADS */
|
||||
|
||||
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,
|
||||
int *status, char **title, int *contentlength, struct ast_variable **cookies, struct ast_variable *headers,
|
||||
unsigned int *static_content)
|
||||
{
|
||||
char *c;
|
||||
|
@ -734,7 +495,7 @@ static struct ast_str *handle_uri(struct ast_tcptls_session_instance *ser, char
|
|||
|
||||
if (urih) {
|
||||
*static_content = urih->static_content;
|
||||
out = urih->callback(ser, uri, method, vars, status, title, contentlength);
|
||||
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),
|
||||
|
@ -910,7 +671,7 @@ static void *httpd_helper_thread(void *data)
|
|||
"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, &static_content);
|
||||
&status, &title, &contentlength, &vars, headers, &static_content);
|
||||
}
|
||||
|
||||
/* If they aren't mopped up already, clean up the cookies */
|
||||
|
@ -1026,55 +787,6 @@ static void add_redirect(const char *value)
|
|||
AST_RWLIST_UNLOCK(&uri_redirects);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_UPLOADS
|
||||
static void destroy_post_mapping(struct ast_http_post_mapping *post_map)
|
||||
{
|
||||
if (post_map->from) {
|
||||
ast_free(post_map->from);
|
||||
}
|
||||
if (post_map->to) {
|
||||
ast_free(post_map->to);
|
||||
}
|
||||
ast_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);
|
||||
}
|
||||
#endif /* ENABLE_UPLOADS */
|
||||
|
||||
static int __ast_http_load(int reload)
|
||||
{
|
||||
struct ast_config *cfg;
|
||||
|
@ -1115,10 +827,6 @@ static int __ast_http_load(int reload)
|
|||
}
|
||||
AST_RWLIST_UNLOCK(&uri_redirects);
|
||||
|
||||
#ifdef ENABLE_UPLOADS
|
||||
destroy_post_mappings();
|
||||
#endif /* ENABLE_UPLOADS */
|
||||
|
||||
if (cfg) {
|
||||
v = ast_variable_browse(cfg, "general");
|
||||
for (; v; v = v->next) {
|
||||
|
@ -1165,12 +873,6 @@ static int __ast_http_load(int reload)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_UPLOADS
|
||||
for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next) {
|
||||
add_post_mapping(v->name, v->value);
|
||||
}
|
||||
#endif /* ENABLE_UPLOADS */
|
||||
|
||||
ast_config_destroy(cfg);
|
||||
}
|
||||
|
||||
|
@ -1197,10 +899,6 @@ static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_a
|
|||
struct ast_http_uri *urih;
|
||||
struct http_uri_redirect *redirect;
|
||||
|
||||
#ifdef ENABLE_UPLOADS
|
||||
struct ast_http_post_mapping *post_map;
|
||||
#endif /* ENABLE_UPLOADS */
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "http show status";
|
||||
|
@ -1251,17 +949,6 @@ static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_a
|
|||
}
|
||||
AST_RWLIST_UNLOCK(&uri_redirects);
|
||||
|
||||
|
||||
#ifdef ENABLE_UPLOADS
|
||||
ast_cli(a->fd, "\nPOST mappings:\n");
|
||||
AST_RWLIST_RDLOCK(&post_mappings);
|
||||
AST_LIST_TRAVERSE(&post_mappings, post_map, entry) {
|
||||
ast_cli(a->fd, "%s/%s => %s\n", prefix, post_map->from, post_map->to);
|
||||
}
|
||||
ast_cli(a->fd, "%s\n", AST_LIST_EMPTY(&post_mappings) ? "None.\n" : "");
|
||||
AST_RWLIST_UNLOCK(&post_mappings);
|
||||
#endif /* ENABLE_UPLOADS */
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -1276,10 +963,6 @@ static struct ast_cli_entry cli_http[] = {
|
|||
|
||||
int ast_http_init(void)
|
||||
{
|
||||
#ifdef ENABLE_UPLOADS
|
||||
g_mime_init(0);
|
||||
#endif /* ENABLE_UPLOADS */
|
||||
|
||||
ast_http_uri_link(&statusuri);
|
||||
ast_http_uri_link(&staticuri);
|
||||
ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
|
||||
|
|
|
@ -3631,17 +3631,17 @@ generic_callback_out:
|
|||
return out;
|
||||
}
|
||||
|
||||
static struct ast_str *manager_http_callback(struct ast_tcptls_session_instance *ser, const char *uri, enum ast_http_method method, struct ast_variable *params, int *status, char **title, int *contentlength)
|
||||
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)
|
||||
{
|
||||
return generic_http_callback(FORMAT_HTML, &ser->requestor, uri, method, params, status, title, contentlength);
|
||||
}
|
||||
|
||||
static struct ast_str *mxml_http_callback(struct ast_tcptls_session_instance *ser, const char *uri, enum ast_http_method method, struct ast_variable *params, int *status, char **title, int *contentlength)
|
||||
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)
|
||||
{
|
||||
return generic_http_callback(FORMAT_XML, &ser->requestor, uri, method, params, status, title, contentlength);
|
||||
}
|
||||
|
||||
static struct ast_str *rawman_http_callback(struct ast_tcptls_session_instance *ser, const char *uri, enum ast_http_method method, struct ast_variable *params, int *status, char **title, int *contentlength)
|
||||
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)
|
||||
{
|
||||
return generic_http_callback(FORMAT_RAW, &ser->requestor, uri, method, params, status, title, contentlength);
|
||||
}
|
||||
|
@ -3651,6 +3651,8 @@ struct ast_http_uri rawmanuri = {
|
|||
.uri = "rawman",
|
||||
.callback = rawman_http_callback,
|
||||
.supports_get = 1,
|
||||
.data = NULL,
|
||||
.key = __FILE__,
|
||||
};
|
||||
|
||||
struct ast_http_uri manageruri = {
|
||||
|
@ -3658,6 +3660,8 @@ struct ast_http_uri manageruri = {
|
|||
.uri = "manager",
|
||||
.callback = manager_http_callback,
|
||||
.supports_get = 1,
|
||||
.data = NULL,
|
||||
.key = __FILE__,
|
||||
};
|
||||
|
||||
struct ast_http_uri managerxmluri = {
|
||||
|
@ -3665,6 +3669,8 @@ struct ast_http_uri managerxmluri = {
|
|||
.uri = "mxml",
|
||||
.callback = mxml_http_callback,
|
||||
.supports_get = 1,
|
||||
.data = NULL,
|
||||
.key = __FILE__,
|
||||
};
|
||||
|
||||
static int registered = 0;
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2006, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file
|
||||
* \brief HTTP POST upload support for Asterisk HTTP server
|
||||
*
|
||||
* \author Terry Wilson <twilson@digium.com
|
||||
*
|
||||
* \ref AstHTTP - AMI over the http protocol
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>gmime</depend>
|
||||
***/
|
||||
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 111213 $")
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <gmime/gmime.h>
|
||||
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/http.h"
|
||||
#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
|
||||
#include "asterisk/tcptls.h"
|
||||
#include "asterisk/manager.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/ast_version.h"
|
||||
|
||||
#define MAX_PREFIX 80
|
||||
|
||||
/* just a little structure to hold callback info for gmime */
|
||||
struct mime_cbinfo {
|
||||
int count;
|
||||
const char *post_dir;
|
||||
};
|
||||
|
||||
/* all valid URIs must be prepended by the string in prefix. */
|
||||
static char prefix[MAX_PREFIX];
|
||||
|
||||
static void post_raw(GMimePart *part, const char *post_dir, const char *fn)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
GMimeDataWrapper *content;
|
||||
GMimeStream *stream;
|
||||
int fd;
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);
|
||||
|
||||
ast_debug(1, "Posting raw data to %s\n", filename);
|
||||
|
||||
if ((fd = open(filename, O_CREAT | O_WRONLY, 0666)) == -1) {
|
||||
ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
stream = g_mime_stream_fs_new(fd);
|
||||
|
||||
content = g_mime_part_get_content_object(part);
|
||||
g_mime_data_wrapper_write_to_stream(content, stream);
|
||||
g_mime_stream_flush(stream);
|
||||
|
||||
g_object_unref(content);
|
||||
g_object_unref(stream);
|
||||
}
|
||||
|
||||
static GMimeMessage *parse_message(FILE *f)
|
||||
{
|
||||
GMimeMessage *message;
|
||||
GMimeParser *parser;
|
||||
GMimeStream *stream;
|
||||
|
||||
stream = g_mime_stream_file_new(f);
|
||||
|
||||
parser = g_mime_parser_new_with_stream(stream);
|
||||
g_mime_parser_set_respect_content_length(parser, 1);
|
||||
|
||||
g_object_unref(stream);
|
||||
|
||||
message = g_mime_parser_construct_message(parser);
|
||||
|
||||
g_object_unref(parser);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
static void process_message_callback(GMimeObject *part, gpointer user_data)
|
||||
{
|
||||
struct mime_cbinfo *cbinfo = user_data;
|
||||
|
||||
cbinfo->count++;
|
||||
|
||||
/* We strip off the headers before we get here, so should only see GMIME_IS_PART */
|
||||
if (GMIME_IS_MESSAGE_PART(part)) {
|
||||
ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PART\n");
|
||||
return;
|
||||
} else if (GMIME_IS_MESSAGE_PARTIAL(part)) {
|
||||
ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PARTIAL\n");
|
||||
return;
|
||||
} else if (GMIME_IS_MULTIPART(part)) {
|
||||
GList *l;
|
||||
|
||||
ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MULTIPART, trying to process subparts\n");
|
||||
l = GMIME_MULTIPART(part)->subparts;
|
||||
while (l) {
|
||||
process_message_callback(l->data, cbinfo);
|
||||
l = l->next;
|
||||
}
|
||||
} else if (GMIME_IS_PART(part)) {
|
||||
const char *filename;
|
||||
|
||||
if (ast_strlen_zero(filename = g_mime_part_get_filename(GMIME_PART(part)))) {
|
||||
ast_debug(1, "Skipping part with no filename\n");
|
||||
return;
|
||||
}
|
||||
|
||||
post_raw(GMIME_PART(part), cbinfo->post_dir, filename);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Encountered unknown MIME part. This should never happen!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int process_message(GMimeMessage *message, const char *post_dir)
|
||||
{
|
||||
struct mime_cbinfo cbinfo = {
|
||||
.count = 0,
|
||||
.post_dir = post_dir,
|
||||
};
|
||||
|
||||
g_mime_message_foreach_part(message, process_message_callback, &cbinfo);
|
||||
|
||||
return cbinfo.count;
|
||||
}
|
||||
|
||||
static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
|
||||
{
|
||||
struct ast_variable *var;
|
||||
unsigned long ident = 0;
|
||||
char buf[4096];
|
||||
FILE *f;
|
||||
size_t res;
|
||||
int content_len = 0;
|
||||
struct ast_str *post_dir;
|
||||
GMimeMessage *message;
|
||||
int message_count = 0;
|
||||
|
||||
if (!urih) {
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Missing URI handle")),
|
||||
NULL, "There was an error parsing the request");
|
||||
}
|
||||
|
||||
for (var = vars; var; var = var->next) {
|
||||
if (strcasecmp(var->name, "mansession_id")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sscanf(var->value, "%lx", &ident) != 1) {
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Bad Request")),
|
||||
NULL, "The was an error parsing the request.");
|
||||
}
|
||||
|
||||
if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
|
||||
return ast_http_error((*status = 401),
|
||||
(*title = ast_strdup("Unauthorized")),
|
||||
NULL, "You are not authorized to make this request.");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!var) {
|
||||
return ast_http_error((*status = 401),
|
||||
(*title = ast_strdup("Unauthorized")),
|
||||
NULL, "You are not authorized to make this request.");
|
||||
}
|
||||
|
||||
if (!(f = tmpfile())) {
|
||||
ast_log(LOG_ERROR, "Could not create temp file.\n");
|
||||
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;
|
||||
}
|
||||
ast_debug(1, "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);
|
||||
}
|
||||
}
|
||||
|
||||
for (res = sizeof(buf); content_len; content_len -= res) {
|
||||
if (content_len < res) {
|
||||
res = content_len;
|
||||
}
|
||||
fread(buf, 1, res, ser->f);
|
||||
fwrite(buf, 1, res, f);
|
||||
}
|
||||
|
||||
if (fseek(f, SEEK_SET, 0)) {
|
||||
ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
|
||||
fclose(f);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
post_dir = urih->data;
|
||||
|
||||
message = parse_message(f); /* Takes ownership and will close f */
|
||||
|
||||
if (!message) {
|
||||
ast_log(LOG_ERROR, "Error parsing MIME data\n");
|
||||
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Bad Request")),
|
||||
NULL, "The was an error parsing the request.");
|
||||
}
|
||||
|
||||
if (!(message_count = process_message(message, post_dir->str))) {
|
||||
ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
|
||||
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Bad Request")),
|
||||
NULL, "The was an error parsing the request.");
|
||||
}
|
||||
|
||||
return ast_http_error((*status = 200),
|
||||
(*title = ast_strdup("OK")),
|
||||
NULL, "File successfully uploaded.");
|
||||
}
|
||||
|
||||
static int __ast_http_post_load(int reload)
|
||||
{
|
||||
struct ast_config *cfg;
|
||||
struct ast_variable *v;
|
||||
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
|
||||
|
||||
if ((cfg = ast_config_load2("http.conf", "http", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
ast_http_uri_unlink_all_with_key(__FILE__);
|
||||
}
|
||||
|
||||
if (cfg) {
|
||||
for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
|
||||
if (!strcasecmp(v->name, "prefix")) {
|
||||
ast_copy_string(prefix, v->value, sizeof(prefix));
|
||||
if (prefix[strlen(prefix)] == '/') {
|
||||
prefix[strlen(prefix)] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next) {
|
||||
struct ast_http_uri *urih;
|
||||
struct ast_str *ds;
|
||||
|
||||
if (!(urih = ast_calloc(sizeof(*urih), 1))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(ds = ast_str_create(32)))
|
||||
return -1;
|
||||
|
||||
|
||||
urih->description = ast_strdup("HTTP POST mapping");
|
||||
urih->uri = ast_strdup(v->name);
|
||||
ast_str_set(&ds, 0, "%s/%s", prefix, v->value);
|
||||
urih->data = ds;
|
||||
urih->has_subtree = 0;
|
||||
urih->supports_get = 0;
|
||||
urih->supports_post = 1;
|
||||
urih->callback = http_post_callback;
|
||||
urih->key = __FILE__;
|
||||
|
||||
ast_http_uri_link(urih);
|
||||
}
|
||||
|
||||
ast_config_destroy(cfg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_http_uri_unlink_all_with_key(__FILE__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reload(void)
|
||||
{
|
||||
|
||||
__ast_http_post_load(1);
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
g_mime_init(0);
|
||||
|
||||
__ast_http_post_load(0);
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "HTTP POST support",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.reload = reload,
|
||||
);
|
|
@ -323,8 +323,7 @@ static int load_file(const char *filename, char **ret)
|
|||
}
|
||||
|
||||
/*! \brief Callback that is executed everytime an http request is received by this module */
|
||||
static struct ast_str *phoneprov_callback(struct ast_tcptls_session_instance *ser, const char *uri, enum ast_http_method method,
|
||||
struct ast_variable *vars, int *status, char **title, int *contentlength)
|
||||
static struct ast_str *phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
|
||||
{
|
||||
struct http_route *route;
|
||||
struct http_route search_route = {
|
||||
|
@ -987,6 +986,8 @@ static struct ast_http_uri phoneprovuri = {
|
|||
.uri = "phoneprov",
|
||||
.has_subtree = 1,
|
||||
.supports_get = 1,
|
||||
.data = NULL,
|
||||
.key = __FILE__,
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
|
|
Loading…
Reference in New Issue