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 | __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 = "0901e4e500243c855563a2d78b0c03e4" , .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().
00409 { 00410 struct ast_config *cfg; 00411 struct ast_variable *v; 00412 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 00413 00414 cfg = ast_config_load2("http.conf", "http", config_flags); 00415 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) { 00416 return 0; 00417 } 00418 00419 if (reload) { 00420 ast_http_uri_unlink_all_with_key(__FILE__); 00421 } 00422 00423 if (cfg) { 00424 for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { 00425 if (!strcasecmp(v->name, "prefix")) { 00426 ast_copy_string(prefix, v->value, sizeof(prefix)); 00427 if (prefix[strlen(prefix)] == '/') { 00428 prefix[strlen(prefix)] = '\0'; 00429 } 00430 } 00431 } 00432 00433 for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next) { 00434 struct ast_http_uri *urih; 00435 struct ast_str *ds; 00436 00437 if (!(urih = ast_calloc(sizeof(*urih), 1))) { 00438 ast_config_destroy(cfg); 00439 return -1; 00440 } 00441 00442 if (!(ds = ast_str_create(32))) { 00443 ast_free(urih); 00444 ast_config_destroy(cfg); 00445 return -1; 00446 } 00447 00448 urih->description = ast_strdup("HTTP POST mapping"); 00449 urih->uri = ast_strdup(v->name); 00450 ast_str_set(&ds, 0, "%s", v->value); 00451 urih->data = ds; 00452 urih->has_subtree = 0; 00453 urih->supports_get = 0; 00454 urih->supports_post = 1; 00455 urih->callback = http_post_callback; 00456 urih->key = __FILE__; 00457 urih->mallocd = urih->dmallocd = 1; 00458 00459 ast_http_uri_link(urih); 00460 } 00461 00462 ast_config_destroy(cfg); 00463 } 00464 return 0; 00465 }
| 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().
00162 { 00163 int current; 00164 int comp; 00165 int found = 0; 00166 00167 for (current = 0; current < inlen-matchlen; current++, inbuf++) { 00168 if (*inbuf == *matchbuf) { 00169 found=1; 00170 for (comp = 1; comp < matchlen; comp++) { 00171 if (inbuf[comp] != matchbuf[comp]) { 00172 found = 0; 00173 break; 00174 } 00175 } 00176 if (found) { 00177 break; 00178 } 00179 } 00180 } 00181 if (found) { 00182 return current; 00183 } else { 00184 return -1; 00185 } 00186 }
| 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().
00297 { 00298 struct ast_variable *var; 00299 unsigned long ident = 0; 00300 FILE *f; 00301 int content_len = 0; 00302 struct ast_str *post_dir; 00303 GMimeMessage *message; 00304 int message_count = 0; 00305 char * boundary_marker = NULL; 00306 00307 if (!urih) { 00308 return ast_http_error((*status = 400), 00309 (*title = ast_strdup("Missing URI handle")), 00310 NULL, "There was an error parsing the request"); 00311 } 00312 00313 for (var = vars; var; var = var->next) { 00314 if (strcasecmp(var->name, "mansession_id")) { 00315 continue; 00316 } 00317 00318 if (sscanf(var->value, "%30lx", &ident) != 1) { 00319 return ast_http_error((*status = 400), 00320 (*title = ast_strdup("Bad Request")), 00321 NULL, "The was an error parsing the request."); 00322 } 00323 00324 if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) { 00325 return ast_http_error((*status = 401), 00326 (*title = ast_strdup("Unauthorized")), 00327 NULL, "You are not authorized to make this request."); 00328 } 00329 00330 break; 00331 } 00332 00333 if (!var) { 00334 return ast_http_error((*status = 401), 00335 (*title = ast_strdup("Unauthorized")), 00336 NULL, "You are not authorized to make this request."); 00337 } 00338 00339 if (!(f = tmpfile())) { 00340 ast_log(LOG_ERROR, "Could not create temp file.\n"); 00341 return NULL; 00342 } 00343 00344 for (var = headers; var; var = var->next) { 00345 fprintf(f, "%s: %s\r\n", var->name, var->value); 00346 00347 if (!strcasecmp(var->name, "Content-Length")) { 00348 if ((sscanf(var->value, "%30u", &content_len)) != 1) { 00349 ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n"); 00350 fclose(f); 00351 00352 return NULL; 00353 } 00354 ast_debug(1, "Got a Content-Length of %d\n", content_len); 00355 } else if (!strcasecmp(var->name, "Content-Type")) { 00356 boundary_marker = strstr(var->value, "boundary="); 00357 if (boundary_marker) { 00358 boundary_marker += strlen("boundary="); 00359 } 00360 } 00361 } 00362 00363 fprintf(f, "\r\n"); 00364 00365 if (0 > readmimefile(ser->f, f, boundary_marker, content_len)) { 00366 if (option_debug) { 00367 ast_log(LOG_DEBUG, "Cannot find boundary marker in POST request.\n"); 00368 } 00369 fclose(f); 00370 00371 return NULL; 00372 } 00373 00374 if (fseek(f, SEEK_SET, 0)) { 00375 ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n"); 00376 fclose(f); 00377 00378 return NULL; 00379 } 00380 00381 post_dir = urih->data; 00382 00383 message = parse_message(f); /* Takes ownership and will close f */ 00384 00385 if (!message) { 00386 ast_log(LOG_ERROR, "Error parsing MIME data\n"); 00387 00388 return ast_http_error((*status = 400), 00389 (*title = ast_strdup("Bad Request")), 00390 NULL, "The was an error parsing the request."); 00391 } 00392 00393 if (!(message_count = process_message(message, ast_str_buffer(post_dir)))) { 00394 ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n"); 00395 g_object_unref(message); 00396 return ast_http_error((*status = 400), 00397 (*title = ast_strdup("Bad Request")), 00398 NULL, "The was an error parsing the request."); 00399 } 00400 00401 g_object_unref(message); 00402 00403 return ast_http_error((*status = 200), 00404 (*title = ast_strdup("OK")), 00405 NULL, "File successfully uploaded."); 00406 }
| 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.
00482 { 00483 g_mime_init(0); 00484 00485 __ast_http_post_load(0); 00486 00487 return AST_MODULE_LOAD_SUCCESS; 00488 }
| static GMimeMessage* parse_message | ( | FILE * | f | ) | [static] |
Definition at line 91 of file res_http_post.c.
Referenced by http_post_callback().
00092 { 00093 GMimeMessage *message; 00094 GMimeParser *parser; 00095 GMimeStream *stream; 00096 00097 stream = g_mime_stream_file_new(f); 00098 00099 parser = g_mime_parser_new_with_stream(stream); 00100 g_mime_parser_set_respect_content_length(parser, 1); 00101 00102 g_object_unref(stream); 00103 00104 message = g_mime_parser_construct_message(parser); 00105 00106 g_object_unref(parser); 00107 00108 return message; 00109 }
| 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().
00065 { 00066 char filename[PATH_MAX]; 00067 GMimeDataWrapper *content; 00068 GMimeStream *stream; 00069 int fd; 00070 00071 snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn); 00072 00073 ast_debug(1, "Posting raw data to %s\n", filename); 00074 00075 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666)) == -1) { 00076 ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename); 00077 00078 return; 00079 } 00080 00081 stream = g_mime_stream_fs_new(fd); 00082 00083 content = g_mime_part_get_content_object(part); 00084 g_mime_data_wrapper_write_to_stream(content, stream); 00085 g_mime_stream_flush(stream); 00086 00087 g_object_unref(content); 00088 g_object_unref(stream); 00089 }
| 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, and process_message_callback().
Referenced by http_post_callback().
00148 { 00149 struct mime_cbinfo cbinfo = { 00150 .count = 0, 00151 .post_dir = post_dir, 00152 }; 00153 00154 g_mime_message_foreach_part(message, process_message_callback, &cbinfo); 00155 00156 return cbinfo.count; 00157 }
| 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().
00112 { 00113 struct mime_cbinfo *cbinfo = user_data; 00114 00115 cbinfo->count++; 00116 00117 /* We strip off the headers before we get here, so should only see GMIME_IS_PART */ 00118 if (GMIME_IS_MESSAGE_PART(part)) { 00119 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PART\n"); 00120 return; 00121 } else if (GMIME_IS_MESSAGE_PARTIAL(part)) { 00122 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PARTIAL\n"); 00123 return; 00124 } else if (GMIME_IS_MULTIPART(part)) { 00125 GList *l; 00126 00127 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MULTIPART, trying to process subparts\n"); 00128 l = GMIME_MULTIPART(part)->subparts; 00129 while (l) { 00130 process_message_callback(l->data, cbinfo); 00131 l = l->next; 00132 } 00133 } else if (GMIME_IS_PART(part)) { 00134 const char *filename; 00135 00136 if (ast_strlen_zero(filename = g_mime_part_get_filename(GMIME_PART(part)))) { 00137 ast_debug(1, "Skipping part with no filename\n"); 00138 return; 00139 } 00140 00141 post_raw(GMIME_PART(part), cbinfo->post_dir, filename); 00142 } else { 00143 ast_log(LOG_ERROR, "Encountered unknown MIME part. This should never happen!\n"); 00144 } 00145 }
| 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().
00198 { 00199 int find_filename = 0; 00200 char buf[4096]; 00201 int marker; 00202 int x; 00203 int char_in_buf = 0; 00204 int num_to_read; 00205 int boundary_len; 00206 char * path_end, * path_start, * filespec; 00207 00208 if (NULL == fin || NULL == fout || NULL == boundary || 0 >= contentlen) { 00209 return -1; 00210 } 00211 00212 boundary_len = strlen(boundary); 00213 while (0 < contentlen || 0 < char_in_buf) { 00214 /* determine how much I will read into the buffer */ 00215 if (contentlen > sizeof(buf) - char_in_buf) { 00216 num_to_read = sizeof(buf)- char_in_buf; 00217 } else { 00218 num_to_read = contentlen; 00219 } 00220 00221 if (0 < num_to_read) { 00222 if (fread(&(buf[char_in_buf]), 1, num_to_read, fin) < num_to_read) { 00223 ast_log(LOG_WARNING, "fread() failed: %s\n", strerror(errno)); 00224 num_to_read = 0; 00225 } 00226 contentlen -= num_to_read; 00227 char_in_buf += num_to_read; 00228 } 00229 /* If I am looking for the filename spec */ 00230 if (find_filename) { 00231 path_end = filespec = NULL; 00232 x = strlen("filename=\""); 00233 marker = find_sequence(buf, char_in_buf, "filename=\"", x ); 00234 if (0 <= marker) { 00235 marker += x; /* Index beyond the filename marker */ 00236 path_start = &buf[marker]; 00237 for (path_end = path_start, x = 0; x < char_in_buf-marker; x++, path_end++) { 00238 if ('\\' == *path_end) { /* convert backslashses to forward slashes */ 00239 *path_end = '/'; 00240 } 00241 if ('\"' == *path_end) { /* If at the end of the file name spec */ 00242 *path_end = '\0'; /* temporarily null terminate the file spec for basename */ 00243 filespec = basename(path_start); 00244 *path_end = '\"'; 00245 break; 00246 } 00247 } 00248 } 00249 if (filespec) { /* If the file name path was found in the header */ 00250 if (fwrite(buf, 1, marker, fout) != marker) { 00251 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno)); 00252 } 00253 x = (int)(path_end+1 - filespec); 00254 if (fwrite(filespec, 1, x, fout) != x) { 00255 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno)); 00256 } 00257 x = (int)(path_end+1 - buf); 00258 memmove(buf, &(buf[x]), char_in_buf-x); 00259 char_in_buf -= x; 00260 } 00261 find_filename = 0; 00262 } else { /* I am looking for the boundary marker */ 00263 marker = find_sequence(buf, char_in_buf, boundary, boundary_len); 00264 if (0 > marker) { 00265 if (char_in_buf < (boundary_len)) { 00266 /*no possibility to find the boundary, write all you have */ 00267 if (fwrite(buf, 1, char_in_buf, fout) != char_in_buf) { 00268 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno)); 00269 } 00270 char_in_buf = 0; 00271 } else { 00272 /* write all except for area where the boundary marker could be */ 00273 if (fwrite(buf, 1, char_in_buf -(boundary_len -1), fout) != char_in_buf - (boundary_len - 1)) { 00274 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno)); 00275 } 00276 x = char_in_buf -(boundary_len -1); 00277 memmove(buf, &(buf[x]), char_in_buf-x); 00278 char_in_buf = (boundary_len -1); 00279 } 00280 } else { 00281 /* write up through the boundary, then look for filename in the rest */ 00282 if (fwrite(buf, 1, marker + boundary_len, fout) != marker + boundary_len) { 00283 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno)); 00284 } 00285 x = marker + boundary_len; 00286 memmove(buf, &(buf[x]), char_in_buf-x); 00287 char_in_buf -= marker + boundary_len; 00288 find_filename =1; 00289 } 00290 } 00291 } 00292 return 0; 00293 }
| 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.
00475 { 00476 __ast_http_post_load(1); 00477 00478 return AST_MODULE_LOAD_SUCCESS; 00479 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 467 of file res_http_post.c.
References ast_http_uri_unlink_all_with_key().
00468 { 00469 ast_http_uri_unlink_all_with_key(__FILE__); 00470 00471 return 0; 00472 }
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 = "0901e4e500243c855563a2d78b0c03e4" , .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.
1.6.2