/* * 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 */