/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2012, Anthony Minessale II * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Anthony Minessale II * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Karl Anderson * Darren Schreiber * * * kazoo_commands.c -- clones of mod_commands commands slightly modified for kazoo * */ #include "mod_kazoo.h" #include #include #define UUID_SET_DESC "Set a variable" #define UUID_SET_SYNTAX " [value]" #define UUID_MULTISET_DESC "Set multiple variables" #define UUID_MULTISET_SYNTAX " =;=..." #define KZ_HTTP_PUT_DESC "upload a local freeswitch file to a url" #define KZ_HTTP_PUT_SYNTAX "localfile url" #define KZ_FIRST_OF_DESC "returns first-of existing event header in params" #define KZ_FIRST_OF_SYNTAX "list of headers to check" #define MAX_FIRST_OF 25 #define MAX_HISTORY 50 #define HST_ARRAY_DELIM "|:" #define HST_ITEM_DELIM ':' static void process_history_item(char* value, cJSON *json) { char *argv[4] = { 0 }; char *item = strdup(value); int argc = switch_separate_string(item, HST_ITEM_DELIM, argv, (sizeof(argv) / sizeof(argv[0]))); cJSON *jitem = cJSON_CreateObject(); char *epoch = NULL, *callid = NULL, *type = NULL; int add = 0; if(argc == 4) { add = 1; epoch = argv[0]; callid = argv[1]; type = argv[2]; if(!strncmp(type, "bl_xfer", 7)) { char *split = strchr(argv[3], '/'); if(split) *(split++) = '\0'; cJSON_AddItemToObject(jitem, "Call-ID", cJSON_CreateString(callid)); cJSON_AddItemToObject(jitem, "Type", cJSON_CreateString("blind")); cJSON_AddItemToObject(jitem, "Extension", cJSON_CreateString(argv[3])); } else if(!strncmp(type, "att_xfer", 8)) { char *split = strchr(argv[3], '/'); if(split) { *(split++) = '\0'; cJSON_AddItemToObject(jitem, "Call-ID", cJSON_CreateString(callid)); cJSON_AddItemToObject(jitem, "Type", cJSON_CreateString("attended")); cJSON_AddItemToObject(jitem, "Transferee", cJSON_CreateString(argv[3])); cJSON_AddItemToObject(jitem, "Transferer", cJSON_CreateString(split)); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "TRANSFER TYPE '%s' NOT HANDLED => %s\n", type, item); add = 0; } } else if(!strncmp(type, "uuid_br", 7)) { cJSON_AddItemToObject(jitem, "Call-ID", cJSON_CreateString(callid)); cJSON_AddItemToObject(jitem, "Type", cJSON_CreateString("bridge")); cJSON_AddItemToObject(jitem, "Other-Leg", cJSON_CreateString(argv[3])); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "TRANSFER TYPE '%s' NOT HANDLED => %s\n", type, item); add = 0; } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "TRANSFER TYPE SPLIT ERROR %i => %s\n", argc, item); } if(add) { cJSON_AddItemToObject(json, epoch, jitem); } else { cJSON_Delete(jitem); } switch_safe_free(item); } SWITCH_STANDARD_API(kz_json_history) { char *mycmd = NULL, *argv[MAX_HISTORY] = { 0 }; int n, argc = 0; cJSON *json = cJSON_CreateObject(); char* output = NULL; switch_event_header_t *header = NULL; if (!zstr(cmd) && (mycmd = strdup(cmd))) { if (!strncmp(mycmd, "ARRAY::", 7)) { mycmd += 7; argc = switch_separate_string_string(mycmd, HST_ARRAY_DELIM, argv, (sizeof(argv) / sizeof(argv[0]))); for(n=0; n < argc; n++) { process_history_item(argv[n], json); } } else if (strchr(mycmd, HST_ITEM_DELIM)) { process_history_item(mycmd, json); } else if (stream->param_event) { header = switch_event_get_header_ptr(stream->param_event, mycmd); if (header != NULL) { if(header->idx) { for(n = 0; n < header->idx; n++) { process_history_item(header->array[n], json); } } else { process_history_item(header->value, json); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "TRANSFER HISTORY HEADER NOT FOUND => %s\n", mycmd); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "TRANSFER HISTORY NOT PARSED => %s\n", mycmd); } } output = cJSON_PrintUnformatted(json); stream->write_function(stream, "%s", output); switch_safe_free(output); cJSON_Delete(json); return SWITCH_STATUS_SUCCESS; } SWITCH_STANDARD_API(kz_first_of) { char delim = '|'; char *mycmd = NULL, *argv[MAX_FIRST_OF] = { 0 }; int n, argc = 0; switch_event_header_t *header = NULL; if (!zstr(cmd) && (mycmd = strdup(cmd))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "FIRST-OF %s\n", mycmd); if (!zstr(mycmd) && *mycmd == '^' && *(mycmd+1) == '^') { mycmd += 2; delim = *mycmd++; } argc = switch_separate_string(mycmd, delim, argv, (sizeof(argv) / sizeof(argv[0]))); for(n=0; n < argc; n++) { char* item = argv[n]; if(*item == '#') { if(*(++item) != '\0') { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "RETURNING default %s\n", item); stream->write_function(stream, item); break; } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "CHECKING %s\n", item); header = switch_event_get_header_ptr(stream->param_event, item); if(header) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "RETURNING %s : %s\n", item, header->value); stream->write_function(stream, header->value); break; } } } } switch_safe_free(mycmd); return SWITCH_STATUS_SUCCESS; } switch_status_t kz_uuid_setvar(int urldecode, const char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream) { switch_core_session_t *psession = NULL; char *mycmd = NULL, *argv[3] = { 0 }; int argc = 0; if (!zstr(cmd) && (mycmd = strdup(cmd))) { argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); if ((argc == 2 || argc == 3) && !zstr(argv[0])) { char *uuid = argv[0]; char *var_name = argv[1]; char *var_value = NULL; if (argc == 3) { var_value = argv[2]; } if ((psession = switch_core_session_locate(uuid))) { switch_channel_t *channel; switch_event_t *event; channel = switch_core_session_get_channel(psession); if (zstr(var_name)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No variable name specified.\n"); stream->write_function(stream, "-ERR No variable specified\n"); } else { if(urldecode) { switch_url_decode(var_value); } switch_channel_set_variable(channel, var_name, var_value); kz_check_set_profile_var(channel, var_name, var_value); stream->write_function(stream, "+OK\n"); } if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(channel, event); switch_event_fire(&event); } switch_core_session_rwunlock(psession); } else { stream->write_function(stream, "-ERR No such channel!\n"); } goto done; } } stream->write_function(stream, "-USAGE: %s\n", UUID_SET_SYNTAX); done: switch_safe_free(mycmd); return SWITCH_STATUS_SUCCESS; } SWITCH_STANDARD_API(uuid_setvar_function) { return kz_uuid_setvar(0, cmd, session, stream); } SWITCH_STANDARD_API(uuid_setvar_encoded_function) { return kz_uuid_setvar(1, cmd, session, stream); } switch_status_t kz_uuid_setvar_multi(int urldecode, const char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream) { switch_core_session_t *psession = NULL; char delim = ';'; char *mycmd = NULL, *vars, *argv[64] = { 0 }; int argc = 0; char *var_name, *var_value = NULL; if (!zstr(cmd) && (mycmd = strdup(cmd))) { char *uuid = mycmd; if (!(vars = strchr(uuid, ' '))) { goto done; } *vars++ = '\0'; if (*vars == '^' && *(vars+1) == '^') { vars += 2; delim = *vars++; } if ((psession = switch_core_session_locate(uuid))) { switch_channel_t *channel = switch_core_session_get_channel(psession); switch_event_t *event; int x, y = 0; argc = switch_separate_string(vars, delim, argv, (sizeof(argv) / sizeof(argv[0]))); for (x = 0; x < argc; x++) { var_name = argv[x]; if (var_name && (var_value = strchr(var_name, '='))) { *var_value++ = '\0'; } if (zstr(var_name)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No variable name specified.\n"); stream->write_function(stream, "-ERR No variable specified\n"); } else { if(urldecode) { switch_url_decode(var_value); } switch_channel_set_variable(channel, var_name, var_value); kz_check_set_profile_var(channel, var_name, var_value); y++; } } /* keep kazoo nodes in sync */ if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(channel, event); switch_event_fire(&event); } switch_core_session_rwunlock(psession); if (y) { stream->write_function(stream, "+OK\n"); goto done; } } else { stream->write_function(stream, "-ERR No such channel!\n"); } } stream->write_function(stream, "-USAGE: %s\n", UUID_MULTISET_SYNTAX); done: switch_safe_free(mycmd); return SWITCH_STATUS_SUCCESS; } SWITCH_STANDARD_API(uuid_setvar_multi_function) { return kz_uuid_setvar_multi(0, cmd, session, stream); } SWITCH_STANDARD_API(uuid_setvar_multi_encoded_function) { return kz_uuid_setvar_multi(1, cmd, session, stream); } static size_t body_callback(char *buffer, size_t size, size_t nitems, void *userdata) { return size * nitems; } static size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata) { switch_event_t* event = (switch_event_t*)userdata; int len = strlen(buffer); char buf[1024]; if(len > 2 && len < 1024) { snprintf(buf, sizeof(buf), "%s", buffer); switch_event_add_header_string(event, SWITCH_STACK_PUSH | SWITCH_STACK_BOTTOM, "Reply-Headers", buf); } return nitems * size; } SWITCH_STANDARD_API(kz_http_put) { switch_status_t status = SWITCH_STATUS_SUCCESS; switch_memory_pool_t *lpool = NULL; switch_memory_pool_t *pool = NULL; char *args = NULL; char *argv[10] = { 0 }; int argc = 0; switch_event_t *params = NULL; char *url = NULL; char *filename = NULL; int delete_file = 0; switch_curl_slist_t *headers = NULL; /* optional linked-list of HTTP headers */ char *ext; /* file extension, used for MIME type identification */ const char *mime_type = "application/octet-stream"; char *buf = NULL; char *error = NULL; CURL *curl_handle = NULL; long httpRes = 0; struct stat file_info = {0}; FILE *file_to_put = NULL; int fd; if (session) { pool = switch_core_session_get_pool(session); } else { switch_core_new_memory_pool(&lpool); pool = lpool; } if (zstr(cmd)) { stream->write_function(stream, "USAGE: %s\n", KZ_HTTP_PUT_SYNTAX); status = SWITCH_STATUS_SUCCESS; goto done; } args = strdup(cmd); argc = switch_separate_string(args, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); if (argc != 2) { stream->write_function(stream, "USAGE: %s\n", KZ_HTTP_PUT_SYNTAX); status = SWITCH_STATUS_SUCCESS; goto done; } /* parse params and get profile */ url = switch_core_strdup(pool, argv[0]); if (*url == '{') { switch_event_create_brackets(url, '{', '}', ',', ¶ms, &url, SWITCH_FALSE); } filename = switch_core_strdup(pool, argv[1]); /* guess what type of mime content this is going to be */ if ((ext = strrchr(filename, '.'))) { ext++; if (!(mime_type = switch_core_mime_ext2type(ext))) { mime_type = "application/octet-stream"; } } buf = switch_mprintf("Content-Type: %s", mime_type); headers = switch_curl_slist_append(headers, buf); /* open file and get the file size */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "opening %s for upload to %s\n", filename, url); fd = open(filename, O_RDONLY); if (fd == -1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open() error: %s\n", strerror(errno)); status = SWITCH_STATUS_FALSE; stream->write_function(stream, "-ERR error opening file\n"); goto done; } if (fstat(fd, &file_info) == -1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "fstat() error: %s\n", strerror(errno)); stream->write_function(stream, "-ERR fstat error\n"); goto done; } close(fd); /* libcurl requires FILE* */ file_to_put = fopen(filename, "rb"); if (!file_to_put) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "fopen() error: %s\n", strerror(errno)); status = SWITCH_STATUS_FALSE; goto done; } curl_handle = switch_curl_easy_init(); if (!curl_handle) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "switch_curl_easy_init() failure\n"); status = SWITCH_STATUS_FALSE; stream->write_function(stream, "-ERR switch_curl_easy init failure\n"); goto done; } switch_curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1); switch_curl_easy_setopt(curl_handle, CURLOPT_PUT, 1); switch_curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); switch_curl_easy_setopt(curl_handle, CURLOPT_URL, url); switch_curl_easy_setopt(curl_handle, CURLOPT_READDATA, file_to_put); switch_curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); switch_curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1); switch_curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 10); switch_curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1); switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-kazoo/1.0"); switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, stream->param_event); switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback); switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, body_callback); switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); switch_curl_easy_perform(curl_handle); switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes); switch_curl_easy_cleanup(curl_handle); if (httpRes == 200 || httpRes == 201 || httpRes == 202 || httpRes == 204) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s saved to %s\n", filename, url); switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-Output", "%s saved to %s", filename, url); stream->write_function(stream, "+OK %s saved to %s", filename, url); delete_file = 1; } else { error = switch_mprintf("Received HTTP error %ld trying to save %s to %s", httpRes, filename, url); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s\n", error); switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-Error", "%s", error); switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-HTTP-Error", "%ld", httpRes); stream->write_function(stream, "-ERR %s", error); status = SWITCH_STATUS_GENERR; } done: if (file_to_put) { fclose(file_to_put); if(delete_file) { remove(filename); } } if (headers) { switch_curl_slist_free_all(headers); } switch_safe_free(buf); switch_safe_free(error); switch_safe_free(args); if (lpool) { switch_core_destroy_memory_pool(&lpool); } if (params) { switch_event_destroy(¶ms); } return status; } SWITCH_STANDARD_API(kz_expand_api) { if (!zstr(cmd)) { char * val = kz_expand(cmd); stream->write_function(stream, "+OK %s", val); switch_safe_free(val); } else { stream->write_function(stream, "ERR invalid input"); } return SWITCH_STATUS_SUCCESS; } void add_kz_commands(switch_loadable_module_interface_t **module_interface) { switch_api_interface_t *api_interface = NULL; SWITCH_ADD_API(api_interface, "kz_uuid_setvar_multi", UUID_SET_DESC, uuid_setvar_multi_function, UUID_MULTISET_SYNTAX); SWITCH_ADD_API(api_interface, "kz_uuid_setvar_multi_encoded", UUID_SET_DESC, uuid_setvar_multi_encoded_function, UUID_MULTISET_SYNTAX); switch_console_set_complete("add kz_uuid_setvar_multi ::console::list_uuid"); switch_console_set_complete("add kz_uuid_setvar_multi_encoded ::console::list_uuid"); SWITCH_ADD_API(api_interface, "kz_uuid_setvar", UUID_MULTISET_DESC, uuid_setvar_function, UUID_SET_SYNTAX); SWITCH_ADD_API(api_interface, "kz_uuid_setvar_encoded", UUID_MULTISET_DESC, uuid_setvar_encoded_function, UUID_SET_SYNTAX); switch_console_set_complete("add kz_uuid_setvar ::console::list_uuid"); switch_console_set_complete("add kz_uuid_setvar_encoded ::console::list_uuid"); SWITCH_ADD_API(api_interface, "kz_http_put", KZ_HTTP_PUT_DESC, kz_http_put, KZ_HTTP_PUT_SYNTAX); SWITCH_ADD_API(api_interface, "first-of", KZ_FIRST_OF_DESC, kz_first_of, KZ_FIRST_OF_SYNTAX); SWITCH_ADD_API(api_interface, "kz_json_history", KZ_FIRST_OF_DESC, kz_json_history, KZ_FIRST_OF_SYNTAX); SWITCH_ADD_API(api_interface, "kz_expand", KZ_FIRST_OF_DESC, kz_expand_api, KZ_FIRST_OF_SYNTAX); }