diff --git a/src/mod/applications/mod_httapi/mod_httapi.c b/src/mod/applications/mod_httapi/mod_httapi.c index 0204d9dab5..0242b8081c 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; @@ -250,6 +259,12 @@ static void console_clean_log(const char *level_str, const char *msg) switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, level, "%s", switch_str_nil(msg)); } +static switch_status_t parse_continue(const char *tag_name, client_t *client, switch_xml_t tag, const char *body) +{ + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t parse_log(const char *tag_name, client_t *client, switch_xml_t tag, const char *body) { const char *level = switch_xml_attr(tag, "level"); @@ -900,7 +915,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: @@ -912,6 +926,21 @@ static switch_status_t parse_record(const char *tag_name, client_t *client, swit return status; } +static switch_status_t parse_common(const char *tag_name, client_t *client, switch_xml_t tag, const char *body) +{ + const char *action = switch_xml_attr(tag, "action"); + const char *tmp_action = switch_xml_attr(tag, "temp-action"); + + if (action) { + switch_event_add_header_string(client->params, SWITCH_STACK_BOTTOM, "url", action); + } + + if (tmp_action) { + switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, "url", tmp_action); + } + + return SWITCH_STATUS_SUCCESS; +} static switch_status_t parse_xml(client_t *client) { @@ -982,6 +1011,8 @@ static switch_status_t parse_xml(client_t *client) } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Process Tag: [%s]\n", tag->name); + + parse_common(tag->name, client, tag, expanded); handler(tag->name, client, tag, expanded); if (expanded && expanded != tag->txt) { @@ -1117,8 +1148,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 +1184,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 +1210,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 +1223,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 +1248,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 +1310,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 +1407,9 @@ static switch_status_t httapi_sync(client_t *client) save_params = NULL; } + if (fd) { + fclose(fd); + } return status; } @@ -1779,7 +1838,8 @@ SWITCH_STANDARD_APP(httapi_function) client_t *client; switch_event_t *params = NULL; uint32_t loops = 0, all_extended = 0; - + switch_caller_profile_t *caller_profile; + if (!zstr(data)) { switch_event_create_brackets((char *)data, '{', '}', ',', ¶ms, &parsed, SWITCH_TRUE); } @@ -1812,6 +1872,10 @@ SWITCH_STANDARD_APP(httapi_function) all_extended = switch_true(switch_event_get_header(client->params, "full_channel_data_on_every_req")); } + if ((caller_profile = switch_channel_get_caller_profile(channel))) { + switch_caller_profile_event_set_data(caller_profile, "Caller", client->params); + } + while(switch_channel_ready(channel)) { switch_status_t status = SWITCH_STATUS_FALSE; @@ -2165,30 +2229,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 +2324,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(¶ms, 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, ¶ms))) { + httapi_sync(client); + client_destroy(&client); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find suitable profile\n"); + switch_event_destroy(¶ms); + } + + unlink(context->write.file); + return SWITCH_STATUS_SUCCESS; + } + if (context->del_on_close) { if (context->cache_file) { @@ -2224,6 +2368,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 +2428,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); @@ -2298,6 +2451,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_httapi_load) bind_parser("conference", parse_conference); bind_parser("break", parse_break); bind_parser("log", parse_log); + bind_parser("continue", parse_continue); if (do_config() != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_FALSE; diff --git a/src/mod/applications/mod_httapi/mod_httapi_doc.txt b/src/mod/applications/mod_httapi/mod_httapi_doc.txt index f6c94e6fd4..e865209682 100644 --- a/src/mod/applications/mod_httapi/mod_httapi_doc.txt +++ b/src/mod/applications/mod_httapi/mod_httapi_doc.txt @@ -71,7 +71,8 @@ ATTRS: file : The file name : Param name to save result. error-file : Error file to play on invalid input. -action : Change the new target url +action : Change the new target url. +temp-action : Change url to submit to. just for the next loop. digit-timeout : Timeout waiting for digits after file plays (when input bindings are present) input-timeout : Timeout waiting for more digits in a multi-digit input. loops : max times to play the file when input bindings are present. @@ -82,13 +83,14 @@ asr-grammar : ASR grammar to use *EXPR* - : Records a file, optionally collects input and posts the file back to the target url + : Records a file, optionally collects input and posts the file back to the target url. ATTRS: file : The file name : Param name to save result. error-file : Error file to play on invalid input. -action : Change the new target url +action : Change the new target url. +temp-action : Change url to submit to. just for the next loop. digit-timeout : Timeout waiting for digits after file plays (when input bindings are present) input-timeout : Timeout waiting for more digits in a multi-digit input. @@ -103,7 +105,8 @@ ATTRS: milliseconds : Number of milliseconds to pause name : Param name to save result. error-file : Error file to play on invalid input. -action : Change the new target url +action : Change the new target url. +temp-action : Change url to submit to. just for the next loop. digit-timeout : Timeout waiting for digits after file plays (when input bindings are present) input-timeout : Timeout waiting for more digits in a multi-digit input. loops : max times to play the file when input bindings are present. @@ -119,7 +122,8 @@ ATTRS: file : The file name : Param name to save result. error-file : Error file to play on invalid input. -action : Change the new target url +action : Change the new target url. +temp-action : Change url to submit to. just for the next loop. digit-timeout : Timeout waiting for digits after file plays (when input bindings are present) input-timeout : Timeout waiting for more digits in a multi-digit input. loops : max times to play the file when input bindings are present. @@ -137,7 +141,8 @@ ATTRS: file : The file name : Param name to save result. error-file : Error file to play on invalid input. -action : Change the new target url +action : Change the new target url. +temp-action : Change url to submit to. just for the next loop. digit-timeout : Timeout waiting for digits after file plays (when input bindings are present) input-timeout : Timeout waiting for more digits in a multi-digit input. loops : max times to play the file when input bindings are present. @@ -150,25 +155,30 @@ gender : gender (fs param) -*DATA* +*DATA* : Execute a FreeSWITCH app. ATTRS: application : The app to run +action : Change url to submit to. +temp-action : Change url to submit to. just for the next loop. +data : Alternate source for app data *DATA* : The app data -DATA +DATA : Send a SMS message. ATTRS: to : The dest number +action : Change url to submit to. +temp-action : Change url to submit to. just for the next loop. *DATA* : The message data -*DATA* +*DATA* : Place an outbound call or transfer. ATTRS: @@ -176,6 +186,8 @@ context : Dialplan context. dialplan : Dialplan dialplan. caller-id-name : Caller ID Name. caller-id-number : Caller ID Number. +action : Change url to submit to. +temp-action : Change url to submit to. just for the next loop. *DATA* : Number to dial or originate string @@ -189,28 +201,28 @@ ATTRS: limit : Timeout in seconds. name : Name to use for input values. action : URL action to use. +temp-action : Change url to submit to. just for the next loop. - - + : Start a conference call. ATTRS: profile : Conference profile to use. +action : Change url to submit to. +temp-action : Change url to submit to. just for the next loop. - - - + : Hangup the call ATTRS: cause : Hangup cause - - +action : Change url to submit to. +temp-action : Change url to submit to. just for the next loop. @@ -221,11 +233,21 @@ cause : Hangup cause - + : Exit the httapi application and continue in the dialplan. ATTRS: level : The log level to use. clean : If true do not pring log prefix. +action : Change url to submit to. +temp-action : Change url to submit to. just for the next loop. + + + : Just continue (no-op) + +ATTRS: +action : Change url to submit to. +temp-action : Change url to submit to. just for the next loop. +