@ -31,6 +31,41 @@
< support_level > core < / support_level >
* * */
/*** DOCUMENTATION
< configInfo name = " res_http_media_cache " language = " en_US " >
< synopsis > HTTP media cache < / synopsis >
< configFile name = " http_media_cache.conf " >
< configObject name = " general " >
< synopsis > General configuration < / synopsis >
< configOption name = " timeout_secs " default = " 180 " >
< synopsis > The maximum time the transfer is allowed to complete in seconds . See https : //curl.se/libcurl/c/CURLOPT_TIMEOUT.html for details.</synopsis>
< / configOption >
< configOption name = " user_agent " >
< synopsis > The HTTP User - Agent to use for requests . See https : //curl.se/libcurl/c/CURLOPT_USERAGENT.html for details.</synopsis>
< / configOption >
< configOption name = " follow_location " default = " 1 " >
< synopsis > Follow HTTP 3 xx redirects on requests . See https : //curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html for details.</synopsis>
< / configOption >
< configOption name = " max_redirects " default = " 8 " >
< synopsis > The maximum number of redirects to follow . See https : //curl.se/libcurl/c/CURLOPT_MAXREDIRS.html for details.</synopsis>
< / configOption >
< configOption name = " proxy " >
< synopsis > The proxy to use for requests . See https : //curl.se/libcurl/c/CURLOPT_PROXY.html for details.</synopsis>
< / configOption >
< configOption name = " protocols " >
< synopsis > The comma separated list of allowed protocols for the request . Available with cURL 7.85 .0 or later . See https : //curl.se/libcurl/c/CURLOPT_PROTOCOLS_STR.html for details.</synopsis>
< / configOption >
< configOption name = " redirect_protocols " >
< synopsis > The comma separated list of allowed protocols for redirects . Available with cURL 7.85 .0 or later . See https : //curl.se/libcurl/c/CURLOPT_REDIR_PROTOCOLS_STR.html for details.</synopsis>
< / configOption >
< configOption name = " dns_cache_timeout_secs " default = " 60 " >
< synopsis > The life - time for DNS cache entries . See https : //curl.se/libcurl/c/CURLOPT_DNS_CACHE_TIMEOUT.html for details.</synopsis>
< / configOption >
< / configObject >
< / configFile >
< / configInfo >
* * */
# include "asterisk.h"
# include <curl/curl.h>
@ -44,6 +79,123 @@
# define MAX_HEADER_LENGTH 1023
# ifdef CURL_AT_LEAST_VERSION
# if CURL_AT_LEAST_VERSION(7, 85, 0)
# define AST_CURL_HAS_PROTOCOLS_STR 1
# endif
# endif
static int http_media_cache_config_pre_apply ( void ) ;
/*! \brief General configuration options for http media cache. */
struct conf_general_options {
/*! \brief Request timeout to use */
int curl_timeout ;
/*! \brief Follow 3xx redirects automatically. */
int curl_followlocation ;
/*! \brief Number of redirects to follow for one request. */
int curl_maxredirs ;
/*! \brief Life-time of CURL DNS cache entries. */
int curl_dns_cache_timeout ;
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD ( curl_useragent ) ; /*! \brief User-agent to use for requests. */
AST_STRING_FIELD ( curl_proxy ) ; /*! \brief Proxy to use for requests. None by default. */
AST_STRING_FIELD ( curl_protocols ) ; /*! \brief Allowed protocols to use for requests. All by default. */
AST_STRING_FIELD ( curl_redir_protocols ) ; /*! \brief Allowed protocols to use on redirect. All by default. */
) ;
} ;
/*! \brief All configuration options for http media cache. */
struct conf {
/*! The general section configuration options. */
struct conf_general_options * general ;
} ;
/*! \brief Locking container for safe configuration access. */
static AO2_GLOBAL_OBJ_STATIC ( confs ) ;
/*! \brief Mapping of the http media cache conf struct's general to the general context in the config file. */
static struct aco_type general_option = {
. type = ACO_GLOBAL ,
. name = " general " ,
. item_offset = offsetof ( struct conf , general ) ,
. category = " general " ,
. category_match = ACO_WHITELIST_EXACT ,
} ;
static struct aco_type * general_options [ ] = ACO_TYPES ( & general_option ) ;
/*! \brief Disposes of the http media cache conf object */
static void conf_destructor ( void * obj )
{
struct conf * cfg = obj ;
ast_string_field_free_memory ( cfg - > general ) ;
ao2_cleanup ( cfg - > general ) ;
}
/*! \brief Creates the http media cache conf object. */
static void * conf_alloc ( void )
{
struct conf * cfg ;
if ( ! ( cfg = ao2_alloc ( sizeof ( * cfg ) , conf_destructor ) ) ) {
return NULL ;
}
if ( ! ( cfg - > general = ao2_alloc ( sizeof ( * cfg - > general ) , NULL ) ) ) {
ao2_ref ( cfg , - 1 ) ;
return NULL ;
}
if ( ast_string_field_init ( cfg - > general , 256 ) ) {
ao2_ref ( cfg , - 1 ) ;
return NULL ;
}
return cfg ;
}
/*! \brief The conf file that's processed for the module. */
static struct aco_file conf_file = {
/*! The config file name. */
. filename = " res_http_media_cache.conf " ,
/*! The mapping object types to be processed. */
. types = ACO_TYPES ( & general_option ) ,
} ;
CONFIG_INFO_STANDARD ( cfg_info , confs , conf_alloc ,
. pre_apply_config = http_media_cache_config_pre_apply ,
. files = ACO_FILES ( & conf_file ) ) ;
/*!
* \ brief Pre - apply callback for the config framework .
*
* This validates that used options match the ones supported by CURL .
*/
static int http_media_cache_config_pre_apply ( void )
{
# ifndef AST_CURL_HAS_PROTOCOLS_STR
struct conf * cfg = aco_pending_config ( & cfg_info ) ;
if ( ! ast_strlen_zero ( cfg - > general - > curl_protocols ) ) {
ast_log ( AST_LOG_ERROR , " 'protocols' not supported by linked CURL library. Please recompile against newer CURL. \n " ) ;
return - 1 ;
}
if ( ! ast_strlen_zero ( cfg - > general - > curl_redir_protocols ) ) {
ast_log ( AST_LOG_ERROR , " 'redirect_protocols' not supported by linked CURL library. Please recompile against newer CURL. \n " ) ;
return - 1 ;
}
# endif
return 0 ;
}
/*! \brief Data passed to cURL callbacks */
struct curl_bucket_file_data {
/*! The \c ast_bucket_file object that caused the operation */
@ -324,6 +476,8 @@ static int bucket_file_expired(struct ast_bucket_file *bucket_file)
*/
static CURL * get_curl_instance ( struct curl_bucket_file_data * cb_data )
{
RAII_VAR ( struct conf * , cfg , ao2_global_obj_ref ( confs ) , ao2_cleanup ) ;
CURLcode rc ;
CURL * curl ;
curl = curl_easy_init ( ) ;
@ -332,14 +486,47 @@ static CURL *get_curl_instance(struct curl_bucket_file_data *cb_data)
}
curl_easy_setopt ( curl , CURLOPT_NOSIGNAL , 1 ) ;
curl_easy_setopt ( curl , CURLOPT_TIMEOUT , 180 ) ;
curl_easy_setopt ( curl , CURLOPT_HEADERFUNCTION , curl_header_callback ) ;
curl_easy_setopt ( curl , CURLOPT_USERAGENT , AST_CURL_USER_AGENT ) ;
curl_easy_setopt ( curl , CURLOPT_FOLLOWLOCATION , 1 ) ;
curl_easy_setopt ( curl , CURLOPT_MAXREDIRS , 8 ) ;
curl_easy_setopt ( curl , CURLOPT_URL , ast_sorcery_object_get_id ( cb_data - > bucket_file ) ) ;
curl_easy_setopt ( curl , CURLOPT_HEADERDATA , cb_data ) ;
curl_easy_setopt ( curl , CURLOPT_TIMEOUT , cfg - > general - > curl_timeout ) ;
curl_easy_setopt ( curl , CURLOPT_USERAGENT , cfg - > general - > curl_useragent ) ;
curl_easy_setopt ( curl , CURLOPT_FOLLOWLOCATION , cfg - > general - > curl_followlocation ? 1 : 0 ) ;
curl_easy_setopt ( curl , CURLOPT_MAXREDIRS , cfg - > general - > curl_maxredirs ) ;
if ( ! ast_strlen_zero ( cfg - > general - > curl_proxy ) ) {
curl_easy_setopt ( curl , CURLOPT_PROXY , cfg - > general - > curl_proxy ) ;
}
if ( ! ast_strlen_zero ( cfg - > general - > curl_protocols ) ) {
# ifdef AST_CURL_HAS_PROTOCOLS_STR
CURLcode rc = curl_easy_setopt ( curl , CURLOPT_PROTOCOLS_STR , cfg - > general - > curl_protocols ) ;
if ( rc ! = CURLE_OK ) {
ast_log ( AST_LOG_ERROR , " Setting protocols to '%s' failed: %d \n " , cfg - > general - > curl_protocols , rc ) ;
curl_easy_cleanup ( curl ) ;
return NULL ;
}
# endif
}
if ( ! ast_strlen_zero ( cfg - > general - > curl_redir_protocols ) ) {
# ifdef AST_CURL_HAS_PROTOCOLS_STR
CURLcode rc = curl_easy_setopt ( curl , CURLOPT_REDIR_PROTOCOLS_STR , cfg - > general - > curl_redir_protocols ) ;
if ( rc ! = CURLE_OK ) {
ast_log ( AST_LOG_ERROR , " Setting redirect_protocols to '%s' failed: %d \n " , cfg - > general - > curl_redir_protocols , rc ) ;
curl_easy_cleanup ( curl ) ;
return NULL ;
}
# endif
}
rc = curl_easy_setopt ( curl , CURLOPT_DNS_CACHE_TIMEOUT , cfg - > general - > curl_dns_cache_timeout ) ;
if ( rc ! = CURLE_OK ) {
ast_log ( AST_LOG_ERROR , " Setting dns_cache_timeout to '%d' failed: %d \n " , cfg - > general - > curl_dns_cache_timeout , rc ) ;
curl_easy_cleanup ( curl ) ;
return NULL ;
}
return curl ;
}
@ -541,11 +728,73 @@ static struct ast_sorcery_wizard https_bucket_file_wizard = {
static int unload_module ( void )
{
aco_info_destroy ( & cfg_info ) ;
ao2_global_obj_release ( confs ) ;
return 0 ;
}
static int load_module ( void )
{
if ( aco_info_init ( & cfg_info ) ) {
aco_info_destroy ( & cfg_info ) ;
return AST_MODULE_LOAD_DECLINE ;
}
aco_option_register ( & cfg_info , " timeout_secs " , ACO_EXACT , general_options ,
" 180 " , OPT_INT_T , 0 ,
FLDSET ( struct conf_general_options , curl_timeout ) ) ;
aco_option_register ( & cfg_info , " user_agent " , ACO_EXACT , general_options ,
AST_CURL_USER_AGENT , OPT_STRINGFIELD_T , 0 ,
STRFLDSET ( struct conf_general_options , curl_useragent ) ) ;
aco_option_register ( & cfg_info , " follow_location " , ACO_EXACT , general_options ,
" yes " , OPT_BOOL_T , 1 ,
FLDSET ( struct conf_general_options , curl_followlocation ) ) ;
aco_option_register ( & cfg_info , " max_redirects " , ACO_EXACT , general_options ,
" 8 " , OPT_INT_T , 0 ,
FLDSET ( struct conf_general_options , curl_maxredirs ) ) ;
aco_option_register ( & cfg_info , " proxy " , ACO_EXACT , general_options ,
NULL , OPT_STRINGFIELD_T , 1 ,
STRFLDSET ( struct conf_general_options , curl_proxy ) ) ;
aco_option_register ( & cfg_info , " dns_cache_timeout_secs " , ACO_EXACT , general_options ,
" 60 " , OPT_INT_T , 0 ,
FLDSET ( struct conf_general_options , curl_dns_cache_timeout ) ) ;
aco_option_register ( & cfg_info , " protocols " , ACO_EXACT , general_options ,
NULL , OPT_STRINGFIELD_T , 1 ,
STRFLDSET ( struct conf_general_options , curl_protocols ) ) ;
aco_option_register ( & cfg_info , " redirect_protocols " , ACO_EXACT , general_options ,
NULL , OPT_STRINGFIELD_T , 1 ,
STRFLDSET ( struct conf_general_options , curl_redir_protocols ) ) ;
if ( aco_process_config ( & cfg_info , 0 ) = = ACO_PROCESS_ERROR ) {
struct conf * cfg ;
ast_log ( LOG_NOTICE , " Could not load res_http_media_cache config; using defaults \n " ) ;
cfg = conf_alloc ( ) ;
if ( ! cfg ) {
aco_info_destroy ( & cfg_info ) ;
return AST_MODULE_LOAD_DECLINE ;
}
if ( aco_set_defaults ( & general_option , " general " , cfg - > general ) ) {
ast_log ( LOG_ERROR , " Failed to initialize res_http_media_cache defaults. \n " ) ;
ao2_ref ( cfg , - 1 ) ;
aco_info_destroy ( & cfg_info ) ;
return AST_MODULE_LOAD_DECLINE ;
}
ao2_global_obj_replace_unref ( confs , cfg ) ;
ao2_ref ( cfg , - 1 ) ;
}
if ( ast_bucket_scheme_register ( " http " , & http_bucket_wizard , & http_bucket_file_wizard ,
NULL , NULL ) ) {
ast_log ( LOG_ERROR , " Failed to register Bucket HTTP wizard scheme implementation \n " ) ;