/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005/2006, Anthony Minessale II * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Anthony Minessale II * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Anthony Minessale II * Michael Jerris * * * switch_channel.c -- Media Channel Interface * */ #include #include struct switch_cause_table { const char *name; switch_call_cause_t cause; }; static struct switch_cause_table CAUSE_CHART[] = { { "UNALLOCATED", SWITCH_CAUSE_UNALLOCATED }, { "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 }, { "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 }, { "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 }, { NULL, 0 } }; struct switch_channel { char *name; switch_buffer_t *dtmf_buffer; switch_mutex_t *dtmf_mutex; switch_mutex_t *flag_mutex; switch_mutex_t *profile_mutex; switch_core_session_t *session; switch_channel_state_t state; uint32_t flags; uint32_t state_flags; switch_caller_profile_t *caller_profile; switch_caller_profile_t *originator_caller_profile; switch_caller_profile_t *originatee_caller_profile; switch_caller_extension_t *caller_extension; const switch_state_handler_table_t *state_handlers[SWITCH_MAX_STATE_HANDLERS]; int state_handler_index; switch_hash_t *variables; switch_hash_t *private_hash; switch_channel_timetable_t *times; switch_call_cause_t hangup_cause; int freq; int bits; int channels; int ms; int kbps; }; SWITCH_DECLARE(char *) switch_channel_cause2str(switch_call_cause_t cause) { uint8_t x; char *str = "UNALLOCATED"; for(x = 0; CAUSE_CHART[x].name; x++) { if (CAUSE_CHART[x].cause == cause) { str = (char *) CAUSE_CHART[x].name; } } return str; } SWITCH_DECLARE(switch_call_cause_t) switch_channel_str2cause(char *str) { uint8_t x; switch_call_cause_t cause = SWITCH_CAUSE_UNALLOCATED; for(x = 0; CAUSE_CHART[x].name; x++) { if (!strcasecmp(CAUSE_CHART[x].name, str)) { cause = CAUSE_CHART[x].cause; } } return cause; } SWITCH_DECLARE(switch_call_cause_t) switch_channel_get_cause(switch_channel_t *channel) { assert(channel != NULL); return channel->hangup_cause; } SWITCH_DECLARE(switch_channel_timetable_t *) switch_channel_get_timetable(switch_channel_t *channel) { assert(channel != NULL); return channel->times; } SWITCH_DECLARE(switch_status_t) switch_channel_alloc(switch_channel_t **channel, switch_memory_pool_t *pool) { assert(pool != NULL); if (((*channel) = switch_core_alloc(pool, sizeof(switch_channel_t))) == 0) { return SWITCH_STATUS_MEMERR; } switch_core_hash_init(&(*channel)->variables, pool); switch_core_hash_init(&(*channel)->private_hash, pool); switch_buffer_create_dynamic(&(*channel)->dtmf_buffer, 128, 128, 0); switch_mutex_init(&(*channel)->dtmf_mutex, SWITCH_MUTEX_NESTED, pool); switch_mutex_init(&(*channel)->flag_mutex, SWITCH_MUTEX_NESTED, pool); switch_mutex_init(&(*channel)->profile_mutex, SWITCH_MUTEX_NESTED, pool); (*channel)->hangup_cause = SWITCH_CAUSE_UNALLOCATED; return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_channel_set_raw_mode(switch_channel_t *channel, int freq, int bits, int channels, int ms, int kbps) { assert(channel != NULL); channel->freq = freq; channel->bits = bits; channel->channels = channels; channel->ms = ms; channel->kbps = kbps; return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_channel_get_raw_mode(switch_channel_t *channel, int *freq, int *bits, int *channels, int *ms, int *kbps) { if (freq) { *freq = channel->freq; } if (bits) { *bits = channel->bits; } if (channels) { *channels = channel->channels; } if (ms) { *ms = channel->ms; } if (kbps) { *kbps = channel->kbps; } return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_size_t) switch_channel_has_dtmf(switch_channel_t *channel) { switch_size_t has; assert(channel != NULL); switch_mutex_lock(channel->dtmf_mutex); has = switch_buffer_inuse(channel->dtmf_buffer); switch_mutex_unlock(channel->dtmf_mutex); return has; } SWITCH_DECLARE(switch_status_t) switch_channel_queue_dtmf(switch_channel_t *channel, char *dtmf) { switch_status_t status; register switch_size_t len, inuse; switch_size_t wr = 0; char *p; assert(channel != NULL); switch_mutex_lock(channel->dtmf_mutex); inuse = switch_buffer_inuse(channel->dtmf_buffer); len = strlen(dtmf); if (len + inuse > switch_buffer_len(channel->dtmf_buffer)) { switch_buffer_toss(channel->dtmf_buffer, strlen(dtmf)); } p = dtmf; while(wr < len && p) { if (is_dtmf(*p)) { wr++; } else { break; } p++; } status = switch_buffer_write(channel->dtmf_buffer, dtmf, wr) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_MEMERR; switch_mutex_unlock(channel->dtmf_mutex); return status; } SWITCH_DECLARE(switch_size_t) switch_channel_dequeue_dtmf(switch_channel_t *channel, char *dtmf, switch_size_t len) { switch_size_t bytes; switch_event_t *event; assert(channel != NULL); switch_mutex_lock(channel->dtmf_mutex); if ((bytes = switch_buffer_read(channel->dtmf_buffer, dtmf, len)) > 0) { *(dtmf + bytes) = '\0'; } switch_mutex_unlock(channel->dtmf_mutex); if (bytes && switch_event_create(&event, SWITCH_EVENT_DTMF) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(channel, event); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "DTMF-String", dtmf); switch_event_fire(&event); } return bytes; } SWITCH_DECLARE(void) switch_channel_uninit(switch_channel_t *channel) { switch_buffer_destroy(&channel->dtmf_buffer); } SWITCH_DECLARE(switch_status_t) switch_channel_init(switch_channel_t *channel, switch_core_session_t *session, switch_channel_state_t state, uint32_t flags) { assert(channel != NULL); channel->state = state; channel->flags = flags; channel->session = session; switch_channel_set_raw_mode(channel, 8000, 16, 1, 20, 8); return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(void) switch_channel_presence(switch_channel_t *channel, char *rpid, char *status) { char *id = switch_channel_get_variable(channel, "presence_id"); switch_event_t *event; switch_event_types_t type = SWITCH_EVENT_PRESENCE_IN; if (!status) { type = SWITCH_EVENT_PRESENCE_OUT; } if (!id) { return; } if (switch_event_create(&event, type) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", __FILE__); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", __FILE__); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s", id); if (type == SWITCH_EVENT_PRESENCE_IN) { if (!rpid) { rpid = "unknown"; } switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "%s", status); } switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); switch_event_fire(&event); } } SWITCH_DECLARE(char *) switch_channel_get_variable(switch_channel_t *channel, char *varname) { char *v = NULL; assert(channel != NULL); if (!(v=switch_core_hash_find(channel->variables, varname))) { if (!(v = switch_caller_get_field_by_name(channel->caller_profile, varname))) { if (!strcmp(varname, "base_dir")) { return SWITCH_GLOBAL_dirs.base_dir; } v = switch_core_get_variable(varname); } } return v; } SWITCH_DECLARE(switch_hash_index_t *) switch_channel_variable_first(switch_channel_t *channel, switch_memory_pool_t *pool) { assert(channel != NULL); return switch_hash_first(pool, channel->variables); } SWITCH_DECLARE(switch_status_t) switch_channel_set_private(switch_channel_t *channel, char *key, void *private_info) { assert(channel != NULL); switch_core_hash_insert_dup(channel->private_hash, switch_core_session_strdup(channel->session, key), private_info); return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(void *) switch_channel_get_private(switch_channel_t *channel, char *key) { assert(channel != NULL); return switch_core_hash_find(channel->private_hash, key); } SWITCH_DECLARE(switch_status_t) switch_channel_set_name(switch_channel_t *channel, char *name) { assert(channel != NULL); channel->name = NULL; if (name) { char *uuid = switch_core_session_get_uuid(channel->session); channel->name = switch_core_session_strdup(channel->session, name); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "New Chan %s [%s]\n", name, uuid); } return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(char *) switch_channel_get_name(switch_channel_t *channel) { assert(channel != NULL); return channel->name ? channel->name : "N/A"; } SWITCH_DECLARE(switch_status_t) switch_channel_set_variable(switch_channel_t *channel, char *varname, char *value) { assert(channel != NULL); if (varname) { switch_core_hash_delete(channel->variables, varname); if (value) { switch_core_hash_insert_dup(channel->variables, varname, switch_core_session_strdup(channel->session, value)); } else { switch_core_hash_delete(channel->variables, varname); } return SWITCH_STATUS_SUCCESS; } return SWITCH_STATUS_FALSE; } SWITCH_DECLARE(int) switch_channel_test_flag(switch_channel_t *channel, switch_channel_flag_t flags) { assert(channel != NULL); return switch_test_flag(channel, flags) ? 1 : 0; } SWITCH_DECLARE(void) switch_channel_set_flag(switch_channel_t *channel, switch_channel_flag_t flags) { assert(channel != NULL); switch_set_flag_locked(channel, flags); } SWITCH_DECLARE(void) switch_channel_set_state_flag(switch_channel_t *channel, switch_channel_flag_t flags) { assert(channel != NULL); switch_mutex_lock(channel->flag_mutex); channel->state_flags |= flags; switch_mutex_unlock(channel->flag_mutex); } SWITCH_DECLARE(void) switch_channel_clear_flag(switch_channel_t *channel, switch_channel_flag_t flags) { assert(channel != NULL); switch_clear_flag_locked(channel, flags); } SWITCH_DECLARE(switch_channel_state_t) switch_channel_get_state(switch_channel_t *channel) { switch_channel_state_t state; assert(channel != NULL); switch_mutex_lock(channel->flag_mutex); state = channel->state; switch_mutex_unlock(channel->flag_mutex); return state; } SWITCH_DECLARE(uint8_t) switch_channel_ready(switch_channel_t *channel) { assert(channel != NULL); return (channel->state > CS_RING && channel->state < CS_HANGUP && !switch_test_flag(channel, CF_TRANSFER)) ? 1 : 0; } static const char *state_names[] = { "CS_NEW", "CS_INIT", "CS_RING", "CS_TRANSMIT", "CS_EXECUTE", "CS_LOOPBACK", "CS_HOLD", "CS_HIBERNATE", "CS_HANGUP", "CS_DONE", 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(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_DONE; } 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; assert(channel != NULL); switch_mutex_lock(channel->flag_mutex); last_state = channel->state; 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_LOOPBACK: case CS_TRANSMIT: case CS_RING: case CS_EXECUTE: case CS_HANGUP: case CS_DONE: default: break; } break; */ switch (last_state) { case CS_NEW: switch (state) { default: ok++; break; } break; case CS_INIT: switch (state) { case CS_LOOPBACK: case CS_TRANSMIT: case CS_RING: case CS_EXECUTE: case CS_HOLD: case CS_HIBERNATE: ok++; default: break; } break; case CS_LOOPBACK: switch (state) { case CS_TRANSMIT: case CS_RING: case CS_EXECUTE: case CS_HOLD: case CS_HIBERNATE: ok++; default: break; } break; case CS_TRANSMIT: switch (state) { case CS_LOOPBACK: case CS_RING: case CS_EXECUTE: case CS_HOLD: case CS_HIBERNATE: ok++; default: break; } break; case CS_HOLD: switch (state) { case CS_LOOPBACK: case CS_RING: case CS_EXECUTE: case CS_TRANSMIT: case CS_HIBERNATE: ok++; default: break; } break; case CS_HIBERNATE: switch (state) { case CS_LOOPBACK: case CS_RING: case CS_EXECUTE: case CS_TRANSMIT: case CS_HOLD: ok++; default: break; } break; case CS_RING: switch_clear_flag(channel, CF_TRANSFER); switch (state) { case CS_LOOPBACK: case CS_EXECUTE: case CS_TRANSMIT: case CS_HOLD: case CS_HIBERNATE: ok++; default: break; } break; case CS_EXECUTE: switch (state) { case CS_LOOPBACK: case CS_TRANSMIT: case CS_RING: case CS_HOLD: case CS_HIBERNATE: ok++; default: break; } break; case CS_HANGUP: switch (state) { case CS_DONE: ok++; default: break; } break; default: break; } if (ok) { if (state > CS_RING) { switch_channel_presence(channel, "unknown", (char*)state_names[state]); } switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char *) file, func, line, SWITCH_LOG_DEBUG, "%s State Change %s -> %s\n", channel->name, state_names[last_state], state_names[state]); switch_mutex_lock(channel->flag_mutex); channel->state = state; switch_mutex_unlock(channel->flag_mutex); if (state == CS_HANGUP && channel->hangup_cause == SWITCH_CAUSE_UNALLOCATED) { channel->hangup_cause = SWITCH_CAUSE_NORMAL_CLEARING; } if (state < CS_HANGUP) { switch_event_t *event; if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_STATE) == SWITCH_STATUS_SUCCESS) { if (state == CS_RING) { switch_channel_event_set_data(channel, event); } else { char state_num[25]; snprintf(state_num, sizeof(state_num), "%d", channel->state); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-State", (char *) switch_channel_state_name(channel->state)); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-State-Number", (char *) state_num); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Name", switch_channel_get_name(channel)); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(channel->session)); } switch_event_fire(&event); } } if (state < CS_DONE) { switch_core_session_signal_state_change(channel->session); } } else { switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char *) file, func, line, 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! if (channel->state < CS_HANGUP) { // not cool lets crash this bad boy and figure out wtf is going on assert(0); } } done: if (channel->state_flags) { channel->flags |= channel->state_flags; channel->state_flags = 0; } switch_mutex_unlock(channel->flag_mutex); return channel->state; } SWITCH_DECLARE(void) switch_channel_event_set_data(switch_channel_t *channel, switch_event_t *event) { switch_caller_profile_t *caller_profile, *originator_caller_profile, *originatee_caller_profile; switch_hash_index_t *hi; switch_codec_t *codec; void *val; const void *var; char state_num[25]; caller_profile = switch_channel_get_caller_profile(channel); originator_caller_profile = switch_channel_get_originator_caller_profile(channel); originatee_caller_profile = switch_channel_get_originatee_caller_profile(channel); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-State", (char *) switch_channel_state_name(channel->state)); snprintf(state_num, sizeof(state_num), "%d", channel->state); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-State-Number", (char *) state_num); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Name", switch_channel_get_name(channel)); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(channel->session)); if ((codec = switch_core_session_get_read_codec(channel->session))) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Read-Codec-Name", codec->implementation->iananame); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Read-Codec-Rate", "%u", codec->implementation->samples_per_second); } if ((codec = switch_core_session_get_write_codec(channel->session))) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Write-Codec-Name", codec->implementation->iananame); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Write-Codec-Rate", "%u", codec->implementation->samples_per_second); } /* Index Caller's Profile */ if (caller_profile) { switch_caller_profile_event_set_data(caller_profile, "Caller", event); } /* Index Originator's Profile */ if (originator_caller_profile) { switch_caller_profile_event_set_data(originator_caller_profile, "Originator", event); } /* Index Originatee's Profile */ if (originatee_caller_profile) { switch_caller_profile_event_set_data(originatee_caller_profile, "Originatee", event); } /* Index Variables */ for (hi = switch_hash_first(switch_core_session_get_pool(channel->session), channel->variables); hi; hi = switch_hash_next(hi)) { char buf[1024]; switch_hash_this(hi, &var, NULL, &val); snprintf(buf, sizeof(buf), "variable_%s", (char *) var); switch_event_add_header(event, SWITCH_STACK_BOTTOM, buf, (char *) val); } } SWITCH_DECLARE(void) switch_channel_set_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile) { switch_channel_timetable_t *times; assert(channel != NULL); assert(channel->session != NULL); switch_mutex_lock(channel->profile_mutex); if (!caller_profile->uuid) { caller_profile->uuid = switch_core_session_strdup(channel->session, switch_core_session_get_uuid(channel->session)); } if (!caller_profile->chan_name) { caller_profile->chan_name = switch_core_session_strdup(channel->session, channel->name); } if (!caller_profile->context) { caller_profile->chan_name = switch_core_session_strdup(channel->session, "default"); } if (!channel->caller_profile) { switch_event_t *event; if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_CREATE) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(channel, event); switch_event_fire(&event); } } if ((times = (switch_channel_timetable_t *) switch_core_session_alloc(channel->session, sizeof(*times)))) { times->next = channel->times; channel->times = times; } channel->times->created = switch_time_now(); caller_profile->next = channel->caller_profile; channel->caller_profile = caller_profile; 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; assert(channel != NULL); switch_mutex_lock(channel->profile_mutex); profile = channel->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) { assert(channel != NULL); switch_mutex_lock(channel->profile_mutex); caller_profile->next = channel->originator_caller_profile; channel->originator_caller_profile = caller_profile; switch_mutex_unlock(channel->profile_mutex); } SWITCH_DECLARE(void) switch_channel_set_originatee_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile) { assert(channel != NULL); switch_mutex_lock(channel->profile_mutex); caller_profile->next = channel->originatee_caller_profile; channel->originatee_caller_profile = 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; assert(channel != NULL); switch_mutex_lock(channel->profile_mutex); profile = channel->originator_caller_profile; switch_mutex_unlock(channel->profile_mutex); return profile; } SWITCH_DECLARE(char *) switch_channel_get_uuid(switch_channel_t *channel) { assert(channel != NULL); assert(channel->session != NULL); return switch_core_session_get_uuid(channel->session); } SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_originatee_caller_profile(switch_channel_t *channel) { switch_caller_profile_t *profile; assert(channel != NULL); switch_mutex_lock(channel->profile_mutex); profile = channel->originatee_caller_profile; switch_mutex_unlock(channel->profile_mutex); return profile; } SWITCH_DECLARE(int) switch_channel_add_state_handler(switch_channel_t *channel, const switch_state_handler_table_t *state_handler) { int x, index; assert(channel != NULL); switch_mutex_lock(channel->flag_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->flag_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; assert(channel != NULL); if (index > SWITCH_MAX_STATE_HANDLERS || index > channel->state_handler_index) { return NULL; } switch_mutex_lock(channel->flag_mutex); h = channel->state_handlers[index]; switch_mutex_unlock(channel->flag_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_mutex_lock(channel->flag_mutex); assert(channel != NULL); 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]; } } } for (index = 0; index < SWITCH_MAX_STATE_HANDLERS; index++) { channel->state_handlers[index] = NULL; } if (state_handler) { for (index = 0; index < channel->state_handler_index; index++) { channel->state_handlers[index] = new_handlers[index]; } } switch_mutex_unlock(channel->flag_mutex); } SWITCH_DECLARE(void) switch_channel_set_caller_extension(switch_channel_t *channel, switch_caller_extension_t *caller_extension) { assert(channel != NULL); switch_mutex_lock(channel->profile_mutex); caller_extension->next = channel->caller_extension; channel->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) { assert(channel != NULL); return channel->caller_extension; } 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) { assert(channel != NULL); switch_mutex_lock(channel->flag_mutex); if (channel->times && !channel->times->hungup) { channel->times->hungup = switch_time_now(); } if (channel->state < CS_HANGUP) { switch_event_t *event; switch_channel_state_t last_state = channel->state; channel->state = CS_HANGUP; channel->hangup_cause = hangup_cause; switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char *) file, func, line, SWITCH_LOG_NOTICE, "Hangup %s [%s] [%s]\n", channel->name, state_names[last_state], switch_channel_cause2str(channel->hangup_cause)); if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_HANGUP) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Hangup-Cause", switch_channel_cause2str(channel->hangup_cause)); switch_channel_event_set_data(channel, event); switch_event_fire(&event); } switch_channel_set_variable(channel, "hangup_cause", switch_channel_cause2str(channel->hangup_cause)); switch_channel_presence(channel, "unavailable", switch_channel_cause2str(channel->hangup_cause)); switch_core_session_kill_channel(channel->session, SWITCH_SIG_KILL); switch_core_session_signal_state_change(channel->session); } switch_mutex_unlock(channel->flag_mutex); return channel->state; } 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; char *uuid = switch_core_session_get_uuid(channel->session); switch_status_t status; assert(channel != NULL); if (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; } msg.message_id = SWITCH_MESSAGE_INDICATE_PROGRESS; msg.from = channel->name; status = switch_core_session_message_send(uuid, &msg); if (status == SWITCH_STATUS_SUCCESS) { switch_event_t *event; switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char *) file, func, line, SWITCH_LOG_NOTICE, "Pre-Answer %s!\n", channel->name); switch_channel_set_flag(channel, CF_EARLY_MEDIA); if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_PROGRESS) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(channel, event); switch_event_fire(&event); } } return status; } SWITCH_DECLARE(switch_status_t) switch_channel_perform_ringback(switch_channel_t *channel, const char *file, const char *func, int line) { switch_core_session_message_t msg; char *uuid = switch_core_session_get_uuid(channel->session); switch_status_t status; assert(channel != NULL); if (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; } msg.message_id = SWITCH_MESSAGE_INDICATE_RINGING; msg.from = channel->name; status = switch_core_session_message_send(uuid, &msg); if (status == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char *) file, func, line, SWITCH_LOG_NOTICE, "Ringback %s!\n", channel->name); } return status; } SWITCH_DECLARE(switch_status_t) switch_channel_perform_answer(switch_channel_t *channel, const char *file, const char *func, int line) { assert(channel != NULL); if (channel->state >= CS_HANGUP) { return SWITCH_STATUS_FALSE; } if (switch_channel_test_flag(channel, CF_ANSWERED)) { return SWITCH_STATUS_SUCCESS; } if (switch_core_session_answer_channel(channel->session) == SWITCH_STATUS_SUCCESS) { switch_event_t *event; channel->times->answered = switch_time_now(); switch_channel_set_flag(channel, CF_ANSWERED); switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char *) file, func, line, SWITCH_LOG_NOTICE, "Answer %s!\n", channel->name); if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_ANSWER) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(channel, event); switch_event_fire(&event); } return SWITCH_STATUS_SUCCESS; } return SWITCH_STATUS_FALSE; } SWITCH_DECLARE(char *) switch_channel_expand_variables(switch_channel_t *channel, char *in) { char *p, *c; char *data, *indup; size_t sp = 0, len = 0, olen = 0, vtype = 0, br = 0, cpos, block = 128; char *sub_val = NULL, *func_val = NULL; if (!strchr(in, '$') && !strchr(in, '&')) { return in; } olen = strlen(in); indup = strdup(in); if ((data = malloc(olen))) { memset(data, 0, olen); c = data; for(p = indup; *p; p++) { vtype = 0; if (*p == '$') { vtype = 1; } if (*p == '&') { vtype = 2; } if (vtype) { char *s = p, *e, *vname, *vval = NULL; size_t nlen; s++; if (*s == '{') { br = 1; s++; } e = s; vname = s; while (*e) { if (!br && *e == ' ') { *e++ = '\0'; sp++; break; } if (br == 1 && *e == '}') { br = 0; *e++ = '\0'; break; } if (vtype == 2) { if (*e == '(') { *e++ = '\0'; vval = e; br = 2; } if (br == 2 && *e == ')') { *e++ = '\0'; br = 0; break; } } e++; } p = e; if (vtype == 1) { sub_val = switch_channel_get_variable(channel, vname); } else { switch_stream_handle_t stream = {0}; SWITCH_STANDARD_STREAM(stream); if (stream.data) { if (switch_api_execute(vname, vval, channel->session, &stream) == SWITCH_STATUS_SUCCESS) { func_val = stream.data; sub_val = func_val; } else { free(stream.data); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); free(data); free(indup); return in; } } nlen = sub_val ? strlen(sub_val) : 0; if (len + nlen >= olen) { olen = (olen + len + nlen + block); cpos = c - data; data = realloc(data, olen); c = data + cpos; memset(c, 0, olen - cpos); } if (nlen) { len += nlen; strcat(c, sub_val); c += nlen; } switch_safe_free(func_val); } if (sp) { *c++ = ' '; sp = 0; len++; } if (*p == '$' || *p == '&') { p--; } else { *c++ = *p; len++; } } } free(indup); return data; }