From 0dfc0110d310dc30193ada4cc901725077f7530c Mon Sep 17 00:00:00 2001
From: Anthony Minessale <anthm@freeswitch.org>
Date: Fri, 6 Jan 2012 18:28:44 -0600
Subject: [PATCH] add record support to http format put and post (needs testing
 and debugging)

---
 src/mod/applications/mod_httapi/mod_httapi.c | 175 ++++++++++++++++---
 1 file changed, 150 insertions(+), 25 deletions(-)

diff --git a/src/mod/applications/mod_httapi/mod_httapi.c b/src/mod/applications/mod_httapi/mod_httapi.c
index 0204d9dab5..51b82a9f79 100644
--- a/src/mod/applications/mod_httapi/mod_httapi.c
+++ b/src/mod/applications/mod_httapi/mod_httapi.c
@@ -162,7 +162,16 @@ struct http_file_context {
 	switch_file_t *lock_fd;
 	switch_memory_pool_t *pool;
 	int del_on_close;
-
+	struct {
+		char *dest_url;
+		char *params;
+		char *file_name;
+		char *profile_name;
+		char *file;
+		char *method;
+		char *name;
+		char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
+	} write;
 };
 
 typedef struct http_file_context http_file_context_t;
@@ -900,7 +909,6 @@ static switch_status_t parse_record(const char *tag_name, client_t *client, swit
 	if (!zstr(tmp_record_path) && switch_file_exists(tmp_record_path, client->pool) == SWITCH_STATUS_SUCCESS) {
 		char *key = switch_core_sprintf(client->pool, "attach_file:%s:%s.%s", name, fname, ext);
 		switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, key, tmp_record_path);
-		status = SWITCH_STATUS_TERM;
 	}
 
  end:
@@ -1117,8 +1125,10 @@ static client_t *client_create(switch_core_session_t *session, const char *profi
 
 	switch_event_create(&client->headers, SWITCH_EVENT_CLONE);
 	
-	client->session = session;
-	client->channel = switch_core_session_get_channel(session);
+	if (session) {
+		client->session = session;
+		client->channel = switch_core_session_get_channel(session);
+	}
 
 	
 	client->profile = profile;
@@ -1151,12 +1161,18 @@ static void cleanup_attachments(client_t *client)
 	for (hp = client->params->headers; hp; hp = hp->next) {
 		if (!strncasecmp(hp->name, "attach_file:", 12)) {
 			if (switch_file_exists(hp->value, client->pool)) {
+				printf("DELETE %s\n", hp->value);
 				unlink(hp->value);
 			}
 		}	
 	}
 }
 
+size_t put_file_read( void *ptr, size_t size, size_t nmemb, void *userdata)
+{
+	return fread(ptr, size, nmemb, (FILE *) userdata);
+}
+
 static switch_status_t httapi_sync(client_t *client)
 								  
 {
@@ -1171,7 +1187,9 @@ static switch_status_t httapi_sync(client_t *client)
 	char *method = NULL;
 	struct curl_httppost *formpost=NULL;
 	switch_event_t *save_params = NULL;
-
+	const char *put_file;
+	FILE *fd = NULL;
+	
 	if (client->one_time_params && client->one_time_params->headers) {
 		save_params = client->params;
 		switch_event_dup(&client->params, save_params);
@@ -1182,7 +1200,7 @@ static switch_status_t httapi_sync(client_t *client)
 	}
 	
 	if (!(session_id = switch_event_get_header(client->params, "HTTAPI_SESSION_ID"))) {
-		if (!(session_id = switch_channel_get_variable(client->channel, "HTTAPI_SESSION_ID"))) {
+		if (client->channel && !(session_id = switch_channel_get_variable(client->channel, "HTTAPI_SESSION_ID"))) {
 			session_id = switch_core_session_get_uuid(client->session);
 		}
 	}
@@ -1207,7 +1225,17 @@ static switch_status_t httapi_sync(client_t *client)
 
 	dynamic_url = switch_event_expand_headers(client->params, url);
 
-	switch_curl_process_form_post_params(client->params, curl_handle, &formpost);
+	if ((put_file = switch_event_get_header(client->params, "put_file"))) {
+		if (!(fd = fopen(put_file, "rb"))) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Can't open [%s]\n", put_file);
+			put_file = NULL;
+		}
+	}
+
+	if (!put_file) {
+		switch_curl_process_form_post_params(client->params, curl_handle, &formpost);	
+		get_style_method = 1;
+	}
 
 	if (formpost) {
 		get_style_method = 1;
@@ -1259,7 +1287,12 @@ static switch_status_t httapi_sync(client_t *client)
 		switch_curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, method);
 	}
 
-	if (formpost) {
+	if (put_file) {
+		curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1L);
+		curl_easy_setopt(curl_handle, CURLOPT_READDATA, fd);
+		curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, put_file_read);
+		
+	} else if (formpost) {
 		curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
 	} else {
 		switch_curl_easy_setopt(curl_handle, CURLOPT_POST, !get_style_method);
@@ -1351,6 +1384,9 @@ static switch_status_t httapi_sync(client_t *client)
 		save_params = NULL;
 	}
 
+	if (fd) {
+		fclose(fd);
+	}
 
 	return status;
 }
@@ -2165,30 +2201,78 @@ static switch_status_t http_file_file_open(switch_file_handle_t *handle, const c
 	char *file_dup;
 	switch_status_t status;
 
-	if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This format does not support writing!\n");
-		return SWITCH_STATUS_FALSE;
-	}
-
 	context = switch_core_alloc(handle->memory_pool, sizeof(*context));
 	context->pool = handle->memory_pool;
+
+	if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
+		char *ext;
+		
+
+		context->fh.channels = handle->channels;
+		context->fh.native_rate = handle->native_rate;
+		context->fh.samples = handle->samples;
+		context->fh.samplerate = handle->samplerate;
+		context->fh.prefix = handle->prefix;
+		
+		context->write.dest_url = switch_core_sprintf(context->pool, "http://%s", path);
+
+		if ((context->write.params = strchr(context->write.dest_url, ';'))) {
+			*context->write.params++ = '\0';
+			context->write.file_name = switch_find_parameter(context->write.params, "file", context->pool);
+			context->write.profile_name = switch_find_parameter(context->write.params, "profile", context->pool);
+			context->write.method = switch_find_parameter(context->write.params, "method", context->pool);
+			context->write.name = switch_find_parameter(context->write.params, "name", context->pool);
+		}
+		
+		if (!context->write.file_name) {
+			char *p;
+			if ((p = strrchr(context->write.dest_url, '/'))) {
+				p++;
+				context->write.file_name = switch_core_strdup(context->pool, p);
+			}	
+		}
+
+		if ((ext = strrchr(context->write.file_name, '.'))) {
+			ext++;
+		} else {
+			ext = "wav";
+		}
+		
+		if (!context->write.profile_name) context->write.profile_name = "default";
+		if (!context->write.method) context->write.method = !strcasecmp(ext, "cgi") ? "post" : "put";
+		if (!context->write.name) context->write.name = "recorded_file";
+
+		switch_uuid_str(context->write.uuid_str, sizeof(context->write.uuid_str));
+		
+		context->write.file = switch_core_sprintf(context->pool, "%s%s%s_%s",
+												  SWITCH_GLOBAL_dirs.temp_dir, SWITCH_PATH_SEPARATOR, context->write.uuid_str, context->write.file_name);
+		
+
+		if (switch_core_file_open(&context->fh, context->write.file, handle->channels, handle->samplerate, handle->flags, NULL) != SWITCH_STATUS_SUCCESS) {
+			return SWITCH_STATUS_GENERR;
+		}
+
+	} else {
+
 	
-	file_dup = switch_core_sprintf(handle->memory_pool, "http://%s", path);
+		file_dup = switch_core_sprintf(handle->memory_pool, "http://%s", path);
+		
+		if ((status = locate_url_file(context, file_dup)) != SWITCH_STATUS_SUCCESS) {
+			return status;
+		}
+
+
 	
-	if ((status = locate_url_file(context, file_dup)) != SWITCH_STATUS_SUCCESS) {
-		return status;
+		if ((status = switch_core_file_open(&context->fh,
+											context->cache_file, 
+											handle->channels, 
+											handle->samplerate, 
+											SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL)) != SWITCH_STATUS_SUCCESS) {
+			return status;
+		}
 	}
 
 	handle->private_info = context;
-	
-	if ((status = switch_core_file_open(&context->fh,
-										context->cache_file, 
-										handle->channels, 
-										handle->samplerate, 
-										SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL)) != SWITCH_STATUS_SUCCESS) {
-		return status;
-	}
-
 	handle->samples = context->fh.samples;
 	handle->format = context->fh.format;
 	handle->sections = context->fh.sections;
@@ -2212,6 +2296,38 @@ static switch_status_t http_file_file_close(switch_file_handle_t *handle)
 	if (switch_test_flag((&context->fh), SWITCH_FILE_OPEN)) {
 		switch_core_file_close(&context->fh);
 	}
+
+	if (context->write.file) {
+		client_t *client;
+		switch_event_t *params;
+		char *key;
+
+		switch_event_create(&params, SWITCH_EVENT_CLONE);
+		params->flags |= EF_UNIQ_HEADERS;
+
+		if (!strcasecmp(context->write.method, "put")) {
+			switch_event_add_header(params, SWITCH_STACK_BOTTOM, "put_file", context->write.file);
+		} else {
+			key = switch_core_sprintf(context->pool, "attach_file:%s:%s", context->write.name, context->write.file_name);
+			switch_event_add_header(params, SWITCH_STACK_BOTTOM, key, context->write.file);
+		}
+
+		switch_event_add_header(params, SWITCH_STACK_BOTTOM, "url", context->write.dest_url);
+		switch_event_add_header(params, SWITCH_STACK_BOTTOM, "file_driver", "true");
+		switch_event_add_header(params, SWITCH_STACK_BOTTOM, "HTTAPI_SESSION_ID", context->write.uuid_str);
+
+		if ((client = client_create(NULL, context->write.profile_name, &params))) {
+			httapi_sync(client);
+			client_destroy(&client);
+		} else {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find suitable profile\n");
+			switch_event_destroy(&params);
+		}
+			
+		unlink(context->write.file);
+		return SWITCH_STATUS_SUCCESS;
+	}
+
 	
 	if (context->del_on_close) {
 		if (context->cache_file) {
@@ -2224,6 +2340,14 @@ static switch_status_t http_file_file_close(switch_file_handle_t *handle)
 	return SWITCH_STATUS_SUCCESS;
 }
 
+
+static switch_status_t http_file_write(switch_file_handle_t *handle, void *data, size_t *len)
+{
+	http_file_context_t *context = handle->private_info;
+	
+	return switch_core_file_write(&context->fh, data, len);
+}
+
 static switch_status_t http_file_file_read(switch_file_handle_t *handle, void *data, size_t *len)
 {
 	http_file_context_t *context = handle->private_info;
@@ -2276,6 +2400,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_httapi_load)
 	file_interface->file_open = http_file_file_open;
 	file_interface->file_close = http_file_file_close;
 	file_interface->file_read = http_file_file_read;
+	file_interface->file_write = http_file_write;
 	file_interface->file_seek = http_file_file_seek;
 	
 	switch_snprintf(globals.cache_path, sizeof(globals.cache_path), "%s%shttp_file_cache", SWITCH_GLOBAL_dirs.storage_dir, SWITCH_PATH_SEPARATOR);