Wed Jan 27 20:02:47 2016

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 = "ac1f6a56484a8820659555499174e588" , .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 54 of file res_http_post.c.


Function Documentation

static int __ast_http_post_load ( int  reload  )  [static]

Definition at line 426 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::uri, and ast_variable::value.

Referenced by load_module(), and 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 }

static void __reg_module ( void   )  [static]

Definition at line 510 of file res_http_post.c.

static void __unreg_module ( void   )  [static]

Definition at line 510 of file res_http_post.c.

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

Definition at line 180 of file res_http_post.c.

Referenced by readmimefile().

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 }

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

References ast_debug, ast_http_error(), ast_http_get_cookies(), ast_http_manid_from_vars(), AST_HTTP_POST, ast_log(), ast_str_buffer(), ast_variables_destroy(), astman_is_authed(), 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().

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); /* Takes ownership and will close 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 }

static int load_module ( void   )  [static]

Definition at line 497 of file res_http_post.c.

References __ast_http_post_load(), and AST_MODULE_LOAD_SUCCESS.

00498 {
00499    g_mime_init(0);
00500 
00501    __ast_http_post_load(0);
00502 
00503    return AST_MODULE_LOAD_SUCCESS;
00504 }

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

Definition at line 99 of file res_http_post.c.

Referenced by http_post_callback().

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 }

static void post_raw ( GMimePart *  part,
const char *  post_dir,
const char *  fn 
) [static]

Definition at line 70 of file res_http_post.c.

References ast_debug, ast_log(), and LOG_WARNING.

Referenced by process_message_callback().

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 }

static int process_message ( GMimeMessage *  message,
const char *  post_dir 
) [static]

Definition at line 163 of file res_http_post.c.

References mime_cbinfo::count, and process_message_callback().

Referenced by http_post_callback().

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 }

static void process_message_callback ( GMimeObject *  part,
gpointer  user_data 
) [static]

Definition at line 122 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().

00124 {
00125    struct mime_cbinfo *cbinfo = user_data;
00126 
00127    cbinfo->count++;
00128 
00129    /* We strip off the headers before we get here, so should only see GMIME_IS_PART */
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 }

static int readmimefile ( FILE *  fin,
FILE *  fout,
char *  boundary,
int  contentlen 
) [static]

Definition at line 216 of file res_http_post.c.

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

Referenced by http_post_callback().

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       /* determine how much I will read into the buffer */
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       /* If I am looking for the filename spec */
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;  /* Index beyond the filename marker */
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) {   /* convert backslashses to forward slashes */
00258                   *path_end = '/';
00259                }
00260                if ('\"' == *path_end) {   /* If at the end of the file name spec */
00261                   *path_end = '\0';    /* temporarily null terminate the file spec for basename */
00262                   filespec = basename(path_start);
00263                   *path_end = '\"';
00264                   break;
00265                }
00266             }
00267          }
00268          if (filespec) {   /* If the file name path was found in the header */
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 { /* I am looking for the boundary marker */
00282          marker = find_sequence(buf, char_in_buf, boundary, boundary_len);
00283          if (0 > marker) {
00284             if (char_in_buf < (boundary_len)) {
00285                /*no possibility to find the boundary, write all you have */
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                /* write all except for area where the boundary marker could be */
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             /* write up through the boundary, then look for filename in the rest */
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 }

static int reload ( void   )  [static]

Definition at line 490 of file res_http_post.c.

References __ast_http_post_load(), and AST_MODULE_LOAD_SUCCESS.

00491 {
00492    __ast_http_post_load(1);
00493 
00494    return AST_MODULE_LOAD_SUCCESS;
00495 }

static int unload_module ( void   )  [static]

Definition at line 483 of file res_http_post.c.

References ast_http_uri_unlink_all_with_key().

00484 {
00485    ast_http_uri_unlink_all_with_key(__FILE__);
00486 
00487    return 0;
00488 }


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

Definition at line 510 of file res_http_post.c.

Definition at line 510 of file res_http_post.c.

char prefix[MAX_PREFIX] [static]

Definition at line 68 of file res_http_post.c.


Generated on 27 Jan 2016 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1