diff --git a/conf/vanilla/dialplan/default.xml b/conf/vanilla/dialplan/default.xml
index d4ae8e711a..a6e26c1928 100644
--- a/conf/vanilla/dialplan/default.xml
+++ b/conf/vanilla/dialplan/default.xml
@@ -828,5 +828,13 @@
+
+
+
+
+
+
+
+
diff --git a/conf/vanilla/sip_profiles/internal.xml b/conf/vanilla/sip_profiles/internal.xml
index 1529248097..1da1e0470c 100644
--- a/conf/vanilla/sip_profiles/internal.xml
+++ b/conf/vanilla/sip_profiles/internal.xml
@@ -418,5 +418,9 @@
-->
+
+
diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/nta.c b/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
index bb68025749..9b2e295007 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
@@ -6460,8 +6460,14 @@ static int nta_incoming_response_headers(nta_incoming_t *irq,
clone = 1, sip->sip_call_id = sip_call_id_copy(home, irq->irq_call_id);
if (!sip->sip_cseq)
clone = 1, sip->sip_cseq = sip_cseq_copy(home, irq->irq_cseq);
- if (!sip->sip_via)
- clone = 1, sip->sip_via = sip_via_copy(home, irq->irq_via);
+ if (!sip->sip_via) {
+ clone = 1;
+ /* 100 responses are not forwarded by proxies, so only include the topmost Via header */
+ if (sip->sip_status && sip->sip_status->st_status == 100)
+ sip->sip_via = (sip_via_t *)msg_header_copy_one(home, (msg_header_t const *)irq->irq_via);
+ else
+ sip->sip_via = sip_via_copy(home, irq->irq_via);
+ }
if (clone)
msg_set_parent(msg, (msg_t *)irq->irq_home);
@@ -6530,7 +6536,7 @@ int nta_incoming_complete_response(nta_incoming_t *irq,
if (sip_to_tag(home, sip->sip_to, irq->irq_tag) < 0)
return -1;
- if (status < 300 && !sip->sip_record_route && irq->irq_record_route)
+ if (status > 100 && status < 300 && !sip->sip_record_route && irq->irq_record_route)
if (sip_add_dup(msg, sip, (sip_header_t *)irq->irq_record_route) < 0)
return -1;
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c
index 8ff2684881..2d21acbaa2 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c
@@ -173,6 +173,8 @@ int nua_stack_set_defaults(nua_handle_t *nh,
NHP_SET(nhp, keepalive, 120000);
+ NHP_SET(nhp, auto_invite_100, 1);
+
NHP_SET(nhp, appl_method,
sip_allow_make(home, "INVITE, REGISTER, PUBLISH, SUBSCRIBE"));
@@ -1013,6 +1015,10 @@ static int nhp_set_tags(su_home_t *home,
else if (tag == ntatag_default_proxy) {
NHP_SET_STR_BY_URL(nhp, url_string_t, proxy, value);
}
+ /* NUTAG_AUTO_INVITE_100() */
+ else if (tag == nutag_auto_invite_100) {
+ NHP_SET(nhp, auto_invite_100, value != 0);
+ }
/* NUTAG_DETECT_NETWORK_UPDATES(detect_network_updates) */
else if (ngp && tag == nutag_detect_network_updates) {
int detector = (int)value;
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.h b/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.h
index 5c26e4ef13..d48ceca155 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.h
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.h
@@ -115,6 +115,9 @@ struct nua_handle_preferences
/** Enable Retry-After */
unsigned nhp_retry_after_enable:1;
+ /** Enable/Disable automatic 100 Trying when receiving INVITE */
+ unsigned nhp_auto_invite_100:1;
+
unsigned:0;
/* Default lifetime for implicit subscriptions created by REFER */
@@ -215,6 +218,7 @@ struct nua_handle_preferences
unsigned nhb_proxy:1;
unsigned nhb_timer_autorequire:1;
unsigned nhb_retry_after_enable:1;
+ unsigned nhb_auto_invite_100:1;
unsigned :0;
} set_bits;
unsigned set_unsigned[2];
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_server.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_server.c
index deae102ca2..cf8e23577d 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_server.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_server.c
@@ -262,7 +262,8 @@ int nua_stack_process_request(nua_handle_t *nh,
if (sr->sr_status <= 100) {
SR_STATUS1(sr, SIP_100_TRYING);
- if (method == sip_method_invite || sip->sip_timestamp) {
+ if ((method == sip_method_invite && nh->nh_prefs->nhp_auto_invite_100) ||
+ sip->sip_timestamp) {
nta_incoming_treply(irq, SIP_100_TRYING,
SIPTAG_USER_AGENT_STR(user_agent),
TAG_END());
@@ -459,7 +460,12 @@ nua_stack_respond(nua_t *nua, nua_handle_t *nh,
nua_server_params(sr, tags);
nua_server_respond(sr, tags);
- nua_server_report(sr);
+
+ if (!(sr->sr_method == sip_method_invite && status == 100)) {
+ /* Since we don't change state, do not notify application when
+ we send 100 Trying for INVITE */
+ nua_server_report(sr);
+ }
}
int nua_server_params(nua_server_request_t *sr, tagi_t const *tags)
@@ -528,6 +534,13 @@ int nua_server_respond(nua_server_request_t *sr, tagi_t const *tags)
goto internal_error;
}
+ if (sr->sr_status == 100) {
+ return nta_incoming_treply(sr->sr_irq, SIP_100_TRYING,
+ SIPTAG_USER_AGENT_STR(NH_PGET(nh, user_agent)),
+ TAG_END());
+ return 0;
+ }
+
if (sr->sr_status < 200) {
next.msg = nta_incoming_create_response(sr->sr_irq, 0, NULL);
next.sip = sip_object(next.msg);
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c
index 7670b93268..21dede4343 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c
@@ -2796,6 +2796,13 @@ tag_typedef_t nutag_auth_cache = INTTAG_TYPEDEF(auth_cache);
* Reference tag for NUTAG_AUTH_CACHE().
*/
+/**@def NUTAG_AUTO_INVITE_100(x)
+ */
+tag_typedef_t nutag_auto_invite_100 = INTTAG_TYPEDEF(auto_invite_100);
+
+/**@def NUTAG_AUTO_INVITE_100(x)
+ * Reference tag for NUTAG_AUTO_INVITE_100().
+ */
/**@def NUTAG_DETECT_NETWORK_UPDATES(x)
*
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h b/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h
index 2c3a72ca04..3a3f96e02b 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h
@@ -611,6 +611,13 @@ SOFIAPUBVAR tag_typedef_t nutag_shutdown_events;
nutag_shutdown_events_ref, tag_bool_vr(&(x))
SOFIAPUBVAR tag_typedef_t nutag_shutdown_events_ref;
+#define NUTAG_AUTO_INVITE_100(x) \
+ nutag_auto_invite_100, tag_bool_v(x)
+SOFIAPUBVAR tag_typedef_t nutag_auto_invite_100;
+#define NUTAG_AUTO_INVITE_100_REF(x) \
+ nutag_auto_invite_100_ref, tag_bool_vr(&(x))
+SOFIAPUBVAR tag_typedef_t nutag_auto_invite_100_ref;
+
/* Pass nua handle as tagged argument */
#if SU_INLINE_TAG_CAST
su_inline tag_value_t nutag_handle_v(nua_handle_t *v) { return (tag_value_t)v; }
diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h
index b348dccc51..bc37dd2e6c 100644
--- a/src/include/switch_channel.h
+++ b/src/include/switch_channel.h
@@ -447,6 +447,10 @@ SWITCH_DECLARE(void) switch_channel_check_zrtp(switch_channel_t *channel);
*/
#define switch_channel_mark_pre_answered(channel) switch_channel_perform_mark_pre_answered(channel, __FILE__, __SWITCH_FUNC__, __LINE__)
+SWITCH_DECLARE(switch_status_t) switch_channel_perform_acknowledge_call(switch_channel_t *channel,
+ const char *file, const char *func, int line);
+#define switch_channel_acknowledge_call(channel) switch_channel_perform_acknowledge_call(channel, __FILE__, __SWITCH_FUNC__, __LINE__)
+
SWITCH_DECLARE(switch_status_t) switch_channel_perform_ring_ready_value(switch_channel_t *channel,
switch_ring_ready_t rv,
const char *file, const char *func, int line);
diff --git a/src/include/switch_types.h b/src/include/switch_types.h
index 0ebcef406e..fff02cfcb0 100644
--- a/src/include/switch_types.h
+++ b/src/include/switch_types.h
@@ -1052,6 +1052,7 @@ typedef enum {
SWITCH_MESSAGE_REDIRECT_AUDIO,
SWITCH_MESSAGE_TRANSMIT_TEXT,
SWITCH_MESSAGE_INDICATE_ANSWER,
+ SWITCH_MESSAGE_INDICATE_ACKNOWLEDGE_CALL,
SWITCH_MESSAGE_INDICATE_PROGRESS,
SWITCH_MESSAGE_INDICATE_BRIDGE,
SWITCH_MESSAGE_INDICATE_UNBRIDGE,
diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c
index 9e9f446549..937d214827 100644
--- a/src/mod/applications/mod_dptools/mod_dptools.c
+++ b/src/mod/applications/mod_dptools/mod_dptools.c
@@ -1048,6 +1048,11 @@ SWITCH_STANDARD_APP(capture_text_function)
switch_ivr_capture_text(session, switch_true((char *)data));
}
+SWITCH_STANDARD_APP(acknowledge_call_function)
+{
+ switch_channel_acknowledge_call(switch_core_session_get_channel(session));
+}
+
SWITCH_STANDARD_APP(ring_ready_function)
{
if (!zstr(data)) {
@@ -6298,6 +6303,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
SWITCH_ADD_APP(app_interface, "capture_text", "capture text", "capture text", capture_text_function, "", SAF_NONE);
+ SWITCH_ADD_APP(app_interface, "acknowledge_call", "Indicate Call Acknowledged", "Indicate Call Acknowledged on a channel.", acknowledge_call_function, "", SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_APP(app_interface, "ring_ready", "Indicate Ring_Ready", "Indicate Ring_Ready on a channel.", ring_ready_function, "", SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_APP(app_interface, "remove_bugs", "Remove media bugs", "Remove all media bugs from a channel.", remove_bugs_function, "[]", SAF_NONE);
SWITCH_ADD_APP(app_interface, "break", "Break", "Set the break flag.", break_function, "", SAF_SUPPORT_NOMEDIA);
diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c
index 1c5d0e48a1..96a13be7f7 100644
--- a/src/mod/endpoints/mod_sofia/mod_sofia.c
+++ b/src/mod/endpoints/mod_sofia/mod_sofia.c
@@ -55,6 +55,7 @@ static switch_status_t sofia_on_init(switch_core_session_t *session);
static switch_status_t sofia_on_exchange_media(switch_core_session_t *session);
static switch_status_t sofia_on_soft_execute(switch_core_session_t *session);
+static switch_status_t sofia_acknowledge_call(switch_core_session_t *session);
static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session,
switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause);
@@ -137,6 +138,14 @@ static switch_status_t sofia_on_routing(switch_core_session_t *session)
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_assert(tech_pvt != NULL);
+ if (sofia_test_pflag(tech_pvt->profile, PFLAG_AUTO_INVITE_100) &&
+ !switch_channel_test_flag(channel, CF_ANSWERED) &&
+ switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
+ if (sofia_acknowledge_call(session) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Call appears to be already acknowledged\n");
+ }
+ }
+
if (!sofia_test_flag(tech_pvt, TFLAG_HOLD_LOCK)) {
sofia_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD);
switch_channel_clear_flag(channel, CF_LEG_HOLDING);
@@ -642,6 +651,19 @@ static switch_status_t sofia_on_soft_execute(switch_core_session_t *session)
return SWITCH_STATUS_SUCCESS;
}
+static switch_status_t sofia_acknowledge_call(switch_core_session_t *session)
+{
+ struct private_object *tech_pvt = switch_core_session_get_private(session);
+
+ if (!tech_pvt->sent_100) {
+ nua_respond(tech_pvt->nh, SIP_100_TRYING, TAG_END());
+ tech_pvt->sent_100 = 1;
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ return SWITCH_STATUS_FALSE;
+}
+
static switch_status_t sofia_answer_channel(switch_core_session_t *session)
{
private_object_t *tech_pvt = (private_object_t *) switch_core_session_get_private(session);
@@ -656,6 +678,10 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session)
char *sticky = NULL;
const char *call_info = switch_channel_get_variable(channel, "presence_call_info_full");
+ if(sofia_acknowledge_call(session) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Dialplan did not acknowledge_call; sent 100 Trying");
+ }
+
if (switch_channel_test_flag(channel, CF_CONFERENCE) && !switch_stristr(";isfocus", tech_pvt->reply_contact)) {
tech_pvt->reply_contact = switch_core_session_sprintf(session, "%s;isfocus", tech_pvt->reply_contact);
}
@@ -2176,6 +2202,12 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
}
}
+ /* Dialplan should really use acknowledge_call application instead of respond application to send 100 */
+ if (code == 100) {
+ status = sofia_acknowledge_call(session);
+ goto end_lock;
+ }
+
if (tech_pvt->proxy_refer_uuid) {
if (tech_pvt->proxy_refer_msg) {
nua_respond(tech_pvt->nh, code, su_strdup(nua_handle_home(tech_pvt->nh), reason), SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
@@ -2295,10 +2327,17 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
SIPTAG_HEADER_STR("X-FS-Support: " FREESWITCH_SUPPORT)), TAG_END());
}
break;
+ case SWITCH_MESSAGE_INDICATE_ACKNOWLEDGE_CALL:
+ status = sofia_acknowledge_call(session);
+ break;
case SWITCH_MESSAGE_INDICATE_RINGING:
{
switch_ring_ready_t ring_ready_val = msg->numeric_arg;
+ if(sofia_acknowledge_call(session) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Dialplan did not acknowledge_call; sent 100 Trying");
+ }
+
if (!switch_channel_test_flag(channel, CF_RING_READY) && !sofia_test_flag(tech_pvt, TFLAG_BYE) &&
!switch_channel_test_flag(channel, CF_EARLY_MEDIA) && !switch_channel_test_flag(channel, CF_ANSWERED)) {
char *extra_header = sofia_glue_get_extra_headers(channel, SOFIA_SIP_PROGRESS_HEADER_PREFIX);
@@ -2360,6 +2399,10 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
int send_sip_code = 183;
const char * p_send_sip_msg = sip_183_Session_progress;
+ if(sofia_acknowledge_call(session) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Dialplan did not acknowledge_call; sent 100 Trying");
+ }
+
b_sdp = switch_channel_get_variable(channel, SWITCH_B_SDP_VARIABLE);
is_proxy = (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA));
is_3pcc_proxy = (sofia_test_pflag(tech_pvt->profile, PFLAG_3PCC_PROXY) && sofia_test_flag(tech_pvt, TFLAG_3PCC));
diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h
index a66fc92420..b8d1a32a0c 100644
--- a/src/mod/endpoints/mod_sofia/mod_sofia.h
+++ b/src/mod/endpoints/mod_sofia/mod_sofia.h
@@ -307,6 +307,7 @@ typedef enum {
PFLAG_PROXY_INFO,
PFLAG_PROXY_MESSAGE,
PFLAG_FIRE_BYE_RESPONSE_EVENTS,
+ PFLAG_AUTO_INVITE_100,
/* No new flags below this line */
PFLAG_MAX
@@ -839,6 +840,7 @@ struct private_object {
sip_contact_t *contact;
int q850_cause;
int got_bye;
+ int sent_100;
nua_event_t want_event;
switch_rtp_bug_flag_t rtp_bugs;
char *user_via;
diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c
index 27750e25ba..c8311dc828 100644
--- a/src/mod/endpoints/mod_sofia/sofia.c
+++ b/src/mod/endpoints/mod_sofia/sofia.c
@@ -3155,6 +3155,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void
NTATAG_TCP_RPORT(0),
NTATAG_TLS_RPORT(0),
NUTAG_RETRY_AFTER_ENABLE(0),
+ NUTAG_AUTO_INVITE_100(0),
TAG_IF(!strchr(profile->sipip, ':'),
SOATAG_AF(SOA_AF_IP4_ONLY)),
TAG_IF(strchr(profile->sipip, ':'),
@@ -4517,6 +4518,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
}
profile->tls_verify_policy = TPTLS_VERIFY_NONE;
+ sofia_set_pflag(profile, PFLAG_AUTO_INVITE_100);
/* lib default */
profile->tls_verify_depth = 2;
@@ -5326,6 +5328,12 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
} else {
sofia_clear_pflag(profile, PFLAG_SECURE);
}
+ } else if (!strcasecmp(var, "auto-invite-100")) {
+ if (switch_true(val)) {
+ sofia_set_pflag(profile, PFLAG_AUTO_INVITE_100);
+ } else {
+ sofia_clear_pflag(profile, PFLAG_AUTO_INVITE_100);
+ }
} else {
found = 0;
}
@@ -9833,6 +9841,8 @@ void sofia_handle_sip_i_reinvite(switch_core_session_t *session,
switch_channel_t *channel = NULL;
private_object_t *tech_pvt = NULL;
+ nua_respond(nh, SIP_100_TRYING, TAG_END());
+
if (session) {
channel = switch_core_session_get_channel(session);
tech_pvt = switch_core_session_get_private(session);
diff --git a/src/switch_channel.c b/src/switch_channel.c
index 8086944190..44ed2ee685 100644
--- a/src/switch_channel.c
+++ b/src/switch_channel.c
@@ -3305,6 +3305,13 @@ static switch_status_t send_ind(switch_channel_t *channel, switch_core_session_m
return switch_core_session_perform_receive_message(channel->session, &msg, file, func, line);
}
+SWITCH_DECLARE(switch_status_t) switch_channel_perform_acknowledge_call(switch_channel_t *channel,
+ const char *file, const char *func, int line)
+{
+ send_ind(channel, SWITCH_MESSAGE_INDICATE_ACKNOWLEDGE_CALL, file, func, line);
+
+ return SWITCH_STATUS_SUCCESS;
+}
SWITCH_DECLARE(switch_status_t) switch_channel_perform_mark_ring_ready_value(switch_channel_t *channel,
switch_ring_ready_t rv,