freeswitch/src/switch_channel.c

5428 lines
163 KiB
C

/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
* 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 <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Anthony Minessale II <anthm@freeswitch.org>
* Michael Jerris <mike@jerris.com>
*
*
* switch_channel.c -- Media Channel Interface
*
*/
#include <switch.h>
#include <switch_channel.h>
#include <pcre.h>
struct switch_cause_table {
const char *name;
switch_call_cause_t cause;
};
typedef struct switch_device_state_binding_s {
switch_device_state_function_t function;
void *user_data;
struct switch_device_state_binding_s *next;
} switch_device_state_binding_t;
static struct {
switch_memory_pool_t *pool;
switch_hash_t *device_hash;
switch_mutex_t *device_mutex;
switch_device_state_binding_t *device_bindings;
} globals;
static struct switch_cause_table CAUSE_CHART[] = {
{"NONE", SWITCH_CAUSE_NONE},
{"UNALLOCATED_NUMBER", SWITCH_CAUSE_UNALLOCATED_NUMBER},
{"NO_ROUTE_TRANSIT_NET", SWITCH_CAUSE_NO_ROUTE_TRANSIT_NET},
{"NO_ROUTE_DESTINATION", SWITCH_CAUSE_NO_ROUTE_DESTINATION},
{"CHANNEL_UNACCEPTABLE", SWITCH_CAUSE_CHANNEL_UNACCEPTABLE},
{"CALL_AWARDED_DELIVERED", SWITCH_CAUSE_CALL_AWARDED_DELIVERED},
{"NORMAL_CLEARING", SWITCH_CAUSE_NORMAL_CLEARING},
{"USER_BUSY", SWITCH_CAUSE_USER_BUSY},
{"NO_USER_RESPONSE", SWITCH_CAUSE_NO_USER_RESPONSE},
{"NO_ANSWER", SWITCH_CAUSE_NO_ANSWER},
{"SUBSCRIBER_ABSENT", SWITCH_CAUSE_SUBSCRIBER_ABSENT},
{"CALL_REJECTED", SWITCH_CAUSE_CALL_REJECTED},
{"NUMBER_CHANGED", SWITCH_CAUSE_NUMBER_CHANGED},
{"REDIRECTION_TO_NEW_DESTINATION", SWITCH_CAUSE_REDIRECTION_TO_NEW_DESTINATION},
{"EXCHANGE_ROUTING_ERROR", SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR},
{"DESTINATION_OUT_OF_ORDER", SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER},
{"INVALID_NUMBER_FORMAT", SWITCH_CAUSE_INVALID_NUMBER_FORMAT},
{"FACILITY_REJECTED", SWITCH_CAUSE_FACILITY_REJECTED},
{"RESPONSE_TO_STATUS_ENQUIRY", SWITCH_CAUSE_RESPONSE_TO_STATUS_ENQUIRY},
{"NORMAL_UNSPECIFIED", SWITCH_CAUSE_NORMAL_UNSPECIFIED},
{"NORMAL_CIRCUIT_CONGESTION", SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION},
{"NETWORK_OUT_OF_ORDER", SWITCH_CAUSE_NETWORK_OUT_OF_ORDER},
{"NORMAL_TEMPORARY_FAILURE", SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE},
{"SWITCH_CONGESTION", SWITCH_CAUSE_SWITCH_CONGESTION},
{"ACCESS_INFO_DISCARDED", SWITCH_CAUSE_ACCESS_INFO_DISCARDED},
{"REQUESTED_CHAN_UNAVAIL", SWITCH_CAUSE_REQUESTED_CHAN_UNAVAIL},
{"PRE_EMPTED", SWITCH_CAUSE_PRE_EMPTED},
{"FACILITY_NOT_SUBSCRIBED", SWITCH_CAUSE_FACILITY_NOT_SUBSCRIBED},
{"OUTGOING_CALL_BARRED", SWITCH_CAUSE_OUTGOING_CALL_BARRED},
{"INCOMING_CALL_BARRED", SWITCH_CAUSE_INCOMING_CALL_BARRED},
{"BEARERCAPABILITY_NOTAUTH", SWITCH_CAUSE_BEARERCAPABILITY_NOTAUTH},
{"BEARERCAPABILITY_NOTAVAIL", SWITCH_CAUSE_BEARERCAPABILITY_NOTAVAIL},
{"SERVICE_UNAVAILABLE", SWITCH_CAUSE_SERVICE_UNAVAILABLE},
{"BEARERCAPABILITY_NOTIMPL", SWITCH_CAUSE_BEARERCAPABILITY_NOTIMPL},
{"CHAN_NOT_IMPLEMENTED", SWITCH_CAUSE_CHAN_NOT_IMPLEMENTED},
{"FACILITY_NOT_IMPLEMENTED", SWITCH_CAUSE_FACILITY_NOT_IMPLEMENTED},
{"SERVICE_NOT_IMPLEMENTED", SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED},
{"INVALID_CALL_REFERENCE", SWITCH_CAUSE_INVALID_CALL_REFERENCE},
{"INCOMPATIBLE_DESTINATION", SWITCH_CAUSE_INCOMPATIBLE_DESTINATION},
{"INVALID_MSG_UNSPECIFIED", SWITCH_CAUSE_INVALID_MSG_UNSPECIFIED},
{"MANDATORY_IE_MISSING", SWITCH_CAUSE_MANDATORY_IE_MISSING},
{"MESSAGE_TYPE_NONEXIST", SWITCH_CAUSE_MESSAGE_TYPE_NONEXIST},
{"WRONG_MESSAGE", SWITCH_CAUSE_WRONG_MESSAGE},
{"IE_NONEXIST", SWITCH_CAUSE_IE_NONEXIST},
{"INVALID_IE_CONTENTS", SWITCH_CAUSE_INVALID_IE_CONTENTS},
{"WRONG_CALL_STATE", SWITCH_CAUSE_WRONG_CALL_STATE},
{"RECOVERY_ON_TIMER_EXPIRE", SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE},
{"MANDATORY_IE_LENGTH_ERROR", SWITCH_CAUSE_MANDATORY_IE_LENGTH_ERROR},
{"PROTOCOL_ERROR", SWITCH_CAUSE_PROTOCOL_ERROR},
{"INTERWORKING", SWITCH_CAUSE_INTERWORKING},
{"SUCCESS", SWITCH_CAUSE_SUCCESS},
{"ORIGINATOR_CANCEL", SWITCH_CAUSE_ORIGINATOR_CANCEL},
{"CRASH", SWITCH_CAUSE_CRASH},
{"SYSTEM_SHUTDOWN", SWITCH_CAUSE_SYSTEM_SHUTDOWN},
{"LOSE_RACE", SWITCH_CAUSE_LOSE_RACE},
{"MANAGER_REQUEST", SWITCH_CAUSE_MANAGER_REQUEST},
{"BLIND_TRANSFER", SWITCH_CAUSE_BLIND_TRANSFER},
{"ATTENDED_TRANSFER", SWITCH_CAUSE_ATTENDED_TRANSFER},
{"ALLOTTED_TIMEOUT", SWITCH_CAUSE_ALLOTTED_TIMEOUT},
{"USER_CHALLENGE", SWITCH_CAUSE_USER_CHALLENGE},
{"MEDIA_TIMEOUT", SWITCH_CAUSE_MEDIA_TIMEOUT},
{"PICKED_OFF", SWITCH_CAUSE_PICKED_OFF},
{"USER_NOT_REGISTERED", SWITCH_CAUSE_USER_NOT_REGISTERED},
{"PROGRESS_TIMEOUT", SWITCH_CAUSE_PROGRESS_TIMEOUT},
{"INVALID_GATEWAY", SWITCH_CAUSE_INVALID_GATEWAY},
{"GATEWAY_DOWN", SWITCH_CAUSE_GATEWAY_DOWN},
{"INVALID_URL", SWITCH_CAUSE_INVALID_URL},
{"INVALID_PROFILE", SWITCH_CAUSE_INVALID_PROFILE},
{"NO_PICKUP", SWITCH_CAUSE_NO_PICKUP},
{"SRTP_READ_ERROR", SWITCH_CAUSE_SRTP_READ_ERROR},
{NULL, 0}
};
typedef enum {
OCF_HANGUP = (1 << 0)
} opaque_channel_flag_t;
typedef enum {
LP_NEITHER,
LP_ORIGINATOR,
LP_ORIGINATEE
} switch_originator_type_t;
struct switch_channel {
char *name;
switch_call_direction_t direction;
switch_call_direction_t logical_direction;
switch_queue_t *dtmf_queue;
switch_queue_t *dtmf_log_queue;
switch_mutex_t*dtmf_mutex;
switch_mutex_t *flag_mutex;
switch_mutex_t *state_mutex;
switch_mutex_t *thread_mutex;
switch_mutex_t *profile_mutex;
switch_core_session_t *session;
switch_channel_state_t state;
switch_channel_state_t running_state;
switch_channel_callstate_t callstate;
uint32_t flags[CF_FLAG_MAX];
uint32_t caps[CC_FLAG_MAX];
uint8_t state_flags[CF_FLAG_MAX];
uint32_t private_flags;
switch_caller_profile_t *caller_profile;
const switch_state_handler_table_t *state_handlers[SWITCH_MAX_STATE_HANDLERS];
int state_handler_index;
switch_event_t *variables;
switch_event_t *scope_variables;
switch_hash_t *private_hash;
switch_hash_t *app_flag_hash;
switch_call_cause_t hangup_cause;
int vi;
int event_count;
int profile_index;
opaque_channel_flag_t opaque_flags;
switch_originator_type_t last_profile_type;
switch_caller_extension_t *queued_extension;
switch_event_t *app_list;
switch_event_t *api_list;
switch_event_t *var_list;
switch_hold_record_t *hold_record;
switch_device_node_t *device_node;
char *device_id;
};
static void process_device_hup(switch_channel_t *channel);
static void switch_channel_check_device_state(switch_channel_t *channel, switch_channel_callstate_t callstate);
SWITCH_DECLARE(switch_hold_record_t *) switch_channel_get_hold_record(switch_channel_t *channel)
{
return channel->hold_record;
}
SWITCH_DECLARE(const char *) switch_channel_cause2str(switch_call_cause_t cause)
{
uint8_t x;
const char *str = "UNKNOWN";
for (x = 0; x < (sizeof(CAUSE_CHART) / sizeof(struct switch_cause_table)) - 1; x++) {
if (CAUSE_CHART[x].cause == cause) {
str = CAUSE_CHART[x].name;
break;
}
}
return str;
}
SWITCH_DECLARE(switch_call_cause_t) switch_channel_str2cause(const char *str)
{
uint8_t x;
switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;
if (!zstr(str)) {
if (*str > 47 && *str < 58) {
cause = atoi(str);
} else {
for (x = 0; x < (sizeof(CAUSE_CHART) / sizeof(struct switch_cause_table)) - 1 && CAUSE_CHART[x].name; x++) {
if (!strcasecmp(CAUSE_CHART[x].name, str)) {
cause = CAUSE_CHART[x].cause;
break;
}
}
}
}
return cause;
}
SWITCH_DECLARE(switch_call_cause_t) switch_channel_get_cause(switch_channel_t *channel)
{
return channel->hangup_cause;
}
SWITCH_DECLARE(switch_call_cause_t *) switch_channel_get_cause_ptr(switch_channel_t *channel)
{
return &channel->hangup_cause;
}
struct switch_callstate_table {
const char *name;
switch_channel_callstate_t callstate;
};
static struct switch_callstate_table CALLSTATE_CHART[] = {
{"DOWN", CCS_DOWN},
{"DIALING", CCS_DIALING},
{"RINGING", CCS_RINGING},
{"EARLY", CCS_EARLY},
{"ACTIVE", CCS_ACTIVE},
{"HELD", CCS_HELD},
{"RING_WAIT", CCS_RING_WAIT},
{"HANGUP", CCS_HANGUP},
{"UNHELD", CCS_UNHELD},
{NULL, 0}
};
struct switch_device_state_table {
const char *name;
switch_device_state_t device_state;
};
static struct switch_device_state_table DEVICE_STATE_CHART[] = {
{"DOWN", SDS_DOWN},
{"RINGING", SDS_RINGING},
{"ACTIVE", SDS_ACTIVE},
{"ACTIVE_MULTI", SDS_ACTIVE_MULTI},
{"HELD", SDS_HELD},
{"UNHELD", SDS_UNHELD},
{"HANGUP", SDS_HANGUP},
{NULL, 0}
};
SWITCH_DECLARE(void) switch_channel_perform_set_callstate(switch_channel_t *channel, switch_channel_callstate_t callstate,
const char *file, const char *func, int line)
{
switch_event_t *event;
switch_channel_callstate_t o_callstate = channel->callstate;
if (o_callstate == callstate || o_callstate == CCS_HANGUP) return;
channel->callstate = callstate;
if (channel->device_node) {
channel->device_node->callstate = callstate;
}
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_DEBUG,
"(%s) Callstate Change %s -> %s\n", channel->name,
switch_channel_callstate2str(o_callstate), switch_channel_callstate2str(callstate));
switch_channel_check_device_state(channel, channel->callstate);
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_CALLSTATE) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Original-Channel-Call-State", switch_channel_callstate2str(o_callstate));
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Call-State-Number", "%d", callstate);
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
}
SWITCH_DECLARE(switch_channel_callstate_t) switch_channel_get_callstate(switch_channel_t *channel)
{
return channel->callstate;
}
SWITCH_DECLARE(const char *) switch_channel_callstate2str(switch_channel_callstate_t callstate)
{
uint8_t x;
const char *str = "UNKNOWN";
for (x = 0; x < (sizeof(CALLSTATE_CHART) / sizeof(struct switch_cause_table)) - 1; x++) {
if (CALLSTATE_CHART[x].callstate == callstate) {
str = CALLSTATE_CHART[x].name;
break;
}
}
return str;
}
SWITCH_DECLARE(const char *) switch_channel_device_state2str(switch_device_state_t device_state)
{
uint8_t x;
const char *str = "UNKNOWN";
for (x = 0; x < (sizeof(DEVICE_STATE_CHART) / sizeof(struct switch_cause_table)) - 1; x++) {
if (DEVICE_STATE_CHART[x].device_state == device_state) {
str = DEVICE_STATE_CHART[x].name;
break;
}
}
return str;
}
SWITCH_DECLARE(switch_channel_callstate_t) switch_channel_str2callstate(const char *str)
{
uint8_t x;
switch_channel_callstate_t callstate = (switch_channel_callstate_t) SWITCH_CAUSE_NONE;
if (*str > 47 && *str < 58) {
callstate = atoi(str);
} else {
for (x = 0; x < (sizeof(CALLSTATE_CHART) / sizeof(struct switch_callstate_table)) - 1 && CALLSTATE_CHART[x].name; x++) {
if (!strcasecmp(CALLSTATE_CHART[x].name, str)) {
callstate = CALLSTATE_CHART[x].callstate;
break;
}
}
}
return callstate;
}
SWITCH_DECLARE(void) switch_channel_perform_audio_sync(switch_channel_t *channel, const char *file, const char *func, int line)
{
if (switch_channel_media_up(channel)) {
switch_core_session_message_t *msg = NULL;
msg = switch_core_session_alloc(channel->session, sizeof(*msg));
MESSAGE_STAMP_FFL(msg);
msg->message_id = SWITCH_MESSAGE_INDICATE_AUDIO_SYNC;
msg->from = channel->name;
msg->_file = file;
msg->_func = func;
msg->_line = line;
switch_core_session_queue_message(channel->session, msg);
}
}
SWITCH_DECLARE(void) switch_channel_perform_video_sync(switch_channel_t *channel, const char *file, const char *func, int line)
{
if (switch_channel_media_up(channel)) {
switch_core_session_message_t *msg = NULL;
msg = switch_core_session_alloc(channel->session, sizeof(*msg));
MESSAGE_STAMP_FFL(msg);
msg->message_id = SWITCH_MESSAGE_INDICATE_VIDEO_SYNC;
msg->from = channel->name;
msg->_file = file;
msg->_func = func;
msg->_line = line;
switch_core_session_request_video_refresh(channel->session);
switch_core_session_queue_message(channel->session, msg);
}
}
SWITCH_DECLARE(switch_call_cause_t) switch_channel_cause_q850(switch_call_cause_t cause)
{
if (cause <= SWITCH_CAUSE_INTERWORKING) {
return cause;
} else {
return SWITCH_CAUSE_NORMAL_CLEARING;
}
}
SWITCH_DECLARE(switch_call_cause_t) switch_channel_get_cause_q850(switch_channel_t *channel)
{
return switch_channel_cause_q850(channel->hangup_cause);
}
SWITCH_DECLARE(switch_channel_timetable_t *) switch_channel_get_timetable(switch_channel_t *channel)
{
switch_channel_timetable_t *times = NULL;
if (channel->caller_profile) {
switch_mutex_lock(channel->profile_mutex);
times = channel->caller_profile->times;
switch_mutex_unlock(channel->profile_mutex);
}
return times;
}
SWITCH_DECLARE(void) switch_channel_set_direction(switch_channel_t *channel, switch_call_direction_t direction)
{
if (!switch_core_session_in_thread(channel->session)) {
channel->direction = channel->logical_direction = direction;
switch_channel_set_variable(channel, "direction", switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
}
}
SWITCH_DECLARE(switch_call_direction_t) switch_channel_direction(switch_channel_t *channel)
{
return channel->direction;
}
SWITCH_DECLARE(switch_call_direction_t) switch_channel_logical_direction(switch_channel_t *channel)
{
return channel->logical_direction;
}
SWITCH_DECLARE(switch_status_t) switch_channel_alloc(switch_channel_t **channel, switch_call_direction_t direction, switch_memory_pool_t *pool)
{
switch_assert(pool != NULL);
if (((*channel) = switch_core_alloc(pool, sizeof(switch_channel_t))) == 0) {
return SWITCH_STATUS_MEMERR;
}
switch_event_create_plain(&(*channel)->variables, SWITCH_EVENT_CHANNEL_DATA);
switch_core_hash_init(&(*channel)->private_hash);
switch_queue_create(&(*channel)->dtmf_queue, SWITCH_DTMF_LOG_LEN, pool);
switch_queue_create(&(*channel)->dtmf_log_queue, SWITCH_DTMF_LOG_LEN, pool);
switch_mutex_init(&(*channel)->dtmf_mutex, SWITCH_MUTEX_NESTED, pool);
switch_mutex_init(&(*channel)->flag_mutex, SWITCH_MUTEX_NESTED, pool);
switch_mutex_init(&(*channel)->state_mutex, SWITCH_MUTEX_NESTED, pool);
switch_mutex_init(&(*channel)->thread_mutex, SWITCH_MUTEX_NESTED, pool);
switch_mutex_init(&(*channel)->profile_mutex, SWITCH_MUTEX_NESTED, pool);
(*channel)->hangup_cause = SWITCH_CAUSE_NONE;
(*channel)->name = "";
(*channel)->direction = (*channel)->logical_direction = direction;
switch_channel_set_variable(*channel, "direction", switch_channel_direction(*channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_channel_dtmf_lock(switch_channel_t *channel)
{
return switch_mutex_lock(channel->dtmf_mutex);
}
SWITCH_DECLARE(switch_status_t) switch_channel_try_dtmf_lock(switch_channel_t *channel)
{
return switch_mutex_trylock(channel->dtmf_mutex);
}
SWITCH_DECLARE(switch_status_t) switch_channel_dtmf_unlock(switch_channel_t *channel)
{
return switch_mutex_unlock(channel->dtmf_mutex);
}
SWITCH_DECLARE(switch_size_t) switch_channel_has_dtmf(switch_channel_t *channel)
{
switch_size_t has;
switch_mutex_lock(channel->dtmf_mutex);
has = switch_queue_size(channel->dtmf_queue);
switch_mutex_unlock(channel->dtmf_mutex);
return has;
}
SWITCH_DECLARE(switch_status_t) switch_channel_queue_dtmf(switch_channel_t *channel, const switch_dtmf_t *dtmf)
{
switch_status_t status;
void *pop;
switch_dtmf_t new_dtmf = { 0 };
switch_bool_t sensitive = switch_true(switch_channel_get_variable_dup(channel, SWITCH_SENSITIVE_DTMF_VARIABLE, SWITCH_FALSE, -1));
switch_assert(dtmf);
switch_mutex_lock(channel->dtmf_mutex);
new_dtmf = *dtmf;
if (sensitive) {
switch_set_flag((&new_dtmf), DTMF_FLAG_SENSITIVE);
}
if ((status = switch_core_session_recv_dtmf(channel->session, dtmf) != SWITCH_STATUS_SUCCESS)) {
goto done;
}
if (is_dtmf(new_dtmf.digit)) {
switch_dtmf_t *dt;
int x = 0;
if (!sensitive) {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_INFO, "RECV DTMF %c:%d\n", new_dtmf.digit, new_dtmf.duration);
}
if (new_dtmf.digit != 'w' && new_dtmf.digit != 'W') {
if (new_dtmf.duration > switch_core_max_dtmf_duration(0)) {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "%s EXCESSIVE DTMF DIGIT LEN [%d]\n",
switch_channel_get_name(channel), new_dtmf.duration);
new_dtmf.duration = switch_core_max_dtmf_duration(0);
} else if (new_dtmf.duration < switch_core_min_dtmf_duration(0)) {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "%s SHORT DTMF DIGIT LEN [%d]\n",
switch_channel_get_name(channel), new_dtmf.duration);
new_dtmf.duration = switch_core_min_dtmf_duration(0);
}
}
if (!new_dtmf.duration) {
new_dtmf.duration = switch_core_default_dtmf_duration(0);
}
switch_zmalloc(dt, sizeof(*dt));
*dt = new_dtmf;
while (switch_queue_trypush(channel->dtmf_queue, dt) != SWITCH_STATUS_SUCCESS) {
if (switch_queue_trypop(channel->dtmf_queue, &pop) == SWITCH_STATUS_SUCCESS) {
free(pop);
}
if (++x > 100) {
status = SWITCH_STATUS_FALSE;
free(dt);
goto done;
}
}
}
status = SWITCH_STATUS_SUCCESS;
done:
switch_mutex_unlock(channel->dtmf_mutex);
switch_core_media_break(channel->session, SWITCH_MEDIA_TYPE_AUDIO);
return status;
}
SWITCH_DECLARE(switch_status_t) switch_channel_queue_dtmf_string(switch_channel_t *channel, const char *dtmf_string)
{
char *p;
switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0), 0, SWITCH_DTMF_APP };
int sent = 0, dur;
char *string;
int i, argc;
char *argv[256];
if (zstr(dtmf_string)) {
return SWITCH_STATUS_FALSE;
}
dtmf.flags = DTMF_FLAG_SKIP_PROCESS;
if (*dtmf_string == '~') {
dtmf_string++;
dtmf.flags = 0;
}
string = switch_core_session_strdup(channel->session, dtmf_string);
argc = switch_separate_string(string, '+', argv, (sizeof(argv) / sizeof(argv[0])));
for (i = 0; i < argc; i++) {
dtmf.duration = switch_core_default_dtmf_duration(0);
dur = switch_core_default_dtmf_duration(0) / 8;
if ((p = strchr(argv[i], '@'))) {
*p++ = '\0';
if ((dur = atoi(p)) > (int)switch_core_min_dtmf_duration(0) / 8) {
dtmf.duration = dur * 8;
}
}
for (p = argv[i]; p && *p; p++) {
if (is_dtmf(*p)) {
dtmf.digit = *p;
if (dtmf.duration > switch_core_max_dtmf_duration(0)) {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_WARNING, "EXCESSIVE DTMF DIGIT LEN %c %d\n", dtmf.digit, dtmf.duration);
dtmf.duration = switch_core_max_dtmf_duration(0);
} else if (dtmf.duration < switch_core_min_dtmf_duration(0)) {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_WARNING, "SHORT DTMF DIGIT LEN %c %d\n", dtmf.digit, dtmf.duration);
dtmf.duration = switch_core_min_dtmf_duration(0);
} else if (!dtmf.duration) {
dtmf.duration = switch_core_default_dtmf_duration(0);
}
if (switch_channel_queue_dtmf(channel, &dtmf) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "%s Queue dtmf\ndigit=%c ms=%u samples=%u\n",
switch_channel_get_name(channel), dtmf.digit, dur, dtmf.duration);
sent++;
}
}
}
}
return sent ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(switch_status_t) switch_channel_dequeue_dtmf(switch_channel_t *channel, switch_dtmf_t *dtmf)
{
switch_event_t *event;
void *pop;
switch_dtmf_t *dt;
switch_status_t status = SWITCH_STATUS_FALSE;
int sensitive = 0;
switch_mutex_lock(channel->dtmf_mutex);
if (switch_queue_trypop(channel->dtmf_queue, &pop) == SWITCH_STATUS_SUCCESS) {
dt = (switch_dtmf_t *) pop;
*dtmf = *dt;
sensitive = switch_test_flag(dtmf, DTMF_FLAG_SENSITIVE);
if (!sensitive && switch_queue_trypush(channel->dtmf_log_queue, dt) != SWITCH_STATUS_SUCCESS) {
free(dt);
}
dt = NULL;
if (dtmf->duration > switch_core_max_dtmf_duration(0)) {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_WARNING, "%s EXCESSIVE DTMF DIGIT [%c] LEN [%d]\n",
switch_channel_get_name(channel), sensitive ? 'S' : dtmf->digit, dtmf->duration);
dtmf->duration = switch_core_max_dtmf_duration(0);
} else if (dtmf->duration < switch_core_min_dtmf_duration(0)) {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_WARNING, "%s SHORT DTMF DIGIT [%c] LEN [%d]\n",
switch_channel_get_name(channel), sensitive ? 'S' : dtmf->digit, dtmf->duration);
dtmf->duration = switch_core_min_dtmf_duration(0);
} else if (!dtmf->duration) {
dtmf->duration = switch_core_default_dtmf_duration(0);
}
status = SWITCH_STATUS_SUCCESS;
}
switch_mutex_unlock(channel->dtmf_mutex);
if (!sensitive && status == SWITCH_STATUS_SUCCESS && switch_event_create(&event, SWITCH_EVENT_DTMF) == SWITCH_STATUS_SUCCESS) {
const char *dtmf_source_str = NULL;
switch_channel_event_set_data(channel, event);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "DTMF-Digit", "%c", dtmf->digit);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "DTMF-Duration", "%u", dtmf->duration);
switch(dtmf->source) {
case SWITCH_DTMF_INBAND_AUDIO: /* From audio */
dtmf_source_str = "INBAND_AUDIO";
break;
case SWITCH_DTMF_RTP: /* From RTP as a telephone event */
dtmf_source_str = "RTP";
break;
case SWITCH_DTMF_ENDPOINT: /* From endpoint signaling */
dtmf_source_str = "ENDPOINT";
break;
case SWITCH_DTMF_APP: /* Injected by application */
dtmf_source_str = "APP";
break;
case SWITCH_DTMF_UNKNOWN: /* Unknown source */
default:
dtmf_source_str = "UNKNOWN";
break;
}
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "DTMF-Source", "%s", dtmf_source_str);
if (switch_channel_test_flag(channel, CF_DIVERT_EVENTS)) {
switch_core_session_queue_event(channel->session, &event);
} else {
switch_event_fire(&event);
}
}
return status;
}
SWITCH_DECLARE(switch_size_t) switch_channel_dequeue_dtmf_string(switch_channel_t *channel, char *dtmf_str, switch_size_t len)
{
switch_size_t x = 0;
switch_dtmf_t dtmf = { 0 };
memset(dtmf_str, 0, len);
while (x < len - 1 && switch_channel_dequeue_dtmf(channel, &dtmf) == SWITCH_STATUS_SUCCESS) {
dtmf_str[x++] = dtmf.digit;
}
return x;
}
SWITCH_DECLARE(void) switch_channel_flush_dtmf(switch_channel_t *channel)
{
void *pop;
switch_mutex_lock(channel->dtmf_mutex);
while (switch_queue_trypop(channel->dtmf_queue, &pop) == SWITCH_STATUS_SUCCESS) {
switch_dtmf_t *dt = (switch_dtmf_t *) pop;
if (channel->state >= CS_HANGUP || switch_queue_trypush(channel->dtmf_log_queue, dt) != SWITCH_STATUS_SUCCESS) {
free(dt);
}
}
switch_mutex_unlock(channel->dtmf_mutex);
}
SWITCH_DECLARE(void) switch_channel_uninit(switch_channel_t *channel)
{
void *pop;
switch_channel_flush_dtmf(channel);
while (switch_queue_trypop(channel->dtmf_log_queue, &pop) == SWITCH_STATUS_SUCCESS) {
switch_safe_free(pop);
}
if (channel->private_hash) {
switch_core_hash_destroy(&channel->private_hash);
}
if (channel->app_flag_hash) {
switch_core_hash_destroy(&channel->app_flag_hash);
}
switch_mutex_lock(channel->profile_mutex);
switch_event_destroy(&channel->variables);
switch_event_destroy(&channel->api_list);
switch_event_destroy(&channel->var_list);
switch_event_destroy(&channel->app_list);
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(switch_status_t) switch_channel_init(switch_channel_t *channel, switch_core_session_t *session, switch_channel_state_t state,
switch_channel_flag_t flag)
{
switch_assert(channel != NULL);
channel->state = state;
switch_channel_set_flag(channel, flag);
channel->session = session;
channel->running_state = CS_NONE;
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(void) switch_channel_perform_presence(switch_channel_t *channel, const char *rpid, const char *status, const char *id,
const char *file, const char *func, int line)
{
switch_event_t *event;
switch_event_types_t type = SWITCH_EVENT_PRESENCE_IN;
const char *call_info = NULL;
char *call_info_state = "active";
if (switch_channel_test_flag(channel, CF_NO_PRESENCE)) {
return;
}
if (!status) {
type = SWITCH_EVENT_PRESENCE_OUT;
status = "idle";
}
if (!id) {
id = switch_channel_get_variable(channel, "presence_id");
}
if (!id) {
return;
}
call_info = switch_channel_get_variable(channel, "presence_call_info");
if (switch_event_create(&event, type) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", "any");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", __FILE__);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", id);
if (type == SWITCH_EVENT_PRESENCE_IN) {
if (!rpid) {
rpid = "unknown";
}
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", rpid);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "status", status);
}
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
if (!strcasecmp(status, "idle") || !switch_channel_up_nosig(channel)) {
call_info_state = "idle";
} else if (!strcasecmp(status, "hold-private")) {
call_info_state = "held-private";
} else if (!strcasecmp(status, "hold")) {
call_info_state = "held";
} else if (!switch_channel_test_flag(channel, CF_ANSWERED)) {
if (channel->direction == SWITCH_CALL_DIRECTION_OUTBOUND) {
call_info_state = "progressing";
} else {
if (switch_channel_test_flag(channel, CF_SLA_INTERCEPT)) {
call_info_state = "idle";
} else {
call_info_state = "alerting";
}
}
}
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-info-state", call_info_state);
if (call_info) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-info", call_info);
}
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction",
channel->direction == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", channel->event_count++);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Presence-Calling-File", file);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Presence-Calling-Function", func);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Presence-Calling-Line", "%d", line);
if (switch_true(switch_channel_get_variable(channel, "presence_privacy"))) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Presence-Privacy", "true");
}
switch_event_fire(&event);
}
}
SWITCH_DECLARE(void) switch_channel_mark_hold(switch_channel_t *channel, switch_bool_t on)
{
switch_event_t *event;
if (!!on == !!switch_channel_test_flag(channel, CF_LEG_HOLDING)) {
goto end;
}
if (on) {
switch_channel_set_flag(channel, CF_LEG_HOLDING);
} else {
switch_channel_clear_flag(channel, CF_LEG_HOLDING);
}
if (switch_event_create(&event, on ? SWITCH_EVENT_CHANNEL_HOLD : SWITCH_EVENT_CHANNEL_UNHOLD) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
end:
if (on) {
if (switch_true(switch_channel_get_variable(channel, "flip_record_on_hold"))) {
switch_core_session_t *other_session;
if (switch_core_session_get_partner(channel->session, &other_session) == SWITCH_STATUS_SUCCESS) {
switch_core_media_bug_transfer_recordings(channel->session, other_session);
switch_core_session_rwunlock(other_session);
}
}
}
}
SWITCH_DECLARE(const char *) switch_channel_get_hold_music(switch_channel_t *channel)
{
const char *var;
if (!(var = switch_channel_get_variable(channel, SWITCH_TEMP_HOLD_MUSIC_VARIABLE))) {
var = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE);
}
if (!zstr(var)) {
char *expanded = switch_channel_expand_variables(channel, var);
if (expanded != var) {
var = switch_core_session_strdup(channel->session, expanded);
free(expanded);
}
}
return var;
}
SWITCH_DECLARE(const char *) switch_channel_get_hold_music_partner(switch_channel_t *channel)
{
switch_core_session_t *session;
const char *r = NULL;
if (switch_core_session_get_partner(channel->session, &session) == SWITCH_STATUS_SUCCESS) {
r = switch_channel_get_hold_music(switch_core_session_get_channel(session));
switch_core_session_rwunlock(session);
}
return r;
}
SWITCH_DECLARE(void) switch_channel_set_scope_variables(switch_channel_t *channel, switch_event_t **event)
{
switch_mutex_lock(channel->profile_mutex);
if (event && *event) { /* push */
(*event)->next = channel->scope_variables;
channel->scope_variables = *event;
*event = NULL;
} else if (channel->scope_variables) { /* pop */
switch_event_t *top_event = channel->scope_variables;
channel->scope_variables = channel->scope_variables->next;
switch_event_destroy(&top_event);
}
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(switch_status_t) switch_channel_get_scope_variables(switch_channel_t *channel, switch_event_t **event)
{
switch_status_t status = SWITCH_STATUS_FALSE;
switch_event_t *new_event;
switch_mutex_lock(channel->profile_mutex);
if (channel->scope_variables) {
switch_event_t *ep;
switch_event_header_t *hp;
switch_event_create_plain(&new_event, SWITCH_EVENT_CHANNEL_DATA);
status = SWITCH_STATUS_SUCCESS;
*event = new_event;
for (ep = channel->scope_variables; ep; ep = ep->next) {
for (hp = ep->headers; hp; hp = hp->next) {
if (!switch_event_get_header(new_event, hp->value)) {
switch_event_add_header_string(new_event, SWITCH_STACK_BOTTOM, hp->name, hp->value);
}
}
}
}
switch_mutex_unlock(channel->profile_mutex);
return status;
}
SWITCH_DECLARE(const char *) switch_channel_get_variable_dup(switch_channel_t *channel, const char *varname, switch_bool_t dup, int idx)
{
const char *v = NULL, *r = NULL, *vdup = NULL;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
if (!zstr(varname)) {
if (channel->scope_variables) {
switch_event_t *ep;
for (ep = channel->scope_variables; ep; ep = ep->next) {
if ((v = switch_event_get_header_idx(ep, varname, idx))) {
break;
}
}
}
if (!v && (!channel->variables || !(v = switch_event_get_header_idx(channel->variables, varname, idx)))) {
switch_caller_profile_t *cp = switch_channel_get_caller_profile(channel);
if (cp) {
if (!strncmp(varname, "aleg_", 5)) {
cp = cp->originator_caller_profile;
varname += 5;
} else if (!strncmp(varname, "bleg_", 5)) {
cp = cp->originatee_caller_profile;
varname += 5;
}
}
if (!cp || !(v = switch_caller_get_field_by_name(cp, varname))) {
if ((vdup = switch_core_get_variable_pdup(varname, switch_core_session_get_pool(channel->session)))) {
v = vdup;
}
}
}
}
if (dup && v != vdup) {
if (v) {
r = switch_core_session_strdup(channel->session, v);
}
} else {
r = v;
}
switch_mutex_unlock(channel->profile_mutex);
return r;
}
SWITCH_DECLARE(const char *) switch_channel_get_variable_partner(switch_channel_t *channel, const char *varname)
{
const char *uuid;
const char *val = NULL, *r = NULL;
switch_assert(channel != NULL);
if (!zstr(varname)) {
if ((uuid = switch_channel_get_partner_uuid(channel))) {
switch_core_session_t *session;
if ((session = switch_core_session_locate(uuid))) {
switch_channel_t *tchannel = switch_core_session_get_channel(session);
val = switch_channel_get_variable(tchannel, varname);
switch_core_session_rwunlock(session);
}
}
}
if (val)
r = switch_core_session_strdup(channel->session, val);
return r;
}
SWITCH_DECLARE(void) switch_channel_variable_last(switch_channel_t *channel)
{
switch_assert(channel != NULL);
if (!channel->vi) {
return;
}
channel->vi = 0;
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(switch_event_header_t *) switch_channel_variable_first(switch_channel_t *channel)
{
switch_event_header_t *hi = NULL;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
if (channel->variables && (hi = channel->variables->headers)) {
channel->vi = 1;
} else {
switch_mutex_unlock(channel->profile_mutex);
}
return hi;
}
SWITCH_DECLARE(switch_status_t) switch_channel_set_private(switch_channel_t *channel, const char *key, const void *private_info)
{
switch_assert(channel != NULL);
switch_core_hash_insert_locked(channel->private_hash, key, private_info, channel->profile_mutex);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(void *) switch_channel_get_private(switch_channel_t *channel, const char *key)
{
void *val;
switch_assert(channel != NULL);
val = switch_core_hash_find_locked(channel->private_hash, key, channel->profile_mutex);
return val;
}
SWITCH_DECLARE(void *) switch_channel_get_private_partner(switch_channel_t *channel, const char *key)
{
const char *uuid;
void *val = NULL;
switch_assert(channel != NULL);
if ((uuid = switch_channel_get_partner_uuid(channel))) {
switch_core_session_t *session;
if ((session = switch_core_session_locate(uuid))) {
val = switch_core_hash_find_locked(channel->private_hash, key, channel->profile_mutex);
switch_core_session_rwunlock(session);
}
}
return val;
}
SWITCH_DECLARE(switch_status_t) switch_channel_set_name(switch_channel_t *channel, const char *name)
{
const char *old = NULL;
switch_assert(channel != NULL);
if (!zstr(channel->name)) {
old = channel->name;
}
channel->name = NULL;
if (name) {
char *uuid = switch_core_session_get_uuid(channel->session);
channel->name = switch_core_session_strdup(channel->session, name);
switch_channel_set_variable(channel, SWITCH_CHANNEL_NAME_VARIABLE, name);
if (old) {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_NOTICE, "Rename Channel %s->%s [%s]\n", old, name, uuid);
} else {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_NOTICE, "New Channel %s [%s]\n", name, uuid);
}
}
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(char *) switch_channel_get_name(switch_channel_t *channel)
{
switch_assert(channel != NULL);
return (!zstr(channel->name)) ? channel->name : "N/A";
}
SWITCH_DECLARE(switch_status_t) switch_channel_set_profile_var(switch_channel_t *channel, const char *name, const char *val)
{
char *v;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_mutex_lock(channel->profile_mutex);
if (!strcasecmp(name, "device_id") && !zstr(val)) {
const char *device_id;
if (!(device_id = switch_channel_set_device_id(channel, val))) {
/* one time setting */
switch_mutex_unlock(channel->profile_mutex);
return status;
}
val = device_id;
}
if (!zstr(val)) {
v = switch_core_strdup(channel->caller_profile->pool, val);
} else {
v = SWITCH_BLANK_STRING;
}
if (!strcasecmp(name, "dialplan")) {
channel->caller_profile->dialplan = v;
} else if (!strcasecmp(name, "username")) {
channel->caller_profile->username = v;
} else if (!strcasecmp(name, "caller_id_name")) {
channel->caller_profile->caller_id_name = v;
} else if (!strcasecmp(name, "caller_id_number")) {
channel->caller_profile->caller_id_number = v;
} else if (!strcasecmp(name, "callee_id_name")) {
channel->caller_profile->callee_id_name = v;
} else if (!strcasecmp(name, "callee_id_number")) {
channel->caller_profile->callee_id_number = v;
} else if (val && !strcasecmp(name, "caller_ton")) {
channel->caller_profile->caller_ton = (uint8_t) atoi(v);
} else if (val && !strcasecmp(name, "caller_numplan")) {
channel->caller_profile->caller_numplan = (uint8_t) atoi(v);
} else if (val && !strcasecmp(name, "destination_number_ton")) {
channel->caller_profile->destination_number_ton = (uint8_t) atoi(v);
} else if (val && !strcasecmp(name, "destination_number_numplan")) {
channel->caller_profile->destination_number_numplan = (uint8_t) atoi(v);
} else if (!strcasecmp(name, "ani")) {
channel->caller_profile->ani = v;
} else if (!strcasecmp(name, "aniii")) {
channel->caller_profile->aniii = v;
} else if (!strcasecmp(name, "network_addr")) {
channel->caller_profile->network_addr = v;
} else if (!strcasecmp(name, "rdnis")) {
channel->caller_profile->rdnis = v;
} else if (!strcasecmp(name, "destination_number")) {
channel->caller_profile->destination_number = v;
} else if (!strcasecmp(name, "uuid")) {
channel->caller_profile->uuid = v;
} else if (!strcasecmp(name, "source")) {
channel->caller_profile->source = v;
} else if (!strcasecmp(name, "context")) {
channel->caller_profile->context = v;
} else if (!strcasecmp(name, "chan_name")) {
channel->caller_profile->chan_name = v;
} else {
profile_node_t *pn, *n = switch_core_alloc(channel->caller_profile->pool, sizeof(*n));
int var_found;
n->var = switch_core_strdup(channel->caller_profile->pool, name);
n->val = v;
if (!channel->caller_profile->soft) {
channel->caller_profile->soft = n;
} else {
var_found = 0;
for(pn = channel->caller_profile->soft; pn ; pn = pn->next) {
if (!strcasecmp(pn->var,n->var)) {
pn->val = n->val;
var_found = 1;
break;
}
if(!pn->next) {
break;
}
}
if (pn && !pn->next && !var_found) {
pn->next = n;
}
}
}
switch_mutex_unlock(channel->profile_mutex);
return status;
}
SWITCH_DECLARE(void) switch_channel_process_export(switch_channel_t *channel, switch_channel_t *peer_channel,
switch_event_t *var_event, const char *export_varname)
{
const char *export_vars = switch_channel_get_variable(channel, export_varname);
char *cptmp = switch_core_session_strdup(channel->session, export_vars);
int argc;
char *argv[256];
if (zstr(export_vars)) return;
if (var_event) {
switch_event_del_header(var_event, export_varname);
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, export_varname, export_vars);
}
if (peer_channel) {
switch_channel_set_variable(peer_channel, export_varname, export_vars);
}
if ((argc = switch_separate_string(cptmp, ',', argv, (sizeof(argv) / sizeof(argv[0]))))) {
int x;
for (x = 0; x < argc; x++) {
const char *vval;
if ((vval = switch_channel_get_variable(channel, argv[x]))) {
char *vvar = argv[x];
if (!strncasecmp(vvar, "nolocal:", 8)) { /* remove this later ? */
vvar += 8;
} else if (!strncasecmp(vvar, "_nolocal_", 9)) {
vvar += 9;
}
if (var_event) {
switch_event_del_header(var_event, vvar);
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, vvar, vval);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_DEBUG,
"%s EXPORTING[%s] [%s]=[%s] to event\n",
switch_channel_get_name(channel),
export_varname,
vvar, vval);
}
if (peer_channel) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_DEBUG,
"%s EXPORTING[%s] [%s]=[%s] to %s\n",
switch_channel_get_name(channel),
export_varname,
vvar, vval, switch_channel_get_name(peer_channel));
switch_channel_set_variable(peer_channel, vvar, vval);
}
}
}
}
}
SWITCH_DECLARE(switch_status_t) switch_channel_export_variable_var_check(switch_channel_t *channel,
const char *varname, const char *val,
const char *export_varname, switch_bool_t var_check)
{
char *var_name = NULL;
const char *exports;
char *var, *new_exports, *new_exports_d = NULL;
int local = 1;
exports = switch_channel_get_variable(channel, export_varname);
var = switch_core_session_strdup(channel->session, varname);
if (var) {
if (!strncasecmp(var, "nolocal:", 8)) { /* remove this later ? */
var_name = var + 8;
local = 0;
} else if (!strncasecmp(var, "_nolocal_", 9)) {
var_name = var + 9;
local = 0;
} else {
var_name = var;
}
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_DEBUG, "EXPORT (%s) %s[%s]=[%s]\n",
export_varname, local ? "" : "(REMOTE ONLY) ",
var_name ? var_name : "", val ? val : "UNDEF");
switch_channel_set_variable_var_check(channel, var, val, var_check);
if (var && val) {
if (exports) {
new_exports_d = switch_mprintf("%s,%s", exports, var);
new_exports = new_exports_d;
} else {
new_exports = var;
}
switch_channel_set_variable(channel, export_varname, new_exports);
switch_safe_free(new_exports_d);
}
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_channel_export_variable_printf(switch_channel_t *channel, const char *varname,
const char *export_varname, const char *fmt, ...)
{
switch_status_t status = SWITCH_STATUS_FALSE;
char *data = NULL;
va_list ap;
int ret;
switch_assert(channel != NULL);
va_start(ap, fmt);
ret = switch_vasprintf(&data, fmt, ap);
va_end(ap);
if (ret == -1) {
return SWITCH_STATUS_FALSE;
}
status = switch_channel_export_variable(channel, varname, data, export_varname);
free(data);
return status;
}
SWITCH_DECLARE(uint32_t) switch_channel_del_variable_prefix(switch_channel_t *channel, const char *prefix)
{
switch_event_t *event;
switch_event_header_t *hp;
uint32_t r = 0;
switch_channel_get_variables(channel, &event);
if (event) {
for (hp = event->headers; hp; hp = hp->next) {
if (zstr(prefix) || !strncasecmp(hp->name, prefix, strlen(prefix))) {
switch_channel_set_variable(channel, hp->name, NULL);
}
}
}
switch_event_destroy(&event);
return r;
}
SWITCH_DECLARE(switch_status_t) switch_channel_transfer_variable_prefix(switch_channel_t *orig_channel, switch_channel_t *new_channel, const char *prefix)
{
switch_event_header_t *hi = NULL;
int x = 0;
if ((hi = switch_channel_variable_first(orig_channel))) {
for (; hi; hi = hi->next) {
char *var = hi->name;
char *val = hi->value;
if (zstr(prefix) || !strncasecmp(var, prefix, strlen(prefix))) {
x++;
switch_channel_set_variable(new_channel, var, val);
}
}
switch_channel_variable_last(orig_channel);
}
return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(void) switch_channel_set_presence_data_vals(switch_channel_t *channel, const char *presence_data_cols)
{
char *cols[128] = { 0 };
char header_name[128] = "";
int col_count = 0, i = 0;
char *data_copy = NULL;
if (zstr(presence_data_cols)) {
presence_data_cols = switch_channel_get_variable_dup(channel, "presence_data_cols", SWITCH_FALSE, -1);
if (zstr(presence_data_cols)) {
return;
}
}
data_copy = strdup(presence_data_cols);
col_count = switch_split(data_copy, ':', cols);
for (i = 0; i < col_count; i++) {
const char *val = NULL;
switch_snprintf(header_name, sizeof(header_name), "PD-%s", cols[i]);
val = switch_channel_get_variable(channel, cols[i]);
switch_channel_set_profile_var(channel, header_name, val);
}
switch_safe_free(data_copy);
}
SWITCH_DECLARE(switch_status_t) switch_channel_set_variable_var_check(switch_channel_t *channel,
const char *varname, const char *value, switch_bool_t var_check)
{
switch_status_t status = SWITCH_STATUS_FALSE;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
if (channel->variables && !zstr(varname)) {
if (zstr(value)) {
switch_event_del_header(channel->variables, varname);
} else {
int ok = 1;
if (var_check) {
ok = !switch_string_var_check_const(value);
}
if (ok) {
switch_event_add_header_string(channel->variables, SWITCH_STACK_BOTTOM, varname, value);
} else {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_CRIT, "Invalid data (${%s} contains a variable)\n", varname);
}
}
status = SWITCH_STATUS_SUCCESS;
}
switch_mutex_unlock(channel->profile_mutex);
return status;
}
SWITCH_DECLARE(switch_status_t) switch_channel_add_variable_var_check(switch_channel_t *channel,
const char *varname, const char *value, switch_bool_t var_check, switch_stack_t stack)
{
switch_status_t status = SWITCH_STATUS_FALSE;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
if (channel->variables && !zstr(varname)) {
if (zstr(value)) {
switch_event_del_header(channel->variables, varname);
} else {
int ok = 1;
if (var_check) {
ok = !switch_string_var_check_const(value);
}
if (ok) {
switch_event_add_header_string(channel->variables, stack, varname, value);
} else {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_CRIT, "Invalid data (${%s} contains a variable)\n", varname);
}
}
status = SWITCH_STATUS_SUCCESS;
}
switch_mutex_unlock(channel->profile_mutex);
return status;
}
switch_status_t switch_event_base_add_header(switch_event_t *event, switch_stack_t stack, const char *header_name, char *data);
SWITCH_DECLARE(switch_status_t) switch_channel_set_variable_printf(switch_channel_t *channel, const char *varname, const char *fmt, ...)
{
int ret = 0;
char *data;
va_list ap;
switch_status_t status = SWITCH_STATUS_FALSE;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
if (channel->variables && !zstr(varname)) {
switch_event_del_header(channel->variables, varname);
va_start(ap, fmt);
ret = switch_vasprintf(&data, fmt, ap);
va_end(ap);
if (ret == -1) {
switch_mutex_unlock(channel->profile_mutex);
return SWITCH_STATUS_MEMERR;
}
status = switch_channel_set_variable(channel, varname, data);
free(data);
}
switch_mutex_unlock(channel->profile_mutex);
return status;
}
SWITCH_DECLARE(switch_status_t) switch_channel_set_variable_name_printf(switch_channel_t *channel, const char *val, const char *fmt, ...)
{
int ret = 0;
char *varname;
va_list ap;
switch_status_t status = SWITCH_STATUS_FALSE;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
va_start(ap, fmt);
ret = switch_vasprintf(&varname, fmt, ap);
va_end(ap);
if (ret == -1) {
switch_mutex_unlock(channel->profile_mutex);
return SWITCH_STATUS_MEMERR;
}
status = switch_channel_set_variable(channel, varname, val);
free(varname);
switch_mutex_unlock(channel->profile_mutex);
return status;
}
SWITCH_DECLARE(switch_status_t) switch_channel_set_variable_partner_var_check(switch_channel_t *channel,
const char *varname, const char *value, switch_bool_t var_check)
{
const char *uuid;
switch_assert(channel != NULL);
if (!zstr(varname)) {
if ((uuid = switch_channel_get_partner_uuid(channel))) {
switch_core_session_t *session;
if ((session = switch_core_session_locate(uuid))) {
switch_channel_t *tchannel = switch_core_session_get_channel(session);
switch_channel_set_variable_var_check(tchannel, varname, value, var_check);
switch_core_session_rwunlock(session);
}
return SWITCH_STATUS_SUCCESS;
}
}
return SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(uint32_t) switch_channel_test_flag(switch_channel_t *channel, switch_channel_flag_t flag)
{
uint32_t r = 0;
switch_assert(channel != NULL);
switch_mutex_lock(channel->flag_mutex);
r = channel->flags[flag];
switch_mutex_unlock(channel->flag_mutex);
return r;
}
SWITCH_DECLARE(switch_bool_t) switch_channel_set_flag_partner(switch_channel_t *channel, switch_channel_flag_t flag)
{
const char *uuid;
switch_assert(channel != NULL);
if ((uuid = switch_channel_get_partner_uuid(channel))) {
switch_core_session_t *session;
if ((session = switch_core_session_locate(uuid))) {
switch_channel_set_flag(switch_core_session_get_channel(session), flag);
switch_core_session_rwunlock(session);
return SWITCH_TRUE;
}
}
return SWITCH_FALSE;
}
SWITCH_DECLARE(uint32_t) switch_channel_test_flag_partner(switch_channel_t *channel, switch_channel_flag_t flag)
{
const char *uuid;
int r = 0;
switch_assert(channel != NULL);
if ((uuid = switch_channel_get_partner_uuid(channel))) {
switch_core_session_t *session;
if ((session = switch_core_session_locate(uuid))) {
r = switch_channel_test_flag(switch_core_session_get_channel(session), flag);
switch_core_session_rwunlock(session);
}
}
return r;
}
SWITCH_DECLARE(switch_bool_t) switch_channel_clear_flag_partner(switch_channel_t *channel, switch_channel_flag_t flag)
{
const char *uuid;
switch_assert(channel != NULL);
if ((uuid = switch_channel_get_partner_uuid(channel))) {
switch_core_session_t *session;
if ((session = switch_core_session_locate(uuid))) {
switch_channel_clear_flag(switch_core_session_get_channel(session), flag);
switch_core_session_rwunlock(session);
return SWITCH_TRUE;
}
}
return SWITCH_FALSE;
}
SWITCH_DECLARE(void) switch_channel_wait_for_state(switch_channel_t *channel, switch_channel_t *other_channel, switch_channel_state_t want_state)
{
switch_assert(channel);
for (;;) {
if ((channel->state < CS_HANGUP && channel->state == channel->running_state && channel->running_state == want_state) ||
(other_channel && switch_channel_down_nosig(other_channel)) || switch_channel_down(channel)) {
break;
}
switch_cond_next();
}
}
SWITCH_DECLARE(void) switch_channel_wait_for_state_timeout(switch_channel_t *channel, switch_channel_state_t want_state, uint32_t timeout)
{
uint32_t count = 0;
for (;;) {
if ((channel->state == channel->running_state && channel->running_state == want_state) || channel->state >= CS_HANGUP) {
break;
}
switch_channel_check_signal(channel, SWITCH_TRUE);
switch_cond_next();
if (++count >= timeout) {
break;
}
}
}
SWITCH_DECLARE(switch_status_t) switch_channel_wait_for_flag(switch_channel_t *channel,
switch_channel_flag_t want_flag,
switch_bool_t pres, uint32_t to, switch_channel_t *super_channel)
{
if (to) {
to++;
}
for (;;) {
if (pres) {
if (switch_channel_test_flag(channel, want_flag)) {
break;
}
} else {
if (!switch_channel_test_flag(channel, want_flag)) {
break;
}
}
switch_cond_next();
if (super_channel && !switch_channel_ready(super_channel)) {
return SWITCH_STATUS_FALSE;
}
if (switch_channel_down(channel)) {
return SWITCH_STATUS_FALSE;
}
if (to && !--to) {
return SWITCH_STATUS_TIMEOUT;
}
}
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(void) switch_channel_set_cap_value(switch_channel_t *channel, switch_channel_cap_t cap, uint32_t value)
{
switch_assert(channel);
switch_assert(channel->flag_mutex);
switch_mutex_lock(channel->flag_mutex);
channel->caps[cap] = value;
switch_mutex_unlock(channel->flag_mutex);
}
SWITCH_DECLARE(void) switch_channel_clear_cap(switch_channel_t *channel, switch_channel_cap_t cap)
{
switch_assert(channel != NULL);
switch_assert(channel->flag_mutex);
switch_mutex_lock(channel->flag_mutex);
channel->caps[cap] = 0;
switch_mutex_unlock(channel->flag_mutex);
}
SWITCH_DECLARE(uint32_t) switch_channel_test_cap(switch_channel_t *channel, switch_channel_cap_t cap)
{
switch_assert(channel != NULL);
return channel->caps[cap] ? 1 : 0;
}
SWITCH_DECLARE(uint32_t) switch_channel_test_cap_partner(switch_channel_t *channel, switch_channel_cap_t cap)
{
const char *uuid;
int r = 0;
switch_assert(channel != NULL);
if ((uuid = switch_channel_get_partner_uuid(channel))) {
switch_core_session_t *session;
if ((session = switch_core_session_locate(uuid))) {
r = switch_channel_test_cap(switch_core_session_get_channel(session), cap);
switch_core_session_rwunlock(session);
}
}
return r;
}
SWITCH_DECLARE(char *) switch_channel_get_flag_string(switch_channel_t *channel)
{
switch_stream_handle_t stream = { 0 };
char *r;
int i = 0;
SWITCH_STANDARD_STREAM(stream);
switch_mutex_lock(channel->flag_mutex);
for (i = 0; i < CF_FLAG_MAX; i++) {
if (channel->flags[i]) {
stream.write_function(&stream, "%d=%d;", i, channel->flags[i]);
}
}
switch_mutex_unlock(channel->flag_mutex);
r = (char *) stream.data;
if (end_of(r) == ';') {
end_of(r) = '\0';
}
return r;
}
SWITCH_DECLARE(char *) switch_channel_get_cap_string(switch_channel_t *channel)
{
switch_stream_handle_t stream = { 0 };
char *r;
int i = 0;
SWITCH_STANDARD_STREAM(stream);
switch_mutex_lock(channel->flag_mutex);
for (i = 0; i < CC_FLAG_MAX; i++) {
if (channel->caps[i]) {
stream.write_function(&stream, "%d=%d;", i, channel->caps[i]);
}
}
switch_mutex_unlock(channel->flag_mutex);
r = (char *) stream.data;
if (end_of(r) == ';') {
end_of(r) = '\0';
}
return r;
}
SWITCH_DECLARE(void) switch_channel_set_flag_value(switch_channel_t *channel, switch_channel_flag_t flag, uint32_t value)
{
int HELD = 0;
int just_set = 0;
switch_assert(channel);
switch_assert(channel->flag_mutex);
switch_mutex_lock(channel->flag_mutex);
if (flag == CF_LEG_HOLDING && !channel->flags[flag] && channel->flags[CF_ANSWERED]) {
HELD = 1;
}
if (channel->flags[flag] != value) {
just_set = 1;
channel->flags[flag] = value;
}
switch_mutex_unlock(channel->flag_mutex);
if (flag == CF_VIDEO_READY && just_set) {
switch_core_session_request_video_refresh(channel->session);
}
if (flag == CF_ORIGINATOR && switch_channel_test_flag(channel, CF_ANSWERED) && switch_channel_up_nosig(channel)) {
switch_channel_set_callstate(channel, CCS_RING_WAIT);
}
if (flag == CF_DIALPLAN) {
if (channel->direction == SWITCH_CALL_DIRECTION_INBOUND) {
channel->logical_direction = SWITCH_CALL_DIRECTION_OUTBOUND;
if (channel->device_node) {
channel->device_node->direction = SWITCH_CALL_DIRECTION_INBOUND;
}
} else {
channel->logical_direction = SWITCH_CALL_DIRECTION_INBOUND;
if (channel->device_node) {
channel->device_node->direction = SWITCH_CALL_DIRECTION_OUTBOUND;
}
}
}
if (HELD) {
switch_hold_record_t *hr;
const char *brto = switch_channel_get_partner_uuid(channel);
switch_channel_set_callstate(channel, CCS_HELD);
switch_mutex_lock(channel->profile_mutex);
channel->caller_profile->times->last_hold = switch_time_now();
hr = switch_core_session_alloc(channel->session, sizeof(*hr));
hr->on = switch_time_now();
if (brto) {
hr->uuid = switch_core_session_strdup(channel->session, brto);
}
if (channel->hold_record) {
hr->next = channel->hold_record;
}
channel->hold_record = hr;
switch_mutex_unlock(channel->profile_mutex);
}
if (flag == CF_OUTBOUND) {
switch_channel_set_variable(channel, "is_outbound", "true");
}
if (flag == CF_RECOVERED) {
switch_channel_set_variable(channel, "recovered", "true");
}
if (flag == CF_VIDEO_ECHO || flag == CF_VIDEO_BLANK || flag == CF_VIDEO_DECODED_READ || flag == CF_VIDEO_PASSIVE) {
switch_core_session_start_video_thread(channel->session);
}
if (flag == CF_VIDEO_DECODED_READ && channel->flags[CF_VIDEO]) {
switch_core_session_request_video_refresh(channel->session);
}
}
SWITCH_DECLARE(void) switch_channel_set_flag_recursive(switch_channel_t *channel, switch_channel_flag_t flag)
{
switch_assert(channel);
switch_assert(channel->flag_mutex);
switch_mutex_lock(channel->flag_mutex);
channel->flags[flag]++;
switch_mutex_unlock(channel->flag_mutex);
if (flag == CF_OUTBOUND) {
switch_channel_set_variable(channel, "is_outbound", "true");
}
if (flag == CF_RECOVERED) {
switch_channel_set_variable(channel, "recovered", "true");
}
}
SWITCH_DECLARE(void) switch_channel_set_private_flag(switch_channel_t *channel, uint32_t flags)
{
switch_assert(channel != NULL);
switch_mutex_lock(channel->flag_mutex);
channel->private_flags |= flags;
switch_mutex_unlock(channel->flag_mutex);
}
SWITCH_DECLARE(void) switch_channel_clear_private_flag(switch_channel_t *channel, uint32_t flags)
{
switch_assert(channel != NULL);
switch_mutex_lock(channel->flag_mutex);
channel->private_flags &= ~flags;
switch_mutex_unlock(channel->flag_mutex);
}
SWITCH_DECLARE(int) switch_channel_test_private_flag(switch_channel_t *channel, uint32_t flags)
{
switch_assert(channel != NULL);
return (channel->private_flags & flags);
}
SWITCH_DECLARE(void) switch_channel_set_app_flag_key(const char *key, switch_channel_t *channel, uint32_t flags)
{
uint32_t *flagp = NULL;
switch_byte_t new = 0;
switch_assert(channel != NULL);
switch_mutex_lock(channel->flag_mutex);
if (!channel->app_flag_hash) {
switch_core_hash_init(&channel->app_flag_hash);
new++;
}
if (new || !(flagp = switch_core_hash_find(channel->app_flag_hash, key))) {
flagp = switch_core_session_alloc(channel->session, sizeof(uint32_t));
switch_core_hash_insert(channel->app_flag_hash, key, flagp);
}
switch_assert(flagp);
*flagp |= flags;
switch_mutex_unlock(channel->flag_mutex);
}
SWITCH_DECLARE(void) switch_channel_clear_app_flag_key(const char *key, switch_channel_t *channel, uint32_t flags)
{
uint32_t *flagp = NULL;
switch_assert(channel != NULL);
switch_mutex_lock(channel->flag_mutex);
if (channel->app_flag_hash && (flagp = switch_core_hash_find(channel->app_flag_hash, key))) {
if (!flags) {
*flagp = 0;
} else {
*flagp &= ~flags;
}
}
switch_mutex_unlock(channel->flag_mutex);
}
SWITCH_DECLARE(int) switch_channel_test_app_flag_key(const char *key, switch_channel_t *channel, uint32_t flags)
{
int r = 0;
uint32_t *flagp = NULL;
switch_assert(channel != NULL);
switch_mutex_lock(channel->flag_mutex);
if (channel->app_flag_hash && (flagp = switch_core_hash_find(channel->app_flag_hash, key))) {
r = (*flagp & flags);
}
switch_mutex_unlock(channel->flag_mutex);
return r;
}
SWITCH_DECLARE(void) switch_channel_set_state_flag(switch_channel_t *channel, switch_channel_flag_t flag)
{
switch_assert(channel != NULL);
switch_mutex_lock(channel->flag_mutex);
channel->state_flags[0] = 1;
channel->state_flags[flag] = 1;
switch_mutex_unlock(channel->flag_mutex);
}
SWITCH_DECLARE(void) switch_channel_clear_state_flag(switch_channel_t *channel, switch_channel_flag_t flag)
{
switch_assert(channel != NULL);
switch_mutex_lock(channel->flag_mutex);
channel->state_flags[flag] = 0;
switch_mutex_unlock(channel->flag_mutex);
}
SWITCH_DECLARE(void) switch_channel_clear_flag(switch_channel_t *channel, switch_channel_flag_t flag)
{
int ACTIVE = 0;
int CLEAR = 0;
switch_assert(channel != NULL);
switch_assert(channel->flag_mutex);
switch_mutex_lock(channel->flag_mutex);
if (flag == CF_LEG_HOLDING && channel->flags[flag] && channel->flags[CF_ANSWERED]) {
ACTIVE = 1;
}
if (flag == CF_VIDEO_PASSIVE && channel->flags[CF_VIDEO]) {
channel->flags[CF_VIDEO_READY] = 1;
if (channel->flags[flag]) {
CLEAR = 1;
}
}
channel->flags[flag] = 0;
switch_mutex_unlock(channel->flag_mutex);
if (flag == CF_DIALPLAN) {
if (channel->direction == SWITCH_CALL_DIRECTION_OUTBOUND) {
channel->logical_direction = SWITCH_CALL_DIRECTION_OUTBOUND;
if (channel->device_node) {
channel->device_node->direction = SWITCH_CALL_DIRECTION_INBOUND;
}
}
}
if (ACTIVE) {
switch_channel_set_callstate(channel, CCS_UNHELD);
switch_mutex_lock(channel->profile_mutex);
if (channel->caller_profile->times->last_hold) {
channel->caller_profile->times->hold_accum += (switch_time_now() - channel->caller_profile->times->last_hold);
}
if (channel->hold_record) {
channel->hold_record->off = switch_time_now();
}
if (switch_channel_test_flag(channel, CF_PROXY_MODE) && switch_channel_test_flag(channel, CF_BRIDGED)) {
switch_channel_set_callstate(channel, CCS_ACTIVE);
}
switch_mutex_unlock(channel->profile_mutex);
}
if (flag == CF_ORIGINATOR && switch_channel_test_flag(channel, CF_ANSWERED) && switch_channel_up_nosig(channel)) {
switch_channel_set_callstate(channel, CCS_ACTIVE);
}
if (flag == CF_OUTBOUND) {
switch_channel_set_variable(channel, "is_outbound", NULL);
}
if (flag == CF_RECOVERED) {
switch_channel_set_variable(channel, "recovered", NULL);
}
if (flag == CF_VIDEO_PASSIVE && CLEAR) {
switch_core_session_wake_video_thread(channel->session);
}
if (flag == CF_RECOVERING && !channel->hangup_cause) {
switch_core_recovery_track(channel->session);
}
}
SWITCH_DECLARE(void) switch_channel_clear_flag_recursive(switch_channel_t *channel, switch_channel_flag_t flag)
{
switch_assert(channel != NULL);
switch_assert(channel->flag_mutex);
switch_mutex_lock(channel->flag_mutex);
if (channel->flags[flag]) {
channel->flags[flag]--;
}
switch_mutex_unlock(channel->flag_mutex);
if (flag == CF_OUTBOUND) {
switch_channel_set_variable(channel, "is_outbound", NULL);
}
}
SWITCH_DECLARE(switch_channel_state_t) switch_channel_get_state(switch_channel_t *channel)
{
switch_channel_state_t state;
switch_assert(channel != NULL);
state = channel->state;
return state;
}
SWITCH_DECLARE(switch_channel_state_t) switch_channel_get_running_state(switch_channel_t *channel)
{
switch_channel_state_t state;
switch_assert(channel != NULL);
state = channel->running_state;
return state;
}
SWITCH_DECLARE(int) switch_channel_state_change_pending(switch_channel_t *channel)
{
if (switch_channel_down_nosig(channel) || !switch_core_session_in_thread(channel->session)) {
return 0;
}
return channel->running_state != channel->state;
}
SWITCH_DECLARE(int) switch_channel_check_signal(switch_channel_t *channel, switch_bool_t in_thread_only)
{
switch_ivr_parse_signal_data(channel->session, SWITCH_FALSE, in_thread_only);
return 0;
}
SWITCH_DECLARE(int) switch_channel_test_ready(switch_channel_t *channel, switch_bool_t check_ready, switch_bool_t check_media)
{
int ret = 0;
switch_assert(channel != NULL);
switch_channel_check_signal(channel, SWITCH_TRUE);
if (check_media) {
ret = ((switch_channel_test_flag(channel, CF_ANSWERED) ||
switch_channel_test_flag(channel, CF_EARLY_MEDIA)) && !switch_channel_test_flag(channel, CF_PROXY_MODE) &&
switch_core_session_get_read_codec(channel->session) && switch_core_session_get_write_codec(channel->session));
if (!ret)
return ret;
}
if (!check_ready)
return ret;
ret = 0;
if (!channel->hangup_cause && channel->state > CS_ROUTING && channel->state < CS_HANGUP && channel->state != CS_RESET &&
!switch_channel_test_flag(channel, CF_TRANSFER) && !switch_channel_test_flag(channel, CF_NOT_READY) &&
!switch_channel_state_change_pending(channel)) {
ret++;
}
return ret;
}
static const char *state_names[] = {
"CS_NEW",
"CS_INIT",
"CS_ROUTING",
"CS_SOFT_EXECUTE",
"CS_EXECUTE",
"CS_EXCHANGE_MEDIA",
"CS_PARK",
"CS_CONSUME_MEDIA",
"CS_HIBERNATE",
"CS_RESET",
"CS_HANGUP",
"CS_REPORTING",
"CS_DESTROY",
"CS_NONE",
NULL
};
SWITCH_DECLARE(const char *) switch_channel_state_name(switch_channel_state_t state)
{
return state_names[state];
}
SWITCH_DECLARE(switch_channel_state_t) switch_channel_name_state(const char *name)
{
uint32_t x = 0;
for (x = 0; state_names[x]; x++) {
if (!strcasecmp(state_names[x], name)) {
return (switch_channel_state_t) x;
}
}
return CS_DESTROY;
}
static inline void careful_set(switch_channel_t *channel, switch_channel_state_t *state, switch_channel_state_t val) {
if (switch_mutex_trylock(channel->thread_mutex) == SWITCH_STATUS_SUCCESS) {
*state = val;
switch_mutex_unlock(channel->thread_mutex);
} else {
switch_mutex_t *mutex = switch_core_session_get_mutex(channel->session);
int x = 0;
for (x = 0; x < 100; x++) {
if (switch_mutex_trylock(mutex) == SWITCH_STATUS_SUCCESS) {
*state = val;
switch_mutex_unlock(mutex);
break;
} else {
switch_cond_next();
}
}
if (x == 100) {
*state = val;
}
}
}
SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_running_state(switch_channel_t *channel, switch_channel_state_t state,
const char *file, const char *func, int line)
{
int x;
switch_mutex_lock(channel->flag_mutex);
if (channel->state_flags[0]) {
for (x = 1; x < CF_FLAG_MAX; x++) {
if (channel->state_flags[x]) {
channel->flags[x] = 1;
channel->state_flags[x] = 0;
}
}
channel->state_flags[0] = 0;
}
switch_mutex_unlock(channel->flag_mutex);
switch_channel_clear_flag(channel, CF_TAGGED);
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_DEBUG, "(%s) Running State Change %s (Cur %d Tot %" SWITCH_SIZE_T_FMT ")\n",
channel->name, state_names[state], switch_core_session_count(), switch_core_session_id() - 1);
switch_mutex_lock(channel->state_mutex);
careful_set(channel, &channel->running_state, state);
if (state <= CS_DESTROY) {
switch_event_t *event;
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
if (state < CS_HANGUP) {
if (state == CS_ROUTING) {
switch_channel_set_callstate(channel, CCS_RINGING);
} else if (switch_channel_test_flag(channel, CF_ANSWERED)) {
switch_channel_set_callstate(channel, CCS_ACTIVE);
} else if (switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
switch_channel_set_callstate(channel, CCS_EARLY);
}
}
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_STATE) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
}
switch_mutex_unlock(channel->state_mutex);
return (switch_channel_state_t) SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_state(switch_channel_t *channel,
const char *file, const char *func, int line, switch_channel_state_t state)
{
switch_channel_state_t last_state;
int ok = 0;
switch_assert(channel != NULL);
switch_assert(state <= CS_DESTROY);
switch_mutex_lock(channel->state_mutex);
last_state = channel->state;
switch_assert(last_state <= CS_DESTROY);
if (last_state == state) {
goto done;
}
if (last_state >= CS_HANGUP && state < last_state) {
goto done;
}
/* STUB for more dev
case CS_INIT:
switch(state) {
case CS_NEW:
case CS_INIT:
case CS_EXCHANGE_MEDIA:
case CS_SOFT_EXECUTE:
case CS_ROUTING:
case CS_EXECUTE:
case CS_HANGUP:
case CS_DESTROY:
default:
break;
}
break;
*/
switch (last_state) {
case CS_NEW:
case CS_RESET:
switch (state) {
default:
ok++;
break;
}
break;
case CS_INIT:
switch (state) {
case CS_EXCHANGE_MEDIA:
case CS_SOFT_EXECUTE:
case CS_ROUTING:
case CS_EXECUTE:
case CS_PARK:
case CS_CONSUME_MEDIA:
case CS_HIBERNATE:
case CS_RESET:
ok++;
default:
break;
}
break;
case CS_EXCHANGE_MEDIA:
switch (state) {
case CS_SOFT_EXECUTE:
case CS_ROUTING:
case CS_EXECUTE:
case CS_PARK:
case CS_CONSUME_MEDIA:
case CS_HIBERNATE:
case CS_RESET:
ok++;
default:
break;
}
break;
case CS_SOFT_EXECUTE:
switch (state) {
case CS_EXCHANGE_MEDIA:
case CS_ROUTING:
case CS_EXECUTE:
case CS_PARK:
case CS_CONSUME_MEDIA:
case CS_HIBERNATE:
case CS_RESET:
ok++;
default:
break;
}
break;
case CS_PARK:
switch (state) {
case CS_EXCHANGE_MEDIA:
case CS_ROUTING:
case CS_EXECUTE:
case CS_SOFT_EXECUTE:
case CS_HIBERNATE:
case CS_RESET:
case CS_CONSUME_MEDIA:
ok++;
default:
break;
}
break;
case CS_CONSUME_MEDIA:
switch (state) {
case CS_EXCHANGE_MEDIA:
case CS_ROUTING:
case CS_EXECUTE:
case CS_SOFT_EXECUTE:
case CS_HIBERNATE:
case CS_RESET:
case CS_PARK:
ok++;
default:
break;
}
break;
case CS_HIBERNATE:
switch (state) {
case CS_EXCHANGE_MEDIA:
case CS_INIT:
case CS_ROUTING:
case CS_EXECUTE:
case CS_SOFT_EXECUTE:
case CS_PARK:
case CS_CONSUME_MEDIA:
case CS_RESET:
ok++;
default:
break;
}
break;
case CS_ROUTING:
switch (state) {
case CS_EXCHANGE_MEDIA:
case CS_EXECUTE:
case CS_SOFT_EXECUTE:
case CS_PARK:
case CS_CONSUME_MEDIA:
case CS_HIBERNATE:
case CS_RESET:
ok++;
default:
break;
}
break;
case CS_EXECUTE:
switch (state) {
case CS_EXCHANGE_MEDIA:
case CS_SOFT_EXECUTE:
case CS_ROUTING:
case CS_PARK:
case CS_CONSUME_MEDIA:
case CS_HIBERNATE:
case CS_RESET:
ok++;
default:
break;
}
break;
case CS_HANGUP:
switch (state) {
case CS_REPORTING:
case CS_DESTROY:
ok++;
default:
break;
}
break;
case CS_REPORTING:
switch (state) {
case CS_DESTROY:
ok++;
default:
break;
}
break;
default:
break;
}
if (ok) {
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_DEBUG, "(%s) State Change %s -> %s\n",
channel->name, state_names[last_state], state_names[state]);
careful_set(channel, &channel->state, state);
if (state == CS_HANGUP && !channel->hangup_cause) {
channel->hangup_cause = SWITCH_CAUSE_NORMAL_CLEARING;
}
if (state <= CS_DESTROY) {
switch_core_session_signal_state_change(channel->session);
}
} else {
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_WARNING,
"(%s) Invalid State Change %s -> %s\n", channel->name, state_names[last_state], state_names[state]);
/* we won't tolerate an invalid state change so we can make sure we are as robust as a nice cup of dark coffee! */
/* not cool lets crash this bad boy and figure out wtf is going on */
switch_assert(channel->state >= CS_HANGUP);
}
done:
switch_mutex_unlock(channel->state_mutex);
return channel->state;
}
SWITCH_DECLARE(void) switch_channel_state_thread_lock(switch_channel_t *channel)
{
switch_mutex_lock(channel->thread_mutex);
}
SWITCH_DECLARE(switch_status_t) switch_channel_state_thread_trylock(switch_channel_t *channel)
{
return switch_mutex_trylock(channel->thread_mutex);
}
SWITCH_DECLARE(void) switch_channel_state_thread_unlock(switch_channel_t *channel)
{
switch_mutex_unlock(channel->thread_mutex);
}
SWITCH_DECLARE(void) switch_channel_event_set_basic_data(switch_channel_t *channel, switch_event_t *event)
{
switch_caller_profile_t *caller_profile, *originator_caller_profile = NULL, *originatee_caller_profile = NULL;
switch_codec_implementation_t impl = { 0 };
char state_num[25];
const char *v;
switch_mutex_lock(channel->profile_mutex);
if ((caller_profile = channel->caller_profile)) {
originator_caller_profile = caller_profile->originator_caller_profile;
originatee_caller_profile = caller_profile->originatee_caller_profile;
}
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-State", switch_channel_state_name(channel->running_state));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Call-State", switch_channel_callstate2str(channel->callstate));
switch_snprintf(state_num, sizeof(state_num), "%d", channel->state);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-State-Number", state_num);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Name", switch_channel_get_name(channel));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(channel->session));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Call-Direction",
channel->direction == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Presence-Call-Direction",
channel->direction == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-HIT-Dialplan",
switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND ||
switch_channel_test_flag(channel, CF_DIALPLAN) ? "true" : "false");
if ((v = switch_channel_get_variable_dup(channel, "presence_id", SWITCH_FALSE, -1))) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Presence-ID", v);
}
if ((v = switch_channel_get_variable_dup(channel, "presence_data", SWITCH_FALSE, -1))) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Presence-Data", v);
}
if ((v = switch_channel_get_variable_dup(channel, "presence_data_cols", SWITCH_FALSE, -1))) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Presence-Data-Cols", v);
switch_event_add_presence_data_cols(channel, event, "PD-");
}
if ((v = switch_channel_get_variable_dup(channel, "call_uuid", SWITCH_FALSE, -1))) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Call-UUID", v);
} else {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Call-UUID", switch_core_session_get_uuid(channel->session));
}
if (switch_channel_down_nosig(channel)) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Answer-State", "hangup");
} else if (switch_channel_test_flag(channel, CF_ANSWERED)) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Answer-State", "answered");
} else if (switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Answer-State", "early");
} else {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Answer-State", "ringing");
}
if (channel->hangup_cause) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Hangup-Cause", switch_channel_cause2str(channel->hangup_cause));
}
switch_core_session_get_read_impl(channel->session, &impl);
if (impl.iananame) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Read-Codec-Name", impl.iananame);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Read-Codec-Rate", "%u", impl.actual_samples_per_second);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Read-Codec-Bit-Rate", "%d", impl.bits_per_second);
}
switch_core_session_get_write_impl(channel->session, &impl);
if (impl.iananame) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Write-Codec-Name", impl.iananame);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Write-Codec-Rate", "%u", impl.actual_samples_per_second);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Write-Codec-Bit-Rate", "%d", impl.bits_per_second);
}
/* Index Caller's Profile */
if (caller_profile) {
switch_caller_profile_event_set_data(caller_profile, "Caller", event);
}
/* Index Originator/ee's Profile */
if (originator_caller_profile && channel->last_profile_type == LP_ORIGINATOR) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Other-Type", "originator");
switch_caller_profile_event_set_data(originator_caller_profile, "Other-Leg", event);
} else if (originatee_caller_profile && channel->last_profile_type == LP_ORIGINATEE) { /* Index Originatee's Profile */
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Other-Type", "originatee");
switch_caller_profile_event_set_data(originatee_caller_profile, "Other-Leg", event);
}
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(void) switch_channel_event_set_extended_data(switch_channel_t *channel, switch_event_t *event)
{
switch_event_header_t *hi;
int global_verbose_events = -1;
switch_mutex_lock(channel->profile_mutex);
switch_core_session_ctl(SCSC_VERBOSE_EVENTS, &global_verbose_events);
if (global_verbose_events ||
switch_channel_test_flag(channel, CF_VERBOSE_EVENTS) ||
switch_event_get_header(event, "presence-data-cols") ||
event->event_id == SWITCH_EVENT_CHANNEL_CREATE ||
event->event_id == SWITCH_EVENT_CHANNEL_ORIGINATE ||
event->event_id == SWITCH_EVENT_CHANNEL_UUID ||
event->event_id == SWITCH_EVENT_CHANNEL_ANSWER ||
event->event_id == SWITCH_EVENT_CHANNEL_PARK ||
event->event_id == SWITCH_EVENT_CHANNEL_UNPARK ||
event->event_id == SWITCH_EVENT_CHANNEL_BRIDGE ||
event->event_id == SWITCH_EVENT_CHANNEL_UNBRIDGE ||
event->event_id == SWITCH_EVENT_CHANNEL_PROGRESS ||
event->event_id == SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA ||
event->event_id == SWITCH_EVENT_CHANNEL_HANGUP ||
event->event_id == SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE ||
event->event_id == SWITCH_EVENT_REQUEST_PARAMS ||
event->event_id == SWITCH_EVENT_CHANNEL_DATA ||
event->event_id == SWITCH_EVENT_CHANNEL_EXECUTE ||
event->event_id == SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE ||
event->event_id == SWITCH_EVENT_CHANNEL_DESTROY ||
event->event_id == SWITCH_EVENT_SESSION_HEARTBEAT ||
event->event_id == SWITCH_EVENT_API ||
event->event_id == SWITCH_EVENT_RECORD_START ||
event->event_id == SWITCH_EVENT_RECORD_STOP ||
event->event_id == SWITCH_EVENT_PLAYBACK_START ||
event->event_id == SWITCH_EVENT_PLAYBACK_STOP ||
event->event_id == SWITCH_EVENT_CALL_UPDATE ||
event->event_id == SWITCH_EVENT_MEDIA_BUG_START ||
event->event_id == SWITCH_EVENT_MEDIA_BUG_STOP ||
event->event_id == SWITCH_EVENT_CHANNEL_HOLD ||
event->event_id == SWITCH_EVENT_CHANNEL_UNHOLD ||
event->event_id == SWITCH_EVENT_TEXT ||
event->event_id == SWITCH_EVENT_CUSTOM) {
/* Index Variables */
if (channel->scope_variables) {
switch_event_t *ep;
for (ep = channel->scope_variables; ep; ep = ep->next) {
for (hi = ep->headers; hi; hi = hi->next) {
char buf[1024];
char *vvar = NULL, *vval = NULL;
vvar = (char *) hi->name;
vval = (char *) hi->value;
switch_assert(vvar && vval);
switch_snprintf(buf, sizeof(buf), "scope_variable_%s", vvar);
if (!switch_event_get_header(event, buf)) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, buf, vval);
}
}
}
}
if (channel->variables) {
for (hi = channel->variables->headers; hi; hi = hi->next) {
char buf[1024];
char *vvar = NULL, *vval = NULL;
vvar = (char *) hi->name;
vval = (char *) hi->value;
switch_assert(vvar && vval);
switch_snprintf(buf, sizeof(buf), "variable_%s", vvar);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, buf, vval);
}
}
}
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(void) switch_channel_event_set_data(switch_channel_t *channel, switch_event_t *event)
{
switch_mutex_lock(channel->profile_mutex);
switch_channel_event_set_basic_data(channel, event);
switch_channel_event_set_extended_data(channel, event);
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(void) switch_channel_step_caller_profile(switch_channel_t *channel)
{
switch_caller_profile_t *cp;
switch_mutex_lock(channel->profile_mutex);
cp = switch_caller_profile_clone(channel->session, channel->caller_profile);
switch_mutex_unlock(channel->profile_mutex);
switch_channel_set_caller_profile(channel, cp);
}
SWITCH_DECLARE(void) switch_channel_set_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
char *uuid = NULL;
switch_assert(channel != NULL);
switch_assert(channel->session != NULL);
switch_mutex_lock(channel->profile_mutex);
switch_assert(caller_profile != NULL);
caller_profile->direction = channel->direction;
caller_profile->logical_direction = channel->logical_direction;
uuid = switch_core_session_get_uuid(channel->session);
if (!caller_profile->uuid || strcasecmp(caller_profile->uuid, uuid)) {
caller_profile->uuid = switch_core_session_strdup(channel->session, uuid);
}
if (!caller_profile->chan_name || strcasecmp(caller_profile->chan_name, channel->name)) {
caller_profile->chan_name = switch_core_session_strdup(channel->session, channel->name);
}
if (!caller_profile->context) {
caller_profile->context = switch_core_session_strdup(channel->session, "default");
}
if (!caller_profile->times) {
caller_profile->times = (switch_channel_timetable_t *) switch_core_session_alloc(channel->session, sizeof(*caller_profile->times));
caller_profile->times->profile_created = switch_micro_time_now();
}
if (channel->caller_profile && channel->caller_profile->times) {
channel->caller_profile->times->transferred = caller_profile->times->profile_created;
caller_profile->times->answered = channel->caller_profile->times->answered;
caller_profile->times->progress = channel->caller_profile->times->progress;
caller_profile->times->progress_media = channel->caller_profile->times->progress_media;
caller_profile->times->created = channel->caller_profile->times->created;
caller_profile->times->hungup = channel->caller_profile->times->hungup;
if (channel->caller_profile->caller_extension) {
switch_caller_extension_clone(&caller_profile->caller_extension, channel->caller_profile->caller_extension, caller_profile->pool);
}
} else {
caller_profile->times->created = switch_micro_time_now();
}
caller_profile->next = channel->caller_profile;
channel->caller_profile = caller_profile;
caller_profile->profile_index = switch_core_sprintf(caller_profile->pool, "%d", ++channel->profile_index);
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_caller_profile(switch_channel_t *channel)
{
switch_caller_profile_t *profile;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
if ((profile = channel->caller_profile) && profile->hunt_caller_profile) {
profile = profile->hunt_caller_profile;
}
switch_mutex_unlock(channel->profile_mutex);
return profile;
}
SWITCH_DECLARE(void) switch_channel_set_originator_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
switch_assert(channel != NULL);
switch_assert(channel->caller_profile != NULL);
switch_mutex_lock(channel->profile_mutex);
if (!caller_profile->times) {
caller_profile->times = (switch_channel_timetable_t *) switch_core_alloc(caller_profile->pool, sizeof(*caller_profile->times));
}
if (channel->caller_profile) {
caller_profile->next = channel->caller_profile->originator_caller_profile;
channel->caller_profile->originator_caller_profile = caller_profile;
channel->last_profile_type = LP_ORIGINATOR;
}
switch_assert(channel->caller_profile->originator_caller_profile->next != channel->caller_profile->originator_caller_profile);
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(void) switch_channel_set_hunt_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
switch_assert(channel != NULL);
switch_assert(channel->caller_profile != NULL);
switch_mutex_lock(channel->profile_mutex);
channel->caller_profile->hunt_caller_profile = NULL;
if (channel->caller_profile && caller_profile) {
caller_profile->direction = channel->direction;
caller_profile->logical_direction = channel->logical_direction;
channel->caller_profile->hunt_caller_profile = caller_profile;
}
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(void) switch_channel_set_origination_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
switch_assert(channel != NULL);
switch_assert(channel->caller_profile != NULL);
switch_mutex_lock(channel->profile_mutex);
if (channel->caller_profile) {
caller_profile->next = channel->caller_profile->origination_caller_profile;
channel->caller_profile->origination_caller_profile = caller_profile;
}
switch_assert(channel->caller_profile->origination_caller_profile->next != channel->caller_profile->origination_caller_profile);
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_origination_caller_profile(switch_channel_t *channel)
{
switch_caller_profile_t *profile = NULL;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
if (channel->caller_profile) {
profile = channel->caller_profile->origination_caller_profile;
}
switch_mutex_unlock(channel->profile_mutex);
return profile;
}
SWITCH_DECLARE(void) switch_channel_set_originatee_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
switch_assert(channel != NULL);
switch_assert(channel->caller_profile != NULL);
switch_mutex_lock(channel->profile_mutex);
if (channel->caller_profile) {
caller_profile->next = channel->caller_profile->originatee_caller_profile;
channel->caller_profile->originatee_caller_profile = caller_profile;
channel->last_profile_type = LP_ORIGINATEE;
}
switch_assert(channel->caller_profile->originatee_caller_profile->next != channel->caller_profile->originatee_caller_profile);
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_originator_caller_profile(switch_channel_t *channel)
{
switch_caller_profile_t *profile = NULL;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
if (channel->caller_profile) {
profile = channel->caller_profile->originator_caller_profile;
}
switch_mutex_unlock(channel->profile_mutex);
return profile;
}
SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_originatee_caller_profile(switch_channel_t *channel)
{
switch_caller_profile_t *profile = NULL;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
if (channel->caller_profile) {
profile = channel->caller_profile->originatee_caller_profile;
}
switch_mutex_unlock(channel->profile_mutex);
return profile;
}
SWITCH_DECLARE(char *) switch_channel_get_uuid(switch_channel_t *channel)
{
switch_assert(channel != NULL);
switch_assert(channel->session != NULL);
return switch_core_session_get_uuid(channel->session);
}
SWITCH_DECLARE(int) switch_channel_add_state_handler(switch_channel_t *channel, const switch_state_handler_table_t *state_handler)
{
int x, index;
switch_assert(channel != NULL);
switch_mutex_lock(channel->state_mutex);
for (x = 0; x < SWITCH_MAX_STATE_HANDLERS; x++) {
if (channel->state_handlers[x] == state_handler) {
index = x;
goto end;
}
}
index = channel->state_handler_index++;
if (channel->state_handler_index >= SWITCH_MAX_STATE_HANDLERS) {
index = -1;
goto end;
}
channel->state_handlers[index] = state_handler;
end:
switch_mutex_unlock(channel->state_mutex);
return index;
}
SWITCH_DECLARE(const switch_state_handler_table_t *) switch_channel_get_state_handler(switch_channel_t *channel, int index)
{
const switch_state_handler_table_t *h = NULL;
switch_assert(channel != NULL);
if (index >= SWITCH_MAX_STATE_HANDLERS || index > channel->state_handler_index) {
return NULL;
}
switch_mutex_lock(channel->state_mutex);
h = channel->state_handlers[index];
switch_mutex_unlock(channel->state_mutex);
return h;
}
SWITCH_DECLARE(void) switch_channel_clear_state_handler(switch_channel_t *channel, const switch_state_handler_table_t *state_handler)
{
int index, i = channel->state_handler_index;
const switch_state_handler_table_t *new_handlers[SWITCH_MAX_STATE_HANDLERS] = { 0 };
switch_assert(channel != NULL);
switch_mutex_lock(channel->state_mutex);
channel->state_handler_index = 0;
if (state_handler) {
for (index = 0; index < i; index++) {
if (channel->state_handlers[index] != state_handler) {
new_handlers[channel->state_handler_index++] = channel->state_handlers[index];
}
}
} else {
for (index = 0; index < i; index++) {
if (channel->state_handlers[index] && switch_test_flag(channel->state_handlers[index], SSH_FLAG_STICKY)) {
new_handlers[channel->state_handler_index++] = channel->state_handlers[index];
}
}
}
for (index = 0; index < SWITCH_MAX_STATE_HANDLERS; index++) {
channel->state_handlers[index] = NULL;
}
if (channel->state_handler_index > 0) {
for (index = 0; index < channel->state_handler_index; index++) {
channel->state_handlers[index] = new_handlers[index];
}
}
switch_mutex_unlock(channel->state_mutex);
}
SWITCH_DECLARE(void) switch_channel_restart(switch_channel_t *channel)
{
switch_channel_set_state(channel, CS_RESET);
switch_channel_wait_for_state_timeout(channel, CS_RESET, 5000);
switch_channel_set_state(channel, CS_EXECUTE);
}
/* XXX This is a somewhat simple operation. Were essentially taking the extension that one channel
was executing and generating a new extension for another channel that starts out where the
original one left off with an optional forward offset. Since all we are really doing is
copying a few basic pool-allocated structures from one channel to another there really is
not much to worry about here in terms of threading since we use read-write locks.
While the features are nice, they only really are needed in one specific crazy attended
transfer scenario where one channel was in the middle of calling a particular extension
when it was rudely cut off by a transfer key press. XXX */
SWITCH_DECLARE(switch_status_t) switch_channel_caller_extension_masquerade(switch_channel_t *orig_channel, switch_channel_t *new_channel, uint32_t offset)
{
switch_caller_profile_t *caller_profile;
switch_caller_extension_t *extension = NULL, *orig_extension = NULL;
switch_caller_application_t *ap;
switch_status_t status = SWITCH_STATUS_FALSE;
switch_event_header_t *hi = NULL;
const char *no_copy = switch_channel_get_variable(orig_channel, "attended_transfer_no_copy");
char *dup;
int i, argc = 0;
char *argv[128];
if (no_copy) {
dup = switch_core_session_strdup(new_channel->session, no_copy);
argc = switch_separate_string(dup, ',', argv, (sizeof(argv) / sizeof(argv[0])));
}
switch_mutex_lock(orig_channel->profile_mutex);
switch_mutex_lock(new_channel->profile_mutex);
caller_profile = switch_caller_profile_clone(new_channel->session, new_channel->caller_profile);
switch_assert(caller_profile);
extension = switch_caller_extension_new(new_channel->session, caller_profile->destination_number, caller_profile->destination_number);
orig_extension = switch_channel_get_caller_extension(orig_channel);
if (extension && orig_extension) {
for (ap = orig_extension->current_application; ap && offset > 0; offset--) {
ap = ap->next;
}
for (; ap; ap = ap->next) {
switch_caller_extension_add_application(new_channel->session, extension, ap->application_name, ap->application_data);
}
caller_profile->destination_number = switch_core_strdup(caller_profile->pool, orig_channel->caller_profile->destination_number);
switch_channel_set_caller_profile(new_channel, caller_profile);
switch_channel_set_caller_extension(new_channel, extension);
for (hi = orig_channel->variables->headers; hi; hi = hi->next) {
int ok = 1;
for (i = 0; i < argc; i++) {
if (!strcasecmp(argv[i], hi->name)) {
ok = 0;
break;
}
}
if (!ok)
continue;
switch_channel_set_variable(new_channel, hi->name, hi->value);
}
status = SWITCH_STATUS_SUCCESS;
}
switch_mutex_unlock(new_channel->profile_mutex);
switch_mutex_unlock(orig_channel->profile_mutex);
return status;
}
SWITCH_DECLARE(void) switch_channel_invert_cid(switch_channel_t *channel)
{
const char *tname, *tnum;
switch_caller_profile_t *cp;
cp = switch_channel_get_caller_profile(channel);
tname = cp->caller_id_name;
tnum = cp->caller_id_number;
#ifdef DEEP_DEBUG_CID
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SWAP [%s][%s] [%s][%s]\n", cp->caller_id_name, cp->caller_id_number, cp->callee_id_name, cp->callee_id_number);
#endif
cp->caller_id_name = cp->callee_id_name;
cp->caller_id_number = cp->callee_id_number;
cp->callee_id_name = tname;
cp->callee_id_number = tnum;
if (zstr(cp->caller_id_name)) {
cp->caller_id_name = "Unknown";
}
if (zstr(cp->caller_id_number)) {
cp->caller_id_number = "Unknown";
}
}
SWITCH_DECLARE(void) switch_channel_flip_cid(switch_channel_t *channel)
{
switch_event_t *event;
const char *tmp = NULL;
switch_mutex_lock(channel->profile_mutex);
if (channel->caller_profile->callee_id_name) {
tmp = channel->caller_profile->caller_id_name;
switch_channel_set_variable(channel, "pre_transfer_caller_id_name", channel->caller_profile->caller_id_name);
channel->caller_profile->caller_id_name = switch_core_strdup(channel->caller_profile->pool, channel->caller_profile->callee_id_name);
}
if (switch_channel_test_flag(channel, CF_BRIDGED)) {
channel->caller_profile->callee_id_name = SWITCH_BLANK_STRING;
} else if (tmp) {
channel->caller_profile->callee_id_name = tmp;
}
if (channel->caller_profile->callee_id_number) {
tmp = channel->caller_profile->caller_id_number;
switch_channel_set_variable(channel, "pre_transfer_caller_id_number", channel->caller_profile->caller_id_number);
channel->caller_profile->caller_id_number = switch_core_strdup(channel->caller_profile->pool, channel->caller_profile->callee_id_number);
}
if (switch_channel_test_flag(channel, CF_BRIDGED)) {
channel->caller_profile->callee_id_number = SWITCH_BLANK_STRING;
} else if (tmp) {
channel->caller_profile->callee_id_number = tmp;
}
switch_mutex_unlock(channel->profile_mutex);
if (switch_event_create(&event, SWITCH_EVENT_CALL_UPDATE) == SWITCH_STATUS_SUCCESS) {
const char *uuid = switch_channel_get_partner_uuid(channel);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Direction", "RECV");
if (uuid) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridged-To", uuid);
}
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_INFO, "%s Flipping CID from \"%s\" <%s> to \"%s\" <%s>\n",
switch_channel_get_name(channel),
switch_str_nil(switch_channel_get_variable(channel, "pre_transfer_caller_id_name")),
switch_str_nil(switch_channel_get_variable(channel, "pre_transfer_caller_id_number")),
channel->caller_profile->caller_id_name,
channel->caller_profile->caller_id_number
);
}
SWITCH_DECLARE(void) switch_channel_sort_cid(switch_channel_t *channel)
{
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND && switch_channel_test_flag(channel, CF_BLEG)) {
switch_channel_flip_cid(channel);
switch_channel_clear_flag(channel, CF_BLEG);
} else if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND && !switch_channel_test_flag(channel, CF_DIALPLAN)) {
switch_channel_set_flag(channel, CF_DIALPLAN);
switch_channel_flip_cid(channel);
}
}
SWITCH_DECLARE(switch_caller_extension_t *) switch_channel_get_queued_extension(switch_channel_t *channel)
{
switch_caller_extension_t *caller_extension;
switch_mutex_lock(channel->profile_mutex);
caller_extension = channel->queued_extension;
channel->queued_extension = NULL;
switch_mutex_unlock(channel->profile_mutex);
return caller_extension;
}
SWITCH_DECLARE(void) switch_channel_transfer_to_extension(switch_channel_t *channel, switch_caller_extension_t *caller_extension)
{
switch_mutex_lock(channel->profile_mutex);
channel->queued_extension = caller_extension;
switch_mutex_unlock(channel->profile_mutex);
switch_channel_set_flag(channel, CF_TRANSFER);
switch_channel_set_state(channel, CS_ROUTING);
}
SWITCH_DECLARE(void) switch_channel_set_caller_extension(switch_channel_t *channel, switch_caller_extension_t *caller_extension)
{
switch_assert(channel != NULL);
switch_channel_sort_cid(channel);
switch_mutex_lock(channel->profile_mutex);
caller_extension->next = channel->caller_profile->caller_extension;
channel->caller_profile->caller_extension = caller_extension;
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(switch_caller_extension_t *) switch_channel_get_caller_extension(switch_channel_t *channel)
{
switch_caller_extension_t *extension = NULL;
switch_assert(channel != NULL);
switch_mutex_lock(channel->profile_mutex);
if (channel->caller_profile) {
extension = channel->caller_profile->caller_extension;
}
switch_mutex_unlock(channel->profile_mutex);
return extension;
}
SWITCH_DECLARE(void) switch_channel_set_bridge_time(switch_channel_t *channel)
{
switch_mutex_lock(channel->profile_mutex);
if (channel->caller_profile && channel->caller_profile->times) {
channel->caller_profile->times->bridged = switch_micro_time_now();
}
switch_mutex_unlock(channel->profile_mutex);
}
SWITCH_DECLARE(void) switch_channel_set_hangup_time(switch_channel_t *channel)
{
if (channel->caller_profile && channel->caller_profile->times && !channel->caller_profile->times->hungup) {
switch_mutex_lock(channel->profile_mutex);
channel->caller_profile->times->hungup = switch_micro_time_now();
switch_mutex_unlock(channel->profile_mutex);
}
}
SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_hangup(switch_channel_t *channel,
const char *file, const char *func, int line, switch_call_cause_t hangup_cause)
{
int ok = 0;
switch_assert(channel != NULL);
/* one per customer */
switch_mutex_lock(channel->state_mutex);
if (!(channel->opaque_flags & OCF_HANGUP)) {
channel->opaque_flags |= OCF_HANGUP;
ok = 1;
}
switch_mutex_unlock(channel->state_mutex);
if (switch_channel_test_flag(channel, CF_LEG_HOLDING)) {
switch_channel_mark_hold(channel, SWITCH_FALSE);
switch_channel_set_flag(channel, CF_HANGUP_HELD);
}
if (!ok) {
return channel->state;
}
switch_channel_clear_flag(channel, CF_BLOCK_STATE);
if (channel->state < CS_HANGUP) {
switch_channel_state_t last_state;
switch_event_t *event;
const char *var;
switch_mutex_lock(channel->profile_mutex);
if (channel->hold_record && !channel->hold_record->off) {
channel->hold_record->off = switch_time_now();
}
switch_mutex_unlock(channel->profile_mutex);
switch_mutex_lock(channel->state_mutex);
last_state = channel->state;
channel->state = CS_HANGUP;
switch_mutex_unlock(channel->state_mutex);
channel->hangup_cause = hangup_cause;
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Hangup %s [%s] [%s]\n",
channel->name, state_names[last_state], switch_channel_cause2str(channel->hangup_cause));
switch_channel_set_variable_partner(channel, "last_bridge_hangup_cause", switch_channel_cause2str(hangup_cause));
if ((var = switch_channel_get_variable(channel, SWITCH_PROTO_SPECIFIC_HANGUP_CAUSE_VARIABLE))) {
switch_channel_set_variable_partner(channel, "last_bridge_" SWITCH_PROTO_SPECIFIC_HANGUP_CAUSE_VARIABLE, var);
}
if (switch_channel_test_flag(channel, CF_BRIDGE_ORIGINATOR)) {
switch_channel_set_variable(channel, "last_bridge_role", "originator");
} else if (switch_channel_test_flag(channel, CF_BRIDGED)) {
switch_channel_set_variable(channel, "last_bridge_role", "originatee");
}
if (!switch_core_session_running(channel->session) && !switch_core_session_started(channel->session)) {
switch_core_session_thread_launch(channel->session);
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_HANGUP) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
switch_core_session_kill_channel(channel->session, SWITCH_SIG_KILL);
switch_core_session_signal_state_change(channel->session);
switch_core_session_hangup_state(channel->session, SWITCH_FALSE);
}
return channel->state;
}
static switch_status_t send_ind(switch_channel_t *channel, switch_core_session_message_types_t msg_id, const char *file, const char *func, int line)
{
switch_core_session_message_t msg = { 0 };
msg.message_id = msg_id;
msg.from = channel->name;
return switch_core_session_perform_receive_message(channel->session, &msg, file, func, line);
}
SWITCH_DECLARE(switch_status_t) switch_channel_perform_acknowledge_call(switch_channel_t *channel,
const char *file, const char *func, int line)
{
send_ind(channel, SWITCH_MESSAGE_INDICATE_ACKNOWLEDGE_CALL, file, func, line);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_channel_perform_mark_ring_ready_value(switch_channel_t *channel,
switch_ring_ready_t rv,
const char *file, const char *func, int line)
{
switch_event_t *event;
if (!switch_channel_test_flag(channel, CF_RING_READY) && !switch_channel_test_flag(channel, CF_ANSWERED)) {
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Ring-Ready %s!\n", channel->name);
switch_channel_set_flag_value(channel, CF_RING_READY, rv);
switch_mutex_lock(channel->profile_mutex);
if (channel->caller_profile && channel->caller_profile->times && !channel->caller_profile->times->progress) {
channel->caller_profile->times->progress = switch_micro_time_now();
if (channel->caller_profile->originator_caller_profile) {
switch_core_session_t *other_session;
if ((other_session = switch_core_session_locate(channel->caller_profile->originator_caller_profile->uuid))) {
switch_channel_t *other_channel;
other_channel = switch_core_session_get_channel(other_session);
switch_mutex_lock(other_channel->profile_mutex);
if (other_channel->caller_profile && !other_channel->caller_profile->times->progress) {
other_channel->caller_profile->times->progress = channel->caller_profile->times->progress;
}
switch_mutex_unlock(other_channel->profile_mutex);
switch_core_session_rwunlock(other_session);
}
channel->caller_profile->originator_caller_profile->times->progress = channel->caller_profile->times->progress;
}
}
switch_mutex_unlock(channel->profile_mutex);
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_PROGRESS) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
switch_channel_execute_on(channel, SWITCH_CHANNEL_EXECUTE_ON_RING_VARIABLE);
switch_channel_api_on(channel, SWITCH_CHANNEL_API_ON_RING_VARIABLE);
switch_channel_set_callstate(channel, CCS_RINGING);
send_ind(channel, SWITCH_MESSAGE_RING_EVENT, file, func, line);
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(void) switch_channel_check_zrtp(switch_channel_t *channel)
{
if (!switch_channel_test_flag(channel, CF_ZRTP_PASSTHRU)
&& switch_channel_test_flag(channel, CF_ZRTP_PASSTHRU_REQ)
&& switch_channel_test_flag(channel, CF_ZRTP_HASH)) {
switch_core_session_t *other_session;
switch_channel_t *other_channel;
int doit = 1;
if (switch_core_session_get_partner(channel->session, &other_session) == SWITCH_STATUS_SUCCESS) {
other_channel = switch_core_session_get_channel(other_session);
if (switch_channel_test_flag(other_channel, CF_ZRTP_HASH) && !switch_channel_test_flag(other_channel, CF_ZRTP_PASSTHRU)) {
switch_channel_set_flag(channel, CF_ZRTP_PASSTHRU);
switch_channel_set_flag(other_channel, CF_ZRTP_PASSTHRU);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_INFO,
"%s Activating ZRTP passthru mode.\n", switch_channel_get_name(channel));
switch_channel_set_variable(channel, "zrtp_passthru_active", "true");
switch_channel_set_variable(other_channel, "zrtp_passthru_active", "true");
switch_channel_set_variable(channel, "zrtp_secure_media", "false");
switch_channel_set_variable(other_channel, "zrtp_secure_media", "false");
doit = 0;
}
switch_core_session_rwunlock(other_session);
}
if (doit) {
switch_channel_set_variable(channel, "zrtp_passthru_active", "false");
switch_channel_set_variable(channel, "zrtp_secure_media", "true");
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_INFO,
"%s ZRTP not negotiated on both sides; disabling ZRTP passthru mode.\n", switch_channel_get_name(channel));
switch_channel_clear_flag(channel, CF_ZRTP_PASSTHRU);
switch_channel_clear_flag(channel, CF_ZRTP_HASH);
if (switch_core_session_get_partner(channel->session, &other_session) == SWITCH_STATUS_SUCCESS) {
other_channel = switch_core_session_get_channel(other_session);
switch_channel_set_variable(other_channel, "zrtp_passthru_active", "false");
switch_channel_set_variable(other_channel, "zrtp_secure_media", "true");
switch_channel_clear_flag(other_channel, CF_ZRTP_PASSTHRU);
switch_channel_clear_flag(other_channel, CF_ZRTP_HASH);
switch_core_session_rwunlock(other_session);
}
}
}
}
SWITCH_DECLARE(switch_status_t) switch_channel_perform_mark_pre_answered(switch_channel_t *channel, const char *file, const char *func, int line)
{
switch_event_t *event;
if (!switch_channel_test_flag(channel, CF_EARLY_MEDIA) && !switch_channel_test_flag(channel, CF_ANSWERED)) {
const char *uuid;
switch_core_session_t *other_session;
switch_core_media_check_dtls(channel->session, SWITCH_MEDIA_TYPE_AUDIO);
switch_channel_check_zrtp(channel);
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Pre-Answer %s!\n", channel->name);
switch_channel_set_flag(channel, CF_EARLY_MEDIA);
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "EARLY MEDIA");
if (switch_true(switch_channel_get_variable(channel, "video_mirror_input"))) {
switch_channel_set_flag(channel, CF_VIDEO_MIRROR_INPUT);
}
if (channel->caller_profile && channel->caller_profile->times) {
switch_mutex_lock(channel->profile_mutex);
channel->caller_profile->times->progress_media = switch_micro_time_now();
if (channel->caller_profile->originator_caller_profile) {
switch_core_session_t *osession;
if ((osession = switch_core_session_locate(channel->caller_profile->originator_caller_profile->uuid))) {
switch_channel_t *other_channel;
other_channel = switch_core_session_get_channel(osession);
if (other_channel->caller_profile) {
other_channel->caller_profile->times->progress_media = channel->caller_profile->times->progress_media;
}
switch_core_session_rwunlock(osession);
}
channel->caller_profile->originator_caller_profile->times->progress_media = channel->caller_profile->times->progress_media;
}
switch_mutex_unlock(channel->profile_mutex);
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
switch_channel_execute_on(channel, SWITCH_CHANNEL_EXECUTE_ON_PRE_ANSWER_VARIABLE);
switch_channel_execute_on(channel, SWITCH_CHANNEL_EXECUTE_ON_MEDIA_VARIABLE);
switch_channel_api_on(channel, SWITCH_CHANNEL_API_ON_PRE_ANSWER_VARIABLE);
switch_channel_api_on(channel, SWITCH_CHANNEL_API_ON_MEDIA_VARIABLE);
if (switch_true(switch_channel_get_variable(channel, SWITCH_PASSTHRU_PTIME_MISMATCH_VARIABLE))) {
switch_channel_set_flag(channel, CF_PASSTHRU_PTIME_MISMATCH);
}
/* if we're the child of another channel and the other channel is in a blocking read they will never realize we have answered so send
a SWITCH_SIG_BREAK to interrupt any blocking reads on that channel
*/
if ((uuid = switch_channel_get_variable(channel, SWITCH_ORIGINATOR_VARIABLE))
&& (other_session = switch_core_session_locate(uuid))) {
switch_core_session_kill_channel(other_session, SWITCH_SIG_BREAK);
switch_core_session_rwunlock(other_session);
}
switch_channel_set_callstate(channel, CCS_EARLY);
send_ind(channel, SWITCH_MESSAGE_PROGRESS_EVENT, file, func, line);
switch_core_media_check_autoadj(channel->session);
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(switch_status_t) switch_channel_perform_pre_answer(switch_channel_t *channel, const char *file, const char *func, int line)
{
switch_core_session_message_t msg = { 0 };
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_assert(channel != NULL);
if (channel->hangup_cause || channel->state >= CS_HANGUP) {
return SWITCH_STATUS_FALSE;
}
if (switch_channel_test_flag(channel, CF_ANSWERED)) {
return SWITCH_STATUS_SUCCESS;
}
if (switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
return SWITCH_STATUS_SUCCESS;
}
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
msg.message_id = SWITCH_MESSAGE_INDICATE_PROGRESS;
msg.from = channel->name;
status = switch_core_session_perform_receive_message(channel->session, &msg, file, func, line);
}
if (status == SWITCH_STATUS_SUCCESS) {
switch_channel_perform_mark_pre_answered(channel, file, func, line);
switch_channel_audio_sync(channel);
} else {
switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
}
return status;
}
SWITCH_DECLARE(switch_status_t) switch_channel_perform_ring_ready_value(switch_channel_t *channel, switch_ring_ready_t rv,
const char *file, const char *func, int line)
{
switch_core_session_message_t msg = { 0 };
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_assert(channel != NULL);
if (channel->hangup_cause || channel->state >= CS_HANGUP) {
return SWITCH_STATUS_FALSE;
}
if (switch_channel_test_flag(channel, CF_ANSWERED)) {
return SWITCH_STATUS_SUCCESS;
}
if (switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
return SWITCH_STATUS_SUCCESS;
}
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
msg.message_id = SWITCH_MESSAGE_INDICATE_RINGING;
msg.from = channel->name;
msg.numeric_arg = rv;
status = switch_core_session_perform_receive_message(channel->session, &msg, file, func, line);
}
if (status == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Ring Ready %s!\n", channel->name);
switch_channel_perform_mark_ring_ready_value(channel, rv, file, func, line);
} else {
switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
}
return status;
}
static void do_api_on(switch_channel_t *channel, const char *variable)
{
char *app;
char *arg = NULL;
switch_stream_handle_t stream = { 0 };
app = switch_core_session_strdup(channel->session, variable);
if ((arg = strchr(app, ' '))) {
*arg++ = '\0';
}
SWITCH_STANDARD_STREAM(stream);
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "%s process %s: %s(%s)\n%s\n",
channel->name, variable, app, switch_str_nil(arg), (char *) stream.data);
switch_api_execute(app, arg, NULL, &stream);
free(stream.data);
}
SWITCH_DECLARE(switch_status_t) switch_channel_api_on(switch_channel_t *channel, const char *variable_prefix)
{
switch_event_header_t *hp;
switch_event_t *event;
int x = 0;
switch_channel_get_variables(channel, &event);
for (hp = event->headers; hp; hp = hp->next) {
char *var = hp->name;
char *val = hp->value;
if (!strncasecmp(var, variable_prefix, strlen(variable_prefix))) {
if (hp->idx) {
int i;
for (i = 0; i < hp->idx; i++) {
x++;
do_api_on(channel, hp->array[i]);
}
} else {
x++;
do_api_on(channel, val);
}
}
}
switch_event_destroy(&event);
return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}
static void do_execute_on(switch_channel_t *channel, const char *variable)
{
char *arg = NULL;
char *p;
int bg = 0;
char *app;
app = switch_core_session_strdup(channel->session, variable);
for(p = app; p && *p; p++) {
if (*p == ' ' || (*p == ':' && (*(p+1) != ':'))) {
*p++ = '\0';
arg = p;
break;
} else if (*p == ':' && (*(p+1) == ':')) {
bg++;
break;
}
}
if (!strncasecmp(app, "perl", 4)) {
bg++;
}
if (bg) {
switch_core_session_execute_application_async(channel->session, app, arg);
} else {
switch_core_session_execute_application(channel->session, app, arg);
}
}
SWITCH_DECLARE(switch_status_t) switch_channel_execute_on(switch_channel_t *channel, const char *variable_prefix)
{
switch_event_header_t *hp;
switch_event_t *event, *cevent;
int x = 0;
switch_core_get_variables(&event);
switch_channel_get_variables(channel, &cevent);
switch_event_merge(event, cevent);
for (hp = event->headers; hp; hp = hp->next) {
char *var = hp->name;
char *val = hp->value;
if (!strncasecmp(var, variable_prefix, strlen(variable_prefix))) {
if (hp->idx) {
int i;
for (i = 0; i < hp->idx; i++) {
x++;
do_execute_on(channel, hp->array[i]);
}
} else {
x++;
do_execute_on(channel, val);
}
}
}
switch_event_destroy(&event);
switch_event_destroy(&cevent);
return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(switch_status_t) switch_channel_perform_mark_answered(switch_channel_t *channel, const char *file, const char *func, int line)
{
switch_event_t *event;
const char *uuid;
switch_core_session_t *other_session;
const char *var;
switch_assert(channel != NULL);
if (channel->hangup_cause || channel->state >= CS_HANGUP) {
return SWITCH_STATUS_FALSE;
}
if (switch_channel_test_flag(channel, CF_ANSWERED)) {
return SWITCH_STATUS_SUCCESS;
}
switch_core_media_check_dtls(channel->session, SWITCH_MEDIA_TYPE_AUDIO);
if (channel->caller_profile && channel->caller_profile->times) {
switch_mutex_lock(channel->profile_mutex);
channel->caller_profile->times->answered = switch_micro_time_now();
switch_mutex_unlock(channel->profile_mutex);
}
switch_channel_check_zrtp(channel);
switch_channel_set_flag(channel, CF_ANSWERED);
if (switch_true(switch_channel_get_variable(channel, "video_mirror_input"))) {
switch_channel_set_flag(channel, CF_VIDEO_MIRROR_INPUT);
//switch_channel_set_flag(channel, CF_VIDEO_DECODED_READ);
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_ANSWER) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
/* if we're the child of another channel and the other channel is in a blocking read they will never realize we have answered so send
a SWITCH_SIG_BREAK to interrupt any blocking reads on that channel
*/
if ((uuid = switch_channel_get_variable(channel, SWITCH_ORIGINATOR_VARIABLE))
&& (other_session = switch_core_session_locate(uuid))) {
switch_core_session_kill_channel(other_session, SWITCH_SIG_BREAK);
switch_core_session_rwunlock(other_session);
}
if (switch_true(switch_channel_get_variable(channel, SWITCH_PASSTHRU_PTIME_MISMATCH_VARIABLE))) {
switch_channel_set_flag(channel, CF_PASSTHRU_PTIME_MISMATCH);
}
if ((var = switch_channel_get_variable(channel, SWITCH_ENABLE_HEARTBEAT_EVENTS_VARIABLE))) {
uint32_t seconds = 60;
int tmp;
if (switch_is_number(var)) {
tmp = atoi(var);
if (tmp > 0) {
seconds = tmp;
}
} else if (!switch_true(var)) {
seconds = 0;
}
if (seconds) {
switch_core_session_enable_heartbeat(channel->session, seconds);
}
}
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ANSWER");
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Channel [%s] has been answered\n",
channel->name);
if (switch_channel_get_variable(channel, "absolute_codec_string")) {
/* inherit_codec == true will implicitly clear the absolute_codec_string
variable if used since it was the reason it was set in the first place and is no longer needed */
if (switch_true(switch_channel_get_variable(channel, "inherit_codec"))) {
switch_channel_set_variable(channel, "absolute_codec_string", NULL);
}
}
switch_channel_execute_on(channel, SWITCH_CHANNEL_EXECUTE_ON_ANSWER_VARIABLE);
if (!switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
switch_channel_execute_on(channel, SWITCH_CHANNEL_EXECUTE_ON_MEDIA_VARIABLE);
switch_channel_api_on(channel, SWITCH_CHANNEL_API_ON_MEDIA_VARIABLE);
}
switch_channel_api_on(channel, SWITCH_CHANNEL_API_ON_ANSWER_VARIABLE);
switch_channel_presence(channel, "unknown", "answered", NULL);
//switch_channel_audio_sync(channel);
switch_core_recovery_track(channel->session);
switch_channel_set_callstate(channel, CCS_ACTIVE);
send_ind(channel, SWITCH_MESSAGE_ANSWER_EVENT, file, func, line);
switch_core_media_check_autoadj(channel->session);
if (switch_channel_test_flag(channel, CF_RTT)) {
switch_channel_set_flag_partner(channel, CF_RTT);
}
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_channel_perform_answer(switch_channel_t *channel, const char *file, const char *func, int line)
{
switch_core_session_message_t msg = { 0 };
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_assert(channel != NULL);
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
return SWITCH_STATUS_SUCCESS;
}
if (channel->hangup_cause || channel->state >= CS_HANGUP) {
return SWITCH_STATUS_FALSE;
}
if (switch_channel_test_flag(channel, CF_ANSWERED)) {
return SWITCH_STATUS_SUCCESS;
}
msg.message_id = SWITCH_MESSAGE_INDICATE_ANSWER;
msg.from = channel->name;
status = switch_core_session_perform_receive_message(channel->session, &msg, file, func, line);
if (status == SWITCH_STATUS_SUCCESS) {
switch_channel_perform_mark_answered(channel, file, func, line);
if (!switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
switch_channel_audio_sync(channel);
}
} else {
switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
}
if (switch_core_session_in_thread(channel->session) && !switch_channel_test_flag(channel, CF_PROXY_MODE) &&
!switch_channel_test_flag(channel, CF_HAS_TEXT)) {
const char *delay;
if ((delay = switch_channel_get_variable(channel, "answer_delay"))) {
uint32_t msec = atoi(delay);
if (msec) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_DEBUG, "Answer delay for %u msec\n", msec);
switch_ivr_sleep(channel->session, msec, SWITCH_TRUE, NULL);
}
}
}
return status;
}
#define resize(l) {\
char *dp;\
olen += (len + l + block);\
cpos = c - data;\
if ((dp = realloc(data, olen))) {\
data = dp;\
c = data + cpos;\
memset(c, 0, olen - cpos);\
}} \
SWITCH_DECLARE(char *) switch_channel_expand_variables_check(switch_channel_t *channel, const char *in, switch_event_t *var_list, switch_event_t *api_list, uint32_t recur)
{
char *p, *c = NULL;
char *data, *indup, *endof_indup;
size_t sp = 0, len = 0, olen = 0, vtype = 0, br = 0, cpos, block = 128;
char *cloned_sub_val = NULL, *sub_val = NULL, *expanded_sub_val = NULL;
char *func_val = NULL, *sb = NULL;
int nv = 0;
if (recur > 100) {
return (char *) in;
}
if (zstr(in)) {
return (char *) in;
}
nv = switch_string_var_check_const(in) || switch_string_has_escaped_data(in);
if (!nv) {
return (char *) in;
}
nv = 0;
olen = strlen(in) + 1;
indup = strdup(in);
endof_indup = end_of_p(indup) + 1;
if ((data = malloc(olen))) {
memset(data, 0, olen);
c = data;
for (p = indup; p && p < endof_indup && *p; p++) {
int global = 0;
vtype = 0;
if (*p == '\\') {
if (*(p + 1) == '$') {
nv = 1;
p++;
if (*(p + 1) == '$') {
p++;
}
} else if (*(p + 1) == '\'') {
p++;
continue;
} else if (*(p + 1) == '\\') {
if (len + 1 >= olen) {
resize(1);
}
*c++ = *p++;
len++;
continue;
}
}
if (*p == '$' && !nv) {
if (*(p + 1) == '$') {
p++;
global++;
}
if (*(p + 1)) {
if (*(p + 1) == '{') {
vtype = global ? 3 : 1;
} else {
nv = 1;
}
} else {
nv = 1;
}
}
if (nv) {
if (len + 1 >= olen) {
resize(1);
}
*c++ = *p;
len++;
nv = 0;
continue;
}
if (vtype) {
char *s = p, *e, *vname, *vval = NULL;
size_t nlen;
s++;
if ((vtype == 1 || vtype == 3) && *s == '{') {
br = 1;
s++;
}
e = s;
vname = s;
while (*e) {
if (br == 1 && *e == '}') {
br = 0;
*e++ = '\0';
break;
}
if (br > 0) {
if (e != s && *e == '{') {
br++;
} else if (br > 1 && *e == '}') {
br--;
}
}
e++;
}
p = e > endof_indup ? endof_indup : e;
vval = NULL;
for(sb = vname; sb && *sb; sb++) {
if (*sb == ' ') {
vval = sb;
break;
} else if (*sb == '(') {
vval = sb;
br = 1;
break;
}
}
if (vval) {
e = vval - 1;
*vval++ = '\0';
while (*e == ' ') {
*e-- = '\0';
}
e = vval;
while (e && *e) {
if (*e == '(') {
br++;
} else if (br > 1 && *e == ')') {
br--;
} else if (br == 1 && *e == ')') {
*e = '\0';
break;
}
e++;
}
vtype = 2;
}
if (vtype == 1 || vtype == 3) {
char *expanded = NULL;
int offset = 0;
int ooffset = 0;
char *ptr;
int idx = -1;
if ((expanded = switch_channel_expand_variables_check(channel, (char *) vname, var_list, api_list, recur+1)) == vname) {
expanded = NULL;
} else {
vname = expanded;
}
if ((ptr = strchr(vname, ':'))) {
*ptr++ = '\0';
offset = atoi(ptr);
if ((ptr = strchr(ptr, ':'))) {
ptr++;
ooffset = atoi(ptr);
}
}
if ((ptr = strchr(vname, '[')) && strchr(ptr, ']')) {
*ptr++ = '\0';
idx = atoi(ptr);
}
if ((sub_val = (char *) switch_channel_get_variable_dup(channel, vname, SWITCH_TRUE, idx))) {
if (var_list && !switch_event_check_permission_list(var_list, vname)) {
sub_val = "<Variable Expansion Permission Denied>";
}
if ((expanded_sub_val = switch_channel_expand_variables_check(channel, sub_val, var_list, api_list, recur+1)) == sub_val) {
expanded_sub_val = NULL;
} else {
sub_val = expanded_sub_val;
}
if (offset || ooffset) {
cloned_sub_val = strdup(sub_val);
switch_assert(cloned_sub_val);
sub_val = cloned_sub_val;
}
if (offset >= 0) {
if ((size_t) offset > strlen(sub_val)) {
*sub_val = '\0';
} else {
sub_val += offset;
}
} else if ((size_t) abs(offset) <= strlen(sub_val)) {
sub_val = cloned_sub_val + (strlen(cloned_sub_val) + offset);
}
if (ooffset > 0 && (size_t) ooffset < strlen(sub_val)) {
if ((ptr = (char *) sub_val + ooffset)) {
*ptr = '\0';
}
}
}
switch_safe_free(expanded);
} else {
switch_stream_handle_t stream = { 0 };
char *expanded = NULL;
SWITCH_STANDARD_STREAM(stream);
if (stream.data) {
char *expanded_vname = NULL;
if ((expanded_vname = switch_channel_expand_variables_check(channel, (char *) vname, var_list, api_list, recur+1)) == vname) {
expanded_vname = NULL;
} else {
vname = expanded_vname;
}
if ((expanded = switch_channel_expand_variables_check(channel, vval, var_list, api_list, recur+1)) == vval) {
expanded = NULL;
} else {
vval = expanded;
}
if (!switch_core_test_flag(SCF_API_EXPANSION) || (api_list && !switch_event_check_permission_list(api_list, vname))) {
func_val = NULL;
sub_val = "<API Execute Permission Denied>";
} else {
if (switch_api_execute(vname, vval, channel->session, &stream) == SWITCH_STATUS_SUCCESS) {
func_val = stream.data;
sub_val = func_val;
} else {
free(stream.data);
}
}
switch_safe_free(expanded);
switch_safe_free(expanded_vname);
} else {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_CRIT, "Memory Error!\n");
free(data);
free(indup);
return (char *) in;
}
}
if ((nlen = sub_val ? strlen(sub_val) : 0)) {
if (len + nlen >= olen) {
resize(nlen);
}
len += nlen;
strcat(c, sub_val);
c += nlen;
}
switch_safe_free(func_val);
switch_safe_free(cloned_sub_val);
switch_safe_free(expanded_sub_val);
sub_val = NULL;
vname = NULL;
br = 0;
}
if (sp) {
if (len + 1 >= olen) {
resize(1);
}
*c++ = ' ';
sp = 0;
len++;
}
if (*p == '$') {
p--;
} else {
if (len + 1 >= olen) {
resize(1);
}
*c++ = *p;
len++;
}
}
}
free(indup);
return data;
}
SWITCH_DECLARE(char *) switch_channel_build_param_string(switch_channel_t *channel, switch_caller_profile_t *caller_profile, const char *prefix)
{
switch_stream_handle_t stream = { 0 };
switch_size_t encode_len = 1024, new_len = 0;
char *encode_buf = NULL;
const char *prof[13] = { 0 }, *prof_names[13] = {
0};
char *e = NULL;
switch_event_header_t *hi;
uint32_t x = 0;
SWITCH_STANDARD_STREAM(stream);
if (prefix) {
stream.write_function(&stream, "%s&", prefix);
}
encode_buf = malloc(encode_len);
switch_assert(encode_buf);
if (!caller_profile) {
caller_profile = switch_channel_get_caller_profile(channel);
}
switch_assert(caller_profile != NULL);
prof[0] = caller_profile->context;
prof[1] = caller_profile->destination_number;
prof[2] = caller_profile->caller_id_name;
prof[3] = caller_profile->caller_id_number;
prof[4] = caller_profile->network_addr;
prof[5] = caller_profile->ani;
prof[6] = caller_profile->aniii;
prof[7] = caller_profile->rdnis;
prof[8] = caller_profile->source;
prof[9] = caller_profile->chan_name;
prof[10] = caller_profile->uuid;
prof[11] = caller_profile->transfer_source;
prof_names[0] = "context";
prof_names[1] = "destination_number";
prof_names[2] = "caller_id_name";
prof_names[3] = "caller_id_number";
prof_names[4] = "network_addr";
prof_names[5] = "ani";
prof_names[6] = "aniii";
prof_names[7] = "rdnis";
prof_names[8] = "source";
prof_names[9] = "chan_name";
prof_names[10] = "uuid";
prof_names[11] = "transfer_source";
for (x = 0; prof[x]; x++) {
if (zstr(prof[x])) {
continue;
}
new_len = (strlen(prof[x]) * 3) + 1;
if (encode_len < new_len) {
char *tmp;
encode_len = new_len;
if (!(tmp = realloc(encode_buf, encode_len))) {
abort();
}
encode_buf = tmp;
}
switch_url_encode(prof[x], encode_buf, encode_len);
stream.write_function(&stream, "%s=%s&", prof_names[x], encode_buf);
}
if (channel->caller_profile->soft) {
profile_node_t *pn;
for(pn = channel->caller_profile->soft; pn; pn = pn->next) {
char *var = pn->var;
char *val = pn->val;
new_len = (strlen((char *) var) * 3) + 1;
if (encode_len < new_len) {
char *tmp;
encode_len = new_len;
tmp = realloc(encode_buf, encode_len);
switch_assert(tmp);
encode_buf = tmp;
}
switch_url_encode((char *) val, encode_buf, encode_len);
stream.write_function(&stream, "%s=%s&", (char *) var, encode_buf);
}
}
if ((hi = switch_channel_variable_first(channel))) {
for (; hi; hi = hi->next) {
char *var = hi->name;
char *val = hi->value;
new_len = (strlen((char *) var) * 3) + 1;
if (encode_len < new_len) {
char *tmp;
encode_len = new_len;
tmp = realloc(encode_buf, encode_len);
switch_assert(tmp);
encode_buf = tmp;
}
switch_url_encode((char *) val, encode_buf, encode_len);
stream.write_function(&stream, "%s=%s&", (char *) var, encode_buf);
}
switch_channel_variable_last(channel);
}
e = (char *) stream.data + (strlen((char *) stream.data) - 1);
if (e && *e == '&') {
*e = '\0';
}
switch_safe_free(encode_buf);
return stream.data;
}
SWITCH_DECLARE(switch_status_t) switch_channel_pass_callee_id(switch_channel_t *channel, switch_channel_t *other_channel)
{
int x = 0;
switch_assert(channel);
switch_assert(other_channel);
switch_mutex_lock(channel->profile_mutex);
switch_mutex_lock(other_channel->profile_mutex);
if (!zstr(channel->caller_profile->callee_id_name)) {
other_channel->caller_profile->callee_id_name = switch_core_strdup(other_channel->caller_profile->pool, channel->caller_profile->callee_id_name);
x++;
}
if (!zstr(channel->caller_profile->callee_id_number)) {
other_channel->caller_profile->callee_id_number = switch_core_strdup(other_channel->caller_profile->pool, channel->caller_profile->callee_id_number);
x++;
}
switch_mutex_unlock(other_channel->profile_mutex);
switch_mutex_unlock(channel->profile_mutex);
return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(switch_status_t) switch_channel_get_variables(switch_channel_t *channel, switch_event_t **event)
{
switch_status_t status;
switch_mutex_lock(channel->profile_mutex);
if (channel->variables) {
status = switch_event_dup(event, channel->variables);
} else {
status = switch_event_create(event, SWITCH_EVENT_CHANNEL_DATA);
}
switch_mutex_unlock(channel->profile_mutex);
return status;
}
SWITCH_DECLARE(switch_core_session_t *) switch_channel_get_session(switch_channel_t *channel)
{
switch_assert(channel);
return channel->session;
}
SWITCH_DECLARE(switch_status_t) switch_channel_set_timestamps(switch_channel_t *channel)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
const char *cid_buf = NULL;
switch_caller_profile_t *caller_profile;
switch_app_log_t *app_log, *ap;
char *last_app = NULL, *last_arg = NULL;
char start[80] = "", resurrect[80] = "", answer[80] = "", hold[80],
bridge[80] = "", progress[80] = "", progress_media[80] = "", end[80] = "", tmp[80] = "",
profile_start[80] = "";
int32_t duration = 0, legbillsec = 0, billsec = 0, mduration = 0, billmsec = 0, legbillmsec = 0, progressmsec = 0, progress_mediamsec = 0;
int32_t answersec = 0, answermsec = 0, waitsec = 0, waitmsec = 0;
switch_time_t answerusec = 0;
switch_time_t uduration = 0, legbillusec = 0, billusec = 0, progresssec = 0, progressusec = 0, progress_mediasec = 0, progress_mediausec = 0, waitusec = 0;
time_t tt_created = 0, tt_answered = 0, tt_resurrected = 0, tt_bridged, tt_last_hold, tt_hold_accum,
tt_progress = 0, tt_progress_media = 0, tt_hungup = 0, mtt_created = 0, mtt_answered = 0, mtt_bridged = 0,
mtt_hungup = 0, tt_prof_created, mtt_progress = 0, mtt_progress_media = 0;
void *pop;
char dtstr[SWITCH_DTMF_LOG_LEN + 1] = "";
int x = 0;
switch_mutex_lock(channel->profile_mutex);
if (switch_channel_test_flag(channel, CF_TIMESTAMP_SET)) {
switch_mutex_unlock(channel->profile_mutex);
return SWITCH_STATUS_FALSE;
}
if (!(caller_profile = channel->caller_profile) || !channel->variables) {
switch_mutex_unlock(channel->profile_mutex);
return SWITCH_STATUS_FALSE;
}
switch_channel_set_flag(channel, CF_TIMESTAMP_SET);
if ((app_log = switch_core_session_get_app_log(channel->session))) {
for (ap = app_log; ap && ap->next; ap = ap->next);
last_app = ap->app;
last_arg = ap->arg;
}
if (!zstr(caller_profile->caller_id_name)) {
cid_buf = switch_core_session_sprintf(channel->session, "\"%s\" <%s>", caller_profile->caller_id_name,
switch_str_nil(caller_profile->caller_id_number));
} else {
cid_buf = caller_profile->caller_id_number;
}
while (x < SWITCH_DTMF_LOG_LEN && switch_queue_trypop(channel->dtmf_log_queue, &pop) == SWITCH_STATUS_SUCCESS) {
switch_dtmf_t *dt = (switch_dtmf_t *) pop;
if (dt) {
dtstr[x++] = dt->digit;
free(dt);
dt = NULL;
}
}
if (x) {
const char *var = switch_channel_get_variable(channel, "digits_dialed_filter");
char *digit_string = dtstr;
char *X = NULL;
switch_regex_t *re = NULL;
char *substituted = NULL;
if (!zstr(var)) {
int proceed = 0;
int ovector[30];
if ((proceed = switch_regex_perform(dtstr, var, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
int len = (strlen(dtstr) + strlen(var) + 10) * proceed;
int i = 0;
const char *replace = NULL;
X = malloc(len);
for (i = 0; i < proceed; i++) {
if (pcre_get_substring(dtstr, ovector, proceed, i, &replace) >= 0) {
if (replace) {
switch_size_t plen = strlen(replace);
memset(X, 'X', plen);
*(X+plen) = '\0';
switch_safe_free(substituted);
substituted = switch_string_replace(substituted ? substituted : dtstr, replace, X);
pcre_free_substring(replace);
}
}
}
if (!zstr(substituted)) {
digit_string = substituted;
}
}
}
switch_channel_set_variable(channel, "digits_dialed", digit_string);
switch_regex_safe_free(re);
switch_safe_free(substituted);
switch_safe_free(X);
} else {
switch_channel_set_variable(channel, "digits_dialed", "none");
}
if (caller_profile->times) {
switch_time_exp_t tm;
switch_size_t retsize;
const char *fmt = "%Y-%m-%d %T";
switch_time_exp_lt(&tm, caller_profile->times->created);
switch_strftime_nocheck(start, &retsize, sizeof(start), fmt, &tm);
switch_channel_set_variable(channel, "start_stamp", start);
switch_time_exp_lt(&tm, caller_profile->times->profile_created);
switch_strftime_nocheck(profile_start, &retsize, sizeof(profile_start), fmt, &tm);
switch_channel_set_variable(channel, "profile_start_stamp", profile_start);
if (caller_profile->times->answered) {
switch_time_exp_lt(&tm, caller_profile->times->answered);
switch_strftime_nocheck(answer, &retsize, sizeof(answer), fmt, &tm);
switch_channel_set_variable(channel, "answer_stamp", answer);
}
if (caller_profile->times->bridged) {
switch_time_exp_lt(&tm, caller_profile->times->bridged);
switch_strftime_nocheck(bridge, &retsize, sizeof(bridge), fmt, &tm);
switch_channel_set_variable(channel, "bridge_stamp", bridge);
}
if (caller_profile->times->last_hold) {
switch_time_exp_lt(&tm, caller_profile->times->last_hold);
switch_strftime_nocheck(hold, &retsize, sizeof(hold), fmt, &tm);
switch_channel_set_variable(channel, "hold_stamp", hold);
}
if (caller_profile->times->resurrected) {
switch_time_exp_lt(&tm, caller_profile->times->resurrected);
switch_strftime_nocheck(resurrect, &retsize, sizeof(resurrect), fmt, &tm);
switch_channel_set_variable(channel, "resurrect_stamp", resurrect);
}
if (caller_profile->times->progress) {
switch_time_exp_lt(&tm, caller_profile->times->progress);
switch_strftime_nocheck(progress, &retsize, sizeof(progress), fmt, &tm);
switch_channel_set_variable(channel, "progress_stamp", progress);
}
if (caller_profile->times->progress_media) {
switch_time_exp_lt(&tm, caller_profile->times->progress_media);
switch_strftime_nocheck(progress_media, &retsize, sizeof(progress_media), fmt, &tm);
switch_channel_set_variable(channel, "progress_media_stamp", progress_media);
}
if (channel->hold_record) {
switch_hold_record_t *hr;
switch_stream_handle_t stream = { 0 };
SWITCH_STANDARD_STREAM(stream);
stream.write_function(&stream, "{", SWITCH_VA_NONE);
for (hr = channel->hold_record; hr; hr = hr->next) {
stream.write_function(&stream, "{%"SWITCH_TIME_T_FMT",%"SWITCH_TIME_T_FMT"},", hr->on, hr->off);
}
end_of((char *)stream.data) = '}';
switch_channel_set_variable(channel, "hold_events", (char *)stream.data);
free(stream.data);
}
switch_time_exp_lt(&tm, caller_profile->times->hungup);
switch_strftime_nocheck(end, &retsize, sizeof(end), fmt, &tm);
switch_channel_set_variable(channel, "end_stamp", end);
tt_created = (time_t) (caller_profile->times->created / 1000000);
mtt_created = (time_t) (caller_profile->times->created / 1000);
switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_created);
switch_channel_set_variable(channel, "start_epoch", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->created);
switch_channel_set_variable(channel, "start_uepoch", tmp);
tt_prof_created = (time_t) (caller_profile->times->profile_created / 1000000);
switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_prof_created);
switch_channel_set_variable(channel, "profile_start_epoch", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->profile_created);
switch_channel_set_variable(channel, "profile_start_uepoch", tmp);
tt_answered = (time_t) (caller_profile->times->answered / 1000000);
mtt_answered = (time_t) (caller_profile->times->answered / 1000);
switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_answered);
switch_channel_set_variable(channel, "answer_epoch", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->answered);
switch_channel_set_variable(channel, "answer_uepoch", tmp);
tt_bridged = (time_t) (caller_profile->times->bridged / 1000000);
mtt_bridged = (time_t) (caller_profile->times->bridged / 1000);
switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_bridged);
switch_channel_set_variable(channel, "bridge_epoch", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->bridged);
switch_channel_set_variable(channel, "bridge_uepoch", tmp);
tt_last_hold = (time_t) (caller_profile->times->last_hold / 1000000);
switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_last_hold);
switch_channel_set_variable(channel, "last_hold_epoch", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->last_hold);
switch_channel_set_variable(channel, "last_hold_uepoch", tmp);
tt_hold_accum = (time_t) (caller_profile->times->hold_accum / 1000000);
switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_hold_accum);
switch_channel_set_variable(channel, "hold_accum_seconds", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->hold_accum);
switch_channel_set_variable(channel, "hold_accum_usec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->hold_accum / 1000);
switch_channel_set_variable(channel, "hold_accum_ms", tmp);
tt_resurrected = (time_t) (caller_profile->times->resurrected / 1000000);
switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_resurrected);
switch_channel_set_variable(channel, "resurrect_epoch", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->resurrected);
switch_channel_set_variable(channel, "resurrect_uepoch", tmp);
tt_progress = (time_t) (caller_profile->times->progress / 1000000);
mtt_progress = (time_t) (caller_profile->times->progress / 1000);
switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_progress);
switch_channel_set_variable(channel, "progress_epoch", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->progress);
switch_channel_set_variable(channel, "progress_uepoch", tmp);
tt_progress_media = (time_t) (caller_profile->times->progress_media / 1000000);
mtt_progress_media = (time_t) (caller_profile->times->progress_media / 1000);
switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_progress_media);
switch_channel_set_variable(channel, "progress_media_epoch", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->progress_media);
switch_channel_set_variable(channel, "progress_media_uepoch", tmp);
tt_hungup = (time_t) (caller_profile->times->hungup / 1000000);
mtt_hungup = (time_t) (caller_profile->times->hungup / 1000);
switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_hungup);
switch_channel_set_variable(channel, "end_epoch", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->hungup);
switch_channel_set_variable(channel, "end_uepoch", tmp);
duration = (int32_t) (tt_hungup - tt_created);
mduration = (int32_t) (mtt_hungup - mtt_created);
uduration = caller_profile->times->hungup - caller_profile->times->created;
if (caller_profile->times->bridged > caller_profile->times->created) {
waitsec = (int32_t) (tt_bridged - tt_created);
waitmsec = (int32_t) (mtt_bridged - mtt_created);
waitusec = caller_profile->times->bridged - caller_profile->times->created;
} else {
waitsec = 0;
waitmsec = 0;
waitusec = 0;
}
if (caller_profile->times->answered) {
billsec = (int32_t) (tt_hungup - tt_answered);
billmsec = (int32_t) (mtt_hungup - mtt_answered);
billusec = caller_profile->times->hungup - caller_profile->times->answered;
legbillsec = (int32_t) (tt_hungup - tt_created);
legbillmsec = (int32_t) (mtt_hungup - mtt_created);
legbillusec = caller_profile->times->hungup - caller_profile->times->created;
answersec = (int32_t) (tt_answered - tt_created);
answermsec = (int32_t) (mtt_answered - mtt_created);
answerusec = caller_profile->times->answered - caller_profile->times->created;
}
if (caller_profile->times->progress) {
progresssec = (int32_t) (tt_progress - tt_created);
progressmsec = (int32_t) (mtt_progress - mtt_created);
progressusec = caller_profile->times->progress - caller_profile->times->created;
}
if (caller_profile->times->progress_media) {
progress_mediasec = (int32_t) (tt_progress_media - tt_created);
progress_mediamsec = (int32_t) (mtt_progress_media - mtt_created);
progress_mediausec = caller_profile->times->progress_media - caller_profile->times->created;
}
}
switch_channel_set_variable(channel, "last_app", last_app);
switch_channel_set_variable(channel, "last_arg", last_arg);
switch_channel_set_variable(channel, "caller_id", cid_buf);
switch_snprintf(tmp, sizeof(tmp), "%d", duration);
switch_channel_set_variable(channel, "duration", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", billsec);
switch_channel_set_variable(channel, "billsec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%"SWITCH_TIME_T_FMT, progresssec);
switch_channel_set_variable(channel, "progresssec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", answersec);
switch_channel_set_variable(channel, "answersec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", waitsec);
switch_channel_set_variable(channel, "waitsec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%"SWITCH_TIME_T_FMT, progress_mediasec);
switch_channel_set_variable(channel, "progress_mediasec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", legbillsec);
switch_channel_set_variable(channel, "flow_billsec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", mduration);
switch_channel_set_variable(channel, "mduration", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", billmsec);
switch_channel_set_variable(channel, "billmsec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", progressmsec);
switch_channel_set_variable(channel, "progressmsec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", answermsec);
switch_channel_set_variable(channel, "answermsec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", waitmsec);
switch_channel_set_variable(channel, "waitmsec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", progress_mediamsec);
switch_channel_set_variable(channel, "progress_mediamsec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%d", legbillmsec);
switch_channel_set_variable(channel, "flow_billmsec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, uduration);
switch_channel_set_variable(channel, "uduration", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, billusec);
switch_channel_set_variable(channel, "billusec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, progressusec);
switch_channel_set_variable(channel, "progressusec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, answerusec);
switch_channel_set_variable(channel, "answerusec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, waitusec);
switch_channel_set_variable(channel, "waitusec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, progress_mediausec);
switch_channel_set_variable(channel, "progress_mediausec", tmp);
switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, legbillusec);
switch_channel_set_variable(channel, "flow_billusec", tmp);
switch_mutex_unlock(channel->profile_mutex);
return status;
}
SWITCH_DECLARE(const char *) switch_channel_get_partner_uuid_copy(switch_channel_t *channel, char *buf, switch_size_t blen)
{
const char *uuid = NULL;
switch_mutex_lock(channel->profile_mutex);
if (!(uuid = switch_channel_get_variable_dup(channel, SWITCH_SIGNAL_BOND_VARIABLE, SWITCH_TRUE, -1))) {
uuid = switch_channel_get_variable_dup(channel, SWITCH_ORIGINATE_SIGNAL_BOND_VARIABLE, SWITCH_TRUE, -1);
}
if (uuid) {
strncpy(buf, uuid, blen);
uuid = (const char *) buf;
}
switch_mutex_unlock(channel->profile_mutex);
return uuid;
}
SWITCH_DECLARE(const char *) switch_channel_get_partner_uuid(switch_channel_t *channel)
{
const char *uuid = NULL;
if (!(uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE))) {
uuid = switch_channel_get_variable(channel, SWITCH_ORIGINATE_SIGNAL_BOND_VARIABLE);
}
return uuid;
}
SWITCH_DECLARE(void) switch_channel_handle_cause(switch_channel_t *channel, switch_call_cause_t cause)
{
switch_core_session_t *session = channel->session;
const char *transfer_on_fail = NULL;
char *tof_data = NULL;
char *tof_array[4] = { 0 };
//int tof_arrayc = 0;
if (!switch_channel_up_nosig(channel)) {
return;
}
transfer_on_fail = switch_channel_get_variable(channel, "transfer_on_fail");
tof_data = switch_core_session_strdup(session, transfer_on_fail);
switch_split(tof_data, ' ', tof_array);
transfer_on_fail = tof_array[0];
/*
if the variable continue_on_fail is set it can be:
'true' to continue on all failures.
'false' to not continue.
A list of codes either names or numbers eg "user_busy,normal_temporary_failure,603"
failure_causes acts as the opposite version
EXCEPTION... ATTENDED_TRANSFER never is a reason to continue.......
*/
if (cause != SWITCH_CAUSE_ATTENDED_TRANSFER) {
const char *continue_on_fail = NULL, *failure_causes = NULL;
continue_on_fail = switch_channel_get_variable(channel, "continue_on_fail");
failure_causes = switch_channel_get_variable(channel, "failure_causes");
if (continue_on_fail || failure_causes) {
const char *cause_str;
char cause_num[35] = "";
cause_str = switch_channel_cause2str(cause);
switch_snprintf(cause_num, sizeof(cause_num), "%u", cause);
if (failure_causes) {
char *lbuf = switch_core_session_strdup(session, failure_causes);
char *argv[256] = { 0 };
int argc = switch_separate_string(lbuf, ',', argv, (sizeof(argv) / sizeof(argv[0])));
int i, x = 0;
for (i = 0; i < argc; i++) {
if (!strcasecmp(argv[i], cause_str) || !strcasecmp(argv[i], cause_num)) {
x++;
break;
}
}
if (!x) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
"Failure causes [%s]: Cause: %s\n", failure_causes, cause_str);
return;
}
}
if (continue_on_fail) {
if (switch_true(continue_on_fail)) {
return;
} else {
char *lbuf = switch_core_session_strdup(session, continue_on_fail);
char *argv[256] = { 0 };
int argc = switch_separate_string(lbuf, ',', argv, (sizeof(argv) / sizeof(argv[0])));
int i;
for (i = 0; i < argc; i++) {
if (!strcasecmp(argv[i], cause_str) || !strcasecmp(argv[i], cause_num)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
"Continue on fail [%s]: Cause: %s\n", continue_on_fail, cause_str);
return;
}
}
}
}
} else {
/* no answer is *always* a reason to continue */
if (cause == SWITCH_CAUSE_NO_ANSWER || cause == SWITCH_CAUSE_NO_USER_RESPONSE || cause == SWITCH_CAUSE_ORIGINATOR_CANCEL) {
return;
}
}
if (transfer_on_fail || failure_causes) {
const char *cause_str;
char cause_num[35] = "";
cause_str = switch_channel_cause2str(cause);
switch_snprintf(cause_num, sizeof(cause_num), "%u", cause);
if ((tof_array[1] == NULL ) || (!strcasecmp(tof_array[1], "auto_cause"))){
tof_array[1] = (char *) cause_str;
}
if (failure_causes) {
char *lbuf = switch_core_session_strdup(session, failure_causes);
char *argv[256] = { 0 };
int argc = switch_separate_string(lbuf, ',', argv, (sizeof(argv) / sizeof(argv[0])));
int i, x = 0;
for (i = 0; i < argc; i++) {
if (!strcasecmp(argv[i], cause_str) || !strcasecmp(argv[i], cause_num)) {
x++;
break;
}
}
if (!x) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
"Failure causes [%s]: Cause: %s\n", failure_causes, cause_str);
switch_ivr_session_transfer(session, tof_array[1], tof_array[2], tof_array[3]);
}
}
if (transfer_on_fail) {
if (switch_true(transfer_on_fail)) {
return;
} else {
char *lbuf = switch_core_session_strdup(session, transfer_on_fail);
char *argv[256] = { 0 };
int argc = switch_separate_string(lbuf, ',', argv, (sizeof(argv) / sizeof(argv[0])));
int i;
for (i = 0; i < argc; i++) {
if (!strcasecmp(argv[i], cause_str) || !strcasecmp(argv[i], cause_num)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
"Transfer on fail [%s]: Cause: %s\n", transfer_on_fail, cause_str);
switch_ivr_session_transfer(session, tof_array[1], tof_array[2], tof_array[3]);
}
}
}
}
}
}
if (!switch_channel_test_flag(channel, CF_TRANSFER) && !switch_channel_test_flag(channel, CF_CONFIRM_BLIND_TRANSFER) &&
switch_channel_get_state(channel) != CS_ROUTING) {
switch_channel_hangup(channel, cause);
}
}
SWITCH_DECLARE(void) switch_channel_global_init(switch_memory_pool_t *pool)
{
memset(&globals, 0, sizeof(globals));
globals.pool = pool;
switch_mutex_init(&globals.device_mutex, SWITCH_MUTEX_NESTED, pool);
switch_core_hash_init(&globals.device_hash);
}
SWITCH_DECLARE(void) switch_channel_global_uninit(void)
{
switch_core_hash_destroy(&globals.device_hash);
}
static void fetch_device_stats(switch_device_record_t *drec)
{
switch_device_node_t *np;
memset(&drec->stats, 0, sizeof(switch_device_stats_t));
switch_mutex_lock(drec->mutex);
for(np = drec->uuid_list; np; np = np->next) {
drec->stats.total++;
if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
drec->stats.total_in++;
} else {
drec->stats.total_out++;
}
if (!np->hup_profile) {
drec->stats.offhook++;
if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
drec->stats.offhook_in++;
} else {
drec->stats.offhook_out++;
}
if (np->callstate == CCS_HELD) {
drec->stats.held++;
if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
drec->stats.held_in++;
} else {
drec->stats.held_out++;
}
} else if (np->callstate == CCS_UNHELD) {
drec->stats.unheld++;
if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
drec->stats.unheld_in++;
} else {
drec->stats.unheld_out++;
}
} else {
if (np->callstate == CCS_EARLY) {
drec->stats.early++;
if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
drec->stats.early_in++;
} else {
drec->stats.early_out++;
}
} else if (np->callstate == CCS_RINGING) {
drec->stats.ringing++;
if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
drec->stats.ringing_in++;
} else {
drec->stats.ringing_out++;
}
} else if (np->callstate == CCS_RING_WAIT) {
drec->stats.ring_wait++;
} else if (np->callstate == CCS_HANGUP) {
drec->stats.hup++;
if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
drec->stats.hup_in++;
} else {
drec->stats.hup_out++;
}
} else if (np->callstate != CCS_DOWN) {
drec->stats.active++;
if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
drec->stats.active_in++;
} else {
drec->stats.active_out++;
}
}
}
} else {
drec->stats.hup++;
if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
drec->stats.hup_in++;
} else {
drec->stats.hup_out++;
}
}
}
switch_mutex_unlock(drec->mutex);
}
SWITCH_DECLARE(void) switch_channel_clear_device_record(switch_channel_t *channel)
{
switch_memory_pool_t *pool;
int sanity = 100;
switch_device_node_t *np;
switch_event_t *event;
if (!channel->device_node || !switch_channel_test_flag(channel, CF_FINAL_DEVICE_LEG)) {
return;
}
while(--sanity && channel->device_node->parent->refs) {
switch_yield(100000);
}
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Destroying device cdr %s on device [%s]\n",
channel->device_node->parent->uuid,
channel->device_node->parent->device_id);
if (switch_event_create(&event, SWITCH_EVENT_CALL_DETAIL) == SWITCH_STATUS_SUCCESS) {
int x = 0;
char prefix[80] = "";
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Type", "device");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Device-ID", channel->device_node->parent->device_id);
switch_mutex_lock(channel->device_node->parent->mutex);
for(np = channel->device_node->parent->uuid_list; np; np = np->next) {
switch_snprintf(prefix, sizeof(prefix), "Call-%d", ++x);
switch_caller_profile_event_set_data(np->hup_profile, prefix, event);
}
switch_mutex_unlock(channel->device_node->parent->mutex);
switch_event_fire(&event);
}
switch_mutex_lock(channel->device_node->parent->mutex);
for(np = channel->device_node->parent->uuid_list; np; np = np->next) {
if (np->xml_cdr) {
switch_xml_free(np->xml_cdr);
}
if (np->event) {
switch_event_destroy(&np->event);
}
}
switch_mutex_unlock(channel->device_node->parent->mutex);
pool = channel->device_node->parent->pool;
switch_mutex_lock(globals.device_mutex);
switch_core_destroy_memory_pool(&pool);
switch_mutex_unlock(globals.device_mutex);
}
SWITCH_DECLARE(void) switch_channel_process_device_hangup(switch_channel_t *channel)
{
switch_channel_check_device_state(channel, channel->callstate);
process_device_hup(channel);
}
static void process_device_hup(switch_channel_t *channel)
{
switch_hold_record_t *hr, *newhr, *last = NULL;
switch_device_record_t *drec = NULL;
switch_device_node_t *node;
if (!channel->device_node) {
return;
}
switch_mutex_lock(globals.device_mutex);
node = channel->device_node;
drec = channel->device_node->parent;
node->hup_profile = switch_caller_profile_dup(drec->pool, channel->caller_profile);
fetch_device_stats(drec);
switch_ivr_generate_xml_cdr(channel->session, &node->xml_cdr);
if (switch_event_create(&node->event, SWITCH_EVENT_CALL_DETAIL) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_extended_data(channel, node->event);
}
for (hr = channel->hold_record; hr; hr = hr->next) {
newhr = switch_core_alloc(drec->pool, sizeof(*newhr));
newhr->on = hr->on;
newhr->off = hr->off;
if (hr->uuid) {
newhr->uuid = switch_core_strdup(drec->pool, hr->uuid);
}
if (!node->hold_record) {
node->hold_record = newhr;
} else if (last) {
last->next = newhr;
}
last = newhr;
}
if (!drec->stats.offhook) { /* this is final call */
switch_core_hash_delete(globals.device_hash, drec->device_id);
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Processing last call from device [%s]\n",
drec->device_id);
switch_channel_set_flag(channel, CF_FINAL_DEVICE_LEG);
} else {
channel->device_node = NULL;
}
drec->refs--;
switch_mutex_unlock(globals.device_mutex);
}
static void switch_channel_check_device_state(switch_channel_t *channel, switch_channel_callstate_t callstate)
{
switch_device_record_t *drec = NULL;
switch_device_state_binding_t *ptr = NULL;
switch_event_t *event = NULL;
if (!channel->device_node) {
return;
}
drec = channel->device_node->parent;
switch_mutex_lock(globals.device_mutex);
switch_mutex_lock(drec->mutex);
fetch_device_stats(drec);
if (drec->state != SDS_HANGUP) {
if (drec->stats.offhook == 0 || drec->stats.hup == drec->stats.total) {
drec->state = SDS_HANGUP;
} else {
if (drec->stats.active == 0) {
if ((drec->stats.ringing_out + drec->stats.early_out) > 0 || drec->stats.ring_wait > 0) {
drec->state = SDS_RINGING;
} else {
if (drec->stats.held > 0) {
drec->state = SDS_HELD;
} else if (drec->stats.unheld > 0) {
drec->state = SDS_UNHELD;
} else {
drec->state = SDS_DOWN;
}
}
} else if (drec->stats.active == 1) {
drec->state = SDS_ACTIVE;
} else {
drec->state = SDS_ACTIVE_MULTI;
}
}
}
if ((drec->state == SDS_DOWN && drec->last_state == SDS_DOWN) || (drec->state == SDS_HANGUP && drec->last_state == SDS_HANGUP)) {
switch_mutex_unlock(drec->mutex);
switch_mutex_unlock(globals.device_mutex);
return;
}
if (!drec->call_start) {
drec->call_start = switch_micro_time_now();
}
switch(drec->state) {
case SDS_RINGING:
if (!drec->ring_start) {
drec->ring_start = switch_micro_time_now();
drec->ring_stop = 0;
}
break;
case SDS_ACTIVE:
case SDS_ACTIVE_MULTI:
if (!drec->active_start) {
drec->active_start = switch_micro_time_now();
drec->active_stop = 0;
}
break;
case SDS_HELD:
if (!drec->hold_start) {
drec->hold_start = switch_micro_time_now();
drec->hold_stop = 0;
}
break;
default:
break;
}
if (callstate != CCS_UNHELD && drec->active_start && drec->state != SDS_ACTIVE && drec->state != SDS_ACTIVE_MULTI) {
drec->active_stop = switch_micro_time_now();
}
if (drec->ring_start && !drec->ring_stop && drec->state != SDS_RINGING) {
drec->ring_stop = switch_micro_time_now();
}
if (drec->hold_start && !drec->hold_stop && drec->state != SDS_HELD) {
drec->hold_stop = switch_micro_time_now();
}
if (switch_event_create(&event, SWITCH_EVENT_DEVICE_STATE) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Device-ID", drec->device_id);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Last-Device-State", switch_channel_device_state2str(drec->last_state));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Device-State", switch_channel_device_state2str(drec->state));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Device-Call-State", switch_channel_callstate2str(callstate));
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Total-Legs", "%u", drec->stats.total);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Offhook", "%u", drec->stats.offhook);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Ringing", "%u", drec->stats.ringing);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Early", "%u", drec->stats.early);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Active", "%u", drec->stats.active);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Held", "%u", drec->stats.held);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-UnHeld", "%u", drec->stats.unheld);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Hup", "%u", drec->stats.hup);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Talk-Time-Start-Uepoch", "%"SWITCH_TIME_T_FMT, drec->active_start);
if (drec->active_stop) {
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Talk-Time-Stop-Uepoch", "%"SWITCH_TIME_T_FMT, drec->active_stop);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Talk-Time-Milliseconds", "%u", (uint32_t)(drec->active_stop - drec->active_start) / 1000);
}
}
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG1,
"%s device: %s\nState: %s Dev State: %s/%s Total:%u Offhook:%u "
"Ringing:%u Early:%u Active:%u Held:%u Unheld:%u Hungup:%u Dur: %u Ringtime: %u Holdtime: %u %s\n",
switch_channel_get_name(channel),
drec->device_id,
switch_channel_callstate2str(callstate),
switch_channel_device_state2str(drec->last_state),
switch_channel_device_state2str(drec->state),
drec->stats.total,
drec->stats.offhook,
drec->stats.ringing,
drec->stats.early,
drec->stats.active,
drec->stats.held,
drec->stats.unheld,
drec->stats.hup,
drec->active_stop ? (uint32_t)(drec->active_stop - drec->active_start) / 1000 : 0,
drec->ring_stop ? (uint32_t)(drec->ring_stop - drec->ring_start) / 1000 : 0,
drec->hold_stop ? (uint32_t)(drec->hold_stop - drec->hold_start) / 1000 : 0,
switch_channel_test_flag(channel, CF_FINAL_DEVICE_LEG) ? "FINAL LEG" : "");
for (ptr = globals.device_bindings; ptr; ptr = ptr->next) {
ptr->function(channel->session, callstate, drec);
}
drec->last_stats = drec->stats;
if (drec->active_stop) {
drec->active_start = drec->active_stop = 0;
if (drec->state == SDS_ACTIVE || drec->state == SDS_ACTIVE_MULTI) {
drec->active_start = switch_micro_time_now();
}
}
if (drec->hold_stop) {
drec->hold_start = drec->hold_stop = 0;
if (drec->state == SDS_HELD) {
drec->hold_start = switch_micro_time_now();
}
}
if (drec->ring_stop) {
drec->ring_start = drec->ring_stop = 0;
if (drec->state == SDS_RINGING) {
drec->ring_start = switch_micro_time_now();
}
}
drec->last_call_time = switch_micro_time_now();
drec->last_state = drec->state;
switch_mutex_unlock(drec->mutex);
switch_mutex_unlock(globals.device_mutex);
if (event) {
switch_event_fire(&event);
}
}
/* assumed to be called under a lock */
static void add_uuid(switch_device_record_t *drec, switch_channel_t *channel)
{
switch_device_node_t *node;
switch_assert(drec);
switch_channel_set_flag(channel, CF_DEVICE_LEG);
node = switch_core_alloc(drec->pool, sizeof(*node));
node->uuid = switch_core_strdup(drec->pool, switch_core_session_get_uuid(channel->session));
node->parent = drec;
node->callstate = channel->callstate;
node->direction = channel->logical_direction == SWITCH_CALL_DIRECTION_INBOUND ? SWITCH_CALL_DIRECTION_OUTBOUND : SWITCH_CALL_DIRECTION_INBOUND;
channel->device_node = node;
if (!drec->uuid_list) {
drec->uuid_list = node;
drec->uuid = node->uuid;
} else {
drec->uuid_tail->next = node;
}
drec->uuid_tail = node;
drec->refs++;
}
static switch_status_t create_device_record(switch_device_record_t **drecp, const char *device_id)
{
switch_device_record_t *drec;
switch_memory_pool_t *pool;
switch_assert(drecp);
switch_core_new_memory_pool(&pool);
drec = switch_core_alloc(pool, sizeof(*drec));
drec->pool = pool;
drec->device_id = switch_core_strdup(drec->pool, device_id);
switch_mutex_init(&drec->mutex, SWITCH_MUTEX_NESTED, drec->pool);
*drecp = drec;
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(const char *) switch_channel_set_device_id(switch_channel_t *channel, const char *device_id)
{
switch_device_record_t *drec;
if (channel->device_node) {
return NULL;
}
channel->device_id = switch_core_session_strdup(channel->session, device_id);
switch_mutex_lock(globals.device_mutex);
if (!(drec = switch_core_hash_find(globals.device_hash, channel->device_id))) {
create_device_record(&drec, channel->device_id);
switch_core_hash_insert(globals.device_hash, drec->device_id, drec);
}
add_uuid(drec, channel);
switch_mutex_unlock(globals.device_mutex);
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Setting DEVICE ID to [%s]\n", device_id);
switch_channel_check_device_state(channel, channel->callstate);
return device_id;
}
SWITCH_DECLARE(switch_device_record_t *) switch_channel_get_device_record(switch_channel_t *channel)
{
if (channel->device_node) {
switch_mutex_lock(channel->device_node->parent->mutex);
return channel->device_node->parent;
}
return NULL;
}
SWITCH_DECLARE(void) switch_channel_release_device_record(switch_device_record_t **drecp)
{
if (drecp && *drecp) {
switch_mutex_unlock((*drecp)->mutex);
*drecp = NULL;
}
}
SWITCH_DECLARE(switch_status_t) switch_channel_bind_device_state_handler(switch_device_state_function_t function, void *user_data)
{
switch_device_state_binding_t *binding = NULL, *ptr = NULL;
assert(function != NULL);
if (!(binding = (switch_device_state_binding_t *) switch_core_alloc(globals.pool, sizeof(*binding)))) {
return SWITCH_STATUS_MEMERR;
}
binding->function = function;
binding->user_data = user_data;
switch_mutex_lock(globals.device_mutex);
for (ptr = globals.device_bindings; ptr && ptr->next; ptr = ptr->next);
if (ptr) {
ptr->next = binding;
} else {
globals.device_bindings = binding;
}
switch_mutex_unlock(globals.device_mutex);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_channel_unbind_device_state_handler(switch_device_state_function_t function)
{
switch_device_state_binding_t *ptr, *last = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
switch_mutex_lock(globals.device_mutex);
for (ptr = globals.device_bindings; ptr; ptr = ptr->next) {
if (ptr->function == function) {
status = SWITCH_STATUS_SUCCESS;
if (last) {
last->next = ptr->next;
} else {
globals.device_bindings = ptr->next;
last = NULL;
continue;
}
}
last = ptr;
}
switch_mutex_unlock(globals.device_mutex);
return status;
}
SWITCH_DECLARE(switch_status_t) switch_channel_pass_sdp(switch_channel_t *from_channel, switch_channel_t *to_channel, const char *sdp)
{
switch_status_t status = SWITCH_STATUS_FALSE;
char *use_sdp = (char *) sdp;
char *patched_sdp = NULL;
if (!switch_channel_get_variable(to_channel, SWITCH_B_SDP_VARIABLE)) {
const char *var;
if ((var = switch_channel_get_variable(from_channel, "bypass_media_sdp_filter"))) {
if ((patched_sdp = switch_core_media_process_sdp_filter(use_sdp, var, from_channel->session))) {
use_sdp = patched_sdp;
}
}
switch_channel_set_variable(to_channel, SWITCH_B_SDP_VARIABLE, use_sdp);
}
switch_safe_free(patched_sdp);
return status;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/