00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $")
00037
00038 #include <sys/stat.h>
00039 #include <fcntl.h>
00040 #include <gmime/gmime.h>
00041 #if defined (__OpenBSD__) || defined(__FreeBSD__) || defined(__Darwin__)
00042 #include <libgen.h>
00043 #endif
00044
00045 #include "asterisk/linkedlists.h"
00046 #include "asterisk/http.h"
00047 #include "asterisk/paths.h"
00048 #include "asterisk/tcptls.h"
00049 #include "asterisk/manager.h"
00050 #include "asterisk/cli.h"
00051 #include "asterisk/module.h"
00052 #include "asterisk/ast_version.h"
00053
00054 #define MAX_PREFIX 80
00055
00056
00057 #ifdef GMIME_TYPE_CONTENT_TYPE
00058 #define AST_GMIME_VER_24
00059 #endif
00060
00061
00062 struct mime_cbinfo {
00063 int count;
00064 const char *post_dir;
00065 };
00066
00067
00068 static char prefix[MAX_PREFIX];
00069
00070 static void post_raw(GMimePart *part, const char *post_dir, const char *fn)
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 }
00098
00099 static GMimeMessage *parse_message(FILE *f)
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 }
00118
00119 #ifdef AST_GMIME_VER_24
00120 static void process_message_callback(GMimeObject *parent, GMimeObject *part, gpointer user_data)
00121 #else
00122 static void process_message_callback(GMimeObject *part, gpointer user_data)
00123 #endif
00124 {
00125 struct mime_cbinfo *cbinfo = user_data;
00126
00127 cbinfo->count++;
00128
00129
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 }
00162
00163 static int process_message(GMimeMessage *message, const char *post_dir)
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 }
00178
00179
00180 static int find_sequence(char * inbuf, int inlen, char * matchbuf, int matchlen)
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 }
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216 static int readmimefile(FILE * fin, FILE * fout, char * boundary, int contentlen)
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
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
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;
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) {
00258 *path_end = '/';
00259 }
00260 if ('\"' == *path_end) {
00261 *path_end = '\0';
00262 filespec = basename(path_start);
00263 *path_end = '\"';
00264 break;
00265 }
00266 }
00267 }
00268 if (filespec) {
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 {
00282 marker = find_sequence(buf, char_in_buf, boundary, boundary_len);
00283 if (0 > marker) {
00284 if (char_in_buf < (boundary_len)) {
00285
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
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
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 }
00313
00314 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)
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);
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 }
00425
00426 static int __ast_http_post_load(int 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 }
00482
00483 static int unload_module(void)
00484 {
00485 ast_http_uri_unlink_all_with_key(__FILE__);
00486
00487 return 0;
00488 }
00489
00490 static int reload(void)
00491 {
00492 __ast_http_post_load(1);
00493
00494 return AST_MODULE_LOAD_SUCCESS;
00495 }
00496
00497 static int load_module(void)
00498 {
00499 g_mime_init(0);
00500
00501 __ast_http_post_load(0);
00502
00503 return AST_MODULE_LOAD_SUCCESS;
00504 }
00505
00506 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "HTTP POST support",
00507 .load = load_module,
00508 .unload = unload_module,
00509 .reload = reload,
00510 );