From 0bc11afa73cd08189c2b20e676dcaa109625d4bd Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Thu, 13 May 2010 08:54:50 +0200 Subject: [PATCH] Skinny: split out server part And try to keep each file below 2000 lines --- src/mod/endpoints/mod_skinny/Makefile.am | 2 +- src/mod/endpoints/mod_skinny/mod_skinny.c | 1 + .../endpoints/mod_skinny/skinny_protocol.c | 1826 ---------------- .../endpoints/mod_skinny/skinny_protocol.h | 17 +- src/mod/endpoints/mod_skinny/skinny_server.c | 1877 +++++++++++++++++ src/mod/endpoints/mod_skinny/skinny_server.h | 64 + 6 files changed, 1947 insertions(+), 1840 deletions(-) create mode 100644 src/mod/endpoints/mod_skinny/skinny_server.c create mode 100644 src/mod/endpoints/mod_skinny/skinny_server.h diff --git a/src/mod/endpoints/mod_skinny/Makefile.am b/src/mod/endpoints/mod_skinny/Makefile.am index 1ac0cd30c2..496c9c3a7c 100644 --- a/src/mod/endpoints/mod_skinny/Makefile.am +++ b/src/mod/endpoints/mod_skinny/Makefile.am @@ -3,7 +3,7 @@ include $(top_srcdir)/build/modmake.rulesam MODNAME=mod_skinny mod_LTLIBRARIES = mod_skinny.la -mod_skinny_la_SOURCES = mod_skinny.c skinny_protocol.c skinny_tables.c skinny_api.c +mod_skinny_la_SOURCES = mod_skinny.c skinny_protocol.c skinny_tables.c skinny_api.c skinny_server.c mod_skinny_la_CFLAGS = $(AM_CFLAGS) mod_skinny_la_LIBADD = $(switch_builddir)/libfreeswitch.la mod_skinny_la_LDFLAGS = -avoid-version -module -no-undefined -shared diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index d0653bca08..0dcc6ac167 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -32,6 +32,7 @@ #include #include "mod_skinny.h" #include "skinny_protocol.h" +#include "skinny_server.h" #include "skinny_tables.h" #include "skinny_labels.h" #include "skinny_api.h" diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.c b/src/mod/endpoints/mod_skinny/skinny_protocol.c index 6ef2d22057..f1479ba0dd 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.c +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.c @@ -37,29 +37,6 @@ skinny_globals_t globals; -struct soft_key_template_definition soft_key_template_default[] = { - { SKINNY_DISP_REDIAL, SOFTKEY_REDIAL }, - { SKINNY_DISP_NEWCALL, SOFTKEY_NEWCALL }, - { SKINNY_DISP_HOLD, SOFTKEY_HOLD }, - { SKINNY_DISP_TRANSFER, SOFTKEY_TRANSFER }, - { SKINNY_DISP_CFWDALL, SOFTKEY_CFWDALL }, - { SKINNY_DISP_CFWDBUSY, SOFTKEY_CFWDBUSY }, - { SKINNY_DISP_CFWDNOANSWER, SOFTKEY_CFWDNOANSWER }, - { SKINNY_DISP_BACKSPACE, SOFTKEY_BACKSPACE }, - { SKINNY_DISP_ENDCALL, SOFTKEY_ENDCALL }, - { SKINNY_DISP_RESUME, SOFTKEY_RESUME }, - { SKINNY_DISP_ANSWER, SOFTKEY_ANSWER }, - { SKINNY_DISP_INFO, SOFTKEY_INFO }, - { SKINNY_DISP_CONFRM, SOFTKEY_CONFRM }, - { SKINNY_DISP_PARK, SOFTKEY_PARK }, - { SKINNY_DISP_JOIN, SOFTKEY_JOIN }, - { SKINNY_DISP_MEETME, SOFTKEY_MEETMECONFRM }, - { SKINNY_DISP_CALLPICKUP, SOFTKEY_CALLPICKUP }, - { SKINNY_DISP_GRPCALLPICKUP, SOFTKEY_GRPCALLPICKUP }, - { SKINNY_DISP_DND, SOFTKEY_DND }, - { SKINNY_DISP_IDIVERT, SOFTKEY_IDIVERT }, -}; - /*****************************************************************************/ /* SKINNY FUNCTIONS */ /*****************************************************************************/ @@ -266,70 +243,6 @@ switch_status_t skinny_device_event(listener_t *listener, switch_event_t **ev, s } /*****************************************************************************/ -switch_status_t skinny_session_send_call_info(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) -{ - private_t *tech_pvt; - switch_channel_t *channel; - - const char *caller_party_name; - const char *caller_party_number; - const char *called_party_name; - const char *called_party_number; - uint32_t call_type = 0; - - channel = switch_core_session_get_channel(session); - tech_pvt = switch_core_session_get_private(session); - - switch_assert(tech_pvt->caller_profile != NULL); - - /* Calling party */ - if (zstr((caller_party_name = switch_channel_get_variable(channel, "effective_caller_id_name"))) && - zstr((caller_party_name = switch_channel_get_variable(channel, "caller_id_name")))) { - caller_party_name = SWITCH_DEFAULT_CLID_NAME; - } - if (zstr((caller_party_number = switch_channel_get_variable(channel, "effective_caller_id_number"))) && - zstr((caller_party_number = switch_channel_get_variable(channel, "caller_id_number")))) { - caller_party_number = "0000000000"; - } - /* Called party */ - if (zstr((called_party_name = switch_channel_get_variable(channel, "effective_called_id_name"))) && - zstr((called_party_name = switch_channel_get_variable(channel, "called_id_name")))) { - called_party_name = SWITCH_DEFAULT_CLID_NAME; - } - if (zstr((called_party_number = switch_channel_get_variable(channel, "effective_called_id_number"))) && - zstr((called_party_number = switch_channel_get_variable(channel, "called_id_number")))) { - called_party_number = "0000000000"; - } - if (switch_channel_test_flag(channel, CF_OUTBOUND)) { - call_type = SKINNY_INBOUND_CALL; - } else { - call_type = SKINNY_OUTBOUND_CALL; - } - skinny_send_call_info(listener, - caller_party_name, /* char calling_party_name[40], */ - caller_party_number, /* char calling_party[24], */ - called_party_name, /* char called_party_name[40], */ - called_party_number, /* char called_party[24], */ - line_instance, /* uint32_t line_instance, */ - tech_pvt->call_id, /* uint32_t call_id, */ - call_type, /* uint32_t call_type, */ - "", /* TODO char original_called_party_name[40], */ - "", /* TODO char original_called_party[24], */ - "", /* TODO char last_redirecting_party_name[40], */ - "", /* TODO char last_redirecting_party[24], */ - 0, /* TODO uint32_t original_called_party_redirect_reason, */ - 0, /* TODO uint32_t last_redirecting_reason, */ - "", /* TODO char calling_party_voice_mailbox[24], */ - "", /* TODO char called_party_voice_mailbox[24], */ - "", /* TODO char original_called_party_voice_mailbox[24], */ - "", /* TODO char last_redirecting_voice_mailbox[24], */ - 1, /* uint32_t call_instance, */ - 1, /* uint32_t call_security_status, */ - 0 /* uint32_t party_pi_restriction_bits */ - ); - return SWITCH_STATUS_SUCCESS; -} - /*****************************************************************************/ switch_status_t skinny_session_walk_lines(skinny_profile_t *profile, char *channel_uuid, switch_core_db_callback_func_t callback, void *data) { @@ -367,701 +280,6 @@ switch_status_t skinny_session_walk_lines_by_call_id(skinny_profile_t *profile, return SWITCH_STATUS_SUCCESS; } -/*****************************************************************************/ -struct skinny_ring_lines_helper { - private_t *tech_pvt; - uint32_t lines_count; -}; - -int skinny_ring_lines_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - struct skinny_ring_lines_helper *helper = pArg; - char *tmp; - - char *device_name = argv[0]; - uint32_t device_instance = atoi(argv[1]); - /* uint32_t position = atoi(argv[2]); */ - uint32_t line_instance = atoi(argv[3]); - /* char *label = argv[4]; */ - /* char *value = argv[5]; */ - /* char *caller_name = argv[6]; */ - /* uint32_t ring_on_idle = atoi(argv[7]); */ - /* uint32_t ring_on_active = atoi(argv[8]); */ - /* uint32_t busy_trigger = atoi(argv[9]); */ - /* char *forward_all = argv[10]; */ - /* char *forward_busy = argv[11]; */ - /* char *forward_noanswer = argv[12]; */ - /* uint32_t noanswer_duration = atoi(argv[13]); */ - /* char *channel_uuid = argv[14]; */ - /* uint32_t call_id = atoi(argv[15]); */ - /* uint32_t call_state = atoi(argv[16]); */ - - listener_t *listener = NULL; - - skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, - device_name, device_instance, &listener); - if(listener) { - helper->lines_count++; - skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_RING_IN); - send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, SKINNY_KEY_SET_RING_IN, 0xffff); - if ((tmp = switch_mprintf("%s%s", SKINNY_DISP_FROM, helper->tech_pvt->caller_profile->destination_number))) { - send_display_prompt_status(listener, 0, tmp, line_instance, helper->tech_pvt->call_id); - switch_safe_free(tmp); - } - if ((tmp = switch_mprintf("\005\000\000\000%s", helper->tech_pvt->caller_profile->destination_number))) { - send_display_pri_notify(listener, 10 /* message_timeout */, 5 /* priority */, tmp); - switch_safe_free(tmp); - } - skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance); - send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_BLINK); - send_set_ringer(listener, SKINNY_RING_INSIDE, SKINNY_RING_FOREVER, 0, helper->tech_pvt->call_id); - } - return 0; -} - -switch_call_cause_t skinny_ring_lines(private_t *tech_pvt) -{ - switch_status_t status; - struct skinny_ring_lines_helper helper = {0}; - - switch_assert(tech_pvt); - switch_assert(tech_pvt->profile); - switch_assert(tech_pvt->session); - - helper.tech_pvt = tech_pvt; - helper.lines_count = 0; - - status = skinny_session_walk_lines(tech_pvt->profile, - switch_core_session_get_uuid(tech_pvt->session), skinny_ring_lines_callback, &helper); - if(status != SWITCH_STATUS_SUCCESS) { - return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; - } else if(helper.lines_count == 0) { - return SWITCH_CAUSE_UNALLOCATED_NUMBER; - } else { - return SWITCH_CAUSE_SUCCESS; - } -} - -/*****************************************************************************/ -switch_status_t skinny_create_incoming_session(listener_t *listener, uint32_t *line_instance_p, switch_core_session_t **session) -{ - uint32_t line_instance; - switch_core_session_t *nsession; - switch_channel_t *channel; - private_t *tech_pvt; - char name[128]; - char *sql; - struct line_stat_res_message *button = NULL; - - line_instance = *line_instance_p; - if((nsession = skinny_profile_find_session(listener->profile, listener, line_instance_p, 0))) { - if(skinny_line_get_state(listener, *line_instance_p, 0) == SKINNY_OFF_HOOK) { - /* Reuse existing session */ - *session = nsession; - return SWITCH_STATUS_SUCCESS; - } - switch_core_session_rwunlock(nsession); - } - *line_instance_p = line_instance; - if(*line_instance_p == 0) { - *line_instance_p = 1; - } - - skinny_hold_active_calls(listener); - - skinny_line_get(listener, *line_instance_p, &button); - - if (!button || !button->shortname) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Line %d not found on device %s %d\n", - *line_instance_p, listener->device_name, listener->device_instance); - goto error; - } - - if (!(nsession = switch_core_session_request(skinny_get_endpoint_interface(), - SWITCH_CALL_DIRECTION_INBOUND, NULL))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error Creating Session\n"); - goto error; - } - - if (!(tech_pvt = (struct private_object *) switch_core_session_alloc(nsession, sizeof(*tech_pvt)))) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT, - "Error Creating Session private object\n"); - goto error; - } - - switch_core_session_add_stream(nsession, NULL); - - tech_init(tech_pvt, listener->profile, nsession); - - channel = switch_core_session_get_channel(nsession); - - snprintf(name, sizeof(name), "SKINNY/%s/%s:%d/%d", listener->profile->name, - listener->device_name, listener->device_instance, *line_instance_p); - switch_channel_set_name(channel, name); - - if (switch_core_session_thread_launch(nsession) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT, - "Error Creating Session thread\n"); - goto error; - } - if (switch_core_session_read_lock(nsession) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT, - "Error Locking Session\n"); - goto error; - } - /* First create the caller profile in the patterns Dialplan */ - if (!(tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(nsession), - NULL, listener->profile->patterns_dialplan, - button->displayname, button->shortname, - listener->remote_ip, NULL, NULL, NULL, - "skinny" /* modname */, - listener->profile->patterns_context, - "")) != 0) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT, - "Error Creating Session caller profile\n"); - goto error; - } - - switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); - - if ((sql = switch_mprintf( - "INSERT INTO skinny_active_lines " - "(device_name, device_instance, line_instance, channel_uuid, call_id, call_state) " - "SELECT device_name, device_instance, line_instance, '%s', %d, %d " - "FROM skinny_lines " - "WHERE value='%s'", - switch_core_session_get_uuid(nsession), tech_pvt->call_id, SKINNY_ON_HOOK, button->shortname - ))) { - skinny_execute_sql(listener->profile, sql, listener->profile->sql_mutex); - switch_safe_free(sql); - } - - send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id); - send_set_speaker_mode(listener, SKINNY_SPEAKER_ON); - send_set_lamp(listener, SKINNY_BUTTON_LINE, *line_instance_p, SKINNY_LAMP_ON); - skinny_line_set_state(listener, *line_instance_p, tech_pvt->call_id, SKINNY_OFF_HOOK); - send_select_soft_keys(listener, *line_instance_p, tech_pvt->call_id, SKINNY_KEY_SET_OFF_HOOK, 0xffff); - send_display_prompt_status(listener, 0, "\200\000", - *line_instance_p, tech_pvt->call_id); - send_activate_call_plane(listener, *line_instance_p); - - goto done; -error: - if (nsession) { - switch_core_session_destroy(&nsession); - } - - return SWITCH_STATUS_FALSE; - -done: - *session = nsession; - return SWITCH_STATUS_SUCCESS; -} - -skinny_action_t skinny_session_dest_match_pattern(switch_core_session_t *session, char **data) -{ - skinny_action_t action = SKINNY_ACTION_DROP; - switch_channel_t *channel = NULL; - private_t *tech_pvt = NULL; - - switch_assert(session); - - channel = switch_core_session_get_channel(session); - tech_pvt = switch_core_session_get_private(session); - - /* this part of the code is similar to switch_core_standard_on_routing() */ - if (!zstr(tech_pvt->profile->patterns_dialplan)) { - switch_dialplan_interface_t *dialplan_interface = NULL; - switch_caller_extension_t *extension = NULL; - char *expanded = NULL; - char *dpstr = NULL; - char *dp[25]; - int argc, x; - - if ((dpstr = switch_core_session_strdup(session, tech_pvt->profile->patterns_dialplan))) { - expanded = switch_channel_expand_variables(channel, dpstr); - argc = switch_separate_string(expanded, ',', dp, (sizeof(dp) / sizeof(dp[0]))); - for (x = 0; x < argc; x++) { - char *dpname = dp[x]; - char *dparg = NULL; - - if (dpname) { - if ((dparg = strchr(dpname, ':'))) { - *dparg++ = '\0'; - } - } else { - continue; - } - if (!(dialplan_interface = switch_loadable_module_get_dialplan_interface(dpname))) { - continue; - } - - extension = dialplan_interface->hunt_function(session, dparg, NULL); - UNPROTECT_INTERFACE(dialplan_interface); - - if (extension) { - goto found; - } - } - } -found: - while (extension && extension->current_application) { - switch_caller_application_t *current_application = extension->current_application; - - extension->current_application = extension->current_application->next; - - if (!strcmp(current_application->application_name, "skinny-route")) { - action = SKINNY_ACTION_ROUTE; - } else if (!strcmp(current_application->application_name, "skinny-drop")) { - action = SKINNY_ACTION_DROP; - } else if (!strcmp(current_application->application_name, "skinny-wait")) { - action = SKINNY_ACTION_WAIT; - *data = switch_core_session_strdup(session, current_application->application_data); - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, - "Unknown skinny dialplan application %s\n", current_application->application_name); - } - } - } - return action; -} - - -struct skinny_session_process_dest_helper { - private_t *tech_pvt; - listener_t *listener; - uint32_t line_instance; -}; - -int skinny_session_process_dest_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - struct skinny_session_process_dest_helper *helper = pArg; - listener_t *listener = NULL; - - char *device_name = argv[0]; - uint32_t device_instance = atoi(argv[1]); - /* uint32_t position = atoi(argv[2]); */ - uint32_t line_instance = atoi(argv[3]); - /* char *label = argv[4]; */ - /* char *value = argv[5]; */ - /* char *caller_name = argv[6]; */ - /* uint32_t ring_on_idle = atoi(argv[7]); */ - /* uint32_t ring_on_active = atoi(argv[8]); */ - /* uint32_t busy_trigger = atoi(argv[9]); */ - /* char *forward_all = argv[10]; */ - /* char *forward_busy = argv[11]; */ - /* char *forward_noanswer = argv[12]; */ - /* uint32_t noanswer_duration = atoi(argv[13]); */ - /* char *channel_uuid = argv[14]; */ - /* uint32_t call_id = atoi(argv[15]); */ - /* uint32_t call_state = atoi(argv[16]); */ - - skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, device_name, device_instance, &listener); - if(listener) { - if(!strcmp(device_name, helper->listener->device_name) - && (device_instance == helper->listener->device_instance) - && (line_instance == helper->line_instance)) {/* the calling line */ - /* nothing */ - } else { - send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON); - skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_IN_USE_REMOTELY); - send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, 10, 0xffff); - send_display_prompt_status(listener, 0, SKINNY_DISP_IN_USE_REMOTE, - line_instance, helper->tech_pvt->call_id); - skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance); - } - } - return 0; -} - -switch_status_t skinny_session_process_dest(switch_core_session_t *session, listener_t *listener, uint32_t line_instance, char *dest, char append_dest, uint32_t backspace) -{ - skinny_action_t action; - switch_channel_t *channel = NULL; - private_t *tech_pvt = NULL; - char *data = NULL; - struct skinny_session_process_dest_helper helper = {0}; - - switch_assert(session); - switch_assert(listener); - switch_assert(listener->profile); - - channel = switch_core_session_get_channel(session); - tech_pvt = switch_core_session_get_private(session); - - if (!dest) { - if (strlen(tech_pvt->caller_profile->destination_number) == 0) {/* no digit yet */ - send_start_tone(listener, SKINNY_TONE_DIALTONE, 0, line_instance, tech_pvt->call_id); - } - if (backspace) { /* backspace */ - tech_pvt->caller_profile->destination_number[strlen(tech_pvt->caller_profile->destination_number)-1] = '\0'; - if (strlen(tech_pvt->caller_profile->destination_number) == 0) { - send_select_soft_keys(listener, line_instance, tech_pvt->call_id, SKINNY_KEY_SET_OFF_HOOK, 0xffff); - } - send_back_space_request(listener, line_instance, tech_pvt->call_id); - } - if (append_dest != '\0') {/* append digit */ - tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, - "%s%c", tech_pvt->caller_profile->destination_number, append_dest); - } - if (strlen(tech_pvt->caller_profile->destination_number) == 1) {/* first digit */ - if(!backspace) { - send_stop_tone(listener, line_instance, tech_pvt->call_id); - } - send_select_soft_keys(listener, line_instance, tech_pvt->call_id, - SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT, 0xffff); - } - } else { - tech_pvt->caller_profile->destination_number = switch_core_strdup(tech_pvt->caller_profile->pool, - dest); - } - if(dest) { - action = SKINNY_ACTION_ROUTE; - } else { - action = skinny_session_dest_match_pattern(session, &data); - } - switch(action) { - case SKINNY_ACTION_ROUTE: - tech_pvt->caller_profile->dialplan = switch_core_strdup(tech_pvt->caller_profile->pool, listener->profile->dialplan); - tech_pvt->caller_profile->context = switch_core_strdup(tech_pvt->caller_profile->pool, listener->profile->context); - send_dialed_number(listener, tech_pvt->caller_profile->destination_number, line_instance, tech_pvt->call_id); - skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_PROCEED); - skinny_session_send_call_info(session, listener, line_instance); - - skinny_session_start_media(session, listener, line_instance); - - helper.tech_pvt = tech_pvt; - helper.listener = listener; - helper.line_instance = line_instance; - skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), skinny_session_process_dest_callback, &helper); - break; - case SKINNY_ACTION_WAIT: - /* for now, wait forever */ - break; - case SKINNY_ACTION_DROP: - default: - switch_channel_hangup(channel, SWITCH_CAUSE_UNALLOCATED_NUMBER); - } - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_session_ring_out(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) -{ - switch_channel_t *channel = NULL; - private_t *tech_pvt = NULL; - - switch_assert(session); - switch_assert(listener); - switch_assert(listener->profile); - - channel = switch_core_session_get_channel(session); - tech_pvt = switch_core_session_get_private(session); - - skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_RING_OUT); - send_select_soft_keys(listener, line_instance, tech_pvt->call_id, - SKINNY_KEY_SET_RING_OUT, 0xffff); - send_display_prompt_status(listener, 0, SKINNY_DISP_RING_OUT, - line_instance, tech_pvt->call_id); - skinny_session_send_call_info(session, listener, line_instance); - - return SWITCH_STATUS_SUCCESS; -} - - -struct skinny_session_answer_helper { - private_t *tech_pvt; - listener_t *listener; - uint32_t line_instance; -}; - -int skinny_session_answer_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - struct skinny_session_answer_helper *helper = pArg; - listener_t *listener = NULL; - - char *device_name = argv[0]; - uint32_t device_instance = atoi(argv[1]); - /* uint32_t position = atoi(argv[2]); */ - uint32_t line_instance = atoi(argv[3]); - /* char *label = argv[4]; */ - /* char *value = argv[5]; */ - /* char *caller_name = argv[6]; */ - /* uint32_t ring_on_idle = atoi(argv[7]); */ - /* uint32_t ring_on_active = atoi(argv[8]); */ - /* uint32_t busy_trigger = atoi(argv[9]); */ - /* char *forward_all = argv[10]; */ - /* char *forward_busy = argv[11]; */ - /* char *forward_noanswer = argv[12]; */ - /* uint32_t noanswer_duration = atoi(argv[13]); */ - /* char *channel_uuid = argv[14]; */ - /* uint32_t call_id = atoi(argv[15]); */ - /* uint32_t call_state = atoi(argv[16]); */ - - skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, device_name, device_instance, &listener); - if(listener) { - if(!strcmp(device_name, helper->listener->device_name) - && (device_instance == helper->listener->device_instance) - && (line_instance == helper->line_instance)) {/* the answering line */ - /* nothing */ - } else { - send_define_current_time_date(listener); - send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON); - skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_IN_USE_REMOTELY); - send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, 10, 0x0002); - send_display_prompt_status(listener, 0, SKINNY_DISP_IN_USE_REMOTE, line_instance, helper->tech_pvt->call_id); - send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, helper->tech_pvt->call_id); - } - } - return 0; -} - -switch_status_t skinny_session_answer(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) -{ - struct skinny_session_answer_helper helper = {0}; - switch_channel_t *channel = NULL; - private_t *tech_pvt = NULL; - - switch_assert(session); - switch_assert(listener); - switch_assert(listener->profile); - - channel = switch_core_session_get_channel(session); - tech_pvt = switch_core_session_get_private(session); - - send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id); - send_set_speaker_mode(listener, SKINNY_SPEAKER_ON); - send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON); - skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_OFF_HOOK); - send_activate_call_plane(listener, line_instance); - - helper.tech_pvt = tech_pvt; - helper.listener = listener; - helper.line_instance = line_instance; - - skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), skinny_session_answer_callback, &helper); - - skinny_session_start_media(session, listener, line_instance); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_session_start_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) -{ - switch_channel_t *channel = NULL; - private_t *tech_pvt = NULL; - - switch_assert(session); - switch_assert(listener); - switch_assert(listener->profile); - - channel = switch_core_session_get_channel(session); - tech_pvt = switch_core_session_get_private(session); - - send_stop_tone(listener, line_instance, tech_pvt->call_id); - send_open_receive_channel(listener, - tech_pvt->call_id, /* uint32_t conference_id, */ - tech_pvt->call_id, /* uint32_t pass_thru_party_id, */ - 20, /* uint32_t packets, */ - SKINNY_CODEC_ULAW_64K, /* uint32_t payload_capacity, */ - 0, /* uint32_t echo_cancel_type, */ - 0, /* uint32_t g723_bitrate, */ - 0, /* uint32_t conference_id2, */ - 0 /* uint32_t reserved[10] */ - ); - skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_CONNECTED); - send_select_soft_keys(listener, line_instance, tech_pvt->call_id, - SKINNY_KEY_SET_CONNECTED, 0xffff); - send_display_prompt_status(listener, - 0, - SKINNY_DISP_CONNECTED, - line_instance, - tech_pvt->call_id); - skinny_session_send_call_info(session, listener, line_instance); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_session_hold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) -{ - switch_channel_t *channel = NULL; - private_t *tech_pvt = NULL; - - switch_assert(session); - switch_assert(listener); - switch_assert(listener->profile); - - channel = switch_core_session_get_channel(session); - tech_pvt = switch_core_session_get_private(session); - - skinny_session_stop_media(session, listener, line_instance); - switch_ivr_hold(session, NULL, 1); - - send_define_current_time_date(listener); - send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_WINK); - skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_HOLD); - send_select_soft_keys(listener, line_instance, tech_pvt->call_id, SKINNY_KEY_SET_ON_HOLD, 0xffff); - send_display_prompt_status(listener, 0, SKINNY_DISP_HOLD, - line_instance, tech_pvt->call_id); - skinny_session_send_call_info(tech_pvt->session, listener, line_instance); - send_set_speaker_mode(listener, SKINNY_SPEAKER_OFF); - send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_session_unhold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) -{ - switch_channel_t *channel = NULL; - private_t *tech_pvt = NULL; - - switch_assert(session); - switch_assert(listener); - switch_assert(listener->profile); - - channel = switch_core_session_get_channel(session); - tech_pvt = switch_core_session_get_private(session); - - skinny_hold_active_calls(listener); - send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id); - send_set_speaker_mode(listener, SKINNY_SPEAKER_ON); - send_select_soft_keys(listener, line_instance, tech_pvt->call_id, SKINNY_KEY_SET_RING_OUT, 0xffff); - skinny_session_start_media(session, listener, line_instance); - switch_ivr_unhold(session); - send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON); - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_session_transfer(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) -{ - switch_status_t status = SWITCH_STATUS_SUCCESS; - private_t *tech_pvt = NULL; - switch_channel_t *channel = NULL; - const char *remote_uuid = NULL; - switch_core_session_t *session2 = NULL; - private_t *tech_pvt2 = NULL; - - switch_assert(session); - switch_assert(listener); - switch_assert(listener->profile); - - tech_pvt = switch_core_session_get_private(session); - channel = switch_core_session_get_channel(session); - remote_uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); - - if (tech_pvt->transfer_from_call_id) { - if((session2 = skinny_profile_find_session(listener->profile, listener, &line_instance, tech_pvt->transfer_from_call_id))) { - switch_channel_t *channel2 = switch_core_session_get_channel(session2); - const char *remote_uuid2 = switch_channel_get_variable(channel2, SWITCH_SIGNAL_BOND_VARIABLE); - if (switch_ivr_uuid_bridge(remote_uuid, remote_uuid2) == SWITCH_STATUS_SUCCESS) { - switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); - switch_channel_hangup(channel2, SWITCH_CAUSE_NORMAL_CLEARING); - } else { - /* TODO: How to inform the user that the bridge is not possible? */ - } - switch_core_session_rwunlock(session2); - } - } else { - if(remote_uuid) { - /* TODO CallSelectStat */ - status = skinny_create_incoming_session(listener, &line_instance, &session2); - tech_pvt2 = switch_core_session_get_private(session2); - tech_pvt2->transfer_from_call_id = tech_pvt->call_id; - tech_pvt->transfer_to_call_id = tech_pvt2->call_id; - skinny_session_process_dest(session2, listener, line_instance, NULL, '\0', 0); - switch_core_session_rwunlock(session2); - } else { - /* TODO: How to inform the user that the bridge is not possible? */ - } - } - return status; -} - -switch_status_t skinny_session_stop_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) -{ - switch_channel_t *channel = NULL; - private_t *tech_pvt = NULL; - - switch_assert(session); - switch_assert(listener); - switch_assert(listener->profile); - - channel = switch_core_session_get_channel(session); - tech_pvt = switch_core_session_get_private(session); - - send_close_receive_channel(listener, - tech_pvt->call_id, /* uint32_t conference_id, */ - tech_pvt->party_id, /* uint32_t pass_thru_party_id, */ - tech_pvt->call_id /* uint32_t conference_id2, */ - ); - send_stop_media_transmission(listener, - tech_pvt->call_id, /* uint32_t conference_id, */ - tech_pvt->party_id, /* uint32_t pass_thru_party_id, */ - tech_pvt->call_id /* uint32_t conference_id2, */ - ); - - return SWITCH_STATUS_SUCCESS; -} - -struct skinny_hold_active_calls_helper { - listener_t *listener; -}; - -int skinny_hold_active_calls_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - struct skinny_hold_active_calls_helper *helper = pArg; - switch_core_session_t *session; - - /* char *device_name = argv[0]; */ - /* uint32_t device_instance = atoi(argv[1]); */ - /* uint32_t position = atoi(argv[2]); */ - uint32_t line_instance = atoi(argv[3]); - /* char *label = argv[4]; */ - /* char *value = argv[5]; */ - /* char *caller_name = argv[6]; */ - /* uint32_t ring_on_idle = atoi(argv[7]); */ - /* uint32_t ring_on_active = atoi(argv[8]); */ - /* uint32_t busy_trigger = atoi(argv[9]); */ - /* char *forward_all = argv[10]; */ - /* char *forward_busy = argv[11]; */ - /* char *forward_noanswer = argv[12]; */ - /* uint32_t noanswer_duration = atoi(argv[13]); */ - /* char *channel_uuid = argv[14]; */ - uint32_t call_id = atoi(argv[15]); - /* uint32_t call_state = atoi(argv[16]); */ - - session = skinny_profile_find_session(helper->listener->profile, helper->listener, &line_instance, call_id); - - if(session) { - skinny_session_hold_line(session, helper->listener, line_instance); - switch_core_session_rwunlock(session); - } - - return 0; -} - -switch_status_t skinny_hold_active_calls(listener_t *listener) -{ - struct skinny_hold_active_calls_helper helper = {0}; - char *sql; - - helper.listener = listener; - - if ((sql = switch_mprintf( - "SELECT skinny_lines.*, channel_uuid, call_id, call_state " - "FROM skinny_active_lines " - "INNER JOIN skinny_lines " - "ON skinny_active_lines.device_name = skinny_lines.device_name " - "AND skinny_active_lines.device_instance = skinny_lines.device_instance " - "AND skinny_active_lines.line_instance = skinny_lines.line_instance " - "WHERE skinny_lines.device_name='%s' AND skinny_lines.device_instance=%d AND call_state=%d", - listener->device_name, listener->device_instance, SKINNY_CONNECTED))) { - skinny_execute_sql_callback(listener->profile, listener->profile->sql_mutex, sql, skinny_hold_active_calls_callback, &helper); - switch_safe_free(sql); - } - - return SWITCH_STATUS_SUCCESS; -} /*****************************************************************************/ /* SKINNY BUTTONS */ /*****************************************************************************/ @@ -1689,1050 +907,6 @@ switch_status_t send_reset(listener_t *listener, uint32_t reset_type) return skinny_send_reply(listener, message); } -/*****************************************************************************/ -/* SKINNY MESSAGE HANDLERS */ -/*****************************************************************************/ -switch_status_t skinny_handle_keep_alive_message(listener_t *listener, skinny_message_t *request) -{ - skinny_message_t *message; - - message = switch_core_alloc(listener->pool, 12); - message->type = KEEP_ALIVE_ACK_MESSAGE; - message->length = 4; - keepalive_listener(listener, NULL); - skinny_send_reply(listener, message); - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *request) -{ - switch_status_t status = SWITCH_STATUS_FALSE; - skinny_profile_t *profile; - switch_event_t *event = NULL; - switch_event_t *params = NULL; - switch_xml_t xroot, xdomain, xgroup, xuser, xskinny, xparams, xparam, xbuttons, xbutton; - listener_t *listener2 = NULL; - char *sql; - assert(listener->profile); - profile = listener->profile; - - skinny_check_data_length(request, sizeof(request->data.reg)); - - if (!zstr(listener->device_name)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "A device is already registred on this listener.\n"); - send_register_reject(listener, "A device is already registred on this listener"); - return SWITCH_STATUS_FALSE; - } - - /* Check directory */ - skinny_device_event(listener, ¶ms, SWITCH_EVENT_REQUEST_PARAMS, SWITCH_EVENT_SUBCLASS_ANY); - switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "action", "skinny-auth"); - - if (switch_xml_locate_user("id", request->data.reg.device_name, profile->domain, "", &xroot, &xdomain, &xuser, &xgroup, params) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Can't find device [%s@%s]\n" - "You must define a domain called '%s' in your directory and add a user with id=\"%s\".\n" - , request->data.reg.device_name, profile->domain, profile->domain, request->data.reg.device_name); - send_register_reject(listener, "Device not found"); - status = SWITCH_STATUS_FALSE; - goto end; - } - - skinny_profile_find_listener_by_device_name_and_instance(listener->profile, - request->data.reg.device_name, request->data.reg.instance, &listener2); - if (listener2) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "Device %s:%d is already registred on another listener.\n", - request->data.reg.device_name, request->data.reg.instance); - send_register_reject(listener, "Device is already registred on another listener"); - status = SWITCH_STATUS_FALSE; - goto end; - } - - if ((sql = switch_mprintf( - "INSERT INTO skinny_devices " - "(name, user_id, instance, ip, type, max_streams, codec_string) " - "VALUES ('%s','%d','%d', '%s', '%d', '%d', '%s')", - request->data.reg.device_name, - request->data.reg.user_id, - request->data.reg.instance, - inet_ntoa(request->data.reg.ip), - request->data.reg.device_type, - request->data.reg.max_streams, - "" /* codec_string */ - ))) { - skinny_execute_sql(profile, sql, profile->sql_mutex); - switch_safe_free(sql); - } - - - strncpy(listener->device_name, request->data.reg.device_name, 16); - listener->device_instance = request->data.reg.instance; - listener->device_type = request->data.reg.device_type; - - xskinny = switch_xml_child(xuser, "skinny"); - if (xskinny) { - if ((xparams = switch_xml_child(xskinny, "params"))) { - for (xparam = switch_xml_child(xparams, "param"); xparam; xparam = xparam->next) { - const char *name = switch_xml_attr_soft(xparam, "name"); - const char *value = switch_xml_attr_soft(xparam, "value"); - if (!strcasecmp(name, "skinny-firmware-version")) { - strncpy(listener->firmware_version, value, 16); - } - } - } - if ((xbuttons = switch_xml_child(xskinny, "buttons"))) { - uint32_t line_instance = 1; - for (xbutton = switch_xml_child(xbuttons, "button"); xbutton; xbutton = xbutton->next) { - uint32_t position = atoi(switch_xml_attr_soft(xbutton, "position")); - uint32_t type = skinny_str2button(switch_xml_attr_soft(xbutton, "type")); - const char *label = switch_xml_attr_soft(xbutton, "label"); - const char *value = switch_xml_attr_soft(xbutton, "value"); - if(type == SKINNY_BUTTON_LINE) { - const char *caller_name = switch_xml_attr_soft(xbutton, "caller-name"); - uint32_t ring_on_idle = atoi(switch_xml_attr_soft(xbutton, "ring-on-idle")); - uint32_t ring_on_active = atoi(switch_xml_attr_soft(xbutton, "ring-on-active")); - uint32_t busy_trigger = atoi(switch_xml_attr_soft(xbutton, "busy-trigger")); - const char *forward_all = switch_xml_attr_soft(xbutton, "forward-all"); - const char *forward_busy = switch_xml_attr_soft(xbutton, "forward-busy"); - const char *forward_noanswer = switch_xml_attr_soft(xbutton, "forward-noanswer"); - uint32_t noanswer_duration = atoi(switch_xml_attr_soft(xbutton, "noanswer-duration")); - if ((sql = switch_mprintf( - "INSERT INTO skinny_lines " - "(device_name, device_instance, position, line_instance, " - "label, value, caller_name, " - "ring_on_idle, ring_on_active, busy_trigger, " - "forward_all, forward_busy, forward_noanswer, noanswer_duration) " - "VALUES('%s', %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', %d)", - request->data.reg.device_name, request->data.reg.instance, position, line_instance++, - label, value, caller_name, - ring_on_idle, ring_on_active, busy_trigger, - forward_all, forward_busy, forward_noanswer, noanswer_duration))) { - skinny_execute_sql(profile, sql, profile->sql_mutex); - switch_safe_free(sql); - } - } else { - const char *settings = switch_xml_attr_soft(xbutton, "settings"); - if ((sql = switch_mprintf( - "INSERT INTO skinny_buttons " - "(device_name, device_instance, position, type, label, value, settings) " - "VALUES('%s', %d, %d, %d, '%s', '%s', '%s')", - request->data.reg.device_name, - request->data.reg.instance, - position, - type, - label, - value, - settings))) { - skinny_execute_sql(profile, sql, profile->sql_mutex); - switch_safe_free(sql); - } - } - } - } - } - if (xroot) { - switch_xml_free(xroot); - } - - status = SWITCH_STATUS_SUCCESS; - - /* Reply with RegisterAckMessage */ - send_register_ack(listener, profile->keep_alive, profile->date_format, "", profile->keep_alive, ""); - - /* Send CapabilitiesReqMessage */ - send_capabilities_req(listener); - - /* skinny::register event */ - skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_REGISTER); - switch_event_fire(&event); - - keepalive_listener(listener, NULL); - -end: - if(params) { - switch_event_destroy(¶ms); - } - - return status; -} - -switch_status_t skinny_handle_port_message(listener_t *listener, skinny_message_t *request) -{ - char *sql; - skinny_profile_t *profile; - - switch_assert(listener->profile); - switch_assert(listener->device_name); - - profile = listener->profile; - - skinny_check_data_length(request, sizeof(request->data.as_uint16)); - - if ((sql = switch_mprintf( - "UPDATE skinny_devices SET port=%d WHERE name='%s' and instance=%d", - request->data.port.port, - listener->device_name, - listener->device_instance - ))) { - skinny_execute_sql(profile, sql, profile->sql_mutex); - switch_safe_free(sql); - } - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_keypad_button_message(listener_t *listener, skinny_message_t *request) -{ - uint32_t line_instance = 0; - switch_core_session_t *session; - - skinny_check_data_length(request, sizeof(request->data.keypad_button)); - - if(request->data.keypad_button.line_instance) { - line_instance = request->data.keypad_button.line_instance; - } else { - line_instance = 1; - } - - session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.keypad_button.call_id); - - if(session) { - switch_channel_t *channel = NULL; - private_t *tech_pvt = NULL; - char digit = '\0'; - - channel = switch_core_session_get_channel(session); - tech_pvt = switch_core_session_get_private(session); - - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "SEND DTMF ON CALL %d [%d]\n", tech_pvt->call_id, request->data.keypad_button.button); - - if (request->data.keypad_button.button == 14) { - digit = '*'; - } else if (request->data.keypad_button.button == 15) { - digit = '#'; - } else if (request->data.keypad_button.button >= 0 && request->data.keypad_button.button <= 9) { - digit = '0' + request->data.keypad_button.button; - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "UNKNOW DTMF RECEIVED ON CALL %d [%d]\n", tech_pvt->call_id, request->data.keypad_button.button); - } - - /* TODO check call_id and line */ - - if((skinny_line_get_state(listener, line_instance, tech_pvt->call_id) == SKINNY_OFF_HOOK)) { - - skinny_session_process_dest(session, listener, line_instance, NULL, digit, 0); - } else { - if(digit != '\0') { - switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0)}; - dtmf.digit = digit; - switch_channel_queue_dtmf(channel, &dtmf); - } - } - } - - if(session) { - switch_core_session_rwunlock(session); - } - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_stimulus_message(listener_t *listener, skinny_message_t *request) -{ - switch_status_t status = SWITCH_STATUS_SUCCESS; - struct speed_dial_stat_res_message *button = NULL; - uint32_t line_instance = 0; - uint32_t call_id = 0; - switch_core_session_t *session = NULL; - - skinny_check_data_length(request, sizeof(request->data.stimulus)-sizeof(request->data.stimulus.call_id)); - - if(skinny_check_data_length_soft(request, sizeof(request->data.stimulus))) { - call_id = request->data.stimulus.call_id; - } - - switch(request->data.stimulus.instance_type) { - case SKINNY_BUTTON_LAST_NUMBER_REDIAL: - skinny_create_incoming_session(listener, &line_instance, &session); - skinny_session_process_dest(session, listener, line_instance, "redial", '\0', 0); - break; - case SKINNY_BUTTON_SPEED_DIAL: - skinny_speed_dial_get(listener, request->data.stimulus.instance, &button); - if(strlen(button->line) > 0) { - skinny_create_incoming_session(listener, &line_instance, &session); - skinny_session_process_dest(session, listener, line_instance, button->line, '\0', 0); - } - break; - case SKINNY_BUTTON_HOLD: - session = skinny_profile_find_session(listener->profile, listener, &line_instance, call_id); - - if(session) { - status = skinny_session_hold_line(session, listener, line_instance); - } - break; - case SKINNY_BUTTON_VOICEMAIL: - skinny_create_incoming_session(listener, &line_instance, &session); - skinny_session_process_dest(session, listener, line_instance, "vmain", '\0', 0); - break; - default: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown Stimulus Type Received [%d]\n", request->data.stimulus.instance_type); - } - - if(session) { - switch_core_session_rwunlock(session); - } - - return status; -} - -switch_status_t skinny_handle_off_hook_message(listener_t *listener, skinny_message_t *request) -{ - uint32_t line_instance; - switch_core_session_t *session = NULL; - private_t *tech_pvt = NULL; - - skinny_check_data_length(request, sizeof(request->data.off_hook)); - - if(request->data.off_hook.line_instance > 0) { - line_instance = request->data.off_hook.line_instance; - } else { - line_instance = 1; - } - - session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.off_hook.call_id); - - if(session) { /*answering a call */ - skinny_session_answer(session, listener, line_instance); - } else { /* start a new call */ - skinny_create_incoming_session(listener, &line_instance, &session); - tech_pvt = switch_core_session_get_private(session); - assert(tech_pvt != NULL); - - skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 0); - } - - if(session) { - switch_core_session_rwunlock(session); - } - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_on_hook_message(listener_t *listener, skinny_message_t *request) -{ - switch_status_t status = SWITCH_STATUS_SUCCESS; - switch_core_session_t *session = NULL; - uint32_t line_instance = 0; - - skinny_check_data_length(request, sizeof(request->data.on_hook)); - - line_instance = request->data.on_hook.line_instance; - - session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.on_hook.call_id); - - if(session) { - switch_channel_t *channel = NULL; - - channel = switch_core_session_get_channel(session); - - switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); - } - - if(session) { - switch_core_session_rwunlock(session); - } - - return status; -} - -switch_status_t skinny_handle_speed_dial_stat_request(listener_t *listener, skinny_message_t *request) -{ - skinny_message_t *message; - struct speed_dial_stat_res_message *button = NULL; - - skinny_check_data_length(request, sizeof(request->data.speed_dial_req)); - - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.speed_dial_res)); - message->type = SPEED_DIAL_STAT_RES_MESSAGE; - message->length = 4 + sizeof(message->data.speed_dial_res); - - skinny_speed_dial_get(listener, request->data.speed_dial_req.number, &button); - - memcpy(&message->data.speed_dial_res, button, sizeof(struct speed_dial_stat_res_message)); - - skinny_send_reply(listener, message); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_line_stat_request(listener_t *listener, skinny_message_t *request) -{ - skinny_message_t *message; - struct line_stat_res_message *button = NULL; - - skinny_check_data_length(request, sizeof(request->data.line_req)); - - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.line_res)); - message->type = LINE_STAT_RES_MESSAGE; - message->length = 4 + sizeof(message->data.line_res); - - skinny_line_get(listener, request->data.line_req.number, &button); - - memcpy(&message->data.line_res, button, sizeof(struct line_stat_res_message)); - - skinny_send_reply(listener, message); - - return SWITCH_STATUS_SUCCESS; -} - -int skinny_config_stat_res_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - skinny_message_t *message = pArg; - char *device_name = argv[0]; - int user_id = atoi(argv[1]); - int instance = atoi(argv[2]); - char *user_name = argv[3]; - char *server_name = argv[4]; - int number_lines = atoi(argv[5]); - int number_speed_dials = atoi(argv[6]); - - strncpy(message->data.config_res.device_name, device_name, 16); - message->data.config_res.user_id = user_id; - message->data.config_res.instance = instance; - strncpy(message->data.config_res.user_name, user_name, 40); - strncpy(message->data.config_res.server_name, server_name, 40); - message->data.config_res.number_lines = number_lines; - message->data.config_res.number_speed_dials = number_speed_dials; - - return 0; -} - -switch_status_t skinny_handle_config_stat_request(listener_t *listener, skinny_message_t *request) -{ - char *sql; - skinny_message_t *message; - skinny_profile_t *profile; - - switch_assert(listener->profile); - switch_assert(listener->device_name); - - profile = listener->profile; - - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.config_res)); - message->type = CONFIG_STAT_RES_MESSAGE; - message->length = 4 + sizeof(message->data.config_res); - - if ((sql = switch_mprintf( - "SELECT name, user_id, instance, '' AS user_name, '' AS server_name, " - "(SELECT COUNT(*) FROM skinny_lines WHERE device_name='%s' AND device_instance=%d) AS number_lines, " - "(SELECT COUNT(*) FROM skinny_buttons WHERE device_name='%s' AND device_instance=%d AND type=%d) AS number_speed_dials " - "FROM skinny_devices WHERE name='%s' ", - listener->device_name, - listener->device_instance, - listener->device_name, - listener->device_instance, - SKINNY_BUTTON_SPEED_DIAL, - listener->device_name - ))) { - skinny_execute_sql_callback(profile, profile->sql_mutex, sql, skinny_config_stat_res_callback, message); - switch_safe_free(sql); - } - skinny_send_reply(listener, message); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_time_date_request(listener_t *listener, skinny_message_t *request) -{ - return send_define_current_time_date(listener); -} - -struct button_template_helper { - skinny_message_t *message; - int count[SKINNY_BUTTON_UNDEFINED+1]; - int max_position; -}; - -int skinny_handle_button_template_request_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - struct button_template_helper *helper = pArg; - skinny_message_t *message = helper->message; - /* char *device_name = argv[0]; */ - /* uint32_t device_instance = argv[1]; */ - int position = atoi(argv[2]); - uint32_t type = atoi(argv[3]); - /* int relative_position = atoi(argv[4]); */ - - message->data.button_template.btn[position-1].instance_number = ++helper->count[type]; - message->data.button_template.btn[position-1].button_definition = type; - - message->data.button_template.button_count++; - message->data.button_template.total_button_count++; - if(position > helper->max_position) { - helper->max_position = position; - } - - return 0; -} - -switch_status_t skinny_handle_button_template_request(listener_t *listener, skinny_message_t *request) -{ - skinny_message_t *message; - struct button_template_helper helper = {0}; - skinny_profile_t *profile; - char *sql; - - switch_assert(listener->profile); - switch_assert(listener->device_name); - - profile = listener->profile; - - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.button_template)); - message->type = BUTTON_TEMPLATE_RES_MESSAGE; - message->length = 4 + sizeof(message->data.button_template); - - message->data.button_template.button_offset = 0; - message->data.button_template.button_count = 0; - message->data.button_template.total_button_count = 0; - - helper.message = message; - - /* Add buttons */ - if ((sql = switch_mprintf( - "SELECT device_name, device_instance, position, MIN(type, %d) AS type " - "FROM skinny_buttons " - "WHERE device_name='%s' AND device_instance=%d " - "ORDER BY position", - SKINNY_BUTTON_UNDEFINED, - listener->device_name, listener->device_instance - ))) { - skinny_execute_sql_callback(profile, profile->sql_mutex, sql, skinny_handle_button_template_request_callback, &helper); - switch_safe_free(sql); - } - - /* Add lines */ - if ((sql = switch_mprintf( - "SELECT device_name, device_instance, position, %d AS type " - "FROM skinny_lines " - "WHERE device_name='%s' AND device_instance=%d " - "ORDER BY position", - SKINNY_BUTTON_LINE, - listener->device_name, listener->device_instance - ))) { - skinny_execute_sql_callback(profile, profile->sql_mutex, sql, skinny_handle_button_template_request_callback, &helper); - switch_safe_free(sql); - } - - /* Fill remaining buttons with Undefined */ - for(int i = 0; i+1 < helper.max_position; i++) { - if(message->data.button_template.btn[i].button_definition == SKINNY_BUTTON_UNKNOWN) { - message->data.button_template.btn[i].instance_number = ++helper.count[SKINNY_BUTTON_UNDEFINED]; - message->data.button_template.btn[i].button_definition = SKINNY_BUTTON_UNDEFINED; - message->data.button_template.button_count++; - message->data.button_template.total_button_count++; - } - } - - - - return skinny_send_reply(listener, message);; -} - -switch_status_t skinny_handle_version_request(listener_t *listener, skinny_message_t *request) -{ - if (zstr(listener->firmware_version)) { - char *id_str; - skinny_device_type_params_t *params; - id_str = switch_mprintf("%d", listener->device_type); - params = (skinny_device_type_params_t *) switch_core_hash_find(listener->profile->device_type_params_hash, id_str); - if (params) { - if (!zstr(params->firmware_version)) { - strncpy(listener->firmware_version, params->firmware_version, 16); - } - } - } - - if (!zstr(listener->firmware_version)) { - return send_version(listener, listener->firmware_version); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, - "Device %s:%d is requesting for firmware version, but none is set.\n", - listener->device_name, listener->device_instance); - return SWITCH_STATUS_SUCCESS; - } -} - -switch_status_t skinny_handle_capabilities_response(listener_t *listener, skinny_message_t *request) -{ - char *sql; - skinny_profile_t *profile; - - uint32_t i = 0; - uint32_t n = 0; - char *codec_order[SWITCH_MAX_CODECS]; - char *codec_string; - - size_t string_len, string_pos, pos; - - switch_assert(listener->profile); - switch_assert(listener->device_name); - - profile = listener->profile; - - skinny_check_data_length(request, sizeof(request->data.cap_res.count)); - - n = request->data.cap_res.count; - if (n > SWITCH_MAX_CODECS) { - n = SWITCH_MAX_CODECS; - } - string_len = -1; - - skinny_check_data_length(request, sizeof(request->data.cap_res.count) + n * sizeof(request->data.cap_res.caps[0])); - - for (i = 0; i < n; i++) { - char *codec = skinny_codec2string(request->data.cap_res.caps[i].codec); - codec_order[i] = codec; - string_len += strlen(codec)+1; - } - i = 0; - pos = 0; - codec_string = switch_core_alloc(listener->pool, string_len+1); - for (string_pos = 0; string_pos < string_len; string_pos++) { - char *codec = codec_order[i]; - switch_assert(i < n); - if(pos == strlen(codec)) { - codec_string[string_pos] = ','; - i++; - pos = 0; - } else { - codec_string[string_pos] = codec[pos++]; - } - } - codec_string[string_len] = '\0'; - if ((sql = switch_mprintf( - "UPDATE skinny_devices SET codec_string='%s' WHERE name='%s'", - codec_string, - listener->device_name - ))) { - skinny_execute_sql(profile, sql, profile->sql_mutex); - switch_safe_free(sql); - } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, - "Codecs %s supported.\n", codec_string); - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_alarm(listener_t *listener, skinny_message_t *request) -{ - switch_event_t *event = NULL; - - skinny_check_data_length(request, sizeof(request->data.alarm)); - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, - "Received alarm: Severity=%d, DisplayMessage=%s, Param1=%d, Param2=%d.\n", - request->data.alarm.alarm_severity, request->data.alarm.display_message, - request->data.alarm.alarm_param1, request->data.alarm.alarm_param2); - /* skinny::alarm event */ - skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_ALARM); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Alarm-Severity", "%d", request->data.alarm.alarm_severity); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Alarm-DisplayMessage", "%s", request->data.alarm.display_message); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Alarm-Param1", "%d", request->data.alarm.alarm_param1); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Alarm-Param2", "%d", request->data.alarm.alarm_param2); - switch_event_fire(&event); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_open_receive_channel_ack_message(listener_t *listener, skinny_message_t *request) -{ - switch_status_t status = SWITCH_STATUS_SUCCESS; - uint32_t line_instance = 0; - switch_core_session_t *session; - - skinny_check_data_length(request, sizeof(request->data.open_receive_channel_ack)); - - session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.open_receive_channel_ack.pass_thru_party_id); - - if(session) { - const char *err = NULL; - private_t *tech_pvt = NULL; - switch_channel_t *channel = NULL; - struct in_addr addr; - - tech_pvt = switch_core_session_get_private(session); - channel = switch_core_session_get_channel(session); - - /* Codec */ - tech_pvt->iananame = "PCMU"; /* TODO */ - tech_pvt->codec_ms = 10; /* TODO */ - tech_pvt->rm_rate = 8000; /* TODO */ - tech_pvt->rm_fmtp = NULL; /* TODO */ - tech_pvt->agreed_pt = (switch_payload_t) 0; /* TODO */ - tech_pvt->rm_encoding = switch_core_strdup(switch_core_session_get_pool(session), ""); - skinny_tech_set_codec(tech_pvt, 0); - if ((status = skinny_tech_set_codec(tech_pvt, 0)) != SWITCH_STATUS_SUCCESS) { - goto end; - } - - /* Request a local port from the core's allocator */ - if (!(tech_pvt->local_sdp_audio_port = switch_rtp_request_port(listener->profile->ip))) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_CRIT, "No RTP ports available!\n"); - return SWITCH_STATUS_FALSE; - } - tech_pvt->local_sdp_audio_ip = switch_core_strdup(switch_core_session_get_pool(session), listener->profile->ip); - - tech_pvt->remote_sdp_audio_ip = inet_ntoa(request->data.open_receive_channel_ack.ip); - tech_pvt->remote_sdp_audio_port = request->data.open_receive_channel_ack.port; - - tech_pvt->rtp_session = switch_rtp_new(tech_pvt->local_sdp_audio_ip, - tech_pvt->local_sdp_audio_port, - tech_pvt->remote_sdp_audio_ip, - tech_pvt->remote_sdp_audio_port, - tech_pvt->agreed_pt, - tech_pvt->read_impl.samples_per_packet, - tech_pvt->codec_ms * 1000, - (switch_rtp_flag_t) 0, "soft", &err, - switch_core_session_get_pool(session)); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, - "AUDIO RTP [%s] %s:%d->%s:%d codec: %u ms: %d [%s]\n", - switch_channel_get_name(channel), - tech_pvt->local_sdp_audio_ip, - tech_pvt->local_sdp_audio_port, - tech_pvt->remote_sdp_audio_ip, - tech_pvt->remote_sdp_audio_port, - tech_pvt->agreed_pt, - tech_pvt->read_impl.microseconds_per_packet / 1000, - switch_rtp_ready(tech_pvt->rtp_session) ? "SUCCESS" : err); - inet_aton(tech_pvt->local_sdp_audio_ip, &addr); - send_start_media_transmission(listener, - tech_pvt->call_id, /* uint32_t conference_id, */ - tech_pvt->party_id, /* uint32_t pass_thru_party_id, */ - addr.s_addr, /* uint32_t remote_ip, */ - tech_pvt->local_sdp_audio_port, /* uint32_t remote_port, */ - 20, /* uint32_t ms_per_packet, */ - SKINNY_CODEC_ULAW_64K, /* uint32_t payload_capacity, */ - 184, /* uint32_t precedence, */ - 0, /* uint32_t silence_suppression, */ - 0, /* uint16_t max_frames_per_packet, */ - 0 /* uint32_t g723_bitrate */ - ); - - if (switch_channel_get_state(channel) == CS_NEW) { - switch_channel_set_state(channel, CS_INIT); - } - switch_channel_mark_answered(channel); - } -end: - if(session) { - switch_core_session_rwunlock(session); - } - return status; -} - -switch_status_t skinny_handle_soft_key_set_request(listener_t *listener, skinny_message_t *request) -{ - skinny_message_t *message; - skinny_profile_t *profile; - - switch_assert(listener->profile); - switch_assert(listener->device_name); - - profile = listener->profile; - - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.soft_key_set)); - message->type = SOFT_KEY_SET_RES_MESSAGE; - message->length = 4 + sizeof(message->data.soft_key_set); - - message->data.soft_key_set.soft_key_set_offset = 0; - message->data.soft_key_set.soft_key_set_count = 11; - message->data.soft_key_set.total_soft_key_set_count = 11; - - /* TODO fill the set */ - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOOK].soft_key_template_index[0] = SOFTKEY_NEWCALL; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOOK].soft_key_template_index[1] = SOFTKEY_REDIAL; - - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_OFF_HOOK].soft_key_template_index[1] = SOFTKEY_REDIAL; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_OFF_HOOK].soft_key_template_index[2] = SOFTKEY_ENDCALL; - - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT].soft_key_template_index[0] = SOFTKEY_BACKSPACE; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT].soft_key_template_index[2] = SOFTKEY_ENDCALL; - - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[0] = SOFTKEY_ENDCALL; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[1] = SOFTKEY_HOLD; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[2] = SOFTKEY_NEWCALL; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[3] = SOFTKEY_TRANSFER; - - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_RING_IN].soft_key_template_index[0] = SOFTKEY_ANSWER; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_RING_IN].soft_key_template_index[1] = SOFTKEY_ENDCALL; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_RING_IN].soft_key_template_index[2] = SOFTKEY_NEWCALL; - - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOLD].soft_key_template_index[0] = SOFTKEY_NEWCALL; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOLD].soft_key_template_index[1] = SOFTKEY_RESUME; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOLD].soft_key_template_index[2] = SOFTKEY_ENDCALL; - - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_OFF_HOOK_WITH_FEATURES].soft_key_template_index[1] = SOFTKEY_REDIAL; - message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_OFF_HOOK_WITH_FEATURES].soft_key_template_index[2] = SOFTKEY_ENDCALL; - - skinny_send_reply(listener, message); - - /* Init the states */ - send_select_soft_keys(listener, 0, 0, SKINNY_KEY_SET_ON_HOOK, 0xffff); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_soft_key_event_message(listener_t *listener, skinny_message_t *request) -{ - switch_status_t status = SWITCH_STATUS_SUCCESS; - uint32_t line_instance = 0; - switch_core_session_t *session = NULL; - switch_channel_t *channel = NULL; - private_t *tech_pvt = NULL; - - switch_assert(listener); - switch_assert(listener->profile); - - skinny_check_data_length(request, sizeof(request->data.soft_key_event)); - - line_instance = request->data.soft_key_event.line_instance; - - switch(request->data.soft_key_event.event) { - case SOFTKEY_REDIAL: - status = skinny_create_incoming_session(listener, &line_instance, &session); - - skinny_session_process_dest(session, listener, line_instance, "redial", '\0', 0); - break; - case SOFTKEY_NEWCALL: - status = skinny_create_incoming_session(listener, &line_instance, &session); - tech_pvt = switch_core_session_get_private(session); - - skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 0); - break; - case SOFTKEY_HOLD: - session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); - - if(session) { - status = skinny_session_hold_line(session, listener, line_instance); - } - break; - case SOFTKEY_TRANSFER: - session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); - - if(session) { - status = skinny_session_transfer(session, listener, line_instance); - } - break; - case SOFTKEY_BACKSPACE: - session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); - - if(session) { - skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 1); - } - break; - case SOFTKEY_ENDCALL: - session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); - - if(session) { - channel = switch_core_session_get_channel(session); - - switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); - } - break; - case SOFTKEY_RESUME: - session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); - - if(session) { - status = skinny_session_unhold_line(session, listener, line_instance); - } - break; - case SOFTKEY_ANSWER: - session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); - - if(session) { - status = skinny_session_answer(session, listener, line_instance); - } - break; - default: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, - "Unknown SoftKeyEvent type: %d.\n", request->data.soft_key_event.event); - } - - if(session) { - switch_core_session_rwunlock(session); - } - - return status; -} - -switch_status_t skinny_handle_unregister(listener_t *listener, skinny_message_t *request) -{ - switch_event_t *event = NULL; - skinny_message_t *message; - - /* skinny::unregister event */ - skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_UNREGISTER); - switch_event_fire(&event); - - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.unregister_ack)); - message->type = UNREGISTER_ACK_MESSAGE; - message->length = 4 + sizeof(message->data.unregister_ack); - message->data.unregister_ack.unregister_status = 0; /* OK */ - skinny_send_reply(listener, message); - - /* Close socket */ - switch_clear_flag_locked(listener, LFLAG_RUNNING); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_soft_key_template_request(listener_t *listener, skinny_message_t *request) -{ - skinny_message_t *message; - skinny_profile_t *profile; - - switch_assert(listener->profile); - switch_assert(listener->device_name); - - profile = listener->profile; - - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.soft_key_template)); - message->type = SOFT_KEY_TEMPLATE_RES_MESSAGE; - message->length = 4 + sizeof(message->data.soft_key_template); - - message->data.soft_key_template.soft_key_offset = 0; - message->data.soft_key_template.soft_key_count = 21; - message->data.soft_key_template.total_soft_key_count = 21; - - memcpy(message->data.soft_key_template.soft_key, - soft_key_template_default, - sizeof(soft_key_template_default)); - - skinny_send_reply(listener, message); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_headset_status_message(listener_t *listener, skinny_message_t *request) -{ - skinny_check_data_length(request, sizeof(request->data.headset_status)); - - /* Nothing to do */ - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_register_available_lines_message(listener_t *listener, skinny_message_t *request) -{ - skinny_check_data_length(request, sizeof(request->data.reg_lines)); - - /* Do nothing */ - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_service_url_stat_request(listener_t *listener, skinny_message_t *request) -{ - skinny_message_t *message; - struct service_url_stat_res_message *button = NULL; - - skinny_check_data_length(request, sizeof(request->data.service_url_req)); - - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.service_url_res)); - message->type = SERVICE_URL_STAT_RES_MESSAGE; - message->length = 4 + sizeof(message->data.service_url_res); - - skinny_service_url_get(listener, request->data.service_url_req.service_url_index, &button); - - memcpy(&message->data.service_url_res, button, sizeof(struct service_url_stat_res_message)); - - skinny_send_reply(listener, message); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_feature_stat_request(listener_t *listener, skinny_message_t *request) -{ - skinny_message_t *message; - struct feature_stat_res_message *button = NULL; - - skinny_check_data_length(request, sizeof(request->data.feature_req)); - - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.feature_res)); - message->type = FEATURE_STAT_RES_MESSAGE; - message->length = 4 + sizeof(message->data.feature_res); - - skinny_feature_get(listener, request->data.feature_req.feature_index, &button); - - memcpy(&message->data.feature_res, button, sizeof(struct feature_stat_res_message)); - - skinny_send_reply(listener, message); - - return SWITCH_STATUS_SUCCESS; -} - -switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *request) -{ - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, - "Received %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); - if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "Device should send a register message first.\n"); - return SWITCH_STATUS_FALSE; - } - switch(request->type) { - case KEEP_ALIVE_MESSAGE: - return skinny_handle_keep_alive_message(listener, request); - case REGISTER_MESSAGE: - return skinny_handle_register(listener, request); - case PORT_MESSAGE: - return skinny_handle_port_message(listener, request); - case KEYPAD_BUTTON_MESSAGE: - return skinny_handle_keypad_button_message(listener, request); - case STIMULUS_MESSAGE: - return skinny_handle_stimulus_message(listener, request); - case OFF_HOOK_MESSAGE: - return skinny_handle_off_hook_message(listener, request); - case ON_HOOK_MESSAGE: - return skinny_handle_on_hook_message(listener, request); - case SPEED_DIAL_STAT_REQ_MESSAGE: - return skinny_handle_speed_dial_stat_request(listener, request); - case LINE_STAT_REQ_MESSAGE: - return skinny_handle_line_stat_request(listener, request); - case CONFIG_STAT_REQ_MESSAGE: - return skinny_handle_config_stat_request(listener, request); - case TIME_DATE_REQ_MESSAGE: - return skinny_handle_time_date_request(listener, request); - case BUTTON_TEMPLATE_REQ_MESSAGE: - return skinny_handle_button_template_request(listener, request); - case VERSION_REQ_MESSAGE: - return skinny_handle_version_request(listener, request); - case CAPABILITIES_RES_MESSAGE: - return skinny_handle_capabilities_response(listener, request); - case ALARM_MESSAGE: - return skinny_handle_alarm(listener, request); - case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE: - return skinny_handle_open_receive_channel_ack_message(listener, request); - case SOFT_KEY_SET_REQ_MESSAGE: - return skinny_handle_soft_key_set_request(listener, request); - case SOFT_KEY_EVENT_MESSAGE: - return skinny_handle_soft_key_event_message(listener, request); - case UNREGISTER_MESSAGE: - return skinny_handle_unregister(listener, request); - case SOFT_KEY_TEMPLATE_REQ_MESSAGE: - return skinny_handle_soft_key_template_request(listener, request); - case HEADSET_STATUS_MESSAGE: - return skinny_headset_status_message(listener, request); - case REGISTER_AVAILABLE_LINES_MESSAGE: - return skinny_handle_register_available_lines_message(listener, request); - case SERVICE_URL_STAT_REQ_MESSAGE: - return skinny_handle_service_url_stat_request(listener, request); - case FEATURE_STAT_REQ_MESSAGE: - return skinny_handle_feature_stat_request(listener, request); - default: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, - "Unhandled request %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); - return SWITCH_STATUS_SUCCESS; - } -} - switch_status_t skinny_perform_send_reply(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply) { char *ptr; diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.h b/src/mod/endpoints/mod_skinny/skinny_protocol.h index 1a1fda959c..45bac96243 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.h +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.h @@ -621,7 +621,7 @@ enum skinny_codecs { SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257 }; -typedef switch_status_t (*skinny_command_t) (char **argv, int argc, switch_stream_handle_t *stream); +char* skinny_codec2string(enum skinny_codecs skinnycodec); /*****************************************************************************/ /* SKINNY FUNCTIONS */ @@ -638,22 +638,12 @@ switch_status_t skinny_read_packet(listener_t *listener, skinny_message_t **req) switch_status_t skinny_device_event(listener_t *listener, switch_event_t **ev, switch_event_types_t event_id, const char *subclass_name); -switch_status_t skinny_session_send_call_info(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); switch_status_t skinny_session_walk_lines(skinny_profile_t *profile, char *channel_uuid, switch_core_db_callback_func_t callback, void *data); -switch_call_cause_t skinny_ring_lines(private_t *tech_pvt); - -switch_status_t skinny_create_ingoing_session(listener_t *listener, uint32_t *line_instance, switch_core_session_t **session); -switch_status_t skinny_session_process_dest(switch_core_session_t *session, listener_t *listener, uint32_t line_instance, char *dest, char append_dest, uint32_t backspace); -switch_status_t skinny_session_ring_out(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); -switch_status_t skinny_session_answer(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); -switch_status_t skinny_session_start_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); -switch_status_t skinny_session_hold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); -switch_status_t skinny_session_unhold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); -switch_status_t skinny_session_stop_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); -switch_status_t skinny_hold_active_calls(listener_t *listener); void skinny_line_get(listener_t *listener, uint32_t instance, struct line_stat_res_message **button); void skinny_speed_dial_get(listener_t *listener, uint32_t instance, struct speed_dial_stat_res_message **button); +void skinny_service_url_get(listener_t *listener, uint32_t instance, struct service_url_stat_res_message **button); +void skinny_feature_get(listener_t *listener, uint32_t instance, struct feature_stat_res_message **button); switch_status_t skinny_perform_send_reply(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply); #define skinny_send_reply(listener, reply) skinny_perform_send_reply(listener, __FILE__, __SWITCH_FUNC__, __LINE__, reply) @@ -737,6 +727,7 @@ switch_status_t send_define_time_date(listener_t *listener, switch_status_t send_define_current_time_date(listener_t *listener); switch_status_t send_version(listener_t *listener, char *version); +switch_status_t send_capabilities_req(listener_t *listener); switch_status_t send_register_reject(listener_t *listener, char *error); switch_status_t send_open_receive_channel(listener_t *listener, diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c new file mode 100644 index 0000000000..cce32b9943 --- /dev/null +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -0,0 +1,1877 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2010, Mathieu Parent + * + * 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 + * Mathieu Parent + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Mathieu Parent + * + * + * skinny_server.c -- Skinny Call Control Protocol (SCCP) Endpoint Module + * + */ +#include +#include "mod_skinny.h" +#include "skinny_protocol.h" +#include "skinny_tables.h" +#include "skinny_labels.h" +#include "skinny_server.h" + +struct soft_key_template_definition soft_key_template_default[] = { + { SKINNY_DISP_REDIAL, SOFTKEY_REDIAL }, + { SKINNY_DISP_NEWCALL, SOFTKEY_NEWCALL }, + { SKINNY_DISP_HOLD, SOFTKEY_HOLD }, + { SKINNY_DISP_TRANSFER, SOFTKEY_TRANSFER }, + { SKINNY_DISP_CFWDALL, SOFTKEY_CFWDALL }, + { SKINNY_DISP_CFWDBUSY, SOFTKEY_CFWDBUSY }, + { SKINNY_DISP_CFWDNOANSWER, SOFTKEY_CFWDNOANSWER }, + { SKINNY_DISP_BACKSPACE, SOFTKEY_BACKSPACE }, + { SKINNY_DISP_ENDCALL, SOFTKEY_ENDCALL }, + { SKINNY_DISP_RESUME, SOFTKEY_RESUME }, + { SKINNY_DISP_ANSWER, SOFTKEY_ANSWER }, + { SKINNY_DISP_INFO, SOFTKEY_INFO }, + { SKINNY_DISP_CONFRM, SOFTKEY_CONFRM }, + { SKINNY_DISP_PARK, SOFTKEY_PARK }, + { SKINNY_DISP_JOIN, SOFTKEY_JOIN }, + { SKINNY_DISP_MEETME, SOFTKEY_MEETMECONFRM }, + { SKINNY_DISP_CALLPICKUP, SOFTKEY_CALLPICKUP }, + { SKINNY_DISP_GRPCALLPICKUP, SOFTKEY_GRPCALLPICKUP }, + { SKINNY_DISP_DND, SOFTKEY_DND }, + { SKINNY_DISP_IDIVERT, SOFTKEY_IDIVERT }, +}; + +/*****************************************************************************/ +/* SESSION FUNCTIONS */ +/*****************************************************************************/ +switch_status_t skinny_create_incoming_session(listener_t *listener, uint32_t *line_instance_p, switch_core_session_t **session) +{ + uint32_t line_instance; + switch_core_session_t *nsession; + switch_channel_t *channel; + private_t *tech_pvt; + char name[128]; + char *sql; + struct line_stat_res_message *button = NULL; + + line_instance = *line_instance_p; + if((nsession = skinny_profile_find_session(listener->profile, listener, line_instance_p, 0))) { + if(skinny_line_get_state(listener, *line_instance_p, 0) == SKINNY_OFF_HOOK) { + /* Reuse existing session */ + *session = nsession; + return SWITCH_STATUS_SUCCESS; + } + switch_core_session_rwunlock(nsession); + } + *line_instance_p = line_instance; + if(*line_instance_p == 0) { + *line_instance_p = 1; + } + + skinny_hold_active_calls(listener); + + skinny_line_get(listener, *line_instance_p, &button); + + if (!button || !button->shortname) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Line %d not found on device %s %d\n", + *line_instance_p, listener->device_name, listener->device_instance); + goto error; + } + + if (!(nsession = switch_core_session_request(skinny_get_endpoint_interface(), + SWITCH_CALL_DIRECTION_INBOUND, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error Creating Session\n"); + goto error; + } + + if (!(tech_pvt = (struct private_object *) switch_core_session_alloc(nsession, sizeof(*tech_pvt)))) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT, + "Error Creating Session private object\n"); + goto error; + } + + switch_core_session_add_stream(nsession, NULL); + + tech_init(tech_pvt, listener->profile, nsession); + + channel = switch_core_session_get_channel(nsession); + + snprintf(name, sizeof(name), "SKINNY/%s/%s:%d/%d", listener->profile->name, + listener->device_name, listener->device_instance, *line_instance_p); + switch_channel_set_name(channel, name); + + if (switch_core_session_thread_launch(nsession) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT, + "Error Creating Session thread\n"); + goto error; + } + if (switch_core_session_read_lock(nsession) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT, + "Error Locking Session\n"); + goto error; + } + /* First create the caller profile in the patterns Dialplan */ + if (!(tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(nsession), + NULL, listener->profile->patterns_dialplan, + button->displayname, button->shortname, + listener->remote_ip, NULL, NULL, NULL, + "skinny" /* modname */, + listener->profile->patterns_context, + "")) != 0) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT, + "Error Creating Session caller profile\n"); + goto error; + } + + switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); + + if ((sql = switch_mprintf( + "INSERT INTO skinny_active_lines " + "(device_name, device_instance, line_instance, channel_uuid, call_id, call_state) " + "SELECT device_name, device_instance, line_instance, '%s', %d, %d " + "FROM skinny_lines " + "WHERE value='%s'", + switch_core_session_get_uuid(nsession), tech_pvt->call_id, SKINNY_ON_HOOK, button->shortname + ))) { + skinny_execute_sql(listener->profile, sql, listener->profile->sql_mutex); + switch_safe_free(sql); + } + + send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id); + send_set_speaker_mode(listener, SKINNY_SPEAKER_ON); + send_set_lamp(listener, SKINNY_BUTTON_LINE, *line_instance_p, SKINNY_LAMP_ON); + skinny_line_set_state(listener, *line_instance_p, tech_pvt->call_id, SKINNY_OFF_HOOK); + send_select_soft_keys(listener, *line_instance_p, tech_pvt->call_id, SKINNY_KEY_SET_OFF_HOOK, 0xffff); + send_display_prompt_status(listener, 0, "\200\000", + *line_instance_p, tech_pvt->call_id); + send_activate_call_plane(listener, *line_instance_p); + + goto done; +error: + if (nsession) { + switch_core_session_destroy(&nsession); + } + + return SWITCH_STATUS_FALSE; + +done: + *session = nsession; + return SWITCH_STATUS_SUCCESS; +} + +skinny_action_t skinny_session_dest_match_pattern(switch_core_session_t *session, char **data) +{ + skinny_action_t action = SKINNY_ACTION_DROP; + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + switch_assert(session); + + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + /* this part of the code is similar to switch_core_standard_on_routing() */ + if (!zstr(tech_pvt->profile->patterns_dialplan)) { + switch_dialplan_interface_t *dialplan_interface = NULL; + switch_caller_extension_t *extension = NULL; + char *expanded = NULL; + char *dpstr = NULL; + char *dp[25]; + int argc, x; + + if ((dpstr = switch_core_session_strdup(session, tech_pvt->profile->patterns_dialplan))) { + expanded = switch_channel_expand_variables(channel, dpstr); + argc = switch_separate_string(expanded, ',', dp, (sizeof(dp) / sizeof(dp[0]))); + for (x = 0; x < argc; x++) { + char *dpname = dp[x]; + char *dparg = NULL; + + if (dpname) { + if ((dparg = strchr(dpname, ':'))) { + *dparg++ = '\0'; + } + } else { + continue; + } + if (!(dialplan_interface = switch_loadable_module_get_dialplan_interface(dpname))) { + continue; + } + + extension = dialplan_interface->hunt_function(session, dparg, NULL); + UNPROTECT_INTERFACE(dialplan_interface); + + if (extension) { + goto found; + } + } + } +found: + while (extension && extension->current_application) { + switch_caller_application_t *current_application = extension->current_application; + + extension->current_application = extension->current_application->next; + + if (!strcmp(current_application->application_name, "skinny-route")) { + action = SKINNY_ACTION_ROUTE; + } else if (!strcmp(current_application->application_name, "skinny-drop")) { + action = SKINNY_ACTION_DROP; + } else if (!strcmp(current_application->application_name, "skinny-wait")) { + action = SKINNY_ACTION_WAIT; + *data = switch_core_session_strdup(session, current_application->application_data); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, + "Unknown skinny dialplan application %s\n", current_application->application_name); + } + } + } + return action; +} + + +struct skinny_session_process_dest_helper { + private_t *tech_pvt; + listener_t *listener; + uint32_t line_instance; +}; + +int skinny_session_process_dest_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + struct skinny_session_process_dest_helper *helper = pArg; + listener_t *listener = NULL; + + char *device_name = argv[0]; + uint32_t device_instance = atoi(argv[1]); + /* uint32_t position = atoi(argv[2]); */ + uint32_t line_instance = atoi(argv[3]); + /* char *label = argv[4]; */ + /* char *value = argv[5]; */ + /* char *caller_name = argv[6]; */ + /* uint32_t ring_on_idle = atoi(argv[7]); */ + /* uint32_t ring_on_active = atoi(argv[8]); */ + /* uint32_t busy_trigger = atoi(argv[9]); */ + /* char *forward_all = argv[10]; */ + /* char *forward_busy = argv[11]; */ + /* char *forward_noanswer = argv[12]; */ + /* uint32_t noanswer_duration = atoi(argv[13]); */ + /* char *channel_uuid = argv[14]; */ + /* uint32_t call_id = atoi(argv[15]); */ + /* uint32_t call_state = atoi(argv[16]); */ + + skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, device_name, device_instance, &listener); + if(listener) { + if(!strcmp(device_name, helper->listener->device_name) + && (device_instance == helper->listener->device_instance) + && (line_instance == helper->line_instance)) {/* the calling line */ + /* nothing */ + } else { + send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON); + skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_IN_USE_REMOTELY); + send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, 10, 0xffff); + send_display_prompt_status(listener, 0, SKINNY_DISP_IN_USE_REMOTE, + line_instance, helper->tech_pvt->call_id); + skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance); + } + } + return 0; +} + +switch_status_t skinny_session_process_dest(switch_core_session_t *session, listener_t *listener, uint32_t line_instance, char *dest, char append_dest, uint32_t backspace) +{ + skinny_action_t action; + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + char *data = NULL; + struct skinny_session_process_dest_helper helper = {0}; + + switch_assert(session); + switch_assert(listener); + switch_assert(listener->profile); + + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + if (!dest) { + if (strlen(tech_pvt->caller_profile->destination_number) == 0) {/* no digit yet */ + send_start_tone(listener, SKINNY_TONE_DIALTONE, 0, line_instance, tech_pvt->call_id); + } + if (backspace) { /* backspace */ + tech_pvt->caller_profile->destination_number[strlen(tech_pvt->caller_profile->destination_number)-1] = '\0'; + if (strlen(tech_pvt->caller_profile->destination_number) == 0) { + send_select_soft_keys(listener, line_instance, tech_pvt->call_id, SKINNY_KEY_SET_OFF_HOOK, 0xffff); + } + send_back_space_request(listener, line_instance, tech_pvt->call_id); + } + if (append_dest != '\0') {/* append digit */ + tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, + "%s%c", tech_pvt->caller_profile->destination_number, append_dest); + } + if (strlen(tech_pvt->caller_profile->destination_number) == 1) {/* first digit */ + if(!backspace) { + send_stop_tone(listener, line_instance, tech_pvt->call_id); + } + send_select_soft_keys(listener, line_instance, tech_pvt->call_id, + SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT, 0xffff); + } + } else { + tech_pvt->caller_profile->destination_number = switch_core_strdup(tech_pvt->caller_profile->pool, + dest); + } + if(dest) { + action = SKINNY_ACTION_ROUTE; + } else { + action = skinny_session_dest_match_pattern(session, &data); + } + switch(action) { + case SKINNY_ACTION_ROUTE: + tech_pvt->caller_profile->dialplan = switch_core_strdup(tech_pvt->caller_profile->pool, listener->profile->dialplan); + tech_pvt->caller_profile->context = switch_core_strdup(tech_pvt->caller_profile->pool, listener->profile->context); + send_dialed_number(listener, tech_pvt->caller_profile->destination_number, line_instance, tech_pvt->call_id); + skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_PROCEED); + skinny_session_send_call_info(session, listener, line_instance); + + skinny_session_start_media(session, listener, line_instance); + + helper.tech_pvt = tech_pvt; + helper.listener = listener; + helper.line_instance = line_instance; + skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), skinny_session_process_dest_callback, &helper); + break; + case SKINNY_ACTION_WAIT: + /* for now, wait forever */ + break; + case SKINNY_ACTION_DROP: + default: + switch_channel_hangup(channel, SWITCH_CAUSE_UNALLOCATED_NUMBER); + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_session_send_call_info(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) +{ + private_t *tech_pvt; + switch_channel_t *channel; + + const char *caller_party_name; + const char *caller_party_number; + const char *called_party_name; + const char *called_party_number; + uint32_t call_type = 0; + + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + switch_assert(tech_pvt->caller_profile != NULL); + + /* Calling party */ + if (zstr((caller_party_name = switch_channel_get_variable(channel, "effective_caller_id_name"))) && + zstr((caller_party_name = switch_channel_get_variable(channel, "caller_id_name")))) { + caller_party_name = SWITCH_DEFAULT_CLID_NAME; + } + if (zstr((caller_party_number = switch_channel_get_variable(channel, "effective_caller_id_number"))) && + zstr((caller_party_number = switch_channel_get_variable(channel, "caller_id_number")))) { + caller_party_number = "0000000000"; + } + /* Called party */ + if (zstr((called_party_name = switch_channel_get_variable(channel, "effective_called_id_name"))) && + zstr((called_party_name = switch_channel_get_variable(channel, "called_id_name")))) { + called_party_name = SWITCH_DEFAULT_CLID_NAME; + } + if (zstr((called_party_number = switch_channel_get_variable(channel, "effective_called_id_number"))) && + zstr((called_party_number = switch_channel_get_variable(channel, "called_id_number")))) { + called_party_number = "0000000000"; + } + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + call_type = SKINNY_INBOUND_CALL; + } else { + call_type = SKINNY_OUTBOUND_CALL; + } + skinny_send_call_info(listener, + caller_party_name, /* char calling_party_name[40], */ + caller_party_number, /* char calling_party[24], */ + called_party_name, /* char called_party_name[40], */ + called_party_number, /* char called_party[24], */ + line_instance, /* uint32_t line_instance, */ + tech_pvt->call_id, /* uint32_t call_id, */ + call_type, /* uint32_t call_type, */ + "", /* TODO char original_called_party_name[40], */ + "", /* TODO char original_called_party[24], */ + "", /* TODO char last_redirecting_party_name[40], */ + "", /* TODO char last_redirecting_party[24], */ + 0, /* TODO uint32_t original_called_party_redirect_reason, */ + 0, /* TODO uint32_t last_redirecting_reason, */ + "", /* TODO char calling_party_voice_mailbox[24], */ + "", /* TODO char called_party_voice_mailbox[24], */ + "", /* TODO char original_called_party_voice_mailbox[24], */ + "", /* TODO char last_redirecting_voice_mailbox[24], */ + 1, /* uint32_t call_instance, */ + 1, /* uint32_t call_security_status, */ + 0 /* uint32_t party_pi_restriction_bits */ + ); + return SWITCH_STATUS_SUCCESS; +} + +struct skinny_ring_lines_helper { + private_t *tech_pvt; + uint32_t lines_count; +}; + +int skinny_ring_lines_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + struct skinny_ring_lines_helper *helper = pArg; + char *tmp; + + char *device_name = argv[0]; + uint32_t device_instance = atoi(argv[1]); + /* uint32_t position = atoi(argv[2]); */ + uint32_t line_instance = atoi(argv[3]); + /* char *label = argv[4]; */ + /* char *value = argv[5]; */ + /* char *caller_name = argv[6]; */ + /* uint32_t ring_on_idle = atoi(argv[7]); */ + /* uint32_t ring_on_active = atoi(argv[8]); */ + /* uint32_t busy_trigger = atoi(argv[9]); */ + /* char *forward_all = argv[10]; */ + /* char *forward_busy = argv[11]; */ + /* char *forward_noanswer = argv[12]; */ + /* uint32_t noanswer_duration = atoi(argv[13]); */ + /* char *channel_uuid = argv[14]; */ + /* uint32_t call_id = atoi(argv[15]); */ + /* uint32_t call_state = atoi(argv[16]); */ + + listener_t *listener = NULL; + + skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, + device_name, device_instance, &listener); + if(listener) { + helper->lines_count++; + skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_RING_IN); + send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, SKINNY_KEY_SET_RING_IN, 0xffff); + if ((tmp = switch_mprintf("%s%s", SKINNY_DISP_FROM, helper->tech_pvt->caller_profile->destination_number))) { + send_display_prompt_status(listener, 0, tmp, line_instance, helper->tech_pvt->call_id); + switch_safe_free(tmp); + } + if ((tmp = switch_mprintf("\005\000\000\000%s", helper->tech_pvt->caller_profile->destination_number))) { + send_display_pri_notify(listener, 10 /* message_timeout */, 5 /* priority */, tmp); + switch_safe_free(tmp); + } + skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance); + send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_BLINK); + send_set_ringer(listener, SKINNY_RING_INSIDE, SKINNY_RING_FOREVER, 0, helper->tech_pvt->call_id); + } + return 0; +} + +switch_call_cause_t skinny_ring_lines(private_t *tech_pvt) +{ + switch_status_t status; + struct skinny_ring_lines_helper helper = {0}; + + switch_assert(tech_pvt); + switch_assert(tech_pvt->profile); + switch_assert(tech_pvt->session); + + helper.tech_pvt = tech_pvt; + helper.lines_count = 0; + + status = skinny_session_walk_lines(tech_pvt->profile, + switch_core_session_get_uuid(tech_pvt->session), skinny_ring_lines_callback, &helper); + if(status != SWITCH_STATUS_SUCCESS) { + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } else if(helper.lines_count == 0) { + return SWITCH_CAUSE_UNALLOCATED_NUMBER; + } else { + return SWITCH_CAUSE_SUCCESS; + } +} + +switch_status_t skinny_session_ring_out(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + switch_assert(session); + switch_assert(listener); + switch_assert(listener->profile); + + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_RING_OUT); + send_select_soft_keys(listener, line_instance, tech_pvt->call_id, + SKINNY_KEY_SET_RING_OUT, 0xffff); + send_display_prompt_status(listener, 0, SKINNY_DISP_RING_OUT, + line_instance, tech_pvt->call_id); + skinny_session_send_call_info(session, listener, line_instance); + + return SWITCH_STATUS_SUCCESS; +} + + +struct skinny_session_answer_helper { + private_t *tech_pvt; + listener_t *listener; + uint32_t line_instance; +}; + +int skinny_session_answer_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + struct skinny_session_answer_helper *helper = pArg; + listener_t *listener = NULL; + + char *device_name = argv[0]; + uint32_t device_instance = atoi(argv[1]); + /* uint32_t position = atoi(argv[2]); */ + uint32_t line_instance = atoi(argv[3]); + /* char *label = argv[4]; */ + /* char *value = argv[5]; */ + /* char *caller_name = argv[6]; */ + /* uint32_t ring_on_idle = atoi(argv[7]); */ + /* uint32_t ring_on_active = atoi(argv[8]); */ + /* uint32_t busy_trigger = atoi(argv[9]); */ + /* char *forward_all = argv[10]; */ + /* char *forward_busy = argv[11]; */ + /* char *forward_noanswer = argv[12]; */ + /* uint32_t noanswer_duration = atoi(argv[13]); */ + /* char *channel_uuid = argv[14]; */ + /* uint32_t call_id = atoi(argv[15]); */ + /* uint32_t call_state = atoi(argv[16]); */ + + skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, device_name, device_instance, &listener); + if(listener) { + if(!strcmp(device_name, helper->listener->device_name) + && (device_instance == helper->listener->device_instance) + && (line_instance == helper->line_instance)) {/* the answering line */ + /* nothing */ + } else { + send_define_current_time_date(listener); + send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON); + skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_IN_USE_REMOTELY); + send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, 10, 0x0002); + send_display_prompt_status(listener, 0, SKINNY_DISP_IN_USE_REMOTE, line_instance, helper->tech_pvt->call_id); + send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, helper->tech_pvt->call_id); + } + } + return 0; +} + +switch_status_t skinny_session_answer(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) +{ + struct skinny_session_answer_helper helper = {0}; + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + switch_assert(session); + switch_assert(listener); + switch_assert(listener->profile); + + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id); + send_set_speaker_mode(listener, SKINNY_SPEAKER_ON); + send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON); + skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_OFF_HOOK); + send_activate_call_plane(listener, line_instance); + + helper.tech_pvt = tech_pvt; + helper.listener = listener; + helper.line_instance = line_instance; + + skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), skinny_session_answer_callback, &helper); + + skinny_session_start_media(session, listener, line_instance); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_session_start_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + switch_assert(session); + switch_assert(listener); + switch_assert(listener->profile); + + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + send_stop_tone(listener, line_instance, tech_pvt->call_id); + send_open_receive_channel(listener, + tech_pvt->call_id, /* uint32_t conference_id, */ + tech_pvt->call_id, /* uint32_t pass_thru_party_id, */ + 20, /* uint32_t packets, */ + SKINNY_CODEC_ULAW_64K, /* uint32_t payload_capacity, */ + 0, /* uint32_t echo_cancel_type, */ + 0, /* uint32_t g723_bitrate, */ + 0, /* uint32_t conference_id2, */ + 0 /* uint32_t reserved[10] */ + ); + skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_CONNECTED); + send_select_soft_keys(listener, line_instance, tech_pvt->call_id, + SKINNY_KEY_SET_CONNECTED, 0xffff); + send_display_prompt_status(listener, + 0, + SKINNY_DISP_CONNECTED, + line_instance, + tech_pvt->call_id); + skinny_session_send_call_info(session, listener, line_instance); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_session_hold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + switch_assert(session); + switch_assert(listener); + switch_assert(listener->profile); + + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + skinny_session_stop_media(session, listener, line_instance); + switch_ivr_hold(session, NULL, 1); + + send_define_current_time_date(listener); + send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_WINK); + skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_HOLD); + send_select_soft_keys(listener, line_instance, tech_pvt->call_id, SKINNY_KEY_SET_ON_HOLD, 0xffff); + send_display_prompt_status(listener, 0, SKINNY_DISP_HOLD, + line_instance, tech_pvt->call_id); + skinny_session_send_call_info(tech_pvt->session, listener, line_instance); + send_set_speaker_mode(listener, SKINNY_SPEAKER_OFF); + send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_session_unhold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + switch_assert(session); + switch_assert(listener); + switch_assert(listener->profile); + + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + skinny_hold_active_calls(listener); + send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id); + send_set_speaker_mode(listener, SKINNY_SPEAKER_ON); + send_select_soft_keys(listener, line_instance, tech_pvt->call_id, SKINNY_KEY_SET_RING_OUT, 0xffff); + skinny_session_start_media(session, listener, line_instance); + switch_ivr_unhold(session); + send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_session_transfer(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + private_t *tech_pvt = NULL; + switch_channel_t *channel = NULL; + const char *remote_uuid = NULL; + switch_core_session_t *session2 = NULL; + private_t *tech_pvt2 = NULL; + + switch_assert(session); + switch_assert(listener); + switch_assert(listener->profile); + + tech_pvt = switch_core_session_get_private(session); + channel = switch_core_session_get_channel(session); + remote_uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); + + if (tech_pvt->transfer_from_call_id) { + if((session2 = skinny_profile_find_session(listener->profile, listener, &line_instance, tech_pvt->transfer_from_call_id))) { + switch_channel_t *channel2 = switch_core_session_get_channel(session2); + const char *remote_uuid2 = switch_channel_get_variable(channel2, SWITCH_SIGNAL_BOND_VARIABLE); + if (switch_ivr_uuid_bridge(remote_uuid, remote_uuid2) == SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel2, SWITCH_CAUSE_NORMAL_CLEARING); + } else { + /* TODO: How to inform the user that the bridge is not possible? */ + } + switch_core_session_rwunlock(session2); + } + } else { + if(remote_uuid) { + /* TODO CallSelectStat */ + status = skinny_create_incoming_session(listener, &line_instance, &session2); + tech_pvt2 = switch_core_session_get_private(session2); + tech_pvt2->transfer_from_call_id = tech_pvt->call_id; + tech_pvt->transfer_to_call_id = tech_pvt2->call_id; + skinny_session_process_dest(session2, listener, line_instance, NULL, '\0', 0); + switch_core_session_rwunlock(session2); + } else { + /* TODO: How to inform the user that the bridge is not possible? */ + } + } + return status; +} + +switch_status_t skinny_session_stop_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + switch_assert(session); + switch_assert(listener); + switch_assert(listener->profile); + + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + send_close_receive_channel(listener, + tech_pvt->call_id, /* uint32_t conference_id, */ + tech_pvt->party_id, /* uint32_t pass_thru_party_id, */ + tech_pvt->call_id /* uint32_t conference_id2, */ + ); + send_stop_media_transmission(listener, + tech_pvt->call_id, /* uint32_t conference_id, */ + tech_pvt->party_id, /* uint32_t pass_thru_party_id, */ + tech_pvt->call_id /* uint32_t conference_id2, */ + ); + + return SWITCH_STATUS_SUCCESS; +} + +struct skinny_hold_active_calls_helper { + listener_t *listener; +}; + +int skinny_hold_active_calls_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + struct skinny_hold_active_calls_helper *helper = pArg; + switch_core_session_t *session; + + /* char *device_name = argv[0]; */ + /* uint32_t device_instance = atoi(argv[1]); */ + /* uint32_t position = atoi(argv[2]); */ + uint32_t line_instance = atoi(argv[3]); + /* char *label = argv[4]; */ + /* char *value = argv[5]; */ + /* char *caller_name = argv[6]; */ + /* uint32_t ring_on_idle = atoi(argv[7]); */ + /* uint32_t ring_on_active = atoi(argv[8]); */ + /* uint32_t busy_trigger = atoi(argv[9]); */ + /* char *forward_all = argv[10]; */ + /* char *forward_busy = argv[11]; */ + /* char *forward_noanswer = argv[12]; */ + /* uint32_t noanswer_duration = atoi(argv[13]); */ + /* char *channel_uuid = argv[14]; */ + uint32_t call_id = atoi(argv[15]); + /* uint32_t call_state = atoi(argv[16]); */ + + session = skinny_profile_find_session(helper->listener->profile, helper->listener, &line_instance, call_id); + + if(session) { + skinny_session_hold_line(session, helper->listener, line_instance); + switch_core_session_rwunlock(session); + } + + return 0; +} + +switch_status_t skinny_hold_active_calls(listener_t *listener) +{ + struct skinny_hold_active_calls_helper helper = {0}; + char *sql; + + helper.listener = listener; + + if ((sql = switch_mprintf( + "SELECT skinny_lines.*, channel_uuid, call_id, call_state " + "FROM skinny_active_lines " + "INNER JOIN skinny_lines " + "ON skinny_active_lines.device_name = skinny_lines.device_name " + "AND skinny_active_lines.device_instance = skinny_lines.device_instance " + "AND skinny_active_lines.line_instance = skinny_lines.line_instance " + "WHERE skinny_lines.device_name='%s' AND skinny_lines.device_instance=%d AND call_state=%d", + listener->device_name, listener->device_instance, SKINNY_CONNECTED))) { + skinny_execute_sql_callback(listener->profile, listener->profile->sql_mutex, sql, skinny_hold_active_calls_callback, &helper); + switch_safe_free(sql); + } + + return SWITCH_STATUS_SUCCESS; +} + +/*****************************************************************************/ +/* SKINNY MESSAGE HANDLERS */ +/*****************************************************************************/ +switch_status_t skinny_handle_keep_alive_message(listener_t *listener, skinny_message_t *request) +{ + skinny_message_t *message; + + message = switch_core_alloc(listener->pool, 12); + message->type = KEEP_ALIVE_ACK_MESSAGE; + message->length = 4; + keepalive_listener(listener, NULL); + skinny_send_reply(listener, message); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *request) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + skinny_profile_t *profile; + switch_event_t *event = NULL; + switch_event_t *params = NULL; + switch_xml_t xroot, xdomain, xgroup, xuser, xskinny, xparams, xparam, xbuttons, xbutton; + listener_t *listener2 = NULL; + char *sql; + assert(listener->profile); + profile = listener->profile; + + skinny_check_data_length(request, sizeof(request->data.reg)); + + if (!zstr(listener->device_name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "A device is already registred on this listener.\n"); + send_register_reject(listener, "A device is already registred on this listener"); + return SWITCH_STATUS_FALSE; + } + + /* Check directory */ + skinny_device_event(listener, ¶ms, SWITCH_EVENT_REQUEST_PARAMS, SWITCH_EVENT_SUBCLASS_ANY); + switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "action", "skinny-auth"); + + if (switch_xml_locate_user("id", request->data.reg.device_name, profile->domain, "", &xroot, &xdomain, &xuser, &xgroup, params) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Can't find device [%s@%s]\n" + "You must define a domain called '%s' in your directory and add a user with id=\"%s\".\n" + , request->data.reg.device_name, profile->domain, profile->domain, request->data.reg.device_name); + send_register_reject(listener, "Device not found"); + status = SWITCH_STATUS_FALSE; + goto end; + } + + skinny_profile_find_listener_by_device_name_and_instance(listener->profile, + request->data.reg.device_name, request->data.reg.instance, &listener2); + if (listener2) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Device %s:%d is already registred on another listener.\n", + request->data.reg.device_name, request->data.reg.instance); + send_register_reject(listener, "Device is already registred on another listener"); + status = SWITCH_STATUS_FALSE; + goto end; + } + + if ((sql = switch_mprintf( + "INSERT INTO skinny_devices " + "(name, user_id, instance, ip, type, max_streams, codec_string) " + "VALUES ('%s','%d','%d', '%s', '%d', '%d', '%s')", + request->data.reg.device_name, + request->data.reg.user_id, + request->data.reg.instance, + inet_ntoa(request->data.reg.ip), + request->data.reg.device_type, + request->data.reg.max_streams, + "" /* codec_string */ + ))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + + + strncpy(listener->device_name, request->data.reg.device_name, 16); + listener->device_instance = request->data.reg.instance; + listener->device_type = request->data.reg.device_type; + + xskinny = switch_xml_child(xuser, "skinny"); + if (xskinny) { + if ((xparams = switch_xml_child(xskinny, "params"))) { + for (xparam = switch_xml_child(xparams, "param"); xparam; xparam = xparam->next) { + const char *name = switch_xml_attr_soft(xparam, "name"); + const char *value = switch_xml_attr_soft(xparam, "value"); + if (!strcasecmp(name, "skinny-firmware-version")) { + strncpy(listener->firmware_version, value, 16); + } + } + } + if ((xbuttons = switch_xml_child(xskinny, "buttons"))) { + uint32_t line_instance = 1; + for (xbutton = switch_xml_child(xbuttons, "button"); xbutton; xbutton = xbutton->next) { + uint32_t position = atoi(switch_xml_attr_soft(xbutton, "position")); + uint32_t type = skinny_str2button(switch_xml_attr_soft(xbutton, "type")); + const char *label = switch_xml_attr_soft(xbutton, "label"); + const char *value = switch_xml_attr_soft(xbutton, "value"); + if(type == SKINNY_BUTTON_LINE) { + const char *caller_name = switch_xml_attr_soft(xbutton, "caller-name"); + uint32_t ring_on_idle = atoi(switch_xml_attr_soft(xbutton, "ring-on-idle")); + uint32_t ring_on_active = atoi(switch_xml_attr_soft(xbutton, "ring-on-active")); + uint32_t busy_trigger = atoi(switch_xml_attr_soft(xbutton, "busy-trigger")); + const char *forward_all = switch_xml_attr_soft(xbutton, "forward-all"); + const char *forward_busy = switch_xml_attr_soft(xbutton, "forward-busy"); + const char *forward_noanswer = switch_xml_attr_soft(xbutton, "forward-noanswer"); + uint32_t noanswer_duration = atoi(switch_xml_attr_soft(xbutton, "noanswer-duration")); + if ((sql = switch_mprintf( + "INSERT INTO skinny_lines " + "(device_name, device_instance, position, line_instance, " + "label, value, caller_name, " + "ring_on_idle, ring_on_active, busy_trigger, " + "forward_all, forward_busy, forward_noanswer, noanswer_duration) " + "VALUES('%s', %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', %d)", + request->data.reg.device_name, request->data.reg.instance, position, line_instance++, + label, value, caller_name, + ring_on_idle, ring_on_active, busy_trigger, + forward_all, forward_busy, forward_noanswer, noanswer_duration))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + } else { + const char *settings = switch_xml_attr_soft(xbutton, "settings"); + if ((sql = switch_mprintf( + "INSERT INTO skinny_buttons " + "(device_name, device_instance, position, type, label, value, settings) " + "VALUES('%s', %d, %d, %d, '%s', '%s', '%s')", + request->data.reg.device_name, + request->data.reg.instance, + position, + type, + label, + value, + settings))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + } + } + } + } + if (xroot) { + switch_xml_free(xroot); + } + + status = SWITCH_STATUS_SUCCESS; + + /* Reply with RegisterAckMessage */ + send_register_ack(listener, profile->keep_alive, profile->date_format, "", profile->keep_alive, ""); + + /* Send CapabilitiesReqMessage */ + send_capabilities_req(listener); + + /* skinny::register event */ + skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_REGISTER); + switch_event_fire(&event); + + keepalive_listener(listener, NULL); + +end: + if(params) { + switch_event_destroy(¶ms); + } + + return status; +} + +switch_status_t skinny_handle_port_message(listener_t *listener, skinny_message_t *request) +{ + char *sql; + skinny_profile_t *profile; + + switch_assert(listener->profile); + switch_assert(listener->device_name); + + profile = listener->profile; + + skinny_check_data_length(request, sizeof(request->data.as_uint16)); + + if ((sql = switch_mprintf( + "UPDATE skinny_devices SET port=%d WHERE name='%s' and instance=%d", + request->data.port.port, + listener->device_name, + listener->device_instance + ))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_keypad_button_message(listener_t *listener, skinny_message_t *request) +{ + uint32_t line_instance = 0; + switch_core_session_t *session; + + skinny_check_data_length(request, sizeof(request->data.keypad_button)); + + if(request->data.keypad_button.line_instance) { + line_instance = request->data.keypad_button.line_instance; + } else { + line_instance = 1; + } + + session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.keypad_button.call_id); + + if(session) { + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + char digit = '\0'; + + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "SEND DTMF ON CALL %d [%d]\n", tech_pvt->call_id, request->data.keypad_button.button); + + if (request->data.keypad_button.button == 14) { + digit = '*'; + } else if (request->data.keypad_button.button == 15) { + digit = '#'; + } else if (request->data.keypad_button.button >= 0 && request->data.keypad_button.button <= 9) { + digit = '0' + request->data.keypad_button.button; + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "UNKNOW DTMF RECEIVED ON CALL %d [%d]\n", tech_pvt->call_id, request->data.keypad_button.button); + } + + /* TODO check call_id and line */ + + if((skinny_line_get_state(listener, line_instance, tech_pvt->call_id) == SKINNY_OFF_HOOK)) { + + skinny_session_process_dest(session, listener, line_instance, NULL, digit, 0); + } else { + if(digit != '\0') { + switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0)}; + dtmf.digit = digit; + switch_channel_queue_dtmf(channel, &dtmf); + } + } + } + + if(session) { + switch_core_session_rwunlock(session); + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_stimulus_message(listener_t *listener, skinny_message_t *request) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + struct speed_dial_stat_res_message *button = NULL; + uint32_t line_instance = 0; + uint32_t call_id = 0; + switch_core_session_t *session = NULL; + + skinny_check_data_length(request, sizeof(request->data.stimulus)-sizeof(request->data.stimulus.call_id)); + + if(skinny_check_data_length_soft(request, sizeof(request->data.stimulus))) { + call_id = request->data.stimulus.call_id; + } + + switch(request->data.stimulus.instance_type) { + case SKINNY_BUTTON_LAST_NUMBER_REDIAL: + skinny_create_incoming_session(listener, &line_instance, &session); + skinny_session_process_dest(session, listener, line_instance, "redial", '\0', 0); + break; + case SKINNY_BUTTON_SPEED_DIAL: + skinny_speed_dial_get(listener, request->data.stimulus.instance, &button); + if(strlen(button->line) > 0) { + skinny_create_incoming_session(listener, &line_instance, &session); + skinny_session_process_dest(session, listener, line_instance, button->line, '\0', 0); + } + break; + case SKINNY_BUTTON_HOLD: + session = skinny_profile_find_session(listener->profile, listener, &line_instance, call_id); + + if(session) { + status = skinny_session_hold_line(session, listener, line_instance); + } + break; + case SKINNY_BUTTON_VOICEMAIL: + skinny_create_incoming_session(listener, &line_instance, &session); + skinny_session_process_dest(session, listener, line_instance, "vmain", '\0', 0); + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown Stimulus Type Received [%d]\n", request->data.stimulus.instance_type); + } + + if(session) { + switch_core_session_rwunlock(session); + } + + return status; +} + +switch_status_t skinny_handle_off_hook_message(listener_t *listener, skinny_message_t *request) +{ + uint32_t line_instance; + switch_core_session_t *session = NULL; + private_t *tech_pvt = NULL; + + skinny_check_data_length(request, sizeof(request->data.off_hook)); + + if(request->data.off_hook.line_instance > 0) { + line_instance = request->data.off_hook.line_instance; + } else { + line_instance = 1; + } + + session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.off_hook.call_id); + + if(session) { /*answering a call */ + skinny_session_answer(session, listener, line_instance); + } else { /* start a new call */ + skinny_create_incoming_session(listener, &line_instance, &session); + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 0); + } + + if(session) { + switch_core_session_rwunlock(session); + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_on_hook_message(listener_t *listener, skinny_message_t *request) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_core_session_t *session = NULL; + uint32_t line_instance = 0; + + skinny_check_data_length(request, sizeof(request->data.on_hook)); + + line_instance = request->data.on_hook.line_instance; + + session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.on_hook.call_id); + + if(session) { + switch_channel_t *channel = NULL; + + channel = switch_core_session_get_channel(session); + + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + } + + if(session) { + switch_core_session_rwunlock(session); + } + + return status; +} + +switch_status_t skinny_handle_speed_dial_stat_request(listener_t *listener, skinny_message_t *request) +{ + skinny_message_t *message; + struct speed_dial_stat_res_message *button = NULL; + + skinny_check_data_length(request, sizeof(request->data.speed_dial_req)); + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.speed_dial_res)); + message->type = SPEED_DIAL_STAT_RES_MESSAGE; + message->length = 4 + sizeof(message->data.speed_dial_res); + + skinny_speed_dial_get(listener, request->data.speed_dial_req.number, &button); + + memcpy(&message->data.speed_dial_res, button, sizeof(struct speed_dial_stat_res_message)); + + skinny_send_reply(listener, message); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_line_stat_request(listener_t *listener, skinny_message_t *request) +{ + skinny_message_t *message; + struct line_stat_res_message *button = NULL; + + skinny_check_data_length(request, sizeof(request->data.line_req)); + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.line_res)); + message->type = LINE_STAT_RES_MESSAGE; + message->length = 4 + sizeof(message->data.line_res); + + skinny_line_get(listener, request->data.line_req.number, &button); + + memcpy(&message->data.line_res, button, sizeof(struct line_stat_res_message)); + + skinny_send_reply(listener, message); + + return SWITCH_STATUS_SUCCESS; +} + +int skinny_config_stat_res_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + skinny_message_t *message = pArg; + char *device_name = argv[0]; + int user_id = atoi(argv[1]); + int instance = atoi(argv[2]); + char *user_name = argv[3]; + char *server_name = argv[4]; + int number_lines = atoi(argv[5]); + int number_speed_dials = atoi(argv[6]); + + strncpy(message->data.config_res.device_name, device_name, 16); + message->data.config_res.user_id = user_id; + message->data.config_res.instance = instance; + strncpy(message->data.config_res.user_name, user_name, 40); + strncpy(message->data.config_res.server_name, server_name, 40); + message->data.config_res.number_lines = number_lines; + message->data.config_res.number_speed_dials = number_speed_dials; + + return 0; +} + +switch_status_t skinny_handle_config_stat_request(listener_t *listener, skinny_message_t *request) +{ + char *sql; + skinny_message_t *message; + skinny_profile_t *profile; + + switch_assert(listener->profile); + switch_assert(listener->device_name); + + profile = listener->profile; + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.config_res)); + message->type = CONFIG_STAT_RES_MESSAGE; + message->length = 4 + sizeof(message->data.config_res); + + if ((sql = switch_mprintf( + "SELECT name, user_id, instance, '' AS user_name, '' AS server_name, " + "(SELECT COUNT(*) FROM skinny_lines WHERE device_name='%s' AND device_instance=%d) AS number_lines, " + "(SELECT COUNT(*) FROM skinny_buttons WHERE device_name='%s' AND device_instance=%d AND type=%d) AS number_speed_dials " + "FROM skinny_devices WHERE name='%s' ", + listener->device_name, + listener->device_instance, + listener->device_name, + listener->device_instance, + SKINNY_BUTTON_SPEED_DIAL, + listener->device_name + ))) { + skinny_execute_sql_callback(profile, profile->sql_mutex, sql, skinny_config_stat_res_callback, message); + switch_safe_free(sql); + } + skinny_send_reply(listener, message); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_time_date_request(listener_t *listener, skinny_message_t *request) +{ + return send_define_current_time_date(listener); +} + +struct button_template_helper { + skinny_message_t *message; + int count[SKINNY_BUTTON_UNDEFINED+1]; + int max_position; +}; + +int skinny_handle_button_template_request_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + struct button_template_helper *helper = pArg; + skinny_message_t *message = helper->message; + /* char *device_name = argv[0]; */ + /* uint32_t device_instance = argv[1]; */ + int position = atoi(argv[2]); + uint32_t type = atoi(argv[3]); + /* int relative_position = atoi(argv[4]); */ + + message->data.button_template.btn[position-1].instance_number = ++helper->count[type]; + message->data.button_template.btn[position-1].button_definition = type; + + message->data.button_template.button_count++; + message->data.button_template.total_button_count++; + if(position > helper->max_position) { + helper->max_position = position; + } + + return 0; +} + +switch_status_t skinny_handle_button_template_request(listener_t *listener, skinny_message_t *request) +{ + skinny_message_t *message; + struct button_template_helper helper = {0}; + skinny_profile_t *profile; + char *sql; + + switch_assert(listener->profile); + switch_assert(listener->device_name); + + profile = listener->profile; + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.button_template)); + message->type = BUTTON_TEMPLATE_RES_MESSAGE; + message->length = 4 + sizeof(message->data.button_template); + + message->data.button_template.button_offset = 0; + message->data.button_template.button_count = 0; + message->data.button_template.total_button_count = 0; + + helper.message = message; + + /* Add buttons */ + if ((sql = switch_mprintf( + "SELECT device_name, device_instance, position, MIN(type, %d) AS type " + "FROM skinny_buttons " + "WHERE device_name='%s' AND device_instance=%d " + "ORDER BY position", + SKINNY_BUTTON_UNDEFINED, + listener->device_name, listener->device_instance + ))) { + skinny_execute_sql_callback(profile, profile->sql_mutex, sql, skinny_handle_button_template_request_callback, &helper); + switch_safe_free(sql); + } + + /* Add lines */ + if ((sql = switch_mprintf( + "SELECT device_name, device_instance, position, %d AS type " + "FROM skinny_lines " + "WHERE device_name='%s' AND device_instance=%d " + "ORDER BY position", + SKINNY_BUTTON_LINE, + listener->device_name, listener->device_instance + ))) { + skinny_execute_sql_callback(profile, profile->sql_mutex, sql, skinny_handle_button_template_request_callback, &helper); + switch_safe_free(sql); + } + + /* Fill remaining buttons with Undefined */ + for(int i = 0; i+1 < helper.max_position; i++) { + if(message->data.button_template.btn[i].button_definition == SKINNY_BUTTON_UNKNOWN) { + message->data.button_template.btn[i].instance_number = ++helper.count[SKINNY_BUTTON_UNDEFINED]; + message->data.button_template.btn[i].button_definition = SKINNY_BUTTON_UNDEFINED; + message->data.button_template.button_count++; + message->data.button_template.total_button_count++; + } + } + + + + return skinny_send_reply(listener, message);; +} + +switch_status_t skinny_handle_version_request(listener_t *listener, skinny_message_t *request) +{ + if (zstr(listener->firmware_version)) { + char *id_str; + skinny_device_type_params_t *params; + id_str = switch_mprintf("%d", listener->device_type); + params = (skinny_device_type_params_t *) switch_core_hash_find(listener->profile->device_type_params_hash, id_str); + if (params) { + if (!zstr(params->firmware_version)) { + strncpy(listener->firmware_version, params->firmware_version, 16); + } + } + } + + if (!zstr(listener->firmware_version)) { + return send_version(listener, listener->firmware_version); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Device %s:%d is requesting for firmware version, but none is set.\n", + listener->device_name, listener->device_instance); + return SWITCH_STATUS_SUCCESS; + } +} + +switch_status_t skinny_handle_capabilities_response(listener_t *listener, skinny_message_t *request) +{ + char *sql; + skinny_profile_t *profile; + + uint32_t i = 0; + uint32_t n = 0; + char *codec_order[SWITCH_MAX_CODECS]; + char *codec_string; + + size_t string_len, string_pos, pos; + + switch_assert(listener->profile); + switch_assert(listener->device_name); + + profile = listener->profile; + + skinny_check_data_length(request, sizeof(request->data.cap_res.count)); + + n = request->data.cap_res.count; + if (n > SWITCH_MAX_CODECS) { + n = SWITCH_MAX_CODECS; + } + string_len = -1; + + skinny_check_data_length(request, sizeof(request->data.cap_res.count) + n * sizeof(request->data.cap_res.caps[0])); + + for (i = 0; i < n; i++) { + char *codec = skinny_codec2string(request->data.cap_res.caps[i].codec); + codec_order[i] = codec; + string_len += strlen(codec)+1; + } + i = 0; + pos = 0; + codec_string = switch_core_alloc(listener->pool, string_len+1); + for (string_pos = 0; string_pos < string_len; string_pos++) { + char *codec = codec_order[i]; + switch_assert(i < n); + if(pos == strlen(codec)) { + codec_string[string_pos] = ','; + i++; + pos = 0; + } else { + codec_string[string_pos] = codec[pos++]; + } + } + codec_string[string_len] = '\0'; + if ((sql = switch_mprintf( + "UPDATE skinny_devices SET codec_string='%s' WHERE name='%s'", + codec_string, + listener->device_name + ))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Codecs %s supported.\n", codec_string); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_alarm(listener_t *listener, skinny_message_t *request) +{ + switch_event_t *event = NULL; + + skinny_check_data_length(request, sizeof(request->data.alarm)); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, + "Received alarm: Severity=%d, DisplayMessage=%s, Param1=%d, Param2=%d.\n", + request->data.alarm.alarm_severity, request->data.alarm.display_message, + request->data.alarm.alarm_param1, request->data.alarm.alarm_param2); + /* skinny::alarm event */ + skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_ALARM); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Alarm-Severity", "%d", request->data.alarm.alarm_severity); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Alarm-DisplayMessage", "%s", request->data.alarm.display_message); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Alarm-Param1", "%d", request->data.alarm.alarm_param1); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Alarm-Param2", "%d", request->data.alarm.alarm_param2); + switch_event_fire(&event); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_open_receive_channel_ack_message(listener_t *listener, skinny_message_t *request) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + uint32_t line_instance = 0; + switch_core_session_t *session; + + skinny_check_data_length(request, sizeof(request->data.open_receive_channel_ack)); + + session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.open_receive_channel_ack.pass_thru_party_id); + + if(session) { + const char *err = NULL; + private_t *tech_pvt = NULL; + switch_channel_t *channel = NULL; + struct in_addr addr; + + tech_pvt = switch_core_session_get_private(session); + channel = switch_core_session_get_channel(session); + + /* Codec */ + tech_pvt->iananame = "PCMU"; /* TODO */ + tech_pvt->codec_ms = 10; /* TODO */ + tech_pvt->rm_rate = 8000; /* TODO */ + tech_pvt->rm_fmtp = NULL; /* TODO */ + tech_pvt->agreed_pt = (switch_payload_t) 0; /* TODO */ + tech_pvt->rm_encoding = switch_core_strdup(switch_core_session_get_pool(session), ""); + skinny_tech_set_codec(tech_pvt, 0); + if ((status = skinny_tech_set_codec(tech_pvt, 0)) != SWITCH_STATUS_SUCCESS) { + goto end; + } + + /* Request a local port from the core's allocator */ + if (!(tech_pvt->local_sdp_audio_port = switch_rtp_request_port(listener->profile->ip))) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_CRIT, "No RTP ports available!\n"); + return SWITCH_STATUS_FALSE; + } + tech_pvt->local_sdp_audio_ip = switch_core_strdup(switch_core_session_get_pool(session), listener->profile->ip); + + tech_pvt->remote_sdp_audio_ip = inet_ntoa(request->data.open_receive_channel_ack.ip); + tech_pvt->remote_sdp_audio_port = request->data.open_receive_channel_ack.port; + + tech_pvt->rtp_session = switch_rtp_new(tech_pvt->local_sdp_audio_ip, + tech_pvt->local_sdp_audio_port, + tech_pvt->remote_sdp_audio_ip, + tech_pvt->remote_sdp_audio_port, + tech_pvt->agreed_pt, + tech_pvt->read_impl.samples_per_packet, + tech_pvt->codec_ms * 1000, + (switch_rtp_flag_t) 0, "soft", &err, + switch_core_session_get_pool(session)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, + "AUDIO RTP [%s] %s:%d->%s:%d codec: %u ms: %d [%s]\n", + switch_channel_get_name(channel), + tech_pvt->local_sdp_audio_ip, + tech_pvt->local_sdp_audio_port, + tech_pvt->remote_sdp_audio_ip, + tech_pvt->remote_sdp_audio_port, + tech_pvt->agreed_pt, + tech_pvt->read_impl.microseconds_per_packet / 1000, + switch_rtp_ready(tech_pvt->rtp_session) ? "SUCCESS" : err); + inet_aton(tech_pvt->local_sdp_audio_ip, &addr); + send_start_media_transmission(listener, + tech_pvt->call_id, /* uint32_t conference_id, */ + tech_pvt->party_id, /* uint32_t pass_thru_party_id, */ + addr.s_addr, /* uint32_t remote_ip, */ + tech_pvt->local_sdp_audio_port, /* uint32_t remote_port, */ + 20, /* uint32_t ms_per_packet, */ + SKINNY_CODEC_ULAW_64K, /* uint32_t payload_capacity, */ + 184, /* uint32_t precedence, */ + 0, /* uint32_t silence_suppression, */ + 0, /* uint16_t max_frames_per_packet, */ + 0 /* uint32_t g723_bitrate */ + ); + + if (switch_channel_get_state(channel) == CS_NEW) { + switch_channel_set_state(channel, CS_INIT); + } + switch_channel_mark_answered(channel); + } +end: + if(session) { + switch_core_session_rwunlock(session); + } + return status; +} + +switch_status_t skinny_handle_soft_key_set_request(listener_t *listener, skinny_message_t *request) +{ + skinny_message_t *message; + skinny_profile_t *profile; + + switch_assert(listener->profile); + switch_assert(listener->device_name); + + profile = listener->profile; + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.soft_key_set)); + message->type = SOFT_KEY_SET_RES_MESSAGE; + message->length = 4 + sizeof(message->data.soft_key_set); + + message->data.soft_key_set.soft_key_set_offset = 0; + message->data.soft_key_set.soft_key_set_count = 11; + message->data.soft_key_set.total_soft_key_set_count = 11; + + /* TODO fill the set */ + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOOK].soft_key_template_index[0] = SOFTKEY_NEWCALL; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOOK].soft_key_template_index[1] = SOFTKEY_REDIAL; + + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_OFF_HOOK].soft_key_template_index[1] = SOFTKEY_REDIAL; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_OFF_HOOK].soft_key_template_index[2] = SOFTKEY_ENDCALL; + + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT].soft_key_template_index[0] = SOFTKEY_BACKSPACE; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT].soft_key_template_index[2] = SOFTKEY_ENDCALL; + + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[0] = SOFTKEY_ENDCALL; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[1] = SOFTKEY_HOLD; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[2] = SOFTKEY_NEWCALL; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[3] = SOFTKEY_TRANSFER; + + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_RING_IN].soft_key_template_index[0] = SOFTKEY_ANSWER; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_RING_IN].soft_key_template_index[1] = SOFTKEY_ENDCALL; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_RING_IN].soft_key_template_index[2] = SOFTKEY_NEWCALL; + + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOLD].soft_key_template_index[0] = SOFTKEY_NEWCALL; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOLD].soft_key_template_index[1] = SOFTKEY_RESUME; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOLD].soft_key_template_index[2] = SOFTKEY_ENDCALL; + + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_OFF_HOOK_WITH_FEATURES].soft_key_template_index[1] = SOFTKEY_REDIAL; + message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_OFF_HOOK_WITH_FEATURES].soft_key_template_index[2] = SOFTKEY_ENDCALL; + + skinny_send_reply(listener, message); + + /* Init the states */ + send_select_soft_keys(listener, 0, 0, SKINNY_KEY_SET_ON_HOOK, 0xffff); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_soft_key_event_message(listener_t *listener, skinny_message_t *request) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + uint32_t line_instance = 0; + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + switch_assert(listener); + switch_assert(listener->profile); + + skinny_check_data_length(request, sizeof(request->data.soft_key_event)); + + line_instance = request->data.soft_key_event.line_instance; + + switch(request->data.soft_key_event.event) { + case SOFTKEY_REDIAL: + status = skinny_create_incoming_session(listener, &line_instance, &session); + + skinny_session_process_dest(session, listener, line_instance, "redial", '\0', 0); + break; + case SOFTKEY_NEWCALL: + status = skinny_create_incoming_session(listener, &line_instance, &session); + tech_pvt = switch_core_session_get_private(session); + + skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 0); + break; + case SOFTKEY_HOLD: + session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); + + if(session) { + status = skinny_session_hold_line(session, listener, line_instance); + } + break; + case SOFTKEY_TRANSFER: + session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); + + if(session) { + status = skinny_session_transfer(session, listener, line_instance); + } + break; + case SOFTKEY_BACKSPACE: + session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); + + if(session) { + skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 1); + } + break; + case SOFTKEY_ENDCALL: + session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); + + if(session) { + channel = switch_core_session_get_channel(session); + + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + } + break; + case SOFTKEY_RESUME: + session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); + + if(session) { + status = skinny_session_unhold_line(session, listener, line_instance); + } + break; + case SOFTKEY_ANSWER: + session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id); + + if(session) { + status = skinny_session_answer(session, listener, line_instance); + } + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Unknown SoftKeyEvent type: %d.\n", request->data.soft_key_event.event); + } + + if(session) { + switch_core_session_rwunlock(session); + } + + return status; +} + +switch_status_t skinny_handle_unregister(listener_t *listener, skinny_message_t *request) +{ + switch_event_t *event = NULL; + skinny_message_t *message; + + /* skinny::unregister event */ + skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_UNREGISTER); + switch_event_fire(&event); + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.unregister_ack)); + message->type = UNREGISTER_ACK_MESSAGE; + message->length = 4 + sizeof(message->data.unregister_ack); + message->data.unregister_ack.unregister_status = 0; /* OK */ + skinny_send_reply(listener, message); + + /* Close socket */ + switch_clear_flag_locked(listener, LFLAG_RUNNING); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_soft_key_template_request(listener_t *listener, skinny_message_t *request) +{ + skinny_message_t *message; + skinny_profile_t *profile; + + switch_assert(listener->profile); + switch_assert(listener->device_name); + + profile = listener->profile; + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.soft_key_template)); + message->type = SOFT_KEY_TEMPLATE_RES_MESSAGE; + message->length = 4 + sizeof(message->data.soft_key_template); + + message->data.soft_key_template.soft_key_offset = 0; + message->data.soft_key_template.soft_key_count = 21; + message->data.soft_key_template.total_soft_key_count = 21; + + memcpy(message->data.soft_key_template.soft_key, + soft_key_template_default, + sizeof(soft_key_template_default)); + + skinny_send_reply(listener, message); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_headset_status_message(listener_t *listener, skinny_message_t *request) +{ + skinny_check_data_length(request, sizeof(request->data.headset_status)); + + /* Nothing to do */ + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_register_available_lines_message(listener_t *listener, skinny_message_t *request) +{ + skinny_check_data_length(request, sizeof(request->data.reg_lines)); + + /* Do nothing */ + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_service_url_stat_request(listener_t *listener, skinny_message_t *request) +{ + skinny_message_t *message; + struct service_url_stat_res_message *button = NULL; + + skinny_check_data_length(request, sizeof(request->data.service_url_req)); + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.service_url_res)); + message->type = SERVICE_URL_STAT_RES_MESSAGE; + message->length = 4 + sizeof(message->data.service_url_res); + + skinny_service_url_get(listener, request->data.service_url_req.service_url_index, &button); + + memcpy(&message->data.service_url_res, button, sizeof(struct service_url_stat_res_message)); + + skinny_send_reply(listener, message); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_feature_stat_request(listener_t *listener, skinny_message_t *request) +{ + skinny_message_t *message; + struct feature_stat_res_message *button = NULL; + + skinny_check_data_length(request, sizeof(request->data.feature_req)); + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.feature_res)); + message->type = FEATURE_STAT_RES_MESSAGE; + message->length = 4 + sizeof(message->data.feature_res); + + skinny_feature_get(listener, request->data.feature_req.feature_index, &button); + + memcpy(&message->data.feature_res, button, sizeof(struct feature_stat_res_message)); + + skinny_send_reply(listener, message); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *request) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Received %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); + if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Device should send a register message first.\n"); + return SWITCH_STATUS_FALSE; + } + switch(request->type) { + case KEEP_ALIVE_MESSAGE: + return skinny_handle_keep_alive_message(listener, request); + case REGISTER_MESSAGE: + return skinny_handle_register(listener, request); + case PORT_MESSAGE: + return skinny_handle_port_message(listener, request); + case KEYPAD_BUTTON_MESSAGE: + return skinny_handle_keypad_button_message(listener, request); + case STIMULUS_MESSAGE: + return skinny_handle_stimulus_message(listener, request); + case OFF_HOOK_MESSAGE: + return skinny_handle_off_hook_message(listener, request); + case ON_HOOK_MESSAGE: + return skinny_handle_on_hook_message(listener, request); + case SPEED_DIAL_STAT_REQ_MESSAGE: + return skinny_handle_speed_dial_stat_request(listener, request); + case LINE_STAT_REQ_MESSAGE: + return skinny_handle_line_stat_request(listener, request); + case CONFIG_STAT_REQ_MESSAGE: + return skinny_handle_config_stat_request(listener, request); + case TIME_DATE_REQ_MESSAGE: + return skinny_handle_time_date_request(listener, request); + case BUTTON_TEMPLATE_REQ_MESSAGE: + return skinny_handle_button_template_request(listener, request); + case VERSION_REQ_MESSAGE: + return skinny_handle_version_request(listener, request); + case CAPABILITIES_RES_MESSAGE: + return skinny_handle_capabilities_response(listener, request); + case ALARM_MESSAGE: + return skinny_handle_alarm(listener, request); + case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE: + return skinny_handle_open_receive_channel_ack_message(listener, request); + case SOFT_KEY_SET_REQ_MESSAGE: + return skinny_handle_soft_key_set_request(listener, request); + case SOFT_KEY_EVENT_MESSAGE: + return skinny_handle_soft_key_event_message(listener, request); + case UNREGISTER_MESSAGE: + return skinny_handle_unregister(listener, request); + case SOFT_KEY_TEMPLATE_REQ_MESSAGE: + return skinny_handle_soft_key_template_request(listener, request); + case HEADSET_STATUS_MESSAGE: + return skinny_headset_status_message(listener, request); + case REGISTER_AVAILABLE_LINES_MESSAGE: + return skinny_handle_register_available_lines_message(listener, request); + case SERVICE_URL_STAT_REQ_MESSAGE: + return skinny_handle_service_url_stat_request(listener, request); + case FEATURE_STAT_REQ_MESSAGE: + return skinny_handle_feature_stat_request(listener, request); + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Unhandled request %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); + return SWITCH_STATUS_SUCCESS; + } +} + +/* 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: + */ + diff --git a/src/mod/endpoints/mod_skinny/skinny_server.h b/src/mod/endpoints/mod_skinny/skinny_server.h new file mode 100644 index 0000000000..f4651e184c --- /dev/null +++ b/src/mod/endpoints/mod_skinny/skinny_server.h @@ -0,0 +1,64 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2010, Mathieu Parent + * + * 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 + * Mathieu Parent + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Mathieu Parent + * + * + * skinny_server.h -- Skinny Call Control Protocol (SCCP) Endpoint Module + * + */ +#ifndef _SKINNY_SERVER_H +#define _SKINNY_SERVER_H + +#include + +/* SESSION FUNCTIONS */ +switch_status_t skinny_create_ingoing_session(listener_t *listener, uint32_t *line_instance, switch_core_session_t **session); +switch_status_t skinny_session_process_dest(switch_core_session_t *session, listener_t *listener, uint32_t line_instance, char *dest, char append_dest, uint32_t backspace); +switch_status_t skinny_session_send_call_info(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); +switch_call_cause_t skinny_ring_lines(private_t *tech_pvt); +switch_status_t skinny_session_ring_out(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); +switch_status_t skinny_session_answer(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); +switch_status_t skinny_session_start_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); +switch_status_t skinny_session_hold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); +switch_status_t skinny_session_unhold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); +switch_status_t skinny_session_stop_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance); +switch_status_t skinny_hold_active_calls(listener_t *listener); + +/* SKINNY MESSAGE HANDLERS */ + +#endif /* _SKINNY_SERVER_H */ + +/* 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: + */ +