/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2014, 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 * Yossi Neiman * Seven Du * * mod_curl.c -- API for performing http queries * */ #include #include #ifdef _MSC_VER #include #else #include #endif /* 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|json|content-type |connect-timeout |timeout ] [get|head|post|delete|put [data]]"; #define HTTP_SENDFILE_ACK_EVENT "curl_sendfile::ack" #define HTTP_SENDFILE_RESPONSE_SIZE 32768 static struct { switch_memory_pool_t *pool; } globals; typedef enum { CSO_NONE = (1 << 0), CSO_EVENT = (1 << 1), CSO_STREAM = (1 << 2) } curlsendfile_output_t; 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; switch_curl_slist_t *headers; }; typedef struct http_data_obj http_data_t; struct http_sendfile_data_obj { switch_memory_pool_t *pool; switch_file_t *file_handle; long http_response_code; char *http_response; switch_curl_slist_t *headers; char *mydata; char *url; char *identifier_str; char *filename_element; char *filename_element_name; char *extrapost_elements; switch_CURL *curl_handle; struct curl_httppost *formpost; struct curl_httppost *lastptr; uint8_t flags; /* This is for where to send output of the curl_sendfile commands */ switch_stream_handle_t *stream; char *sendfile_response; switch_size_t sendfile_response_count; }; typedef struct http_sendfile_data_obj http_sendfile_data_t; struct data_stream { const char *data; size_t length; }; struct callback_obj { switch_memory_pool_t *pool; char *name; }; typedef struct callback_obj callback_t; struct curl_options_obj { long connect_timeout; long timeout; }; typedef struct curl_options_obj curl_options_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'; http_data->headers = switch_curl_slist_append(http_data->headers, header); return realsize; } static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) { struct data_stream *dstream = (struct data_stream*)stream; size_t nmax = size*nmemb; size_t ncur = (dstream->length > nmax) ? nmax : dstream->length; memmove(ptr, dstream->data, ncur); dstream->data += ncur; dstream->length -= ncur; return ncur; } static http_data_t *do_lookup_url(switch_memory_pool_t *pool, const char *url, const char *method, const char *data, const char *content_type, curl_options_t *options) { switch_CURL *curl_handle = NULL; long httpRes = 0; http_data_t *http_data = NULL; switch_curl_slist_t *headers = NULL; struct data_stream dstream = { 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); if (!method) { method = "get"; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "method: %s, url: %s, content-type: %s\n", method, url, content_type); curl_handle = switch_curl_easy_init(); if (options) { if (options->connect_timeout) { switch_curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, options->connect_timeout); } if (options->timeout) { switch_curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, options->timeout); } } if (!strncasecmp(url, "https", 5)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Not verifying TLS cert for %s; connection is not secure\n", url); switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0); switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0); } if (!strcasecmp(method, "head")) { switch_curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1); } else if (!strcasecmp(method, "post")) { switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, strlen(data)); switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, (void *) data); if (content_type) { char *ct = switch_mprintf("Content-Type: %s", content_type); headers = switch_curl_slist_append(headers, ct); switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); switch_safe_free(ct); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Post data: %s\n", data); } else if (!strcasecmp(method, "delete")) { switch_curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "DELETE"); switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, strlen(data)); switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, (void *) data); if (content_type) { char *ct = switch_mprintf("Content-Type: %s", content_type); headers = switch_curl_slist_append(headers, ct); switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); switch_safe_free(ct); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "DELETE data: %s\n", data); } else if (!strcasecmp(method, "put")) { dstream.data = data; dstream.length = strlen(data); switch_curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1); switch_curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, read_callback); switch_curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)dstream.length); switch_curl_easy_setopt(curl_handle, CURLOPT_READDATA, (void *) &dstream); if (content_type) { char *ct = switch_mprintf("Content-Type: %s", content_type); headers = switch_curl_slist_append(headers, ct); headers = switch_curl_slist_append(headers, "Expect:"); switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); switch_safe_free(ct); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PUT data: %s\n", data); } else { switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPGET, 1); } switch_curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1); switch_curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 15); switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); switch_curl_easy_setopt(curl_handle, CURLOPT_URL, url); switch_curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, file_callback); switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) http_data); switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback); switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *) http_data); switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-curl/1.0"); switch_curl_easy_perform(curl_handle); switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes); switch_curl_easy_cleanup(curl_handle); switch_curl_slist_free_all(headers); if (http_data->stream.data && !zstr((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; } static char *print_json(switch_memory_pool_t *pool, http_data_t *http_data) { cJSON *top = cJSON_CreateObject(), *headers = cJSON_CreateArray(); char *data = NULL; char tmp[32], *f = NULL; switch_curl_slist_t *header = http_data->headers; if(!top || !headers) { cJSON_Delete(headers); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to alloc memory for cJSON structures.\n"); goto curl_json_output_end; } switch_snprintf(tmp, sizeof(tmp), "%ld", http_data->http_response_code); cJSON_AddItemToObject(top, "status_code", cJSON_CreateString(tmp)); if (http_data->http_response) { cJSON_AddItemToObject(top, "body", cJSON_CreateString(http_data->http_response)); } /* parse header data */ while (header) { cJSON *obj = NULL; /* remove trailing \r */ if ((data = strrchr(header->data, '\r'))) { *data = '\0'; } if (zstr(header->data)) { header = header->next; continue; } if ((data = strchr(header->data, ':'))) { *data = '\0'; data++; while (*data == ' ' && *data != '\0') { data++; } obj = cJSON_CreateObject(); cJSON_AddItemToObject(obj, "key", cJSON_CreateString(header->data)); cJSON_AddItemToObject(obj, "value", cJSON_CreateString(data)); cJSON_AddItemToArray(headers, obj); } else { if (!strncmp("HTTP", header->data, 4)) { char *argv[3] = { 0 }; int argc; if ((argc = switch_separate_string(header->data, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { if (argc > 2) { cJSON_AddItemToObject(top, "version", cJSON_CreateString(argv[0])); cJSON_AddItemToObject(top, "phrase", cJSON_CreateString(argv[2])); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unparsable header: argc: %d\n", argc); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Starts with HTTP but not parsable: %s\n", header->data); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unparsable header: %s\n", header->data); } } header = header->next; } cJSON_AddItemToObject(top, "headers", headers); f = cJSON_PrintUnformatted(top); data = switch_core_strdup(pool, f); switch_safe_free(f); curl_json_output_end: cJSON_Delete(top); /* should free up all children */ return data; } static size_t http_sendfile_response_callback(void *ptr, size_t size, size_t nmemb, void *data) { register unsigned int realsize = (unsigned int) (size * nmemb); http_sendfile_data_t *http_data = data; if(http_data->sendfile_response_count + realsize < HTTP_SENDFILE_RESPONSE_SIZE) { // I'm not sure why we need the (realsize+1) here, but it truncates the data by 1 char if I don't do this switch_copy_string(&http_data->sendfile_response[http_data->sendfile_response_count], ptr, (realsize+1)); http_data->sendfile_response_count += realsize; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Response page is more than %d bytes long, truncating.\n", HTTP_SENDFILE_RESPONSE_SIZE); realsize = 0; } return realsize; } // This function and do_lookup_url functions could possibly be merged together. Or at least have do_lookup_url call this up as part of the initialization routine as it is a subset of the operations. static void http_sendfile_initialize_curl(http_sendfile_data_t *http_data) { uint8_t count; http_data->curl_handle = curl_easy_init(); if (!strncasecmp(http_data->url, "https", 5)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Not verifying TLS cert for %s; connection is not secure\n", http_data->url); curl_easy_setopt(http_data->curl_handle, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(http_data->curl_handle, CURLOPT_SSL_VERIFYHOST, 0); } /* From the docs: * Optionally, you can provide data to POST using the CURLOPT_READFUNCTION and CURLOPT_READDATA * options but then you must make sure to not set CURLOPT_POSTFIELDS to anything but NULL * curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, strlen(data)); * curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, (void *) data); */ // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Post data: %s\n", data); curl_easy_setopt(http_data->curl_handle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(http_data->curl_handle, CURLOPT_MAXREDIRS, 15); curl_easy_setopt(http_data->curl_handle, CURLOPT_URL, http_data->url); curl_easy_setopt(http_data->curl_handle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(http_data->curl_handle, CURLOPT_USERAGENT, "freeswitch-curl/1.0"); http_data->sendfile_response = switch_core_alloc(http_data->pool, sizeof(char) * HTTP_SENDFILE_RESPONSE_SIZE); memset(http_data->sendfile_response, 0, sizeof(char) * HTTP_SENDFILE_RESPONSE_SIZE); // Set the function where we will copy out the response body data to curl_easy_setopt(http_data->curl_handle, CURLOPT_WRITEFUNCTION, http_sendfile_response_callback); curl_easy_setopt(http_data->curl_handle, CURLOPT_WRITEDATA, (void *) http_data); /* Add the file to upload as a POST form field */ curl_formadd(&http_data->formpost, &http_data->lastptr, CURLFORM_COPYNAME, http_data->filename_element_name, CURLFORM_FILE, http_data->filename_element, CURLFORM_END); if(!zstr(http_data->extrapost_elements)) { // Now to parse out the individual post element/value pairs char *argv[64] = { 0 }; // Probably don't need 64 but eh does it really use that much memory? uint32_t argc = 0; char *temp_extrapost = switch_core_strdup(http_data->pool, http_data->extrapost_elements); argc = switch_separate_string(temp_extrapost, '&', argv, (sizeof(argv) / sizeof(argv[0]))); for(count = 0; count < argc; count++) { char *argv2[4] = { 0 }; uint32_t argc2 = switch_separate_string(argv[count], '=', argv2, (sizeof(argv2) / sizeof(argv2[0]))); if(argc2 == 2) { switch_url_decode(argv2[0]); switch_url_decode(argv2[1]); curl_formadd(&http_data->formpost, &http_data->lastptr, CURLFORM_COPYNAME, argv2[0], CURLFORM_COPYCONTENTS, argv2[1], CURLFORM_END); } } } /* Fill in the submit field too, even if this isn't really needed */ curl_formadd(&http_data->formpost, &http_data->lastptr, CURLFORM_COPYNAME, "submit", CURLFORM_COPYCONTENTS, "or_die", CURLFORM_END); /* what URL that receives this POST */ curl_easy_setopt(http_data->curl_handle, CURLOPT_HTTPPOST, http_data->formpost); // This part actually fires off the curl, captures the HTTP response code, and then frees up the handle. curl_easy_perform(http_data->curl_handle); curl_easy_getinfo(http_data->curl_handle, CURLINFO_RESPONSE_CODE, &http_data->http_response_code); curl_easy_cleanup(http_data->curl_handle); // Clean up the form data from POST curl_formfree(http_data->formpost); } static switch_status_t http_sendfile_test_file_open(http_sendfile_data_t *http_data, switch_event_t *event) { switch_status_t retval = switch_file_open(&http_data->file_handle, http_data->filename_element, SWITCH_FOPEN_READ, SWITCH_FPROT_UREAD,http_data->pool); if(retval != SWITCH_STATUS_SUCCESS) { if(switch_test_flag(http_data, CSO_EVENT)) { if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, HTTP_SENDFILE_ACK_EVENT) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Command-Execution-Identifier", http_data->identifier_str); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Filename", http_data->filename_element); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "File-Access", "Failure"); switch_event_fire(&event); switch_event_destroy(&event); } else switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to create event to notify of failure to open file %s\n", http_data->filename_element); } if((switch_test_flag(http_data, CSO_STREAM) || switch_test_flag(http_data, CSO_NONE)) && http_data->stream) http_data->stream->write_function(http_data->stream, "-Err Unable to open file %s\n", http_data->filename_element); if(switch_test_flag(http_data, CSO_NONE) && !http_data->stream) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "curl_sendfile: Unable to open file %s\n", http_data->filename_element); } return retval; } static void http_sendfile_success_report(http_sendfile_data_t *http_data, switch_event_t *event) { if(switch_test_flag(http_data, CSO_EVENT)) { if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, HTTP_SENDFILE_ACK_EVENT) == SWITCH_STATUS_SUCCESS) { char *code_as_string = switch_core_alloc(http_data->pool, 16); memset(code_as_string, 0, 16); switch_snprintf(code_as_string, 16, "%d", http_data->http_response_code); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Command-Execution-Identifier", http_data->identifier_str); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Filename", http_data->filename_element); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "File-Access", "Success"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "REST-HTTP-Code", code_as_string); switch_event_add_body(event, "%s", http_data->sendfile_response); switch_event_fire(&event); switch_event_destroy(&event); } else switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to create a event to report on success of curl_sendfile.\n"); } if((switch_test_flag(http_data, CSO_STREAM) || switch_test_flag(http_data, CSO_NONE) || switch_test_flag(http_data, CSO_EVENT)) && http_data->stream) { if(http_data->http_response_code == 200) http_data->stream->write_function(http_data->stream, "+200 Ok\n"); else http_data->stream->write_function(http_data->stream, "-%d Err\n", http_data->http_response_code); if(http_data->sendfile_response_count && switch_test_flag(http_data, CSO_STREAM)) http_data->stream->write_function(http_data->stream, "%s\n", http_data->sendfile_response); } if(switch_test_flag(http_data, CSO_NONE) && !http_data->stream) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Sending of file %s to url %s resulted with code %lu\n", http_data->filename_element, http_data->url, http_data->http_response_code); } #define HTTP_SENDFILE_APP_SYNTAX " [nopost|postparam1=foo&postparam2=bar... [event|none [identifier ]]]" SWITCH_STANDARD_APP(http_sendfile_app_function) { switch_event_t *event = NULL; char *argv[10] = { 0 }, *argv2[10] = { 0 }; int argc = 0, argc2 = 0; http_sendfile_data_t *http_data = NULL; switch_memory_pool_t *pool = switch_core_session_get_pool(session); switch_channel_t *channel = switch_core_session_get_channel(session); assert(channel != NULL); http_data = switch_core_alloc(pool, sizeof(http_sendfile_data_t)); memset(http_data, 0, sizeof(http_sendfile_data_t)); http_data->pool = pool; // Either the parameters are provided on the data="" or else they are provided as chanvars. No mixing & matching if(!zstr(data)) { http_data->mydata = switch_core_strdup(http_data->pool, data); if ((argc = switch_separate_string(http_data->mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { uint8_t i = 0; if (argc < 2 || argc > 5) goto http_sendfile_app_usage; http_data->url = switch_core_strdup(http_data->pool, argv[i++]); switch_url_decode(argv[i]); argc2 = switch_separate_string(argv[i++], '=', argv2, (sizeof(argv2) / sizeof(argv2[0]))); if(argc2 == 2) { http_data->filename_element_name = switch_core_strdup(pool, argv2[0]); http_data->filename_element = switch_core_strdup(pool, argv2[1]); } else goto http_sendfile_app_usage; if(argc > 2) { http_data->extrapost_elements = switch_core_strdup(pool, argv[i++]); if(argc > 3) { if(!strncasecmp(argv[i++], "event", 5)) { switch_set_flag(http_data, CSO_EVENT); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Setting output to event handler.\n"); } if(argc > 4) { if(strncasecmp(argv[i], "uuid", 4)) http_data->identifier_str = switch_core_session_get_uuid(session); else http_data->identifier_str = switch_core_strdup(pool, argv[i++]); } } } } } else { char *send_output = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_report", SWITCH_TRUE, -1); char *identifier = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_identifier", SWITCH_TRUE, -1); http_data->url = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_url", SWITCH_TRUE, -1); http_data->filename_element_name = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_filename_element", SWITCH_TRUE, -1); http_data->filename_element = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_filename", SWITCH_TRUE, -1); http_data->extrapost_elements = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_extrapost", SWITCH_TRUE, -1); if(zstr(http_data->url) || zstr(http_data->filename_element) || zstr(http_data->filename_element_name)) goto http_sendfile_app_usage; if(!zstr(send_output)) { if(!strncasecmp(send_output, "event", 5)) switch_set_flag(http_data, CSO_EVENT); else if(!strncasecmp(send_output, "none", 4)) switch_set_flag(http_data, CSO_NONE); else { switch_set_flag(http_data, CSO_NONE); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Invalid parameter %s specified for curl_sendfile_report. Setting default of 'none'.\n", send_output); } } else { switch_set_flag(http_data, CSO_NONE); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "No parameter specified for curl_sendfile_report. Setting default of 'none'.\n"); } if(!zstr(identifier)) { if(!strncasecmp(identifier, "uuid", 4)) http_data->identifier_str = switch_core_session_get_uuid(session); else if(!zstr(identifier)) http_data->identifier_str = identifier; } } switch_url_decode(http_data->filename_element_name); switch_url_decode(http_data->filename_element); // We need to check the file now... if(http_sendfile_test_file_open(http_data, event) != SWITCH_STATUS_SUCCESS) goto http_sendfile_app_done; switch_file_close(http_data->file_handle); switch_url_decode(http_data->url); http_sendfile_initialize_curl(http_data); http_sendfile_success_report(http_data, event); goto http_sendfile_app_done; http_sendfile_app_usage: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failure: Usage: \nOr you can set chanvars curl_senfile_url, curl_sendfile_filename_element, curl_sendfile_filename, curl_sendfile_extrapost\n", HTTP_SENDFILE_APP_SYNTAX); http_sendfile_app_done: if (http_data->headers) { switch_curl_slist_free_all(http_data->headers); } return; } #define HTTP_SENDFILE_SYNTAX " [nopost|postparam1=foo&postparam2=bar... [event|stream|both|none [identifier ]]]" SWITCH_STANDARD_API(http_sendfile_function) { switch_status_t status = SWITCH_STATUS_FALSE; switch_bool_t new_memory_pool = SWITCH_FALSE; char *argv[10] = { 0 }, *argv2[10] = { 0 }; int argc = 0, argc2 = 0; http_sendfile_data_t *http_data = NULL; switch_memory_pool_t *pool = NULL; switch_event_t *event = NULL; if(zstr(cmd)) { status = SWITCH_STATUS_SUCCESS; goto http_sendfile_usage; } if(session) { pool = switch_core_session_get_pool(session); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "We're using a session's memory pool for curl_sendfile. Maybe we should consider always making a new memory pool?\n"); } else { switch_core_new_memory_pool(&pool); new_memory_pool = SWITCH_TRUE; // So we can properly destroy the memory pool } http_data = switch_core_alloc(pool, sizeof(http_sendfile_data_t)); memset(http_data, 0, sizeof(http_sendfile_data_t)); http_data->mydata = switch_core_strdup(pool, cmd); http_data->stream = stream; http_data->pool = pool; // stream->write_function(stream,"\ncmd is %s\nmydata is %s\n", cmd, http_data->mydata); if ((argc = switch_separate_string(http_data->mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { uint8_t i = 0; if (argc < 2 || argc > 5) { status = SWITCH_STATUS_SUCCESS; goto http_sendfile_usage; } http_data->url = switch_core_strdup(pool, argv[i++]); switch_url_decode(argv[i]); argc2 = switch_separate_string(argv[i++], '=', argv2, (sizeof(argv2) / sizeof(argv2[0]))); if(argc2 == 2) { http_data->filename_element_name = switch_core_strdup(pool, argv2[0]); http_data->filename_element = switch_core_strdup(pool, argv2[1]); } else goto http_sendfile_usage; switch_url_decode(http_data->filename_element_name); switch_url_decode(http_data->filename_element); if(argc > 2) { http_data->extrapost_elements = switch_core_strdup(pool, argv[i++]); if(argc > 3) { if(!strncasecmp(argv[i], "event", 5)) switch_set_flag(http_data, CSO_EVENT); else if(!strncasecmp(argv[i], "stream", 6)) switch_set_flag(http_data, CSO_STREAM); else if(!strncasecmp(argv[i], "both", 4)) { switch_set_flag(http_data, CSO_EVENT); switch_set_flag(http_data, CSO_STREAM); } else { if(strncasecmp(argv[i], "none", 4)) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Invalid 4th parameter set for curl_sendfile. Defaulting to \"none\"\n"); switch_set_flag(http_data, CSO_NONE); } i++; if(argc > 4) http_data->identifier_str = switch_core_strdup(pool, argv[i++]); } } } // We need to check the file now... if(http_sendfile_test_file_open(http_data, event) != SWITCH_STATUS_SUCCESS) goto http_sendfile_done; switch_file_close(http_data->file_handle); switch_url_decode(http_data->url); http_sendfile_initialize_curl(http_data); http_sendfile_success_report(http_data, event); status = SWITCH_STATUS_SUCCESS; goto http_sendfile_done; http_sendfile_usage: stream->write_function(stream, "-USAGE\n%s\n", HTTP_SENDFILE_SYNTAX); goto http_sendfile_done; http_sendfile_done: if (http_data && http_data->headers) { switch_curl_slist_free_all(http_data->headers); } if (new_memory_pool == SWITCH_TRUE) { switch_core_destroy_memory_pool(&pool); } return status; } 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 = switch_core_session_get_pool(session); switch_channel_t *channel = switch_core_session_get_channel(session); char *url = NULL; char *method = NULL; char *postdata = NULL; char *content_type = NULL; switch_bool_t do_headers = SWITCH_FALSE; switch_bool_t do_json = SWITCH_FALSE; http_data_t *http_data = NULL; switch_curl_slist_t *slist = NULL; switch_stream_handle_t stream = { 0 }; int i = 0; curl_options_t options = { 0 }; const char *curl_timeout; 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); } url = switch_core_strdup(pool, argv[0]); for (i = 1; i < argc; i++) { if (!strcasecmp("headers", argv[i])) { do_headers = SWITCH_TRUE; } else if (!strcasecmp("json", argv[i])) { do_json = SWITCH_TRUE; } else if (!strcasecmp("get", argv[i]) || !strcasecmp("head", argv[i])) { method = switch_core_strdup(pool, argv[i]); } else if (!strcasecmp("post", argv[i])) { method = "post"; if (++i < argc) { postdata = switch_core_strdup(pool, argv[i]); switch_url_decode(postdata); } else { postdata = ""; } } else if (!strcasecmp("delete", argv[i])) { method = "delete"; if (++i < argc) { postdata = switch_core_strdup(pool, argv[i]); switch_url_decode(postdata); } else { postdata = ""; } } else if (!strcasecmp("put", argv[i])) { method = "put"; if (++i < argc) { postdata = switch_core_strdup(pool, argv[i]); switch_url_decode(postdata); } else { postdata = ""; } } else if (!strcasecmp("content-type", argv[i])) { if (++i < argc) { content_type = switch_core_strdup(pool, argv[i]); } } } } curl_timeout = switch_channel_get_variable(channel, "curl_connect_timeout"); if (curl_timeout) options.connect_timeout = atoi(curl_timeout); curl_timeout = switch_channel_get_variable(channel, "curl_timeout"); if (curl_timeout) options.timeout = atoi(curl_timeout); http_data = do_lookup_url(pool, url, method, postdata, content_type, &options); if (do_json) { switch_channel_set_variable(channel, "curl_response_data", print_json(pool, http_data)); } else { SWITCH_STANDARD_STREAM(stream); if (do_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_channel_set_variable(channel, "curl_response_data", stream.data); } switch_channel_set_variable(channel, "curl_response_code", switch_core_sprintf(pool, "%ld", http_data->http_response_code)); switch_channel_set_variable(channel, "curl_method", method); switch_goto_status(SWITCH_STATUS_SUCCESS, done); usage: switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Usage: %s\n", SYNTAX); switch_goto_status(status, done); done: switch_safe_free(stream.data); if (http_data && http_data->headers) { switch_curl_slist_free_all(http_data->headers); } if (!session && pool) { 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; char *method = NULL; char *postdata = NULL; char *content_type = NULL; switch_bool_t do_headers = SWITCH_FALSE; switch_bool_t do_json = SWITCH_FALSE; switch_curl_slist_t *slist = NULL; http_data_t *http_data = NULL; int i = 0; switch_memory_pool_t *pool = NULL; curl_options_t options = { 0 }; if (zstr(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]); for (i = 1; i < argc; i++) { if (!strcasecmp("headers", argv[i])) { do_headers = SWITCH_TRUE; } else if (!strcasecmp("json", argv[i])) { do_json = SWITCH_TRUE; } else if (!strcasecmp("get", argv[i]) || !strcasecmp("head", argv[i])) { method = switch_core_strdup(pool, argv[i]); } else if (!strcasecmp("post", argv[i])) { method = "post"; if (++i < argc) { postdata = switch_core_strdup(pool, argv[i]); switch_url_decode(postdata); } else { postdata = ""; } } else if (!strcasecmp("delete", argv[i])) { method = "delete"; if (++i < argc) { postdata = switch_core_strdup(pool, argv[i]); switch_url_decode(postdata); } else { postdata = ""; } } else if (!strcasecmp("put", argv[i])) { method = "put"; if (++i < argc) { postdata = switch_core_strdup(pool, argv[i]); switch_url_decode(postdata); } else { postdata = ""; } } else if (!strcasecmp("content-type", argv[i])) { if (++i < argc) { content_type = switch_core_strdup(pool, argv[i]); } } else if (!strcasecmp("connect-timeout", argv[i])) { if (++i < argc) { int tmp = atoi(argv[i]); if (tmp > 0) { options.connect_timeout = tmp; } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid connect-timeout!\n"); } } } else if (!strcasecmp("timeout", argv[i])) { if (++i < argc) { int tmp = atoi(argv[i]); if (tmp > 0) { options.timeout = tmp; } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid timeout!\n"); } } } } http_data = do_lookup_url(pool, url, method, postdata, content_type, &options); if (do_json) { stream->write_function(stream, "%s", print_json(pool, http_data)); } else { if (do_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: if (http_data && http_data->headers) { switch_curl_slist_free_all(http_data->headers); } switch_safe_free(mydata); if (!session && pool) { 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; if (switch_event_reserve_subclass(HTTP_SENDFILE_ACK_EVENT) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", HTTP_SENDFILE_ACK_EVENT); return SWITCH_STATUS_TERM; } /* 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, SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); SWITCH_ADD_API(api_interface, "curl_sendfile", "curl_sendfile API", http_sendfile_function, HTTP_SENDFILE_SYNTAX); SWITCH_ADD_APP(app_interface, "curl_sendfile", "Send a file and some optional post variables via HTTP", "Send a file and some optional post variables via HTTP", http_sendfile_app_function, HTTP_SENDFILE_APP_SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); /* 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) { switch_event_free_subclass(HTTP_SENDFILE_ACK_EVENT); /* 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 noet */