Thu Apr 28 2011 17:14:01

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
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "HTTP POST support" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .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().

{
   struct ast_config *cfg;
   struct ast_variable *v;
   struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };

   cfg = ast_config_load2("http.conf", "http", config_flags);
   if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
      return 0;
   }

   if (reload) {
      ast_http_uri_unlink_all_with_key(__FILE__);
   }

   if (cfg) {
      for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
         if (!strcasecmp(v->name, "prefix")) {
            ast_copy_string(prefix, v->value, sizeof(prefix));
            if (prefix[strlen(prefix)] == '/') {
               prefix[strlen(prefix)] = '\0';
            }
         }
      }

      for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next) {
         struct ast_http_uri *urih;
         struct ast_str *ds;

         if (!(urih = ast_calloc(sizeof(*urih), 1))) {
            ast_config_destroy(cfg);
            return -1;
         }

         if (!(ds = ast_str_create(32))) {
            ast_free(urih);
            ast_config_destroy(cfg);
            return -1;
         }

         urih->description = ast_strdup("HTTP POST mapping");
         urih->uri = ast_strdup(v->name);
         ast_str_set(&ds, 0, "%s", v->value);
         urih->data = ds;
         urih->has_subtree = 0;
         urih->supports_get = 0;
         urih->supports_post = 1;
         urih->callback = http_post_callback;
         urih->key = __FILE__;
         urih->mallocd = urih->dmallocd = 1;

         ast_http_uri_link(urih);
      }

      ast_config_destroy(cfg);
   }
   return 0;
}
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().

{
   int current;
   int comp;
   int found = 0;

   for (current = 0; current < inlen-matchlen; current++, inbuf++) {
      if (*inbuf == *matchbuf) {
         found=1;
         for (comp = 1; comp < matchlen; comp++) {
            if (inbuf[comp] != matchbuf[comp]) {
               found = 0;
               break;
            }
         }
         if (found) {
            break;
         }
      }
   }
   if (found) {
      return current;
   } else {
      return -1;
   }
}
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().

{
   struct ast_variable *var;
   unsigned long ident = 0;
   FILE *f;
   int content_len = 0;
   struct ast_str *post_dir;
   GMimeMessage *message;
   int message_count = 0;
   char * boundary_marker = NULL;

   if (!urih) {
      return ast_http_error((*status = 400),
            (*title = ast_strdup("Missing URI handle")),
            NULL, "There was an error parsing the request");
   }

   for (var = vars; var; var = var->next) {
      if (strcasecmp(var->name, "mansession_id")) {
         continue;
      }

      if (sscanf(var->value, "%30lx", &ident) != 1) {
         return ast_http_error((*status = 400),
                     (*title = ast_strdup("Bad Request")),
                     NULL, "The was an error parsing the request.");
      }

      if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
         return ast_http_error((*status = 401),
                     (*title = ast_strdup("Unauthorized")),
                     NULL, "You are not authorized to make this request.");
      }

      break;
   }

   if (!var) {
      return ast_http_error((*status = 401),
                  (*title = ast_strdup("Unauthorized")),
                  NULL, "You are not authorized to make this request.");
   }

   if (!(f = tmpfile())) {
      ast_log(LOG_ERROR, "Could not create temp file.\n");
      return NULL;
   }

   for (var = headers; var; var = var->next) {
      fprintf(f, "%s: %s\r\n", var->name, var->value);

      if (!strcasecmp(var->name, "Content-Length")) {
         if ((sscanf(var->value, "%30u", &content_len)) != 1) {
            ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
            fclose(f);

            return NULL;
         }
         ast_debug(1, "Got a Content-Length of %d\n", content_len);
      } else if (!strcasecmp(var->name, "Content-Type")) {
         boundary_marker = strstr(var->value, "boundary=");
         if (boundary_marker) {
            boundary_marker += strlen("boundary=");
         }
      }
   }

   fprintf(f, "\r\n");

   if (0 > readmimefile(ser->f, f, boundary_marker, content_len)) {
      if (option_debug) {
         ast_log(LOG_DEBUG, "Cannot find boundary marker in POST request.\n");
      }
      fclose(f);
      
      return NULL;
   }

   if (fseek(f, SEEK_SET, 0)) {
      ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
      fclose(f);

      return NULL;
   }

   post_dir = urih->data;

   message = parse_message(f); /* Takes ownership and will close f */

   if (!message) {
      ast_log(LOG_ERROR, "Error parsing MIME data\n");

      return ast_http_error((*status = 400),
                  (*title = ast_strdup("Bad Request")),
                  NULL, "The was an error parsing the request.");
   }

   if (!(message_count = process_message(message, ast_str_buffer(post_dir)))) {
      ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
      g_object_unref(message);
      return ast_http_error((*status = 400),
                  (*title = ast_strdup("Bad Request")),
                  NULL, "The was an error parsing the request.");
   }

   g_object_unref(message);

   return ast_http_error((*status = 200),
               (*title = ast_strdup("OK")),
               NULL, "File successfully uploaded.");
}
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.

{
   g_mime_init(0);

   __ast_http_post_load(0);

   return AST_MODULE_LOAD_SUCCESS;
}
static GMimeMessage* parse_message ( FILE *  f) [static]

Definition at line 91 of file res_http_post.c.

Referenced by http_post_callback().

{
   GMimeMessage *message;
   GMimeParser *parser;
   GMimeStream *stream;

   stream = g_mime_stream_file_new(f);

   parser = g_mime_parser_new_with_stream(stream);
   g_mime_parser_set_respect_content_length(parser, 1);
   
   g_object_unref(stream);

   message = g_mime_parser_construct_message(parser);

   g_object_unref(parser);

   return message;
}
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().

{
   char filename[PATH_MAX];
   GMimeDataWrapper *content;
   GMimeStream *stream;
   int fd;

   snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);

   ast_debug(1, "Posting raw data to %s\n", filename);

   if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666)) == -1) {
      ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);

      return;
   }

   stream = g_mime_stream_fs_new(fd);

   content = g_mime_part_get_content_object(part);
   g_mime_data_wrapper_write_to_stream(content, stream);
   g_mime_stream_flush(stream);

   g_object_unref(content);
   g_object_unref(stream);
}
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, mime_cbinfo::post_dir, and process_message_callback().

Referenced by http_post_callback().

{
   struct mime_cbinfo cbinfo = {
      .count = 0,
      .post_dir = post_dir,
   };

   g_mime_message_foreach_part(message, process_message_callback, &cbinfo);

   return cbinfo.count;
}
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().

{
   struct mime_cbinfo *cbinfo = user_data;

   cbinfo->count++;

   /* We strip off the headers before we get here, so should only see GMIME_IS_PART */
   if (GMIME_IS_MESSAGE_PART(part)) {
      ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PART\n");
      return;
   } else if (GMIME_IS_MESSAGE_PARTIAL(part)) {
      ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PARTIAL\n");
      return;
   } else if (GMIME_IS_MULTIPART(part)) {
      GList *l;
      
      ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MULTIPART, trying to process subparts\n");
      l = GMIME_MULTIPART(part)->subparts;
      while (l) {
         process_message_callback(l->data, cbinfo);
         l = l->next;
      }
   } else if (GMIME_IS_PART(part)) {
      const char *filename;

      if (ast_strlen_zero(filename = g_mime_part_get_filename(GMIME_PART(part)))) {
         ast_debug(1, "Skipping part with no filename\n");
         return;
      }

      post_raw(GMIME_PART(part), cbinfo->post_dir, filename);
   } else {
      ast_log(LOG_ERROR, "Encountered unknown MIME part. This should never happen!\n");
   }
}
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().

{
   int find_filename = 0;
   char buf[4096];
   int marker;
   int x;
   int char_in_buf = 0;
   int num_to_read;
   int boundary_len;
   char * path_end, * path_start, * filespec;

   if (NULL == fin || NULL == fout || NULL == boundary || 0 >= contentlen) {
      return -1;
   }

   boundary_len = strlen(boundary);
   while (0 < contentlen || 0 < char_in_buf) {
      /* determine how much I will read into the buffer */
      if (contentlen > sizeof(buf) - char_in_buf) {
         num_to_read = sizeof(buf)- char_in_buf;
      } else {
         num_to_read = contentlen;
      }

      if (0 < num_to_read) {
         if (fread(&(buf[char_in_buf]), 1, num_to_read, fin) < num_to_read) {
            ast_log(LOG_WARNING, "fread() failed: %s\n", strerror(errno));
            num_to_read = 0;
         }
         contentlen -= num_to_read;
         char_in_buf += num_to_read;
      }
      /* If I am looking for the filename spec */
      if (find_filename) {
         path_end = filespec = NULL;
         x = strlen("filename=\"");
         marker = find_sequence(buf, char_in_buf, "filename=\"", x );
         if (0 <= marker) {
            marker += x;  /* Index beyond the filename marker */
            path_start = &buf[marker];
            for (path_end = path_start, x = 0; x < char_in_buf-marker; x++, path_end++) {
               if ('\\' == *path_end) {   /* convert backslashses to forward slashes */
                  *path_end = '/';
               }
               if ('\"' == *path_end) {   /* If at the end of the file name spec */
                  *path_end = '\0';    /* temporarily null terminate the file spec for basename */
                  filespec = basename(path_start);
                  *path_end = '\"';
                  break;
               }
            }
         }
         if (filespec) {   /* If the file name path was found in the header */
            if (fwrite(buf, 1, marker, fout) != marker) {
               ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
            }
            x = (int)(path_end+1 - filespec);
            if (fwrite(filespec, 1, x, fout) != x) {
               ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
            }
            x = (int)(path_end+1 - buf);
            memmove(buf, &(buf[x]), char_in_buf-x);
            char_in_buf -= x;
         }
         find_filename = 0;
      } else { /* I am looking for the boundary marker */
         marker = find_sequence(buf, char_in_buf, boundary, boundary_len);
         if (0 > marker) {
            if (char_in_buf < (boundary_len)) {
               /*no possibility to find the boundary, write all you have */
               if (fwrite(buf, 1, char_in_buf, fout) != char_in_buf) {
                  ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
               }
               char_in_buf = 0;
            } else {
               /* write all except for area where the boundary marker could be */
               if (fwrite(buf, 1, char_in_buf -(boundary_len -1), fout) != char_in_buf - (boundary_len - 1)) {
                  ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
               }
               x = char_in_buf -(boundary_len -1);
               memmove(buf, &(buf[x]), char_in_buf-x);
               char_in_buf = (boundary_len -1);
            }
         } else {
            /* write up through the boundary, then look for filename in the rest */
            if (fwrite(buf, 1, marker + boundary_len, fout) != marker + boundary_len) {
               ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
            }
            x = marker + boundary_len;
            memmove(buf, &(buf[x]), char_in_buf-x);
            char_in_buf -= marker + boundary_len;
            find_filename =1;
         }
      }
   }
   return 0;
}
static int reload ( void  ) [static]
static int unload_module ( void  ) [static]

Definition at line 467 of file res_http_post.c.

References ast_http_uri_unlink_all_with_key().

{
   ast_http_uri_unlink_all_with_key(__FILE__);

   return 0;
}

Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "HTTP POST support" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .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.