HTTP POST upload support for Asterisk HTTP server. More...
#include "asterisk.h"#include <sys/stat.h>#include <fcntl.h>#include <gmime/gmime.h>#include "asterisk/linkedlists.h"#include "asterisk/http.h"#include "asterisk/paths.h"#include "asterisk/tcptls.h"#include "asterisk/manager.h"#include "asterisk/cli.h"#include "asterisk/module.h"#include "asterisk/ast_version.h"
Go to the source code of this file.
Data Structures | |
| struct | mime_cbinfo |
Defines | |
| #define | MAX_PREFIX 80 |
Functions | |
| static int | __ast_http_post_load (int reload) |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static int | find_sequence (char *inbuf, int inlen, char *matchbuf, int matchlen) |
| 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) |
| static int | load_module (void) |
| static GMimeMessage * | parse_message (FILE *f) |
| static void | post_raw (GMimePart *part, const char *post_dir, const char *fn) |
| static int | process_message (GMimeMessage *message, const char *post_dir) |
| static void | process_message_callback (GMimeObject *part, gpointer user_data) |
| static int | readmimefile (FILE *fin, FILE *fout, char *boundary, int contentlen) |
| static int | reload (void) |
| static int | unload_module (void) |
Variables | |
| static struct ast_module_info __MODULE_INFO_SECTION | __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "HTTP POST support" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| static char | prefix [MAX_PREFIX] |
HTTP POST upload support for Asterisk HTTP server.
AMI over HTTP support - AMI over the http protocol
Definition in file res_http_post.c.
| #define MAX_PREFIX 80 |
Definition at line 53 of file res_http_post.c.
| static int __ast_http_post_load | ( | int | reload | ) | [static] |
Definition at line 408 of file res_http_post.c.
References ast_calloc, ast_config_destroy(), ast_config_load2(), ast_copy_string(), ast_free, ast_http_uri_link(), ast_http_uri_unlink_all_with_key(), ast_str_create(), ast_str_set(), ast_strdup, ast_variable_browse(), ast_http_uri::callback, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, ast_http_uri::data, ast_http_uri::description, ast_http_uri::dmallocd, ast_http_uri::has_subtree, http_post_callback(), ast_http_uri::key, ast_http_uri::mallocd, ast_variable::name, ast_variable::next, ast_http_uri::supports_get, ast_http_uri::supports_post, ast_http_uri::uri, and ast_variable::value.
Referenced by load_module(), and reload().
{
struct ast_config *cfg;
struct ast_variable *v;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
cfg = ast_config_load2("http.conf", "http", config_flags);
if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
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))) {
ast_config_destroy(cfg);
return -1;
}
if (!(ds = ast_str_create(32))) {
ast_free(urih);
ast_config_destroy(cfg);
return -1;
}
urih->description = ast_strdup("HTTP POST mapping");
urih->uri = ast_strdup(v->name);
ast_str_set(&ds, 0, "%s", 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__;
urih->mallocd = urih->dmallocd = 1;
ast_http_uri_link(urih);
}
ast_config_destroy(cfg);
}
return 0;
}
| static void __reg_module | ( | void | ) | [static] |
Definition at line 494 of file res_http_post.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 494 of file res_http_post.c.
| static int find_sequence | ( | char * | inbuf, |
| int | inlen, | ||
| char * | matchbuf, | ||
| int | matchlen | ||
| ) | [static] |
Definition at line 161 of file res_http_post.c.
Referenced by readmimefile().
{
int current;
int comp;
int found = 0;
for (current = 0; current < inlen-matchlen; current++, inbuf++) {
if (*inbuf == *matchbuf) {
found=1;
for (comp = 1; comp < matchlen; comp++) {
if (inbuf[comp] != matchbuf[comp]) {
found = 0;
break;
}
}
if (found) {
break;
}
}
}
if (found) {
return current;
} else {
return -1;
}
}
| 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 | ||
| ) | [static, read] |
Definition at line 296 of file res_http_post.c.
References ast_debug, ast_http_error(), ast_log(), ast_str_buffer(), ast_strdup, astman_verify_session_writepermissions(), ast_http_uri::data, EVENT_FLAG_CONFIG, ast_tcptls_session_instance::f, f, LOG_DEBUG, LOG_ERROR, ast_variable::name, ast_variable::next, option_debug, parse_message(), process_message(), readmimefile(), ast_variable::value, and var.
Referenced by __ast_http_post_load().
{
struct ast_variable *var;
unsigned long ident = 0;
FILE *f;
int content_len = 0;
struct ast_str *post_dir;
GMimeMessage *message;
int message_count = 0;
char * boundary_marker = NULL;
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, "%30lx", &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) {
fprintf(f, "%s: %s\r\n", var->name, var->value);
if (!strcasecmp(var->name, "Content-Length")) {
if ((sscanf(var->value, "%30u", &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")) {
boundary_marker = strstr(var->value, "boundary=");
if (boundary_marker) {
boundary_marker += strlen("boundary=");
}
}
}
fprintf(f, "\r\n");
if (0 > readmimefile(ser->f, f, boundary_marker, content_len)) {
if (option_debug) {
ast_log(LOG_DEBUG, "Cannot find boundary marker in POST request.\n");
}
fclose(f);
return NULL;
}
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, ast_str_buffer(post_dir)))) {
ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
g_object_unref(message);
return ast_http_error((*status = 400),
(*title = ast_strdup("Bad Request")),
NULL, "The was an error parsing the request.");
}
g_object_unref(message);
return ast_http_error((*status = 200),
(*title = ast_strdup("OK")),
NULL, "File successfully uploaded.");
}
| static int load_module | ( | void | ) | [static] |
Definition at line 481 of file res_http_post.c.
References __ast_http_post_load(), and AST_MODULE_LOAD_SUCCESS.
{
g_mime_init(0);
__ast_http_post_load(0);
return AST_MODULE_LOAD_SUCCESS;
}
| static GMimeMessage* parse_message | ( | FILE * | f | ) | [static] |
Definition at line 91 of file res_http_post.c.
Referenced by http_post_callback().
{
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 post_raw | ( | GMimePart * | part, |
| const char * | post_dir, | ||
| const char * | fn | ||
| ) | [static] |
Definition at line 64 of file res_http_post.c.
References ast_debug, ast_log(), and LOG_WARNING.
Referenced by process_message_callback().
{
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 | O_TRUNC, 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 int process_message | ( | GMimeMessage * | message, |
| const char * | post_dir | ||
| ) | [static] |
Definition at line 147 of file res_http_post.c.
References mime_cbinfo::count, mime_cbinfo::post_dir, and process_message_callback().
Referenced by http_post_callback().
{
struct mime_cbinfo cbinfo = {
.count = 0,
.post_dir = post_dir,
};
g_mime_message_foreach_part(message, process_message_callback, &cbinfo);
return cbinfo.count;
}
| static void process_message_callback | ( | GMimeObject * | part, |
| gpointer | user_data | ||
| ) | [static] |
Definition at line 111 of file res_http_post.c.
References ast_debug, ast_log(), ast_strlen_zero(), mime_cbinfo::count, LOG_ERROR, LOG_WARNING, mime_cbinfo::post_dir, and post_raw().
Referenced by process_message().
{
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 readmimefile | ( | FILE * | fin, |
| FILE * | fout, | ||
| char * | boundary, | ||
| int | contentlen | ||
| ) | [static] |
Definition at line 197 of file res_http_post.c.
References ast_log(), buf, errno, find_sequence(), fwrite, and LOG_WARNING.
Referenced by http_post_callback().
{
int find_filename = 0;
char buf[4096];
int marker;
int x;
int char_in_buf = 0;
int num_to_read;
int boundary_len;
char * path_end, * path_start, * filespec;
if (NULL == fin || NULL == fout || NULL == boundary || 0 >= contentlen) {
return -1;
}
boundary_len = strlen(boundary);
while (0 < contentlen || 0 < char_in_buf) {
/* determine how much I will read into the buffer */
if (contentlen > sizeof(buf) - char_in_buf) {
num_to_read = sizeof(buf)- char_in_buf;
} else {
num_to_read = contentlen;
}
if (0 < num_to_read) {
if (fread(&(buf[char_in_buf]), 1, num_to_read, fin) < num_to_read) {
ast_log(LOG_WARNING, "fread() failed: %s\n", strerror(errno));
num_to_read = 0;
}
contentlen -= num_to_read;
char_in_buf += num_to_read;
}
/* If I am looking for the filename spec */
if (find_filename) {
path_end = filespec = NULL;
x = strlen("filename=\"");
marker = find_sequence(buf, char_in_buf, "filename=\"", x );
if (0 <= marker) {
marker += x; /* Index beyond the filename marker */
path_start = &buf[marker];
for (path_end = path_start, x = 0; x < char_in_buf-marker; x++, path_end++) {
if ('\\' == *path_end) { /* convert backslashses to forward slashes */
*path_end = '/';
}
if ('\"' == *path_end) { /* If at the end of the file name spec */
*path_end = '\0'; /* temporarily null terminate the file spec for basename */
filespec = basename(path_start);
*path_end = '\"';
break;
}
}
}
if (filespec) { /* If the file name path was found in the header */
if (fwrite(buf, 1, marker, fout) != marker) {
ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
}
x = (int)(path_end+1 - filespec);
if (fwrite(filespec, 1, x, fout) != x) {
ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
}
x = (int)(path_end+1 - buf);
memmove(buf, &(buf[x]), char_in_buf-x);
char_in_buf -= x;
}
find_filename = 0;
} else { /* I am looking for the boundary marker */
marker = find_sequence(buf, char_in_buf, boundary, boundary_len);
if (0 > marker) {
if (char_in_buf < (boundary_len)) {
/*no possibility to find the boundary, write all you have */
if (fwrite(buf, 1, char_in_buf, fout) != char_in_buf) {
ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
}
char_in_buf = 0;
} else {
/* write all except for area where the boundary marker could be */
if (fwrite(buf, 1, char_in_buf -(boundary_len -1), fout) != char_in_buf - (boundary_len - 1)) {
ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
}
x = char_in_buf -(boundary_len -1);
memmove(buf, &(buf[x]), char_in_buf-x);
char_in_buf = (boundary_len -1);
}
} else {
/* write up through the boundary, then look for filename in the rest */
if (fwrite(buf, 1, marker + boundary_len, fout) != marker + boundary_len) {
ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
}
x = marker + boundary_len;
memmove(buf, &(buf[x]), char_in_buf-x);
char_in_buf -= marker + boundary_len;
find_filename =1;
}
}
}
return 0;
}
| static int reload | ( | void | ) | [static] |
Definition at line 474 of file res_http_post.c.
References __ast_http_post_load(), and AST_MODULE_LOAD_SUCCESS.
{
__ast_http_post_load(1);
return AST_MODULE_LOAD_SUCCESS;
}
| static int unload_module | ( | void | ) | [static] |
Definition at line 467 of file res_http_post.c.
References ast_http_uri_unlink_all_with_key().
{
ast_http_uri_unlink_all_with_key(__FILE__);
return 0;
}
struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "HTTP POST support" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static] |
Definition at line 494 of file res_http_post.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 494 of file res_http_post.c.
char prefix[MAX_PREFIX] [static] |
Definition at line 62 of file res_http_post.c.