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: 362307 $")
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 char * boundary_marker = NULL;
00323
00324 if (method != AST_HTTP_POST) {
00325 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
00326 return -1;
00327 }
00328
00329 if (!astman_is_authed(ast_http_manid_from_vars(headers))) {
00330 ast_http_error(ser, 403, "Access Denied", "Sorry, I cannot let you do that, Dave.");
00331 return -1;
00332 }
00333
00334 if (!urih) {
00335 ast_http_error(ser, 400, "Missing URI handle", "There was an error parsing the request");
00336 return -1;
00337 }
00338
00339 cookies = ast_http_get_cookies(headers);
00340 for (var = cookies; var; var = var->next) {
00341 if (!strcasecmp(var->name, "mansession_id")) {
00342 sscanf(var->value, "%30lx", &ident);
00343 break;
00344 }
00345 }
00346 if (cookies) {
00347 ast_variables_destroy(cookies);
00348 }
00349
00350 if (ident == 0) {
00351 ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
00352 return -1;
00353 }
00354 if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
00355 ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
00356 return -1;
00357 }
00358
00359 if (!(f = tmpfile())) {
00360 ast_log(LOG_ERROR, "Could not create temp file.\n");
00361 ast_http_error(ser, 500, "Internal server error", "Could not create temp file.");
00362 return -1;
00363 }
00364
00365 for (var = headers; var; var = var->next) {
00366 fprintf(f, "%s: %s\r\n", var->name, var->value);
00367
00368 if (!strcasecmp(var->name, "Content-Length")) {
00369 if ((sscanf(var->value, "%30u", &content_len)) != 1) {
00370 ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
00371 fclose(f);
00372 ast_http_error(ser, 500, "Internal server error", "Invalid Content-Length in POST request!");
00373 return -1;
00374 }
00375 ast_debug(1, "Got a Content-Length of %d\n", content_len);
00376 } else if (!strcasecmp(var->name, "Content-Type")) {
00377 boundary_marker = strstr(var->value, "boundary=");
00378 if (boundary_marker) {
00379 boundary_marker += strlen("boundary=");
00380 }
00381 }
00382 }
00383
00384 fprintf(f, "\r\n");
00385
00386 if (0 > readmimefile(ser->f, f, boundary_marker, content_len)) {
00387 ast_debug(1, "Cannot find boundary marker in POST request.\n");
00388 fclose(f);
00389
00390 return -1;
00391 }
00392
00393 if (fseek(f, SEEK_SET, 0)) {
00394 ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
00395 fclose(f);
00396 ast_http_error(ser, 500, "Internal server error", "Failed to seek temp file back to beginning.");
00397 return -1;
00398 }
00399
00400 post_dir = urih->data;
00401
00402 message = parse_message(f);
00403
00404 if (!message) {
00405 ast_log(LOG_ERROR, "Error parsing MIME data\n");
00406
00407 ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
00408 return -1;
00409 }
00410
00411 if (!process_message(message, ast_str_buffer(post_dir))) {
00412 ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
00413 g_object_unref(message);
00414 ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
00415 return -1;
00416 }
00417 g_object_unref(message);
00418
00419 ast_http_error(ser, 200, "OK", "File successfully uploaded.");
00420 return 0;
00421 }
00422
00423 static int __ast_http_post_load(int reload)
00424 {
00425 struct ast_config *cfg;
00426 struct ast_variable *v;
00427 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00428
00429 cfg = ast_config_load2("http.conf", "http", config_flags);
00430 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
00431 return 0;
00432 }
00433
00434 if (reload) {
00435 ast_http_uri_unlink_all_with_key(__FILE__);
00436 }
00437
00438 if (cfg) {
00439 for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
00440 if (!strcasecmp(v->name, "prefix")) {
00441 ast_copy_string(prefix, v->value, sizeof(prefix));
00442 if (prefix[strlen(prefix)] == '/') {
00443 prefix[strlen(prefix)] = '\0';
00444 }
00445 }
00446 }
00447
00448 for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next) {
00449 struct ast_http_uri *urih;
00450 struct ast_str *ds;
00451
00452 if (!(urih = ast_calloc(sizeof(*urih), 1))) {
00453 ast_config_destroy(cfg);
00454 return -1;
00455 }
00456
00457 if (!(ds = ast_str_create(32))) {
00458 ast_free(urih);
00459 ast_config_destroy(cfg);
00460 return -1;
00461 }
00462
00463 urih->description = ast_strdup("HTTP POST mapping");
00464 urih->uri = ast_strdup(v->name);
00465 ast_str_set(&ds, 0, "%s", v->value);
00466 urih->data = ds;
00467 urih->has_subtree = 0;
00468 urih->callback = http_post_callback;
00469 urih->key = __FILE__;
00470 urih->mallocd = urih->dmallocd = 1;
00471
00472 ast_http_uri_link(urih);
00473 }
00474
00475 ast_config_destroy(cfg);
00476 }
00477 return 0;
00478 }
00479
00480 static int unload_module(void)
00481 {
00482 ast_http_uri_unlink_all_with_key(__FILE__);
00483
00484 return 0;
00485 }
00486
00487 static int reload(void)
00488 {
00489 __ast_http_post_load(1);
00490
00491 return AST_MODULE_LOAD_SUCCESS;
00492 }
00493
00494 static int load_module(void)
00495 {
00496 g_mime_init(0);
00497
00498 __ast_http_post_load(0);
00499
00500 return AST_MODULE_LOAD_SUCCESS;
00501 }
00502
00503 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "HTTP POST support",
00504 .load = load_module,
00505 .unload = unload_module,
00506 .reload = reload,
00507 );