Skinny: Implement shared lines and more

- Shared lines (same line on several phones)
  + Active lines stored in db
- Separate SQL mutex per profile
- New event skinny::call_state
- Clean port_message
- More protocol understanding:
  + stimulus may have call_id
  + off_hook may have call_id
  + set_ringer may have call_id and line_instance
  + New DisplayPriNotifyMessage
- Use Cisco terms for Call state
  + Congestion -> LineInUse
  + CallRemoteMultiline -> InUseRemotely
- Add line_instance to Lines table (relative position contrary to
  absolute position)
- Less assertions as FS already does some
This commit is contained in:
Mathieu Parent 2010-04-01 14:08:12 +02:00
parent 0bdc156882
commit b8ac781c05
6 changed files with 977 additions and 624 deletions

View File

@ -65,6 +65,7 @@ static char lines_sql[] =
" device_name VARCHAR(16),\n"
" device_instance INTEGER,\n"
" position INTEGER,\n"
" line_instance INTEGER,\n"
" label VARCHAR(40),\n"
" value VARCHAR(24),\n"
" caller_name VARCHAR(44),\n"
@ -88,6 +89,16 @@ static char buttons_sql[] =
" settings VARCHAR(44)\n"
");\n";
static char active_lines_sql[] =
"CREATE TABLE skinny_active_lines (\n"
" device_name VARCHAR(16),\n"
" device_instance INTEGER,\n"
" line_instance INTEGER,\n"
" channel_uuid VARCHAR(256),\n"
" call_id INTEGER,\n"
" call_state INTEGER\n"
");\n";
/*****************************************************************************/
/* PROFILES FUNCTIONS */
/*****************************************************************************/
@ -138,48 +149,106 @@ static switch_status_t skinny_profile_find_listener_by_device_name(skinny_profil
return SWITCH_STATUS_SUCCESS;
}
struct skinny_profile_find_listener_helper {
skinny_profile_t *profile;
listener_t *listener;
uint32_t line;
};
static int skinny_profile_find_listener_callback(void *pArg, int argc, char **argv, char **columnNames)
switch_status_t skinny_profile_find_listener_by_device_name_and_instance(skinny_profile_t *profile, const char *device_name, uint32_t device_instance, listener_t **listener)
{
struct skinny_profile_find_listener_helper *helper = pArg;
skinny_profile_t *profile = helper->profile;
char *device_name = argv[0];
/* uint32_t position = atoi(argv[1]); */
uint32_t relative_position = atoi(argv[2]);
skinny_profile_find_listener_by_device_name(profile, device_name, &helper->listener);
if(helper->listener) {
helper->line = relative_position;
switch_mutex_lock(profile->listener_mutex);
for (listener_t *l = profile->listeners; l; l = l->next) {
if (!strcmp(l->device_name, device_name) && (l->device_instance == device_instance)) {
*listener = l;
}
}
return 0;
switch_mutex_unlock(profile->listener_mutex);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t skinny_profile_find_listener_by_dest(skinny_profile_t *profile, const char *dest, listener_t **l, uint32_t *line)
struct skinny_profile_find_session_uuid_helper {
skinny_profile_t *profile;
char *channel_uuid;
uint32_t line_instance;
};
int skinny_profile_find_session_uuid_callback(void *pArg, int argc, char **argv, char **columnNames)
{
struct skinny_profile_find_session_uuid_helper *helper = pArg;
char *channel_uuid = argv[0];
uint32_t line_instance = atoi(argv[1]);
if(helper->channel_uuid == NULL) {
helper->channel_uuid = switch_mprintf("%s", channel_uuid);
helper->line_instance = line_instance;
}
return 0;
}
char * skinny_profile_find_session_uuid(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id)
{
struct skinny_profile_find_session_uuid_helper helper = {0};
char *sql;
struct skinny_profile_find_listener_helper helper = {0};
helper.profile = profile;
char *device_condition = NULL;
char *line_instance_condition = NULL;
char *call_id_condition = NULL;
if ((sql = switch_mprintf("SELECT device_name, position, "
"(SELECT count(*) from skinny_lines sl2 "
"WHERE sl2.device_name= sl1.device_name AND sl2.device_instance= sl1.device_instance AND sl2.position <= sl1.position) AS relative_position "
"FROM skinny_lines sl1 WHERE value='%s'",
dest))) {
skinny_execute_sql_callback(profile, profile->listener_mutex, sql, skinny_profile_find_listener_callback, &helper);
switch_assert(profile);
helper.profile = profile;
helper.channel_uuid = NULL;
if(listener) {
device_condition = switch_mprintf("device_name='%s' AND device_instance=%d",
listener->device_name, listener->device_instance);
} else {
device_condition = switch_mprintf("1=1");
}
switch_assert(device_condition);
if(*line_instance_p > 0) {
line_instance_condition = switch_mprintf("line_instance=%d", *line_instance_p);
} else {
line_instance_condition = switch_mprintf("1=1");
}
switch_assert(line_instance_condition);
if(call_id > 0) {
call_id_condition = switch_mprintf("call_id=%d", call_id);
} else {
call_id_condition = switch_mprintf("1=1");
}
switch_assert(call_id_condition);
if((sql = switch_mprintf(
"SELECT channel_uuid, line_instance "
"FROM skinny_active_lines "
"WHERE %s AND %s AND %s "
"ORDER BY channel_uuid DESC",
device_condition, line_instance_condition, call_id_condition
))) {
skinny_execute_sql_callback(profile, profile->sql_mutex, sql,
skinny_profile_find_session_uuid_callback, &helper);
switch_safe_free(sql);
}
switch_safe_free(device_condition);
switch_safe_free(line_instance_condition);
switch_safe_free(call_id_condition);
*line_instance_p = helper.line_instance;
return helper.channel_uuid;
}
switch_core_session_t * skinny_profile_find_session(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id)
{
char *uuid;
switch_core_session_t *result = NULL;
uuid = skinny_profile_find_session_uuid(profile, listener, line_instance_p, call_id);
*line = helper.line;
*l = helper.listener;
return SWITCH_STATUS_SUCCESS;
if(!zstr(uuid)) {
/* TODO Why should we force? */
result = switch_core_session_force_locate(uuid);
if(!result) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Unable to find session %s on %s:%d, line %d\n",
uuid, listener->device_name, listener->device_instance, *line_instance_p);
}
switch_safe_free(uuid);
}
return result;
}
/*****************************************************************************/
@ -262,28 +331,73 @@ switch_bool_t skinny_execute_sql_callback(skinny_profile_t *profile,
/*****************************************************************************/
/* CHANNEL FUNCTIONS */
/*****************************************************************************/
uint32_t skinny_line_perform_set_state(listener_t *listener, const char *file, const char *func, int line, uint32_t instance, uint32_t state, uint32_t call_id)
void skinny_line_perform_set_state(const char *file, const char *func, int line, listener_t *listener, uint32_t line_instance, uint32_t call_id, uint32_t call_state)
{
switch_assert(listener);
switch_event_t *event = NULL;
switch_assert(listener);
skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_CALL_STATE);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Line-Instance", "%d", line_instance);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Call-Id", "%d", call_id);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Call-State", "%d", call_state);
switch_event_fire(&event);
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG,
"Device %s, line %d State Change %s (%d) -> %s (%d) (no session)\n",
listener->device_name, instance,
skinny_soft_key_set2str(listener->line_state[instance]), listener->line_state[instance],
skinny_soft_key_set2str(state), state);
send_select_soft_keys(listener, instance, call_id, state, 0xffff);
listener->line_state[instance] = state;
return listener->line_state[instance];
"Device %s:%d, Line %d, Call %d Change State to %s (%d)\n",
listener->device_name, listener->device_instance, line_instance, call_id,
skinny_call_state2str(call_state), call_state);
}
uint32_t skinny_line_get_state(listener_t *listener, uint32_t instance)
struct skinny_line_get_state_helper {
uint32_t call_state;
};
int skinny_line_get_state_callback(void *pArg, int argc, char **argv, char **columnNames)
{
switch_assert(listener);
return listener->line_state[instance];
struct skinny_line_get_state_helper *helper = pArg;
helper->call_state = atoi(argv[0]);
return 0;
}
uint32_t skinny_line_get_state(listener_t *listener, uint32_t line_instance, uint32_t call_id)
{
char *line_instance_condition;
char *call_id_condition;
char *sql;
struct skinny_line_get_state_helper helper = {0};
switch_assert(listener);
if(line_instance > 0) {
line_instance_condition = switch_mprintf("line_instance=%d", line_instance);
} else {
line_instance_condition = switch_mprintf("1=1");
}
switch_assert(line_instance_condition);
if(call_id > 0) {
call_id_condition = switch_mprintf("call_id=%d", call_id);
} else {
call_id_condition = switch_mprintf("1=1");
}
switch_assert(call_id_condition);
if ((sql = switch_mprintf(
"SELECT call_state FROM skinny_active_lines "
"WHERE device_name='%s' AND device_instance=%d "
"AND %s AND %s",
listener->device_name, listener->device_instance,
line_instance_condition, call_id_condition
))) {
skinny_execute_sql_callback(listener->profile, listener->profile->sql_mutex, sql, skinny_line_get_state_callback, &helper);
switch_safe_free(sql);
}
switch_safe_free(line_instance_condition);
switch_safe_free(call_id_condition);
return helper.call_state;
}
switch_status_t skinny_tech_set_codec(private_t *tech_pvt, int force)
{
int ms;
@ -358,9 +472,7 @@ switch_status_t skinny_tech_set_codec(private_t *tech_pvt, int force)
tech_pvt->read_impl.microseconds_per_packet,
tech_pvt->read_impl.samples_per_packet
) != SWITCH_STATUS_SUCCESS) {
switch_channel_t *channel = NULL;
channel = switch_core_session_get_channel(tech_pvt->session);
assert(channel != NULL);
switch_channel_t *channel = switch_core_session_get_channel(tech_pvt->session);
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
switch_goto_status(SWITCH_STATUS_FALSE, end);
@ -407,28 +519,19 @@ switch_status_t skinny_tech_set_codec(private_t *tech_pvt, int force)
return status;
}
void tech_init(private_t *tech_pvt, switch_core_session_t *session, listener_t *listener, uint32_t line)
void tech_init(private_t *tech_pvt, skinny_profile_t *profile, switch_core_session_t *session)
{
struct line_stat_res_message *button = NULL;
switch_assert(tech_pvt);
switch_assert(session);
switch_assert(listener);
tech_pvt->read_frame.data = tech_pvt->databuf;
tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf);
switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
tech_pvt->call_id = listener->profile->next_call_id++;
tech_pvt->listener = listener;
tech_pvt->line = line;
tech_pvt->call_id = ++profile->next_call_id;
tech_pvt->profile = profile;
switch_core_session_set_private(session, tech_pvt);
tech_pvt->session = session;
skinny_line_get(listener, line, &button);
tech_pvt->line_name = switch_core_strdup(listener->pool, button->name);
tech_pvt->line_shortname = switch_core_strdup(listener->pool, button->shortname);
tech_pvt->line_displayname = switch_core_strdup(listener->pool, button->displayname);
}
/*
@ -438,14 +541,9 @@ void tech_init(private_t *tech_pvt, switch_core_session_t *session, listener_t *
*/
switch_status_t channel_on_init(switch_core_session_t *session)
{
switch_channel_t *channel;
private_t *tech_pvt = NULL;
switch_channel_t *channel = switch_core_session_get_channel(session);
private_t *tech_pvt = switch_core_session_get_private(session);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
switch_set_flag_locked(tech_pvt, TFLAG_IO);
/* Move channel's state machine to ROUTING. This means the call is trying
@ -464,14 +562,7 @@ switch_status_t channel_on_init(switch_core_session_t *session)
switch_status_t channel_on_routing(switch_core_session_t *session)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL ROUTING\n", switch_channel_get_name(channel));
@ -480,15 +571,7 @@ switch_status_t channel_on_routing(switch_core_session_t *session)
switch_status_t channel_on_execute(switch_core_session_t *session)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel));
@ -498,13 +581,8 @@ switch_status_t channel_on_execute(switch_core_session_t *session)
switch_status_t channel_on_destroy(switch_core_session_t *session)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
switch_channel_t *channel = switch_core_session_get_channel(session);
private_t *tech_pvt = switch_core_session_get_private(session);
if (tech_pvt) {
if (switch_core_codec_ready(&tech_pvt->read_codec)) {
@ -521,73 +599,99 @@ switch_status_t channel_on_destroy(switch_core_session_t *session)
return SWITCH_STATUS_SUCCESS;
}
struct channel_on_hangup_helper {
private_t *tech_pvt;
};
int channel_on_hangup_callback(void *pArg, int argc, char **argv, char **columnNames)
{
struct channel_on_hangup_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(call_state == SKINNY_CONNECTED) {
stop_tone(listener, line_instance, call_id);
}
set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_OFF);
clear_prompt_status(listener, line_instance, call_id);
if(call_state == SKINNY_CONNECTED) { /* calling parties */
close_receive_channel(listener,
call_id, /* uint32_t conference_id, */
helper->tech_pvt->party_id, /* uint32_t pass_thru_party_id, */
call_id /* uint32_t conference_id2, */
);
stop_media_transmission(listener,
call_id, /* uint32_t conference_id, */
helper->tech_pvt->party_id, /* uint32_t pass_thru_party_id, */
call_id /* uint32_t conference_id2, */
);
switch_mutex_lock(globals.calls_mutex);
globals.calls--;
if (globals.calls < 0) {
globals.calls = 0;
}
switch_mutex_unlock(globals.calls_mutex);
}
skinny_line_set_state(listener, line_instance, call_id, SKINNY_ON_HOOK);
send_select_soft_keys(listener, line_instance, call_id, SKINNY_KEY_SET_ON_HOOK, 0xffff);
/* TODO: DefineTimeDate */
set_speaker_mode(listener, SKINNY_SPEAKER_OFF);
set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, call_id);
}
return 0;
}
switch_status_t channel_on_hangup(switch_core_session_t *session)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
listener_t *listener = NULL;
struct channel_on_hangup_helper helper = {0};
switch_channel_t *channel = switch_core_session_get_channel(session);
private_t *tech_pvt = switch_core_session_get_private(session);
char *sql;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
listener = tech_pvt->listener;
assert(listener != NULL);
switch_clear_flag_locked(tech_pvt, TFLAG_IO);
switch_clear_flag_locked(tech_pvt, TFLAG_VOICE);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel));
listener->session[tech_pvt->line] = NULL;
helper.tech_pvt= tech_pvt;
stop_tone(listener, tech_pvt->line, tech_pvt->call_id);
set_lamp(listener, SKINNY_BUTTON_LINE, tech_pvt->line, SKINNY_LAMP_OFF);
clear_prompt_status(listener, tech_pvt->line, tech_pvt->call_id);
if( skinny_line_get_state(tech_pvt->listener, tech_pvt->line) == SKINNY_KEY_SET_CONNECTED ) {
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, */
);
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, */
);
switch_mutex_lock(globals.calls_mutex);
globals.calls--;
if (globals.calls < 0) {
globals.calls = 0;
}
switch_mutex_unlock(globals.calls_mutex);
skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), channel_on_hangup_callback, &helper);
if ((sql = switch_mprintf(
"DELETE FROM skinny_active_lines WHERE channel_uuid='%s'",
switch_core_session_get_uuid(session)
))) {
skinny_execute_sql(tech_pvt->profile, sql, tech_pvt->profile->sql_mutex);
switch_safe_free(sql);
}
send_call_state(listener,
SKINNY_ON_HOOK,
tech_pvt->line,
tech_pvt->call_id);
skinny_line_set_state(listener, tech_pvt->line, SKINNY_KEY_SET_ON_HOOK, tech_pvt->call_id);
/* TODO: DefineTimeDate */
set_speaker_mode(listener, SKINNY_SPEAKER_OFF);
set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t channel_kill_channel(switch_core_session_t *session, int sig)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
switch_channel_t *channel = switch_core_session_get_channel(session);
private_t *tech_pvt = switch_core_session_get_private(session);
switch (sig) {
case SWITCH_SIG_KILL:
@ -631,16 +735,10 @@ switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_d
switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
switch_channel_t *channel = switch_core_session_get_channel(session);
private_t *tech_pvt = switch_core_session_get_private(session);
int payload = 0;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
while (!(tech_pvt->read_codec.implementation && switch_rtp_ready(tech_pvt->rtp_session))) {
if (switch_channel_ready(channel)) {
switch_yield(10000);
@ -705,17 +803,10 @@ switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_
switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
private_t *tech_pvt = switch_core_session_get_private(session);
//switch_frame_t *pframe;
switch_status_t status = SWITCH_STATUS_SUCCESS;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
return SWITCH_STATUS_FALSE;
}
@ -737,31 +828,12 @@ switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame
switch_status_t channel_answer_channel(switch_core_session_t *session)
{
private_t *tech_pvt;
switch_channel_t *channel = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
{
switch_channel_t *channel;
private_t *tech_pvt;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = (private_t *) switch_core_session_get_private(session);
assert(tech_pvt != NULL);
switch (msg->message_id) {
case SWITCH_MESSAGE_INDICATE_ANSWER:
{
@ -788,8 +860,7 @@ switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, swi
char *profile_name, *dest;
skinny_profile_t *profile = NULL;
listener_t *listener = NULL;
uint32_t line = 0;
char *sql;
char name[128];
switch_channel_t *channel;
switch_caller_profile_t *caller_profile;
@ -833,26 +904,7 @@ switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, swi
channel = switch_core_session_get_channel(nsession);
switch_channel_set_name(channel, name);
if ((skinny_profile_find_listener_by_dest(profile, dest, &listener, &line) != SWITCH_STATUS_SUCCESS)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Problem while retrieving listener and line for destination %s in profile %s\n", dest, profile_name);
cause = SWITCH_CAUSE_UNALLOCATED_NUMBER;
goto error;
}
if (!listener) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid destination or phone not registred %s in profile %s\n", dest, profile_name);
cause = SWITCH_CAUSE_UNALLOCATED_NUMBER;
goto error;
}
if (line == 0) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid destination or phone not registred %s in profile %s\n", dest, profile_name);
cause = SWITCH_CAUSE_UNALLOCATED_NUMBER;
goto error;
}
tech_init(tech_pvt, nsession, listener, line);
tech_init(tech_pvt, profile, nsession);
caller_profile = switch_caller_profile_clone(nsession, outbound_profile);
switch_channel_set_caller_profile(channel, caller_profile);
@ -861,19 +913,23 @@ switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, swi
switch_channel_set_flag(channel, CF_OUTBOUND);
switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND);
if(tech_pvt->listener->session[tech_pvt->line]) { /* Line is busy */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Device line is busy %s in profile %s\n", dest, profile_name);
cause = SWITCH_CAUSE_USER_BUSY;
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, dest
))) {
skinny_execute_sql(profile, sql, profile->sql_mutex);
switch_safe_free(sql);
}
cause = skinny_ring_lines(tech_pvt);
if(cause != SWITCH_CAUSE_SUCCESS) {
goto error;
}
tech_pvt->listener->session[tech_pvt->line] = nsession;
send_call_state(tech_pvt->listener, SKINNY_RING_IN, tech_pvt->line, tech_pvt->call_id);
skinny_line_set_state(tech_pvt->listener, tech_pvt->line, SKINNY_KEY_SET_RING_IN, tech_pvt->call_id);
display_prompt_status(tech_pvt->listener, 0, "\200\027tel", tech_pvt->line, tech_pvt->call_id);
/* displayprinotifiymessage */
skinny_send_call_info(nsession);
set_lamp(tech_pvt->listener, SKINNY_BUTTON_LINE, tech_pvt->line, SKINNY_LAMP_BLINK);
set_ringer(tech_pvt->listener, SKINNY_RING_OUTSIDE, SKINNY_RING_FOREVER, 0);
*new_session = nsession;
@ -1017,7 +1073,7 @@ static void flush_listener(listener_t *listener, switch_bool_t flush_log, switch
"DELETE FROM skinny_devices "
"WHERE name='%s' and instance=%d",
listener->device_name, listener->device_instance))) {
skinny_execute_sql(profile, sql, profile->listener_mutex);
skinny_execute_sql(profile, sql, profile->sql_mutex);
switch_safe_free(sql);
}
@ -1025,7 +1081,7 @@ static void flush_listener(listener_t *listener, switch_bool_t flush_log, switch
"DELETE FROM skinny_lines "
"WHERE device_name='%s' and device_instance=%d",
listener->device_name, listener->device_instance))) {
skinny_execute_sql(profile, sql, profile->listener_mutex);
skinny_execute_sql(profile, sql, profile->sql_mutex);
switch_safe_free(sql);
}
@ -1033,7 +1089,7 @@ static void flush_listener(listener_t *listener, switch_bool_t flush_log, switch
"DELETE FROM skinny_buttons "
"WHERE device_name='%s' and device_instance=%d",
listener->device_name, listener->device_instance))) {
skinny_execute_sql(profile, sql, profile->listener_mutex);
skinny_execute_sql(profile, sql, profile->sql_mutex);
switch_safe_free(sql);
}
@ -1072,9 +1128,10 @@ static int dump_device_callback(void *pArg, int argc, char **argv, char **column
static switch_status_t dump_device(skinny_profile_t *profile, const char *device_name, switch_stream_handle_t *stream)
{
char *sql;
if ((sql = switch_mprintf("SELECT * FROM skinny_devices WHERE name LIKE '%s'",
if ((sql = switch_mprintf("SELECT name, user_id, instance, ip, type, max_streams, port, codec_string "
"FROM skinny_devices WHERE name='%s'",
device_name))) {
skinny_execute_sql_callback(profile, profile->listener_mutex, sql, dump_device_callback, stream);
skinny_execute_sql_callback(profile, profile->sql_mutex, sql, dump_device_callback, stream);
switch_safe_free(sql);
}
@ -1200,6 +1257,7 @@ static void *SWITCH_THREAD_FUNC listener_run(switch_thread_t *thread, void *obj)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connection Closed\n");
}
/* TODO
for(int line = 0 ; line < SKINNY_MAX_BUTTON_COUNT ; line++) {
if(listener->session[line]) {
switch_channel_clear_flag(switch_core_session_get_channel(listener->session[line]), CF_CONTROLLED);
@ -1208,6 +1266,7 @@ static void *SWITCH_THREAD_FUNC listener_run(switch_thread_t *thread, void *obj)
destroy_pool = 0;
}
}
*/
if(destroy_pool == 0) {
goto no_destroy_pool;
}
@ -1467,11 +1526,13 @@ static switch_status_t load_skinny_config(void)
switch_odbc_handle_exec(profile->master_odbc, devices_sql, NULL, NULL);
switch_odbc_handle_exec(profile->master_odbc, lines_sql, NULL, NULL);
switch_odbc_handle_exec(profile->master_odbc, buttons_sql, NULL, NULL);
switch_odbc_handle_exec(profile->master_odbc, active_lines_sql, NULL, NULL);
} else {
if ((db = switch_core_db_open_file(profile->dbname))) {
switch_core_db_test_reactive(db, "SELECT * FROM skinny_devices", NULL, devices_sql);
switch_core_db_test_reactive(db, "SELECT * FROM skinny_lines", NULL, lines_sql);
switch_core_db_test_reactive(db, "SELECT * FROM skinny_buttons", NULL, buttons_sql);
switch_core_db_test_reactive(db, "SELECT * FROM skinny_active_lines", NULL, active_lines_sql);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open SQL Database!\n");
continue;
@ -1479,9 +1540,10 @@ static switch_status_t load_skinny_config(void)
switch_core_db_close(db);
}
skinny_execute_sql_callback(profile, profile->listener_mutex, "DELETE FROM skinny_devices", NULL, NULL);
skinny_execute_sql_callback(profile, profile->listener_mutex, "DELETE FROM skinny_lines", NULL, NULL);
skinny_execute_sql_callback(profile, profile->listener_mutex, "DELETE FROM skinny_buttons", NULL, NULL);
skinny_execute_sql_callback(profile, profile->sql_mutex, "DELETE FROM skinny_devices", NULL, NULL);
skinny_execute_sql_callback(profile, profile->sql_mutex, "DELETE FROM skinny_lines", NULL, NULL);
skinny_execute_sql_callback(profile, profile->sql_mutex, "DELETE FROM skinny_buttons", NULL, NULL);
skinny_execute_sql_callback(profile, profile->sql_mutex, "DELETE FROM skinny_active_lines", NULL, NULL);
switch_core_hash_insert(globals.profile_hash, profile->name, profile);
profile = NULL;
@ -1528,7 +1590,7 @@ static switch_status_t cmd_profile_device_send_ringer_message(const char *profil
listener_t *listener = NULL;
skinny_profile_find_listener_by_device_name(profile, device_name, &listener);
if(listener) {
set_ringer(listener, skinny_str2ring_type(ring_type), skinny_str2ring_mode(ring_mode), 0);
set_ringer(listener, skinny_str2ring_type(ring_type), skinny_str2ring_mode(ring_mode), 0, 0);
} else {
stream->write_function(stream, "Listener not found!\n");
}
@ -1707,8 +1769,55 @@ done:
static void event_handler(switch_event_t *event)
{
char *subclass;
if (event->event_id == SWITCH_EVENT_HEARTBEAT) {
walk_listeners(kill_expired_listener, NULL);
} else if ((subclass = switch_event_get_header_nil(event, "Event-Subclass")) && !strcasecmp(subclass, SKINNY_EVENT_CALL_STATE)) {
char *profile_name = switch_event_get_header_nil(event, "Skinny-Profile-Name");
char *device_name = switch_event_get_header_nil(event, "Skinny-Device-Name");
uint32_t device_instance = atoi(switch_event_get_header_nil(event, "Skinny-Device-Instance"));
uint32_t line_instance = atoi(switch_event_get_header_nil(event, "Skinny-Line-Instance"));
uint32_t call_id = atoi(switch_event_get_header_nil(event, "Skinny-Call-Id"));
uint32_t call_state = atoi(switch_event_get_header_nil(event, "Skinny-Call-State"));
skinny_profile_t *profile;
listener_t *listener = NULL;
char *line_instance_condition, *call_id_condition;
char *sql;
if ((profile = skinny_find_profile(profile_name))) {
skinny_profile_find_listener_by_device_name_and_instance(profile, device_name, device_instance, &listener);
if(listener) {
if(line_instance > 0) {
line_instance_condition = switch_mprintf("line_instance=%d", line_instance);
} else {
line_instance_condition = switch_mprintf("1=1");
}
switch_assert(line_instance_condition);
if(call_id > 0) {
call_id_condition = switch_mprintf("call_id=%d", call_id);
} else {
call_id_condition = switch_mprintf("1=1");
}
switch_assert(call_id_condition);
if ((sql = switch_mprintf(
"UPDATE skinny_active_lines "
"SET call_state=%d "
"WHERE device_name='%s' AND device_instance=%d "
"AND %s AND %s",
call_state,
listener->device_name, listener->device_instance,
line_instance_condition, call_id_condition
))) {
skinny_execute_sql(listener->profile, sql, listener->profile->sql_mutex);
switch_safe_free(sql);
send_call_state(listener, call_state, line_instance, call_id);
}
switch_safe_free(line_instance_condition);
switch_safe_free(call_id_condition);
}
}
}
}
@ -1776,7 +1885,7 @@ static switch_status_t skinny_list_devices(const char *line, const char *cursor,
if(profile) {
if ((sql = switch_mprintf("SELECT name FROM skinny_devices"))) {
skinny_execute_sql_callback(profile, profile->listener_mutex, sql, skinny_list_devices_callback, &h);
skinny_execute_sql_callback(profile, profile->sql_mutex, sql, skinny_list_devices_callback, &h);
switch_safe_free(sql);
}
}
@ -1904,6 +2013,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_skinny_load)
profile = (skinny_profile_t *) val;
switch_mutex_init(&profile->listener_mutex, SWITCH_MUTEX_NESTED, module_pool);
switch_mutex_init(&profile->sql_mutex, SWITCH_MUTEX_NESTED, module_pool);
switch_mutex_init(&profile->sock_mutex, SWITCH_MUTEX_NESTED, module_pool);
}
@ -1913,6 +2023,11 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_skinny_load)
/* Not such severe to prevent loading */
}
if ((switch_event_bind_removable(modname, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_CALL_STATE, event_handler, NULL, &globals.call_state_node) != SWITCH_STATUS_SUCCESS)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind our call_state handler!\n");
return SWITCH_STATUS_TERM;
}
if (switch_event_reserve_subclass(SKINNY_EVENT_REGISTER) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_REGISTER);
return SWITCH_STATUS_TERM;
@ -1984,6 +2099,7 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skinny_shutdown)
switch_event_free_subclass(SKINNY_EVENT_REGISTER);
switch_event_unbind(&globals.heartbeat_node);
switch_event_unbind(&globals.call_state_node);
globals.running = 0;

View File

@ -42,6 +42,7 @@
#define SKINNY_EVENT_UNREGISTER "skinny::unregister"
#define SKINNY_EVENT_EXPIRE "skinny::expire"
#define SKINNY_EVENT_ALARM "skinny::alarm"
#define SKINNY_EVENT_CALL_STATE "skinny::call_state"
struct skinny_globals {
/* data */
@ -49,6 +50,7 @@ struct skinny_globals {
switch_mutex_t *calls_mutex;
switch_hash_t *profile_hash;
switch_event_node_t *heartbeat_node;
switch_event_node_t *call_state_node;
int running;
};
typedef struct skinny_globals skinny_globals_t;
@ -72,6 +74,7 @@ struct skinny_profile {
char *odbc_user;
char *odbc_pass;
switch_odbc_handle_t *master_odbc;
switch_mutex_t *sql_mutex;
/* stats */
uint32_t ib_calls;
uint32_t ob_calls;
@ -103,8 +106,6 @@ struct listener {
skinny_profile_t *profile;
char device_name[16];
uint32_t device_instance;
switch_core_session_t *session[SKINNY_MAX_LINES];
uint32_t line_state[SKINNY_MAX_LINES]; /* See enum skinny_key_set */
switch_socket_t *sock;
switch_memory_pool_t *pool;
@ -153,14 +154,11 @@ struct private_object {
switch_mutex_t *mutex;
switch_mutex_t *flag_mutex;
/* identification */
struct listener *listener;
uint32_t line;
uint32_t call_id;
uint32_t party_id;
char *line_name;
char *line_shortname;
char *line_displayname;
char dest[10];
skinny_profile_t *profile;
/* codec */
char *iananame;
switch_codec_t read_codec;
@ -182,6 +180,13 @@ struct private_object {
typedef struct private_object private_t;
/*****************************************************************************/
/* PROFILES FUNCTIONS */
/*****************************************************************************/
switch_status_t skinny_profile_find_listener_by_device_name_and_instance(skinny_profile_t *profile, const char *device_name, uint32_t device_instance, listener_t **listener);
char * skinny_profile_find_session_uuid(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id);
switch_core_session_t * skinny_profile_find_session(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id);
/*****************************************************************************/
/* SQL FUNCTIONS */
/*****************************************************************************/
@ -197,13 +202,13 @@ switch_status_t keepalive_listener(listener_t *listener, void *pvt);
/*****************************************************************************/
/* CHANNEL FUNCTIONS */
/*****************************************************************************/
uint32_t skinny_line_perform_set_state(listener_t *listener, const char *file, const char *func, int line, uint32_t instance, uint32_t state, uint32_t call_id);
#define skinny_line_set_state(listener, instance, state, call_id) skinny_line_perform_set_state(listener, __FILE__, __SWITCH_FUNC__, __LINE__, instance, state, call_id)
void skinny_line_perform_set_state(const char *file, const char *func, int line, listener_t *listener, uint32_t line_instance, uint32_t call_id, uint32_t call_state);
#define skinny_line_set_state(listener, line_instance, call_id, call_state) skinny_line_perform_set_state(__FILE__, __SWITCH_FUNC__, __LINE__, listener, line_instance, call_id, call_state)
uint32_t skinny_line_get_state(listener_t *listener, uint32_t instance);
uint32_t skinny_line_get_state(listener_t *listener, uint32_t line_instance, uint32_t call_id);
switch_status_t skinny_tech_set_codec(private_t *tech_pvt, int force);
void tech_init(private_t *tech_pvt, switch_core_session_t *session, listener_t *listener, uint32_t line);
void tech_init(private_t *tech_pvt, skinny_profile_t *profile, switch_core_session_t *session);
switch_status_t channel_on_init(switch_core_session_t *session);
switch_status_t channel_on_hangup(switch_core_session_t *session);
switch_status_t channel_on_destroy(switch_core_session_t *session);

File diff suppressed because it is too large Load Diff

View File

@ -59,6 +59,9 @@ struct register_message {
/* PortMessage */
#define PORT_MESSAGE 0x0002
struct port_message {
uint16_t port;
};
/* KeypadButtonMessage */
#define KEYPAD_BUTTON_MESSAGE 0x0003
@ -73,14 +76,14 @@ struct keypad_button_message {
struct stimulus_message {
uint32_t instance_type; /* See enum skinny_button_definition */
uint32_t instance;
/* uint32_t call_reference; */
uint32_t call_id;
};
/* OffHookMessage */
#define OFF_HOOK_MESSAGE 0x0006
struct off_hook_message {
uint32_t line_instance;
/* uint32_t call_id; */
uint32_t call_id;
};
/* OnHookMessage */
@ -150,7 +153,7 @@ struct open_receive_channel_ack_message {
struct soft_key_event_message {
uint32_t event;
uint32_t line_instance;
uint32_t callreference;
uint32_t call_id;
};
/* UnregisterMessage */
@ -186,10 +189,10 @@ struct register_available_lines_message {
/* RegisterAckMessage */
#define REGISTER_ACK_MESSAGE 0x0081
struct register_ack_message {
uint32_t keepAlive;
char dateFormat[6];
uint32_t keep_alive;
char date_format[6];
char reserved[2];
uint32_t secondaryKeepAlive;
uint32_t secondary_keep_alive;
char reserved2[4];
};
@ -214,7 +217,8 @@ struct stop_tone_message {
struct set_ringer_message {
uint32_t ring_type; /* See enum skinny_ring_type */
uint32_t ring_mode; /* See enum skinny_ring_mode */
uint32_t unknown; /* ?? */
uint32_t line_instance;
uint32_t call_id;
};
/* SetLampMessage */
@ -470,6 +474,14 @@ struct feature_stat_res_message {
uint32_t status;
};
/* DisplayPriNotifyMessage */
#define DISPLAY_PRI_NOTIFY_MESSAGE 0x0120
struct display_pri_notify_message {
uint32_t message_timeout;
uint32_t priority;
char notify[32];
};
/* ServiceUrlStatMessage */
#define SERVICE_URL_STAT_RES_MESSAGE 0x012F
struct service_url_stat_res_message {
@ -487,6 +499,7 @@ struct service_url_stat_res_message {
union skinny_data {
struct register_message reg;
struct port_message port;
struct keypad_button_message keypad_button;
struct stimulus_message stimulus;
struct off_hook_message off_hook;
@ -529,6 +542,7 @@ union skinny_data {
struct unregister_ack_message unregister_ack;
struct dialed_number_message dialed_number;
struct feature_stat_res_message feature_res;
struct display_pri_notify_message display_pri_notify;
struct service_url_stat_res_message service_url_res;
uint16_t as_uint16;
@ -598,16 +612,24 @@ typedef switch_status_t (*skinny_command_t) (char **argv, int argc, switch_strea
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received Too Short Skinny Message (Expected %" SWITCH_SIZE_T_FMT ", got %d).\n", len+4, message->length);\
return SWITCH_STATUS_FALSE;\
}
#define skinny_check_data_length_soft(message, len) \
(message->length >= len+4)
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_send_call_info(switch_core_session_t *session);
switch_status_t skinny_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_session(listener_t *listener, uint32_t line, uint32_t to_state);
switch_status_t skinny_process_dest(listener_t *listener, uint32_t line);
switch_status_t skinny_answer(switch_core_session_t *session);
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);
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);
@ -631,7 +653,8 @@ switch_status_t stop_tone(listener_t *listener,
switch_status_t set_ringer(listener_t *listener,
uint32_t ring_type,
uint32_t ring_mode,
uint32_t unknown);
uint32_t line_instance,
uint32_t call_id);
switch_status_t set_lamp(listener_t *listener,
uint32_t stimulus,
uint32_t stimulus_instance,
@ -710,6 +733,10 @@ switch_status_t send_dialed_number(listener_t *listener,
char called_party[24],
uint32_t line_instance,
uint32_t call_id);
switch_status_t send_display_pri_notify(listener_t *listener,
uint32_t message_timeout,
uint32_t priority,
char *notify);
switch_status_t send_reset(listener_t *listener,
uint32_t reset_type);

View File

@ -77,7 +77,7 @@ struct skinny_table SKINNY_MESSAGE_TYPES[] = {
{"ResetMessage", RESET_MESSAGE},
{"KeepAliveAckMessage", KEEP_ALIVE_ACK_MESSAGE},
{"OpenReceiveChannelMessage", OPEN_RECEIVE_CHANNEL_MESSAGE},
{"OCloseReceiveChannelMessage", CLOSE_RECEIVE_CHANNEL_MESSAGE},
{"CloseReceiveChannelMessage", CLOSE_RECEIVE_CHANNEL_MESSAGE},
{"SoftKeyTemplateResMessage", SOFT_KEY_TEMPLATE_RES_MESSAGE},
{"SoftKeySetResMessage", SOFT_KEY_SET_RES_MESSAGE},
{"SelectSoftKeysMessage", SELECT_SOFT_KEYS_MESSAGE},
@ -88,6 +88,7 @@ struct skinny_table SKINNY_MESSAGE_TYPES[] = {
{"UnregisterAckMessage", UNREGISTER_ACK_MESSAGE},
{"DialedNumberMessage", DIALED_NUMBER_MESSAGE},
{"FeatureResMessage", FEATURE_STAT_RES_MESSAGE},
{"DisplayPriNotifyMessage", DISPLAY_PRI_NOTIFY_MESSAGE},
{"ServiceUrlStatMessage", SERVICE_URL_STAT_RES_MESSAGE},
{NULL, 0}
};
@ -168,13 +169,13 @@ struct skinny_table SKINNY_CALL_STATES[] = {
{"RingIn", SKINNY_RING_IN},
{"Connected", SKINNY_CONNECTED},
{"Busy", SKINNY_BUSY},
{"Congestion", SKINNY_CONGESTION},
{"LineInUse", SKINNY_LINE_IN_USE},
{"Hold", SKINNY_HOLD},
{"CallWaiting", SKINNY_CALL_WAITING},
{"CallTransfer", SKINNY_CALL_TRANSFER},
{"CallPark", SKINNY_CALL_PARK},
{"Proceed", SKINNY_PROCEED},
{"CallRemoteMultiline", SKINNY_CALL_REMOTE_MULTILINE},
{"InUseRemotely", SKINNY_IN_USE_REMOTELY},
{"InvalidNumber", SKINNY_INVALID_NUMBER},
{NULL, 0}
};

View File

@ -84,7 +84,7 @@ uint32_t func(const char *str)\
}
struct skinny_table SKINNY_MESSAGE_TYPES[55];
struct skinny_table SKINNY_MESSAGE_TYPES[56];
const char *skinny_message_type2str(uint32_t id);
uint32_t skinny_str2message_type(const char *str);
#define SKINNY_PUSH_MESSAGE_TYPES SKINNY_DECLARE_PUSH_MATCH(SKINNY_MESSAGE_TYPES)
@ -210,13 +210,13 @@ enum skinny_call_state {
SKINNY_RING_IN = 4,
SKINNY_CONNECTED = 5,
SKINNY_BUSY = 6,
SKINNY_CONGESTION = 7,
SKINNY_LINE_IN_USE = 7,
SKINNY_HOLD = 8,
SKINNY_CALL_WAITING = 9,
SKINNY_CALL_TRANSFER = 10,
SKINNY_CALL_PARK = 11,
SKINNY_PROCEED = 12,
SKINNY_CALL_REMOTE_MULTILINE = 13,
SKINNY_IN_USE_REMOTELY = 13,
SKINNY_INVALID_NUMBER = 14
};
struct skinny_table SKINNY_CALL_STATES[15];