Fri Nov 12 12:08:14 2010

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"
Include dependency graph for res_http_post.c:

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_strhttp_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_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 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 }


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

Definition at line 494 of file res_http_post.c.

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.


Generated by  doxygen 1.6.2