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