share and share alike, only nothing is alike in sip =/

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@16194 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Anthony Minessale 2010-01-07 06:09:35 +00:00
parent f5d74122bd
commit 478f165d25
5 changed files with 335 additions and 232 deletions

View File

@ -283,6 +283,7 @@ struct mod_sofia_globals {
int guess_mask; int guess_mask;
char guess_mask_str[16]; char guess_mask_str[16];
int debug_presence; int debug_presence;
int debug_sla;
int auto_restart; int auto_restart;
int auto_nat; int auto_nat;
int tracelevel; int tracelevel;

View File

@ -2413,6 +2413,8 @@ switch_status_t config_sofia(int reload, char *profile_name)
su_log_set_level(NULL, atoi(val)); su_log_set_level(NULL, atoi(val));
} else if (!strcasecmp(var, "debug-presence")) { } else if (!strcasecmp(var, "debug-presence")) {
mod_sofia_globals.debug_presence = atoi(val); mod_sofia_globals.debug_presence = atoi(val);
} else if (!strcasecmp(var, "debug-sla")) {
mod_sofia_globals.debug_sla = atoi(val);
} else if (!strcasecmp(var, "auto-restart")) { } else if (!strcasecmp(var, "auto-restart")) {
mod_sofia_globals.auto_restart = switch_true(val); mod_sofia_globals.auto_restart = switch_true(val);
} else if (!strcasecmp(var, "rewrite-multicasted-fs-path")) { } else if (!strcasecmp(var, "rewrite-multicasted-fs-path")) {
@ -2771,6 +2773,7 @@ switch_status_t config_sofia(int reload, char *profile_name)
if (switch_true(val)) { if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE); sofia_set_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE);
profile->pres_type = PRES_TYPE_FULL; profile->pres_type = PRES_TYPE_FULL;
sofia_set_pflag(profile, PFLAG_MULTIREG);
profile->sla_contact = switch_core_sprintf(profile->pool, "sla-agent"); profile->sla_contact = switch_core_sprintf(profile->pool, "sla-agent");
} }
} else if (!strcasecmp(var, "disable-srv")) { } else if (!strcasecmp(var, "disable-srv")) {
@ -3384,6 +3387,8 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip, nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
tagi_t tags[]) tagi_t tags[])
{ {
char *call_info = NULL;
if (sip && session) { if (sip && session) {
switch_channel_t *channel = switch_core_session_get_channel(session); switch_channel_t *channel = switch_core_session_get_channel(session);
const char *uuid; const char *uuid;
@ -3392,8 +3397,6 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status
char network_ip[80]; char network_ip[80];
int network_port = 0; int network_port = 0;
switch_caller_profile_t *caller_profile = NULL; switch_caller_profile_t *caller_profile = NULL;
char *call_info = NULL;
sofia_glue_get_addr(nua_current_request(nua), network_ip, sizeof(network_ip), &network_port); sofia_glue_get_addr(nua_current_request(nua), network_ip, sizeof(network_ip), &network_port);
@ -3406,6 +3409,51 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status
switch_channel_clear_flag(channel, CF_REQ_MEDIA); switch_channel_clear_flag(channel, CF_REQ_MEDIA);
if (sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)) {
if (channel && sip->sip_call_info) {
char *p;
call_info = sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_call_info);
if ((p = strchr(call_info, ';'))) {
switch_channel_set_variable(channel, "presence_call_info", p+1);
}
} else if ((status == 180 || status == 183 || status == 200)) {
char buf[128] = "";
char *sql;
char *state = "active";
if (status != 200) {
state = "progressing";
}
if (sip &&
sip->sip_from && sip->sip_from->a_url && sip->sip_from->a_url->url_user && sip->sip_from->a_url->url_host &&
sip->sip_to && sip->sip_to->a_url && sip->sip_to->a_url->url_user && sip->sip_to->a_url->url_host) {
sql = switch_mprintf("select 'appearance-index=1' from sip_subscriptions where hostname='%q' and event='call-info' and "
"sub_to_user='%q' and sub_to_host='%q'",
mod_sofia_globals.hostname, sip->sip_to->a_url->url_user, sip->sip_from->a_url->url_host);
sofia_glue_execute_sql2str(profile, profile->ireg_mutex, sql, buf, sizeof(buf));
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "QUERY SQL %s [%s]\n", sql, buf);
}
free(sql);
if (!zstr(buf)) {
sql = switch_mprintf("update sip_dialogs set call_info='%q',call_info_state='%q' "
"where uuid='%q'", buf, state,
switch_core_session_get_uuid(session));
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "QUERY SQL %s\n", sql);
}
sofia_glue_actually_execute_sql(profile, sql, profile->ireg_mutex);
switch_safe_free(sql);
}
}
}
}
if ((status == 180 || status == 183 || status == 200)) { if ((status == 180 || status == 183 || status == 200)) {
const char *x_freeswitch_support; const char *x_freeswitch_support;
@ -3443,12 +3491,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status
if (!p_contact) { if (!p_contact) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Missing contact header in redirect request\n"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Missing contact header in redirect request\n");
return; goto end;
}
if (sip->sip_call_info) {
call_info = sip_header_as_string(profile->home, (void *) sip->sip_call_info);
switch_channel_set_variable(channel, "presence_call_info", call_info);
} }
if ((br = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE))) { if ((br = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE))) {
@ -3579,7 +3622,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status
if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
if (!sofia_test_flag(tech_pvt, TFLAG_SENT_UPDATE)) { if (!sofia_test_flag(tech_pvt, TFLAG_SENT_UPDATE)) {
return; goto end;
} }
sofia_clear_flag_locked(tech_pvt, TFLAG_SENT_UPDATE); sofia_clear_flag_locked(tech_pvt, TFLAG_SENT_UPDATE);
@ -3610,7 +3653,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status
switch_core_session_queue_message(other_session, msg); switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session); switch_core_session_rwunlock(other_session);
} }
return; goto end;
} }
if ((status == 180 || status == 183 || status == 200)) { if ((status == 180 || status == 183 || status == 200)) {
@ -3664,11 +3707,15 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status
const char *presence_data = switch_channel_get_variable(channel, "presence_data"); const char *presence_data = switch_channel_get_variable(channel, "presence_data");
const char *presence_id = switch_channel_get_variable(channel, "presence_id"); const char *presence_id = switch_channel_get_variable(channel, "presence_id");
char *full_contact = ""; char *full_contact = "";
char *p = NULL;
if (sip->sip_contact) { if (sip->sip_contact) {
full_contact = sip_header_as_string(nua_handle_home(tech_pvt->nh), (void *) sip->sip_contact); full_contact = sip_header_as_string(nua_handle_home(tech_pvt->nh), (void *) sip->sip_contact);
} }
if (call_info && (p = strchr(call_info, ';'))) {
p++;
}
sql = switch_mprintf("insert into sip_dialogs " sql = switch_mprintf("insert into sip_dialogs "
"(call_id,uuid,sip_to_user,sip_to_host,sip_from_user,sip_from_host,contact_user," "(call_id,uuid,sip_to_user,sip_to_host,sip_from_user,sip_from_host,contact_user,"
"contact_host,state,direction,user_agent,profile_name,hostname,contact,presence_id,presence_data,call_info) " "contact_host,state,direction,user_agent,profile_name,hostname,contact,presence_id,presence_data,call_info) "
@ -3678,18 +3725,13 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status
to_user, to_host, from_user, from_host, contact_user, to_user, to_host, from_user, from_host, contact_user,
contact_host, astate, "outbound", user_agent, contact_host, astate, "outbound", user_agent,
profile->name, mod_sofia_globals.hostname, switch_str_nil(full_contact), profile->name, mod_sofia_globals.hostname, switch_str_nil(full_contact),
switch_str_nil(presence_id), switch_str_nil(presence_data), switch_str_nil(call_info)); switch_str_nil(presence_id), switch_str_nil(presence_data), switch_str_nil(p));
switch_assert(sql); switch_assert(sql);
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
} }
if (call_info) {
su_free(profile->home, call_info);
}
} else if (status == 200 && (profile->pres_type)) { } else if (status == 200 && (profile->pres_type)) {
char *sql = NULL; char *sql = NULL;
const char *presence_data = switch_channel_get_variable(channel, "presence_data"); const char *presence_data = switch_channel_get_variable(channel, "presence_data");
@ -3705,6 +3747,12 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status
} }
end:
if (call_info) {
su_free(nua_handle_home(nh), call_info);
}
if (!session && (status == 180 || status == 183 || status == 200)) { if (!session && (status == 180 || status == 183 || status == 200)) {
/* nevermind */ /* nevermind */
nua_handle_bind(nh, NULL); nua_handle_bind(nh, NULL);
@ -5891,10 +5939,16 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
char *sql; char *sql;
char cid[512] = ""; char cid[512] = "";
char *str; char *str;
char *p = NULL;
if (sip->sip_to && sip->sip_to->a_url) { if (sip->sip_to && sip->sip_to->a_url) {
if ((p = strchr(call_info_str, ';'))) {
p++;
}
sql = switch_mprintf("select call_id from sip_dialogs where call_info='%q' and sip_from_user='%q' and sip_from_host='%q'", sql = switch_mprintf("select call_id from sip_dialogs where call_info='%q' and sip_from_user='%q' and sip_from_host='%q'",
call_info_str, sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host); switch_str_nil(p), sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
if ((str = sofia_glue_execute_sql2str(profile, profile->ireg_mutex, sql, cid, sizeof(cid)))) { if ((str = sofia_glue_execute_sql2str(profile, profile->ireg_mutex, sql, cid, sizeof(cid)))) {
bnh = nua_handle_by_call_id(nua, str); bnh = nua_handle_by_call_id(nua, str);
@ -5930,6 +5984,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
} }
if ((uuid = switch_channel_get_variable(b_channel, SWITCH_SIGNAL_BOND_VARIABLE))) { if ((uuid = switch_channel_get_variable(b_channel, SWITCH_SIGNAL_BOND_VARIABLE))) {
switch_channel_set_variable(b_channel, "presence_call_info", NULL);
one_leg = 0; one_leg = 0;
} else { } else {
uuid = switch_core_session_get_uuid(b_session); uuid = switch_core_session_get_uuid(b_session);
@ -5952,7 +6007,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
if (!one_leg && if (!one_leg &&
(!b_tech_pvt || !sofia_test_flag(b_tech_pvt, TFLAG_SIP_HOLD)) && (!b_tech_pvt || !sofia_test_flag(b_tech_pvt, TFLAG_SIP_HOLD)) &&
(!c_tech_pvt || !sofia_test_flag(c_tech_pvt, TFLAG_SIP_HOLD))) { (!c_tech_pvt || !sofia_test_flag(c_tech_pvt, TFLAG_SIP_HOLD))) {
char *ext = switch_core_session_sprintf(b_session, "conference:%s@sla+flags{mintwo}", uuid); char *ext = switch_core_session_sprintf(b_session, "answer,conference:%s@sla+flags{mintwo}", uuid);
switch_channel_set_flag(c_channel, CF_REDIRECT); switch_channel_set_flag(c_channel, CF_REDIRECT);
switch_ivr_session_transfer(b_session, ext, "inline", NULL); switch_ivr_session_transfer(b_session, ext, "inline", NULL);
@ -6117,13 +6172,18 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
const char *presence_data = switch_channel_get_variable(channel, "presence_data"); const char *presence_data = switch_channel_get_variable(channel, "presence_data");
const char *presence_id = switch_channel_get_variable(channel, "presence_id"); const char *presence_id = switch_channel_get_variable(channel, "presence_id");
char *full_contact = ""; char *full_contact = "";
char *p = NULL;
if (sip->sip_contact) { if (sip->sip_contact) {
full_contact = sip_header_as_string(nua_handle_home(tech_pvt->nh), (void *) sip->sip_contact); full_contact = sip_header_as_string(nua_handle_home(tech_pvt->nh), (void *) sip->sip_contact);
} }
if (call_info_str) { if (call_info_str) {
switch_channel_set_variable(channel, "presence_call_info", call_info_str); if ((p = strchr(call_info_str, ';'))) {
p++;
switch_channel_set_variable(channel, "presence_call_info", p);
}
} }
@ -6136,7 +6196,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
to_user, to_host, dialog_from_user, dialog_from_host, to_user, to_host, dialog_from_user, dialog_from_host,
contact_user, contact_host, "confirmed", "inbound", user_agent, contact_user, contact_host, "confirmed", "inbound", user_agent,
profile->name, mod_sofia_globals.hostname, switch_str_nil(full_contact), profile->name, mod_sofia_globals.hostname, switch_str_nil(full_contact),
switch_str_nil(presence_id), switch_str_nil(presence_data), switch_str_nil(call_info_str)); switch_str_nil(presence_id), switch_str_nil(presence_data), switch_str_nil(p));
switch_assert(sql); switch_assert(sql);

View File

@ -3831,7 +3831,8 @@ int sofia_glue_init_sql(sofia_profile_t *profile)
" contact VARCHAR(255),\n" " contact VARCHAR(255),\n"
" presence_id VARCHAR(255),\n" " presence_id VARCHAR(255),\n"
" presence_data VARCHAR(255),\n" " presence_data VARCHAR(255),\n"
" call_info VARCHAR(255)\n" " call_info VARCHAR(255),\n"
" call_info_state VARCHAR(255)\n"
");\n"; ");\n";
char sub_sql[] = char sub_sql[] =
@ -3917,6 +3918,7 @@ int sofia_glue_init_sql(sofia_profile_t *profile)
"create index sd_hostname on sip_dialogs (hostname)", "create index sd_hostname on sip_dialogs (hostname)",
"create index sd_presence_data on sip_dialogs (presence_data)", "create index sd_presence_data on sip_dialogs (presence_data)",
"create index sd_call_info on sip_dialogs (call_info)", "create index sd_call_info on sip_dialogs (call_info)",
"create index sd_call_info on sip_dialogs (call_info_state)",
"create index sp_hostname on sip_presence (hostname)", "create index sp_hostname on sip_presence (hostname)",
"create index sa_nonce on sip_authentication (nonce)", "create index sa_nonce on sip_authentication (nonce)",
"create index sa_hostname on sip_authentication (hostname)", "create index sa_hostname on sip_authentication (hostname)",
@ -3986,7 +3988,7 @@ int sofia_glue_init_sql(sofia_profile_t *profile)
} }
free(test_sql); free(test_sql);
test_sql = switch_mprintf("delete from sip_dialogs where hostname='%q' and call_info like '%%'", mod_sofia_globals.hostname); test_sql = switch_mprintf("delete from sip_dialogs where hostname='%q' and call_info_state like '%%'", mod_sofia_globals.hostname);
if (switch_odbc_handle_exec(odbc_dbh, test_sql, NULL) != SWITCH_ODBC_SUCCESS) { if (switch_odbc_handle_exec(odbc_dbh, test_sql, NULL) != SWITCH_ODBC_SUCCESS) {
switch_odbc_handle_exec(odbc_dbh, "DROP TABLE sip_dialogs", NULL); switch_odbc_handle_exec(odbc_dbh, "DROP TABLE sip_dialogs", NULL);
@ -4053,7 +4055,7 @@ int sofia_glue_init_sql(sofia_profile_t *profile)
switch_core_db_test_reactive(db, test_sql, "DROP TABLE sip_subscriptions", sub_sql); switch_core_db_test_reactive(db, test_sql, "DROP TABLE sip_subscriptions", sub_sql);
free(test_sql); free(test_sql);
test_sql = switch_mprintf("delete from sip_dialogs where hostname='%q' and call_info like '%%'", mod_sofia_globals.hostname); test_sql = switch_mprintf("delete from sip_dialogs where hostname='%q' and call_info_state like '%%'", mod_sofia_globals.hostname);
switch_core_db_test_reactive(db, test_sql, "DROP TABLE sip_dialogs", dialog_sql); switch_core_db_test_reactive(db, test_sql, "DROP TABLE sip_dialogs", dialog_sql);
free(test_sql); free(test_sql);
@ -4131,6 +4133,7 @@ int sofia_glue_init_sql(sofia_profile_t *profile)
switch_core_db_exec(db, "create index if not exists sd_presence_id on sip_dialogs (presence_id)", NULL, NULL, NULL); switch_core_db_exec(db, "create index if not exists sd_presence_id on sip_dialogs (presence_id)", NULL, NULL, NULL);
switch_core_db_exec(db, "create index if not exists sd_presence_data on sip_dialogs (presence_data)", NULL, NULL, NULL); switch_core_db_exec(db, "create index if not exists sd_presence_data on sip_dialogs (presence_data)", NULL, NULL, NULL);
switch_core_db_exec(db, "create index if not exists sd_call_info on sip_dialogs (call_info)", NULL, NULL, NULL); switch_core_db_exec(db, "create index if not exists sd_call_info on sip_dialogs (call_info)", NULL, NULL, NULL);
switch_core_db_exec(db, "create index if not exists sd_call_info on sip_dialogs (call_info_state)", NULL, NULL, NULL);
switch_core_db_exec(db, "create index if not exists sp_hostname on sip_presence (hostname)", NULL, NULL, NULL); switch_core_db_exec(db, "create index if not exists sp_hostname on sip_presence (hostname)", NULL, NULL, NULL);

View File

@ -35,13 +35,21 @@
#include "mod_sofia.h" #include "mod_sofia.h"
#define SUB_OVERLAP 300 #define SUB_OVERLAP 300
struct state_helper {
switch_hash_t *hash;
sofia_profile_t *profile;
switch_memory_pool_t *pool;
};
static int sofia_presence_mwi_callback(void *pArg, int argc, char **argv, char **columnNames); static int sofia_presence_mwi_callback(void *pArg, int argc, char **argv, char **columnNames);
static int sofia_presence_mwi_callback2(void *pArg, int argc, char **argv, char **columnNames); static int sofia_presence_mwi_callback2(void *pArg, int argc, char **argv, char **columnNames);
static int sofia_presence_sub_reg_callback(void *pArg, int argc, char **argv, char **columnNames); static int sofia_presence_sub_reg_callback(void *pArg, int argc, char **argv, char **columnNames);
static int sofia_presence_resub_callback(void *pArg, int argc, char **argv, char **columnNames); static int sofia_presence_resub_callback(void *pArg, int argc, char **argv, char **columnNames);
static int sofia_presence_sub_callback(void *pArg, int argc, char **argv, char **columnNames); static int sofia_presence_sub_callback(void *pArg, int argc, char **argv, char **columnNames);
static int broadsoft_sla_callback(void *pArg, int argc, char **argv, char **columnNames); static int broadsoft_sla_gather_state_callback(void *pArg, int argc, char **argv, char **columnNames);
static int broadsoft_sla_notify_callback(void *pArg, int argc, char **argv, char **columnNames);
static void sync_sla(sofia_profile_t *profile, const char *to_user, const char *to_host, switch_bool_t clear, switch_bool_t unseize);
struct resub_helper { struct resub_helper {
sofia_profile_t *profile; sofia_profile_t *profile;
@ -589,51 +597,29 @@ static void actual_sofia_presence_event_handler(switch_event_t *event)
} }
if (call_info) { if (call_info) {
sql = switch_mprintf("select sip_subscriptions.call_id,sip_subscriptions.expires,'%q',3, sip_dialogs.state,sip_dialogs.call_info, "
"sip_subscriptions.sub_to_host,'%q',event "
"from sip_subscriptions left join sip_dialogs on sip_subscriptions.sub_to_user=sip_dialogs.sip_from_user "
"and sip_subscriptions.sub_to_host=sip_dialogs.sip_from_host "
"where sip_subscriptions.hostname='%q' "
"and sub_to_user='%q' and sub_to_host='%q' "
"and (event='call-info' or event='line-seize') and sip_dialogs.call_info='%q'",
call_info,
call_info_state,
mod_sofia_globals.hostname,
euser,
host,
call_info
);
if (mod_sofia_globals.debug_presence > 1) { #if 0
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PRES SQL %s\n", sql); if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SLA EVENT:\n");
DUMP_EVENT(event);
} }
sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, broadsoft_sla_callback, profile); #endif
switch_safe_free(sql);
sql = switch_mprintf("select sip_subscriptions.call_id,sip_subscriptions.expires,'',3, sip_dialogs.state,sip_dialogs.call_info, "
"sip_subscriptions.sub_to_host,'',event "
"from sip_subscriptions inner join sip_dialogs on sip_subscriptions.sub_to_user=sip_dialogs.sip_from_user "
"and sip_subscriptions.sub_to_host=sip_dialogs.sip_from_host "
"where sip_subscriptions.hostname='%q' "
"and sub_to_user='%q' and sub_to_host='%q' "
"and event='call-info' and sip_dialogs.call_info!='%q'",
mod_sofia_globals.hostname,
euser,
host,
call_info
);
if (mod_sofia_globals.debug_presence > 1) { sql = switch_mprintf("update sip_dialogs set call_info_state='%q' where hostname='%q' and sip_dialogs.sip_from_user='%q' "
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,"PRES SQL %s\n", sql); "and sip_dialogs.sip_from_host='%q' and call_info='%q'",
call_info_state,
mod_sofia_globals.hostname,
euser, host, call_info);
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "STATE SQL %s\n", sql);
} }
sofia_glue_actually_execute_sql(profile, sql, profile->ireg_mutex);
sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, broadsoft_sla_callback, profile);
switch_safe_free(sql); switch_safe_free(sql);
sync_sla(profile, euser, host, SWITCH_TRUE, SWITCH_TRUE);
} }
if ((sql = switch_mprintf( if ((sql = switch_mprintf(
@ -1463,46 +1449,29 @@ static int sofia_presence_mwi_callback2(void *pArg, int argc, char **argv, char
return 0; return 0;
} }
static int broadsoft_sla_callback(void *pArg, int argc, char **argv, char **columnNames) static int broadsoft_sla_notify_callback(void *pArg, int argc, char **argv, char **columnNames)
{ {
struct state_helper *sh = (struct state_helper *) pArg;
char key[256] = "";
char *data = NULL, *tmp;
char *call_id = argv[0]; char *call_id = argv[0];
char *expires = argv[1]; char *expires = argv[1];
char *header = argv[2]; char *user = argv[2];
char *onoff = argv[3]; char *host = argv[3];
char *state = NULL; char *event = argv[4];
char *call_info = NULL; int i;
char *call_info_state = NULL;
char *event = NULL;
char *host = NULL;
sofia_profile_t *profile = (sofia_profile_t *) pArg;
nua_handle_t *nh;
char sstr[128] = "", expires_str[128] = ""; char sstr[128] = "", expires_str[128] = "";
time_t exptime = 3600; time_t exptime = 3600;
int on = atoi(onoff); nua_handle_t *nh;
char buf[512] = "";
const char *r_state = "idle";
int i;
if (mod_sofia_globals.debug_presence > 1) { if (mod_sofia_globals.debug_sla > 1) {
for(i = 0; i < argc; i++) { for(i = 0; i < argc; i++) {
printf("COL [%s]=[%s]\n", columnNames[i], argv[i]); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,"SLA3: %d [%s]=[%s]\n", i, columnNames[i], argv[i]);
} }
printf("\non=%d\n\n", on);
} }
if (on >= 2 && argc >= 7) { switch_snprintf(key, sizeof(key), "%s%s", user, host);
state = argv[4]; data = switch_core_hash_find(sh->hash, key);
call_info = argv[5];
host = argv[6];
call_info_state = argv[7];
if (argc > 8) event = argv[8];
} else {
call_info = header;
}
if (zstr(event)) {
event = "call-info";
}
if (expires) { if (expires) {
long tmp = atol(expires); long tmp = atol(expires);
@ -1514,63 +1483,160 @@ static int broadsoft_sla_callback(void *pArg, int argc, char **argv, char **colu
} else { } else {
switch_snprintf(sstr, sizeof(sstr), "terminated;reason=noresource"); switch_snprintf(sstr, sizeof(sstr), "terminated;reason=noresource");
} }
switch_snprintf(expires_str, sizeof(expires_str), "%u", (unsigned)exptime); switch_snprintf(expires_str, sizeof(expires_str), "%u", (unsigned)exptime);
if (zstr(call_info)) { data = switch_core_hash_find(sh->hash, key);
call_info = header;
}
if (on == 3) { if (data) {
if (!zstr(call_info_state)) { tmp = switch_core_sprintf(sh->pool, "%s,<sip:%s>;appearance-index=*;appearance-state=idle", data, host);
r_state = call_info_state; } else {
} else if (!zstr(argv[4])) { tmp = switch_core_sprintf(sh->pool, "<sip:%s>;appearance-index=*;appearance-state=idle", host);
r_state = "active";
} else {
r_state = "idle";
}
} else if (on) {
r_state = "seized";
} }
if (!strcasecmp(event, "line-seize") && (nh = nua_handle_by_call_id(sh->profile->nua, call_id))) {
char *hack;
if (zstr(call_info)) { if ((hack = (char *)switch_stristr("=seized", tmp))) {
switch_snprintf(buf, sizeof(buf), "<sip:%s>;apperance-index=*", host); switch_snprintf(hack, 7, "=idle ");
call_info = buf; }
nua_notify(nh,
SIPTAG_EXPIRES_STR("0"),
SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"),
SIPTAG_EVENT_STR("line-seize"),
SIPTAG_CALL_INFO_STR(tmp),
TAG_END());
return 0;
} }
if (mod_sofia_globals.debug_presence > 1) { if (!strcasecmp(event, "call-info") && (nh = nua_handle_by_call_id(sh->profile->nua, call_id))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[%s][%s][%s][%s]\n", event, call_info, call_id, r_state);
}
if (!strcasecmp(event, "line-seize") && (nh = nua_handle_by_call_id(profile->nua, call_id))) {
if (strcasecmp(r_state, "seized")) {
char *new_header = switch_mprintf("%s;appearance-state=%s", call_info, r_state);
nua_notify(nh,
SIPTAG_EXPIRES_STR("0"),
SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"),
SIPTAG_EVENT_STR("line-seize"), SIPTAG_CALL_INFO_STR(new_header), TAG_END());
switch_safe_free(new_header);
}
return 0;
}
if (profile && call_id && call_info && (nh = nua_handle_by_call_id(profile->nua, call_id))) {
char *new_header = switch_mprintf("%s;appearance-state=%s", call_info, r_state);
nua_notify(nh, nua_notify(nh,
TAG_IF(*expires_str, SIPTAG_EXPIRES_STR(expires_str)), TAG_IF(*expires_str, SIPTAG_EXPIRES_STR(expires_str)),
SIPTAG_SUBSCRIPTION_STATE_STR(sstr), SIPTAG_SUBSCRIPTION_STATE_STR(sstr),
SIPTAG_EVENT_STR("call-info"), SIPTAG_CALL_INFO_STR(new_header), TAG_END()); SIPTAG_EVENT_STR("call-info"), SIPTAG_CALL_INFO_STR(tmp), TAG_END());
switch_safe_free(new_header);
} }
return 0; return 0;
} }
static int broadsoft_sla_gather_state_callback(void *pArg, int argc, char **argv, char **columnNames)
{
struct state_helper *sh = (struct state_helper *) pArg;
char key[256] = "";
char *data = NULL, *tmp;
char *user = argv[0];
char *host = argv[1];
char *info = argv[2];
char *state = argv[3];
int i;
if (mod_sofia_globals.debug_sla > 1) {
for(i = 0; i < argc; i++) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,"SLA2: %d [%s]=[%s]\n", i, columnNames[i], argv[i]);
}
}
if (zstr(info)) {
return 0;
}
if (zstr(state)) {
state = "idle";
}
switch_snprintf(key, sizeof(key), "%s%s", user, host);
data = switch_core_hash_find(sh->hash, key);
if (data) {
tmp = switch_core_sprintf(sh->pool, "%s,<sip:%s>;%s;appearance-state=%s", data, host, info, state);
} else {
tmp = switch_core_sprintf(sh->pool, "<sip:%s>;%s;appearance-state=%s", host, info, state);
}
switch_core_hash_insert(sh->hash, key, tmp);
return 0;
}
static void sync_sla(sofia_profile_t *profile, const char *to_user, const char *to_host, switch_bool_t clear, switch_bool_t unseize)
{
struct state_helper *sh;
switch_memory_pool_t *pool;
char *sql;
switch_core_new_memory_pool(&pool);
sh = switch_core_alloc(pool, sizeof(*sh));
sh->pool = pool;
switch_core_hash_init(&sh->hash, sh->pool);
sql = switch_mprintf("select sip_from_user,sip_from_host,call_info,call_info_state from sip_dialogs "
"where hostname='%q' "
"and sip_from_user='%q' and sip_from_host='%q' ",
mod_sofia_globals.hostname,
to_user,
to_host
);
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PRES SQL %s\n", sql);
}
sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, broadsoft_sla_gather_state_callback, sh);
switch_safe_free(sql);
if (unseize) {
sql = switch_mprintf("select call_id,expires,sub_to_user,sub_to_host,event "
"from sip_subscriptions "
"where hostname='%q' "
"and sub_to_user='%q' and sub_to_host='%q' "
"and (event='call-info' or event='line-seize')",
mod_sofia_globals.hostname,
to_user,
to_host
);
} else {
sql = switch_mprintf("select call_id,expires,sub_to_user,sub_to_host,event "
"from sip_subscriptions "
"where hostname='%q' "
"and sub_to_user='%q' and sub_to_host='%q' "
"and (event='call-info')",
mod_sofia_globals.hostname,
to_user,
to_host
);
}
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PRES SQL %s\n", sql);
}
sh->profile = profile;
sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, broadsoft_sla_notify_callback, sh);
switch_safe_free(sql);
sh = NULL;
switch_core_destroy_memory_pool(&pool);
if (clear) {
sql = switch_mprintf("delete from sip_dialogs where sip_from_user='%q' and sip_from_host='%q' and call_info_state='seized'",
to_user,
to_host
);
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CLEAR SQL %s\n", sql);
}
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
}
}
void sofia_presence_handle_sip_i_subscribe(int status, void sofia_presence_handle_sip_i_subscribe(int status,
char const *phrase, char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip, nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
@ -1758,7 +1824,7 @@ void sofia_presence_handle_sip_i_subscribe(int status,
if ((p = strchr(protocol, '+'))) { if ((p = strchr(protocol, '+'))) {
*p = '\0'; *p = '\0';
} }
if (switch_event_create(&sevent, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { if (switch_event_create(&sevent, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "proto", protocol); switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "proto", protocol);
switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "login", profile->name); switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "login", profile->name);
@ -1831,7 +1897,8 @@ void sofia_presence_handle_sip_i_subscribe(int status,
switch_mutex_lock(profile->ireg_mutex); switch_mutex_lock(profile->ireg_mutex);
switch_assert(sql != NULL); switch_assert(sql != NULL);
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); sofia_glue_actually_execute_sql(profile, sql, NULL);
switch_safe_free(sql);
if (sub_state == nua_substate_terminated) { if (sub_state == nua_substate_terminated) {
sstr = switch_mprintf("terminated"); sstr = switch_mprintf("terminated");
@ -1843,7 +1910,7 @@ void sofia_presence_handle_sip_i_subscribe(int status,
switch_snprintf(accept + strlen(accept), sizeof(accept) - strlen(accept), "%s%s ", ap->ac_type, ap->ac_next ? "," : ""); switch_snprintf(accept + strlen(accept), sizeof(accept) - strlen(accept), "%s%s ", ap->ac_type, ap->ac_next ? "," : "");
ap = ap->ac_next; ap = ap->ac_next;
} }
/* negative in exptime means keep bumping up sub time to avoid a snafu where every device has it's own rules about subscriptions /* negative in exptime means keep bumping up sub time to avoid a snafu where every device has it's own rules about subscriptions
that somehow barely resemble the RFC not that I blame them because the RFC MAY be amibiguous and SHOULD be deleted. that somehow barely resemble the RFC not that I blame them because the RFC MAY be amibiguous and SHOULD be deleted.
So to avoid the problem we keep resetting the expiration date of the subscription so it never expires. So to avoid the problem we keep resetting the expiration date of the subscription so it never expires.
@ -1865,13 +1932,17 @@ void sofia_presence_handle_sip_i_subscribe(int status,
(long)switch_epoch_time_now(NULL) + exp_delta, (long)switch_epoch_time_now(NULL) + exp_delta,
full_agent, accept, profile->name,mod_sofia_globals.hostname, network_port, network_ip); full_agent, accept, profile->name,mod_sofia_globals.hostname, network_port, network_ip);
if (mod_sofia_globals.debug_presence > 0) { switch_assert(sql != NULL);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "%s SUBSCRIBE %s@%s %s@%s\n", profile->name, from_user, from_host, to_user, to_host);
if (mod_sofia_globals.debug_presence > 0 || mod_sofia_globals.debug_sla > 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "%s SUBSCRIBE %s@%s %s@%s\n%s\n",
profile->name, from_user, from_host, to_user, to_host, sql);
} }
switch_assert(sql != NULL);
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
sofia_glue_actually_execute_sql(profile, sql, profile->ireg_mutex);
switch_safe_free(sql);
sstr = switch_mprintf("active;expires=%ld", exp_delta); sstr = switch_mprintf("active;expires=%ld", exp_delta);
} }
@ -1939,79 +2010,82 @@ void sofia_presence_handle_sip_i_subscribe(int status,
if (sub_state == nua_substate_terminated) { if (sub_state == nua_substate_terminated) {
char *full_call_info = NULL; char *full_call_info = NULL;
char *p = NULL;
if (sip->sip_call_info) { if (sip->sip_call_info) {
full_call_info = sip_header_as_string(profile->home, (void *) sip->sip_call_info); full_call_info = sip_header_as_string(profile->home, (void *) sip->sip_call_info);
} if ((p = strchr(full_call_info, ';'))) {
p++;
}
nua_notify(nh, nua_notify(nh,
SIPTAG_EXPIRES_STR("0"), SIPTAG_EXPIRES_STR("0"),
SIPTAG_SUBSCRIPTION_STATE_STR(sstr), SIPTAG_SUBSCRIPTION_STATE_STR(sstr),
TAG_IF(full_call_info, SIPTAG_CALL_INFO_STR(full_call_info)), TAG_IF(full_call_info, SIPTAG_CALL_INFO_STR(full_call_info)),
TAG_END()); TAG_END());
if (!strcasecmp(event, "line-seize")) { if (!strcasecmp(event, "line-seize")) {
if (full_call_info) { sync_sla(profile, to_user, to_host, SWITCH_FALSE, SWITCH_TRUE);
sql = switch_mprintf("select call_id,expires,'%q',0 from sip_subscriptions where hostname='%q' "
#if 0
sql = switch_mprintf("select call_id,expires,'%q',0,sub_to_host from sip_subscriptions where hostname='%q' "
"and sub_to_user='%q' and sub_to_host='%q' " "and sub_to_user='%q' and sub_to_host='%q' "
"and event='call-info' and contact != '%q'", "and event='call-info' ",
full_call_info, switch_str_nil(p),
mod_sofia_globals.hostname, mod_sofia_globals.hostname,
to_user, to_user,
to_host, to_host
contact_str
); );
if (mod_sofia_globals.debug_presence > 1) { if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "UNSEIZE SQL %s\n", sql); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "UNSEIZE SQL %s\n", sql);
} }
sofia_glue_execute_sql_callback(profile, NULL, sql, broadsoft_sla_callback, profile); sofia_glue_execute_sql_callback(profile, NULL, sql, broadsoft_sla_callback, profile);
switch_safe_free(sql); switch_safe_free(sql);
#endif
} }
}
if (full_call_info) {
su_free(profile->home, full_call_info); su_free(profile->home, full_call_info);
}
}
} else { } else {
if (!strcasecmp(event, "line-seize")) { if (!strcasecmp(event, "line-seize")) {
char *full_call_info = NULL; char *full_call_info = NULL;
char *p;
if (sip->sip_call_info) { if (sip->sip_call_info) {
full_call_info = sip_header_as_string(profile->home, (void *) sip->sip_call_info); full_call_info = sip_header_as_string(profile->home, (void *) sip->sip_call_info);
} if ((p = strchr(full_call_info, ';'))) {
p++;
nua_notify(nh,
SIPTAG_EXPIRES_STR(exp_delta_str),
SIPTAG_SUBSCRIPTION_STATE_STR(sstr),
SIPTAG_EVENT_STR("line-seize"), SIPTAG_CALL_INFO_STR(full_call_info), TAG_END());
if (full_call_info) {
sql = switch_mprintf("select sip_subscriptions.call_id,sip_subscriptions.expires,'%q',4, sip_dialogs.state,sip_dialogs.call_info, "
"sip_subscriptions.sub_to_host,'','call-info' "
"from sip_subscriptions left join sip_dialogs on sip_subscriptions.sub_to_user=sip_dialogs.sip_from_user "
"and sip_subscriptions.sub_to_host=sip_dialogs.sip_from_host "
"where sip_subscriptions.hostname='%q' "
"and sub_to_user='%q' and sub_to_host='%q' "
"and event='call-info'and sip_subscriptions.contact != '%q'",
full_call_info,
mod_sofia_globals.hostname,
to_user,
to_host,
contact_str
);
if (mod_sofia_globals.debug_presence > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SEIZE SQL %s\n", sql);
} }
sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, broadsoft_sla_callback, profile); nua_notify(nh,
SIPTAG_EXPIRES_STR(exp_delta_str),
SIPTAG_SUBSCRIPTION_STATE_STR(sstr),
SIPTAG_EVENT_STR("line-seize"),
TAG_IF(full_call_info, SIPTAG_CALL_INFO_STR(full_call_info)),
TAG_END());
sql = switch_mprintf("insert into sip_dialogs (sip_from_user,sip_from_host,call_info,call_info_state,hostname) "
"values ('%q','%q','%q','seized','%q')",
to_user,
to_host,
switch_str_nil(p),
mod_sofia_globals.hostname
);
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SEIZE SQL %s\n", sql);
}
sofia_glue_actually_execute_sql(profile, sql, profile->ireg_mutex);
switch_safe_free(sql); switch_safe_free(sql);
sync_sla(profile, to_user, to_host, SWITCH_TRUE, SWITCH_FALSE);
su_free(profile->home, full_call_info); su_free(profile->home, full_call_info);
} }
} }
@ -2019,40 +2093,7 @@ void sofia_presence_handle_sip_i_subscribe(int status,
if (!strcasecmp(event, "call-info")) { if (!strcasecmp(event, "call-info")) {
sync_sla(profile, to_user, to_host, SWITCH_FALSE, SWITCH_FALSE);
#if 0
int x = 0;
for (x = 1; x < 5; x++) {
char tmp[128] = "";
switch_snprintf(tmp, sizeof(tmp), "<sip:%s>;apperance-index=%d;appearance-state=idle", to_host, x);
nua_notify(nh,
SIPTAG_EXPIRES_STR(exp_delta_str),
SIPTAG_SUBSCRIPTION_STATE_STR(sstr),
SIPTAG_EVENT_STR("call-info"), SIPTAG_CALL_INFO_STR(tmp), TAG_END());
}
#endif
sql = switch_mprintf("select sip_subscriptions.call_id,sip_subscriptions.expires,'',2, sip_dialogs.state,sip_dialogs.call_info, "
"sip_subscriptions.sub_to_host "
"from sip_subscriptions inner join sip_dialogs on sip_subscriptions.sub_to_user=sip_dialogs.sip_from_user "
"and sip_subscriptions.sub_to_host=sip_dialogs.sip_from_host "
"where sip_subscriptions.hostname='%q' "
"and sub_to_user='%q' and sub_to_host='%q' "
"and event='call-info' and sip_subscriptions.contact != '%q'",
mod_sofia_globals.hostname,
to_user,
to_host,
contact_str
);
if (mod_sofia_globals.debug_presence > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CALL-INFO SQL %s\n", sql);
}
sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, broadsoft_sla_callback, profile);
switch_safe_free(sql);
} }
} }
@ -2061,21 +2102,14 @@ void sofia_presence_handle_sip_i_subscribe(int status,
sent_reply++; sent_reply++;
#if 0
nua_notify(nh,
SIPTAG_SUBSCRIPTION_STATE_STR(sstr), SIPTAG_EVENT_STR(event),
SIPTAG_EXPIRES_STR(exp_delta_str),
SIPTAG_CONTENT_TYPE_STR("application/notice"),
SIPTAG_PAYLOAD_STR("Note: Come to ClueCon http://www.cluecon.com\n\n"), TAG_END());
#endif
switch_safe_free(sstr); switch_safe_free(sstr);
if (!strcasecmp(event, "message-summary")) { if (!strcasecmp(event, "message-summary")) {
if ((sql = switch_mprintf( if ((sql = switch_mprintf(
"select proto,sip_user,'%q',sub_to_user,sub_to_host,event,contact,call_id,full_from," "select proto,sip_user,'%q',sub_to_user,sub_to_host,event,contact,call_id,full_from,"
"full_via,expires,user_agent,accept,profile_name,network_ip" "full_via,expires,user_agent,accept,profile_name,network_ip"
" from sip_subscriptions where event='message-summary' and sip_user='%q' and (sip_host='%q' or presence_hosts like '%%%q%%')", " from sip_subscriptions where event='message-summary' and sip_user='%q' "
"and (sip_host='%q' or presence_hosts like '%%%q%%')",
to_host, to_user, to_host, to_host))) { to_host, to_user, to_host, to_host))) {
sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sofia_presence_sub_reg_callback, profile); sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sofia_presence_sub_reg_callback, profile);

View File

@ -512,6 +512,7 @@ SWITCH_DECLARE(void) switch_channel_presence(switch_channel_t *channel, const ch
call_info_state = "alerting"; call_info_state = "alerting";
} }
} }
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-info-state", call_info_state); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-info-state", call_info_state);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-info", call_info); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-info", call_info);
} }
@ -1920,6 +1921,10 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_hangup(switch_chan
switch_channel_state_t last_state; switch_channel_state_t last_state;
switch_event_t *event; switch_event_t *event;
if (hangup_cause == SWITCH_CAUSE_LOSE_RACE) {
switch_channel_set_variable(channel, "presence_call_info", NULL);
}
switch_mutex_lock(channel->state_mutex); switch_mutex_lock(channel->state_mutex);
last_state = channel->state; last_state = channel->state;
channel->state = CS_HANGUP; channel->state = CS_HANGUP;