diff --git a/formats/format_pcm.c b/formats/format_pcm.c index 4e846d7cfe..674c8b59ad 100644 --- a/formats/format_pcm.c +++ b/formats/format_pcm.c @@ -507,6 +507,7 @@ static struct ast_format_def alaw_f = { static struct ast_format_def pcm_f = { .name = "pcm", .exts = "pcm|ulaw|ul|mu|ulw", + .mime_types = "audio/basic", .write = pcm_write, .seek = pcm_seek, .trunc = pcm_trunc, diff --git a/formats/format_vox.c b/formats/format_vox.c index b63e225be9..023c409678 100644 --- a/formats/format_vox.c +++ b/formats/format_vox.c @@ -128,6 +128,7 @@ static off_t vox_tell(struct ast_filestream *fs) static struct ast_format_def vox_f = { .name = "vox", .exts = "vox", + .mime_types = "audio/x-vox", .write = vox_write, .seek = vox_seek, .trunc = vox_trunc, diff --git a/formats/format_wav.c b/formats/format_wav.c index 81a686e361..ec7e3d3799 100644 --- a/formats/format_wav.c +++ b/formats/format_wav.c @@ -532,6 +532,7 @@ static struct ast_format_def wav16_f = { static struct ast_format_def wav_f = { .name = "wav", .exts = "wav", + .mime_types = "audio/wav|audio/x-wav", .open = wav_open, .rewrite = wav_rewrite, .write = wav_write, diff --git a/include/asterisk/file.h b/include/asterisk/file.h index c17cb327b3..1c2c7a84a0 100644 --- a/include/asterisk/file.h +++ b/include/asterisk/file.h @@ -427,6 +427,18 @@ char *ast_format_str_reduce(char *fmts); */ struct ast_format *ast_get_format_for_file_ext(const char *file_ext); +/*! + * \brief Get a suitable filename extension for the given MIME type + * + * \param mime_type The MIME type for which to find extensions + * \param buffer A pointer to a buffer to receive the extension + * \param capacity The size of 'buffer' in bytes + * + * \retval 1 if an extension was found for the provided MIME type + * \retval 0 if the MIME type was not found + */ +int ast_get_extension_for_mime_type(const char *mime_type, char *buffer, size_t capacity); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/include/asterisk/mod_format.h b/include/asterisk/mod_format.h index 5f93ea442d..6e772f05b2 100644 --- a/include/asterisk/mod_format.h +++ b/include/asterisk/mod_format.h @@ -44,6 +44,7 @@ struct ast_format_def { char name[80]; /*!< Name of format */ char exts[80]; /*!< Extensions (separated by | if more than one) * this format can read. First is assumed for writing (e.g. .mp3) */ + char mime_types[80]; /*!< MIME Types related to the format (separated by | if more than one)*/ struct ast_format *format; /*!< Format of frames it uses/provides (one only) */ /*! * \brief Prepare an input stream for playback. diff --git a/main/file.c b/main/file.c index 8dded81238..3ee58b1684 100644 --- a/main/file.c +++ b/main/file.c @@ -333,20 +333,21 @@ static char *build_filename(const char *filename, const char *ext) /* compare type against the list 'exts' */ /* XXX need a better algorithm */ -static int exts_compare(const char *exts, const char *type) +static int type_in_list(const char *list, const char *type, int (*cmp)(const char *s1, const char *s2)) { - char tmp[256]; - char *stringp = tmp, *ext; + char *stringp = ast_strdupa(list), *item; - ast_copy_string(tmp, exts, sizeof(tmp)); - while ((ext = strsep(&stringp, "|"))) { - if (!strcmp(ext, type)) + while ((item = strsep(&stringp, "|"))) { + if (!cmp(item, type)) { return 1; + } } return 0; } +#define exts_compare(list, type) (type_in_list((list), (type), strcmp)) + /*! * \internal * \brief Close the file stream by canceling any pending read / write callbacks @@ -1926,6 +1927,27 @@ struct ast_format *ast_get_format_for_file_ext(const char *file_ext) return NULL; } +int ast_get_extension_for_mime_type(const char *mime_type, char *buffer, size_t capacity) +{ + struct ast_format_def *f; + SCOPED_RDLOCK(lock, &formats.lock); + + ast_assert(buffer && capacity); + + AST_RWLIST_TRAVERSE(&formats, f, list) { + if (type_in_list(f->mime_types, mime_type, strcasecmp)) { + size_t item_len = strcspn(f->exts, "|"); + size_t bytes_written = snprintf(buffer, capacity, ".%.*s", (int) item_len, f->exts); + if (bytes_written < capacity) { + /* Only return success if we didn't truncate */ + return 1; + } + } + } + + return 0; +} + static struct ast_cli_entry cli_file[] = { AST_CLI_DEFINE(handle_cli_core_show_file_formats, "Displays file formats") }; diff --git a/main/media_cache.c b/main/media_cache.c index e93d1a02fb..e0a6dbb748 100644 --- a/main/media_cache.c +++ b/main/media_cache.c @@ -35,6 +35,7 @@ #include "asterisk/bucket.h" #include "asterisk/astdb.h" #include "asterisk/cli.h" +#include "asterisk/file.h" #include "asterisk/media_cache.h" /*! The name of the AstDB family holding items in the cache. */ @@ -123,6 +124,24 @@ static void media_cache_item_del_from_astdb(struct ast_bucket_file *bucket_file) ast_free(hash_value); } +/*! + * \internal + * \brief Normalize the value of a Content-Type header + * + * This will trim off any optional parameters after the type/subtype. + */ +static void normalize_content_type_header(char *content_type) +{ + char *params = strchr(content_type, ';'); + + if (params) { + *params-- = 0; + while (params > content_type && (*params == ' ' || *params == '\t')) { + *params-- = 0; + } + } +} + /*! * \internal * \brief Update the name of the file backing a \c bucket_file @@ -142,9 +161,32 @@ static void bucket_file_update_path(struct ast_bucket_file *bucket_file, } else if (!strchr(bucket_file->path, '.') && (ext = strrchr(ast_sorcery_object_get_id(bucket_file), '.'))) { /* If we don't have a file extension and were provided one in the URI, use it */ char new_path[PATH_MAX]; + char found_ext[PATH_MAX]; ast_bucket_file_metadata_set(bucket_file, "ext", ext); + /* Don't pass '.' while checking for supported extension */ + if (!ast_get_format_for_file_ext(ext + 1)) { + /* If the file extension passed in the URI isn't supported check for the + * extension based on the MIME type passed in the Content-Type header before + * giving up. + * If a match is found then retrieve the extension from the supported list + * corresponding to the mime-type and use that to rename the file */ + struct ast_bucket_metadata *header = ast_bucket_file_metadata_get(bucket_file, "content-type"); + if (header) { + char *mime_type = ast_strdup(header->value); + if (mime_type) { + normalize_content_type_header(mime_type); + if (!ast_strlen_zero(mime_type)) { + if (ast_get_extension_for_mime_type(mime_type, found_ext, sizeof(found_ext))) { + ext = found_ext; + } + } + ast_free(mime_type); + } + } + } + snprintf(new_path, sizeof(new_path), "%s%s", bucket_file->path, ext); rename(bucket_file->path, new_path); ast_copy_string(bucket_file->path, new_path, sizeof(bucket_file->path)); diff --git a/res/res_http_media_cache.c b/res/res_http_media_cache.c index eba7ecc618..bca576372c 100644 --- a/res/res_http_media_cache.c +++ b/res/res_http_media_cache.c @@ -84,6 +84,7 @@ static size_t curl_header_callback(char *buffer, size_t size, size_t nitems, voi if (strcasecmp(header, "ETag") && strcasecmp(header, "Cache-Control") && strcasecmp(header, "Last-Modified") + && strcasecmp(header, "Content-Type") && strcasecmp(header, "Expires")) { return realsize; }