diff --git a/conf/autoload_configs/cepstral.conf.xml b/conf/autoload_configs/cepstral.conf.xml new file mode 100644 index 0000000000..cf4aa92f41 --- /dev/null +++ b/conf/autoload_configs/cepstral.conf.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/conf/lang/en/ivr/sounds.xml b/conf/lang/en/ivr/sounds.xml new file mode 100644 index 0000000000..58db0a6c15 --- /dev/null +++ b/conf/lang/en/ivr/sounds.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/docs/phrase/phrase_en.xml b/docs/phrase/phrase_en.xml index 34901054e9..30d642460f 100644 --- a/docs/phrase/phrase_en.xml +++ b/docs/phrase/phrase_en.xml @@ -437,7 +437,10 @@ - + + + + diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index 8ab3390e67..0810abb49b 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -1772,9 +1772,10 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames const char *agent_wrap_up_time = argv[11]; const char *agent_state = argv[12]; const char *agent_ready_time = argv[13]; - const char *agent_tier_level = argv[14]; - const char *agent_type = argv[15]; - const char *agent_uuid = argv[16]; + const char *agent_tier_position = argv[14]; + const char *agent_tier_level = argv[15]; + const char *agent_type = argv[16]; + const char *agent_uuid = argv[17]; switch_bool_t contact_agent = SWITCH_TRUE; @@ -1886,8 +1887,16 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames h->reject_delay_time = atoi(agent_reject_delay_time); h->busy_delay_time = atoi(agent_busy_delay_time); h->no_answer_delay_time = atoi(agent_no_answer_delay_time); - + if (!strcasecmp(cbt->strategy, "top-down")) { + switch_core_session_t *member_session = switch_core_session_locate(cbt->member_session_uuid); + if (member_session) { + switch_channel_t *member_channel = switch_core_session_get_channel(member_session); + switch_channel_set_variable(member_channel, "cc_last_agent_tier_position", agent_tier_position); + switch_channel_set_variable(member_channel, "cc_last_agent_tier_level", agent_tier_level); + switch_core_session_rwunlock(member_session); + } + } cc_agent_update("state", cc_agent_state2str(CC_AGENT_STATE_RECEIVING), h->agent_name); sql = switch_mprintf( @@ -1994,38 +2003,97 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName cbt.strategy = queue_strategy; cbt.record_template = queue_record_template; cbt.agent_found = SWITCH_FALSE; - - if (!strcasecmp(queue->strategy, "longest-idle-agent")) { - sql_order_by = switch_mprintf("level, agents.last_offered_call, position"); - } else if (!strcasecmp(queue_strategy, "agent-with-least-talk-time")) { - sql_order_by = switch_mprintf("level, agents.talk_time, position"); - } else if (!strcasecmp(queue_strategy, "agent-with-fewest-calls")) { - sql_order_by = switch_mprintf("level, agents.calls_answered, position"); - } else if (!strcasecmp(queue_strategy, "ring-all")) { - sql = switch_mprintf("UPDATE members SET state = '%q' WHERE state = '%q' AND uuid = '%q' AND system = 'single_box'", - cc_member_state2str(CC_MEMBER_STATE_TRYING), cc_member_state2str(CC_MEMBER_STATE_WAITING), cbt.member_uuid); - cc_execute_sql(NULL, sql, NULL); - switch_safe_free(sql); - sql_order_by = switch_mprintf("level, position"); - } else if(!strcasecmp(queue_strategy, "sequentially-by-agent-order")) { - sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); /* Default to last_offered_call, let add new strategy if needing it differently */ - } else { - /* If the strategy doesn't exist, just fallback to the following */ - sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); - } - sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" - " WHERE tiers.queue = '%q'" - " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" - " ORDER BY %q", - queue_name, - cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), - sql_order_by); + if (!strcasecmp(queue->strategy, "top-down")) { + /* WARNING this use channel variable to help dispatch... might need to be reviewed to save it in DB to make this multi server prooft in the future */ + switch_core_session_t *member_session = switch_core_session_locate(cbt.member_session_uuid); + int position = 0, level = 0; + const char *last_agent_tier_position, *last_agent_tier_level; + if (member_session) { + switch_channel_t *member_channel = switch_core_session_get_channel(member_session); + + if ((last_agent_tier_position = switch_channel_get_variable(member_channel, "cc_last_agent_tier_position"))) { + position = atoi(last_agent_tier_position); + } + if ((last_agent_tier_level = switch_channel_get_variable(member_channel, "cc_last_agent_tier_level"))) { + level = atoi(last_agent_tier_level); + } + switch_core_session_rwunlock(member_session); + } + + sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, agents.last_offered_call as agents_last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + " WHERE tiers.queue = '%q'" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " AND tiers.position > %d" + " AND tiers.level = %d" + " UNION " + "SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, agents.last_offered_call as agents_last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + " WHERE tiers.queue = '%q'" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " ORDER BY dyn_order asc, tiers_level, tiers_position, agents_last_offered_call", + queue_name, + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), + position, + level, + queue_name, + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND) + ); + } else if (!strcasecmp(queue->strategy, "round-robin")) { + sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, agents.last_offered_call as agents_last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + " WHERE tiers.queue = '%q'" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " AND tiers.position > (SELECT tiers.position FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' AND agents.last_offered_call > 0 ORDER BY agents.last_offered_call DESC LIMIT 1)" + " AND tiers.level = (SELECT tiers.level FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' AND agents.last_offered_call > 0 ORDER BY agents.last_offered_call DESC LIMIT 1)" + " UNION " + "SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, agents.last_offered_call as agents_last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + " WHERE tiers.queue = '%q'" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " ORDER BY dyn_order asc, tiers_level, tiers_position, agents_last_offered_call", + queue_name, + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), + queue_name, + queue_name, + queue_name, + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND) + ); + + } else { + + if (!strcasecmp(queue->strategy, "longest-idle-agent")) { + sql_order_by = switch_mprintf("level, agents.last_offered_call, position"); + } else if (!strcasecmp(queue_strategy, "agent-with-least-talk-time")) { + sql_order_by = switch_mprintf("level, agents.talk_time, position"); + } else if (!strcasecmp(queue_strategy, "agent-with-fewest-calls")) { + sql_order_by = switch_mprintf("level, agents.calls_answered, position"); + } else if (!strcasecmp(queue_strategy, "ring-all")) { + sql = switch_mprintf("UPDATE members SET state = '%q' WHERE state = '%q' AND uuid = '%q' AND system = 'single_box'", + cc_member_state2str(CC_MEMBER_STATE_TRYING), cc_member_state2str(CC_MEMBER_STATE_WAITING), cbt.member_uuid); + cc_execute_sql(NULL, sql, NULL); + switch_safe_free(sql); + sql_order_by = switch_mprintf("level, position"); + } else if(!strcasecmp(queue_strategy, "random")) { + sql_order_by = switch_mprintf("level, random()"); + } else if(!strcasecmp(queue_strategy, "sequentially-by-agent-order")) { + sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); /* Default to last_offered_call, let add new strategy if needing it differently */ + } else { + /* If the strategy doesn't exist, just fallback to the following */ + sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); + } + + sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position, tiers.level, agents.type, agents.uuid FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + " WHERE tiers.queue = '%q'" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " ORDER BY %q", + queue_name, + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), + sql_order_by); + switch_safe_free(sql_order_by); + + } cc_execute_sql_callback(NULL /* queue */, NULL /* mutex */, sql, agents_callback, &cbt /* Call back variables */); switch_safe_free(sql); - switch_safe_free(sql_order_by); /* We update a field in the queue struct so we can kick caller out if waiting for too long with no agent */ if (!cbt.queue_name || !(queue = get_queue(cbt.queue_name))) { @@ -2360,7 +2428,7 @@ SWITCH_STANDARD_APP(callcenter_function) switch_safe_free(sql); /* Confirm we took that member in */ - sql = switch_mprintf("SELECT abandoned_epoch FROM members WHERE uuid = '%q' AND session_uuid = '%q' AND state = '%q' AND queue = '%q'", member_session, member_session_uuid, cc_member_state2str(CC_MEMBER_STATE_WAITING), queue_name); + sql = switch_mprintf("SELECT abandoned_epoch FROM members WHERE uuid = '%q' AND session_uuid = '%q' AND state = '%q' AND queue = '%q'", member_uuid, member_session_uuid, cc_member_state2str(CC_MEMBER_STATE_WAITING), queue_name); cc_execute_sql2str(NULL, NULL, sql, res, sizeof(res)); switch_safe_free(sql); diff --git a/src/mod/applications/mod_hash/mod_hash.c b/src/mod/applications/mod_hash/mod_hash.c index e3f01cec96..cfabf5477a 100644 --- a/src/mod/applications/mod_hash/mod_hash.c +++ b/src/mod/applications/mod_hash/mod_hash.c @@ -531,16 +531,33 @@ SWITCH_STANDARD_API(hash_api_function) return SWITCH_STATUS_SUCCESS; } -#define HASH_DUMP_SYNTAX "all|limit|db" +#define HASH_DUMP_SYNTAX "all|limit|db []" SWITCH_STANDARD_API(hash_dump_function) { int mode; switch_hash_index_t *hi; + int argc = 0; + char *argv[4] = { 0 }; + char *mydata = NULL; + int realm = 0; + char *realmvalue = NULL; - if (zstr(cmd)) { + if (!zstr(cmd)) { + mydata = strdup(cmd); + switch_assert(mydata); + argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } else { + realmvalue = "test"; + realm = 0; stream->write_function(stream, "Usage: "HASH_DUMP_SYNTAX"\n"); return SWITCH_STATUS_SUCCESS; - } + } + + cmd = strdup(argv[0]); + if (argc == 2) { + realm = 1; + realmvalue = switch_mprintf("%s_", argv[1]); + } if (!strcmp(cmd, "all")) { mode = 3; @@ -553,7 +570,6 @@ SWITCH_STANDARD_API(hash_dump_function) return SWITCH_STATUS_SUCCESS; } - if (mode & 1) { switch_thread_rwlock_rdlock(globals.limit_hash_rwlock); for (hi = switch_hash_first(NULL, globals.limit_hash); hi; hi = switch_hash_next(hi)) { @@ -577,8 +593,13 @@ SWITCH_STANDARD_API(hash_dump_function) const void *key; switch_ssize_t keylen; switch_hash_this(hi, &key, &keylen, &val); - - stream->write_function(stream, "D/%s/%s\n", key, (char*)val); + if (realm) { + if (strstr(key, realmvalue)) { + stream->write_function(stream, "D/%s/%s\n", key, (char*)val); + } + } else { + stream->write_function(stream, "D/%s/%s\n", key, (char*)val); + } } switch_thread_rwlock_unlock(globals.db_hash_rwlock); } diff --git a/src/mod/applications/mod_lcr/mod_lcr.c b/src/mod/applications/mod_lcr/mod_lcr.c index 93f9c9617d..ac5d90871e 100644 --- a/src/mod/applications/mod_lcr/mod_lcr.c +++ b/src/mod/applications/mod_lcr/mod_lcr.c @@ -133,7 +133,7 @@ struct callback_obj { profile_t *profile; switch_core_session_t *session; switch_event_t *event; - float sell_rate; + float max_rate; }; typedef struct callback_obj callback_t; @@ -622,6 +622,9 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa } else if (CF("lcr_carrier_name")) { additional->carrier_name = switch_core_strdup(pool, switch_str_nil(argv[i])); } else if (CF("lcr_rate_field")) { + if (!argv[i] || zstr(argv[i])) { + goto end; + } additional->rate = (float)atof(switch_str_nil(argv[i])); additional->rate_str = switch_core_sprintf(pool, "%0.5f", additional->rate); } else if (CF("lcr_gw_prefix")) { @@ -669,10 +672,12 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa for (current = cbt->head; current; current = current->next) { - if (cbt->sell_rate && cbt->sell_rate > current->rate) { - continue; + if (cbt->max_rate && (cbt->max_rate < additional->rate)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Skipping [%s] because [%f] is higher than the max_rate of [%f]\n", + additional->carrier_name, additional->rate, cbt->max_rate); + break; } - + key = switch_core_sprintf(pool, "%s:%s", additional->gw_prefix, additional->gw_suffix); if (switch_core_hash_find(cbt->dedup_hash, key)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, @@ -852,9 +857,9 @@ static switch_status_t lcr_do_lookup(callback_t *cb_struct) if (cb_struct->session) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(cb_struct->session), SWITCH_LOG_DEBUG, "we have a session\n"); if ((channel = switch_core_session_get_channel(cb_struct->session))) { - const char *sell_rate = switch_channel_get_variable(channel, "sell_rate"); - if (!zstr(sell_rate)) { - cb_struct->sell_rate = atof(sell_rate); + const char *max_rate = switch_channel_get_variable(channel, "max_rate"); + if (!zstr(max_rate)) { + cb_struct->max_rate = atof(max_rate); } switch_channel_set_variable_var_check(channel, "lcr_rate_field", rate_field, SWITCH_FALSE); switch_channel_set_variable_var_check(channel, "lcr_user_rate_field", user_rate_field, SWITCH_FALSE); diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.c b/src/mod/applications/mod_spandsp/mod_spandsp.c index d9b9ff084a..fd2ac7f947 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp.c @@ -70,33 +70,89 @@ SWITCH_STANDARD_APP(stop_dtmf_session_function) spandsp_stop_inband_dtmf_session(session); } + +SWITCH_STANDARD_APP(spandsp_fax_detect_session_function) +{ + int argc = 0; + char *argv[3] = { 0 }; + char *dupdata; + const char *app = NULL, *arg = NULL; + int timeout = 0; + + if (!zstr(data) && (dupdata = switch_core_session_strdup(session, data))) { + if ((argc = switch_split(dupdata, ' ', argv)) == 3) { + app = argv[0]; + arg = argv[1]; + timeout = atoi(argv[2]); + if (timeout < 0) { + timeout = 0; + } + } + } + + if (app) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Enabling fax detection '%s' '%s'\n", argv[0], argv[1]); + spandsp_fax_detect_session(session, "rw", timeout, 1, app, arg, NULL); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot Enable fax detection '%s' '%s'\n", argv[0], argv[1]); + } +} + +SWITCH_STANDARD_APP(spandsp_stop_fax_detect_session_function) +{ + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Disabling fax detection\n"); + spandsp_fax_stop_detect_session(session); +} + static void event_handler(switch_event_t *event) { mod_spandsp_fax_event_handler(event); } + SWITCH_STANDARD_APP(t38_gateway_function) { switch_channel_t *channel = switch_core_session_get_channel(session); time_t timeout = switch_epoch_time_now(NULL) + 20; const char *var; - - if (zstr(data) || strcasecmp(data, "self")) { - data = "peer"; - } - - switch_channel_set_variable(channel, "t38_leg", data); - - if ((var = switch_channel_get_variable(channel, "t38_gateway_detect_timeout"))) { - long to = atol(var); - if (to > -1) { - timeout = (time_t) (switch_epoch_time_now(NULL) + to); - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s invalid timeout value.\n", switch_channel_get_name(channel)); + int argc = 0; + char *argv[2] = { 0 }; + char *dupdata; + const char *direction = NULL, *flags = NULL; + + if (!zstr(data) && (dupdata = switch_core_session_strdup(session, data))) { + if ((argc = switch_split(dupdata, ' ', argv))) { + if (argc > 0) { + direction = argv[0]; + } + + if (argc > 1) { + flags = argv[1]; + } } } + + if (zstr(direction) || strcasecmp(direction, "self")) { + direction = "peer"; + } - switch_ivr_tone_detect_session(session, "t38", "1100.0", "rw", timeout, 1, data, NULL, t38_gateway_start); + switch_channel_set_variable(channel, "t38_leg", direction); + + if (!zstr(flags) && !strcasecmp(flags, "nocng")) { + t38_gateway_start(session, direction, NULL); + } else { + if ((var = switch_channel_get_variable(channel, "t38_gateway_detect_timeout"))) { + long to = atol(var); + if (to > -1) { + timeout = (time_t) (switch_epoch_time_now(NULL) + to); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s invalid timeout value.\n", switch_channel_get_name(channel)); + } + } + + //switch_ivr_tone_detect_session(session, "t38", "1100.0", "rw", timeout, 1, direction, NULL, t38_gateway_start); + spandsp_fax_detect_session(session, "rw", timeout, 1, direction, NULL, t38_gateway_start); + } } /** @@ -198,6 +254,12 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init) SWITCH_ADD_APP(app_interface, "spandsp_stop_dtmf", "stop inband dtmf", "Stop detecting inband dtmf.", stop_dtmf_session_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "spandsp_start_dtmf", "Detect dtmf", "Detect inband dtmf on the session", dtmf_session_function, "", SAF_MEDIA_TAP); + SWITCH_ADD_APP(app_interface, "spandsp_start_fax_detect", "start fax detect", "start fax detect", spandsp_fax_detect_session_function, + "[ ][ ]", SAF_NONE); + + SWITCH_ADD_APP(app_interface, "spandsp_stop_fax_detect", "stop fax detect", "stop fax detect", spandsp_stop_fax_detect_session_function, "", SAF_NONE); + + mod_spandsp_fax_load(pool); mod_spandsp_codecs_load(module_interface, pool); diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.h b/src/mod/applications/mod_spandsp/mod_spandsp.h index 09e816627a..21de3fa22b 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.h +++ b/src/mod/applications/mod_spandsp/mod_spandsp.h @@ -68,3 +68,9 @@ switch_status_t spandsp_inband_dtmf_session(switch_core_session_t *session); switch_status_t callprogress_detector_start(switch_core_session_t *session, const char *name); switch_status_t callprogress_detector_stop(switch_core_session_t *session); + +switch_status_t spandsp_fax_detect_session(switch_core_session_t *session, + const char *flags, time_t timeout, + int hits, const char *app, const char *data, switch_tone_detect_callback_t callback); + +switch_status_t spandsp_fax_stop_detect_session(switch_core_session_t *session); diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c index 4dd936b8c6..95eefd5c74 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -446,17 +446,29 @@ static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uin /* we need to build a real packet here and make write_frame.packet and write_frame.packetlen point to it */ out_frame.flags = SFF_UDPTL_PACKET | SFF_PROXY_PACKET; out_frame.packet = pkt; - out_frame.packetlen = udptl_build_packet(pvt->udptl_state, pkt, buf, len); - - //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "WRITE %d udptl bytes\n", out_frame.packetlen); + if ((r = udptl_build_packet(pvt->udptl_state, pkt, buf, len)) > 0) { + out_frame.packetlen = r; + //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "WRITE %d udptl bytes\n", out_frame.packetlen); - for (x = 0; x < count; x++) { - if (switch_core_session_write_frame(session, &out_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { - r = -1; - break; + for (x = 0; x < count; x++) { + if (switch_core_session_write_frame(session, &out_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { + r = -1; + break; + } + } + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "INVALID PACKETLEN: %d PASSED: %d:%d\n", r, len, count); + } + + if (r < 0) { + t30_state_t *t30; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "TERMINATING T30 STATE\n"); + if (pvt->t38_state && (t30 = t38_terminal_get_t30_state(pvt->t38_state))) { + t30_terminate(t30); } } + return r; } @@ -1772,8 +1784,8 @@ switch_bool_t t38_gateway_start(switch_core_session_t *session, const char *app, switch_channel_set_variable(channel, "t38_peer", switch_core_session_get_uuid(other_session)); switch_channel_set_variable(other_channel, "t38_peer", switch_core_session_get_uuid(session)); - switch_channel_set_variable(peer ? other_channel : channel, "t38_gateway_format", "audio"); - switch_channel_set_variable(peer ? channel : other_channel, "t38_gateway_format", "udptl"); + switch_channel_set_variable(peer ? other_channel : channel, "t38_gateway_format", "udptl"); + switch_channel_set_variable(peer ? channel : other_channel, "t38_gateway_format", "audio"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s starting gateway mode to %s\n", @@ -1803,6 +1815,250 @@ switch_bool_t t38_gateway_start(switch_core_session_t *session, const char *app, return SWITCH_FALSE; } +typedef struct { + char *app; + char *data; + char *key; + int up; + int total_hits; + int hits; + int sleep; + int expires; + int default_sleep; + int default_expires; + switch_tone_detect_callback_t callback; + modem_connect_tones_rx_state_t rx_tones; + + switch_media_bug_t *bug; + switch_core_session_t *session; + int bug_running; + +} spandsp_fax_tone_container_t; + +static switch_status_t tone_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + spandsp_fax_tone_container_t *cont = switch_channel_get_private(channel, "_fax_tone_detect_"); + + + if (!cont || dtmf->digit != 'f') { + return SWITCH_STATUS_SUCCESS; + } + + if (cont->callback) { + cont->callback(cont->session, cont->app, cont->data); + } else { + switch_channel_execute_on(switch_core_session_get_channel(cont->session), "execute_on_fax_detect"); + if (cont->app) { + switch_core_session_execute_application_async(cont->session, cont->app, cont->data); + } + } + + return SWITCH_STATUS_SUCCESS; + +} + + +static switch_bool_t tone_detect_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) +{ + spandsp_fax_tone_container_t *cont = (spandsp_fax_tone_container_t *) user_data; + switch_frame_t *frame = NULL; + switch_bool_t rval = SWITCH_TRUE; + + switch (type) { + case SWITCH_ABC_TYPE_INIT: + if (cont) { + cont->bug_running = 1; + modem_connect_tones_rx_init(&cont->rx_tones, MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE, NULL, NULL); + } + break; + case SWITCH_ABC_TYPE_CLOSE: + break; + case SWITCH_ABC_TYPE_READ_REPLACE: + case SWITCH_ABC_TYPE_WRITE_REPLACE: + { + int skip = 0; + + if (type == SWITCH_ABC_TYPE_READ_REPLACE) { + frame = switch_core_media_bug_get_read_replace_frame(bug); + } else { + frame = switch_core_media_bug_get_write_replace_frame(bug); + } + + if (cont->sleep) { + cont->sleep--; + if (cont->sleep) { + skip = 1; + } + } + + if (cont->expires) { + cont->expires--; + if (!cont->expires) { + cont->hits = 0; + cont->sleep = 0; + cont->expires = 0; + } + } + + if (!cont->up) { + skip = 1; + } + + if (skip) { + return SWITCH_TRUE; + } + + cont->hits = 0; + modem_connect_tones_rx(&cont->rx_tones, frame->data, frame->samples); + cont->hits = modem_connect_tones_rx_get(&cont->rx_tones); + + if (cont->hits) { + switch_event_t *event; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_DEBUG, + "Fax Tone Detected. [%s][%s]\n", cont->app, switch_str_nil(cont->data)); + + if (cont->callback) { + cont->callback(cont->session, cont->app, cont->data); + } else { + switch_channel_execute_on(switch_core_session_get_channel(cont->session), "execute_on_fax_detect"); + if (cont->app) { + switch_core_session_execute_application_async(cont->session, cont->app, cont->data); + } + } + + + if (switch_event_create(&event, SWITCH_EVENT_DETECTED_TONE) == SWITCH_STATUS_SUCCESS) { + switch_event_t *dup; + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Detected-Fax-Tone", "true"); + + if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) { + switch_event_fire(&dup); + } + + if (switch_core_session_queue_event(cont->session, &event) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_ERROR, + "Event queue failed!\n"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); + switch_event_fire(&event); + } + } + + rval = SWITCH_FALSE; + } + + } + break; + case SWITCH_ABC_TYPE_WRITE: + default: + break; + } + + if (rval == SWITCH_FALSE) { + cont->bug_running = 0; + } + + return rval; +} + +switch_status_t spandsp_fax_stop_detect_session(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + spandsp_fax_tone_container_t *cont = switch_channel_get_private(channel, "_fax_tone_detect_"); + + if (cont) { + switch_channel_set_private(channel, "_fax_tone_detect_", NULL); + cont->up = 0; + switch_core_media_bug_remove(session, &cont->bug); + return SWITCH_STATUS_SUCCESS; + } + return SWITCH_STATUS_FALSE; +} + +switch_status_t spandsp_fax_detect_session(switch_core_session_t *session, + const char *flags, time_t timeout, + int hits, const char *app, const char *data, switch_tone_detect_callback_t callback) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_status_t status; + spandsp_fax_tone_container_t *cont = switch_channel_get_private(channel, "_fax_tone_detect_"); + switch_media_bug_flag_t bflags = 0; + const char *var; + switch_codec_implementation_t read_impl = { 0 }; + switch_core_session_get_read_impl(session, &read_impl); + + if (cont) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Max Tones Reached!\n"); + return SWITCH_STATUS_FALSE; + } + + if (!cont && !(cont = switch_core_session_alloc(session, sizeof(*cont)))) { + return SWITCH_STATUS_MEMERR; + } + + if (app) { + cont->app = switch_core_session_strdup(session, app); + } + + if (data) { + cont->data = switch_core_session_strdup(session, data); + } + + cont->callback = callback; + cont->up = 1; + cont->session = session; + + if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + + cont->default_sleep = 25; + cont->default_expires = 250; + + if ((var = switch_channel_get_variable(channel, "fax_tone_detect_sleep"))) { + int tmp = atoi(var); + if (tmp > 0) { + cont->default_sleep = tmp; + } + } + + if ((var = switch_channel_get_variable(channel, "fax_tone_detect_expires"))) { + int tmp = atoi(var); + if (tmp > 0) { + cont->default_expires = tmp; + } + } + + if (zstr(flags)) { + bflags = SMBF_READ_REPLACE; + } else { + if (strchr(flags, 'r')) { + bflags |= SMBF_READ_REPLACE; + } else if (strchr(flags, 'w')) { + bflags |= SMBF_WRITE_REPLACE; + } + } + + bflags |= SMBF_NO_PAUSE; + + + switch_core_event_hook_add_send_dtmf(session, tone_on_dtmf); + switch_core_event_hook_add_recv_dtmf(session, tone_on_dtmf); + + + if ((status = switch_core_media_bug_add(session, "fax_tone_detect", "", + tone_detect_callback, cont, timeout, bflags, &cont->bug)) != SWITCH_STATUS_SUCCESS) { + cont->bug_running = 0; + return status; + } + + switch_channel_set_private(channel, "_fax_tone_detect_", cont); + + return SWITCH_STATUS_SUCCESS; +} + + /* For Emacs: diff --git a/src/mod/asr_tts/mod_cepstral/mod_cepstral.c b/src/mod/asr_tts/mod_cepstral/mod_cepstral.c index 432a0b7c99..63ed2a23d9 100644 --- a/src/mod/asr_tts/mod_cepstral/mod_cepstral.c +++ b/src/mod/asr_tts/mod_cepstral/mod_cepstral.c @@ -65,6 +65,14 @@ typedef struct { } cepstral_t; +static struct { + char *encoding; +} globals; + +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_encoding, globals.encoding); + + + /* This callback caches the audio in the buffer */ static swift_result_t write_audio(swift_event * event, swift_event_t type, void *udata) { @@ -223,7 +231,7 @@ static switch_status_t cepstral_speech_feed_tts(switch_speech_handle_t *sh, char if (zstr(text)) { return SWITCH_STATUS_FALSE; } - swift_port_speak_file(cepstral->port, text, NULL, &cepstral->tts_stream, NULL); + swift_port_speak_file(cepstral->port, text, globals.encoding, &cepstral->tts_stream, NULL); } else { char *to_say; if (zstr(text)) { @@ -231,7 +239,7 @@ static switch_status_t cepstral_speech_feed_tts(switch_speech_handle_t *sh, char } if ((to_say = switch_mprintf(" %s ", text))) { - swift_port_speak_text(cepstral->port, to_say, 0, NULL, &cepstral->tts_stream, NULL); + swift_port_speak_text(cepstral->port, to_say, 0, globals.encoding, &cepstral->tts_stream, NULL); switch_safe_free(to_say); } } @@ -401,10 +409,54 @@ static void cepstral_float_param_tts(switch_speech_handle_t *sh, char *param, do } +static switch_status_t load_config(void) +{ + char *cf = "cepstral.conf"; + switch_xml_t cfg, xml = NULL, param, settings; + + /* Init to SWIFT default encoding */ + set_global_encoding(SWIFT_DEFAULT_ENCODING); + + if (xml = switch_xml_open_cfg(cf, &cfg, NULL)) { + if ((settings = switch_xml_child(cfg, "settings"))) { + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + if (!strcasecmp(var, "encoding")) { + if (!strcasecmp(val, "utf-8")) { + set_global_encoding(SWIFT_UTF8); + } else if (!strcasecmp(val, "us-ascii")) { + set_global_encoding(SWIFT_ASCII); + } else if (!strcasecmp(val, "iso8859-1")) { + set_global_encoding(SWIFT_ISO_8859_1); + } else if (!strcasecmp(val, "iso8859-15")) { + set_global_encoding(SWIFT_ISO_8859_15); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown value \"%s\" for param \"%s\". Setting to default.\n", val, var); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Param \"%s\" unknown\n", var); + } + } + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Open of \"%s\" failed. Using default settings.\n", cf); + } + + if (xml) { + switch_xml_free(xml); + } + + return SWITCH_STATUS_SUCCESS; +} + SWITCH_MODULE_LOAD_FUNCTION(mod_cepstral_load) { switch_speech_interface_t *speech_interface; + memset(&globals, 0, sizeof(globals)); + load_config(); + /* Open the Swift TTS Engine */ if (!(engine = swift_engine_open(NULL))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open Swift Engine."); diff --git a/src/switch_core.c b/src/switch_core.c index 1e91279091..ef5d1c497d 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -1662,9 +1662,6 @@ static void switch_load_core_config(const char *file) switch_time_set_monotonic(switch_true(val)); } else if (!strcasecmp(var, "enable-softtimer-timerfd")) { switch_time_set_timerfd(switch_true(val)); - if (switch_true(val)) { - switch_clear_flag((&runtime), SCF_CALIBRATE_CLOCK); - } } else if (!strcasecmp(var, "enable-clock-nanosleep")) { switch_time_set_nanosleep(switch_true(val)); } else if (!strcasecmp(var, "enable-cond-yield")) { diff --git a/src/switch_core_codec.c b/src/switch_core_codec.c index 96f8d57726..feaca4b7ed 100644 --- a/src/switch_core_codec.c +++ b/src/switch_core_codec.c @@ -658,7 +658,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_codec_decode(switch_codec_t *codec, if (frames && codec->implementation->decoded_bytes_per_packet * frames > *decoded_data_len) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Buffer size sanity check failed!\n"); - return SWITCH_STATUS_GENERR; + *decoded_data_len = codec->implementation->decoded_bytes_per_packet; + memset(decoded_data, 255, *decoded_data_len); + return SWITCH_STATUS_SUCCESS; } } diff --git a/src/switch_core_sqldb.c b/src/switch_core_sqldb.c index a1baa9e2c5..106c7b0599 100644 --- a/src/switch_core_sqldb.c +++ b/src/switch_core_sqldb.c @@ -1642,18 +1642,12 @@ static char create_registrations_sql[] = SWITCH_DECLARE(switch_status_t) switch_core_add_registration(const char *user, const char *realm, const char *token, const char *url, uint32_t expires, const char *network_ip, const char *network_port, const char *network_proto) { - switch_cache_db_handle_t *dbh; char *sql; if (!switch_test_flag((&runtime), SCF_USE_SQL)) { return SWITCH_STATUS_FALSE; } - if (switch_core_db_handle(&dbh) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); - return SWITCH_STATUS_FALSE; - } - if (runtime.multiple_registrations) { sql = switch_mprintf("delete from registrations where hostname='%q' and (url='%q' or token='%q')", switch_core_get_hostname(), url, switch_str_nil(token)); @@ -1662,9 +1656,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_add_registration(const char *user, c user, realm, switch_core_get_hostname()); } - switch_cache_db_execute_sql(dbh, sql, NULL); - free(sql); - + switch_queue_push(sql_manager.sql_queue[0], sql); + sql = switch_mprintf("insert into registrations (reg_user,realm,token,url,expires,network_ip,network_port,network_proto,hostname) " "values ('%q','%q','%q','%q',%ld,'%q','%q','%q','%q')", switch_str_nil(user), @@ -1677,40 +1670,29 @@ SWITCH_DECLARE(switch_status_t) switch_core_add_registration(const char *user, c switch_str_nil(network_proto), switch_core_get_hostname() ); + + + switch_queue_push(sql_manager.sql_queue[0], sql); - switch_cache_db_execute_sql(dbh, sql, NULL); - switch_cache_db_release_db_handle(&dbh); - - free(sql); - return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_core_del_registration(const char *user, const char *realm, const char *token) { - switch_cache_db_handle_t *dbh; char *sql; if (!switch_test_flag((&runtime), SCF_USE_SQL)) { return SWITCH_STATUS_FALSE; } - if (switch_core_db_handle(&dbh) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); - return SWITCH_STATUS_FALSE; - } - if (!zstr(token) && runtime.multiple_registrations) { sql = switch_mprintf("delete from registrations where reg_user='%q' and realm='%q' and hostname='%q' and token='%q'", user, realm, switch_core_get_hostname(), token); } else { sql = switch_mprintf("delete from registrations where reg_user='%q' and realm='%q' and hostname='%q'", user, realm, switch_core_get_hostname()); } - switch_cache_db_execute_sql(dbh, sql, NULL); - switch_cache_db_release_db_handle(&dbh); - - free(sql); + switch_queue_push(sql_manager.sql_queue[0], sql); return SWITCH_STATUS_SUCCESS; } @@ -1718,7 +1700,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_del_registration(const char *user, c SWITCH_DECLARE(switch_status_t) switch_core_expire_registration(int force) { - switch_cache_db_handle_t *dbh; char *sql; time_t now; @@ -1726,11 +1707,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_expire_registration(int force) return SWITCH_STATUS_FALSE; } - if (switch_core_db_handle(&dbh) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); - return SWITCH_STATUS_FALSE; - } - now = switch_epoch_time_now(NULL); if (force) { @@ -1739,10 +1715,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_expire_registration(int force) sql = switch_mprintf("delete from registrations where expires > 0 and expires <= %ld and hostname='%q'", now, switch_core_get_hostname()); } - switch_cache_db_execute_sql(dbh, sql, NULL); - switch_cache_db_release_db_handle(&dbh); - - free(sql); + switch_queue_push(sql_manager.sql_queue[0], sql); return SWITCH_STATUS_SUCCESS; diff --git a/src/switch_nat.c b/src/switch_nat.c index e9d4b2b9ca..ebea68e7cb 100644 --- a/src/switch_nat.c +++ b/src/switch_nat.c @@ -586,7 +586,7 @@ SWITCH_DECLARE(switch_status_t) switch_nat_add_mapping_internal(switch_port_t po switch_event_t *event = NULL; if (!nat_globals.mapping) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "NAT port mapping disabled\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "NAT port mapping disabled\n"); return status; } diff --git a/src/switch_time.c b/src/switch_time.c index d6aa8c8b08..9ec902f4d7 100644 --- a/src/switch_time.c +++ b/src/switch_time.c @@ -61,14 +61,20 @@ static int MONO = 1; static int MONO = 0; #endif +/* clock_nanosleep works badly on some kernels but really well on others. + timerfd seems to work well as long as it exists so if you have timerfd we'll also enable clock_nanosleep by default. +*/ #if defined(HAVE_TIMERFD_CREATE) -// We'll default this to 1 after we have had some positive feedback that it works well -static int TFD = 0; +static int TFD = 1; +#if defined(HAVE_CLOCK_NANOSLEEP) +static int NANO = 1; +#else +static int NANO = 0; +#endif #else static int TFD = 0; -#endif - static int NANO = 0; +#endif static int OFFSET = 0; @@ -326,6 +332,7 @@ SWITCH_DECLARE(void) switch_time_set_timerfd(switch_bool_t enable) #if defined(HAVE_TIMERFD_CREATE) TFD = enable ? 1 : 0; switch_time_sync(); + #else TFD = 0; #endif @@ -1116,6 +1123,10 @@ SWITCH_MODULE_LOAD_FUNCTION(softtimer_load) switch_time_set_cond_yield(SWITCH_FALSE); } + if (TFD) { + switch_clear_flag((&runtime), SCF_CALIBRATE_CLOCK); + } + if (switch_test_flag((&runtime), SCF_CALIBRATE_CLOCK)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Calibrating timer, please wait...\n"); switch_time_calibrate_clock();