Wed Oct 28 11:51:07 2009

Asterisk developer's documentation


res_http_post.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file 
00021  * \brief HTTP POST upload support for Asterisk HTTP server
00022  *
00023  * \author Terry Wilson <twilson@digium.com
00024  *
00025  * \ref AstHTTP - AMI over the http protocol
00026  */
00027 
00028 /*** MODULEINFO
00029    <depend>gmime</depend>
00030  ***/
00031 
00032 
00033 #include "asterisk.h"
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 226100 $")
00036 
00037 #include <sys/stat.h>
00038 #include <fcntl.h>
00039 #include <gmime/gmime.h>
00040 
00041 #include "asterisk/linkedlists.h"
00042 #include "asterisk/http.h"
00043 #include "asterisk/paths.h"   /* use ast_config_AST_DATA_DIR */
00044 #include "asterisk/tcptls.h"
00045 #include "asterisk/manager.h"
00046 #include "asterisk/cli.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/ast_version.h"
00049 
00050 #define MAX_PREFIX 80
00051 
00052 /* just a little structure to hold callback info for gmime */
00053 struct mime_cbinfo {
00054    int count;
00055    const char *post_dir;
00056 };
00057 
00058 /* all valid URIs must be prepended by the string in prefix. */
00059 static char prefix[MAX_PREFIX];
00060 
00061 static void post_raw(GMimePart *part, const char *post_dir, const char *fn)
00062 {
00063    char filename[PATH_MAX];
00064    GMimeDataWrapper *content;
00065    GMimeStream *stream;
00066    int fd;
00067 
00068    snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);
00069 
00070    ast_debug(1, "Posting raw data to %s\n", filename);
00071 
00072    if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666)) == -1) {
00073       ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);
00074 
00075       return;
00076    }
00077 
00078    stream = g_mime_stream_fs_new(fd);
00079 
00080    content = g_mime_part_get_content_object(part);
00081    g_mime_data_wrapper_write_to_stream(content, stream);
00082    g_mime_stream_flush(stream);
00083 
00084    g_object_unref(content);
00085    g_object_unref(stream);
00086 }
00087 
00088 static GMimeMessage *parse_message(FILE *f)
00089 {
00090    GMimeMessage *message;
00091    GMimeParser *parser;
00092    GMimeStream *stream;
00093 
00094    stream = g_mime_stream_file_new(f);
00095 
00096    parser = g_mime_parser_new_with_stream(stream);
00097    g_mime_parser_set_respect_content_length(parser, 1);
00098    
00099    g_object_unref(stream);
00100 
00101    message = g_mime_parser_construct_message(parser);
00102 
00103    g_object_unref(parser);
00104 
00105    return message;
00106 }
00107 
00108 static void process_message_callback(GMimeObject *part, gpointer user_data)
00109 {
00110    struct mime_cbinfo *cbinfo = user_data;
00111 
00112    cbinfo->count++;
00113 
00114    /* We strip off the headers before we get here, so should only see GMIME_IS_PART */
00115    if (GMIME_IS_MESSAGE_PART(part)) {
00116       ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PART\n");
00117       return;
00118    } else if (GMIME_IS_MESSAGE_PARTIAL(part)) {
00119       ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PARTIAL\n");
00120       return;
00121    } else if (GMIME_IS_MULTIPART(part)) {
00122       GList *l;
00123       
00124       ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MULTIPART, trying to process subparts\n");
00125       l = GMIME_MULTIPART(part)->subparts;
00126       while (l) {
00127          process_message_callback(l->data, cbinfo);
00128          l = l->next;
00129       }
00130    } else if (GMIME_IS_PART(part)) {
00131       const char *filename;
00132 
00133       if (ast_strlen_zero(filename = g_mime_part_get_filename(GMIME_PART(part)))) {
00134          ast_debug(1, "Skipping part with no filename\n");
00135          return;
00136       }
00137 
00138       post_raw(GMIME_PART(part), cbinfo->post_dir, filename);
00139    } else {
00140       ast_log(LOG_ERROR, "Encountered unknown MIME part. This should never happen!\n");
00141    }
00142 }
00143 
00144 static int process_message(GMimeMessage *message, const char *post_dir)
00145 {
00146    struct mime_cbinfo cbinfo = {
00147       .count = 0,
00148       .post_dir = post_dir,
00149    };
00150 
00151    g_mime_message_foreach_part(message, process_message_callback, &cbinfo);
00152 
00153    return cbinfo.count;
00154 }
00155 
00156 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)
00157 {
00158    struct ast_variable *var;
00159    unsigned long ident = 0;
00160    char buf[4096];
00161    FILE *f;
00162    size_t res;
00163    int content_len = 0;
00164    struct ast_str *post_dir;
00165    GMimeMessage *message;
00166    int message_count = 0;
00167 
00168    if (!urih) {
00169       return ast_http_error((*status = 400),
00170             (*title = ast_strdup("Missing URI handle")),
00171             NULL, "There was an error parsing the request");
00172    }
00173 
00174    for (var = vars; var; var = var->next) {
00175       if (strcasecmp(var->name, "mansession_id")) {
00176          continue;
00177       }
00178 
00179       if (sscanf(var->value, "%30lx", &ident) != 1) {
00180          return ast_http_error((*status = 400),
00181                      (*title = ast_strdup("Bad Request")),
00182                      NULL, "The was an error parsing the request.");
00183       }
00184 
00185       if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
00186          return ast_http_error((*status = 401),
00187                      (*title = ast_strdup("Unauthorized")),
00188                      NULL, "You are not authorized to make this request.");
00189       }
00190 
00191       break;
00192    }
00193 
00194    if (!var) {
00195       return ast_http_error((*status = 401),
00196                   (*title = ast_strdup("Unauthorized")),
00197                   NULL, "You are not authorized to make this request.");
00198    }
00199 
00200    if (!(f = tmpfile())) {
00201       ast_log(LOG_ERROR, "Could not create temp file.\n");
00202       return NULL;
00203    }
00204 
00205    for (var = headers; var; var = var->next) {
00206       fprintf(f, "%s: %s\r\n", var->name, var->value);
00207 
00208       if (!strcasecmp(var->name, "Content-Length")) {
00209          if ((sscanf(var->value, "%30u", &content_len)) != 1) {
00210             ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
00211             fclose(f);
00212 
00213             return NULL;
00214          }
00215          ast_debug(1, "Got a Content-Length of %d\n", content_len);
00216       }
00217    }
00218 
00219    fprintf(f, "\r\n");
00220 
00221    for (res = sizeof(buf); content_len; content_len -= res) {
00222       if (content_len < res) {
00223          res = content_len;
00224       }
00225       if (fread(buf, 1, res, ser->f) != res) {
00226          ast_log(LOG_WARNING, "fread() failed: %s\n", strerror(errno));
00227          continue;
00228       }
00229       if (fwrite(buf, 1, res, f) != res) {
00230          ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00231          continue;
00232       }
00233    }
00234 
00235    if (fseek(f, SEEK_SET, 0)) {
00236       ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
00237       fclose(f);
00238 
00239       return NULL;
00240    }
00241 
00242    post_dir = urih->data;
00243 
00244    message = parse_message(f); /* Takes ownership and will close f */
00245 
00246    if (!message) {
00247       ast_log(LOG_ERROR, "Error parsing MIME data\n");
00248 
00249       return ast_http_error((*status = 400),
00250                   (*title = ast_strdup("Bad Request")),
00251                   NULL, "The was an error parsing the request.");
00252    }
00253 
00254    if (!(message_count = process_message(message, post_dir->str))) {
00255       ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
00256       g_object_unref(message);
00257       return ast_http_error((*status = 400),
00258                   (*title = ast_strdup("Bad Request")),
00259                   NULL, "The was an error parsing the request.");
00260    }
00261 
00262    g_object_unref(message);
00263 
00264    return ast_http_error((*status = 200),
00265                (*title = ast_strdup("OK")),
00266                NULL, "File successfully uploaded.");
00267 }
00268 
00269 static int __ast_http_post_load(int reload)
00270 {
00271    struct ast_config *cfg;
00272    struct ast_variable *v;
00273    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00274 
00275    if ((cfg = ast_config_load2("http.conf", "http", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
00276       return 0;
00277    }
00278 
00279    if (reload) {
00280       ast_http_uri_unlink_all_with_key(__FILE__);
00281    }
00282 
00283    if (cfg) {
00284       for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
00285          if (!strcasecmp(v->name, "prefix")) {
00286             ast_copy_string(prefix, v->value, sizeof(prefix));
00287             if (prefix[strlen(prefix)] == '/') {
00288                prefix[strlen(prefix)] = '\0';
00289             }
00290          }
00291       }
00292 
00293       for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next) {
00294          struct ast_http_uri *urih;
00295          struct ast_str *ds;
00296 
00297          if (!(urih = ast_calloc(sizeof(*urih), 1))) {
00298             ast_config_destroy(cfg);
00299             return -1;
00300          }
00301 
00302          if (!(ds = ast_str_create(32))) {
00303             ast_free(urih);
00304             ast_config_destroy(cfg);
00305             return -1;
00306          }
00307 
00308          urih->description = ast_strdup("HTTP POST mapping");
00309          urih->uri = ast_strdup(v->name);
00310          ast_str_set(&ds, 0, "%s", v->value);
00311          urih->data = ds;
00312          urih->has_subtree = 0;
00313          urih->supports_get = 0;
00314          urih->supports_post = 1;
00315          urih->callback = http_post_callback;
00316          urih->key = __FILE__;
00317          urih->mallocd = urih->dmallocd = 1;
00318 
00319          ast_http_uri_link(urih);
00320       }
00321 
00322       ast_config_destroy(cfg);
00323    }
00324    return 0;
00325 }
00326 
00327 static int unload_module(void)
00328 {
00329    ast_http_uri_unlink_all_with_key(__FILE__);
00330 
00331    return 0;
00332 }
00333 
00334 static int reload(void)
00335 {
00336    __ast_http_post_load(1);
00337 
00338    return AST_MODULE_LOAD_SUCCESS;
00339 }
00340 
00341 static int load_module(void)
00342 {
00343    g_mime_init(0);
00344 
00345    __ast_http_post_load(0);
00346 
00347    return AST_MODULE_LOAD_SUCCESS;
00348 }
00349 
00350 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "HTTP POST support",
00351    .load = load_module,
00352    .unload = unload_module,
00353    .reload = reload,
00354 );

Generated on Wed Oct 28 11:51:07 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6