From a84b76be2a38770a0e489e63b192211cffba3c47 Mon Sep 17 00:00:00 2001
From: Travis Cross <tc@traviscross.com>
Date: Fri, 4 Jul 2014 01:48:13 +0000
Subject: [PATCH] Add PUT support to mod_curl `curl` api and app

With both the `curl` api command and application, you can now ask for
data to be PUT where previously it could only be sent via POST.
---
 src/mod/applications/mod_curl/mod_curl.c | 54 +++++++++++++++++++++++-
 1 file changed, 53 insertions(+), 1 deletion(-)

diff --git a/src/mod/applications/mod_curl/mod_curl.c b/src/mod/applications/mod_curl/mod_curl.c
index 8ef317d9c8..de19249c30 100644
--- a/src/mod/applications/mod_curl/mod_curl.c
+++ b/src/mod/applications/mod_curl/mod_curl.c
@@ -51,7 +51,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_curl_load);
  */
 SWITCH_MODULE_DEFINITION(mod_curl, mod_curl_load, mod_curl_shutdown, NULL);
 
-static char *SYNTAX = "curl url [headers|json|content-type <mime-type>|timeout <seconds>] [get|head|post [post_data]]";
+static char *SYNTAX = "curl url [headers|json|content-type <mime-type>|timeout <seconds>] [get|head|post|put [post/put_data]]";
 
 #define HTTP_SENDFILE_ACK_EVENT "curl_sendfile::ack"
 #define HTTP_SENDFILE_RESPONSE_SIZE 32768
@@ -101,6 +101,11 @@ struct http_sendfile_data_obj {
 
 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;
@@ -145,12 +150,28 @@ static size_t header_callback(void *ptr, size_t size, size_t nmemb, void *data)
 	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, ncur = 0;
+	if (dstream->length > nmax) {
+		ncur = nmax;
+		dstream->length -= nmax;
+	} else {
+		ncur = dstream->length;
+		dstream->length = 0;
+	}
+	memmove(ptr, dstream->data, 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));
@@ -192,6 +213,21 @@ static http_data_t *do_lookup_url(switch_memory_pool_t *pool, const char *url, c
 			switch_safe_free(ct);
 		}
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Post 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);
 	}
@@ -756,6 +792,14 @@ SWITCH_STANDARD_APP(curl_app_function)
 				} 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]);
@@ -860,6 +904,14 @@ SWITCH_STANDARD_API(curl_function)
 				} 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]);