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 int | 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 *get_vars, struct ast_variable *headers) |
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 | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "HTTP POST support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .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 54 of file res_http_post.c.
static int __ast_http_post_load | ( | int | reload | ) | [static] |
Definition at line 426 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::uri, and ast_variable::value.
Referenced by load_module(), and reload().
00427 { 00428 struct ast_config *cfg; 00429 struct ast_variable *v; 00430 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 00431 00432 cfg = ast_config_load2("http.conf", "http", config_flags); 00433 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) { 00434 return 0; 00435 } 00436 00437 if (reload) { 00438 ast_http_uri_unlink_all_with_key(__FILE__); 00439 } 00440 00441 if (cfg) { 00442 for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { 00443 if (!strcasecmp(v->name, "prefix")) { 00444 ast_copy_string(prefix, v->value, sizeof(prefix)); 00445 if (prefix[strlen(prefix)] == '/') { 00446 prefix[strlen(prefix)] = '\0'; 00447 } 00448 } 00449 } 00450 00451 for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next) { 00452 struct ast_http_uri *urih; 00453 struct ast_str *ds; 00454 00455 if (!(urih = ast_calloc(sizeof(*urih), 1))) { 00456 ast_config_destroy(cfg); 00457 return -1; 00458 } 00459 00460 if (!(ds = ast_str_create(32))) { 00461 ast_free(urih); 00462 ast_config_destroy(cfg); 00463 return -1; 00464 } 00465 00466 urih->description = ast_strdup("HTTP POST mapping"); 00467 urih->uri = ast_strdup(v->name); 00468 ast_str_set(&ds, 0, "%s", v->value); 00469 urih->data = ds; 00470 urih->has_subtree = 0; 00471 urih->callback = http_post_callback; 00472 urih->key = __FILE__; 00473 urih->mallocd = urih->dmallocd = 1; 00474 00475 ast_http_uri_link(urih); 00476 } 00477 00478 ast_config_destroy(cfg); 00479 } 00480 return 0; 00481 }
static void __reg_module | ( | void | ) | [static] |
Definition at line 510 of file res_http_post.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 510 of file res_http_post.c.
static int find_sequence | ( | char * | inbuf, | |
int | inlen, | |||
char * | matchbuf, | |||
int | matchlen | |||
) | [static] |
Definition at line 180 of file res_http_post.c.
Referenced by readmimefile().
00181 { 00182 int current; 00183 int comp; 00184 int found = 0; 00185 00186 for (current = 0; current < inlen-matchlen; current++, inbuf++) { 00187 if (*inbuf == *matchbuf) { 00188 found=1; 00189 for (comp = 1; comp < matchlen; comp++) { 00190 if (inbuf[comp] != matchbuf[comp]) { 00191 found = 0; 00192 break; 00193 } 00194 } 00195 if (found) { 00196 break; 00197 } 00198 } 00199 } 00200 if (found) { 00201 return current; 00202 } else { 00203 return -1; 00204 } 00205 }
static int 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 * | get_vars, | |||
struct ast_variable * | headers | |||
) | [static] |
Definition at line 314 of file res_http_post.c.
References ast_debug, ast_http_error(), ast_http_get_cookies(), ast_http_manid_from_vars(), AST_HTTP_POST, ast_log(), ast_str_buffer(), ast_variables_destroy(), astman_is_authed(), 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().
00315 { 00316 struct ast_variable *var, *cookies; 00317 unsigned long ident = 0; 00318 FILE *f; 00319 int content_len = 0; 00320 struct ast_str *post_dir; 00321 GMimeMessage *message; 00322 int message_count = 0; 00323 char * boundary_marker = NULL; 00324 00325 if (method != AST_HTTP_POST) { 00326 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method"); 00327 return -1; 00328 } 00329 00330 if (!astman_is_authed(ast_http_manid_from_vars(headers))) { 00331 ast_http_error(ser, 403, "Access Denied", "Sorry, I cannot let you do that, Dave."); 00332 return -1; 00333 } 00334 00335 if (!urih) { 00336 ast_http_error(ser, 400, "Missing URI handle", "There was an error parsing the request"); 00337 return -1; 00338 } 00339 00340 cookies = ast_http_get_cookies(headers); 00341 for (var = cookies; var; var = var->next) { 00342 if (!strcasecmp(var->name, "mansession_id")) { 00343 sscanf(var->value, "%30lx", &ident); 00344 break; 00345 } 00346 } 00347 if (cookies) { 00348 ast_variables_destroy(cookies); 00349 } 00350 00351 if (ident == 0) { 00352 ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request."); 00353 return -1; 00354 } 00355 if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) { 00356 ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request."); 00357 return -1; 00358 } 00359 00360 if (!(f = tmpfile())) { 00361 ast_log(LOG_ERROR, "Could not create temp file.\n"); 00362 ast_http_error(ser, 500, "Internal server error", "Could not create temp file."); 00363 return -1; 00364 } 00365 00366 for (var = headers; var; var = var->next) { 00367 fprintf(f, "%s: %s\r\n", var->name, var->value); 00368 00369 if (!strcasecmp(var->name, "Content-Length")) { 00370 if ((sscanf(var->value, "%30u", &content_len)) != 1) { 00371 ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n"); 00372 fclose(f); 00373 ast_http_error(ser, 500, "Internal server error", "Invalid Content-Length in POST request!"); 00374 return -1; 00375 } 00376 ast_debug(1, "Got a Content-Length of %d\n", content_len); 00377 } else if (!strcasecmp(var->name, "Content-Type")) { 00378 boundary_marker = strstr(var->value, "boundary="); 00379 if (boundary_marker) { 00380 boundary_marker += strlen("boundary="); 00381 } 00382 } 00383 } 00384 00385 fprintf(f, "\r\n"); 00386 00387 if (0 > readmimefile(ser->f, f, boundary_marker, content_len)) { 00388 if (option_debug) { 00389 ast_log(LOG_DEBUG, "Cannot find boundary marker in POST request.\n"); 00390 } 00391 fclose(f); 00392 00393 return -1; 00394 } 00395 00396 if (fseek(f, SEEK_SET, 0)) { 00397 ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n"); 00398 fclose(f); 00399 ast_http_error(ser, 500, "Internal server error", "Failed to seek temp file back to beginning."); 00400 return -1; 00401 } 00402 00403 post_dir = urih->data; 00404 00405 message = parse_message(f); /* Takes ownership and will close f */ 00406 00407 if (!message) { 00408 ast_log(LOG_ERROR, "Error parsing MIME data\n"); 00409 00410 ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request."); 00411 return -1; 00412 } 00413 00414 if (!(message_count = process_message(message, ast_str_buffer(post_dir)))) { 00415 ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n"); 00416 g_object_unref(message); 00417 ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request."); 00418 return -1; 00419 } 00420 g_object_unref(message); 00421 00422 ast_http_error(ser, 200, "OK", "File successfully uploaded."); 00423 return 0; 00424 }
static int load_module | ( | void | ) | [static] |
Definition at line 497 of file res_http_post.c.
References __ast_http_post_load(), and AST_MODULE_LOAD_SUCCESS.
00498 { 00499 g_mime_init(0); 00500 00501 __ast_http_post_load(0); 00502 00503 return AST_MODULE_LOAD_SUCCESS; 00504 }
static GMimeMessage* parse_message | ( | FILE * | f | ) | [static] |
Definition at line 99 of file res_http_post.c.
Referenced by http_post_callback().
00100 { 00101 GMimeMessage *message; 00102 GMimeParser *parser; 00103 GMimeStream *stream; 00104 00105 stream = g_mime_stream_file_new(f); 00106 00107 parser = g_mime_parser_new_with_stream(stream); 00108 g_mime_parser_set_respect_content_length(parser, 1); 00109 00110 g_object_unref(stream); 00111 00112 message = g_mime_parser_construct_message(parser); 00113 00114 g_object_unref(parser); 00115 00116 return message; 00117 }
static void post_raw | ( | GMimePart * | part, | |
const char * | post_dir, | |||
const char * | fn | |||
) | [static] |
Definition at line 70 of file res_http_post.c.
References ast_debug, ast_log(), and LOG_WARNING.
Referenced by process_message_callback().
00071 { 00072 char filename[PATH_MAX]; 00073 GMimeDataWrapper *content; 00074 GMimeStream *stream; 00075 int fd; 00076 00077 snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn); 00078 00079 ast_debug(1, "Posting raw data to %s\n", filename); 00080 00081 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666)) == -1) { 00082 ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename); 00083 00084 return; 00085 } 00086 00087 stream = g_mime_stream_fs_new(fd); 00088 00089 content = g_mime_part_get_content_object(part); 00090 g_mime_data_wrapper_write_to_stream(content, stream); 00091 g_mime_stream_flush(stream); 00092 00093 #ifndef AST_GMIME_VER_24 00094 g_object_unref(content); 00095 #endif 00096 g_object_unref(stream); 00097 }
static int process_message | ( | GMimeMessage * | message, | |
const char * | post_dir | |||
) | [static] |
Definition at line 163 of file res_http_post.c.
References mime_cbinfo::count, and process_message_callback().
Referenced by http_post_callback().
00164 { 00165 struct mime_cbinfo cbinfo = { 00166 .count = 0, 00167 .post_dir = post_dir, 00168 }; 00169 00170 #ifdef AST_GMIME_VER_24 00171 g_mime_message_foreach(message, process_message_callback, &cbinfo); 00172 #else 00173 g_mime_message_foreach_part(message, process_message_callback, &cbinfo); 00174 #endif 00175 00176 return cbinfo.count; 00177 }
static void process_message_callback | ( | GMimeObject * | part, | |
gpointer | user_data | |||
) | [static] |
Definition at line 122 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().
00124 { 00125 struct mime_cbinfo *cbinfo = user_data; 00126 00127 cbinfo->count++; 00128 00129 /* We strip off the headers before we get here, so should only see GMIME_IS_PART */ 00130 if (GMIME_IS_MESSAGE_PART(part)) { 00131 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PART\n"); 00132 return; 00133 } else if (GMIME_IS_MESSAGE_PARTIAL(part)) { 00134 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PARTIAL\n"); 00135 return; 00136 } else if (GMIME_IS_MULTIPART(part)) { 00137 #ifndef AST_GMIME_VER_24 00138 GList *l; 00139 00140 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MULTIPART, trying to process subparts\n"); 00141 l = GMIME_MULTIPART(part)->subparts; 00142 while (l) { 00143 process_message_callback(l->data, cbinfo); 00144 l = l->next; 00145 } 00146 #else 00147 ast_log(LOG_WARNING, "Got unexpected MIME subpart.\n"); 00148 #endif 00149 } else if (GMIME_IS_PART(part)) { 00150 const char *filename; 00151 00152 if (ast_strlen_zero(filename = g_mime_part_get_filename(GMIME_PART(part)))) { 00153 ast_debug(1, "Skipping part with no filename\n"); 00154 return; 00155 } 00156 00157 post_raw(GMIME_PART(part), cbinfo->post_dir, filename); 00158 } else { 00159 ast_log(LOG_ERROR, "Encountered unknown MIME part. This should never happen!\n"); 00160 } 00161 }
static int readmimefile | ( | FILE * | fin, | |
FILE * | fout, | |||
char * | boundary, | |||
int | contentlen | |||
) | [static] |
Definition at line 216 of file res_http_post.c.
References ast_log(), errno, find_sequence(), and LOG_WARNING.
Referenced by http_post_callback().
00217 { 00218 int find_filename = 0; 00219 char buf[4096]; 00220 int marker; 00221 int x; 00222 int char_in_buf = 0; 00223 int num_to_read; 00224 int boundary_len; 00225 char * path_end, * path_start, * filespec; 00226 00227 if (NULL == fin || NULL == fout || NULL == boundary || 0 >= contentlen) { 00228 return -1; 00229 } 00230 00231 boundary_len = strlen(boundary); 00232 while (0 < contentlen || 0 < char_in_buf) { 00233 /* determine how much I will read into the buffer */ 00234 if (contentlen > sizeof(buf) - char_in_buf) { 00235 num_to_read = sizeof(buf)- char_in_buf; 00236 } else { 00237 num_to_read = contentlen; 00238 } 00239 00240 if (0 < num_to_read) { 00241 if (fread(&(buf[char_in_buf]), 1, num_to_read, fin) < num_to_read) { 00242 ast_log(LOG_WARNING, "fread() failed: %s\n", strerror(errno)); 00243 num_to_read = 0; 00244 } 00245 contentlen -= num_to_read; 00246 char_in_buf += num_to_read; 00247 } 00248 /* If I am looking for the filename spec */ 00249 if (find_filename) { 00250 path_end = filespec = NULL; 00251 x = strlen("filename=\""); 00252 marker = find_sequence(buf, char_in_buf, "filename=\"", x ); 00253 if (0 <= marker) { 00254 marker += x; /* Index beyond the filename marker */ 00255 path_start = &buf[marker]; 00256 for (path_end = path_start, x = 0; x < char_in_buf-marker; x++, path_end++) { 00257 if ('\\' == *path_end) { /* convert backslashses to forward slashes */ 00258 *path_end = '/'; 00259 } 00260 if ('\"' == *path_end) { /* If at the end of the file name spec */ 00261 *path_end = '\0'; /* temporarily null terminate the file spec for basename */ 00262 filespec = basename(path_start); 00263 *path_end = '\"'; 00264 break; 00265 } 00266 } 00267 } 00268 if (filespec) { /* If the file name path was found in the header */ 00269 if (fwrite(buf, 1, marker, fout) != marker) { 00270 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno)); 00271 } 00272 x = (int)(path_end+1 - filespec); 00273 if (fwrite(filespec, 1, x, fout) != x) { 00274 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno)); 00275 } 00276 x = (int)(path_end+1 - buf); 00277 memmove(buf, &(buf[x]), char_in_buf-x); 00278 char_in_buf -= x; 00279 } 00280 find_filename = 0; 00281 } else { /* I am looking for the boundary marker */ 00282 marker = find_sequence(buf, char_in_buf, boundary, boundary_len); 00283 if (0 > marker) { 00284 if (char_in_buf < (boundary_len)) { 00285 /*no possibility to find the boundary, write all you have */ 00286 if (fwrite(buf, 1, char_in_buf, fout) != char_in_buf) { 00287 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno)); 00288 } 00289 char_in_buf = 0; 00290 } else { 00291 /* write all except for area where the boundary marker could be */ 00292 if (fwrite(buf, 1, char_in_buf -(boundary_len -1), fout) != char_in_buf - (boundary_len - 1)) { 00293 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno)); 00294 } 00295 x = char_in_buf -(boundary_len -1); 00296 memmove(buf, &(buf[x]), char_in_buf-x); 00297 char_in_buf = (boundary_len -1); 00298 } 00299 } else { 00300 /* write up through the boundary, then look for filename in the rest */ 00301 if (fwrite(buf, 1, marker + boundary_len, fout) != marker + boundary_len) { 00302 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno)); 00303 } 00304 x = marker + boundary_len; 00305 memmove(buf, &(buf[x]), char_in_buf-x); 00306 char_in_buf -= marker + boundary_len; 00307 find_filename =1; 00308 } 00309 } 00310 } 00311 return 0; 00312 }
static int reload | ( | void | ) | [static] |
Definition at line 490 of file res_http_post.c.
References __ast_http_post_load(), and AST_MODULE_LOAD_SUCCESS.
00491 { 00492 __ast_http_post_load(1); 00493 00494 return AST_MODULE_LOAD_SUCCESS; 00495 }
static int unload_module | ( | void | ) | [static] |
Definition at line 483 of file res_http_post.c.
References ast_http_uri_unlink_all_with_key().
00484 { 00485 ast_http_uri_unlink_all_with_key(__FILE__); 00486 00487 return 0; 00488 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "HTTP POST support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, } [static] |
Definition at line 510 of file res_http_post.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 510 of file res_http_post.c.
char prefix[MAX_PREFIX] [static] |
Definition at line 68 of file res_http_post.c.