1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Rupa Schomaker <rupa@rupa.com>
27  * Yossi Neiman <mishehu@freeswitch.org>
28  * Seven Du <dujinfang@gmail.com>
29  *
30  * mod_curl.c -- API for performing http queries
31  *
32  */
33 
34 #include <switch.h>
35 #include <switch_curl.h>
36 #ifdef  _MSC_VER
37 #include <WinSock2.h>
38 #else
39 #include <sys/socket.h>
40 #endif
41 
42 
43 /* Prototypes */
44 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_curl_shutdown);
45 SWITCH_MODULE_RUNTIME_FUNCTION(mod_curl_runtime);
46 SWITCH_MODULE_LOAD_FUNCTION(mod_curl_load);
47 
48 /* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
49  * Defines a switch_loadable_module_function_table_t and a static const char[] modname
50  */
51 SWITCH_MODULE_DEFINITION(mod_curl, mod_curl_load, mod_curl_shutdown, NULL);
52 
53 static char *SYNTAX = "curl url [headers|json|content-type <mime-type>|connect-timeout <seconds>|timeout <seconds>|append_headers <header_name:header_value>[|append_headers <header_name:header_value>]] [get|head|post|delete|put [data]]";
54 
55 #define HTTP_SENDFILE_ACK_EVENT "curl_sendfile::ack"
56 #define HTTP_SENDFILE_RESPONSE_SIZE 32768
57 #define HTTP_MAX_APPEND_HEADERS 10
58 #define HTTP_DEFAULT_MAX_BYTES 64000
59 
60 static struct {
61 	switch_memory_pool_t *pool;
62 	switch_event_node_t *node;
63 	int max_bytes;
64 } globals;
65 
66 static switch_xml_config_item_t instructions[] = {
67 	/* parameter name        type                 reloadable   pointer                         default value     options structure */
68 	SWITCH_CONFIG_ITEM("max-bytes", SWITCH_CONFIG_INT, CONFIG_RELOADABLE, &globals.max_bytes, (void *) HTTP_DEFAULT_MAX_BYTES, NULL,NULL, NULL),
69 	SWITCH_CONFIG_ITEM_END()
70 };
71 
72 typedef enum {
73 	CSO_NONE = (1 << 0),
74 	CSO_EVENT = (1 << 1),
75 	CSO_STREAM = (1 << 2)
76 } curlsendfile_output_t;
77 
78 struct http_data_obj {
79 	switch_stream_handle_t stream;
80 	switch_size_t bytes;
81 	switch_size_t max_bytes;
82 	switch_memory_pool_t *pool;
83 	int err;
84 	long http_response_code;
85 	char *http_response;
86 	switch_curl_slist_t *headers;
87 };
88 typedef struct http_data_obj http_data_t;
89 
90 struct http_sendfile_data_obj {
91 	switch_memory_pool_t *pool;
92 	switch_file_t *file_handle;
93 	long http_response_code;
94 	char *http_response;
95 	switch_curl_slist_t *headers;
96 	char *mydata;
97 	char *url;
98 	char *identifier_str;
99 	char *filename_element;
100 	char *filename_element_name;
101 	char *extrapost_elements;
102 	switch_CURL *curl_handle;
103 	struct curl_httppost *formpost;
104 	struct curl_httppost *lastptr;
105 	uint8_t flags; /* This is for where to send output of the curl_sendfile commands */
106 	switch_stream_handle_t *stream;
107 	char *sendfile_response;
108 	switch_size_t sendfile_response_count;
109 };
110 
111 typedef struct http_sendfile_data_obj http_sendfile_data_t;
112 
113 struct data_stream {
114 	const char *data;
115 	size_t length;
116 };
117 
118 struct callback_obj {
119 	switch_memory_pool_t *pool;
120 	char *name;
121 };
122 typedef struct callback_obj callback_t;
123 
124 struct curl_options_obj {
125 	long connect_timeout;
126 	long timeout;
127 };
128 typedef struct curl_options_obj curl_options_t;
129 
file_callback(void * ptr,size_t size,size_t nmemb,void * data)130 static size_t file_callback(void *ptr, size_t size, size_t nmemb, void *data)
131 {
132 	register unsigned int realsize = (unsigned int) (size * nmemb);
133 	http_data_t *http_data = data;
134 
135 	http_data->bytes += realsize;
136 
137 	if (http_data->bytes > http_data->max_bytes) {
138 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Oversized file detected [%d bytes]\n", (int) http_data->bytes);
139 		http_data->err = 1;
140 		return 0;
141 	}
142 
143 	http_data->stream.write_function(&http_data->stream, "%.*s", realsize, ptr);
144 	return realsize;
145 }
146 
header_callback(void * ptr,size_t size,size_t nmemb,void * data)147 static size_t header_callback(void *ptr, size_t size, size_t nmemb, void *data)
148 {
149 	register unsigned int realsize = (unsigned int) (size * nmemb);
150 	http_data_t *http_data = data;
151 	char *header = NULL;
152 
153 	header = switch_core_alloc(http_data->pool, realsize + 1);
154 	switch_copy_string(header, ptr, realsize);
155 	header[realsize] = '\0';
156 
157 	http_data->headers = switch_curl_slist_append(http_data->headers, header);
158 
159 	return realsize;
160 }
161 
read_callback(void * ptr,size_t size,size_t nmemb,void * stream)162 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
163 {
164 	struct data_stream *dstream = (struct data_stream*)stream;
165 	size_t nmax = size*nmemb;
166 	size_t ncur = (dstream->length > nmax) ? nmax : dstream->length;
167 	memmove(ptr, dstream->data, ncur);
168 	dstream->data += ncur;
169 	dstream->length -= ncur;
170 	return ncur;
171 }
172 
do_lookup_url(switch_memory_pool_t * pool,const char * url,const char * method,const char * data,const char * content_type,char * append_headers[],curl_options_t * options)173 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, char *append_headers[], curl_options_t *options)
174 {
175 	switch_CURL *curl_handle = NULL;
176 	long httpRes = 0;
177 	http_data_t *http_data = NULL;
178 	switch_curl_slist_t *headers = NULL;
179 	struct data_stream dstream = { NULL };
180 
181 	http_data = switch_core_alloc(pool, sizeof(http_data_t));
182 	memset(http_data, 0, sizeof(http_data_t));
183 	http_data->pool = pool;
184 
185 	http_data->max_bytes = globals.max_bytes;
186 	SWITCH_STANDARD_STREAM(http_data->stream);
187 
188 	if (!method) {
189 		method = "get";
190 	}
191 
192 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "method: %s, url: %s, content-type: %s\n", method, url, content_type);
193 	curl_handle = switch_curl_easy_init();
194 
195 	if (options) {
196 		if (options->connect_timeout) {
197 			switch_curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, options->connect_timeout);
198 		}
199 
200 		if (options->timeout) {
201 			switch_curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, options->timeout);
202 		}
203 	}
204 
205 	if (!strncasecmp(url, "https", 5)) {
206 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Not verifying TLS cert for %s; connection is not secure\n", url);
207 		switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
208 		switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
209 	}
210 
211 	if (append_headers) {
212 		int ah_index = 0;
213 		while(append_headers[ah_index] && *append_headers[ah_index]) {
214 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CURL append_header_%d: %s\n", ah_index, append_headers[ah_index]);
215 			headers = switch_curl_slist_append(headers, append_headers[ah_index]);
216 			ah_index++;
217 		}
218 	}
219 
220 	if (!strcasecmp(method, "head")) {
221 		switch_curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1);
222 	} else if (!strcasecmp(method, "post")) {
223 		switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, strlen(data));
224 		switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, (void *) data);
225 		if (content_type) {
226 			char *ct = switch_mprintf("Content-Type: %s", content_type);
227 			headers = switch_curl_slist_append(headers, ct);
228 			switch_safe_free(ct);
229 		}
230 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Post data: %s\n", data);
231 	} else if (!strcasecmp(method, "delete")) {
232 		switch_curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "DELETE");
233 		switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, strlen(data));
234 		switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, (void *) data);
235 		if (content_type) {
236 			char *ct = switch_mprintf("Content-Type: %s", content_type);
237 			headers = switch_curl_slist_append(headers, ct);
238 			switch_safe_free(ct);
239 		}
240 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "DELETE data: %s\n", data);
241 	} else if (!strcasecmp(method, "put")) {
242 		dstream.data = data;
243 		dstream.length = strlen(data);
244 		switch_curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1);
245 		switch_curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, read_callback);
246 		switch_curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)dstream.length);
247 		switch_curl_easy_setopt(curl_handle, CURLOPT_READDATA, (void *) &dstream);
248 		if (content_type) {
249 			char *ct = switch_mprintf("Content-Type: %s", content_type);
250 			headers = switch_curl_slist_append(headers, ct);
251 			headers = switch_curl_slist_append(headers, "Expect:");
252 			switch_safe_free(ct);
253 		}
254 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PUT data: %s\n", data);
255 	} else {
256 		switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPGET, 1);
257 	}
258 
259 	if (headers) {
260 		switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
261 	}
262 
263 	switch_curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
264 	switch_curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 15);
265 	switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
266 	switch_curl_easy_setopt(curl_handle, CURLOPT_URL, url);
267 	switch_curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
268 	switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, file_callback);
269 	switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) http_data);
270 	switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback);
271 	switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *) http_data);
272 	switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-curl/1.0");
273 
274 	switch_curl_easy_perform(curl_handle);
275 	switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes);
276 	switch_curl_easy_cleanup(curl_handle);
277 	switch_curl_slist_free_all(headers);
278 
279 	if (http_data->stream.data && !zstr((char *) http_data->stream.data) && strcmp(" ", http_data->stream.data)) {
280 
281 		http_data->http_response = switch_core_strdup(pool, http_data->stream.data);
282 	}
283 
284 	http_data->http_response_code = httpRes;
285 
286 	switch_safe_free(http_data->stream.data);
287 	return http_data;
288 }
289 
290 
print_json(switch_memory_pool_t * pool,http_data_t * http_data)291 static char *print_json(switch_memory_pool_t *pool, http_data_t *http_data)
292 {
293 	cJSON *top = cJSON_CreateObject(),
294 	      *headers = cJSON_CreateArray();
295 	char *data = NULL;
296 	char tmp[32], *f = NULL;
297 	switch_curl_slist_t *header = http_data->headers;
298 
299 	if(!top || !headers) {
300 		cJSON_Delete(headers);
301 
302 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to alloc memory for cJSON structures.\n");
303 		goto curl_json_output_end;
304 	}
305 
306 	switch_snprintf(tmp, sizeof(tmp), "%ld", http_data->http_response_code);
307 	cJSON_AddItemToObject(top, "status_code", cJSON_CreateString(tmp));
308 	if (http_data->http_response) {
309 		cJSON_AddItemToObject(top, "body", cJSON_CreateString(http_data->http_response));
310 	}
311 
312 	/* parse header data */
313 	while (header) {
314 		cJSON *obj = NULL;
315 		/* remove trailing \r */
316 		if ((data =  strrchr(header->data, '\r'))) {
317 			*data = '\0';
318 		}
319 
320 		if (zstr(header->data)) {
321 			header = header->next;
322 			continue;
323 		}
324 
325 		if ((data = strchr(header->data, ':'))) {
326 			*data = '\0';
327 			data++;
328 			while (*data == ' ' && *data != '\0') {
329 				data++;
330 			}
331 			obj = cJSON_CreateObject();
332 			cJSON_AddItemToObject(obj, "key", cJSON_CreateString(header->data));
333 			cJSON_AddItemToObject(obj, "value", cJSON_CreateString(data));
334 			cJSON_AddItemToArray(headers, obj);
335 		} else {
336 			if (!strncmp("HTTP", header->data, 4)) {
337 				char *argv[3] = { 0 };
338 				int argc;
339 				if ((argc = switch_separate_string(header->data, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
340 					if (argc > 2) {
341 						cJSON_AddItemToObject(top, "version", cJSON_CreateString(argv[0]));
342 						cJSON_AddItemToObject(top, "phrase", cJSON_CreateString(argv[2]));
343 					} else {
344 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unparsable header: argc: %d\n", argc);
345 					}
346 				} else {
347 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Starts with HTTP but not parsable: %s\n", header->data);
348 				}
349 			} else {
350 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unparsable header: %s\n", header->data);
351 			}
352 		}
353 		header = header->next;
354 	}
355 	cJSON_AddItemToObject(top, "headers", headers);
356 	f = cJSON_PrintUnformatted(top);
357 	data = switch_core_strdup(pool, f);
358 	switch_safe_free(f);
359 
360 curl_json_output_end:
361 	cJSON_Delete(top);		/* should free up all children */
362 	return data;
363 }
364 
http_sendfile_response_callback(void * ptr,size_t size,size_t nmemb,void * data)365 static size_t http_sendfile_response_callback(void *ptr, size_t size, size_t nmemb, void *data)
366 {
367 	register unsigned int realsize = (unsigned int) (size * nmemb);
368 	http_sendfile_data_t *http_data = data;
369 
370 	if(http_data->sendfile_response_count + realsize < HTTP_SENDFILE_RESPONSE_SIZE)
371 	{
372 		// 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
373 		switch_copy_string(&http_data->sendfile_response[http_data->sendfile_response_count], ptr, (realsize+1));
374 		http_data->sendfile_response_count += realsize;
375 	}
376 	else
377 	{
378 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Response page is more than %d bytes long, truncating.\n", HTTP_SENDFILE_RESPONSE_SIZE);
379 		realsize = 0;
380 	}
381 
382 	return realsize;
383 }
384 
385 // 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.
http_sendfile_initialize_curl(http_sendfile_data_t * http_data)386 static void http_sendfile_initialize_curl(http_sendfile_data_t *http_data)
387 {
388 	uint8_t count;
389 	http_data->curl_handle = curl_easy_init();
390 
391 	if (!strncasecmp(http_data->url, "https", 5))
392 	{
393 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Not verifying TLS cert for %s; connection is not secure\n", http_data->url);
394 		curl_easy_setopt(http_data->curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
395 		curl_easy_setopt(http_data->curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
396 	}
397 
398 	/* From the docs:
399 	 * Optionally, you can provide data to POST using the CURLOPT_READFUNCTION and CURLOPT_READDATA
400 	 * options but then you must make sure to not set CURLOPT_POSTFIELDS to anything but NULL
401 	 * curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, strlen(data));
402 	 * curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, (void *) data);
403 	 */
404 
405 	// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Post data: %s\n", data);
406 
407 	curl_easy_setopt(http_data->curl_handle, CURLOPT_FOLLOWLOCATION, 1);
408 	curl_easy_setopt(http_data->curl_handle, CURLOPT_MAXREDIRS, 15);
409 	curl_easy_setopt(http_data->curl_handle, CURLOPT_URL, http_data->url);
410 	curl_easy_setopt(http_data->curl_handle, CURLOPT_NOSIGNAL, 1);
411 	curl_easy_setopt(http_data->curl_handle, CURLOPT_USERAGENT, "freeswitch-curl/1.0");
412 
413 	http_data->sendfile_response = switch_core_alloc(http_data->pool, sizeof(char) * HTTP_SENDFILE_RESPONSE_SIZE);
414 	memset(http_data->sendfile_response, 0, sizeof(char) * HTTP_SENDFILE_RESPONSE_SIZE);
415 
416 	// Set the function where we will copy out the response body data to
417 	curl_easy_setopt(http_data->curl_handle, CURLOPT_WRITEFUNCTION, http_sendfile_response_callback);
418 	curl_easy_setopt(http_data->curl_handle, CURLOPT_WRITEDATA, (void *) http_data);
419 
420 	/* Add the file to upload as a POST form field */
421 	curl_formadd(&http_data->formpost, &http_data->lastptr, CURLFORM_COPYNAME, http_data->filename_element_name, CURLFORM_FILE, http_data->filename_element, CURLFORM_END);
422 
423 	if(!zstr(http_data->extrapost_elements))
424 	{
425 		// Now to parse out the individual post element/value pairs
426 		char *argv[64] = { 0 }; // Probably don't need 64 but eh does it really use that much memory?
427 		uint32_t argc = 0;
428 		char *temp_extrapost = switch_core_strdup(http_data->pool, http_data->extrapost_elements);
429 
430 		argc = switch_separate_string(temp_extrapost, '&', argv, (sizeof(argv) / sizeof(argv[0])));
431 
432 		for(count = 0; count < argc; count++)
433 		{
434 			char *argv2[4] = { 0 };
435 			uint32_t argc2 = switch_separate_string(argv[count], '=', argv2, (sizeof(argv2) / sizeof(argv2[0])));
436 
437 			if(argc2 == 2) {
438 				switch_url_decode(argv2[0]);
439 				switch_url_decode(argv2[1]);
440 				curl_formadd(&http_data->formpost, &http_data->lastptr, CURLFORM_COPYNAME, argv2[0], CURLFORM_COPYCONTENTS, argv2[1], CURLFORM_END);
441 			}
442 		}
443 	}
444 
445 	/* Fill in the submit field too, even if this isn't really needed */
446 	curl_formadd(&http_data->formpost, &http_data->lastptr, CURLFORM_COPYNAME, "submit", CURLFORM_COPYCONTENTS, "or_die", CURLFORM_END);
447 
448 	/* what URL that receives this POST */
449 	curl_easy_setopt(http_data->curl_handle, CURLOPT_HTTPPOST, http_data->formpost);
450 
451 	// This part actually fires off the curl, captures the HTTP response code, and then frees up the handle.
452 	curl_easy_perform(http_data->curl_handle);
453 	curl_easy_getinfo(http_data->curl_handle, CURLINFO_RESPONSE_CODE, &http_data->http_response_code);
454 
455 	curl_easy_cleanup(http_data->curl_handle);
456 
457 	// Clean up the form data from POST
458 	curl_formfree(http_data->formpost);
459 }
460 
http_sendfile_test_file_open(http_sendfile_data_t * http_data,switch_event_t * event)461 static switch_status_t http_sendfile_test_file_open(http_sendfile_data_t *http_data, switch_event_t *event)
462 {
463 	switch_status_t retval = switch_file_open(&http_data->file_handle, http_data->filename_element, SWITCH_FOPEN_READ, SWITCH_FPROT_UREAD,http_data->pool);
464 	if(retval != SWITCH_STATUS_SUCCESS)
465 	{
466 		if(switch_test_flag(http_data, CSO_EVENT))
467 		{
468 			if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, HTTP_SENDFILE_ACK_EVENT) == SWITCH_STATUS_SUCCESS)
469 			{
470 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Command-Execution-Identifier", http_data->identifier_str);
471 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Filename", http_data->filename_element);
472 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "File-Access", "Failure");
473 				switch_event_fire(&event);
474 				switch_event_destroy(&event);
475 			}
476 			else
477 				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);
478 		}
479 
480 		if((switch_test_flag(http_data, CSO_STREAM) || switch_test_flag(http_data, CSO_NONE)) && http_data->stream)
481 			http_data->stream->write_function(http_data->stream, "-Err Unable to open file %s\n", http_data->filename_element);
482 
483 		if(switch_test_flag(http_data, CSO_NONE) && !http_data->stream)
484 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "curl_sendfile: Unable to open file %s\n", http_data->filename_element);
485 	}
486 
487 	return retval;
488 }
489 
http_sendfile_success_report(http_sendfile_data_t * http_data,switch_event_t * event)490 static void http_sendfile_success_report(http_sendfile_data_t *http_data, switch_event_t *event)
491 {
492 	if(switch_test_flag(http_data, CSO_EVENT))
493 	{
494 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, HTTP_SENDFILE_ACK_EVENT) == SWITCH_STATUS_SUCCESS)
495 		{
496 			char *code_as_string = switch_core_alloc(http_data->pool, 16);
497 			memset(code_as_string, 0, 16);
498 			switch_snprintf(code_as_string, 16, "%d", http_data->http_response_code);
499 
500 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Command-Execution-Identifier", http_data->identifier_str);
501 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Filename", http_data->filename_element);
502 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "File-Access", "Success");
503 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "REST-HTTP-Code", code_as_string);
504 			switch_event_add_body(event, "%s", http_data->sendfile_response);
505 
506 			switch_event_fire(&event);
507 			switch_event_destroy(&event);
508 		}
509 		else
510 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to create a event to report on success of curl_sendfile.\n");
511 	}
512 
513 	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)
514 	{
515 		if(http_data->http_response_code == 200)
516 			http_data->stream->write_function(http_data->stream, "+200 Ok\n");
517 		else
518 			http_data->stream->write_function(http_data->stream, "-%d Err\n", http_data->http_response_code);
519 
520 		if(http_data->sendfile_response_count && switch_test_flag(http_data, CSO_STREAM))
521 			http_data->stream->write_function(http_data->stream, "%s\n", http_data->sendfile_response);
522 	}
523 
524 	if(switch_test_flag(http_data, CSO_NONE) && !http_data->stream)
525 		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);
526 }
527 
528 #define HTTP_SENDFILE_APP_SYNTAX "<url> <filenameParamName=filepath> [nopost|postparam1=foo&postparam2=bar... [event|none  [identifier ]]]"
SWITCH_STANDARD_APP(http_sendfile_app_function)529 SWITCH_STANDARD_APP(http_sendfile_app_function)
530 {
531 	switch_event_t *event = NULL;
532 	char *argv[10] = { 0 }, *argv2[10] = { 0 };
533 	int argc = 0, argc2 = 0;
534 	http_sendfile_data_t *http_data = NULL;
535 	switch_memory_pool_t *pool = switch_core_session_get_pool(session);
536 	switch_channel_t *channel = switch_core_session_get_channel(session);
537 
538 	assert(channel != NULL);
539 
540 	http_data = switch_core_alloc(pool, sizeof(http_sendfile_data_t));
541 	memset(http_data, 0, sizeof(http_sendfile_data_t));
542 
543 	http_data->pool = pool;
544 
545 	// Either the parameters are provided on the data="" or else they are provided as chanvars.  No mixing & matching
546 	if(!zstr(data))
547 	{
548 		http_data->mydata = switch_core_strdup(http_data->pool, data);
549 
550 		if ((argc = switch_separate_string(http_data->mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])))))
551 		{
552 			uint8_t i = 0;
553 
554 			if (argc < 2 || argc > 5)
555 				goto http_sendfile_app_usage;
556 
557 			http_data->url = switch_core_strdup(http_data->pool, argv[i++]);
558 
559 			switch_url_decode(argv[i]);
560 			argc2 = switch_separate_string(argv[i++], '=', argv2, (sizeof(argv2) / sizeof(argv2[0])));
561 
562 			if(argc2 == 2)
563 			{
564 				http_data->filename_element_name = switch_core_strdup(pool, argv2[0]);
565 				http_data->filename_element = switch_core_strdup(pool, argv2[1]);
566 			}
567 			else
568 				goto http_sendfile_app_usage;
569 
570 			if(argc > 2)
571 			{
572 				http_data->extrapost_elements = switch_core_strdup(pool, argv[i++]);
573 
574 				if(argc > 3)
575 				{
576 					if(!strncasecmp(argv[i++], "event", 5))
577 					{
578 						switch_set_flag(http_data, CSO_EVENT);
579 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Setting output to event handler.\n");
580 					}
581 
582 					if(argc > 4)
583 					{
584 						if(strncasecmp(argv[i], "uuid", 4))
585 							http_data->identifier_str = switch_core_session_get_uuid(session);
586 						else
587 							http_data->identifier_str = switch_core_strdup(pool, argv[i++]);
588 					}
589 				}
590 			}
591 		}
592 	}
593 	else
594 	{
595 		char *send_output = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_report", SWITCH_TRUE, -1);
596 		char *identifier = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_identifier", SWITCH_TRUE, -1);
597 
598 		http_data->url = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_url", SWITCH_TRUE, -1);
599 		http_data->filename_element_name = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_filename_element", SWITCH_TRUE, -1);
600 		http_data->filename_element = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_filename", SWITCH_TRUE, -1);
601 		http_data->extrapost_elements = (char *) switch_channel_get_variable_dup(channel, "curl_sendfile_extrapost", SWITCH_TRUE, -1);
602 
603 
604 		if(zstr(http_data->url) || zstr(http_data->filename_element) || zstr(http_data->filename_element_name))
605 			goto http_sendfile_app_usage;
606 
607 		if(!zstr(send_output))
608 		{
609 			if(!strncasecmp(send_output, "event", 5))
610 				switch_set_flag(http_data, CSO_EVENT);
611 			else if(!strncasecmp(send_output, "none", 4))
612 				switch_set_flag(http_data, CSO_NONE);
613 			else
614 			{
615 				switch_set_flag(http_data, CSO_NONE);
616 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Invalid parameter %s specified for curl_sendfile_report.  Setting default of 'none'.\n", send_output);
617 			}
618 		}
619 		else
620 		{
621 			switch_set_flag(http_data, CSO_NONE);
622 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "No parameter specified for curl_sendfile_report.  Setting default of 'none'.\n");
623 		}
624 
625 		if(!zstr(identifier))
626 		{
627 			if(!strncasecmp(identifier, "uuid", 4))
628 				http_data->identifier_str = switch_core_session_get_uuid(session);
629 			else if(!zstr(identifier))
630 				http_data->identifier_str = identifier;
631 		}
632 	}
633 
634 	switch_url_decode(http_data->filename_element_name);
635 	switch_url_decode(http_data->filename_element);
636 
637 	// We need to check the file now...
638 	if(http_sendfile_test_file_open(http_data, event) != SWITCH_STATUS_SUCCESS)
639 		goto http_sendfile_app_done;
640 
641 	switch_file_close(http_data->file_handle);
642 
643 	switch_url_decode(http_data->url);
644 
645 	http_sendfile_initialize_curl(http_data);
646 
647 	http_sendfile_success_report(http_data, event);
648 
649 	goto http_sendfile_app_done;
650 
651 http_sendfile_app_usage:
652 	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);
653 
654 http_sendfile_app_done:
655 	if (http_data->headers)
656 	{
657 		switch_curl_slist_free_all(http_data->headers);
658 	}
659 
660 	return;
661 }
662 
663 #define HTTP_SENDFILE_SYNTAX "<url> <filenameParamName=filepath> [nopost|postparam1=foo&postparam2=bar... [event|stream|both|none  [identifier ]]]"
SWITCH_STANDARD_API(http_sendfile_function)664 SWITCH_STANDARD_API(http_sendfile_function)
665 {
666 	switch_status_t status = SWITCH_STATUS_FALSE;
667 	switch_bool_t new_memory_pool = SWITCH_FALSE;
668 	char *argv[10] = { 0 }, *argv2[10] = { 0 };
669 	int argc = 0, argc2 = 0;
670 	http_sendfile_data_t *http_data = NULL;
671 	switch_memory_pool_t *pool = NULL;
672 	switch_event_t *event = NULL;
673 
674 	if(zstr(cmd))
675 	{
676 		status = SWITCH_STATUS_SUCCESS;
677 		goto http_sendfile_usage;
678 	}
679 	if(session)
680 	{
681 		pool = switch_core_session_get_pool(session);
682 		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");
683 	}
684 	else
685 	{
686 		switch_core_new_memory_pool(&pool);
687 		new_memory_pool = SWITCH_TRUE;  // So we can properly destroy the memory pool
688 	}
689 
690 	http_data = switch_core_alloc(pool, sizeof(http_sendfile_data_t));
691 	memset(http_data, 0, sizeof(http_sendfile_data_t));
692 
693 	http_data->mydata = switch_core_strdup(pool, cmd);
694 	http_data->stream = stream;
695 	http_data->pool = pool;
696 
697 	// stream->write_function(stream,"\ncmd is %s\nmydata is %s\n", cmd, http_data->mydata);
698 
699 	if ((argc = switch_separate_string(http_data->mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])))))
700 	{
701 		uint8_t i = 0;
702 
703 		if (argc < 2 || argc > 5)
704 		{
705 			status = SWITCH_STATUS_SUCCESS;
706 			goto http_sendfile_usage;
707 		}
708 
709 		http_data->url = switch_core_strdup(pool, argv[i++]);
710 
711 		switch_url_decode(argv[i]);
712 		argc2 = switch_separate_string(argv[i++], '=', argv2, (sizeof(argv2) / sizeof(argv2[0])));
713 
714 		if(argc2 == 2)
715 		{
716 			http_data->filename_element_name = switch_core_strdup(pool, argv2[0]);
717 			http_data->filename_element = switch_core_strdup(pool, argv2[1]);
718 		}
719 		else
720 			goto http_sendfile_usage;
721 
722 		switch_url_decode(http_data->filename_element_name);
723 		switch_url_decode(http_data->filename_element);
724 
725 		if(argc > 2)
726 		{
727 			http_data->extrapost_elements = switch_core_strdup(pool, argv[i++]);
728 
729 			if(argc > 3)
730 			{
731 				if(!strncasecmp(argv[i], "event", 5))
732 					switch_set_flag(http_data, CSO_EVENT);
733 				else if(!strncasecmp(argv[i], "stream", 6))
734 					switch_set_flag(http_data, CSO_STREAM);
735 				else if(!strncasecmp(argv[i], "both", 4))
736 				{
737 					switch_set_flag(http_data, CSO_EVENT);
738 					switch_set_flag(http_data, CSO_STREAM);
739 				}
740 				else
741 				{
742 					if(strncasecmp(argv[i], "none", 4))
743 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Invalid 4th parameter set for curl_sendfile.  Defaulting to \"none\"\n");
744 
745 					switch_set_flag(http_data, CSO_NONE);
746 				}
747 
748 				i++;
749 
750 				if(argc > 4)
751 					http_data->identifier_str = switch_core_strdup(pool, argv[i++]);
752 			}
753 		}
754 	}
755 
756 	// We need to check the file now...
757 	if(http_sendfile_test_file_open(http_data, event) != SWITCH_STATUS_SUCCESS)
758 		goto http_sendfile_done;
759 
760 
761 	switch_file_close(http_data->file_handle);
762 
763 	switch_url_decode(http_data->url);
764 
765 	http_sendfile_initialize_curl(http_data);
766 
767 	http_sendfile_success_report(http_data, event);
768 
769 	status = SWITCH_STATUS_SUCCESS;
770 	goto http_sendfile_done;
771 
772 http_sendfile_usage:
773 	stream->write_function(stream, "-USAGE\n%s\n", HTTP_SENDFILE_SYNTAX);
774 	goto http_sendfile_done;
775 
776 http_sendfile_done:
777 	if (http_data && http_data->headers)
778 	{
779 		switch_curl_slist_free_all(http_data->headers);
780 	}
781 
782 	if (new_memory_pool == SWITCH_TRUE)
783 	{
784 		switch_core_destroy_memory_pool(&pool);
785 	}
786 
787 	return status;
788 }
789 
SWITCH_STANDARD_APP(curl_app_function)790 SWITCH_STANDARD_APP(curl_app_function)
791 {
792 	switch_status_t status = SWITCH_STATUS_SUCCESS;
793 
794 	char *argv[10] = { 0 };
795 	int argc;
796 	char *mydata = NULL;
797 
798 	switch_memory_pool_t *pool = switch_core_session_get_pool(session);
799 	switch_channel_t *channel = switch_core_session_get_channel(session);
800 	char *url = NULL;
801 	char *method = NULL;
802 	char *postdata = NULL;
803 	char *content_type = NULL;
804 	switch_bool_t do_headers = SWITCH_FALSE;
805 	switch_bool_t do_json = SWITCH_FALSE;
806 	http_data_t *http_data = NULL;
807 	switch_curl_slist_t *slist = NULL;
808 	switch_stream_handle_t stream = { 0 };
809 	int i = 0;
810 	curl_options_t options = { 0 };
811 	const char *curl_timeout;
812 	char *append_headers[HTTP_MAX_APPEND_HEADERS + 1] = { 0 };
813 	int ah_index = 0;
814 
815 	if (!(mydata = switch_core_session_strdup(session, data))) {
816 		return;
817 	}
818 
819 	if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
820 		if (argc == 0) {
821 			switch_goto_status(SWITCH_STATUS_SUCCESS, usage);
822 		}
823 
824 		url = switch_core_strdup(pool, argv[0]);
825 
826 		for (i = 1; i < argc; i++) {
827 			if (!strcasecmp("headers", argv[i])) {
828 				do_headers = SWITCH_TRUE;
829 			} else if (!strcasecmp("json", argv[i])) {
830 				do_json = SWITCH_TRUE;
831 			} else if (!strcasecmp("get", argv[i]) || !strcasecmp("head", argv[i])) {
832 				method = switch_core_strdup(pool, argv[i]);
833 			} else if (!strcasecmp("post", argv[i])) {
834 				method = "post";
835 				if (++i < argc) {
836 					postdata = switch_core_strdup(pool, argv[i]);
837 					switch_url_decode(postdata);
838 				} else {
839 					postdata = "";
840 				}
841 			} else if (!strcasecmp("delete", argv[i])) {
842 				method = "delete";
843 				if (++i < argc) {
844 					postdata = switch_core_strdup(pool, argv[i]);
845 					switch_url_decode(postdata);
846 				} else {
847 					postdata = "";
848 				}
849 			} else if (!strcasecmp("put", argv[i])) {
850 				method = "put";
851 				if (++i < argc) {
852 					postdata = switch_core_strdup(pool, argv[i]);
853 					switch_url_decode(postdata);
854 				} else {
855 					postdata = "";
856 				}
857 			} else if (!strcasecmp("content-type", argv[i])) {
858 				if (++i < argc) {
859 					content_type = switch_core_strdup(pool, argv[i]);
860 				}
861 			} else if (!strcasecmp("append_headers", argv[i])) {
862 				if (++i < argc) {
863 					if (ah_index == HTTP_MAX_APPEND_HEADERS) continue;
864 					append_headers[ah_index++] = argv[i];
865 				}
866 			}
867 		}
868 	}
869 
870 	curl_timeout = switch_channel_get_variable(channel, "curl_connect_timeout");
871 
872 	if (curl_timeout) options.connect_timeout = atoi(curl_timeout);
873 
874 	curl_timeout = switch_channel_get_variable(channel, "curl_timeout");
875 
876 	if (curl_timeout) options.timeout = atoi(curl_timeout);
877 
878 
879 	http_data = do_lookup_url(pool, url, method, postdata, content_type, append_headers, &options);
880 
881 	if (do_json) {
882 		switch_channel_set_variable(channel, "curl_response_data", print_json(pool, http_data));
883 	} else {
884 		SWITCH_STANDARD_STREAM(stream);
885 		if (do_headers) {
886 			slist = http_data->headers;
887 			while (slist) {
888 				stream.write_function(&stream, "%s\n", slist->data);
889 				slist = slist->next;
890 			}
891 			stream.write_function(&stream, "\n");
892 		}
893 		stream.write_function(&stream, "%s", http_data->http_response ? http_data->http_response : "");
894 		switch_channel_set_variable(channel, "curl_response_data", stream.data);
895 	}
896 	switch_channel_set_variable(channel, "curl_response_code", switch_core_sprintf(pool, "%ld", http_data->http_response_code));
897 	switch_channel_set_variable(channel, "curl_method", method);
898 
899 	switch_goto_status(SWITCH_STATUS_SUCCESS, done);
900 
901   usage:
902 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Usage: %s\n", SYNTAX);
903 	switch_goto_status(status, done);
904 
905   done:
906 	switch_safe_free(stream.data);
907 	if (http_data && http_data->headers) {
908 		switch_curl_slist_free_all(http_data->headers);
909 	}
910 	if (!session && pool) {
911 		switch_core_destroy_memory_pool(&pool);
912 	}
913 }
914 
SWITCH_STANDARD_API(curl_function)915 SWITCH_STANDARD_API(curl_function)
916 {
917 	switch_status_t status;
918 	char *argv[10] = { 0 };
919 	int argc;
920 	char *mydata = NULL;
921 	char *url = NULL;
922 	char *method = NULL;
923 	char *postdata = NULL;
924 	char *content_type = NULL;
925 	switch_bool_t do_headers = SWITCH_FALSE;
926 	switch_bool_t do_json = SWITCH_FALSE;
927 	switch_curl_slist_t *slist = NULL;
928 	http_data_t *http_data = NULL;
929 	int i = 0;
930 	char *append_headers[HTTP_MAX_APPEND_HEADERS + 1] = { 0 };
931 	int ah_index = 0;
932 
933 	switch_memory_pool_t *pool = NULL;
934 	curl_options_t options = { 0 };
935 
936 	if (zstr(cmd)) {
937 		switch_goto_status(SWITCH_STATUS_SUCCESS, usage);
938 	}
939 
940 	if (session) {
941 		pool = switch_core_session_get_pool(session);
942 	} else {
943 		switch_core_new_memory_pool(&pool);
944 	}
945 
946 	mydata = strdup(cmd);
947 	if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
948 		if (argc < 1) {
949 			switch_goto_status(SWITCH_STATUS_SUCCESS, usage);
950 		}
951 
952 		url = switch_core_strdup(pool, argv[0]);
953 
954 		for (i = 1; i < argc; i++) {
955 			if (!strcasecmp("headers", argv[i])) {
956 				do_headers = SWITCH_TRUE;
957 			} else if (!strcasecmp("json", argv[i])) {
958 				do_json = SWITCH_TRUE;
959 			} else if (!strcasecmp("get", argv[i]) || !strcasecmp("head", argv[i])) {
960 				method = switch_core_strdup(pool, argv[i]);
961 			} else if (!strcasecmp("post", argv[i])) {
962 				method = "post";
963 				if (++i < argc) {
964 					postdata = switch_core_strdup(pool, argv[i]);
965 					switch_url_decode(postdata);
966 				} else {
967 					postdata = "";
968 				}
969 			} else if (!strcasecmp("delete", argv[i])) {
970 				method = "delete";
971 				if (++i < argc) {
972 					postdata = switch_core_strdup(pool, argv[i]);
973 					switch_url_decode(postdata);
974 				} else {
975 					postdata = "";
976 				}
977 			} else if (!strcasecmp("put", argv[i])) {
978 				method = "put";
979 				if (++i < argc) {
980 					postdata = switch_core_strdup(pool, argv[i]);
981 					switch_url_decode(postdata);
982 				} else {
983 					postdata = "";
984 				}
985 			} else if (!strcasecmp("content-type", argv[i])) {
986 				if (++i < argc) {
987 					content_type = switch_core_strdup(pool, argv[i]);
988 				}
989 			} else if (!strcasecmp("connect-timeout", argv[i])) {
990 				if (++i < argc) {
991 					int tmp = atoi(argv[i]);
992 
993 					if (tmp > 0) {
994 						options.connect_timeout = tmp;
995 					} else {
996 						switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid connect-timeout!\n");
997 					}
998 				}
999 			} else if (!strcasecmp("timeout", argv[i])) {
1000 				if (++i < argc) {
1001 					int tmp = atoi(argv[i]);
1002 
1003 					if (tmp > 0) {
1004 						options.timeout = tmp;
1005 					} else {
1006 						switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid timeout!\n");
1007 					}
1008 				}
1009 			} else if (!strcasecmp("append_headers", argv[i])) {
1010 				if (++i < argc) {
1011 					if (ah_index == HTTP_MAX_APPEND_HEADERS) continue;
1012 					append_headers[ah_index++] = argv[i];
1013 				}
1014 			}
1015 		}
1016 
1017 		http_data = do_lookup_url(pool, url, method, postdata, content_type, append_headers, &options);
1018 		if (do_json) {
1019 			stream->write_function(stream, "%s", print_json(pool, http_data));
1020 		} else {
1021 			if (do_headers) {
1022 				slist = http_data->headers;
1023 				while (slist) {
1024 					stream->write_function(stream, "%s\n", slist->data);
1025 					slist = slist->next;
1026 				}
1027 				stream->write_function(stream, "\n");
1028 			}
1029 			stream->write_function(stream, "%s", http_data->http_response ? http_data->http_response : "");
1030 		}
1031 	}
1032 	switch_goto_status(SWITCH_STATUS_SUCCESS, done);
1033 
1034   usage:
1035 	stream->write_function(stream, "-ERR\n%s\n", SYNTAX);
1036 	switch_goto_status(status, done);
1037 
1038   done:
1039 	if (http_data && http_data->headers) {
1040 		switch_curl_slist_free_all(http_data->headers);
1041 	}
1042 	switch_safe_free(mydata);
1043 	if (!session && pool) {
1044 		switch_core_destroy_memory_pool(&pool);
1045 	}
1046 	return status;
1047 }
1048 
do_config(switch_bool_t reload)1049 static void do_config(switch_bool_t reload)
1050 {
1051 	globals.max_bytes = HTTP_DEFAULT_MAX_BYTES;
1052 
1053 	switch_xml_config_parse_module_settings("curl.conf", reload, instructions);
1054 }
1055 
event_handler(switch_event_t * event)1056 static void event_handler(switch_event_t *event)
1057 {
1058 	do_config(SWITCH_TRUE);
1059 }
1060 
1061 /* 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)1062 SWITCH_MODULE_LOAD_FUNCTION(mod_curl_load)
1063 {
1064 	switch_api_interface_t *api_interface;
1065 	switch_application_interface_t *app_interface;
1066 
1067 	memset(&globals, 0, sizeof(globals));
1068 
1069 	if (switch_event_reserve_subclass(HTTP_SENDFILE_ACK_EVENT) != SWITCH_STATUS_SUCCESS) {
1070 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", HTTP_SENDFILE_ACK_EVENT);
1071 		return SWITCH_STATUS_TERM;
1072 	}
1073 
1074 	if ((switch_event_bind_removable(modname, SWITCH_EVENT_RELOADXML, NULL, event_handler, NULL, &globals.node) != SWITCH_STATUS_SUCCESS)) {
1075 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind event!\n");
1076 		switch_event_free_subclass(HTTP_SENDFILE_ACK_EVENT);
1077 		return SWITCH_STATUS_TERM;
1078 	}
1079 
1080 	/* connect my internal structure to the blank pointer passed to me */
1081 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
1082 
1083 	globals.pool = pool;
1084 
1085 	do_config(SWITCH_FALSE);
1086 
1087 	SWITCH_ADD_API(api_interface, "curl", "curl API", curl_function, SYNTAX);
1088 	SWITCH_ADD_APP(app_interface, "curl", "Perform a http request", "Perform a http request",
1089 				   curl_app_function, SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
1090 
1091 	SWITCH_ADD_API(api_interface, "curl_sendfile", "curl_sendfile API", http_sendfile_function, HTTP_SENDFILE_SYNTAX);
1092 	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",
1093 				   http_sendfile_app_function, HTTP_SENDFILE_APP_SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
1094 	/* indicate that the module should continue to be loaded */
1095 	return SWITCH_STATUS_SUCCESS;
1096 }
1097 
1098 /*
1099   Called when the system shuts down
1100   Macro expands to: switch_status_t mod_cidlookup_shutdown() */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_curl_shutdown)1101 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_curl_shutdown)
1102 {
1103 
1104 	switch_event_free_subclass(HTTP_SENDFILE_ACK_EVENT);
1105 
1106 	switch_xml_config_cleanup(instructions);
1107 
1108 	switch_event_unbind(&globals.node);
1109 
1110 	/* Cleanup dynamically allocated config settings */
1111 	return SWITCH_STATUS_SUCCESS;
1112 }
1113 
1114 /* For Emacs:
1115  * Local Variables:
1116  * mode:c
1117  * indent-tabs-mode:t
1118  * tab-width:4
1119  * c-basic-offset:4
1120  * End:
1121  * For VIM:
1122  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
1123  */
1124