Mon Jun 27 16:51:19 2011

Asterisk developer's documentation


res_http_post.c File Reference

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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, }
static struct ast_module_infoast_module_info = &__mod_info
static char prefix [MAX_PREFIX]


Detailed Description

HTTP POST upload support for Asterisk HTTP server.

Author:
Terry Wilson <twilson@digium.com
AMI over HTTP support - AMI over the http protocol

Definition in file res_http_post.c.


Define Documentation

#define MAX_PREFIX   80

Definition at line 53 of file res_http_post.c.


Function Documentation

static int __ast_http_post_load ( int  reload  )  [static]

Definition at line 406 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(), CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, http_post_callback(), ast_variable::name, ast_variable::next, and ast_variable::value.

Referenced by load_module(), and reload().

00407 {
00408    struct ast_config *cfg;
00409    struct ast_variable *v;
00410    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00411 
00412    cfg = ast_config_load2("http.conf", "http", config_flags);
00413    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
00414       return 0;
00415    }
00416 
00417    if (reload) {
00418       ast_http_uri_unlink_all_with_key(__FILE__);
00419    }
00420 
00421    if (cfg) {
00422       for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
00423          if (!strcasecmp(v->name, "prefix")) {
00424             ast_copy_string(prefix, v->value, sizeof(prefix));
00425             if (prefix[strlen(prefix)] == '/') {
00426                prefix[strlen(prefix)] = '\0';
00427             }
00428          }
00429       }
00430 
00431       for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next) {
00432          struct ast_http_uri *urih;
00433          struct ast_str *ds;
00434 
00435          if (!(urih = ast_calloc(sizeof(*urih), 1))) {
00436             ast_config_destroy(cfg);
00437             return -1;
00438          }
00439 
00440          if (!(ds = ast_str_create(32))) {
00441             ast_free(urih);
00442             ast_config_destroy(cfg);
00443             return -1;
00444          }
00445 
00446          urih->description = ast_strdup("HTTP POST mapping");
00447          urih->uri = ast_strdup(v->name);
00448          ast_str_set(&ds, 0, "%s", v->value);
00449          urih->data = ds;
00450          urih->has_subtree = 0;
00451          urih->callback = http_post_callback;
00452          urih->key = __FILE__;
00453          urih->mallocd = urih->dmallocd = 1;
00454 
00455          ast_http_uri_link(urih);
00456       }
00457 
00458       ast_config_destroy(cfg);
00459    }
00460    return 0;
00461 }

static void __reg_module ( void   )  [static]

Definition at line 490 of file res_http_post.c.

static void __unreg_module ( void   )  [static]

Definition at line 490 of file res_http_post.c.

static int find_sequence ( char *  inbuf,
int  inlen,
char *  matchbuf,
int  matchlen 
) [static]

Definition at line 160 of file res_http_post.c.

Referenced by readmimefile().

00161 {
00162    int current;
00163    int comp;
00164    int found = 0;
00165 
00166    for (current = 0; current < inlen-matchlen; current++, inbuf++) {
00167       if (*inbuf == *matchbuf) {
00168          found=1;
00169          for (comp = 1; comp < matchlen; comp++) {
00170             if (inbuf[comp] != matchbuf[comp]) {
00171                found = 0;
00172                break;
00173             }
00174          }
00175          if (found) {
00176             break;
00177          }
00178       }
00179    }
00180    if (found) {
00181       return current;
00182    } else {
00183       return -1;
00184    }
00185 }

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 294 of file res_http_post.c.

References ast_http_error(), ast_http_get_cookies(), ast_http_manid_from_vars(), AST_HTTP_POST, astman_is_authed(), f, and var.

Referenced by __ast_http_post_load().

00295 {
00296    struct ast_variable *var, *cookies;
00297    unsigned long ident = 0;
00298    FILE *f;
00299    int content_len = 0;
00300    struct ast_str *post_dir;
00301    GMimeMessage *message;
00302    int message_count = 0;
00303    char * boundary_marker = NULL;
00304 
00305    if (method != AST_HTTP_POST) {
00306       ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
00307       return -1;
00308    }
00309 
00310    if (!astman_is_authed(ast_http_manid_from_vars(headers))) {
00311       ast_http_error(ser, 403, "Access Denied", "Sorry, I cannot let you do that, Dave.");
00312       return -1;
00313    }
00314 
00315    if (!urih) {
00316       ast_http_error(ser, 400, "Missing URI handle", "There was an error parsing the request");
00317            return -1;
00318    }
00319 
00320    cookies = ast_http_get_cookies(headers);
00321    for (var = cookies; var; var = var->next) {
00322       if (!strcasecmp(var->name, "mansession_id")) {
00323          sscanf(var->value, "%30lx", &ident);
00324          break;
00325       }
00326    }
00327    if (cookies) {
00328       ast_variables_destroy(cookies);
00329    }
00330 
00331    if (ident == 0) {
00332       ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
00333       return -1;
00334    }
00335    if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
00336       ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
00337       return -1;
00338    }
00339 
00340    if (!(f = tmpfile())) {
00341       ast_log(LOG_ERROR, "Could not create temp file.\n");
00342       ast_http_error(ser, 500, "Internal server error", "Could not create temp file.");
00343       return -1;
00344    }
00345 
00346    for (var = headers; var; var = var->next) {
00347       fprintf(f, "%s: %s\r\n", var->name, var->value);
00348 
00349       if (!strcasecmp(var->name, "Content-Length")) {
00350          if ((sscanf(var->value, "%30u", &content_len)) != 1) {
00351             ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
00352             fclose(f);
00353             ast_http_error(ser, 500, "Internal server error", "Invalid Content-Length in POST request!");
00354             return -1;
00355          }
00356          ast_debug(1, "Got a Content-Length of %d\n", content_len);
00357       } else if (!strcasecmp(var->name, "Content-Type")) {
00358          boundary_marker = strstr(var->value, "boundary=");
00359          if (boundary_marker) {
00360             boundary_marker += strlen("boundary=");
00361          }
00362       }
00363    }
00364 
00365    fprintf(f, "\r\n");
00366 
00367    if (0 > readmimefile(ser->f, f, boundary_marker, content_len)) {
00368       if (option_debug) {
00369          ast_log(LOG_DEBUG, "Cannot find boundary marker in POST request.\n");
00370       }
00371       fclose(f);
00372 
00373       return -1;
00374    }
00375 
00376    if (fseek(f, SEEK_SET, 0)) {
00377       ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
00378       fclose(f);
00379       ast_http_error(ser, 500, "Internal server error", "Failed to seek temp file back to beginning.");
00380       return -1;
00381    }
00382 
00383    post_dir = urih->data;
00384 
00385    message = parse_message(f); /* Takes ownership and will close f */
00386 
00387    if (!message) {
00388       ast_log(LOG_ERROR, "Error parsing MIME data\n");
00389 
00390       ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
00391       return -1;
00392    }
00393 
00394    if (!(message_count = process_message(message, ast_str_buffer(post_dir)))) {
00395       ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
00396       g_object_unref(message);
00397       ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
00398       return -1;
00399    }
00400    g_object_unref(message);
00401 
00402    ast_http_error(ser, 200, "OK", "File successfully uploaded.");
00403    return 0;
00404 }

static int load_module ( void   )  [static]

Definition at line 477 of file res_http_post.c.

References __ast_http_post_load(), and AST_MODULE_LOAD_SUCCESS.

00478 {
00479    g_mime_init(0);
00480 
00481    __ast_http_post_load(0);
00482 
00483    return AST_MODULE_LOAD_SUCCESS;
00484 }

static GMimeMessage* parse_message ( FILE *  f  )  [static]

Definition at line 91 of file res_http_post.c.

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().

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 196 of file res_http_post.c.

References ast_log(), errno, find_sequence(), fwrite, and LOG_WARNING.

00197 {
00198    int find_filename = 0;
00199    char buf[4096];
00200    int marker;
00201    int x;
00202    int char_in_buf = 0;
00203    int num_to_read;
00204    int boundary_len;
00205    char * path_end, * path_start, * filespec;
00206 
00207    if (NULL == fin || NULL == fout || NULL == boundary || 0 >= contentlen) {
00208       return -1;
00209    }
00210 
00211    boundary_len = strlen(boundary);
00212    while (0 < contentlen || 0 < char_in_buf) {
00213       /* determine how much I will read into the buffer */
00214       if (contentlen > sizeof(buf) - char_in_buf) {
00215          num_to_read = sizeof(buf)- char_in_buf;
00216       } else {
00217          num_to_read = contentlen;
00218       }
00219 
00220       if (0 < num_to_read) {
00221          if (fread(&(buf[char_in_buf]), 1, num_to_read, fin) < num_to_read) {
00222             ast_log(LOG_WARNING, "fread() failed: %s\n", strerror(errno));
00223             num_to_read = 0;
00224          }
00225          contentlen -= num_to_read;
00226          char_in_buf += num_to_read;
00227       }
00228       /* If I am looking for the filename spec */
00229       if (find_filename) {
00230          path_end = filespec = NULL;
00231          x = strlen("filename=\"");
00232          marker = find_sequence(buf, char_in_buf, "filename=\"", x );
00233          if (0 <= marker) {
00234             marker += x;  /* Index beyond the filename marker */
00235             path_start = &buf[marker];
00236             for (path_end = path_start, x = 0; x < char_in_buf-marker; x++, path_end++) {
00237                if ('\\' == *path_end) {   /* convert backslashses to forward slashes */
00238                   *path_end = '/';
00239                }
00240                if ('\"' == *path_end) {   /* If at the end of the file name spec */
00241                   *path_end = '\0';    /* temporarily null terminate the file spec for basename */
00242                   filespec = basename(path_start);
00243                   *path_end = '\"';
00244                   break;
00245                }
00246             }
00247          }
00248          if (filespec) {   /* If the file name path was found in the header */
00249             if (fwrite(buf, 1, marker, fout) != marker) {
00250                ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00251             }
00252             x = (int)(path_end+1 - filespec);
00253             if (fwrite(filespec, 1, x, fout) != x) {
00254                ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00255             }
00256             x = (int)(path_end+1 - buf);
00257             memmove(buf, &(buf[x]), char_in_buf-x);
00258             char_in_buf -= x;
00259          }
00260          find_filename = 0;
00261       } else { /* I am looking for the boundary marker */
00262          marker = find_sequence(buf, char_in_buf, boundary, boundary_len);
00263          if (0 > marker) {
00264             if (char_in_buf < (boundary_len)) {
00265                /*no possibility to find the boundary, write all you have */
00266                if (fwrite(buf, 1, char_in_buf, fout) != char_in_buf) {
00267                   ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00268                }
00269                char_in_buf = 0;
00270             } else {
00271                /* write all except for area where the boundary marker could be */
00272                if (fwrite(buf, 1, char_in_buf -(boundary_len -1), fout) != char_in_buf - (boundary_len - 1)) {
00273                   ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00274                }
00275                x = char_in_buf -(boundary_len -1);
00276                memmove(buf, &(buf[x]), char_in_buf-x);
00277                char_in_buf = (boundary_len -1);
00278             }
00279          } else {
00280             /* write up through the boundary, then look for filename in the rest */
00281             if (fwrite(buf, 1, marker + boundary_len, fout) != marker + boundary_len) {
00282                ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00283             }
00284             x = marker + boundary_len;
00285             memmove(buf, &(buf[x]), char_in_buf-x);
00286             char_in_buf -= marker + boundary_len;
00287             find_filename =1;
00288          }
00289       }
00290    }
00291    return 0;
00292 }

static int reload ( void   )  [static]

Definition at line 470 of file res_http_post.c.

References __ast_http_post_load(), and AST_MODULE_LOAD_SUCCESS.

00471 {
00472    __ast_http_post_load(1);
00473 
00474    return AST_MODULE_LOAD_SUCCESS;
00475 }

static int unload_module ( void   )  [static]

Definition at line 463 of file res_http_post.c.

References ast_http_uri_unlink_all_with_key().

00464 {
00465    ast_http_uri_unlink_all_with_key(__FILE__);
00466 
00467    return 0;
00468 }


Variable Documentation

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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 490 of file res_http_post.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 490 of file res_http_post.c.

char prefix[MAX_PREFIX] [static]

Definition at line 62 of file res_http_post.c.


Generated on Mon Jun 27 16:51:19 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7