]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-5048 --resolve refactored patch applied with author header
authorRaymond Chandler <intralanman@gmail.com>
Thu, 28 Feb 2013 19:32:50 +0000 (14:32 -0500)
committerRaymond Chandler <intralanman@gmail.com>
Thu, 28 Feb 2013 19:32:50 +0000 (14:32 -0500)
src/mod/applications/mod_curl/mod_curl.c

index d9bb285522469ecc732db83cca295ec975cea497..b1a5cb0393c2656bc1c5a183020a20dfe8f8c789 100644 (file)
@@ -24,6 +24,7 @@
  * Contributor(s):
  * 
  * Rupa Schomaker <rupa@rupa.com>
+ * Yossi Neiman <mishehu@freeswitch.org>
  *
  * mod_curl.c -- API for performing http queries
  *
@@ -32,6 +33,7 @@
 #include <switch.h>
 #include <switch_curl.h>
 #include <json.h>
+#include <sys/socket.h>
 
 /* Prototypes */
 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_curl_shutdown);
@@ -45,10 +47,19 @@ SWITCH_MODULE_DEFINITION(mod_curl, mod_curl_load, mod_curl_shutdown, NULL);
 
 static char *SYNTAX = "curl url [headers|json|content-type <mime-type>] [get|head|post [post_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;
@@ -61,6 +72,29 @@ struct http_data_obj {
 };
 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 callback_obj {
        switch_memory_pool_t *pool;
        char *name;
@@ -230,6 +264,426 @@ static char *print_json(switch_memory_pool_t *pool, http_data_t *http_data)
        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)
+{
+       http_data->curl_handle = curl_easy_init();
+       
+       if (!strncasecmp(http_data->url, "https", 5))
+       {
+               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(uint8_t 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)
+                               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 "<url> <filenameParamName=filepath> [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: <data=\"%s\">\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 && http_data->headers)
+       {
+               switch_curl_slist_free_all(http_data->headers);
+       }
+       
+       return;
+}
+
+#define HTTP_SENDFILE_SYNTAX "<url> <filenameParamName=filepath> [nopost|postparam1=foo&postparam2=bar... [event|stream|both|none  [identifier ]]]"
+SWITCH_STANDARD_API(http_sendfile_function)
+{
+       switch_status_t status;
+       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;
@@ -430,7 +884,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_curl_load)
        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;
 }