diff --git a/src/mod/applications/mod_curl/Makefile b/src/mod/applications/mod_curl/Makefile new file mode 100644 index 0000000000..2c35e6e98f --- /dev/null +++ b/src/mod/applications/mod_curl/Makefile @@ -0,0 +1,2 @@ +BASE=../../../.. +include $(BASE)/build/modmake.rules diff --git a/src/mod/applications/mod_curl/mod_curl.c b/src/mod/applications/mod_curl/mod_curl.c new file mode 100755 index 0000000000..549884755c --- /dev/null +++ b/src/mod/applications/mod_curl/mod_curl.c @@ -0,0 +1,315 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2009, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Rupa Schomaker + * + * mod_curl.c -- API for performing http queries + * + */ + +#include +#include + +/* Prototypes */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_curl_shutdown); +SWITCH_MODULE_RUNTIME_FUNCTION(mod_curl_runtime); +SWITCH_MODULE_LOAD_FUNCTION(mod_curl_load); + +/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime) + * Defines a switch_loadable_module_function_table_t and a static const char[] modname + */ +SWITCH_MODULE_DEFINITION(mod_curl, mod_curl_load, mod_curl_shutdown, NULL); + +static char *SYNTAX = "curl url [headers]"; + +static struct { + switch_memory_pool_t *pool; +} globals; + +struct http_data_obj { + switch_stream_handle_t stream; + switch_size_t bytes; + switch_size_t max_bytes; + switch_memory_pool_t *pool; + int err; + long http_response_code; + char *http_response; + struct curl_slist *headers; +}; +typedef struct http_data_obj http_data_t; + +struct callback_obj { + switch_memory_pool_t *pool; + char *name; +}; +typedef struct callback_obj callback_t; + +static size_t file_callback(void *ptr, size_t size, size_t nmemb, void *data) +{ + register unsigned int realsize = (unsigned int) (size * nmemb); + http_data_t *http_data = data; + + http_data->bytes += realsize; + + if (http_data->bytes > http_data->max_bytes) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Oversized file detected [%d bytes]\n", (int)http_data->bytes); + http_data->err = 1; + return 0; + } + + http_data->stream.write_function( + &http_data->stream, "%.*s", realsize, ptr); + return realsize; +} + +static size_t header_callback(void *ptr, size_t size, size_t nmemb, void *data) +{ + register unsigned int realsize = (unsigned int) (size * nmemb); + http_data_t *http_data = data; + char *header = NULL; + + header = switch_core_alloc(http_data->pool, realsize+1); + switch_copy_string(header, ptr, realsize); + header[realsize] = '\0'; + + /* parse data - placeholder + if ((data = index(header, ':')) { + *data = '\0'; + data++; + while(*data == ' ' && *data != '\0') { + data++; + } + } + */ + + http_data->headers = curl_slist_append(http_data->headers, header); + + return realsize; +} + +static http_data_t *do_lookup_url(switch_memory_pool_t *pool, const char *url) { + + CURL *curl_handle = NULL; + long httpRes = 0; + char hostname[256] = ""; + + http_data_t *http_data = NULL; + + http_data = switch_core_alloc(pool, sizeof(http_data_t)); + memset(http_data, 0, sizeof(http_data_t)); + http_data->pool = pool; + + http_data->max_bytes = 64000; + SWITCH_STANDARD_STREAM(http_data->stream); + + gethostname(hostname, sizeof(hostname)); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "url: %s\n", url); + curl_handle = curl_easy_init(); + + if (!strncasecmp(url, "https", 5)) { + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0); + } + curl_easy_setopt(curl_handle, CURLOPT_POST, SWITCH_FALSE); + curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 10); + curl_easy_setopt(curl_handle, CURLOPT_URL, url); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, file_callback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) http_data); + curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback); + curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *) http_data); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-curl/1.0"); + + curl_easy_perform(curl_handle); + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes); + curl_easy_cleanup(curl_handle); + + if ( http_data->stream.data && + !switch_strlen_zero((char *)http_data->stream.data) && + strcmp(" ", http_data->stream.data) ) { + + http_data->http_response = switch_core_strdup(pool, http_data->stream.data); + } + + http_data->http_response_code = httpRes; + + switch_safe_free(http_data->stream.data); + return http_data; +} + + +SWITCH_STANDARD_APP(curl_app_function) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + + char *argv[10] = { 0 }; + int argc; + char *mydata = NULL; + + switch_memory_pool_t *pool = NULL; + switch_channel_t *channel = switch_core_session_get_channel(session); + char *url = NULL; + http_data_t *http_data = NULL; + + if (session) { + pool = switch_core_session_get_pool(session); + } else { + switch_core_new_memory_pool(&pool); + } + if (!(mydata = switch_core_session_strdup(session, data))) { + return; + } + + if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { + if (argc == 0) { + switch_goto_status(SWITCH_STATUS_SUCCESS, usage); + } + + if (argc > 0) { + url = switch_core_strdup(pool, argv[0]); + } + } + + http_data = do_lookup_url(pool, url); + switch_channel_set_variable(channel, "curl_response_data", http_data->http_response); + switch_channel_set_variable(channel, "curl_response_code", + switch_core_sprintf(pool, "%ld", http_data->http_response_code)); + + switch_goto_status(SWITCH_STATUS_SUCCESS, done); + +usage: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", SYNTAX); + switch_goto_status(status, done); + +done: + if (!session) { + switch_core_destroy_memory_pool(&pool); + } +} +SWITCH_STANDARD_API(curl_function) +{ + switch_status_t status; + char *argv[10] = { 0 }; + int argc; + char *mydata = NULL; + char *url = NULL; + switch_bool_t headers = SWITCH_FALSE; + struct curl_slist *slist = NULL; + http_data_t *http_data = NULL; + + switch_memory_pool_t *pool = NULL; + + if (switch_strlen_zero(cmd)) { + switch_goto_status(SWITCH_STATUS_SUCCESS, usage); + } + + if (session) { + pool = switch_core_session_get_pool(session); + } else { + switch_core_new_memory_pool(&pool); + } + + mydata = strdup(cmd); + if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { + if (argc < 1) { + switch_goto_status(SWITCH_STATUS_SUCCESS, usage); + } + + url = switch_core_strdup(pool, argv[0]); + + if (argc > 1) { + if (!strcasecmp("headers", argv[1])) { + headers = SWITCH_TRUE; + } + } + + http_data = do_lookup_url(pool, url); + stream->write_function(stream, "%ld\n", http_data->http_response_codenot); + if(headers) { + slist = http_data->headers; + while(slist) { + stream->write_function(stream, "%s\n", slist->data); + slist = slist->next; + } + stream->write_function(stream, "\n"); + } + stream->write_function(stream, "%s", http_data->http_response ? http_data->http_response : ""); + } + switch_goto_status(SWITCH_STATUS_SUCCESS, done); + +usage: + stream->write_function(stream, "-ERR\n%s\n", SYNTAX); + switch_goto_status(status, done); + +done: + curl_slist_free_all(http_data->headers); + switch_safe_free(mydata); + if (!session) { + switch_core_destroy_memory_pool(&pool); + } + return status; +} + +/* Macro expands to: switch_status_t mod_cidlookup_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */ +SWITCH_MODULE_LOAD_FUNCTION(mod_curl_load) +{ + switch_api_interface_t *api_interface; + switch_application_interface_t *app_interface; + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + memset(&globals, 0, sizeof(globals)); + + globals.pool = pool; + + SWITCH_ADD_API(api_interface, "curl", "curl API", curl_function, SYNTAX); + SWITCH_ADD_APP(app_interface, "curl", "Perform a http request", "Perform a http request", + curl_app_function, "curl url", SAF_SUPPORT_NOMEDIA); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +/* + Called when the system shuts down + Macro expands to: switch_status_t mod_cidlookup_shutdown() */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_curl_shutdown) +{ + /* Cleanup dynamically allocated config settings */ + + return SWITCH_STATUS_SUCCESS; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + */