From e2f0f6156c3edfdf34b0bf8fea37e4cf3bcb98bb Mon Sep 17 00:00:00 2001 From: System Administrator Date: Thu, 6 Sep 2018 21:08:02 +0200 Subject: [PATCH] Add support for incoming call pre-cheking --- CMakeLists.txt | 0 bindings/_common/MediaSessionMgr.cxx | 14 +- bindings/_common/MediaSessionMgr.h | 2 + bindings/_common/SipStack.i | 1 + bindings/vs_android/tinyWRAP.sln | 0 bindings/vs_android/tinyWRAP.vcxproj | 0 bindings/vs_android/tinyWRAP.vcxproj.filters | 0 tinyBFCP/vs_android/tinyBFCP.vcxproj | 0 tinyBFCP/vs_android/tinyBFCP.vcxproj.filters | 0 tinyDAV/vs_android/tinyDAV.vcxproj | 0 tinyDAV/vs_android/tinyDAV.vcxproj.filters | 0 tinyHTTP/vs_android/tinyHTTP.vcxproj | 0 tinyHTTP/vs_android/tinyHTTP.vcxproj.filters | 0 tinyIPSec/vs_android/tinyIPSec.vcxproj | 0 .../vs_android/tinyIPSec.vcxproj.filters | 0 tinyMEDIA/include/tinymedia/tmedia_defaults.h | 2 + tinyMEDIA/src/tmedia_defaults.c | 11 + tinyMEDIA/vs_android/tinyMEDIA.vcxproj | 0 .../vs_android/tinyMEDIA.vcxproj.filters | 0 tinyMSRP/vs_android/tinyMSRP.vcxproj | 0 tinyMSRP/vs_android/tinyMSRP.vcxproj.filters | 0 tinyNET/vs_android/tinyNET.vcxproj | 0 tinyNET/vs_android/tinyNET.vcxproj.filters | 0 tinyRTP/vs_android/tinyRTP.vcxproj | 0 tinyRTP/vs_android/tinyRTP.vcxproj.filters | 0 tinySAK/vs_android/tinySAK.vcxproj | 0 tinySAK/vs_android/tinySAK.vcxproj.filters | 0 tinySDP/vs_android/tinySDP.vcxproj | 0 tinySDP/vs_android/tinySDP.vcxproj.filters | 0 tinySIGCOMP/vs_android/tinySIGCOMP.vcxproj | 0 .../vs_android/tinySIGCOMP.vcxproj.filters | 0 tinySIP/include/tinysip/api/tsip_api_invite.h | 259 +- .../dialogs/tsip_dialog_invite.common.h | 241 +- .../tinysip/dialogs/tsip_dialog_invite.h | 255 +- tinySIP/include/tinysip/tsip_action.h | 336 +- tinySIP/include/tinysip/tsip_event.h | 223 +- tinySIP/include/tinysip/tsip_ssession.h | 597 +-- tinySIP/src/dialogs/tsip_dialog_invite.c | 3877 +++++++++-------- .../src/dialogs/tsip_dialog_invite.server.c | 1649 +++---- tinySIP/src/tsip_ssession.c | 1619 +++---- tinySIP/vs_android/tinySIP.vcxproj | 0 tinySIP/vs_android/tinySIP.vcxproj.filters | 0 tinySMS/vs_android/tinySMS.vcxproj | 0 tinySMS/vs_android/tinySMS.vcxproj.filters | 0 tinyXCAP/vs_android/tinyXCAP.vcxproj | 0 tinyXCAP/vs_android/tinyXCAP.vcxproj.filters | 0 46 files changed, 4594 insertions(+), 4492 deletions(-) mode change 100644 => 100755 CMakeLists.txt mode change 100644 => 100755 bindings/vs_android/tinyWRAP.sln mode change 100644 => 100755 bindings/vs_android/tinyWRAP.vcxproj mode change 100644 => 100755 bindings/vs_android/tinyWRAP.vcxproj.filters mode change 100644 => 100755 tinyBFCP/vs_android/tinyBFCP.vcxproj mode change 100644 => 100755 tinyBFCP/vs_android/tinyBFCP.vcxproj.filters mode change 100644 => 100755 tinyDAV/vs_android/tinyDAV.vcxproj mode change 100644 => 100755 tinyDAV/vs_android/tinyDAV.vcxproj.filters mode change 100644 => 100755 tinyHTTP/vs_android/tinyHTTP.vcxproj mode change 100644 => 100755 tinyHTTP/vs_android/tinyHTTP.vcxproj.filters mode change 100644 => 100755 tinyIPSec/vs_android/tinyIPSec.vcxproj mode change 100644 => 100755 tinyIPSec/vs_android/tinyIPSec.vcxproj.filters mode change 100644 => 100755 tinyMEDIA/vs_android/tinyMEDIA.vcxproj mode change 100644 => 100755 tinyMEDIA/vs_android/tinyMEDIA.vcxproj.filters mode change 100644 => 100755 tinyMSRP/vs_android/tinyMSRP.vcxproj mode change 100644 => 100755 tinyMSRP/vs_android/tinyMSRP.vcxproj.filters mode change 100644 => 100755 tinyNET/vs_android/tinyNET.vcxproj mode change 100644 => 100755 tinyNET/vs_android/tinyNET.vcxproj.filters mode change 100644 => 100755 tinyRTP/vs_android/tinyRTP.vcxproj mode change 100644 => 100755 tinyRTP/vs_android/tinyRTP.vcxproj.filters mode change 100644 => 100755 tinySAK/vs_android/tinySAK.vcxproj mode change 100644 => 100755 tinySAK/vs_android/tinySAK.vcxproj.filters mode change 100644 => 100755 tinySDP/vs_android/tinySDP.vcxproj mode change 100644 => 100755 tinySDP/vs_android/tinySDP.vcxproj.filters mode change 100644 => 100755 tinySIGCOMP/vs_android/tinySIGCOMP.vcxproj mode change 100644 => 100755 tinySIGCOMP/vs_android/tinySIGCOMP.vcxproj.filters mode change 100644 => 100755 tinySIP/vs_android/tinySIP.vcxproj mode change 100644 => 100755 tinySIP/vs_android/tinySIP.vcxproj.filters mode change 100644 => 100755 tinySMS/vs_android/tinySMS.vcxproj mode change 100644 => 100755 tinySMS/vs_android/tinySMS.vcxproj.filters mode change 100644 => 100755 tinyXCAP/vs_android/tinyXCAP.vcxproj mode change 100644 => 100755 tinyXCAP/vs_android/tinyXCAP.vcxproj.filters diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100644 new mode 100755 diff --git a/bindings/_common/MediaSessionMgr.cxx b/bindings/_common/MediaSessionMgr.cxx index d844e4e8..becad28a 100755 --- a/bindings/_common/MediaSessionMgr.cxx +++ b/bindings/_common/MediaSessionMgr.cxx @@ -473,6 +473,16 @@ int32_t MediaSessionMgr::defaultsGetNoiseSuppLevel() return tmedia_defaults_get_noise_supp_level(); } +bool MediaSessionMgr::defaultsSetConditionalRingingEnabled(bool _cond_ringing_enabled) +{ + return tmedia_defaults_set_conditional_ringing_enabled(_cond_ringing_enabled ? tsk_true : tsk_false) == 0; +} + +bool MediaSessionMgr::defaultsGetConditionalRingingEnabled() +{ + return !!tmedia_defaults_get_conditional_ringing_enabled(); +} + bool MediaSessionMgr::defaultsSet100relEnabled(bool _100rel_enabled) { return tmedia_defaults_set_100rel_enabled(_100rel_enabled ? tsk_true : tsk_false) == 0; @@ -480,7 +490,7 @@ bool MediaSessionMgr::defaultsSet100relEnabled(bool _100rel_enabled) bool MediaSessionMgr::defaultsGet100relEnabled() { - return tmedia_defaults_get_100rel_enabled() == 0; + return !!tmedia_defaults_get_100rel_enabled(); } bool MediaSessionMgr::defaultsSetScreenSize(int32_t sx, int32_t sy) @@ -662,4 +672,4 @@ bool MediaSessionMgr::defaultsSetOpusMaxPlaybackRate(uint32_t opus_maxplaybackra bool MediaSessionMgr::defaultsSetMaxFds(int32_t max_fds) { return (tmedia_defaults_set_max_fds(max_fds) == 0); -} \ No newline at end of file +} diff --git a/bindings/_common/MediaSessionMgr.h b/bindings/_common/MediaSessionMgr.h index ee7869b2..2249faa8 100755 --- a/bindings/_common/MediaSessionMgr.h +++ b/bindings/_common/MediaSessionMgr.h @@ -197,6 +197,8 @@ public: static bool defaultsGetNoiseSuppEnabled(); static bool defaultsSetNoiseSuppLevel(int32_t noise_supp_level); static int32_t defaultsGetNoiseSuppLevel(); + static bool defaultsSetConditionalRingingEnabled(bool _cond_ringing_enabled); + static bool defaultsGetConditionalRingingEnabled(); static bool defaultsSet100relEnabled(bool _100rel_enabled); static bool defaultsGet100relEnabled(); static bool defaultsSetScreenSize(int32_t sx, int32_t sy); diff --git a/bindings/_common/SipStack.i b/bindings/_common/SipStack.i index e2cf2038..024347a9 100755 --- a/bindings/_common/SipStack.i +++ b/bindings/_common/SipStack.i @@ -103,6 +103,7 @@ tsip_event_type_t; #define tsip_event_code_dialog_request_outgoing 802 #define tsip_event_code_dialog_request_cancelled 803 #define tsip_event_code_dialog_request_sent 804 +#define tsip_event_code_dialog_request_prechecking 805 // 9xx ==> Informational #define tsip_event_code_dialog_connecting 900 diff --git a/bindings/vs_android/tinyWRAP.sln b/bindings/vs_android/tinyWRAP.sln old mode 100644 new mode 100755 diff --git a/bindings/vs_android/tinyWRAP.vcxproj b/bindings/vs_android/tinyWRAP.vcxproj old mode 100644 new mode 100755 diff --git a/bindings/vs_android/tinyWRAP.vcxproj.filters b/bindings/vs_android/tinyWRAP.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinyBFCP/vs_android/tinyBFCP.vcxproj b/tinyBFCP/vs_android/tinyBFCP.vcxproj old mode 100644 new mode 100755 diff --git a/tinyBFCP/vs_android/tinyBFCP.vcxproj.filters b/tinyBFCP/vs_android/tinyBFCP.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinyDAV/vs_android/tinyDAV.vcxproj b/tinyDAV/vs_android/tinyDAV.vcxproj old mode 100644 new mode 100755 diff --git a/tinyDAV/vs_android/tinyDAV.vcxproj.filters b/tinyDAV/vs_android/tinyDAV.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinyHTTP/vs_android/tinyHTTP.vcxproj b/tinyHTTP/vs_android/tinyHTTP.vcxproj old mode 100644 new mode 100755 diff --git a/tinyHTTP/vs_android/tinyHTTP.vcxproj.filters b/tinyHTTP/vs_android/tinyHTTP.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinyIPSec/vs_android/tinyIPSec.vcxproj b/tinyIPSec/vs_android/tinyIPSec.vcxproj old mode 100644 new mode 100755 diff --git a/tinyIPSec/vs_android/tinyIPSec.vcxproj.filters b/tinyIPSec/vs_android/tinyIPSec.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinyMEDIA/include/tinymedia/tmedia_defaults.h b/tinyMEDIA/include/tinymedia/tmedia_defaults.h index 91a1ae8b..af323795 100755 --- a/tinyMEDIA/include/tinymedia/tmedia_defaults.h +++ b/tinyMEDIA/include/tinymedia/tmedia_defaults.h @@ -69,6 +69,8 @@ TINYMEDIA_API int tmedia_defaults_set_noise_supp_enabled(tsk_bool_t noise_supp_e TINYMEDIA_API tsk_bool_t tmedia_defaults_get_noise_supp_enabled(); TINYMEDIA_API int tmedia_defaults_set_noise_supp_level(int32_t noise_supp_level); TINYMEDIA_API int32_t tmedia_defaults_get_noise_supp_level(); +TINYMEDIA_API int tmedia_defaults_set_conditional_ringing_enabled(tsk_bool_t _cond_ringing_enabled); +TINYMEDIA_API tsk_bool_t tmedia_defaults_get_conditional_ringing_enabled(); TINYMEDIA_API int tmedia_defaults_set_100rel_enabled(tsk_bool_t _100rel_enabled); TINYMEDIA_API tsk_bool_t tmedia_defaults_get_100rel_enabled(); TINYMEDIA_API int tmedia_defaults_set_screen_size(int32_t sx, int32_t sy); diff --git a/tinyMEDIA/src/tmedia_defaults.c b/tinyMEDIA/src/tmedia_defaults.c index 0c33b5b2..38fc2e57 100755 --- a/tinyMEDIA/src/tmedia_defaults.c +++ b/tinyMEDIA/src/tmedia_defaults.c @@ -49,6 +49,7 @@ static float __agc_level = 8000.0f; static tsk_bool_t __vad_enabled = tsk_false; static tsk_bool_t __noise_supp_enabled = tsk_true; static int32_t __noise_supp_level = -30; +static tsk_bool_t __cond_ringing_enabled = tsk_false; // Whether to let the user decide if sending ringing is needed or not -> If enabled then, the state machine will hang on "PreChecking" state until "accept" request is received from the end-user static tsk_bool_t __100rel_enabled = tsk_true; static int32_t __sx = -1; static int32_t __sy = -1; @@ -333,6 +334,16 @@ int32_t tmedia_defaults_get_noise_supp_level() return __noise_supp_level; } +int tmedia_defaults_set_conditional_ringing_enabled(tsk_bool_t _cond_ringing_enabled) +{ + __cond_ringing_enabled = _cond_ringing_enabled; + return 0; +} +tsk_bool_t tmedia_defaults_get_conditional_ringing_enabled() +{ + return __cond_ringing_enabled; +} + int tmedia_defaults_set_100rel_enabled(tsk_bool_t _100rel_enabled) { __100rel_enabled = _100rel_enabled; diff --git a/tinyMEDIA/vs_android/tinyMEDIA.vcxproj b/tinyMEDIA/vs_android/tinyMEDIA.vcxproj old mode 100644 new mode 100755 diff --git a/tinyMEDIA/vs_android/tinyMEDIA.vcxproj.filters b/tinyMEDIA/vs_android/tinyMEDIA.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinyMSRP/vs_android/tinyMSRP.vcxproj b/tinyMSRP/vs_android/tinyMSRP.vcxproj old mode 100644 new mode 100755 diff --git a/tinyMSRP/vs_android/tinyMSRP.vcxproj.filters b/tinyMSRP/vs_android/tinyMSRP.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinyNET/vs_android/tinyNET.vcxproj b/tinyNET/vs_android/tinyNET.vcxproj old mode 100644 new mode 100755 diff --git a/tinyNET/vs_android/tinyNET.vcxproj.filters b/tinyNET/vs_android/tinyNET.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinyRTP/vs_android/tinyRTP.vcxproj b/tinyRTP/vs_android/tinyRTP.vcxproj old mode 100644 new mode 100755 diff --git a/tinyRTP/vs_android/tinyRTP.vcxproj.filters b/tinyRTP/vs_android/tinyRTP.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinySAK/vs_android/tinySAK.vcxproj b/tinySAK/vs_android/tinySAK.vcxproj old mode 100644 new mode 100755 diff --git a/tinySAK/vs_android/tinySAK.vcxproj.filters b/tinySAK/vs_android/tinySAK.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinySDP/vs_android/tinySDP.vcxproj b/tinySDP/vs_android/tinySDP.vcxproj old mode 100644 new mode 100755 diff --git a/tinySDP/vs_android/tinySDP.vcxproj.filters b/tinySDP/vs_android/tinySDP.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinySIGCOMP/vs_android/tinySIGCOMP.vcxproj b/tinySIGCOMP/vs_android/tinySIGCOMP.vcxproj old mode 100644 new mode 100755 diff --git a/tinySIGCOMP/vs_android/tinySIGCOMP.vcxproj.filters b/tinySIGCOMP/vs_android/tinySIGCOMP.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinySIP/include/tinysip/api/tsip_api_invite.h b/tinySIP/include/tinysip/api/tsip_api_invite.h index b71eb5f0..6417d631 100755 --- a/tinySIP/include/tinysip/api/tsip_api_invite.h +++ b/tinySIP/include/tinysip/api/tsip_api_invite.h @@ -1,129 +1,130 @@ -/* -* Copyright (C) 2010-2011 Mamadou Diop. -* -* Contact: Mamadou Diop -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with DOUBANGO. -* -*/ - -/**@file tsip_api_invite.h - * @brief Public messaging (INVITE) functions. - * - * @author Mamadou Diop - * - - */ -#ifndef TINYSIP_TSIP_INVITE_H -#define TINYSIP_TSIP_INVITE_H - -#include "tinysip_config.h" - -#include "tinysip/tsip_event.h" - -#include "tinymedia/tmedia_common.h" - -TSIP_BEGIN_DECLS - -#define TSIP_INVITE_EVENT(self) ((tsip_invite_event_t*)(self)) - -//@tinyWRAP -typedef enum tsip_invite_event_type_e { - // ============================ - // Sip Events - // - tsip_i_newcall, - - //! in-dialog requests/reponses - tsip_i_request, - tsip_ao_request, - - /* 3GPP TS 24.629: Explicit Call Transfer (ECT) */ - tsip_o_ect_trying, - tsip_o_ect_accepted, - tsip_o_ect_completed, - tsip_o_ect_failed, - tsip_o_ect_notify, - tsip_i_ect_requested, - tsip_i_ect_newcall, - tsip_i_ect_completed, - tsip_i_ect_failed, - tsip_i_ect_notify, - - // ============================ - // Media Events - // - - tsip_m_early_media, - tsip_m_updating, // Trying to update from Audio -> Video for example - tsip_m_updated, // succeed to update - - /* 3GPP TS 24.610: Communication Hold */ - tsip_m_local_hold_ok, - tsip_m_local_hold_nok, - tsip_m_local_resume_ok, - tsip_m_local_resume_nok, - tsip_m_remote_hold, - tsip_m_remote_resume, -} -tsip_invite_event_type_t; - -typedef struct tsip_invite_event_e { - TSIP_DECLARE_EVENT; - - tsip_invite_event_type_t type; - - struct { - unsigned toto:1; - } av; - - /*struct{ - char* dir; - } msrp;*/ -} -tsip_invite_event_t; - -int tsip_invite_event_signal(tsip_invite_event_type_t type, tsip_ssession_handle_t* ss, short status_code, const char *phrase, const struct tsip_message_s* sipmessage); - -TINYSIP_API int tsip_api_invite_send_invite(const tsip_ssession_handle_t *ss, tmedia_type_t type, ...); -TINYSIP_API int tsip_api_invite_send_info(const tsip_ssession_handle_t *ss, ...); -TINYSIP_API int tsip_api_invite_send_hold(const tsip_ssession_handle_t *ss, tmedia_type_t type, ...); -TINYSIP_API int tsip_api_invite_send_resume(const tsip_ssession_handle_t *ss, tmedia_type_t type, ...); -TINYSIP_API int tsip_api_invite_send_large_message(const tsip_ssession_handle_t *ss, ...); -TINYSIP_API int tsip_api_invite_send_ect(const tsip_ssession_handle_t *ss, const char* toUri, ...); -TINYSIP_API int tsip_api_invite_send_ect_accept(const tsip_ssession_handle_t *ss, ...); -TINYSIP_API int tsip_api_invite_send_ect_reject(const tsip_ssession_handle_t *ss, ...); -TINYSIP_API int tsip_api_invite_send_sos(const tsip_ssession_handle_t *ss, ...); -TINYSIP_API int tsip_api_invite_send_dtmf(const tsip_ssession_handle_t *ss, int event, ...); -TINYSIP_API int tsip_api_invite_send_bye(const tsip_ssession_handle_t *ss, ...); - -TINYSIP_GEXTERN const tsk_object_def_t *tsip_invite_event_def_t; - -#if 1 // Backward Compatibility -# define tsip_action_INVITE tsip_api_invite_send_invite -# define tsip_action_HOLD tsip_api_invite_send_hold -# define tsip_action_RESUME tsip_api_invite_send_resume -# define tsip_action_LARGE_MESSAGE tsip_api_invite_send_large_message -# define tsip_action_ECT tsip_api_invite_send_ect -# define tsip_action_SOS tsip_api_invite_send_sos -# define tsip_action_DTMF tsip_api_invite_send_dtmf -# define tsip_action_BYE tsip_api_invite_send_bye -#endif - - -TSIP_END_DECLS - -#endif /* TINYSIP_TSIP_INVITE_H */ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tsip_api_invite.h + * @brief Public messaging (INVITE) functions. + * + * @author Mamadou Diop + * + + */ +#ifndef TINYSIP_TSIP_INVITE_H +#define TINYSIP_TSIP_INVITE_H + +#include "tinysip_config.h" + +#include "tinysip/tsip_event.h" + +#include "tinymedia/tmedia_common.h" + +TSIP_BEGIN_DECLS + +#define TSIP_INVITE_EVENT(self) ((tsip_invite_event_t*)(self)) + +//@tinyWRAP +typedef enum tsip_invite_event_type_e { + // ============================ + // Sip Events + // + tsip_i_newcall, + tsip_i_prechecking, + + //! in-dialog requests/reponses + tsip_i_request, + tsip_ao_request, + + /* 3GPP TS 24.629: Explicit Call Transfer (ECT) */ + tsip_o_ect_trying, + tsip_o_ect_accepted, + tsip_o_ect_completed, + tsip_o_ect_failed, + tsip_o_ect_notify, + tsip_i_ect_requested, + tsip_i_ect_newcall, + tsip_i_ect_completed, + tsip_i_ect_failed, + tsip_i_ect_notify, + + // ============================ + // Media Events + // + + tsip_m_early_media, + tsip_m_updating, // Trying to update from Audio -> Video for example + tsip_m_updated, // succeed to update + + /* 3GPP TS 24.610: Communication Hold */ + tsip_m_local_hold_ok, + tsip_m_local_hold_nok, + tsip_m_local_resume_ok, + tsip_m_local_resume_nok, + tsip_m_remote_hold, + tsip_m_remote_resume, +} +tsip_invite_event_type_t; + +typedef struct tsip_invite_event_e { + TSIP_DECLARE_EVENT; + + tsip_invite_event_type_t type; + + struct { + unsigned toto:1; + } av; + + /*struct{ + char* dir; + } msrp;*/ +} +tsip_invite_event_t; + +int tsip_invite_event_signal(tsip_invite_event_type_t type, tsip_ssession_handle_t* ss, short status_code, const char *phrase, const struct tsip_message_s* sipmessage); + +TINYSIP_API int tsip_api_invite_send_invite(const tsip_ssession_handle_t *ss, tmedia_type_t type, ...); +TINYSIP_API int tsip_api_invite_send_info(const tsip_ssession_handle_t *ss, ...); +TINYSIP_API int tsip_api_invite_send_hold(const tsip_ssession_handle_t *ss, tmedia_type_t type, ...); +TINYSIP_API int tsip_api_invite_send_resume(const tsip_ssession_handle_t *ss, tmedia_type_t type, ...); +TINYSIP_API int tsip_api_invite_send_large_message(const tsip_ssession_handle_t *ss, ...); +TINYSIP_API int tsip_api_invite_send_ect(const tsip_ssession_handle_t *ss, const char* toUri, ...); +TINYSIP_API int tsip_api_invite_send_ect_accept(const tsip_ssession_handle_t *ss, ...); +TINYSIP_API int tsip_api_invite_send_ect_reject(const tsip_ssession_handle_t *ss, ...); +TINYSIP_API int tsip_api_invite_send_sos(const tsip_ssession_handle_t *ss, ...); +TINYSIP_API int tsip_api_invite_send_dtmf(const tsip_ssession_handle_t *ss, int event, ...); +TINYSIP_API int tsip_api_invite_send_bye(const tsip_ssession_handle_t *ss, ...); + +TINYSIP_GEXTERN const tsk_object_def_t *tsip_invite_event_def_t; + +#if 1 // Backward Compatibility +# define tsip_action_INVITE tsip_api_invite_send_invite +# define tsip_action_HOLD tsip_api_invite_send_hold +# define tsip_action_RESUME tsip_api_invite_send_resume +# define tsip_action_LARGE_MESSAGE tsip_api_invite_send_large_message +# define tsip_action_ECT tsip_api_invite_send_ect +# define tsip_action_SOS tsip_api_invite_send_sos +# define tsip_action_DTMF tsip_api_invite_send_dtmf +# define tsip_action_BYE tsip_api_invite_send_bye +#endif + + +TSIP_END_DECLS + +#endif /* TINYSIP_TSIP_INVITE_H */ diff --git a/tinySIP/include/tinysip/dialogs/tsip_dialog_invite.common.h b/tinySIP/include/tinysip/dialogs/tsip_dialog_invite.common.h index 15494ad0..9272cd11 100755 --- a/tinySIP/include/tinysip/dialogs/tsip_dialog_invite.common.h +++ b/tinySIP/include/tinysip/dialogs/tsip_dialog_invite.common.h @@ -1,120 +1,121 @@ -/* -* Copyright (C) 2010-2011 Mamadou Diop. -* -* Contact: Mamadou Diop -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with DOUBANGO. -* -*/ - -/**@file tsip_dialog_invite.common.h - * @brief SIP dialog INVITE (common variables). - * - * @author Mamadou Diop - * - - */ -#ifndef TINYSIP_DIALOG_INVITE_COMMON_H -#define TINYSIP_DIALOG_INVITE_COMMON_H - -#include "tinysip/api/tsip_api_invite.h" - -#define DEBUG_STATE_MACHINE 1 -#define TSIP_DIALOG_INVITE_SIGNAL(self, type, code, phrase, message) \ - tsip_invite_event_signal(type, TSIP_DIALOG(self)->ss, code, phrase, message) -#define TSIP_DIALOG_INVITE_TIMER_SCHEDULE(TX) TSIP_DIALOG_TIMER_SCHEDULE(invite, TX) - -#define TSIP_DIALOG_INVITE_ICE_CONNCHECK_TIMEOUT 16000 - -/* ======================== actions ======================== */ -typedef enum _fsm_action_e { - _fsm_action_accept = tsip_atype_accept, - _fsm_action_reject = tsip_atype_hangup, - _fsm_action_dtmf_send = tsip_atype_dtmf_send, - _fsm_action_msrp_send_msg = tsip_atype_lmessage, - _fsm_action_oINVITE = tsip_atype_invite, - _fsm_action_oCANCEL = tsip_atype_cancel, - _fsm_action_oHold = tsip_atype_hold, - _fsm_action_oResume = tsip_atype_resume, - _fsm_action_oECT = tsip_atype_ect, - _fsm_action_iECT_ACCEPT = tsip_atype_ect_accept, - _fsm_action_iECT_REJECT = tsip_atype_ect_reject, - _fsm_action_iECT_lNOTIFY = tsip_atype_ect_lnotify, - _fsm_action_oINFO = tsip_atype_info_send, - _fsm_action_oBYE = tsip_atype_hangup, - _fsm_action_oShutdown = tsip_atype_shutdown, - _fsm_action_transporterror = tsip_atype_transport_error, - - _fsm_action_iINVITE = 0xFF, - _fsm_action_oUPDATE, - _fsm_action_iUPDATE, - _fsm_action_iCANCEL, - _fsm_action_iPRACK, - _fsm_action_oPRACK, - _fsm_action_iACK, - _fsm_action_oACK, - _fsm_action_iOPTIONS, - _fsm_action_oOPTIONS, - _fsm_action_iBYE, - _fsm_action_iREFER, - _fsm_action_iINFO, - _fsm_action_iNOTIFY, - - _fsm_action_timer100rel, - _fsm_action_timerRefresh, - _fsm_action_timerRSVP, - - _fsm_action_i1xx, - _fsm_action_i2xx, - _fsm_action_i300_to_i699, - _fsm_action_i401_i407, - _fsm_action_i422, - - _fsm_action_shutdown_timedout, /* Any -> Terminated */ - _fsm_action_error, -} -_fsm_action_t; - -/* ======================== states ======================== */ -typedef enum _fsm_state_e { - _fsm_state_Started, - _fsm_state_Outgoing, - _fsm_state_Incoming, - _fsm_state_Trying, - _fsm_state_Ringing, - _fsm_state_Cancelling, - _fsm_state_InProgress, - - _fsm_state_Holding, - _fsm_state_Resuming, - - _fsm_state_oECTing, - _fsm_state_iECTing, - _fsm_state_iECTreq, - - _fsm_state_Connected, - _fsm_state_Terminated -} -_fsm_state_t; - - -#define send_INVITE(self, force_sdp) send_INVITEorUPDATE(self, tsk_true, force_sdp) -#define send_UPDATE(self, force_sdp) send_INVITEorUPDATE(self, tsk_false, force_sdp) - -#else -#error "This file must only be included in a source(.c or .cxx)" - -#endif /* TINYSIP_DIALOG_INVITE_COMMON_H */ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tsip_dialog_invite.common.h + * @brief SIP dialog INVITE (common variables). + * + * @author Mamadou Diop + * + + */ +#ifndef TINYSIP_DIALOG_INVITE_COMMON_H +#define TINYSIP_DIALOG_INVITE_COMMON_H + +#include "tinysip/api/tsip_api_invite.h" + +#define DEBUG_STATE_MACHINE 1 +#define TSIP_DIALOG_INVITE_SIGNAL(self, type, code, phrase, message) \ + tsip_invite_event_signal(type, TSIP_DIALOG(self)->ss, code, phrase, message) +#define TSIP_DIALOG_INVITE_TIMER_SCHEDULE(TX) TSIP_DIALOG_TIMER_SCHEDULE(invite, TX) + +#define TSIP_DIALOG_INVITE_ICE_CONNCHECK_TIMEOUT 16000 + +/* ======================== actions ======================== */ +typedef enum _fsm_action_e { + _fsm_action_accept = tsip_atype_accept, + _fsm_action_reject = tsip_atype_hangup, + _fsm_action_dtmf_send = tsip_atype_dtmf_send, + _fsm_action_msrp_send_msg = tsip_atype_lmessage, + _fsm_action_oINVITE = tsip_atype_invite, + _fsm_action_oCANCEL = tsip_atype_cancel, + _fsm_action_oHold = tsip_atype_hold, + _fsm_action_oResume = tsip_atype_resume, + _fsm_action_oECT = tsip_atype_ect, + _fsm_action_iECT_ACCEPT = tsip_atype_ect_accept, + _fsm_action_iECT_REJECT = tsip_atype_ect_reject, + _fsm_action_iECT_lNOTIFY = tsip_atype_ect_lnotify, + _fsm_action_oINFO = tsip_atype_info_send, + _fsm_action_oBYE = tsip_atype_hangup, + _fsm_action_oShutdown = tsip_atype_shutdown, + _fsm_action_transporterror = tsip_atype_transport_error, + + _fsm_action_iINVITE = 0xFF, + _fsm_action_oUPDATE, + _fsm_action_iUPDATE, + _fsm_action_iCANCEL, + _fsm_action_iPRACK, + _fsm_action_oPRACK, + _fsm_action_iACK, + _fsm_action_oACK, + _fsm_action_iOPTIONS, + _fsm_action_oOPTIONS, + _fsm_action_iBYE, + _fsm_action_iREFER, + _fsm_action_iINFO, + _fsm_action_iNOTIFY, + + _fsm_action_timer100rel, + _fsm_action_timerRefresh, + _fsm_action_timerRSVP, + + _fsm_action_i1xx, + _fsm_action_i2xx, + _fsm_action_i300_to_i699, + _fsm_action_i401_i407, + _fsm_action_i422, + + _fsm_action_shutdown_timedout, /* Any -> Terminated */ + _fsm_action_error, +} +_fsm_action_t; + +/* ======================== states ======================== */ +typedef enum _fsm_state_e { + _fsm_state_Started, + _fsm_state_Outgoing, + _fsm_state_Incoming, + _fsm_state_PreChecking, + _fsm_state_Trying, + _fsm_state_Ringing, + _fsm_state_Cancelling, + _fsm_state_InProgress, + + _fsm_state_Holding, + _fsm_state_Resuming, + + _fsm_state_oECTing, + _fsm_state_iECTing, + _fsm_state_iECTreq, + + _fsm_state_Connected, + _fsm_state_Terminated +} +_fsm_state_t; + + +#define send_INVITE(self, force_sdp) send_INVITEorUPDATE(self, tsk_true, force_sdp) +#define send_UPDATE(self, force_sdp) send_INVITEorUPDATE(self, tsk_false, force_sdp) + +#else +#error "This file must only be included in a source(.c or .cxx)" + +#endif /* TINYSIP_DIALOG_INVITE_COMMON_H */ diff --git a/tinySIP/include/tinysip/dialogs/tsip_dialog_invite.h b/tinySIP/include/tinysip/dialogs/tsip_dialog_invite.h index 0a90e4d5..bb125ef3 100755 --- a/tinySIP/include/tinysip/dialogs/tsip_dialog_invite.h +++ b/tinySIP/include/tinysip/dialogs/tsip_dialog_invite.h @@ -1,127 +1,128 @@ -/* -* Copyright (C) 2010-2011 Mamadou Diop. -* -* Contact: Mamadou Diop -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with DOUBANGO. -* -*/ - -/**@file tsip_dialog_invite.h - * @brief SIP dialog INVITE. - * - * @author Mamadou Diop - * - - */ -#ifndef TINYSIP_DIALOG_INVITE_H -#define TINYSIP_DIALOG_INVITE_H - -#include "tinysip_config.h" -#include "tinysip/dialogs/tsip_dialog.h" - -#include "tinymedia/tmedia_session.h" - - -TSIP_BEGIN_DECLS - -#define TSIP_DIALOG_INVITE(self) ((tsip_dialog_invite_t*)(self)) - -typedef struct tsip_dialog_invite { - TSIP_DECLARE_DIALOG; - - tsk_bool_t support_update; /**< Indicates whether the remote party support UPDATE */ - tsk_bool_t is_client; - tsk_bool_t is_transf; - tsk_bool_t refersub; - tsk_bool_t use_rtcp; - tsk_bool_t use_rtcpmux; - tsk_bool_t is_initial_iack_pending; // we're waiting for the initial incoming ACK (for the 200 OK) to ensure the session - tsk_bool_t is_cancelling; // whether we're cancelling the outgoing INVITE - uint32_t rseq; - uint32_t cseq_out_media_update; // CSeq for the last media update request (INVITE or UPDATE). - uint64_t last_out_fastupdate_time; - - tsip_timer_t timershutdown; - tsip_timer_t timer100rel; - - tsip_response_t* last_o1xxrel; - tsip_request_t* last_iInvite; - tsip_request_t* last_oInvite; - tsip_request_t* last_iRefer; - tmedia_session_mgr_t* msession_mgr; /**< Media session Manager */ - - struct tsip_ssession_s* ss_transf; - - /* ICE */ - struct { - tmedia_type_t media_type; - tsk_bool_t is_jingle; - tsk_bool_t start_smgr; - struct tnet_ice_ctx_s *ctx_audio; - struct tnet_ice_ctx_s *ctx_video; - tsk_fsm_action_id last_action_id; - tsip_action_t* last_action; - tsip_message_t* last_message; - int32_t last_sdp_ro_ver; - } ice; - - /* Session Timers */ - struct { - tsip_timer_t timer; - char* refresher; - uint64_t minse; - tsk_bool_t is_refresher; - } stimers; - /* QoS (Preconditions) */ - struct { - tsip_timer_t timer; - enum tmedia_qos_stype_e type; - enum tmedia_qos_strength_e strength; - } qos; - /* Hold/Resume */ - struct { - unsigned remote:1; - unsigned local:1; - } hold; - - struct { - unsigned _100rel:1; - unsigned precondition:1; - unsigned timer:1; - unsigned norefersub:1; - unsigned ice:1; - } supported; - - struct { - unsigned _100rel:1; - unsigned precondition:1; - unsigned timer:1; - unsigned norefersub; - unsigned ice:1; - } required; -} -tsip_dialog_invite_t; - -tsip_dialog_invite_t* tsip_dialog_invite_create(const tsip_ssession_handle_t* ss, const char* call_id); - -int tsip_dialog_invite_start(tsip_dialog_invite_t *self); - -TINYSIP_GEXTERN const tsk_object_def_t *tsip_dialog_invite_def_t; - -TSIP_END_DECLS - -#endif /* TINYSIP_DIALOG_INVITE_H */ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tsip_dialog_invite.h + * @brief SIP dialog INVITE. + * + * @author Mamadou Diop + * + + */ +#ifndef TINYSIP_DIALOG_INVITE_H +#define TINYSIP_DIALOG_INVITE_H + +#include "tinysip_config.h" +#include "tinysip/dialogs/tsip_dialog.h" + +#include "tinymedia/tmedia_session.h" + + +TSIP_BEGIN_DECLS + +#define TSIP_DIALOG_INVITE(self) ((tsip_dialog_invite_t*)(self)) + +typedef struct tsip_dialog_invite { + TSIP_DECLARE_DIALOG; + + tsk_bool_t support_update; /**< Indicates whether the remote party support UPDATE */ + tsk_bool_t is_client; + tsk_bool_t is_transf; + tsk_bool_t refersub; + tsk_bool_t use_rtcp; + tsk_bool_t use_rtcpmux; + tsk_bool_t is_initial_iack_pending; // we're waiting for the initial incoming ACK (for the 200 OK) to ensure the session + tsk_bool_t is_cancelling; // whether we're cancelling the outgoing INVITE + tsk_bool_t is_conditional_ringing_enabled; // whether to ask end-user before sending 18x ringing message + uint32_t rseq; + uint32_t cseq_out_media_update; // CSeq for the last media update request (INVITE or UPDATE). + uint64_t last_out_fastupdate_time; + + tsip_timer_t timershutdown; + tsip_timer_t timer100rel; + + tsip_response_t* last_o1xxrel; + tsip_request_t* last_iInvite; + tsip_request_t* last_oInvite; + tsip_request_t* last_iRefer; + tmedia_session_mgr_t* msession_mgr; /**< Media session Manager */ + + struct tsip_ssession_s* ss_transf; + + /* ICE */ + struct { + tmedia_type_t media_type; + tsk_bool_t is_jingle; + tsk_bool_t start_smgr; + struct tnet_ice_ctx_s *ctx_audio; + struct tnet_ice_ctx_s *ctx_video; + tsk_fsm_action_id last_action_id; + tsip_action_t* last_action; + tsip_message_t* last_message; + int32_t last_sdp_ro_ver; + } ice; + + /* Session Timers */ + struct { + tsip_timer_t timer; + char* refresher; + uint64_t minse; + tsk_bool_t is_refresher; + } stimers; + /* QoS (Preconditions) */ + struct { + tsip_timer_t timer; + enum tmedia_qos_stype_e type; + enum tmedia_qos_strength_e strength; + } qos; + /* Hold/Resume */ + struct { + unsigned remote:1; + unsigned local:1; + } hold; + + struct { + unsigned _100rel:1; + unsigned precondition:1; + unsigned timer:1; + unsigned norefersub:1; + unsigned ice:1; + } supported; + + struct { + unsigned _100rel:1; + unsigned precondition:1; + unsigned timer:1; + unsigned norefersub; + unsigned ice:1; + } required; +} +tsip_dialog_invite_t; + +tsip_dialog_invite_t* tsip_dialog_invite_create(const tsip_ssession_handle_t* ss, const char* call_id); + +int tsip_dialog_invite_start(tsip_dialog_invite_t *self); + +TINYSIP_GEXTERN const tsk_object_def_t *tsip_dialog_invite_def_t; + +TSIP_END_DECLS + +#endif /* TINYSIP_DIALOG_INVITE_H */ diff --git a/tinySIP/include/tinysip/tsip_action.h b/tinySIP/include/tinysip/tsip_action.h index 15f30a19..74b7a2ac 100755 --- a/tinySIP/include/tinysip/tsip_action.h +++ b/tinySIP/include/tinysip/tsip_action.h @@ -1,168 +1,168 @@ -/* -* Copyright (C) 2010-2011 Mamadou Diop. -* -* Contact: Mamadou Diop -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with DOUBANGO. -* -*/ - -/**@file tsip_action.h - * @brief SIP action. - * - * @author Mamadou Diop - * - - */ -#ifndef TSIP_ACTION_H -#define TSIP_ACTION_H - -#include "tinysip_config.h" - -#include "tsk_buffer.h" -#include "tsk_options.h" -#include "tsk_params.h" - -#include "tinymedia/tmedia_common.h" -#include "tinymedia/tmedia_params.h" - -TSIP_BEGIN_DECLS - -typedef uint64_t tsip_action_id_t; -#define TSIP_ACTION_INVALID_ID 0 -#define TSIP_ACTION_INVALID_HANDLE tsk_null - -/** List of all supported actions */ -typedef enum tsip_action_type_e { - //! Used as configuration action - tsip_atype_config, - tsip_atype_dtmf_send, - - /* === REGISTER == */ - tsip_atype_register, /**< Sends SIP REGISTER request */ - //! Unregister by sending SIP REGISTER request with expires value equals to zero -#define tsip_atype_unregister tsip_atype_hangup - - /* === SUBSCRIBE === */ - tsip_atype_subscribe, /**< Sends SIP SUBSCRIBE request */ - //! Unsubsribe by sending SIP SUBSCRIBE request with expires value equals to zero -#define tsip_atype_unsubscribe tsip_atype_hangup - - /* === MESSAGE === */ - tsip_atype_message_send, /**< Sends SIP MESSAGE request */ - - /* === INFO === */ - tsip_atype_info_send, /**< Sends SIP INFO request */ - - /* === PUBLISH === */ - tsip_atype_publish, /**< Sends SIP PUBLISH request */ - //! Unpublish by sending SIP PUBLISH request with expires value equals to zero -#define tsip_atype_unpublish tsip_atype_hangup - - /* === OPTIONS === */ - tsip_atype_options_send, /**< Sends SIP OPTIONS request */ - - /* === INVITE === */ - tsip_atype_invite, /**< Sends SIP INVITE/reINVITE request */ - tsip_atype_hold, /**< Puts the session on hold state */ - tsip_atype_resume, /**< Resumes a previously held session */ - tsip_atype_ect, /**< Transfer the call */ - tsip_atype_ect_accept, /**< Accept call transfer request */ - tsip_atype_ect_reject, /**< Reject call transfer request */ - tsip_atype_ect_lnotify, /**< Intra-Dialog notify. Never called by the end-user */ - tsip_atype_lmessage, /**< Large message (MSRP). The session must be connected */ -#define tsip_atype_bye tsip_atype_hangup - - - /* === common === */ - //! Accept incoming call (INVITE) or message (SIP MESSAGE) - tsip_atype_accept, - //! Reject incoming call (INVITE) or message (SIP MESSAGE) -#define tsip_atype_reject tsip_atype_hangup - //! Cancel an outgoing request - tsip_atype_cancel, - //! Hangup any SIP dialog (BYE, unREGISTER, unSUBSCRIBE ...). If the dialog is in early state, then it will be canceled. - tsip_atype_hangup, - //! Shutdown a SIP dialog. Should only be called by the stack. - tsip_atype_shutdown, - //! Signal transport error. Should only be called by the stack. - tsip_atype_transport_error, -} -tsip_action_type_t; - -/* internal enum used to pass parameters from the application layer to the stack */ -typedef enum tsip_action_param_type_e { - aptype_null = 0, - - aptype_header, - aptype_config, - aptype_payload, - aptype_resp_line, - aptype_media_type, - aptype_media, -} -tsip_action_param_type_t; - -#define TSIP_ACTION_SET_HEADER(NAME_STR, VALUE_STR) aptype_header, (const char*)NAME_STR, (const char*)VALUE_STR -#define TSIP_ACTION_SET_PAYLOAD(PAY_PTR, PAY_SIZE) aptype_payload, (const void*)PAY_PTR, (tsk_size_t)PAY_SIZE -#define TSIP_ACTION_SET_RESP_LINE(CODE_INT, PHRASE_STR) aptype_resp_line, (int32_t)CODE_INT, (const char*)PHRASE_STR -#define TSIP_ACTION_SET_CONFIG(ACTION_CONFIG_HANDLE) aptype_config, (const tsip_action_handle_t*)ACTION_CONFIG_HANDLE -#define TSIP_ACTION_SET_MEDIA_TYPE(TYPE_ENUM) aptype_media_type, (enum tmedia_type_e)TYPE_ENUM -#define TSIP_ACTION_SET_MEDIA(...) aptype_media, ##__VA_ARGS__ -#define TSIP_ACTION_SET_NULL() aptype_null - -/* private action object. public api should use tsip_action_handle_t. */ -typedef struct tsip_action_s { - TSK_DECLARE_OBJECT; - - tsip_action_type_t type; - tsk_params_L_t *headers; - tsk_buffer_t* payload; - - struct { - short code; - char* phrase; - } line_resp; - - struct { - tmedia_type_t type; - tmedia_params_L_t *params; - } media; - - struct { - int volume; // useless (manager will always use "10") - int event; - } dtmf; - - struct { - char* to; - } ect; -} -tsip_action_t; - -/**< Handle to SIP action */ -typedef void tsip_action_handle_t; - -TINYSIP_API tsip_action_handle_t* tsip_action_create(tsip_action_type_t type, ...); -TINYSIP_API int tsip_action_set(tsip_action_handle_t* self, ...); - -typedef tsk_list_t tsip_actions_L_t; /**< List of @ref tsip_action_handle_t elements. */ -TINYSIP_GEXTERN const tsk_object_def_t *tsip_action_def_t; - -TSIP_END_DECLS - -#endif /* TSIP_ACTION_H */ - +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tsip_action.h + * @brief SIP action. + * + * @author Mamadou Diop + * + + */ +#ifndef TSIP_ACTION_H +#define TSIP_ACTION_H + +#include "tinysip_config.h" + +#include "tsk_buffer.h" +#include "tsk_options.h" +#include "tsk_params.h" + +#include "tinymedia/tmedia_common.h" +#include "tinymedia/tmedia_params.h" + +TSIP_BEGIN_DECLS + +typedef uint64_t tsip_action_id_t; +#define TSIP_ACTION_INVALID_ID 0 +#define TSIP_ACTION_INVALID_HANDLE tsk_null + +/** List of all supported actions */ +typedef enum tsip_action_type_e { + //! Used as configuration action + tsip_atype_config, + tsip_atype_dtmf_send, + + /* === REGISTER == */ + tsip_atype_register, /**< Sends SIP REGISTER request */ + //! Unregister by sending SIP REGISTER request with expires value equals to zero +#define tsip_atype_unregister tsip_atype_hangup + + /* === SUBSCRIBE === */ + tsip_atype_subscribe, /**< Sends SIP SUBSCRIBE request */ + //! Unsubsribe by sending SIP SUBSCRIBE request with expires value equals to zero +#define tsip_atype_unsubscribe tsip_atype_hangup + + /* === MESSAGE === */ + tsip_atype_message_send, /**< Sends SIP MESSAGE request */ + + /* === INFO === */ + tsip_atype_info_send, /**< Sends SIP INFO request */ + + /* === PUBLISH === */ + tsip_atype_publish, /**< Sends SIP PUBLISH request */ + //! Unpublish by sending SIP PUBLISH request with expires value equals to zero +#define tsip_atype_unpublish tsip_atype_hangup + + /* === OPTIONS === */ + tsip_atype_options_send, /**< Sends SIP OPTIONS request */ + + /* === INVITE === */ + tsip_atype_invite, /**< Sends SIP INVITE/reINVITE request */ + tsip_atype_hold, /**< Puts the session on hold state */ + tsip_atype_resume, /**< Resumes a previously held session */ + tsip_atype_ect, /**< Transfer the call */ + tsip_atype_ect_accept, /**< Accept call transfer request */ + tsip_atype_ect_reject, /**< Reject call transfer request */ + tsip_atype_ect_lnotify, /**< Intra-Dialog notify. Never called by the end-user */ + tsip_atype_lmessage, /**< Large message (MSRP). The session must be connected */ +#define tsip_atype_bye tsip_atype_hangup + + + /* === common === */ + //! Accept incoming call (INVITE) or message (SIP MESSAGE) + tsip_atype_accept, + //! Reject incoming call (INVITE) or message (SIP MESSAGE) +#define tsip_atype_reject tsip_atype_hangup + //! Cancel an outgoing request + tsip_atype_cancel, + //! Hangup any SIP dialog (BYE, unREGISTER, unSUBSCRIBE ...). If the dialog is in early state, then it will be canceled. + tsip_atype_hangup, + //! Shutdown a SIP dialog. Should only be called by the stack. + tsip_atype_shutdown, + //! Signal transport error. Should only be called by the stack. + tsip_atype_transport_error, +} +tsip_action_type_t; + +/* internal enum used to pass parameters from the application layer to the stack */ +typedef enum tsip_action_param_type_e { + aptype_null = 0, + + aptype_header, + aptype_config, + aptype_payload, + aptype_resp_line, + aptype_media_type, + aptype_media, +} +tsip_action_param_type_t; + +#define TSIP_ACTION_SET_HEADER(NAME_STR, VALUE_STR) aptype_header, (const char*)NAME_STR, (const char*)VALUE_STR +#define TSIP_ACTION_SET_PAYLOAD(PAY_PTR, PAY_SIZE) aptype_payload, (const void*)PAY_PTR, (tsk_size_t)PAY_SIZE +#define TSIP_ACTION_SET_RESP_LINE(CODE_INT, PHRASE_STR) aptype_resp_line, (int32_t)CODE_INT, (const char*)PHRASE_STR +#define TSIP_ACTION_SET_CONFIG(ACTION_CONFIG_HANDLE) aptype_config, (const tsip_action_handle_t*)ACTION_CONFIG_HANDLE +#define TSIP_ACTION_SET_MEDIA_TYPE(TYPE_ENUM) aptype_media_type, (enum tmedia_type_e)TYPE_ENUM +#define TSIP_ACTION_SET_MEDIA(...) aptype_media, ##__VA_ARGS__ +#define TSIP_ACTION_SET_NULL() aptype_null + +/* private action object. public api should use tsip_action_handle_t. */ +typedef struct tsip_action_s { + TSK_DECLARE_OBJECT; + + tsip_action_type_t type; + tsk_params_L_t *headers; + tsk_buffer_t* payload; + + struct { + short code; + char* phrase; + } line_resp; + + struct { + tmedia_type_t type; + tmedia_params_L_t *params; + } media; + + struct { + int volume; // useless (manager will always use "10") + int event; + } dtmf; + + struct { + char* to; + } ect; +} +tsip_action_t; + +/**< Handle to SIP action */ +typedef void tsip_action_handle_t; + +TINYSIP_API tsip_action_handle_t* tsip_action_create(tsip_action_type_t type, ...); +TINYSIP_API int tsip_action_set(tsip_action_handle_t* self, ...); + +typedef tsk_list_t tsip_actions_L_t; /**< List of @ref tsip_action_handle_t elements. */ +TINYSIP_GEXTERN const tsk_object_def_t *tsip_action_def_t; + +TSIP_END_DECLS + +#endif /* TSIP_ACTION_H */ + diff --git a/tinySIP/include/tinysip/tsip_event.h b/tinySIP/include/tinysip/tsip_event.h index 1cb347e1..900e7d60 100755 --- a/tinySIP/include/tinysip/tsip_event.h +++ b/tinySIP/include/tinysip/tsip_event.h @@ -1,111 +1,112 @@ -/* -* Copyright (C) 2010-2011 Mamadou Diop. -* -* Contact: Mamadou Diop -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with DOUBANGO. -* -*/ - -/**@file tsip_event.h - * @brief SIP event. - * - * @author Mamadou Diop - * - - */ -#ifndef TINYSIP_TSIP_EVENT_H -#define TINYSIP_TSIP_EVENT_H - -#include "tinysip_config.h" - -#include "tinysip/tsip_ssession.h" - -TSIP_BEGIN_DECLS - -#define TSIP_EVENT(self) ((tsip_event_t*)(self)) - -typedef enum tsip_event_type_e { - tsip_event_invite, - tsip_event_message, - tsip_event_info, - tsip_event_options, - tsip_event_publish, - tsip_event_register, - tsip_event_subscribe, - - tsip_event_dialog, - tsip_event_stack, -} -tsip_event_type_t; - -/* SIP codes associated to an internal event */ -// 100-699 are reserved codes - -// 7xx ==> errors @tinyWRAP -#define tsip_event_code_dialog_transport_error 702 -#define tsip_event_code_dialog_global_error 703 -#define tsip_event_code_dialog_message_error 704 - -// 8xx ==> success @tinyWRAP -#define tsip_event_code_dialog_request_incoming 800 -#define tsip_event_code_dialog_request_outgoing 802 -#define tsip_event_code_dialog_request_cancelled 803 -#define tsip_event_code_dialog_request_sent 804 - -// 9xx ==> Informational @tinyWRAP -#define tsip_event_code_dialog_connecting 900 -#define tsip_event_code_dialog_connected 901 -#define tsip_event_code_dialog_terminating 902 -#define tsip_event_code_dialog_terminated 903 -#define tsip_event_code_stack_starting 950 -#define tsip_event_code_stack_started 951 -#define tsip_event_code_stack_stopping 952 -#define tsip_event_code_stack_stopped 953 -#define tsip_event_code_stack_failed_to_start 954 -#define tsip_event_code_stack_failed_to_stop 955 -#define tsip_event_code_stack_disconnected 956 - - -typedef struct tsip_event_s { - TSK_DECLARE_OBJECT; - - tsip_ssession_handle_t* ss; - - short code; - char *phrase; - - tsip_event_type_t type; - struct tsip_message_s *sipmessage; - - //! copy of stack user data (needed by sessionless events) - const void* userdata; -} -tsip_event_t; -#define TSIP_DECLARE_EVENT tsip_event_t __sipevent__ - -TINYSIP_GEXTERN const tsk_object_def_t *tsip_event_def_t; - -int tsip_event_init(tsip_event_t* self, tsip_ssession_t* ss, short code, const char *phrase, const struct tsip_message_s* sipmessage, tsip_event_type_t type); -int tsip_event_signal(tsip_event_type_t type, tsip_ssession_t* ss, short code, const char *phrase); -int tsip_event_signal_2(tsip_event_type_t type, tsip_ssession_t* ss, short code, const char *phrase, const struct tsip_message_s* sipmessage); -int tsip_event_deinit(tsip_event_t* self); - -typedef int (*tsip_stack_callback_f)(const tsip_event_t *sipevent); - -TSIP_END_DECLS - -#endif /* TINYSIP_TSIP_EVENT_H */ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tsip_event.h + * @brief SIP event. + * + * @author Mamadou Diop + * + + */ +#ifndef TINYSIP_TSIP_EVENT_H +#define TINYSIP_TSIP_EVENT_H + +#include "tinysip_config.h" + +#include "tinysip/tsip_ssession.h" + +TSIP_BEGIN_DECLS + +#define TSIP_EVENT(self) ((tsip_event_t*)(self)) + +typedef enum tsip_event_type_e { + tsip_event_invite, + tsip_event_message, + tsip_event_info, + tsip_event_options, + tsip_event_publish, + tsip_event_register, + tsip_event_subscribe, + + tsip_event_dialog, + tsip_event_stack, +} +tsip_event_type_t; + +/* SIP codes associated to an internal event */ +// 100-699 are reserved codes + +// 7xx ==> errors @tinyWRAP +#define tsip_event_code_dialog_transport_error 702 +#define tsip_event_code_dialog_global_error 703 +#define tsip_event_code_dialog_message_error 704 + +// 8xx ==> success @tinyWRAP +#define tsip_event_code_dialog_request_incoming 800 +#define tsip_event_code_dialog_request_outgoing 802 +#define tsip_event_code_dialog_request_cancelled 803 +#define tsip_event_code_dialog_request_sent 804 +#define tsip_event_code_dialog_request_prechecking 805 + +// 9xx ==> Informational @tinyWRAP +#define tsip_event_code_dialog_connecting 900 +#define tsip_event_code_dialog_connected 901 +#define tsip_event_code_dialog_terminating 902 +#define tsip_event_code_dialog_terminated 903 +#define tsip_event_code_stack_starting 950 +#define tsip_event_code_stack_started 951 +#define tsip_event_code_stack_stopping 952 +#define tsip_event_code_stack_stopped 953 +#define tsip_event_code_stack_failed_to_start 954 +#define tsip_event_code_stack_failed_to_stop 955 +#define tsip_event_code_stack_disconnected 956 + + +typedef struct tsip_event_s { + TSK_DECLARE_OBJECT; + + tsip_ssession_handle_t* ss; + + short code; + char *phrase; + + tsip_event_type_t type; + struct tsip_message_s *sipmessage; + + //! copy of stack user data (needed by sessionless events) + const void* userdata; +} +tsip_event_t; +#define TSIP_DECLARE_EVENT tsip_event_t __sipevent__ + +TINYSIP_GEXTERN const tsk_object_def_t *tsip_event_def_t; + +int tsip_event_init(tsip_event_t* self, tsip_ssession_t* ss, short code, const char *phrase, const struct tsip_message_s* sipmessage, tsip_event_type_t type); +int tsip_event_signal(tsip_event_type_t type, tsip_ssession_t* ss, short code, const char *phrase); +int tsip_event_signal_2(tsip_event_type_t type, tsip_ssession_t* ss, short code, const char *phrase, const struct tsip_message_s* sipmessage); +int tsip_event_deinit(tsip_event_t* self); + +typedef int (*tsip_stack_callback_f)(const tsip_event_t *sipevent); + +TSIP_END_DECLS + +#endif /* TINYSIP_TSIP_EVENT_H */ diff --git a/tinySIP/include/tinysip/tsip_ssession.h b/tinySIP/include/tinysip/tsip_ssession.h index b144363d..c4a7d123 100755 --- a/tinySIP/include/tinysip/tsip_ssession.h +++ b/tinySIP/include/tinysip/tsip_ssession.h @@ -1,298 +1,299 @@ -/* -* Copyright (C) 2010-2011 Mamadou Diop. -* -* Contact: Mamadou Diop -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with DOUBANGO. -* -*/ - -/**@file tsip_ssession.h - * @brief SIP ssession. - * - * @author Mamadou Diop - * - - */ -#ifndef TSIP_SSESSION_H -#define TSIP_SSESSION_H - -#include "tinysip_config.h" - -#include "tinymedia/tmedia_common.h" -#include "tinymedia/tmedia_session.h" - -#include "tsk_object.h" -#include "tsk_list.h" -#include "tsk_params.h" -#include "tsk_options.h" - -TSIP_BEGIN_DECLS - -// Forward declarations -struct tsip_message_s; -struct tsip_action_s; - -typedef uint64_t tsip_ssession_id_t; -#define TSIP_SSESSION_INVALID_ID 0 -#define TSIP_SSESSION_INVALID_HANDLE tsk_null - -#if defined(DEBUG) || defined(_DEBUG) -# define TSIP_SSESSION_EXPIRES_DEFAULT 3600000 /* miliseconds. */ -#else -# define TSIP_SSESSION_EXPIRES_DEFAULT 600000000 -#endif - - -#define TSIP_SSESSION(self) ((tsip_ssession_t*)(self)) - -typedef enum tsip_ssession_param_type_e { - sstype_null = 0, - - sstype_header, - sstype_caps, - sstype_userdata, - sstype_to_str, - sstype_from_str, - sstype_to_obj, - sstype_from_obj, - sstype_nocontact, - sstype_expires, - sstype_silent_hangup, - sstype_sigcomp_id, - sstype_auth_ha1, - sstype_auth_impi, - sstype_parent_id, - sstype_ws_src, - sstype_media -} -tsip_ssession_param_type_t; - -#define TSIP_SSESSION_SET_PARENT_ID(PARENT_ID_SSID) sstype_parent_id, ((tsip_ssession_id_t)PARENT_ID_SSID) -#define TSIP_SSESSION_SET_HEADER(NAME_STR, VALUE_STR) sstype_header, (const char*)NAME_STR, (const char*)VALUE_STR -#define TSIP_SSESSION_UNSET_HEADER(NAME_STR) TSIP_SSESSION_SET_HEADER(NAME_STR, (const char*)-1) -#define TSIP_SSESSION_SET_CAPS(NAME_STR, VALUE_STR) sstype_caps, (const char*)NAME_STR, (const char*)VALUE_STR /* RFC 3840 */ -#define TSIP_SSESSION_UNSET_CAPS(NAME_STR) TSIP_SSESSION_SET_CAPS(NAME_STR, (const char*)-1) -#define TSIP_SSESSION_SET_USERDATA(DATA_PTR) sstype_userdata, (const void*)DATA_PTR -#define TSIP_SSESSION_SET_TO_STR(URI_STR) sstype_to_str, (const char*)URI_STR -#define TSIP_SSESSION_SET_FROM_STR(URI_STR) sstype_from_str, (const char*)URI_STR -#define TSIP_SSESSION_SET_TO(URI_STR) TSIP_SSESSION_SET_TO_STR(URI_STR) -#define TSIP_SSESSION_SET_FROM(URI_STR) TSIP_SSESSION_SET_FROM_STR(URI_STR) -#define TSIP_SSESSION_SET_TO_OBJ(URI_OBJ) sstype_to_obj, (const tsip_uri_t*)URI_OBJ -#define TSIP_SSESSION_SET_FROM_OBJ(URI_OBJ) sstype_from_obj, (const tsip_uri_t*)URI_OBJ -#define TSIP_SSESSION_SET_NO_CONTACT(ENABLED_BOOL) sstype_nocontact, (tsk_bool_t)ENABLED_BOOL -#define TSIP_SSESSION_SET_EXPIRES(VALUE_UINT) sstype_expires, (unsigned)VALUE_UINT -#define TSIP_SSESSION_SET_SILENT_HANGUP(ENABLED_BOOL) sstype_silent_hangup, (tsk_bool_t)ENABLED_BOOL -#define TSIP_SSESSION_SET_SIGCOMP_COMPARTMENT(COMPARTMENT_ID_STR) sstype_sigcomp_id, (const char*)COMPARTMENT_ID_STR -#define TSIP_SSESSION_UNSET_SIGCOMP_COMPARTMENT() TSIP_SSESSION_SET_SIGCOMP_COMPARTMENT((const char*)-1) -#define TSIP_SSESSION_SET_AUTH_HA1(AUTH_HA1_STR) sstype_auth_ha1, (const char*)AUTH_HA1_STR -#define TSIP_SSESSION_SET_AUTH_IMPI(AUTH_IMPI_STR) sstype_auth_impi, (const char*)AUTH_IMPI_STR -#define TSIP_SSESSION_SET_WEBSOCKET_SRC(SRC_HOST_STR, SRC_PORT_INT, SRC_PROTO_STR) sstype_ws_src, (const char*)SRC_HOST_STR, (int32_t)SRC_PORT_INT, (const char*)SRC_PROTO_STR -#define TSIP_SSESSION_SET_MEDIA(...) sstype_media, ##__VA_ARGS__ -#define TSIP_SSESSION_SET_NULL() sstype_null - -typedef enum tsip_msession_param_type_e { - mstype_null = 0, - - mstype_set_profile, - mstype_set_srtp_mode, - mstype_set_avpf_mode, - - mstype_set_100rel, - mstype_set_rtcp, - mstype_set_rtcpmux, - mstype_set_ice, - mstype_set_ice_stun, - mstype_set_ice_turn, - mstype_set_stun_server, - mstype_set_stun_cred, - - mstype_set_video_fps, - mstype_set_video_bw_up, - mstype_set_video_bw_down, - mstype_set_video_prefsize, - - mstype_set_qos, - mstype_unset_qos, - - mstype_set_timers, - mstype_unset_timers, - - mstype_set_codecs, - - mstype_set_bypass_encoding, - mstype_set_bypass_decoding, - - mstype_set_rtp_ssrc, - - mstype_set_msrp_cb -} -tsip_msession_param_type_t; - -#define TSIP_MSESSION_SET_SRTP_MODE(SRTP_MODE_ENUM) mstype_set_srtp_mode, (tmedia_srtp_mode_t)SRTP_MODE_ENUM -#define TSIP_MSESSION_SET_AVPF_MODE(MEDIA_MODE_ENUM) mstype_set_avpf_mode, (tmedia_mode_t)MEDIA_MODE_ENUM -#define TSIP_MSESSION_SET_PROFILE(PROFILE_ENUM) mstype_set_srtp_mode, (tmedia_profile_t)PROFILE_ENUM -#define TSIP_MSESSION_SET_100rel(ENABLED_BOOL) mstype_set_100rel, (tsk_bool_t)ENABLED_BOOL -#define TSIP_MSESSION_SET_RTCP(ENABLED_BOOL) mstype_set_rtcp, (tsk_bool_t)ENABLED_BOOL -#define TSIP_MSESSION_SET_RTCPMUX(ENABLED_BOOL) mstype_set_rtcpmux, (tsk_bool_t)ENABLED_BOOL -#define TSIP_MSESSION_SET_ICE(ENABLED_BOOL) mstype_set_ice, (tsk_bool_t)ENABLED_BOOL -#define TSIP_MSESSION_SET_ICE_STUN(ENABLED_BOOL) mstype_set_ice_stun, (tsk_bool_t)ENABLED_BOOL -#define TSIP_MSESSION_SET_ICE_TURN(ENABLED_BOOL) mstype_set_ice_turn, (tsk_bool_t)ENABLED_BOOL -#define TSIP_MSESSION_SET_STUN_SERVER(HOSTNAME, PORT) mstype_set_stun_server, (const char*)HOSTNAME, (uint16_t)PORT -#define TSIP_MSESSION_SET_STUN_CRED(USERNAME, PASSWORD) mstype_set_stun_cred, (const char*)USERNAME, (const char*)PASSWORD -#define TSIP_MSESSION_SET_QOS(TYPE_ENUM, STRENGTH_ENUM) mstype_set_qos, (tmedia_qos_stype_t)TYPE_ENUM, (tmedia_qos_strength_t)STRENGTH_ENUM -#define TSIP_MSESSION_UNSET_QOS() mstype_unset_qos -#define TSIP_MSESSION_SET_VIDEO_FPS(FPS_INT) mstype_set_video_fps, (int32_t)FPS_INT -#define TSIP_MSESSION_SET_VIDEO_BW_UP(BW_INT) mstype_set_video_bw_up, (int32_t)(BW_INT) -#define TSIP_MSESSION_SET_VIDEO_BW_DOWN(BW_INT) mstype_set_video_bw_down, (int32_t)(BW_INT) -#define TSIP_MSESSION_SET_VIDEO_PREFSIZE(PREFSIZE_ENUM) mstype_set_video_prefsize, (int32_t)(PREFSIZE_ENUM) -#define TSIP_MSESSION_SET_TIMERS(TIMEOUT_UINT, REFRESHER_STR) mstype_set_timers, (unsigned)TIMEOUT_UINT, (const char*)REFRESHER_STR -#define TSIP_MSESSION_UNSET_TIMERS() mstype_unset_timers -#define TSIP_MSESSION_SET_CODECS(CODECS_INT) mstype_set_codecs, (signed)CODECS_INT -#define TSIP_MSESSION_SET_BYPASS_ENCODING(ENABLED_BOOL) mstype_set_bypass_encoding, (tsk_bool_t)ENABLED_BOOL -#define TSIP_MSESSION_UNSET_BYPASS_ENCODING() TSIP_MSESSION_SET_BYPASS_ENCODING(tsk_false) -#define TSIP_MSESSION_SET_BYPASS_DECODING(ENABLED_BOOL) mstype_set_bypass_decoding, (tsk_bool_t)ENABLED_BOOL -#define TSIP_MSESSION_SET_RTP_SSRC(MEDIA_ENUM, SSRC_UINT) mstype_set_rtp_ssrc, (tmedia_type_t)MEDIA_ENUM, (uint32_t)SSRC_UINT -#define TSIP_MSESSION_SET_RTP_SSRC_AUDIO(SSRC_UINT) TSIP_MSESSION_SET_RTP_SSRC(tmedia_audio, (SSRC_UINT)) -#define TSIP_MSESSION_SET_RTP_SSRC_VIDEO(SSRC_UINT) TSIP_MSESSION_SET_RTP_SSRC(tmedia_video, (SSRC_UINT)) -#define TSIP_MSESSION_UNSET_BYPASS_DECODING() TSIP_MSESSION_SET_BYPASS_DECODING(tsk_false) -#define TSIP_MSESSION_SET_MSRP_CB(TMEDIA_SESSION_MSRP_CB_F) mstype_set_msrp_cb, (tmedia_session_msrp_cb_f)TMEDIA_SESSION_MSRP_CB_F - -#define TSIP_MSESSION_SET_NULL() mstype_null - -typedef struct tsip_ssession_s { - TSK_DECLARE_OBJECT; - - tsip_ssession_id_t id; - tsip_ssession_id_t id_parent; //for call transfer - unsigned owner:1; - - const struct tsip_stack_s* stack; - const void* userdata; - - //======= - // SIP - //======= - tsk_params_L_t *caps; - tsk_params_L_t *headers; - - unsigned no_contact:1; - struct tsip_uri_s* from; - struct tsip_uri_s* to; - int64_t expires; - tsk_bool_t silent_hangup; - char* sigcomp_id; - char* auth_ha1; - char* auth_impi; - - //======= - // WebSocket - //======= - struct { - // used when the stack is running in webrtc2sip mode to store the 'origin' (a.k.a 'src') - struct { - char* host; - tsk_istr_t port; - char* proto; - } src; - } ws; - - //======= - // Media - //======= - struct { - tmedia_type_t type; - tmedia_profile_t profile; - tmedia_srtp_mode_t srtp_mode; - tmedia_mode_t avpf_mode; - tmedia_codec_id_t codecs; - tsk_bool_t bypass_encoding; - tsk_bool_t bypass_decoding; - - /* Video */ - int32_t video_fps; - int32_t video_bw_up; - int32_t video_bw_down; - tmedia_pref_video_size_t video_pref_size; - - /* RTP*/ - struct { - struct { - uint32_t audio; - uint32_t video; - } ssrc; - } rtp; - - /* Session timers */ - struct { - char* refresher; - unsigned timeout; - } timers; - /* QoS */ - struct { - tmedia_qos_stype_t type; - tmedia_qos_strength_t strength; - } qos; - /* MSRP */ - struct { - tmedia_session_msrp_cb_f callback; - } msrp; - /* STUN */ - struct { - char* username; - char* password; - char* hostname; - uint16_t port; - } stun; - - /* Features */ - unsigned enable_100rel:1; - unsigned enable_ice:1; - unsigned enable_icestun:1; - unsigned enable_iceturn:1; - unsigned enable_rtcp:1; - unsigned enable_rtcpmux:1; - } media; -} -tsip_ssession_t; - -/** A pointer to a SIP Session */ -typedef void tsip_ssession_handle_t; -/** A pointer to a LTE/IMS stack */ -typedef void tsip_stack_handle_t; - -TINYSIP_API tsip_ssession_handle_t* tsip_ssession_create(tsip_stack_handle_t *stack, ...); -TINYSIP_API int tsip_ssession_set(tsip_ssession_handle_t *self, ...); -TINYSIP_API tsip_ssession_id_t tsip_ssession_get_id(const tsip_ssession_handle_t *self); -TINYSIP_API tsip_ssession_id_t tsip_ssession_get_id_parent(const tsip_ssession_handle_t *self); -TINYSIP_API int tsip_ssession_take_ownership(tsip_ssession_handle_t *self); -TINYSIP_API tsk_bool_t tsip_ssession_have_ownership(const tsip_ssession_handle_t *self); -TINYSIP_API int tsip_ssession_respond(const tsip_ssession_handle_t *self, short status, const char* phrase, const void* payload, tsk_size_t size, const struct tsip_message_s* request, ...); -TINYSIP_API const void* tsip_ssession_get_userdata(const tsip_ssession_handle_t *self); -TINYSIP_API tmedia_type_t tsip_ssession_get_mediatype(const tsip_ssession_handle_t *self); -TINYSIP_API tmedia_session_mgr_t* tsip_session_get_mediamgr(const tsip_ssession_handle_t *self); -TINYSIP_API const tsip_stack_handle_t* tsip_ssession_get_stack(const tsip_ssession_handle_t *self); -TINYSIP_API tmedia_codec_id_t tsip_ssession_get_codecs_neg(tsip_ssession_handle_t *self); - -int tsip_ssession_handle(const tsip_ssession_t *self, const struct tsip_action_s* action); - -typedef tsk_list_t tsip_ssessions_L_t; /**< List of @ref tsip_ssession_handle_t elements. */ -TINYSIP_GEXTERN const tsk_object_def_t *tsip_ssession_def_t; - -TSIP_END_DECLS - -#endif /* TSIP_SSESSION_H */ - +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tsip_ssession.h + * @brief SIP ssession. + * + * @author Mamadou Diop + * + + */ +#ifndef TSIP_SSESSION_H +#define TSIP_SSESSION_H + +#include "tinysip_config.h" + +#include "tinymedia/tmedia_common.h" +#include "tinymedia/tmedia_session.h" + +#include "tsk_object.h" +#include "tsk_list.h" +#include "tsk_params.h" +#include "tsk_options.h" + +TSIP_BEGIN_DECLS + +// Forward declarations +struct tsip_message_s; +struct tsip_action_s; + +typedef uint64_t tsip_ssession_id_t; +#define TSIP_SSESSION_INVALID_ID 0 +#define TSIP_SSESSION_INVALID_HANDLE tsk_null + +#if defined(DEBUG) || defined(_DEBUG) +# define TSIP_SSESSION_EXPIRES_DEFAULT 3600000 /* miliseconds. */ +#else +# define TSIP_SSESSION_EXPIRES_DEFAULT 600000000 +#endif + + +#define TSIP_SSESSION(self) ((tsip_ssession_t*)(self)) + +typedef enum tsip_ssession_param_type_e { + sstype_null = 0, + + sstype_header, + sstype_caps, + sstype_userdata, + sstype_to_str, + sstype_from_str, + sstype_to_obj, + sstype_from_obj, + sstype_nocontact, + sstype_expires, + sstype_silent_hangup, + sstype_sigcomp_id, + sstype_auth_ha1, + sstype_auth_impi, + sstype_parent_id, + sstype_ws_src, + sstype_media +} +tsip_ssession_param_type_t; + +#define TSIP_SSESSION_SET_PARENT_ID(PARENT_ID_SSID) sstype_parent_id, ((tsip_ssession_id_t)PARENT_ID_SSID) +#define TSIP_SSESSION_SET_HEADER(NAME_STR, VALUE_STR) sstype_header, (const char*)NAME_STR, (const char*)VALUE_STR +#define TSIP_SSESSION_UNSET_HEADER(NAME_STR) TSIP_SSESSION_SET_HEADER(NAME_STR, (const char*)-1) +#define TSIP_SSESSION_SET_CAPS(NAME_STR, VALUE_STR) sstype_caps, (const char*)NAME_STR, (const char*)VALUE_STR /* RFC 3840 */ +#define TSIP_SSESSION_UNSET_CAPS(NAME_STR) TSIP_SSESSION_SET_CAPS(NAME_STR, (const char*)-1) +#define TSIP_SSESSION_SET_USERDATA(DATA_PTR) sstype_userdata, (const void*)DATA_PTR +#define TSIP_SSESSION_SET_TO_STR(URI_STR) sstype_to_str, (const char*)URI_STR +#define TSIP_SSESSION_SET_FROM_STR(URI_STR) sstype_from_str, (const char*)URI_STR +#define TSIP_SSESSION_SET_TO(URI_STR) TSIP_SSESSION_SET_TO_STR(URI_STR) +#define TSIP_SSESSION_SET_FROM(URI_STR) TSIP_SSESSION_SET_FROM_STR(URI_STR) +#define TSIP_SSESSION_SET_TO_OBJ(URI_OBJ) sstype_to_obj, (const tsip_uri_t*)URI_OBJ +#define TSIP_SSESSION_SET_FROM_OBJ(URI_OBJ) sstype_from_obj, (const tsip_uri_t*)URI_OBJ +#define TSIP_SSESSION_SET_NO_CONTACT(ENABLED_BOOL) sstype_nocontact, (tsk_bool_t)ENABLED_BOOL +#define TSIP_SSESSION_SET_EXPIRES(VALUE_UINT) sstype_expires, (unsigned)VALUE_UINT +#define TSIP_SSESSION_SET_SILENT_HANGUP(ENABLED_BOOL) sstype_silent_hangup, (tsk_bool_t)ENABLED_BOOL +#define TSIP_SSESSION_SET_SIGCOMP_COMPARTMENT(COMPARTMENT_ID_STR) sstype_sigcomp_id, (const char*)COMPARTMENT_ID_STR +#define TSIP_SSESSION_UNSET_SIGCOMP_COMPARTMENT() TSIP_SSESSION_SET_SIGCOMP_COMPARTMENT((const char*)-1) +#define TSIP_SSESSION_SET_AUTH_HA1(AUTH_HA1_STR) sstype_auth_ha1, (const char*)AUTH_HA1_STR +#define TSIP_SSESSION_SET_AUTH_IMPI(AUTH_IMPI_STR) sstype_auth_impi, (const char*)AUTH_IMPI_STR +#define TSIP_SSESSION_SET_WEBSOCKET_SRC(SRC_HOST_STR, SRC_PORT_INT, SRC_PROTO_STR) sstype_ws_src, (const char*)SRC_HOST_STR, (int32_t)SRC_PORT_INT, (const char*)SRC_PROTO_STR +#define TSIP_SSESSION_SET_MEDIA(...) sstype_media, ##__VA_ARGS__ +#define TSIP_SSESSION_SET_NULL() sstype_null + +typedef enum tsip_msession_param_type_e { + mstype_null = 0, + + mstype_set_profile, + mstype_set_srtp_mode, + mstype_set_avpf_mode, + + mstype_set_100rel, + mstype_set_rtcp, + mstype_set_rtcpmux, + mstype_set_ice, + mstype_set_ice_stun, + mstype_set_ice_turn, + mstype_set_stun_server, + mstype_set_stun_cred, + + mstype_set_video_fps, + mstype_set_video_bw_up, + mstype_set_video_bw_down, + mstype_set_video_prefsize, + + mstype_set_qos, + mstype_unset_qos, + + mstype_set_timers, + mstype_unset_timers, + + mstype_set_codecs, + + mstype_set_bypass_encoding, + mstype_set_bypass_decoding, + + mstype_set_rtp_ssrc, + + mstype_set_msrp_cb +} +tsip_msession_param_type_t; + +#define TSIP_MSESSION_SET_SRTP_MODE(SRTP_MODE_ENUM) mstype_set_srtp_mode, (tmedia_srtp_mode_t)SRTP_MODE_ENUM +#define TSIP_MSESSION_SET_AVPF_MODE(MEDIA_MODE_ENUM) mstype_set_avpf_mode, (tmedia_mode_t)MEDIA_MODE_ENUM +#define TSIP_MSESSION_SET_PROFILE(PROFILE_ENUM) mstype_set_srtp_mode, (tmedia_profile_t)PROFILE_ENUM +#define TSIP_MSESSION_SET_100rel(ENABLED_BOOL) mstype_set_100rel, (tsk_bool_t)ENABLED_BOOL +#define TSIP_MSESSION_SET_RTCP(ENABLED_BOOL) mstype_set_rtcp, (tsk_bool_t)ENABLED_BOOL +#define TSIP_MSESSION_SET_RTCPMUX(ENABLED_BOOL) mstype_set_rtcpmux, (tsk_bool_t)ENABLED_BOOL +#define TSIP_MSESSION_SET_ICE(ENABLED_BOOL) mstype_set_ice, (tsk_bool_t)ENABLED_BOOL +#define TSIP_MSESSION_SET_ICE_STUN(ENABLED_BOOL) mstype_set_ice_stun, (tsk_bool_t)ENABLED_BOOL +#define TSIP_MSESSION_SET_ICE_TURN(ENABLED_BOOL) mstype_set_ice_turn, (tsk_bool_t)ENABLED_BOOL +#define TSIP_MSESSION_SET_STUN_SERVER(HOSTNAME, PORT) mstype_set_stun_server, (const char*)HOSTNAME, (uint16_t)PORT +#define TSIP_MSESSION_SET_STUN_CRED(USERNAME, PASSWORD) mstype_set_stun_cred, (const char*)USERNAME, (const char*)PASSWORD +#define TSIP_MSESSION_SET_QOS(TYPE_ENUM, STRENGTH_ENUM) mstype_set_qos, (tmedia_qos_stype_t)TYPE_ENUM, (tmedia_qos_strength_t)STRENGTH_ENUM +#define TSIP_MSESSION_UNSET_QOS() mstype_unset_qos +#define TSIP_MSESSION_SET_VIDEO_FPS(FPS_INT) mstype_set_video_fps, (int32_t)FPS_INT +#define TSIP_MSESSION_SET_VIDEO_BW_UP(BW_INT) mstype_set_video_bw_up, (int32_t)(BW_INT) +#define TSIP_MSESSION_SET_VIDEO_BW_DOWN(BW_INT) mstype_set_video_bw_down, (int32_t)(BW_INT) +#define TSIP_MSESSION_SET_VIDEO_PREFSIZE(PREFSIZE_ENUM) mstype_set_video_prefsize, (int32_t)(PREFSIZE_ENUM) +#define TSIP_MSESSION_SET_TIMERS(TIMEOUT_UINT, REFRESHER_STR) mstype_set_timers, (unsigned)TIMEOUT_UINT, (const char*)REFRESHER_STR +#define TSIP_MSESSION_UNSET_TIMERS() mstype_unset_timers +#define TSIP_MSESSION_SET_CODECS(CODECS_INT) mstype_set_codecs, (signed)CODECS_INT +#define TSIP_MSESSION_SET_BYPASS_ENCODING(ENABLED_BOOL) mstype_set_bypass_encoding, (tsk_bool_t)ENABLED_BOOL +#define TSIP_MSESSION_UNSET_BYPASS_ENCODING() TSIP_MSESSION_SET_BYPASS_ENCODING(tsk_false) +#define TSIP_MSESSION_SET_BYPASS_DECODING(ENABLED_BOOL) mstype_set_bypass_decoding, (tsk_bool_t)ENABLED_BOOL +#define TSIP_MSESSION_SET_RTP_SSRC(MEDIA_ENUM, SSRC_UINT) mstype_set_rtp_ssrc, (tmedia_type_t)MEDIA_ENUM, (uint32_t)SSRC_UINT +#define TSIP_MSESSION_SET_RTP_SSRC_AUDIO(SSRC_UINT) TSIP_MSESSION_SET_RTP_SSRC(tmedia_audio, (SSRC_UINT)) +#define TSIP_MSESSION_SET_RTP_SSRC_VIDEO(SSRC_UINT) TSIP_MSESSION_SET_RTP_SSRC(tmedia_video, (SSRC_UINT)) +#define TSIP_MSESSION_UNSET_BYPASS_DECODING() TSIP_MSESSION_SET_BYPASS_DECODING(tsk_false) +#define TSIP_MSESSION_SET_MSRP_CB(TMEDIA_SESSION_MSRP_CB_F) mstype_set_msrp_cb, (tmedia_session_msrp_cb_f)TMEDIA_SESSION_MSRP_CB_F + +#define TSIP_MSESSION_SET_NULL() mstype_null + +typedef struct tsip_ssession_s { + TSK_DECLARE_OBJECT; + + tsip_ssession_id_t id; + tsip_ssession_id_t id_parent; //for call transfer + unsigned owner:1; + + const struct tsip_stack_s* stack; + const void* userdata; + + //======= + // SIP + //======= + tsk_params_L_t *caps; + tsk_params_L_t *headers; + + unsigned no_contact:1; + struct tsip_uri_s* from; + struct tsip_uri_s* to; + int64_t expires; + tsk_bool_t silent_hangup; + char* sigcomp_id; + char* auth_ha1; + char* auth_impi; + + //======= + // WebSocket + //======= + struct { + // used when the stack is running in webrtc2sip mode to store the 'origin' (a.k.a 'src') + struct { + char* host; + tsk_istr_t port; + char* proto; + } src; + } ws; + + //======= + // Media + //======= + struct { + tmedia_type_t type; + tmedia_profile_t profile; + tmedia_srtp_mode_t srtp_mode; + tmedia_mode_t avpf_mode; + tmedia_codec_id_t codecs; + tsk_bool_t bypass_encoding; + tsk_bool_t bypass_decoding; + + /* Video */ + int32_t video_fps; + int32_t video_bw_up; + int32_t video_bw_down; + tmedia_pref_video_size_t video_pref_size; + + /* RTP*/ + struct { + struct { + uint32_t audio; + uint32_t video; + } ssrc; + } rtp; + + /* Session timers */ + struct { + char* refresher; + unsigned timeout; + } timers; + /* QoS */ + struct { + tmedia_qos_stype_t type; + tmedia_qos_strength_t strength; + } qos; + /* MSRP */ + struct { + tmedia_session_msrp_cb_f callback; + } msrp; + /* STUN */ + struct { + char* username; + char* password; + char* hostname; + uint16_t port; + } stun; + + /* Features */ + unsigned enable_conditional_ringing:1; + unsigned enable_100rel:1; + unsigned enable_ice:1; + unsigned enable_icestun:1; + unsigned enable_iceturn:1; + unsigned enable_rtcp:1; + unsigned enable_rtcpmux:1; + } media; +} +tsip_ssession_t; + +/** A pointer to a SIP Session */ +typedef void tsip_ssession_handle_t; +/** A pointer to a LTE/IMS stack */ +typedef void tsip_stack_handle_t; + +TINYSIP_API tsip_ssession_handle_t* tsip_ssession_create(tsip_stack_handle_t *stack, ...); +TINYSIP_API int tsip_ssession_set(tsip_ssession_handle_t *self, ...); +TINYSIP_API tsip_ssession_id_t tsip_ssession_get_id(const tsip_ssession_handle_t *self); +TINYSIP_API tsip_ssession_id_t tsip_ssession_get_id_parent(const tsip_ssession_handle_t *self); +TINYSIP_API int tsip_ssession_take_ownership(tsip_ssession_handle_t *self); +TINYSIP_API tsk_bool_t tsip_ssession_have_ownership(const tsip_ssession_handle_t *self); +TINYSIP_API int tsip_ssession_respond(const tsip_ssession_handle_t *self, short status, const char* phrase, const void* payload, tsk_size_t size, const struct tsip_message_s* request, ...); +TINYSIP_API const void* tsip_ssession_get_userdata(const tsip_ssession_handle_t *self); +TINYSIP_API tmedia_type_t tsip_ssession_get_mediatype(const tsip_ssession_handle_t *self); +TINYSIP_API tmedia_session_mgr_t* tsip_session_get_mediamgr(const tsip_ssession_handle_t *self); +TINYSIP_API const tsip_stack_handle_t* tsip_ssession_get_stack(const tsip_ssession_handle_t *self); +TINYSIP_API tmedia_codec_id_t tsip_ssession_get_codecs_neg(tsip_ssession_handle_t *self); + +int tsip_ssession_handle(const tsip_ssession_t *self, const struct tsip_action_s* action); + +typedef tsk_list_t tsip_ssessions_L_t; /**< List of @ref tsip_ssession_handle_t elements. */ +TINYSIP_GEXTERN const tsk_object_def_t *tsip_ssession_def_t; + +TSIP_END_DECLS + +#endif /* TSIP_SSESSION_H */ + diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.c b/tinySIP/src/dialogs/tsip_dialog_invite.c index 73bbb2d2..8122b59d 100755 --- a/tinySIP/src/dialogs/tsip_dialog_invite.c +++ b/tinySIP/src/dialogs/tsip_dialog_invite.c @@ -1,1938 +1,1939 @@ -/* -* Copyright (C) 2010-2011 Mamadou Diop. -* -* Contact: Mamadou Diop -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as publishd by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with DOUBANGO. -* -*/ - -/**@file tsip_dialog_invite.c - * @brief SIP dialog INVITE as per RFC 3261. - * The SOA machine is designed as per RFC 3264 and draft-ietf-sipping-sip-offeranswer-12. - * MMTel services implementation follow 3GPP TS 24.173. - * - * @author Mamadou Diop - * - - */ -#include "tinysip/dialogs/tsip_dialog_invite.h" - -#include "tinysip/dialogs/tsip_dialog_invite.common.h" - -#include "tinysip/transactions/tsip_transac_layer.h" -#include "tinysip/transports/tsip_transport_layer.h" -#include "tinysip/dialogs/tsip_dialog_layer.h" - -#include "tinysip/headers/tsip_header_Allow.h" -#include "tinysip/headers/tsip_header_Dummy.h" -#include "tinysip/headers/tsip_header_Max_Forwards.h" -#include "tinysip/headers/tsip_header_Min_SE.h" -#include "tinysip/headers/tsip_header_RAck.h" -#include "tinysip/headers/tsip_header_Require.h" -#include "tinysip/headers/tsip_header_RSeq.h" -#include "tinysip/headers/tsip_header_Session_Expires.h" -#include "tinysip/headers/tsip_header_Supported.h" - -#include "tinysdp/parsers/tsdp_parser_message.h" - -#include "tinymedia/tmedia_defaults.h" - -#include "tsk_debug.h" - -#if METROPOLIS -# define TSIP_INFO_FASTUPDATE_OUT_INTERVAL_MIN 0 // millis -#else -# define TSIP_INFO_FASTUPDATE_OUT_INTERVAL_MIN 1500 // millis -#endif - -#if HAVE_LIBXML2 -#include -#include -#include -#include -#endif - -// http://cdnet.stpi.org.tw/techroom/market/_pdf/2009/eetelecomm_09_009_OneVoiceProfile.pdf -// 3GPP TS 26.114 (MMTel): Media handling and interaction -// 3GPP TS 24.173 (MMTel): Supplementary Services -// -/* ======================== MMTel Supplementary Services ======================== -3GPP TS 24.607 : Originating Identification Presentation -3GPP TS 24.608 : Terminating Identification Presentation -3GPP TS 24.607 : Originating Identification Restriction -3GPP TS 24.608 : Terminating Identification Restriction - -3GPP TS 24.604 : Communication Diversion Unconditional -3GPP TS 24.604 : Communication Diversion on not Logged -3GPP TS 24.604 : Communication Diversion on Busy -3GPP TS 24.604 : Communication Diversion on not Reachable -3GPP TS 24.604 : Communication Diversion on No Reply -3GPP TS 24.611 : Barring of All Incoming Calls -3GPP TS 24.611 : Barring of All Outgoing Calls -3GPP TS 24.611 : Barring of Outgoing International Calls -3GPP TS 24.611 : Barring of Incoming Calls - When Roaming -3GPP TS 24.610 : Communication Hold -3GPP TS 24.606 : Message Waiting Indication -3GPP TS 24.615 : Communication Waiting -3GPP TS 24.605 : Ad-Hoc Multi Party Conference -*/ - -extern int tsip_dialog_add_session_headers(const tsip_dialog_t *self, tsip_request_t* request); - -/* ======================== internal functions ======================== */ -/*static*/ int tsip_dialog_invite_msession_start(tsip_dialog_invite_t *self); -/*static*/ int tsip_dialog_invite_msession_configure(tsip_dialog_invite_t *self); -/*static*/ int send_INVITEorUPDATE(tsip_dialog_invite_t *self, tsk_bool_t is_INVITE, tsk_bool_t force_sdp); -/*static*/ int send_PRACK(tsip_dialog_invite_t *self, const tsip_response_t* r1xx); -/*static*/ int send_ACK(tsip_dialog_invite_t *self, const tsip_response_t* r2xxINVITE); -/*static*/ int send_RESPONSE(tsip_dialog_invite_t *self, const tsip_request_t* request, short code, const char* phrase, tsk_bool_t force_sdp); -/*static*/ int send_ERROR(tsip_dialog_invite_t* self, const tsip_request_t* request, short code, const char* phrase, const char* reason); -/*static*/ int send_BYE(tsip_dialog_invite_t *self); -/*static*/ int send_CANCEL(tsip_dialog_invite_t *self); -/*static*/ int tsip_dialog_invite_notify_parent(tsip_dialog_invite_t *self, const tsip_response_t* response); -/*static*/ int send_INFO(tsip_dialog_invite_t *self, const char* content_type, const void* content_ptr, tsk_size_t content_size); -static int tsip_dialog_invite_OnTerminated(tsip_dialog_invite_t *self); -static int tsip_dialog_invite_msession_onerror_cb(const void* usrdata, const struct tmedia_session_s* session, const char* reason, tsk_bool_t is_fatal); -static int tsip_dialog_invite_msession_rfc5168_cb(const void* usrdata, const struct tmedia_session_s* session, const char* reason, enum tmedia_session_rfc5168_cmd_e command); - -/* ======================== external functions ======================== */ -extern int tsip_dialog_invite_ice_process_ro(tsip_dialog_invite_t * self, const tsdp_message_t* sdp_ro, tsk_bool_t is_remote_offer); -extern int tsip_dialog_invite_ice_set_media_type(tsip_dialog_invite_t * self, tmedia_type_t media_type); -extern int tsip_dialog_invite_stimers_cancel(tsip_dialog_invite_t* self); -extern int tsip_dialog_invite_qos_timer_cancel(tsip_dialog_invite_t* self); -extern int tsip_dialog_invite_qos_timer_schedule(tsip_dialog_invite_t* self); -extern int tsip_dialog_invite_stimers_schedule(tsip_dialog_invite_t* self, uint64_t timeout); -extern int tsip_dialog_invite_stimers_handle(tsip_dialog_invite_t* self, const tsip_message_t* message); -extern int tsip_dialog_invite_hold_handle(tsip_dialog_invite_t* self, const tsip_request_t* rINVITEorUPDATE); - -extern int tsip_dialog_invite_ice_timers_set(tsip_dialog_invite_t *self, int64_t timeout); -extern tsk_bool_t tsip_dialog_invite_ice_is_enabled(const tsip_dialog_invite_t * self); -extern tsk_bool_t tsip_dialog_invite_ice_is_connected(const tsip_dialog_invite_t * self); -extern int tsip_dialog_invite_ice_process_lo(tsip_dialog_invite_t * self, const tsdp_message_t* sdp_lo); - -/* ======================== transitions ======================== */ -static int x0000_Connected_2_Connected_X_oDTMF(va_list *app); -static int x0000_Connected_2_Connected_X_oLMessage(va_list *app); -static int x0000_Connected_2_Connected_X_iACK(va_list *app); -static int x0000_Connected_2_Connected_X_iINVITEorUPDATE(va_list *app); -static int x0000_Connected_2_Connected_X_oINVITE(va_list *app); - - -static int x0000_Any_2_Any_X_i1xx(va_list *app); -static int x0000_Any_2_Any_X_oINFO(va_list *app); -static int x0000_Any_2_Any_X_iINFO(va_list *app); -static int x0000_Any_2_Any_X_i401_407_Challenge(va_list *app); -static int x0000_Any_2_Any_X_i2xxINVITEorUPDATE(va_list *app); - -static int x0000_Any_2_Any_X_iPRACK(va_list *app); -static int x0000_Any_2_Any_X_iOPTIONS(va_list *app); -static int x0000_Any_2_Trying_X_oBYE(va_list *app); /* If not Connected => Cancel will be called instead. See tsip_dialog_hangup() */ -static int x0000_Any_2_Terminated_X_iBYE(va_list *app); -static int x0000_Any_2_Trying_X_shutdown(va_list *app); - -static int x9998_Any_2_Terminated_X_transportError(va_list *app); -static int x9999_Any_2_Any_X_Error(va_list *app); - -/* ======================== conds ======================== */ -static tsk_bool_t _fsm_cond_is_resp2INVITE(tsip_dialog_invite_t* self, tsip_message_t* message) -{ - return TSIP_RESPONSE_IS_TO_INVITE(message); -} -static tsk_bool_t _fsm_cond_is_resp2UPDATE(tsip_dialog_invite_t* self, tsip_message_t* message) -{ - return TSIP_RESPONSE_IS_TO_UPDATE(message); -} -static tsk_bool_t _fsm_cond_is_resp2BYE(tsip_dialog_invite_t* self, tsip_message_t* message) -{ - return TSIP_RESPONSE_IS_TO_BYE(message); -} -static tsk_bool_t _fsm_cond_is_resp2PRACK(tsip_dialog_invite_t* self, tsip_message_t* message) -{ - return TSIP_RESPONSE_IS_TO_PRACK(message); -} -static tsk_bool_t _fsm_cond_is_resp2INFO(tsip_dialog_invite_t* self, tsip_message_t* message) -{ - return TSIP_RESPONSE_IS_TO_INFO(message); -} - -/* ======================== actions ======================== */ -/* #include "tinysip/dialogs/tsip_dialog_invite.common.h" */ - -/* ======================== states ======================== */ -/* #include "tinysip/dialogs/tsip_dialog_invite.common.h" */ - -/* ICE handler */ -extern int tsip_dialog_invite_ice_init(tsip_dialog_invite_t *self); -/* Client-Side dialog */ -extern int tsip_dialog_invite_client_init(tsip_dialog_invite_t *self); -/* Server-Side dialog */ -extern int tsip_dialog_invite_server_init(tsip_dialog_invite_t *self); -/* 3GPP TS 24.610: Communication Hold */ -extern int tsip_dialog_invite_hold_init(tsip_dialog_invite_t *self); -/* 3GPP TS 24.629: Explicit Communication Transfer (ECT) using IP Multimedia (IM) Core Network (CN) subsystem */ -extern int tsip_dialog_invite_ect_init(tsip_dialog_invite_t *self); -/* RFC 4028: Session Timers */ -extern int tsip_dialog_invite_stimers_init(tsip_dialog_invite_t *self); -/* RFC 3312: Integration of Resource Management and Session Initiation Protocol (SIP) */ -extern int tsip_dialog_invite_qos_init(tsip_dialog_invite_t *self); - -int tsip_dialog_invite_event_callback(const tsip_dialog_invite_t *self, tsip_dialog_event_type_t type, const tsip_message_t *msg) -{ - int ret = -1; - - switch(type) { - case tsip_dialog_i_msg: { - if(msg) { - if(TSIP_MESSAGE_IS_RESPONSE(msg)) { /* Response */ - const tsip_action_t* action = tsip_dialog_keep_action(TSIP_DIALOG(self), msg) ? TSIP_DIALOG(self)->curr_action : tsk_null; - if(TSIP_RESPONSE_IS_1XX(msg)) { // 100-199 - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_i1xx, msg, action); - } - else if(TSIP_RESPONSE_IS_2XX(msg)) { // 200-299 - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_i2xx, msg, action); - } - else if(TSIP_RESPONSE_CODE(msg) == 401 || TSIP_RESPONSE_CODE(msg) == 407) { // 401,407 - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_i401_i407, msg, action); - } - else if(TSIP_RESPONSE_CODE(msg) == 422) { // 422 - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_i422, msg, action); - } - else if(TSIP_RESPONSE_IS_3456(msg)) { // 300-699 - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_i300_to_i699, msg, action); - } - else; // Ignore - } - else { /* Request */ - if(TSIP_REQUEST_IS_INVITE(msg)) { // INVITE - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iINVITE, msg, tsk_null); - } - else if(TSIP_REQUEST_IS_UPDATE(msg)) { // UPDATE - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iUPDATE, msg, tsk_null); - } - else if(TSIP_REQUEST_IS_PRACK(msg)) { // PRACK - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iPRACK, msg, tsk_null); - } - else if(TSIP_REQUEST_IS_ACK(msg)) { // ACK - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iACK, msg, tsk_null); - } - else if(TSIP_REQUEST_IS_OPTIONS(msg)) { // OPTIONS - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iOPTIONS, msg, tsk_null); - } - else if(TSIP_REQUEST_IS_BYE(msg)) { // BYE - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iBYE, msg, tsk_null); - } - else if(TSIP_REQUEST_IS_CANCEL(msg)) { // CANCEL - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iCANCEL, msg, tsk_null); - } - else if(TSIP_REQUEST_IS_INFO(msg)) { // INFO - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iINFO, msg, tsk_null); - } - else if(TSIP_REQUEST_IS_NOTIFY(msg)) { // NOTIFY - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iNOTIFY, msg, tsk_null); - } - else if(TSIP_REQUEST_IS_REFER(msg)) { // REFER - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iREFER, msg, tsk_null); - } - } - } - break; - } - - case tsip_dialog_canceled: { - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_oCANCEL, msg, tsk_null); - break; - } - - case tsip_dialog_timedout: { - // Do nothing if request type is "INFO" - if(!TSIP_MESSAGE_IS_REQUEST(msg) || !TSIP_REQUEST_IS_INFO(msg)) { - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_transporterror, msg, tsk_null); - } - break; - } - case tsip_dialog_terminated: - case tsip_dialog_error: - case tsip_dialog_transport_error: { - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_transporterror, msg, tsk_null); - break; - } - - default: - break; - } - - return ret; -} - -/**Timer manager callback. - * - * @param self The owner of the signaled timer. - * @param timer_id The identifier of the signaled timer. - * - * @return Zero if succeed and non-zero error code otherwise. -**/ -int tsip_dialog_invite_timer_callback(const tsip_dialog_invite_t* self, tsk_timer_id_t timer_id) -{ - int ret = -1; - - if(self) { - if(timer_id == self->stimers.timer.id) { - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_timerRefresh, tsk_null, tsk_null); - } - else if(timer_id == self->timer100rel.id) { - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_timer100rel, tsk_null, tsk_null); - } - else if(timer_id == self->qos.timer.id) { - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_timerRSVP, tsk_null, tsk_null); - } - else if(timer_id == self->timershutdown.id) { - ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_shutdown_timedout, tsk_null, tsk_null); - } - } - return ret; -} - -tsip_dialog_invite_t* tsip_dialog_invite_create(const tsip_ssession_handle_t* ss, const char* call_id) -{ - return tsk_object_new(tsip_dialog_invite_def_t, ss, call_id); -} - -int tsip_dialog_invite_init(tsip_dialog_invite_t *self) -{ - /* special cases (fsm) should be tried first */ - - /* ICE */ - tsip_dialog_invite_ice_init(self); - /* Client-Side dialog */ - tsip_dialog_invite_client_init(self); - /* Server-Side dialog */ - tsip_dialog_invite_server_init(self); - /* 3GPP TS 24.610: Communication Hold */ - tsip_dialog_invite_hold_init(self); - /* 3GPP TS 24.629: Explicit Communication Transfer (ECT) using IP Multimedia (IM) Core Network (CN) subsystem */ - tsip_dialog_invite_ect_init(self); - /* RFC 4028: Session Timers */ - tsip_dialog_invite_stimers_init(self); - /* RFC 3312: Integration of Resource Management and Session Initiation Protocol (SIP) */ - tsip_dialog_invite_qos_init(self); - - /* Initialize the state machine (all other cases) */ - tsk_fsm_set(TSIP_DIALOG_GET_FSM(self), - - /*======================= - * === Started === - */ - // Started -> (Any) -> Started - TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_dialog_invite_Started_2_Started_X_any"), - - /*======================= - * === Connected === - */ - // Connected -> (Send DTMF) -> Connected - TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_dtmf_send, _fsm_state_Connected, x0000_Connected_2_Connected_X_oDTMF, "x0000_Connected_2_Connected_X_oDTMF"), - // Connected -> (Send MSRP message) -> Connected - TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_msrp_send_msg, _fsm_state_Connected, x0000_Connected_2_Connected_X_oLMessage, "x0000_Connected_2_Connected_X_oLMessage"), - // Connected -> (iACK) -> Connected - TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_iACK, _fsm_state_Connected, x0000_Connected_2_Connected_X_iACK, "x0000_Connected_2_Connected_X_iACK"), - // Connected -> (iINVITE) -> Connected - TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_iINVITE, _fsm_state_Connected, x0000_Connected_2_Connected_X_iINVITEorUPDATE, "x0000_Connected_2_Connected_X_iINVITE"), - // Connected -> (iUPDATE) -> Connected - TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_iUPDATE, _fsm_state_Connected, x0000_Connected_2_Connected_X_iINVITEorUPDATE, "x0000_Connected_2_Connected_X_iUPDATE"), - // Connected -> (send reINVITE) -> Connected - TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_oINVITE, _fsm_state_Connected, x0000_Connected_2_Connected_X_oINVITE, "x0000_Connected_2_Connected_X_oINVITE"), - - /*======================= - * === BYE/SHUTDOWN === - */ - // Any -> (oBYE) -> Trying - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_oBYE, _fsm_state_Trying, x0000_Any_2_Trying_X_oBYE, "x0000_Any_2_Trying_X_oBYE"), - // Any -> (iBYE) -> Terminated - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_iBYE, _fsm_state_Terminated, x0000_Any_2_Terminated_X_iBYE, "x0000_Any_2_Terminated_X_iBYE"), - // Any -> (i401/407 BYE) -> Any - TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i401_i407, _fsm_cond_is_resp2BYE, tsk_fsm_state_any, x0000_Any_2_Any_X_i401_407_Challenge, "x0000_Any_2_Any_X_i401_407_Challenge"), - // Any -> (i3xx-i6xx BYE) -> Terminated - TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i300_to_i699, _fsm_cond_is_resp2BYE, _fsm_state_Terminated, tsk_null, "x0000_Any_2_Terminated_X_i3xxTOi6xxBYE"), - // Any -> (i2xxx BYE) -> Terminated - TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i2xx, _fsm_cond_is_resp2BYE, _fsm_state_Terminated, tsk_null, "x0000_Any_2_Terminated_X_i2xxBYE"), - // Any -> (Shutdown) -> Trying - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_oShutdown, _fsm_state_Trying, x0000_Any_2_Trying_X_shutdown, "x0000_Any_2_Trying_X_shutdown"), - // Any -> (shutdown timedout) -> Terminated - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_shutdown_timedout, _fsm_state_Terminated, tsk_null, "tsip_dialog_invite_shutdown_timedout"), - - - /*======================= - * === Any === - */ - // Any -> (i1xx) -> Any - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_i1xx, tsk_fsm_state_any, x0000_Any_2_Any_X_i1xx, "x0000_Any_2_Any_X_i1xx"), - // Any -> (oINFO) -> Any - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_oINFO, tsk_fsm_state_any, x0000_Any_2_Any_X_oINFO, "x0000_Any_2_Any_X_oINFO"), - // Any -> (iINFO) -> Any - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_iINFO, tsk_fsm_state_any, x0000_Any_2_Any_X_iINFO, "x0000_Any_2_Any_X_iINFO"), - // Any -> (i401/407) - // - // Any -> (iPRACK) -> Any - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_iPRACK, tsk_fsm_state_any, x0000_Any_2_Any_X_iPRACK, "x0000_Any_2_Any_X_iPRACK"), - // Any -> (iOPTIONS) -> Any - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_iOPTIONS, tsk_fsm_state_any, x0000_Any_2_Any_X_iOPTIONS, "x0000_Any_2_Any_X_iOPTIONS"), - // Any -> (i2xx INVITE) -> Any - TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i2xx, _fsm_cond_is_resp2INVITE, tsk_fsm_state_any, x0000_Any_2_Any_X_i2xxINVITEorUPDATE, "x0000_Any_2_Any_X_i2xxINVITE"), - // Any -> (i2xx UPDATE) -> Any - TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i2xx, _fsm_cond_is_resp2UPDATE, tsk_fsm_state_any, x0000_Any_2_Any_X_i2xxINVITEorUPDATE, "x0000_Any_2_Any_X_i2xxUPDATE"), - // Any -> (i401/407 INVITE) -> Any - TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i401_i407, _fsm_cond_is_resp2INVITE, tsk_fsm_state_any, x0000_Any_2_Any_X_i401_407_Challenge, "x0000_Any_2_Any_X_i401_407_Challenge"), - // Any -> (i401/407 UPDATE) -> Any - TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i401_i407, _fsm_cond_is_resp2UPDATE, tsk_fsm_state_any, x0000_Any_2_Any_X_i401_407_Challenge, "x0000_Any_2_Any_X_i401_407_Challenge"), - // Any -> (i2xx PRACK) -> Any - TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i2xx, _fsm_cond_is_resp2PRACK, tsk_fsm_state_any, tsk_null, "x0000_Any_2_Any_X_i2xxPRACK"), - // Any -> (i2xx INFO) -> Any - TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i2xx, _fsm_cond_is_resp2INFO, tsk_fsm_state_any, tsk_null, "x0000_Any_2_Any_X_i2xxINFO"), - // Any -> (transport error) -> Terminated - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, x9998_Any_2_Terminated_X_transportError, "x9998_Any_2_Terminated_X_transportError"), - // Any -> (transport error) -> Terminated - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_error, _fsm_state_Terminated, x9999_Any_2_Any_X_Error, "x9999_Any_2_Any_X_Error"), - - TSK_FSM_ADD_NULL()); - - /* Sets callback function */ - TSIP_DIALOG(self)->callback = TSIP_DIALOG_EVENT_CALLBACK_F(tsip_dialog_invite_event_callback); - - /* Timers */ - self->timer100rel.id = TSK_INVALID_TIMER_ID; - self->stimers.timer.id = TSK_INVALID_TIMER_ID; - self->timershutdown.id = TSK_INVALID_TIMER_ID; - self->timershutdown.timeout = TSIP_DIALOG_SHUTDOWN_TIMEOUT; - - return 0; -} - -// start sending -int tsip_dialog_invite_start(tsip_dialog_invite_t *self) -{ - int ret = -1; - if(self && !TSIP_DIALOG(self)->running) { - if(!(ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_oINVITE, tsk_null, tsk_null))) { - TSIP_DIALOG(self)->running = tsk_true; - } - } - return ret; -} - -int tsip_dialog_invite_process_ro(tsip_dialog_invite_t *self, const tsip_message_t* message) -{ - tsdp_message_t* sdp_ro = tsk_null; - tmedia_type_t old_media_type; - tmedia_type_t new_media_type; - tsk_bool_t media_session_was_null; - int ret = 0; - tmedia_ro_type_t ro_type = tmedia_ro_type_none; - - if(!self || !message) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if (self->is_cancelling) { - TSK_DEBUG_INFO("Cancelling the INVITE...ignore the incoming SDP"); - return 0; - } - - /* Parse SDP content */ - if(TSIP_MESSAGE_HAS_CONTENT(message)) { - if(tsk_striequals("application/sdp", TSIP_MESSAGE_CONTENT_TYPE(message))) { - if(!(sdp_ro = tsdp_message_parse(TSIP_MESSAGE_CONTENT_DATA(message), TSIP_MESSAGE_CONTENT_DATA_LENGTH(message)))) { - TSK_DEBUG_ERROR("Failed to parse remote sdp message:\n [%s]", (const char*)TSIP_MESSAGE_CONTENT_DATA(message)); - return -2; - } - // ICE processing - if(self->supported.ice) { - tsip_dialog_invite_ice_process_ro(self, sdp_ro, TSIP_MESSAGE_IS_REQUEST(message)); - } - } - else { - TSK_DEBUG_ERROR("[%s] content-type is not supportted", TSIP_MESSAGE_CONTENT_TYPE(message)); - return -3; - } - } - else { - if(TSIP_DIALOG(self)->state == tsip_initial && TSIP_REQUEST_IS_INVITE(message)) { /* Bodiless initial INVITE */ - TSIP_DIALOG_GET_SS(self)->media.type = tmedia_defaults_get_media_type(); // Default media for initial INVITE to send with the first reliable answer - } - else { - return 0; - } - } - - ro_type = (TSIP_REQUEST_IS_INVITE(message) || TSIP_REQUEST_IS_UPDATE(message)) // ACK/PRACK can only contain a response if the initial INVITE was bodiless - ? tmedia_ro_type_offer - :(TSIP_RESPONSE_IS_1XX(message) ? tmedia_ro_type_provisional : tmedia_ro_type_answer); - media_session_was_null = (self->msession_mgr == tsk_null); - old_media_type = TSIP_DIALOG_GET_SS(self)->media.type; - new_media_type = sdp_ro ? tmedia_type_from_sdp(sdp_ro) : old_media_type; - - /* Create session Manager if not already done */ - if(!self->msession_mgr) { - int32_t transport_idx = TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default; - TSIP_DIALOG_GET_SS(self)->media.type = new_media_type; - self->msession_mgr = tmedia_session_mgr_create(TSIP_DIALOG_GET_SS(self)->media.type, TSIP_DIALOG_GET_STACK(self)->network.local_ip[transport_idx], - TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]), (sdp_ro == tsk_null)); - if(TSIP_DIALOG_GET_STACK(self)->natt.ctx) { - tmedia_session_mgr_set_natt_ctx(self->msession_mgr, TSIP_DIALOG_GET_STACK(self)->natt.ctx, TSIP_DIALOG_GET_STACK(self)->network.aor.ip[transport_idx]); - } - ret = tmedia_session_mgr_set_ice_ctx(self->msession_mgr, self->ice.ctx_audio, self->ice.ctx_video); - } - - if(sdp_ro) { - if (tmedia_session_mgr_is_new_ro(self->msession_mgr, sdp_ro)) { - ret = tsip_dialog_invite_msession_configure(self); - } - if((ret = tmedia_session_mgr_set_ro(self->msession_mgr, sdp_ro, ro_type))) { - TSK_DEBUG_ERROR("Failed to set remote offer"); - goto bail; - } - } - - // is media update? - // (old_media_type == new_media_type) means the new session are rejected. This is way we match the CSeq - if(!media_session_was_null && (old_media_type != new_media_type || (TSIP_MESSAGE_IS_RESPONSE(message) && self->cseq_out_media_update == message->CSeq->seq)) && (self->msession_mgr->sdp.lo && self->msession_mgr->sdp.ro)) { - // at this point the media session manager has been succeffuly started and all is ok - TSIP_DIALOG_GET_SS(self)->media.type = new_media_type; - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_m_updated, - TSIP_RESPONSE_CODE(message), TSIP_RESPONSE_PHRASE(message), message); - } - - /* start session manager */ - if(!self->msession_mgr->started && (self->msession_mgr->sdp.lo && self->msession_mgr->sdp.ro)) { - /* Set MSRP Callback */ - if((self->msession_mgr->type & tmedia_msrp) == tmedia_msrp) { - tmedia_session_mgr_set_msrp_cb(self->msession_mgr, TSIP_DIALOG_GET_SS(self)->userdata, TSIP_DIALOG_GET_SS(self)->media.msrp.callback); - } - /* starts session manager*/ - ret = tsip_dialog_invite_msession_start(self); - - if(ret == 0 && TSIP_DIALOG(self)->state == tsip_early) { - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_m_early_media, - TSIP_RESPONSE_CODE(message), TSIP_RESPONSE_PHRASE(message), message); - } - } - -bail: - TSK_OBJECT_SAFE_FREE(sdp_ro); - - return ret; -} - - -//-------------------------------------------------------- -// == STATE MACHINE BEGIN == -//-------------------------------------------------------- - -int x0000_Connected_2_Connected_X_oDTMF(va_list *app) -{ - int ret; - tsip_dialog_invite_t *self; - const tsip_action_t* action; - - self = va_arg(*app, tsip_dialog_invite_t *); - va_arg(*app, const tsip_message_t *); - action = va_arg(*app, const tsip_action_t *); - - if(action) { - ret = tmedia_session_mgr_send_dtmf(self->msession_mgr, action->dtmf.event); - } - else { - TSK_DEBUG_ERROR("Invalid action"); - } - - return 0; /* always */ -} - -int x0000_Connected_2_Connected_X_oLMessage(va_list *app) -{ - int ret; - tsip_dialog_invite_t *self; - const tsip_action_t* action; - - self = va_arg(*app, tsip_dialog_invite_t *); - va_arg(*app, const tsip_message_t *); - action = va_arg(*app, const tsip_action_t *); - - if(action && action->payload) { - ret = tmedia_session_mgr_send_message(self->msession_mgr, action->payload->data, action->payload->size, - action->media.params); - } - else { - TSK_DEBUG_ERROR("Invalid action"); - } - - return 0; -} - -/* Connected -> (iACK) -> Connected */ -int x0000_Connected_2_Connected_X_iACK(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - const tsip_request_t *rACK = va_arg(*app, const tsip_request_t *); - int ret; - - // Nothing to do (in future will be used to ensure the session) - - /* No longer waiting for the initial ACK */ - self->is_initial_iack_pending = tsk_false; - - /* Process remote offer */ - if((ret = tsip_dialog_invite_process_ro(self, rACK))) { - /* Send error */ - return ret; - } - - /* Starts media session if not already done */ - if(!self->msession_mgr->started && (self->msession_mgr->sdp.lo && self->msession_mgr->sdp.ro)) { - ret = tsip_dialog_invite_msession_start(self); - } - - // starts ICE timers now that both parties receive the "candidates" - if(tsip_dialog_invite_ice_is_enabled(self)) { - tsip_dialog_invite_ice_timers_set(self, (self->required.ice ? -1 : TSIP_DIALOG_INVITE_ICE_CONNCHECK_TIMEOUT)); - } - - /* alert the user */ - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, - tsip_event_code_dialog_request_incoming, "Incoming Request", rACK); - - return 0; -} - -/* Connected -> (iINVITE or iUPDATE) -> Connected */ -int x0000_Connected_2_Connected_X_iINVITEorUPDATE(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - const tsip_request_t *rINVITEorUPDATE = va_arg(*app, const tsip_request_t *); - - int ret = 0; - tsk_bool_t bodiless_invite; - tmedia_type_t old_media_type = self->msession_mgr ? self->msession_mgr->type : tmedia_none; - tmedia_type_t new_media_type; - tsk_bool_t force_sdp; - - /* process remote offer */ - if((ret = tsip_dialog_invite_process_ro(self, rINVITEorUPDATE))) { - /* Send error */ - return ret; - } - - // force SDP in 200 OK even if the request has the same SDP version - force_sdp = TSIP_MESSAGE_HAS_CONTENT(rINVITEorUPDATE); - - // get new media_type after processing the remote offer - new_media_type = self->msession_mgr ? self->msession_mgr->type : tmedia_none; - - /** response to bodiless iINVITE always contains SDP as explained below - RFC3261 - 14.1 UAC Behavior - The same offer-answer model that applies to session descriptions in - INVITEs (Section 13.2.1) applies to re-INVITEs. As a result, a UAC - that wants to add a media stream, for example, will create a new - offer that contains this media stream, and send that in an INVITE - request to its peer. It is important to note that the full - description of the session, not just the change, is sent. This - supports stateless session processing in various elements, and - supports failover and recovery capabilities. Of course, a UAC MAY - send a re-INVITE with no session description, in which case the first - reliable non-failure response to the re-INVITE will contain the offer - (in this specification, that is a 2xx response). - */ - bodiless_invite = !TSIP_MESSAGE_HAS_CONTENT(rINVITEorUPDATE) && TSIP_REQUEST_IS_INVITE(rINVITEorUPDATE); - - /* session timers (must be before sending response) */ - if(self->stimers.timer.timeout) { - tsip_dialog_invite_stimers_handle(self, rINVITEorUPDATE); - } - - /* hold/resume */ - tsip_dialog_invite_hold_handle(self, rINVITEorUPDATE); - - // send the response - ret = send_RESPONSE(self, rINVITEorUPDATE, 200, "OK", - (self->msession_mgr && (force_sdp || bodiless_invite || self->msession_mgr->ro_changed || self->msession_mgr->state_changed || (old_media_type != new_media_type)))); - - /* alert the user */ - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, - tsip_event_code_dialog_request_incoming, "Incoming Request.", rINVITEorUPDATE); - - return ret; -} - -/* Connected -> (send reINVITE) -> Connected */ -static int x0000_Connected_2_Connected_X_oINVITE(va_list *app) -{ - int ret; - tsk_bool_t mediaType_changed; - tsip_dialog_invite_t *self; - const tsip_action_t* action; - - self = va_arg(*app, tsip_dialog_invite_t *); - va_arg(*app, const tsip_message_t *); - action = va_arg(*app, const tsip_action_t *); - - /* Get Media type from the action */ - mediaType_changed = (TSIP_DIALOG_GET_SS(self)->media.type != action->media.type && action->media.type != tmedia_none); - if (mediaType_changed) { - if (self->msession_mgr) { - ret = tmedia_session_mgr_set_media_type(self->msession_mgr, action->media.type); - } - self->cseq_out_media_update = TSIP_DIALOG(self)->cseq_value + 1; - } - - /* Appy media params received from the user */ - if(!TSK_LIST_IS_EMPTY(action->media.params)) { - ret = tmedia_session_mgr_set_3(self->msession_mgr, action->media.params); - } - - /* send the request */ - ret = send_INVITE(self, mediaType_changed); - - /* alert the user */ - if(mediaType_changed) { - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_m_updating, - tsip_event_code_dialog_request_outgoing, "Updating media type", self->last_oInvite); - } - - return ret; -} - -/* Any -> (iPRACK) -> Any */ -int x0000_Any_2_Any_X_iPRACK(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - const tsip_request_t *rPRACK = va_arg(*app, const tsip_request_t *); - - const tsip_header_RAck_t* RAck; - - if((RAck = (const tsip_header_RAck_t*)tsip_message_get_header(rPRACK, tsip_htype_RAck))) { - if((RAck->seq == self->rseq) && - (tsk_striequals(RAck->method, self->last_o1xxrel->CSeq->method)) && - (RAck->cseq == self->last_o1xxrel->CSeq->seq)) { - - ++self->rseq; - return send_RESPONSE(self, rPRACK, 200, "OK", tsk_false); - } - } - - /* Send 488 */ - return send_ERROR(self, rPRACK, 488, "Failed to match PRACK request", "SIP; cause=488; text=\"Failed to match PRACK request\""); -} - -/* Any -> (iOPTIONS) -> Any */ -int x0000_Any_2_Any_X_iOPTIONS(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - const tsip_request_t *rOPTIONS = va_arg(*app, const tsip_request_t *); - - /* Alert user */ - - /* Send 2xx */ - send_RESPONSE(self, rOPTIONS, 200, "OK", tsk_false); - - return 0; -} - - -/* Any --> (i401/407 INVITE or UPDATE) --> Any */ -int x0000_Any_2_Any_X_i401_407_Challenge(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - const tsip_response_t *response = va_arg(*app, const tsip_response_t *); - int ret; - - if((ret = tsip_dialog_update(TSIP_DIALOG(self), response))) { - /* Alert the user. */ - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_ao_request, - TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response); - - return ret; - } - - if(TSIP_RESPONSE_IS_TO_INVITE(response) || TSIP_RESPONSE_IS_TO_UPDATE(response)) { - return send_INVITEorUPDATE(self, TSIP_RESPONSE_IS_TO_INVITE(response), tsk_false); - } - else if(TSIP_RESPONSE_IS_TO_BYE(response)) { - return send_BYE(self); - } - else { - TSK_DEBUG_ERROR("Unexpected code called"); - return 0; - } -} - -/* Any --> (i2xx INVITE or i2xx UPDATE) --> Any */ -int x0000_Any_2_Any_X_i2xxINVITEorUPDATE(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - const tsip_response_t *r2xx = va_arg(*app, const tsip_response_t *); - int ret = 0; - - /* Update the dialog state */ - if((ret = tsip_dialog_update(TSIP_DIALOG(self), r2xx))) { - return ret; - } - - /* session timers */ - if(self->stimers.timer.timeout) { - tsip_dialog_invite_stimers_handle(self, r2xx); - } - - /* Process remote offer */ - if((ret = tsip_dialog_invite_process_ro(self, r2xx))) { - send_BYE(self); - return ret; - } - - /* send ACK */ - if(TSIP_RESPONSE_IS_TO_INVITE(r2xx)) { - ret = send_ACK(self, r2xx); - } - - // starts ICE timers now that both parties received the "candidates" - if(tsip_dialog_invite_ice_is_enabled(self)) { - tsip_dialog_invite_ice_timers_set(self, (self->required.ice ? -1 : TSIP_DIALOG_INVITE_ICE_CONNCHECK_TIMEOUT)); - } - - return ret; -} - - -/* Any -> (oBYE) -> Trying */ -int x0000_Any_2_Trying_X_oBYE(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - int ret; - - /* Alert the user */ - TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog"); - - /* send BYE */ - if((ret = send_BYE(self)) == 0) { -#if !TSIP_UNDER_APPLE // FIXME: hangs up on iOS (RTP transport runnable join never exits) - // stop session manager - if(self->msession_mgr && self->msession_mgr->started) { - tmedia_session_mgr_stop(self->msession_mgr); - } -#endif - } - return ret; -} - -/* Any -> (iBYE) -> Terminated */ -int x0000_Any_2_Terminated_X_iBYE(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - const tsip_request_t *rBYE = va_arg(*app, const tsip_request_t *); - - /* set last error (or info) */ - tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Call Terminated", tsip_event_code_dialog_terminated); - - /* send 200 OK */ - return send_RESPONSE(self, rBYE, 200, "OK", tsk_false); -} - -/* Any -> Shutdown -> Terminated */ -int x0000_Any_2_Trying_X_shutdown(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - - /* schedule shutdow timeout */ - TSIP_DIALOG_INVITE_TIMER_SCHEDULE(shutdown); - - /* alert user */ - TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog"); - - if(TSIP_DIALOG(self)->state == tsip_established) { - return send_BYE(self); - } - else if(TSIP_DIALOG(self)->state == tsip_early) { - return send_CANCEL(self); - } - - return 0; -} - - -/* Any -> (i1xx) -> Any */ -int x0000_Any_2_Any_X_i1xx(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - const tsip_response_t *r1xx = va_arg(*app, const tsip_response_t *); - int ret = 0; - - /* Update the dialog state */ - if((ret = tsip_dialog_update(TSIP_DIALOG(self), r1xx))) { - return ret; - } - - /* RFC 3262 - 4 UAC Behavior - If a provisional response is received for an initial request, and - that response contains a Require header field containing the option - tag 100rel, the response is to be sent reliably. If the response is - a 100 (Trying) (as opposed to 101 to 199), this option tag MUST be - ignored, and the procedures below MUST NOT be used. - - Assuming the response is to be transmitted reliably, the UAC MUST - create a new request with method PRACK. This request is sent within - the dialog associated with the provisional response (indeed, the - provisional response may have created the dialog). PRACK requests - MAY contain bodies, which are interpreted according to their type and - disposition. - - Note that the PRACK is like any other non-INVITE request within a - dialog. In particular, a UAC SHOULD NOT retransmit the PRACK request - when it receives a retransmission of the provisional response being - acknowledged, although doing so does not create a protocol error. - - Additional information: We should only process the SDP from reliable responses (require:100rel) - but there was many problem with some clients sending SDP with this tag: tiscali, DTAG, samsung, ... - */ - if((TSIP_RESPONSE_CODE(r1xx) >= 101 && TSIP_RESPONSE_CODE(r1xx) <=199)) { - /* Process Remote offer */ - if(TSIP_MESSAGE_HAS_CONTENT(r1xx) && (ret = tsip_dialog_invite_process_ro(self, r1xx))) { - /* Send Error */ - return ret; - } - // don't send PRACK if 100rel is only inside "supported" header - if(tsip_message_required(r1xx, "100rel") && (ret = send_PRACK(self, r1xx))) { - return ret; - } - } - - /* QoS Reservation */ - if((self->qos.timer.id == TSK_INVALID_TIMER_ID) && tsip_message_required(r1xx, "precondition") && !tmedia_session_mgr_canresume(self->msession_mgr)) { - tsip_dialog_invite_qos_timer_schedule(self); - } - - /* alert the user */ - ret = TSIP_DIALOG_INVITE_SIGNAL(self, tsip_ao_request, - TSIP_RESPONSE_CODE(r1xx), TSIP_RESPONSE_PHRASE(r1xx), r1xx); - if(self->is_transf) { - ret = tsip_dialog_invite_notify_parent(self, r1xx); - } - - return ret; -} - -/* Any -> (oINFO) -> Any */ -int x0000_Any_2_Any_X_oINFO(va_list *app) -{ - tsip_dialog_invite_t *self; - const tsip_action_t* action; - tsip_request_t* rINFO; - - self = va_arg(*app, tsip_dialog_invite_t *); - va_arg(*app, const tsip_message_t *); - action = va_arg(*app, const tsip_action_t *); - - if((rINFO = tsip_dialog_request_new(TSIP_DIALOG(self), "INFO"))) { - int ret; - if(action) { - ret = tsip_dialog_apply_action(TSIP_MESSAGE(rINFO), action); - } - ret = tsip_dialog_request_send(TSIP_DIALOG(self), rINFO); - TSK_OBJECT_SAFE_FREE(rINFO); - return ret; - } - else { - TSK_DEBUG_ERROR("Failed to create new INFO request"); - return -1; - } -} - -int x0000_Any_2_Any_X_iINFO(va_list *app) -{ - tsip_dialog_invite_t * self = va_arg(*app, tsip_dialog_invite_t *); - tsip_request_t* rINFO = (tsip_request_t*)va_arg(*app, const tsip_message_t *); - int ret = -1; - - if (rINFO) { - ret = send_RESPONSE(self, rINFO, 200, "Ok", tsk_false); - { - // int tmedia_session_mgr_recv_rtcp_event(tmedia_session_mgr_t* self, tmedia_type_t media_type, tmedia_rtcp_event_type_t event_type, uint32_t ssrc_media); - if (self->msession_mgr && TSIP_MESSAGE_HAS_CONTENT(rINFO)) { - if (tsk_striequals("application/media_control+xml", TSIP_MESSAGE_CONTENT_TYPE(rINFO))) { /* rfc5168: XML Schema for Media Control */ - static uint32_t __ssrc_media_fake = 0; - static tmedia_type_t __tmedia_type_video = tmedia_video; // TODO: add bfcpvideo? - const char* content_ptr = (const char*)TSIP_MESSAGE_CONTENT_DATA(rINFO); - tsk_size_t content_size = (tsk_size_t)TSIP_MESSAGE_CONTENT_DATA_LENGTH(rINFO); - tsk_bool_t is_fir = tsk_false; - uint64_t sessionId = 0; -#if HAVE_LIBXML2 - { - xmlDoc *pDoc; - xmlNode *pRootElement; - xmlXPathContext *pPathCtx; - xmlXPathObject *pPathObj; - static const xmlChar* __xpath_expr_picture_fast_update = (const xmlChar*)"/media_control/vc_primitive/to_encoder/picture_fast_update"; - static const xmlChar* __xpath_expr_stream_id = (const xmlChar*)"/media_control/vc_primitive/stream_id"; - - if (!(pDoc = xmlParseDoc(content_ptr))) { - TSK_DEBUG_ERROR("Failed to parse XML content [%s]", content_ptr); - return 0; - } - if (!(pRootElement = xmlDocGetRootElement(pDoc))) { - TSK_DEBUG_ERROR("Failed to get root element from XML content [%s]", content_ptr); - xmlFreeDoc(pDoc); - return 0; - } - if (!(pPathCtx = xmlXPathNewContext(pDoc))) { - TSK_DEBUG_ERROR("Failed to create path context from XML content [%s]", content_ptr); - xmlFreeDoc(pDoc); - return 0; - } - // picture_fast_update - if (!(pPathObj = xmlXPathEvalExpression(__xpath_expr_picture_fast_update, pPathCtx))) { - TSK_DEBUG_ERROR("Error: unable to evaluate xpath expression: %s", __xpath_expr_picture_fast_update); - xmlXPathFreeContext(pPathCtx); - xmlFreeDoc(pDoc); - return 0; - } - is_fir = (pPathObj->type == XPATH_NODESET && pPathObj->nodesetval->nodeNr > 0); - xmlXPathFreeObject(pPathObj); - // stream_id - if (!(pPathObj = xmlXPathEvalExpression(__xpath_expr_stream_id, pPathCtx))) { - TSK_DEBUG_ERROR("Error: unable to evaluate xpath expression: %s", __xpath_expr_stream_id); - xmlXPathFreeContext(pPathCtx); - xmlFreeDoc(pDoc); - } - else if (pPathObj->type == XPATH_NODESET && pPathObj->nodesetval->nodeNr > 0 && pPathObj->nodesetval->nodeTab[0]->children && pPathObj->nodesetval->nodeTab[0]->children->content) { - sessionId = tsk_atoi64((const char*)pPathObj->nodesetval->nodeTab[0]->children->content); - } - xmlXPathFreeObject(pPathObj); - - xmlXPathFreeContext(pPathCtx); - xmlFreeDoc(pDoc); - } -#else - is_fir = (tsk_strcontains(content_ptr, content_size, "to_encoder") && tsk_strcontains(content_ptr, content_size, "picture_fast_update")); -#endif - if (is_fir) { - TSK_DEBUG_INFO("Incoming SIP INFO(picture_fast_update)"); - ret = sessionId - ? tmedia_session_mgr_recv_rtcp_event_2(self->msession_mgr, tmedia_rtcp_event_type_fir, sessionId) - : tmedia_session_mgr_recv_rtcp_event(self->msession_mgr, __tmedia_type_video, tmedia_rtcp_event_type_fir, __ssrc_media_fake); - } - else { - TSK_DEBUG_INFO("Incoming SIP INFO(unknown)"); - } - } - } - } - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, - tsip_event_code_dialog_request_incoming, "Incoming Request", rINFO); - } - - return ret; -} - -int x9998_Any_2_Terminated_X_transportError(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - - /* set last error (or info) */ - tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Transport error", tsip_event_code_dialog_terminated); - - return 0; -} - -int x9999_Any_2_Any_X_Error(va_list *app) -{ - return 0; -} - - -//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// == STATE MACHINE END == -//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -int tsip_dialog_invite_msession_configure(tsip_dialog_invite_t *self) -{ - tmedia_srtp_mode_t srtp_mode; - tmedia_mode_t avpf_mode; - tsk_bool_t is_rtcweb_enabled; - tsk_bool_t is_webrtc2sip_mode_enabled; - - if(!self || !self->msession_mgr) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - is_webrtc2sip_mode_enabled = (TSIP_DIALOG_GET_STACK(self)->network.mode == tsip_stack_mode_webrtc2sip); - is_rtcweb_enabled = (((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.profile == tmedia_profile_rtcweb); - srtp_mode = is_rtcweb_enabled ? tmedia_srtp_mode_mandatory : ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.srtp_mode; - avpf_mode = is_rtcweb_enabled ? tmedia_mode_mandatory : ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.avpf_mode; - - // set callback functions - tmedia_session_mgr_set_onerror_cbfn(self->msession_mgr, self, tsip_dialog_invite_msession_onerror_cb); - tmedia_session_mgr_set_rfc5168_cbfn(self->msession_mgr, self, tsip_dialog_invite_msession_rfc5168_cb); - - // set params - return tmedia_session_mgr_set(self->msession_mgr, - TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "srtp-mode", srtp_mode), - TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "avpf-mode", avpf_mode), - TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "webrtc2sip-mode-enabled", is_webrtc2sip_mode_enabled), // hack the media stack - TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "rtcp-enabled", self->use_rtcp), - TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "rtcpmux-enabled", self->use_rtcpmux), - TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "codecs-supported", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.codecs), - - TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "bypass-encoding", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.bypass_encoding), - TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "bypass-decoding", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.bypass_decoding), - - TMEDIA_SESSION_SET_INT32(tmedia_audio, "rtp-ssrc", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.rtp.ssrc.audio), - TMEDIA_SESSION_SET_INT32(tmedia_video, "rtp-ssrc", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.rtp.ssrc.video), - - TMEDIA_SESSION_SET_STR(self->msession_mgr->type, "dtls-file-ca", TSIP_DIALOG_GET_STACK(self)->security.tls.ca), - TMEDIA_SESSION_SET_STR(self->msession_mgr->type, "dtls-file-pbk", TSIP_DIALOG_GET_STACK(self)->security.tls.pbk), - TMEDIA_SESSION_SET_STR(self->msession_mgr->type, "dtls-file-pvk", TSIP_DIALOG_GET_STACK(self)->security.tls.pvk), - TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "dtls-cert-verify", TSIP_DIALOG_GET_STACK(self)->security.tls.verify), - - TMEDIA_SESSION_SET_INT32(tmedia_video, "fps", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.video_fps), - TMEDIA_SESSION_SET_INT32(tmedia_video, "bandwidth-max-upload", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.video_bw_up), - TMEDIA_SESSION_SET_INT32(tmedia_video, "bandwidth-max-download", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.video_bw_down), - TMEDIA_SESSION_SET_INT32(tmedia_video, "pref-size", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.video_pref_size), - - tsk_null); -} - -int tsip_dialog_invite_msession_start(tsip_dialog_invite_t *self) -{ - if(!self || !self->msession_mgr) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if(tsip_dialog_invite_ice_is_enabled(self) && !tsip_dialog_invite_ice_is_connected(self)) { - if(self->msession_mgr->type != self->ice.media_type) { - TSK_DEBUG_INFO("Media session type(%d)<>ICE media type(%d)", self->msession_mgr->type, self->ice.media_type); - // make sure to use the right media types - tsip_dialog_invite_ice_set_media_type(self, self->msession_mgr->type); - } - self->ice.start_smgr = tsk_true; - } - else { - self->ice.start_smgr = tsk_false; - return tmedia_session_mgr_start(self->msession_mgr); - } - return 0; -} - -// send INVITE/UPDATE request -int send_INVITEorUPDATE(tsip_dialog_invite_t *self, tsk_bool_t is_INVITE, tsk_bool_t force_sdp) -{ - int ret = -1; - tsip_request_t *request = tsk_null; - tsk_bool_t bodiless = tsk_false; - -#if BODILESS_INVITE - if(TSIP_DIALOG(self)->state == tsip_initial) { - bodiless = tsk_true; - } -#endif - - if(!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - goto bail; - } - - if((request = tsip_dialog_request_new(TSIP_DIALOG(self), is_INVITE ? "INVITE" : "UPDATE"))) { - /* apply action params to the request (will add a content if the action contains one) */ - if(TSIP_DIALOG(self)->curr_action) { - tsip_dialog_apply_action(request, TSIP_DIALOG(self)->curr_action); - } - - if(!bodiless) { - /* add our payload if current action does not have one */ - if((force_sdp || is_INVITE) || ((self->msession_mgr && self->msession_mgr->state_changed) || (TSIP_DIALOG(self)->state == tsip_initial))) { - if(!TSIP_DIALOG(self)->curr_action || !TSIP_DIALOG(self)->curr_action->payload) { - const tsdp_message_t* sdp_lo; - char* sdp; - if((sdp_lo = tmedia_session_mgr_get_lo(self->msession_mgr)) && (sdp = tsdp_message_tostring(sdp_lo))) { - tsip_message_add_content(request, "application/sdp", sdp, tsk_strlen(sdp)); - if(tsip_dialog_invite_ice_is_enabled(self)) { - ret = tsip_dialog_invite_ice_process_lo(self, sdp_lo); - } - TSK_FREE(sdp); - } - } - } - } - - /* Session timers */ - if(self->stimers.timer.timeout) { - if(self->required.timer) { - tsip_message_add_headers(request, - TSIP_HEADER_SESSION_EXPIRES_VA_ARGS(self->stimers.timer.timeout, !self->stimers.is_refresher), - TSIP_HEADER_REQUIRE_VA_ARGS("timer"), - tsk_null - ); - } - else if(self->supported.timer) { - tsip_message_add_headers(request, - TSIP_HEADER_SESSION_EXPIRES_VA_ARGS(self->stimers.timer.timeout, !self->stimers.is_refresher), - TSIP_HEADER_SUPPORTED_VA_ARGS("timer"), - tsk_null - ); - } - - } - - if(self->stimers.minse) { - tsip_message_add_headers(request, - TSIP_HEADER_MIN_SE_VA_ARGS(self->stimers.minse), - tsk_null - ); - } - - /* 100rel */ - if(self->required._100rel) { - tsip_message_add_headers(request, - TSIP_HEADER_REQUIRE_VA_ARGS("100rel"), - tsk_null - ); - } - else if(self->supported._100rel) { - tsip_message_add_headers(request, - TSIP_HEADER_SUPPORTED_VA_ARGS("100rel"), - tsk_null - ); - } - - /* QoS */ - if(self->required.precondition) { - tsip_message_add_headers(request, - TSIP_HEADER_REQUIRE_VA_ARGS("precondition"), - tsk_null - ); - } - else if(self->supported.precondition) { - tsip_message_add_headers(request, - TSIP_HEADER_SUPPORTED_VA_ARGS("precondition"), - tsk_null - ); - } - - - /* Always added headers */ - // Explicit Communication Transfer (3GPP TS 24.629) - /*tsip_message_add_headers(request, - TSIP_HEADER_SUPPORTED_VA_ARGS("norefersub,replaces"), - tsk_null - );*/ - - /* send the request */ - ret = tsip_dialog_request_send(TSIP_DIALOG(self), request); - if(ret == 0) { - /* update last INVITE */ - TSK_OBJECT_SAFE_FREE(self->last_oInvite); - self->last_oInvite = request; - } - else { - TSK_OBJECT_SAFE_FREE(request); - } - } - -bail: - return ret; -} - -// Send PRACK -int send_PRACK(tsip_dialog_invite_t *self, const tsip_response_t* r1xx) -{ - // "Require: 100rel\r\n" should be checked by the caller of this function - int ret = -1; - tsip_request_t *request = tsk_null; - const tsip_header_RSeq_t* RSeq; - - if(!self || !r1xx || !r1xx->CSeq) { - TSK_DEBUG_ERROR("Invalid parameter"); - goto bail; - } - - - /* RFC 3262 - 4 UAC Behavior - The UAC MUST maintain a sequence number that indicates the most recently - received in-order reliable provisional response for the initial request. - */ - if((RSeq = (const tsip_header_RSeq_t*)tsip_message_get_header(r1xx, tsip_htype_RSeq))) { - - /* RFC 3262 - 4 UAC Behavior - If the UAC receives another reliable provisional - response to the same request, and its RSeq value is not one higher - than the value of the sequence number, that response MUST NOT be - acknowledged with a PRACK, and MUST NOT be processed further by the - UAC. An implementation MAY discard the response, or MAY cache the - response in the hopes of receiving the missing responses. - */ - if(self->rseq && (RSeq->seq <= self->rseq)) { - TSK_DEBUG_WARN("1xx.RSeq value is not one higher than lastINVITE.RSeq."); - ret = 0; /* Not error */ - goto bail; - } - self->rseq = RSeq->seq; - } - - /* RFC 3262 - 4 UAC Behavior - Assuming the response is to be transmitted reliably, the UAC MUST - create a new request with method PRACK. - */ - if(!(request = tsip_dialog_request_new(TSIP_DIALOG(self), "PRACK"))) { - goto bail; - } - - /* RFC 3262 - 7.2 RAck - The first number is the value from the RSeq header in the provisional - response that is being acknowledged. The next number, and the - method, are copied from the CSeq in the response that is being - acknowledged. The method name in the RAck header is case sensitive. - */ - TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_RACK_VA_ARGS(self->rseq, r1xx->CSeq->seq, r1xx->CSeq->method)); - //TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_DUMMY_VA_ARGS("Test", "value")); - - /* Initial INVITE was a bodiless request and 100rel is supported (I'm Alice) - 1. Alice sends an initial INVITE without offer - 2. Bob's answer is sent in the first reliable provisional response, in this case it's a 1xx INVITE response - 3. Alice's answer is sent in the PRACK response - */ - if(self->is_client && (self->last_oInvite && !TSIP_MESSAGE_HAS_CONTENT(self->last_oInvite))) { - const tsdp_message_t* sdp_lo; - char* sdp; - if((sdp_lo = tmedia_session_mgr_get_lo(self->msession_mgr)) && (sdp = tsdp_message_tostring(sdp_lo))) { - tsip_message_add_content(request, "application/sdp", sdp, tsk_strlen(sdp)); - TSK_FREE(sdp); - } - } - - // Send request - ret = tsip_dialog_request_send(TSIP_DIALOG(self), request); - -bail: - TSK_OBJECT_SAFE_FREE(request); - return ret; -} - -// Send CANCEL -int send_CANCEL(tsip_dialog_invite_t *self) -{ - int ret = -1; - if(!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - goto bail; - } - /* RFC 3261 - 9 Canceling a Request - If the request being cancelled contains a Route header field, the - CANCEL request MUST include that Route header field's values. - ==> up to tsip_dialog_request_new() - */ - - /* RFC 3261 - 9 Canceling a Request - Once the CANCEL is constructed, the client SHOULD check whether it - has received any response (provisional or final) for the request - being cancelled (herein referred to as the "original request"). - - If no provisional response has been received, the CANCEL request MUST - NOT be sent; rather, the client MUST wait for the arrival of a - provisional response before sending the request. - ==> up to the caller to check that we are not in the initial state and the FSM - is in Trying state. - */ - - /* RFC 3261 - 9 Canceling a Request - The following procedures are used to construct a CANCEL request. The - Request-URI, Call-ID, To, the numeric part of CSeq, and From header - fields in the CANCEL request MUST be identical to those in the - request being cancelled, including tags. A CANCEL constructed by a - client MUST have only a single Via header field value matching the - top Via value in the request being cancelled. Using the same values - for these header fields allows the CANCEL to be matched with the - request it cancels (Section 9.2 indicates how such matching occurs). - However, the method part of the CSeq header field MUST have a value - of CANCEL. This allows it to be identified and processed as a - transaction in its own right (See Section 17) - */ - if(self->last_oInvite) { - /* to avoid concurrent access, take a reference to the request */ - tsip_request_t* last_oInvite = tsk_object_ref(self->last_oInvite); - tsip_request_t* cancel; - - if((cancel = tsip_request_create("CANCEL", last_oInvite->line.request.uri))) { - const tsk_list_item_t* item; - const tsip_header_t* header; - - tsip_message_add_headers(cancel, - TSIP_HEADER_CSEQ_VA_ARGS(last_oInvite->CSeq->seq, "CANCEL"), - TSIP_HEADER_MAX_FORWARDS_VA_ARGS(TSIP_HEADER_MAX_FORWARDS_DEFAULT), - TSIP_HEADER_CONTENT_LENGTH_VA_ARGS(0), - tsk_null); - - cancel->Call_ID = tsk_object_ref(last_oInvite->Call_ID); - cancel->To = tsk_object_ref(last_oInvite->To); - cancel->From = tsk_object_ref(last_oInvite->From); - cancel->firstVia = tsk_object_ref(last_oInvite->firstVia); - cancel->sigcomp_id = tsk_strdup(TSIP_DIALOG_GET_SS(self)->sigcomp_id); - - // Copy Authorizations, Routes and Proxy-Auth - tsk_list_foreach(item, last_oInvite->headers) { - if(!(header = TSIP_HEADER(item->data))) { - continue; - } - - switch(header->type) { - case tsip_htype_Route: - case tsip_htype_Proxy_Authorization: - case tsip_htype_Authorization: - header = tsk_object_ref((void*)header); - if(!cancel->headers) { - cancel->headers = tsk_list_create(); - } - tsk_list_push_back_data(cancel->headers, (void**)&header); - break; - default: - break; - } - } - - ret = tsip_dialog_add_session_headers(TSIP_DIALOG(self), cancel); - ret = tsip_dialog_request_send(TSIP_DIALOG(self), cancel); - TSK_OBJECT_SAFE_FREE(cancel); - } - else { - TSK_DEBUG_ERROR("Failed to create CANCEL request"); - ret = -2; - } - - TSK_OBJECT_SAFE_FREE(last_oInvite); - return ret; - } - else { - TSK_DEBUG_WARN("There is no INVITE request to cancel"); - return 0; - } - -bail: - return ret; -} - -int tsip_dialog_invite_notify_parent(tsip_dialog_invite_t *self, const tsip_response_t* response) -{ - int ret = -1; - tsip_dialog_t* dlg_parent = tsip_dialog_layer_find_by_ssid(TSIP_DIALOG_GET_STACK(self)->layer_dialog, TSIP_DIALOG_GET_SS(self)->id_parent); - if(dlg_parent) { - tsip_action_t* action = tsip_action_create(tsip_atype_ect_lnotify, - TSIP_ACTION_SET_NULL()); - if(action) { - ret = tsip_dialog_fsm_act(dlg_parent, action->type, response, action); - TSK_OBJECT_SAFE_FREE(action); - } - else { - TSK_DEBUG_ERROR("Failed to create action object"); - } - TSK_OBJECT_SAFE_FREE(dlg_parent); - } - else { - TSK_DEBUG_ERROR("Failed to find parent with id = %llu", TSIP_DIALOG_GET_SS(self)->id_parent); - } - return ret; -} - -// Send BYE -int send_BYE(tsip_dialog_invite_t *self) -{ - int ret = -1; - tsip_request_t *bye = tsk_null; - - if(!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - goto bail; - } - /* RFC 3261 - 15.1.1 UAC Behavior - A BYE request is constructed as would any other request within a - dialog, as described in Section 12. - - Once the BYE is constructed, the UAC core creates a new non-INVITE - client transaction, and passes it the BYE request. The UAC MUST - consider the session terminated (and therefore stop sending or - listening for media) as soon as the BYE request is passed to the - client transaction. If the response for the BYE is a 481 - (Call/Transaction Does Not Exist) or a 408 (Request Timeout) or no - - response at all is received for the BYE (that is, a timeout is - returned by the client transaction), the UAC MUST consider the - session and the dialog terminated. - */ - if((bye = tsip_dialog_request_new(TSIP_DIALOG(self), "BYE"))) { - if(TSIP_DIALOG(self)->curr_action) { - tsip_dialog_apply_action(bye, TSIP_DIALOG(self)->curr_action); - } - ret = tsip_dialog_request_send(TSIP_DIALOG(self), bye); - TSK_OBJECT_SAFE_FREE(bye); - } - -bail: - return ret; -} - -// Send INFO -int send_INFO(tsip_dialog_invite_t *self, const char* content_type, const void* content_ptr, tsk_size_t content_size) -{ - int ret = -1; - tsip_request_t *info = tsk_null; - - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - goto bail; - } - if ((info = tsip_dialog_request_new(TSIP_DIALOG(self), "INFO"))) { - if (TSIP_DIALOG(self)->curr_action) { - ret = tsip_dialog_apply_action(info, TSIP_DIALOG(self)->curr_action); - if (ret) { - goto bail; - } - } - if (content_type && content_ptr && content_size) { - ret = tsip_message_add_content(info, content_type, content_ptr, content_size); - if (ret) { - goto bail; - } - } - ret = tsip_dialog_request_send(TSIP_DIALOG(self), info); - if (ret) { - goto bail; - } - } - -bail: - TSK_OBJECT_SAFE_FREE(info); - return ret; -} - -// Send ACK -int send_ACK(tsip_dialog_invite_t *self, const tsip_response_t* r2xxINVITE) -{ - int ret = -1; - tsip_request_t *request = tsk_null; - - if(!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - goto bail; - } - - if((request = tsip_dialog_request_new(TSIP_DIALOG(self), "ACK"))) { - - /* The initial INVITE sent by us was a bodiless request and we don't support 100rel (We are Alice) - 1. Alice sends initial INVITE without offer - 2. Bob's offer is sent in the 2xx INVITE response - 3. Alice's answer is sent in the ACK request - */ - if(self->is_client && (self->last_oInvite && !TSIP_MESSAGE_HAS_CONTENT(self->last_oInvite))) { - const tsdp_message_t* sdp_lo; - char* sdp; - if((sdp_lo = tmedia_session_mgr_get_lo(self->msession_mgr)) && (sdp = tsdp_message_tostring(sdp_lo))) { - tsip_message_add_content(request, "application/sdp", sdp, tsk_strlen(sdp)); - TSK_FREE(sdp); - } - - // Start media session if not done - if(!self->msession_mgr->started && (self->msession_mgr->sdp.lo && self->msession_mgr->sdp.ro)) { - /* Set MSRP Callback */ - if((self->msession_mgr->type & tmedia_msrp) == tmedia_msrp) { - tmedia_session_mgr_set_msrp_cb(self->msession_mgr, TSIP_DIALOG_GET_SS(self)->userdata, TSIP_DIALOG_GET_SS(self)->media.msrp.callback); - } - // starts session manager - ret = tsip_dialog_invite_msession_start(self); - } - } - - /* RFC 3261 - 13.2.2.4 2xx Responses - The UAC core MUST generate an ACK request for each 2xx received from - the transaction layer. The header fields of the ACK are constructed - in the same way as for any request sent within a dialog (see Section - 12) with the exception of the CSeq and the header fields related to - authentication. The sequence number of the CSeq header field MUST be - the same as the INVITE being acknowledged, but the CSeq method MUST - be ACK. The ACK MUST contain the same credentials as the INVITE. If - the 2xx contains an offer (based on the rules above), the ACK MUST - carry an answer in its body. If the offer in the 2xx response is not - acceptable, the UAC core MUST generate a valid answer in the ACK and - then send a BYE immediately. - ==> Credentials will be added by tsip_dialog_request_new() because they are - associated to the dialog itself. - ==> It's up to us to add/update the CSeq number. - ==> ACK requests sent here will create new client transactions, which means that - they will have there own branches. This is not the case for ACK requests sent from - the transaction layer. - */ - request->CSeq->seq = r2xxINVITE->CSeq->seq; /* As the 2xx has the same CSeq than the INVITE */ - - /* RFC 3261 - 13.2.2.4 2xx Responses - Once the ACK has been constructed, the procedures of [4] are used to - determine the destination address, port and transport. However, the - request is passed to the transport layer directly for transmission, - rather than a client transaction. This is because the UAC core - handles retransmissions of the ACK, not the transaction layer. The - ACK MUST be passed to the client transport every time a - retransmission of the 2xx final response that triggered the ACK - arrives. - */ - if(TSIP_DIALOG_GET_STACK(self)->layer_transport) { - ret = tsip_transport_layer_send(TSIP_DIALOG_GET_STACK(self)->layer_transport, tsk_null, request); - } - else { - ret = -1; - TSK_DEBUG_ERROR("Not Transport layer associated to this stack"); - } - TSK_OBJECT_SAFE_FREE(request); - } - -bail: - return ret; -} - -// Send any response -int send_RESPONSE(tsip_dialog_invite_t *self, const tsip_request_t* request, short code, const char* phrase, tsk_bool_t force_sdp) -{ - tsip_response_t *response; - int ret = -1; - - if((response = tsip_dialog_response_new(TSIP_DIALOG(self), code, phrase, request))) { - if(TSIP_REQUEST_IS_INVITE(request) || TSIP_REQUEST_IS_UPDATE(request)) { - /* Session timers (for 2xx to INVITE or UPDATE) */ - if(self->required.timer) { - tsip_message_add_headers(response, - TSIP_HEADER_REQUIRE_VA_ARGS("timer"), - TSIP_HEADER_SESSION_EXPIRES_VA_ARGS(self->stimers.timer.timeout, tsk_striequals(self->stimers.refresher, "uas")), - tsk_null - ); - } - else if(self->supported.timer) { - tsip_message_add_headers(response, - TSIP_HEADER_SUPPORTED_VA_ARGS("timer"), - TSIP_HEADER_SESSION_EXPIRES_VA_ARGS(self->stimers.timer.timeout, tsk_striequals(self->stimers.refresher, "uas")), - tsk_null - ); - } - if(self->stimers.minse) { - tsip_message_add_headers(response, - TSIP_HEADER_MIN_SE_VA_ARGS(self->stimers.minse), - tsk_null - ); - } - if(code == 422) { - tsip_message_add_headers(response, - TSIP_HEADER_DUMMY_VA_ARGS("Reason", "SIP; cause=422; text=\"Session Interval Too Small\""), - tsk_null - ); - } - - /* 180 Ringing */ - /* 183 Session in Progress */ - if(code == 180 || code == 183) { - if(self->required._100rel) { - if(self->rseq == 0) { - self->rseq = TSK_ABS((rand() ^ rand()) + 1); - } - tsip_message_add_headers(response, - TSIP_HEADER_REQUIRE_VA_ARGS("100rel"), - TSIP_HEADER_RSEQ_VA_ARGS(self->rseq), - tsk_null - ); - TSK_OBJECT_SAFE_FREE(self->last_o1xxrel); - self->last_o1xxrel = tsk_object_ref(response); - - /* No-Initial reliable 1xx will use tsip_dialog_response_send() instead of this function - * ==> can reseset timeout value and make initial schedule */ - TSIP_DIALOG_TIMER_CANCEL(100rel); - self->timer100rel.timeout = tsip_timers_getA(); - TSIP_DIALOG_INVITE_TIMER_SCHEDULE(100rel); - } - } - - /* Precondition */ - if(code == 180 || code == 183) { - if(self->required.precondition) { - tsip_message_add_headers(response, - TSIP_HEADER_REQUIRE_VA_ARGS("precondition"), - tsk_null - ); - } - } - - - /* SDP content */ - if(self->msession_mgr && force_sdp) { - const tsdp_message_t* sdp_lo; - char* sdp = tsk_null; - if((sdp_lo = tmedia_session_mgr_get_lo(self->msession_mgr)) && (sdp = tsdp_message_tostring(sdp_lo))) { - ret = tsip_message_add_content(response, "application/sdp", sdp, tsk_strlen(sdp)); - if(tsip_dialog_invite_ice_is_enabled(self)) { - ret = tsip_dialog_invite_ice_process_lo(self, sdp_lo); - } - } - TSK_FREE(sdp); - } - - /* Add Allow header */ - tsip_message_add_headers(response, - TSIP_HEADER_DUMMY_VA_ARGS("Allow", TSIP_HEADER_ALLOW_DEFAULT), - tsk_null - ); - } - else if(TSIP_REQUEST_IS_REFER(request)) { - if(self->required.norefersub) { - tsip_message_add_headers(response, - TSIP_HEADER_REQUIRE_VA_ARGS("norefersub"), - tsk_null - ); - } - if(self->supported.norefersub) { - tsip_message_add_headers(response, - TSIP_HEADER_SUPPORTED_VA_ARGS("norefersub"), - tsk_null - ); - } - } - - - ret = tsip_dialog_response_send(TSIP_DIALOG(self), response); - TSK_OBJECT_SAFE_FREE(response); - } - return ret; -} - -// Send error response -int send_ERROR(tsip_dialog_invite_t* self, const tsip_request_t* request, short code, const char* phrase, const char* reason) -{ - tsip_response_t *response; - - if(!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if((response = tsip_dialog_response_new(TSIP_DIALOG(self), code, phrase, request))) { - tsip_message_add_headers(response, - TSIP_HEADER_DUMMY_VA_ARGS("Reason", reason), - tsk_null - ); - - tsip_dialog_response_send(TSIP_DIALOG(self), response); - TSK_OBJECT_SAFE_FREE(response); - } - else { - TSK_DEBUG_ERROR("Failed to create new message"); - } - return 0; -} - -// FSM callback to signal that the dialog is in the terminated state -int tsip_dialog_invite_OnTerminated(tsip_dialog_invite_t *self) -{ - TSK_DEBUG_INFO("=== INVITE Dialog terminated ==="); - - /* Cancel all transactions associated to this dialog (will also be done when the dialog is destroyed ) - worth nothing to do it here in order to cancel in-dialog request (such as INFO, REFER...) - */ - tsip_transac_layer_cancel_by_dialog(TSIP_DIALOG_GET_STACK(self)->layer_transac, TSIP_DIALOG(self)); - - /* stop session manager */ - if(self->msession_mgr && self->msession_mgr->started) { - tmedia_session_mgr_stop(self->msession_mgr); - } - // because of C# and Java garbage collectors, the ICE context could - // be destroyed (then stoppped) very late - if(self->ice.ctx_audio) { - tnet_ice_ctx_stop(self->ice.ctx_audio); - } - if(self->ice.ctx_video) { - tnet_ice_ctx_stop(self->ice.ctx_video); - } - - /* alert the user */ - TSIP_DIALOG_SIGNAL_2(self, tsip_event_code_dialog_terminated, - TSIP_DIALOG(self)->last_error.phrase ? TSIP_DIALOG(self)->last_error.phrase : "Call Terminated", - TSIP_DIALOG(self)->last_error.message); - - /* Remove from the dialog layer. */ - return tsip_dialog_remove(TSIP_DIALOG(self)); -} - -// callback function called when media session error occures asynchronously -static int tsip_dialog_invite_msession_onerror_cb(const void* usrdata, const struct tmedia_session_s* session, const char* reason, tsk_bool_t is_fatal) -{ - tsip_dialog_t *self = (tsip_dialog_t*)usrdata; - - if(self && is_fatal) { - char* str = tsk_null; - tsip_action_t* action; - tsk_sprintf(&str, "SIP; cause=488; text=\"%s\"", (reason ? reason : "Internal error")); - action = tsip_action_create(tsip_atype_hangup, - TSIP_ACTION_SET_HEADER("Reason", str), - TSIP_ACTION_SET_NULL()); - TSK_FREE(str); - - tsip_dialog_hangup(self, action); - TSK_OBJECT_SAFE_FREE(action); - } - - return 0; -} - -// callback function called when media session events (related to rfc5168) occures asynchronously -static int tsip_dialog_invite_msession_rfc5168_cb(const void* usrdata, const struct tmedia_session_s* session, const char* reason, enum tmedia_session_rfc5168_cmd_e command) -{ - tsip_dialog_invite_t *self = (tsip_dialog_invite_t*)usrdata; - - if (self) { - if (command == tmedia_session_rfc5168_cmd_picture_fast_update) { - uint64_t now = tsk_time_now(); - if ((now - self->last_out_fastupdate_time) > TSIP_INFO_FASTUPDATE_OUT_INTERVAL_MIN) { - char* content_ptr = tsk_null; - static const char* __content_type = "application/media_control+xml"; - static const void* __content_format = - "\r\n" - " \r\n" - " \r\n" - " \r\n" - " \r\n" - " \r\n" - " \r\n" - " %llu\r\n" - " \r\n" - " \r\n"; - TSK_DEBUG_INFO("Media session is asking the sigaling layer to send SIP INFO('picture_fast_update')"); - tsk_sprintf(&content_ptr, __content_format, session->id); - self->last_out_fastupdate_time = now; - return send_INFO(self, __content_type, content_ptr, tsk_strlen(content_ptr)); - } - else { - /* if too close don't update "last_fir_time" to "now" to be sure interval will increase */ - TSK_DEBUG_INFO("Outgoing SIP INFO ('picture_fast_update') requested but delay too close"); - } - } - } - return 0; -} - - - - - - - - - - - - - - - - - - - -//======================================================== -// SIP dialog INVITE object definition -// -static tsk_object_t* tsip_dialog_invite_ctor(tsk_object_t * self, va_list * app) -{ - tsip_dialog_invite_t *dialog = self; - if(dialog) { - tsip_ssession_handle_t *ss = va_arg(*app, tsip_ssession_handle_t *); - const char* call_id = va_arg(*app, const char *); - - /* Initialize base class */ - tsip_dialog_init(TSIP_DIALOG(self), tsip_dialog_INVITE, call_id, ss, _fsm_state_Started, _fsm_state_Terminated); - - /* FSM */ - TSIP_DIALOG_GET_FSM(dialog)->debug = DEBUG_STATE_MACHINE; - tsk_fsm_set_callback_terminated(TSIP_DIALOG_GET_FSM(dialog), TSK_FSM_ONTERMINATED_F(tsip_dialog_invite_OnTerminated), (const void*)dialog); - - /* default values */ - dialog->supported._100rel = ((tsip_ssession_t*)ss)->media.enable_100rel; - dialog->supported.norefersub = tsk_true; - dialog->required.ice = (((tsip_ssession_t*)ss)->media.profile == tmedia_profile_rtcweb); - dialog->supported.ice = (dialog->required.ice || ((tsip_ssession_t*)ss)->media.enable_ice); -#if 0 /* This was a patch for chrome but no longer needed when using version 23.0.1271.64 or later */ - dialog->ice.is_jingle = (((tsip_ssession_t*)ss)->media.profile == tmedia_profile_rtcweb); -#else - dialog->ice.is_jingle = tsk_false; -#endif - dialog->ice.last_sdp_ro_ver = -1; - dialog->use_rtcp = (((tsip_ssession_t*)ss)->media.profile == tmedia_profile_rtcweb) ? tsk_true : ((tsip_ssession_t*)ss)->media.enable_rtcp; - dialog->use_rtcpmux = (((tsip_ssession_t*)ss)->media.profile == tmedia_profile_rtcweb) ? tsk_true : ((tsip_ssession_t*)ss)->media.enable_rtcpmux; - - dialog->ice.last_action_id = tsk_fsm_state_none; - dialog->refersub = tsk_true; - // ... do the same for preconditions, replaces, .... - - /* Initialize the class itself */ - tsip_dialog_invite_init(self); - } - return self; -} - -static tsk_object_t* tsip_dialog_invite_dtor(tsk_object_t * _self) -{ - tsip_dialog_invite_t *self = _self; - if(self) { - // Cancel all timers - tsip_dialog_invite_stimers_cancel(self); - tsip_dialog_invite_qos_timer_cancel(self); - TSIP_DIALOG_TIMER_CANCEL(shutdown); - TSIP_DIALOG_TIMER_CANCEL(100rel); - - // DeInitialize base class - tsip_dialog_deinit(TSIP_DIALOG(self)); - - // DeInitialize self - TSK_OBJECT_SAFE_FREE(self->ss_transf); - TSK_OBJECT_SAFE_FREE(self->msession_mgr); - - TSK_OBJECT_SAFE_FREE(self->last_oInvite); - TSK_OBJECT_SAFE_FREE(self->last_iInvite); - TSK_OBJECT_SAFE_FREE(self->last_o1xxrel); - TSK_OBJECT_SAFE_FREE(self->last_iRefer); - TSK_FREE(self->stimers.refresher); - - TSK_OBJECT_SAFE_FREE(self->ice.ctx_audio); - TSK_OBJECT_SAFE_FREE(self->ice.ctx_video); - TSK_OBJECT_SAFE_FREE(self->ice.last_action); - TSK_OBJECT_SAFE_FREE(self->ice.last_message); - //... - - TSK_DEBUG_INFO("*** INVITE Dialog destroyed ***"); - } - return self; -} - -static int tsip_dialog_invite_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) -{ - return tsip_dialog_cmp(obj1, obj2); -} - -static const tsk_object_def_t tsip_dialog_invite_def_s = { - sizeof(tsip_dialog_invite_t), - tsip_dialog_invite_ctor, - tsip_dialog_invite_dtor, - tsip_dialog_invite_cmp, -}; -const tsk_object_def_t *tsip_dialog_invite_def_t = &tsip_dialog_invite_def_s; +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as publishd by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tsip_dialog_invite.c + * @brief SIP dialog INVITE as per RFC 3261. + * The SOA machine is designed as per RFC 3264 and draft-ietf-sipping-sip-offeranswer-12. + * MMTel services implementation follow 3GPP TS 24.173. + * + * @author Mamadou Diop + * + + */ +#include "tinysip/dialogs/tsip_dialog_invite.h" + +#include "tinysip/dialogs/tsip_dialog_invite.common.h" + +#include "tinysip/transactions/tsip_transac_layer.h" +#include "tinysip/transports/tsip_transport_layer.h" +#include "tinysip/dialogs/tsip_dialog_layer.h" + +#include "tinysip/headers/tsip_header_Allow.h" +#include "tinysip/headers/tsip_header_Dummy.h" +#include "tinysip/headers/tsip_header_Max_Forwards.h" +#include "tinysip/headers/tsip_header_Min_SE.h" +#include "tinysip/headers/tsip_header_RAck.h" +#include "tinysip/headers/tsip_header_Require.h" +#include "tinysip/headers/tsip_header_RSeq.h" +#include "tinysip/headers/tsip_header_Session_Expires.h" +#include "tinysip/headers/tsip_header_Supported.h" + +#include "tinysdp/parsers/tsdp_parser_message.h" + +#include "tinymedia/tmedia_defaults.h" + +#include "tsk_debug.h" + +#if METROPOLIS +# define TSIP_INFO_FASTUPDATE_OUT_INTERVAL_MIN 0 // millis +#else +# define TSIP_INFO_FASTUPDATE_OUT_INTERVAL_MIN 1500 // millis +#endif + +#if HAVE_LIBXML2 +#include +#include +#include +#include +#endif + +// http://cdnet.stpi.org.tw/techroom/market/_pdf/2009/eetelecomm_09_009_OneVoiceProfile.pdf +// 3GPP TS 26.114 (MMTel): Media handling and interaction +// 3GPP TS 24.173 (MMTel): Supplementary Services +// +/* ======================== MMTel Supplementary Services ======================== +3GPP TS 24.607 : Originating Identification Presentation +3GPP TS 24.608 : Terminating Identification Presentation +3GPP TS 24.607 : Originating Identification Restriction +3GPP TS 24.608 : Terminating Identification Restriction + +3GPP TS 24.604 : Communication Diversion Unconditional +3GPP TS 24.604 : Communication Diversion on not Logged +3GPP TS 24.604 : Communication Diversion on Busy +3GPP TS 24.604 : Communication Diversion on not Reachable +3GPP TS 24.604 : Communication Diversion on No Reply +3GPP TS 24.611 : Barring of All Incoming Calls +3GPP TS 24.611 : Barring of All Outgoing Calls +3GPP TS 24.611 : Barring of Outgoing International Calls +3GPP TS 24.611 : Barring of Incoming Calls - When Roaming +3GPP TS 24.610 : Communication Hold +3GPP TS 24.606 : Message Waiting Indication +3GPP TS 24.615 : Communication Waiting +3GPP TS 24.605 : Ad-Hoc Multi Party Conference +*/ + +extern int tsip_dialog_add_session_headers(const tsip_dialog_t *self, tsip_request_t* request); + +/* ======================== internal functions ======================== */ +/*static*/ int tsip_dialog_invite_msession_start(tsip_dialog_invite_t *self); +/*static*/ int tsip_dialog_invite_msession_configure(tsip_dialog_invite_t *self); +/*static*/ int send_INVITEorUPDATE(tsip_dialog_invite_t *self, tsk_bool_t is_INVITE, tsk_bool_t force_sdp); +/*static*/ int send_PRACK(tsip_dialog_invite_t *self, const tsip_response_t* r1xx); +/*static*/ int send_ACK(tsip_dialog_invite_t *self, const tsip_response_t* r2xxINVITE); +/*static*/ int send_RESPONSE(tsip_dialog_invite_t *self, const tsip_request_t* request, short code, const char* phrase, tsk_bool_t force_sdp); +/*static*/ int send_ERROR(tsip_dialog_invite_t* self, const tsip_request_t* request, short code, const char* phrase, const char* reason); +/*static*/ int send_BYE(tsip_dialog_invite_t *self); +/*static*/ int send_CANCEL(tsip_dialog_invite_t *self); +/*static*/ int tsip_dialog_invite_notify_parent(tsip_dialog_invite_t *self, const tsip_response_t* response); +/*static*/ int send_INFO(tsip_dialog_invite_t *self, const char* content_type, const void* content_ptr, tsk_size_t content_size); +static int tsip_dialog_invite_OnTerminated(tsip_dialog_invite_t *self); +static int tsip_dialog_invite_msession_onerror_cb(const void* usrdata, const struct tmedia_session_s* session, const char* reason, tsk_bool_t is_fatal); +static int tsip_dialog_invite_msession_rfc5168_cb(const void* usrdata, const struct tmedia_session_s* session, const char* reason, enum tmedia_session_rfc5168_cmd_e command); + +/* ======================== external functions ======================== */ +extern int tsip_dialog_invite_ice_process_ro(tsip_dialog_invite_t * self, const tsdp_message_t* sdp_ro, tsk_bool_t is_remote_offer); +extern int tsip_dialog_invite_ice_set_media_type(tsip_dialog_invite_t * self, tmedia_type_t media_type); +extern int tsip_dialog_invite_stimers_cancel(tsip_dialog_invite_t* self); +extern int tsip_dialog_invite_qos_timer_cancel(tsip_dialog_invite_t* self); +extern int tsip_dialog_invite_qos_timer_schedule(tsip_dialog_invite_t* self); +extern int tsip_dialog_invite_stimers_schedule(tsip_dialog_invite_t* self, uint64_t timeout); +extern int tsip_dialog_invite_stimers_handle(tsip_dialog_invite_t* self, const tsip_message_t* message); +extern int tsip_dialog_invite_hold_handle(tsip_dialog_invite_t* self, const tsip_request_t* rINVITEorUPDATE); + +extern int tsip_dialog_invite_ice_timers_set(tsip_dialog_invite_t *self, int64_t timeout); +extern tsk_bool_t tsip_dialog_invite_ice_is_enabled(const tsip_dialog_invite_t * self); +extern tsk_bool_t tsip_dialog_invite_ice_is_connected(const tsip_dialog_invite_t * self); +extern int tsip_dialog_invite_ice_process_lo(tsip_dialog_invite_t * self, const tsdp_message_t* sdp_lo); + +/* ======================== transitions ======================== */ +static int x0000_Connected_2_Connected_X_oDTMF(va_list *app); +static int x0000_Connected_2_Connected_X_oLMessage(va_list *app); +static int x0000_Connected_2_Connected_X_iACK(va_list *app); +static int x0000_Connected_2_Connected_X_iINVITEorUPDATE(va_list *app); +static int x0000_Connected_2_Connected_X_oINVITE(va_list *app); + + +static int x0000_Any_2_Any_X_i1xx(va_list *app); +static int x0000_Any_2_Any_X_oINFO(va_list *app); +static int x0000_Any_2_Any_X_iINFO(va_list *app); +static int x0000_Any_2_Any_X_i401_407_Challenge(va_list *app); +static int x0000_Any_2_Any_X_i2xxINVITEorUPDATE(va_list *app); + +static int x0000_Any_2_Any_X_iPRACK(va_list *app); +static int x0000_Any_2_Any_X_iOPTIONS(va_list *app); +static int x0000_Any_2_Trying_X_oBYE(va_list *app); /* If not Connected => Cancel will be called instead. See tsip_dialog_hangup() */ +static int x0000_Any_2_Terminated_X_iBYE(va_list *app); +static int x0000_Any_2_Trying_X_shutdown(va_list *app); + +static int x9998_Any_2_Terminated_X_transportError(va_list *app); +static int x9999_Any_2_Any_X_Error(va_list *app); + +/* ======================== conds ======================== */ +static tsk_bool_t _fsm_cond_is_resp2INVITE(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + return TSIP_RESPONSE_IS_TO_INVITE(message); +} +static tsk_bool_t _fsm_cond_is_resp2UPDATE(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + return TSIP_RESPONSE_IS_TO_UPDATE(message); +} +static tsk_bool_t _fsm_cond_is_resp2BYE(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + return TSIP_RESPONSE_IS_TO_BYE(message); +} +static tsk_bool_t _fsm_cond_is_resp2PRACK(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + return TSIP_RESPONSE_IS_TO_PRACK(message); +} +static tsk_bool_t _fsm_cond_is_resp2INFO(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + return TSIP_RESPONSE_IS_TO_INFO(message); +} + +/* ======================== actions ======================== */ +/* #include "tinysip/dialogs/tsip_dialog_invite.common.h" */ + +/* ======================== states ======================== */ +/* #include "tinysip/dialogs/tsip_dialog_invite.common.h" */ + +/* ICE handler */ +extern int tsip_dialog_invite_ice_init(tsip_dialog_invite_t *self); +/* Client-Side dialog */ +extern int tsip_dialog_invite_client_init(tsip_dialog_invite_t *self); +/* Server-Side dialog */ +extern int tsip_dialog_invite_server_init(tsip_dialog_invite_t *self); +/* 3GPP TS 24.610: Communication Hold */ +extern int tsip_dialog_invite_hold_init(tsip_dialog_invite_t *self); +/* 3GPP TS 24.629: Explicit Communication Transfer (ECT) using IP Multimedia (IM) Core Network (CN) subsystem */ +extern int tsip_dialog_invite_ect_init(tsip_dialog_invite_t *self); +/* RFC 4028: Session Timers */ +extern int tsip_dialog_invite_stimers_init(tsip_dialog_invite_t *self); +/* RFC 3312: Integration of Resource Management and Session Initiation Protocol (SIP) */ +extern int tsip_dialog_invite_qos_init(tsip_dialog_invite_t *self); + +int tsip_dialog_invite_event_callback(const tsip_dialog_invite_t *self, tsip_dialog_event_type_t type, const tsip_message_t *msg) +{ + int ret = -1; + + switch(type) { + case tsip_dialog_i_msg: { + if(msg) { + if(TSIP_MESSAGE_IS_RESPONSE(msg)) { /* Response */ + const tsip_action_t* action = tsip_dialog_keep_action(TSIP_DIALOG(self), msg) ? TSIP_DIALOG(self)->curr_action : tsk_null; + if(TSIP_RESPONSE_IS_1XX(msg)) { // 100-199 + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_i1xx, msg, action); + } + else if(TSIP_RESPONSE_IS_2XX(msg)) { // 200-299 + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_i2xx, msg, action); + } + else if(TSIP_RESPONSE_CODE(msg) == 401 || TSIP_RESPONSE_CODE(msg) == 407) { // 401,407 + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_i401_i407, msg, action); + } + else if(TSIP_RESPONSE_CODE(msg) == 422) { // 422 + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_i422, msg, action); + } + else if(TSIP_RESPONSE_IS_3456(msg)) { // 300-699 + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_i300_to_i699, msg, action); + } + else; // Ignore + } + else { /* Request */ + if(TSIP_REQUEST_IS_INVITE(msg)) { // INVITE + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iINVITE, msg, tsk_null); + } + else if(TSIP_REQUEST_IS_UPDATE(msg)) { // UPDATE + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iUPDATE, msg, tsk_null); + } + else if(TSIP_REQUEST_IS_PRACK(msg)) { // PRACK + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iPRACK, msg, tsk_null); + } + else if(TSIP_REQUEST_IS_ACK(msg)) { // ACK + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iACK, msg, tsk_null); + } + else if(TSIP_REQUEST_IS_OPTIONS(msg)) { // OPTIONS + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iOPTIONS, msg, tsk_null); + } + else if(TSIP_REQUEST_IS_BYE(msg)) { // BYE + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iBYE, msg, tsk_null); + } + else if(TSIP_REQUEST_IS_CANCEL(msg)) { // CANCEL + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iCANCEL, msg, tsk_null); + } + else if(TSIP_REQUEST_IS_INFO(msg)) { // INFO + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iINFO, msg, tsk_null); + } + else if(TSIP_REQUEST_IS_NOTIFY(msg)) { // NOTIFY + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iNOTIFY, msg, tsk_null); + } + else if(TSIP_REQUEST_IS_REFER(msg)) { // REFER + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iREFER, msg, tsk_null); + } + } + } + break; + } + + case tsip_dialog_canceled: { + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_oCANCEL, msg, tsk_null); + break; + } + + case tsip_dialog_timedout: { + // Do nothing if request type is "INFO" + if(!TSIP_MESSAGE_IS_REQUEST(msg) || !TSIP_REQUEST_IS_INFO(msg)) { + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_transporterror, msg, tsk_null); + } + break; + } + case tsip_dialog_terminated: + case tsip_dialog_error: + case tsip_dialog_transport_error: { + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_transporterror, msg, tsk_null); + break; + } + + default: + break; + } + + return ret; +} + +/**Timer manager callback. + * + * @param self The owner of the signaled timer. + * @param timer_id The identifier of the signaled timer. + * + * @return Zero if succeed and non-zero error code otherwise. +**/ +int tsip_dialog_invite_timer_callback(const tsip_dialog_invite_t* self, tsk_timer_id_t timer_id) +{ + int ret = -1; + + if(self) { + if(timer_id == self->stimers.timer.id) { + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_timerRefresh, tsk_null, tsk_null); + } + else if(timer_id == self->timer100rel.id) { + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_timer100rel, tsk_null, tsk_null); + } + else if(timer_id == self->qos.timer.id) { + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_timerRSVP, tsk_null, tsk_null); + } + else if(timer_id == self->timershutdown.id) { + ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_shutdown_timedout, tsk_null, tsk_null); + } + } + return ret; +} + +tsip_dialog_invite_t* tsip_dialog_invite_create(const tsip_ssession_handle_t* ss, const char* call_id) +{ + return tsk_object_new(tsip_dialog_invite_def_t, ss, call_id); +} + +int tsip_dialog_invite_init(tsip_dialog_invite_t *self) +{ + /* special cases (fsm) should be tried first */ + + /* ICE */ + tsip_dialog_invite_ice_init(self); + /* Client-Side dialog */ + tsip_dialog_invite_client_init(self); + /* Server-Side dialog */ + tsip_dialog_invite_server_init(self); + /* 3GPP TS 24.610: Communication Hold */ + tsip_dialog_invite_hold_init(self); + /* 3GPP TS 24.629: Explicit Communication Transfer (ECT) using IP Multimedia (IM) Core Network (CN) subsystem */ + tsip_dialog_invite_ect_init(self); + /* RFC 4028: Session Timers */ + tsip_dialog_invite_stimers_init(self); + /* RFC 3312: Integration of Resource Management and Session Initiation Protocol (SIP) */ + tsip_dialog_invite_qos_init(self); + + /* Initialize the state machine (all other cases) */ + tsk_fsm_set(TSIP_DIALOG_GET_FSM(self), + + /*======================= + * === Started === + */ + // Started -> (Any) -> Started + TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_dialog_invite_Started_2_Started_X_any"), + + /*======================= + * === Connected === + */ + // Connected -> (Send DTMF) -> Connected + TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_dtmf_send, _fsm_state_Connected, x0000_Connected_2_Connected_X_oDTMF, "x0000_Connected_2_Connected_X_oDTMF"), + // Connected -> (Send MSRP message) -> Connected + TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_msrp_send_msg, _fsm_state_Connected, x0000_Connected_2_Connected_X_oLMessage, "x0000_Connected_2_Connected_X_oLMessage"), + // Connected -> (iACK) -> Connected + TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_iACK, _fsm_state_Connected, x0000_Connected_2_Connected_X_iACK, "x0000_Connected_2_Connected_X_iACK"), + // Connected -> (iINVITE) -> Connected + TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_iINVITE, _fsm_state_Connected, x0000_Connected_2_Connected_X_iINVITEorUPDATE, "x0000_Connected_2_Connected_X_iINVITE"), + // Connected -> (iUPDATE) -> Connected + TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_iUPDATE, _fsm_state_Connected, x0000_Connected_2_Connected_X_iINVITEorUPDATE, "x0000_Connected_2_Connected_X_iUPDATE"), + // Connected -> (send reINVITE) -> Connected + TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_oINVITE, _fsm_state_Connected, x0000_Connected_2_Connected_X_oINVITE, "x0000_Connected_2_Connected_X_oINVITE"), + + /*======================= + * === BYE/SHUTDOWN === + */ + // Any -> (oBYE) -> Trying + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_oBYE, _fsm_state_Trying, x0000_Any_2_Trying_X_oBYE, "x0000_Any_2_Trying_X_oBYE"), + // Any -> (iBYE) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_iBYE, _fsm_state_Terminated, x0000_Any_2_Terminated_X_iBYE, "x0000_Any_2_Terminated_X_iBYE"), + // Any -> (i401/407 BYE) -> Any + TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i401_i407, _fsm_cond_is_resp2BYE, tsk_fsm_state_any, x0000_Any_2_Any_X_i401_407_Challenge, "x0000_Any_2_Any_X_i401_407_Challenge"), + // Any -> (i3xx-i6xx BYE) -> Terminated + TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i300_to_i699, _fsm_cond_is_resp2BYE, _fsm_state_Terminated, tsk_null, "x0000_Any_2_Terminated_X_i3xxTOi6xxBYE"), + // Any -> (i2xxx BYE) -> Terminated + TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i2xx, _fsm_cond_is_resp2BYE, _fsm_state_Terminated, tsk_null, "x0000_Any_2_Terminated_X_i2xxBYE"), + // Any -> (Shutdown) -> Trying + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_oShutdown, _fsm_state_Trying, x0000_Any_2_Trying_X_shutdown, "x0000_Any_2_Trying_X_shutdown"), + // Any -> (shutdown timedout) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_shutdown_timedout, _fsm_state_Terminated, tsk_null, "tsip_dialog_invite_shutdown_timedout"), + + + /*======================= + * === Any === + */ + // Any -> (i1xx) -> Any + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_i1xx, tsk_fsm_state_any, x0000_Any_2_Any_X_i1xx, "x0000_Any_2_Any_X_i1xx"), + // Any -> (oINFO) -> Any + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_oINFO, tsk_fsm_state_any, x0000_Any_2_Any_X_oINFO, "x0000_Any_2_Any_X_oINFO"), + // Any -> (iINFO) -> Any + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_iINFO, tsk_fsm_state_any, x0000_Any_2_Any_X_iINFO, "x0000_Any_2_Any_X_iINFO"), + // Any -> (i401/407) + // + // Any -> (iPRACK) -> Any + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_iPRACK, tsk_fsm_state_any, x0000_Any_2_Any_X_iPRACK, "x0000_Any_2_Any_X_iPRACK"), + // Any -> (iOPTIONS) -> Any + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_iOPTIONS, tsk_fsm_state_any, x0000_Any_2_Any_X_iOPTIONS, "x0000_Any_2_Any_X_iOPTIONS"), + // Any -> (i2xx INVITE) -> Any + TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i2xx, _fsm_cond_is_resp2INVITE, tsk_fsm_state_any, x0000_Any_2_Any_X_i2xxINVITEorUPDATE, "x0000_Any_2_Any_X_i2xxINVITE"), + // Any -> (i2xx UPDATE) -> Any + TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i2xx, _fsm_cond_is_resp2UPDATE, tsk_fsm_state_any, x0000_Any_2_Any_X_i2xxINVITEorUPDATE, "x0000_Any_2_Any_X_i2xxUPDATE"), + // Any -> (i401/407 INVITE) -> Any + TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i401_i407, _fsm_cond_is_resp2INVITE, tsk_fsm_state_any, x0000_Any_2_Any_X_i401_407_Challenge, "x0000_Any_2_Any_X_i401_407_Challenge"), + // Any -> (i401/407 UPDATE) -> Any + TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i401_i407, _fsm_cond_is_resp2UPDATE, tsk_fsm_state_any, x0000_Any_2_Any_X_i401_407_Challenge, "x0000_Any_2_Any_X_i401_407_Challenge"), + // Any -> (i2xx PRACK) -> Any + TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i2xx, _fsm_cond_is_resp2PRACK, tsk_fsm_state_any, tsk_null, "x0000_Any_2_Any_X_i2xxPRACK"), + // Any -> (i2xx INFO) -> Any + TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_i2xx, _fsm_cond_is_resp2INFO, tsk_fsm_state_any, tsk_null, "x0000_Any_2_Any_X_i2xxINFO"), + // Any -> (transport error) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, x9998_Any_2_Terminated_X_transportError, "x9998_Any_2_Terminated_X_transportError"), + // Any -> (transport error) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_error, _fsm_state_Terminated, x9999_Any_2_Any_X_Error, "x9999_Any_2_Any_X_Error"), + + TSK_FSM_ADD_NULL()); + + /* Sets callback function */ + TSIP_DIALOG(self)->callback = TSIP_DIALOG_EVENT_CALLBACK_F(tsip_dialog_invite_event_callback); + + /* Timers */ + self->timer100rel.id = TSK_INVALID_TIMER_ID; + self->stimers.timer.id = TSK_INVALID_TIMER_ID; + self->timershutdown.id = TSK_INVALID_TIMER_ID; + self->timershutdown.timeout = TSIP_DIALOG_SHUTDOWN_TIMEOUT; + + return 0; +} + +// start sending +int tsip_dialog_invite_start(tsip_dialog_invite_t *self) +{ + int ret = -1; + if(self && !TSIP_DIALOG(self)->running) { + if(!(ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_oINVITE, tsk_null, tsk_null))) { + TSIP_DIALOG(self)->running = tsk_true; + } + } + return ret; +} + +int tsip_dialog_invite_process_ro(tsip_dialog_invite_t *self, const tsip_message_t* message) +{ + tsdp_message_t* sdp_ro = tsk_null; + tmedia_type_t old_media_type; + tmedia_type_t new_media_type; + tsk_bool_t media_session_was_null; + int ret = 0; + tmedia_ro_type_t ro_type = tmedia_ro_type_none; + + if(!self || !message) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if (self->is_cancelling) { + TSK_DEBUG_INFO("Cancelling the INVITE...ignore the incoming SDP"); + return 0; + } + + /* Parse SDP content */ + if(TSIP_MESSAGE_HAS_CONTENT(message)) { + if(tsk_striequals("application/sdp", TSIP_MESSAGE_CONTENT_TYPE(message))) { + if(!(sdp_ro = tsdp_message_parse(TSIP_MESSAGE_CONTENT_DATA(message), TSIP_MESSAGE_CONTENT_DATA_LENGTH(message)))) { + TSK_DEBUG_ERROR("Failed to parse remote sdp message:\n [%s]", (const char*)TSIP_MESSAGE_CONTENT_DATA(message)); + return -2; + } + // ICE processing + if(self->supported.ice) { + tsip_dialog_invite_ice_process_ro(self, sdp_ro, TSIP_MESSAGE_IS_REQUEST(message)); + } + } + else { + TSK_DEBUG_ERROR("[%s] content-type is not supportted", TSIP_MESSAGE_CONTENT_TYPE(message)); + return -3; + } + } + else { + if(TSIP_DIALOG(self)->state == tsip_initial && TSIP_REQUEST_IS_INVITE(message)) { /* Bodiless initial INVITE */ + TSIP_DIALOG_GET_SS(self)->media.type = tmedia_defaults_get_media_type(); // Default media for initial INVITE to send with the first reliable answer + } + else { + return 0; + } + } + + ro_type = (TSIP_REQUEST_IS_INVITE(message) || TSIP_REQUEST_IS_UPDATE(message)) // ACK/PRACK can only contain a response if the initial INVITE was bodiless + ? tmedia_ro_type_offer + :(TSIP_RESPONSE_IS_1XX(message) ? tmedia_ro_type_provisional : tmedia_ro_type_answer); + media_session_was_null = (self->msession_mgr == tsk_null); + old_media_type = TSIP_DIALOG_GET_SS(self)->media.type; + new_media_type = sdp_ro ? tmedia_type_from_sdp(sdp_ro) : old_media_type; + + /* Create session Manager if not already done */ + if(!self->msession_mgr) { + int32_t transport_idx = TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default; + TSIP_DIALOG_GET_SS(self)->media.type = new_media_type; + self->msession_mgr = tmedia_session_mgr_create(TSIP_DIALOG_GET_SS(self)->media.type, TSIP_DIALOG_GET_STACK(self)->network.local_ip[transport_idx], + TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]), (sdp_ro == tsk_null)); + if(TSIP_DIALOG_GET_STACK(self)->natt.ctx) { + tmedia_session_mgr_set_natt_ctx(self->msession_mgr, TSIP_DIALOG_GET_STACK(self)->natt.ctx, TSIP_DIALOG_GET_STACK(self)->network.aor.ip[transport_idx]); + } + ret = tmedia_session_mgr_set_ice_ctx(self->msession_mgr, self->ice.ctx_audio, self->ice.ctx_video); + } + + if(sdp_ro) { + if (tmedia_session_mgr_is_new_ro(self->msession_mgr, sdp_ro)) { + ret = tsip_dialog_invite_msession_configure(self); + } + if((ret = tmedia_session_mgr_set_ro(self->msession_mgr, sdp_ro, ro_type))) { + TSK_DEBUG_ERROR("Failed to set remote offer"); + goto bail; + } + } + + // is media update? + // (old_media_type == new_media_type) means the new session are rejected. This is way we match the CSeq + if(!media_session_was_null && (old_media_type != new_media_type || (TSIP_MESSAGE_IS_RESPONSE(message) && self->cseq_out_media_update == message->CSeq->seq)) && (self->msession_mgr->sdp.lo && self->msession_mgr->sdp.ro)) { + // at this point the media session manager has been succeffuly started and all is ok + TSIP_DIALOG_GET_SS(self)->media.type = new_media_type; + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_m_updated, + TSIP_RESPONSE_CODE(message), TSIP_RESPONSE_PHRASE(message), message); + } + + /* start session manager */ + if(!self->msession_mgr->started && (self->msession_mgr->sdp.lo && self->msession_mgr->sdp.ro)) { + /* Set MSRP Callback */ + if((self->msession_mgr->type & tmedia_msrp) == tmedia_msrp) { + tmedia_session_mgr_set_msrp_cb(self->msession_mgr, TSIP_DIALOG_GET_SS(self)->userdata, TSIP_DIALOG_GET_SS(self)->media.msrp.callback); + } + /* starts session manager*/ + ret = tsip_dialog_invite_msession_start(self); + + if(ret == 0 && TSIP_DIALOG(self)->state == tsip_early) { + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_m_early_media, + TSIP_RESPONSE_CODE(message), TSIP_RESPONSE_PHRASE(message), message); + } + } + +bail: + TSK_OBJECT_SAFE_FREE(sdp_ro); + + return ret; +} + + +//-------------------------------------------------------- +// == STATE MACHINE BEGIN == +//-------------------------------------------------------- + +int x0000_Connected_2_Connected_X_oDTMF(va_list *app) +{ + int ret; + tsip_dialog_invite_t *self; + const tsip_action_t* action; + + self = va_arg(*app, tsip_dialog_invite_t *); + va_arg(*app, const tsip_message_t *); + action = va_arg(*app, const tsip_action_t *); + + if(action) { + ret = tmedia_session_mgr_send_dtmf(self->msession_mgr, action->dtmf.event); + } + else { + TSK_DEBUG_ERROR("Invalid action"); + } + + return 0; /* always */ +} + +int x0000_Connected_2_Connected_X_oLMessage(va_list *app) +{ + int ret; + tsip_dialog_invite_t *self; + const tsip_action_t* action; + + self = va_arg(*app, tsip_dialog_invite_t *); + va_arg(*app, const tsip_message_t *); + action = va_arg(*app, const tsip_action_t *); + + if(action && action->payload) { + ret = tmedia_session_mgr_send_message(self->msession_mgr, action->payload->data, action->payload->size, + action->media.params); + } + else { + TSK_DEBUG_ERROR("Invalid action"); + } + + return 0; +} + +/* Connected -> (iACK) -> Connected */ +int x0000_Connected_2_Connected_X_iACK(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + const tsip_request_t *rACK = va_arg(*app, const tsip_request_t *); + int ret; + + // Nothing to do (in future will be used to ensure the session) + + /* No longer waiting for the initial ACK */ + self->is_initial_iack_pending = tsk_false; + + /* Process remote offer */ + if((ret = tsip_dialog_invite_process_ro(self, rACK))) { + /* Send error */ + return ret; + } + + /* Starts media session if not already done */ + if(!self->msession_mgr->started && (self->msession_mgr->sdp.lo && self->msession_mgr->sdp.ro)) { + ret = tsip_dialog_invite_msession_start(self); + } + + // starts ICE timers now that both parties receive the "candidates" + if(tsip_dialog_invite_ice_is_enabled(self)) { + tsip_dialog_invite_ice_timers_set(self, (self->required.ice ? -1 : TSIP_DIALOG_INVITE_ICE_CONNCHECK_TIMEOUT)); + } + + /* alert the user */ + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, + tsip_event_code_dialog_request_incoming, "Incoming Request", rACK); + + return 0; +} + +/* Connected -> (iINVITE or iUPDATE) -> Connected */ +int x0000_Connected_2_Connected_X_iINVITEorUPDATE(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + const tsip_request_t *rINVITEorUPDATE = va_arg(*app, const tsip_request_t *); + + int ret = 0; + tsk_bool_t bodiless_invite; + tmedia_type_t old_media_type = self->msession_mgr ? self->msession_mgr->type : tmedia_none; + tmedia_type_t new_media_type; + tsk_bool_t force_sdp; + + /* process remote offer */ + if((ret = tsip_dialog_invite_process_ro(self, rINVITEorUPDATE))) { + /* Send error */ + return ret; + } + + // force SDP in 200 OK even if the request has the same SDP version + force_sdp = TSIP_MESSAGE_HAS_CONTENT(rINVITEorUPDATE); + + // get new media_type after processing the remote offer + new_media_type = self->msession_mgr ? self->msession_mgr->type : tmedia_none; + + /** response to bodiless iINVITE always contains SDP as explained below + RFC3261 - 14.1 UAC Behavior + The same offer-answer model that applies to session descriptions in + INVITEs (Section 13.2.1) applies to re-INVITEs. As a result, a UAC + that wants to add a media stream, for example, will create a new + offer that contains this media stream, and send that in an INVITE + request to its peer. It is important to note that the full + description of the session, not just the change, is sent. This + supports stateless session processing in various elements, and + supports failover and recovery capabilities. Of course, a UAC MAY + send a re-INVITE with no session description, in which case the first + reliable non-failure response to the re-INVITE will contain the offer + (in this specification, that is a 2xx response). + */ + bodiless_invite = !TSIP_MESSAGE_HAS_CONTENT(rINVITEorUPDATE) && TSIP_REQUEST_IS_INVITE(rINVITEorUPDATE); + + /* session timers (must be before sending response) */ + if(self->stimers.timer.timeout) { + tsip_dialog_invite_stimers_handle(self, rINVITEorUPDATE); + } + + /* hold/resume */ + tsip_dialog_invite_hold_handle(self, rINVITEorUPDATE); + + // send the response + ret = send_RESPONSE(self, rINVITEorUPDATE, 200, "OK", + (self->msession_mgr && (force_sdp || bodiless_invite || self->msession_mgr->ro_changed || self->msession_mgr->state_changed || (old_media_type != new_media_type)))); + + /* alert the user */ + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, + tsip_event_code_dialog_request_incoming, "Incoming Request.", rINVITEorUPDATE); + + return ret; +} + +/* Connected -> (send reINVITE) -> Connected */ +static int x0000_Connected_2_Connected_X_oINVITE(va_list *app) +{ + int ret; + tsk_bool_t mediaType_changed; + tsip_dialog_invite_t *self; + const tsip_action_t* action; + + self = va_arg(*app, tsip_dialog_invite_t *); + va_arg(*app, const tsip_message_t *); + action = va_arg(*app, const tsip_action_t *); + + /* Get Media type from the action */ + mediaType_changed = (TSIP_DIALOG_GET_SS(self)->media.type != action->media.type && action->media.type != tmedia_none); + if (mediaType_changed) { + if (self->msession_mgr) { + ret = tmedia_session_mgr_set_media_type(self->msession_mgr, action->media.type); + } + self->cseq_out_media_update = TSIP_DIALOG(self)->cseq_value + 1; + } + + /* Appy media params received from the user */ + if(!TSK_LIST_IS_EMPTY(action->media.params)) { + ret = tmedia_session_mgr_set_3(self->msession_mgr, action->media.params); + } + + /* send the request */ + ret = send_INVITE(self, mediaType_changed); + + /* alert the user */ + if(mediaType_changed) { + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_m_updating, + tsip_event_code_dialog_request_outgoing, "Updating media type", self->last_oInvite); + } + + return ret; +} + +/* Any -> (iPRACK) -> Any */ +int x0000_Any_2_Any_X_iPRACK(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + const tsip_request_t *rPRACK = va_arg(*app, const tsip_request_t *); + + const tsip_header_RAck_t* RAck; + + if((RAck = (const tsip_header_RAck_t*)tsip_message_get_header(rPRACK, tsip_htype_RAck))) { + if((RAck->seq == self->rseq) && + (tsk_striequals(RAck->method, self->last_o1xxrel->CSeq->method)) && + (RAck->cseq == self->last_o1xxrel->CSeq->seq)) { + + ++self->rseq; + return send_RESPONSE(self, rPRACK, 200, "OK", tsk_false); + } + } + + /* Send 488 */ + return send_ERROR(self, rPRACK, 488, "Failed to match PRACK request", "SIP; cause=488; text=\"Failed to match PRACK request\""); +} + +/* Any -> (iOPTIONS) -> Any */ +int x0000_Any_2_Any_X_iOPTIONS(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + const tsip_request_t *rOPTIONS = va_arg(*app, const tsip_request_t *); + + /* Alert user */ + + /* Send 2xx */ + send_RESPONSE(self, rOPTIONS, 200, "OK", tsk_false); + + return 0; +} + + +/* Any --> (i401/407 INVITE or UPDATE) --> Any */ +int x0000_Any_2_Any_X_i401_407_Challenge(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + int ret; + + if((ret = tsip_dialog_update(TSIP_DIALOG(self), response))) { + /* Alert the user. */ + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_ao_request, + TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response); + + return ret; + } + + if(TSIP_RESPONSE_IS_TO_INVITE(response) || TSIP_RESPONSE_IS_TO_UPDATE(response)) { + return send_INVITEorUPDATE(self, TSIP_RESPONSE_IS_TO_INVITE(response), tsk_false); + } + else if(TSIP_RESPONSE_IS_TO_BYE(response)) { + return send_BYE(self); + } + else { + TSK_DEBUG_ERROR("Unexpected code called"); + return 0; + } +} + +/* Any --> (i2xx INVITE or i2xx UPDATE) --> Any */ +int x0000_Any_2_Any_X_i2xxINVITEorUPDATE(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + const tsip_response_t *r2xx = va_arg(*app, const tsip_response_t *); + int ret = 0; + + /* Update the dialog state */ + if((ret = tsip_dialog_update(TSIP_DIALOG(self), r2xx))) { + return ret; + } + + /* session timers */ + if(self->stimers.timer.timeout) { + tsip_dialog_invite_stimers_handle(self, r2xx); + } + + /* Process remote offer */ + if((ret = tsip_dialog_invite_process_ro(self, r2xx))) { + send_BYE(self); + return ret; + } + + /* send ACK */ + if(TSIP_RESPONSE_IS_TO_INVITE(r2xx)) { + ret = send_ACK(self, r2xx); + } + + // starts ICE timers now that both parties received the "candidates" + if(tsip_dialog_invite_ice_is_enabled(self)) { + tsip_dialog_invite_ice_timers_set(self, (self->required.ice ? -1 : TSIP_DIALOG_INVITE_ICE_CONNCHECK_TIMEOUT)); + } + + return ret; +} + + +/* Any -> (oBYE) -> Trying */ +int x0000_Any_2_Trying_X_oBYE(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + int ret; + + /* Alert the user */ + TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog"); + + /* send BYE */ + if((ret = send_BYE(self)) == 0) { +#if !TSIP_UNDER_APPLE // FIXME: hangs up on iOS (RTP transport runnable join never exits) + // stop session manager + if(self->msession_mgr && self->msession_mgr->started) { + tmedia_session_mgr_stop(self->msession_mgr); + } +#endif + } + return ret; +} + +/* Any -> (iBYE) -> Terminated */ +int x0000_Any_2_Terminated_X_iBYE(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + const tsip_request_t *rBYE = va_arg(*app, const tsip_request_t *); + + /* set last error (or info) */ + tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Call Terminated", tsip_event_code_dialog_terminated); + + /* send 200 OK */ + return send_RESPONSE(self, rBYE, 200, "OK", tsk_false); +} + +/* Any -> Shutdown -> Terminated */ +int x0000_Any_2_Trying_X_shutdown(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + + /* schedule shutdow timeout */ + TSIP_DIALOG_INVITE_TIMER_SCHEDULE(shutdown); + + /* alert user */ + TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog"); + + if(TSIP_DIALOG(self)->state == tsip_established) { + return send_BYE(self); + } + else if(TSIP_DIALOG(self)->state == tsip_early) { + return send_CANCEL(self); + } + + return 0; +} + + +/* Any -> (i1xx) -> Any */ +int x0000_Any_2_Any_X_i1xx(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + const tsip_response_t *r1xx = va_arg(*app, const tsip_response_t *); + int ret = 0; + + /* Update the dialog state */ + if((ret = tsip_dialog_update(TSIP_DIALOG(self), r1xx))) { + return ret; + } + + /* RFC 3262 - 4 UAC Behavior + If a provisional response is received for an initial request, and + that response contains a Require header field containing the option + tag 100rel, the response is to be sent reliably. If the response is + a 100 (Trying) (as opposed to 101 to 199), this option tag MUST be + ignored, and the procedures below MUST NOT be used. + + Assuming the response is to be transmitted reliably, the UAC MUST + create a new request with method PRACK. This request is sent within + the dialog associated with the provisional response (indeed, the + provisional response may have created the dialog). PRACK requests + MAY contain bodies, which are interpreted according to their type and + disposition. + + Note that the PRACK is like any other non-INVITE request within a + dialog. In particular, a UAC SHOULD NOT retransmit the PRACK request + when it receives a retransmission of the provisional response being + acknowledged, although doing so does not create a protocol error. + + Additional information: We should only process the SDP from reliable responses (require:100rel) + but there was many problem with some clients sending SDP with this tag: tiscali, DTAG, samsung, ... + */ + if((TSIP_RESPONSE_CODE(r1xx) >= 101 && TSIP_RESPONSE_CODE(r1xx) <=199)) { + /* Process Remote offer */ + if(TSIP_MESSAGE_HAS_CONTENT(r1xx) && (ret = tsip_dialog_invite_process_ro(self, r1xx))) { + /* Send Error */ + return ret; + } + // don't send PRACK if 100rel is only inside "supported" header + if(tsip_message_required(r1xx, "100rel") && (ret = send_PRACK(self, r1xx))) { + return ret; + } + } + + /* QoS Reservation */ + if((self->qos.timer.id == TSK_INVALID_TIMER_ID) && tsip_message_required(r1xx, "precondition") && !tmedia_session_mgr_canresume(self->msession_mgr)) { + tsip_dialog_invite_qos_timer_schedule(self); + } + + /* alert the user */ + ret = TSIP_DIALOG_INVITE_SIGNAL(self, tsip_ao_request, + TSIP_RESPONSE_CODE(r1xx), TSIP_RESPONSE_PHRASE(r1xx), r1xx); + if(self->is_transf) { + ret = tsip_dialog_invite_notify_parent(self, r1xx); + } + + return ret; +} + +/* Any -> (oINFO) -> Any */ +int x0000_Any_2_Any_X_oINFO(va_list *app) +{ + tsip_dialog_invite_t *self; + const tsip_action_t* action; + tsip_request_t* rINFO; + + self = va_arg(*app, tsip_dialog_invite_t *); + va_arg(*app, const tsip_message_t *); + action = va_arg(*app, const tsip_action_t *); + + if((rINFO = tsip_dialog_request_new(TSIP_DIALOG(self), "INFO"))) { + int ret; + if(action) { + ret = tsip_dialog_apply_action(TSIP_MESSAGE(rINFO), action); + } + ret = tsip_dialog_request_send(TSIP_DIALOG(self), rINFO); + TSK_OBJECT_SAFE_FREE(rINFO); + return ret; + } + else { + TSK_DEBUG_ERROR("Failed to create new INFO request"); + return -1; + } +} + +int x0000_Any_2_Any_X_iINFO(va_list *app) +{ + tsip_dialog_invite_t * self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t* rINFO = (tsip_request_t*)va_arg(*app, const tsip_message_t *); + int ret = -1; + + if (rINFO) { + ret = send_RESPONSE(self, rINFO, 200, "Ok", tsk_false); + { + // int tmedia_session_mgr_recv_rtcp_event(tmedia_session_mgr_t* self, tmedia_type_t media_type, tmedia_rtcp_event_type_t event_type, uint32_t ssrc_media); + if (self->msession_mgr && TSIP_MESSAGE_HAS_CONTENT(rINFO)) { + if (tsk_striequals("application/media_control+xml", TSIP_MESSAGE_CONTENT_TYPE(rINFO))) { /* rfc5168: XML Schema for Media Control */ + static uint32_t __ssrc_media_fake = 0; + static tmedia_type_t __tmedia_type_video = tmedia_video; // TODO: add bfcpvideo? + const char* content_ptr = (const char*)TSIP_MESSAGE_CONTENT_DATA(rINFO); + tsk_size_t content_size = (tsk_size_t)TSIP_MESSAGE_CONTENT_DATA_LENGTH(rINFO); + tsk_bool_t is_fir = tsk_false; + uint64_t sessionId = 0; +#if HAVE_LIBXML2 + { + xmlDoc *pDoc; + xmlNode *pRootElement; + xmlXPathContext *pPathCtx; + xmlXPathObject *pPathObj; + static const xmlChar* __xpath_expr_picture_fast_update = (const xmlChar*)"/media_control/vc_primitive/to_encoder/picture_fast_update"; + static const xmlChar* __xpath_expr_stream_id = (const xmlChar*)"/media_control/vc_primitive/stream_id"; + + if (!(pDoc = xmlParseDoc(content_ptr))) { + TSK_DEBUG_ERROR("Failed to parse XML content [%s]", content_ptr); + return 0; + } + if (!(pRootElement = xmlDocGetRootElement(pDoc))) { + TSK_DEBUG_ERROR("Failed to get root element from XML content [%s]", content_ptr); + xmlFreeDoc(pDoc); + return 0; + } + if (!(pPathCtx = xmlXPathNewContext(pDoc))) { + TSK_DEBUG_ERROR("Failed to create path context from XML content [%s]", content_ptr); + xmlFreeDoc(pDoc); + return 0; + } + // picture_fast_update + if (!(pPathObj = xmlXPathEvalExpression(__xpath_expr_picture_fast_update, pPathCtx))) { + TSK_DEBUG_ERROR("Error: unable to evaluate xpath expression: %s", __xpath_expr_picture_fast_update); + xmlXPathFreeContext(pPathCtx); + xmlFreeDoc(pDoc); + return 0; + } + is_fir = (pPathObj->type == XPATH_NODESET && pPathObj->nodesetval->nodeNr > 0); + xmlXPathFreeObject(pPathObj); + // stream_id + if (!(pPathObj = xmlXPathEvalExpression(__xpath_expr_stream_id, pPathCtx))) { + TSK_DEBUG_ERROR("Error: unable to evaluate xpath expression: %s", __xpath_expr_stream_id); + xmlXPathFreeContext(pPathCtx); + xmlFreeDoc(pDoc); + } + else if (pPathObj->type == XPATH_NODESET && pPathObj->nodesetval->nodeNr > 0 && pPathObj->nodesetval->nodeTab[0]->children && pPathObj->nodesetval->nodeTab[0]->children->content) { + sessionId = tsk_atoi64((const char*)pPathObj->nodesetval->nodeTab[0]->children->content); + } + xmlXPathFreeObject(pPathObj); + + xmlXPathFreeContext(pPathCtx); + xmlFreeDoc(pDoc); + } +#else + is_fir = (tsk_strcontains(content_ptr, content_size, "to_encoder") && tsk_strcontains(content_ptr, content_size, "picture_fast_update")); +#endif + if (is_fir) { + TSK_DEBUG_INFO("Incoming SIP INFO(picture_fast_update)"); + ret = sessionId + ? tmedia_session_mgr_recv_rtcp_event_2(self->msession_mgr, tmedia_rtcp_event_type_fir, sessionId) + : tmedia_session_mgr_recv_rtcp_event(self->msession_mgr, __tmedia_type_video, tmedia_rtcp_event_type_fir, __ssrc_media_fake); + } + else { + TSK_DEBUG_INFO("Incoming SIP INFO(unknown)"); + } + } + } + } + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, + tsip_event_code_dialog_request_incoming, "Incoming Request", rINFO); + } + + return ret; +} + +int x9998_Any_2_Terminated_X_transportError(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + + /* set last error (or info) */ + tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Transport error", tsip_event_code_dialog_terminated); + + return 0; +} + +int x9999_Any_2_Any_X_Error(va_list *app) +{ + return 0; +} + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// == STATE MACHINE END == +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +int tsip_dialog_invite_msession_configure(tsip_dialog_invite_t *self) +{ + tmedia_srtp_mode_t srtp_mode; + tmedia_mode_t avpf_mode; + tsk_bool_t is_rtcweb_enabled; + tsk_bool_t is_webrtc2sip_mode_enabled; + + if(!self || !self->msession_mgr) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + is_webrtc2sip_mode_enabled = (TSIP_DIALOG_GET_STACK(self)->network.mode == tsip_stack_mode_webrtc2sip); + is_rtcweb_enabled = (((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.profile == tmedia_profile_rtcweb); + srtp_mode = is_rtcweb_enabled ? tmedia_srtp_mode_mandatory : ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.srtp_mode; + avpf_mode = is_rtcweb_enabled ? tmedia_mode_mandatory : ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.avpf_mode; + + // set callback functions + tmedia_session_mgr_set_onerror_cbfn(self->msession_mgr, self, tsip_dialog_invite_msession_onerror_cb); + tmedia_session_mgr_set_rfc5168_cbfn(self->msession_mgr, self, tsip_dialog_invite_msession_rfc5168_cb); + + // set params + return tmedia_session_mgr_set(self->msession_mgr, + TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "srtp-mode", srtp_mode), + TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "avpf-mode", avpf_mode), + TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "webrtc2sip-mode-enabled", is_webrtc2sip_mode_enabled), // hack the media stack + TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "rtcp-enabled", self->use_rtcp), + TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "rtcpmux-enabled", self->use_rtcpmux), + TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "codecs-supported", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.codecs), + + TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "bypass-encoding", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.bypass_encoding), + TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "bypass-decoding", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.bypass_decoding), + + TMEDIA_SESSION_SET_INT32(tmedia_audio, "rtp-ssrc", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.rtp.ssrc.audio), + TMEDIA_SESSION_SET_INT32(tmedia_video, "rtp-ssrc", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.rtp.ssrc.video), + + TMEDIA_SESSION_SET_STR(self->msession_mgr->type, "dtls-file-ca", TSIP_DIALOG_GET_STACK(self)->security.tls.ca), + TMEDIA_SESSION_SET_STR(self->msession_mgr->type, "dtls-file-pbk", TSIP_DIALOG_GET_STACK(self)->security.tls.pbk), + TMEDIA_SESSION_SET_STR(self->msession_mgr->type, "dtls-file-pvk", TSIP_DIALOG_GET_STACK(self)->security.tls.pvk), + TMEDIA_SESSION_SET_INT32(self->msession_mgr->type, "dtls-cert-verify", TSIP_DIALOG_GET_STACK(self)->security.tls.verify), + + TMEDIA_SESSION_SET_INT32(tmedia_video, "fps", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.video_fps), + TMEDIA_SESSION_SET_INT32(tmedia_video, "bandwidth-max-upload", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.video_bw_up), + TMEDIA_SESSION_SET_INT32(tmedia_video, "bandwidth-max-download", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.video_bw_down), + TMEDIA_SESSION_SET_INT32(tmedia_video, "pref-size", ((tsip_ssession_t*)TSIP_DIALOG(self)->ss)->media.video_pref_size), + + tsk_null); +} + +int tsip_dialog_invite_msession_start(tsip_dialog_invite_t *self) +{ + if(!self || !self->msession_mgr) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(tsip_dialog_invite_ice_is_enabled(self) && !tsip_dialog_invite_ice_is_connected(self)) { + if(self->msession_mgr->type != self->ice.media_type) { + TSK_DEBUG_INFO("Media session type(%d)<>ICE media type(%d)", self->msession_mgr->type, self->ice.media_type); + // make sure to use the right media types + tsip_dialog_invite_ice_set_media_type(self, self->msession_mgr->type); + } + self->ice.start_smgr = tsk_true; + } + else { + self->ice.start_smgr = tsk_false; + return tmedia_session_mgr_start(self->msession_mgr); + } + return 0; +} + +// send INVITE/UPDATE request +int send_INVITEorUPDATE(tsip_dialog_invite_t *self, tsk_bool_t is_INVITE, tsk_bool_t force_sdp) +{ + int ret = -1; + tsip_request_t *request = tsk_null; + tsk_bool_t bodiless = tsk_false; + +#if BODILESS_INVITE + if(TSIP_DIALOG(self)->state == tsip_initial) { + bodiless = tsk_true; + } +#endif + + if(!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + goto bail; + } + + if((request = tsip_dialog_request_new(TSIP_DIALOG(self), is_INVITE ? "INVITE" : "UPDATE"))) { + /* apply action params to the request (will add a content if the action contains one) */ + if(TSIP_DIALOG(self)->curr_action) { + tsip_dialog_apply_action(request, TSIP_DIALOG(self)->curr_action); + } + + if(!bodiless) { + /* add our payload if current action does not have one */ + if((force_sdp || is_INVITE) || ((self->msession_mgr && self->msession_mgr->state_changed) || (TSIP_DIALOG(self)->state == tsip_initial))) { + if(!TSIP_DIALOG(self)->curr_action || !TSIP_DIALOG(self)->curr_action->payload) { + const tsdp_message_t* sdp_lo; + char* sdp; + if((sdp_lo = tmedia_session_mgr_get_lo(self->msession_mgr)) && (sdp = tsdp_message_tostring(sdp_lo))) { + tsip_message_add_content(request, "application/sdp", sdp, tsk_strlen(sdp)); + if(tsip_dialog_invite_ice_is_enabled(self)) { + ret = tsip_dialog_invite_ice_process_lo(self, sdp_lo); + } + TSK_FREE(sdp); + } + } + } + } + + /* Session timers */ + if(self->stimers.timer.timeout) { + if(self->required.timer) { + tsip_message_add_headers(request, + TSIP_HEADER_SESSION_EXPIRES_VA_ARGS(self->stimers.timer.timeout, !self->stimers.is_refresher), + TSIP_HEADER_REQUIRE_VA_ARGS("timer"), + tsk_null + ); + } + else if(self->supported.timer) { + tsip_message_add_headers(request, + TSIP_HEADER_SESSION_EXPIRES_VA_ARGS(self->stimers.timer.timeout, !self->stimers.is_refresher), + TSIP_HEADER_SUPPORTED_VA_ARGS("timer"), + tsk_null + ); + } + + } + + if(self->stimers.minse) { + tsip_message_add_headers(request, + TSIP_HEADER_MIN_SE_VA_ARGS(self->stimers.minse), + tsk_null + ); + } + + /* 100rel */ + if(self->required._100rel) { + tsip_message_add_headers(request, + TSIP_HEADER_REQUIRE_VA_ARGS("100rel"), + tsk_null + ); + } + else if(self->supported._100rel) { + tsip_message_add_headers(request, + TSIP_HEADER_SUPPORTED_VA_ARGS("100rel"), + tsk_null + ); + } + + /* QoS */ + if(self->required.precondition) { + tsip_message_add_headers(request, + TSIP_HEADER_REQUIRE_VA_ARGS("precondition"), + tsk_null + ); + } + else if(self->supported.precondition) { + tsip_message_add_headers(request, + TSIP_HEADER_SUPPORTED_VA_ARGS("precondition"), + tsk_null + ); + } + + + /* Always added headers */ + // Explicit Communication Transfer (3GPP TS 24.629) + /*tsip_message_add_headers(request, + TSIP_HEADER_SUPPORTED_VA_ARGS("norefersub,replaces"), + tsk_null + );*/ + + /* send the request */ + ret = tsip_dialog_request_send(TSIP_DIALOG(self), request); + if(ret == 0) { + /* update last INVITE */ + TSK_OBJECT_SAFE_FREE(self->last_oInvite); + self->last_oInvite = request; + } + else { + TSK_OBJECT_SAFE_FREE(request); + } + } + +bail: + return ret; +} + +// Send PRACK +int send_PRACK(tsip_dialog_invite_t *self, const tsip_response_t* r1xx) +{ + // "Require: 100rel\r\n" should be checked by the caller of this function + int ret = -1; + tsip_request_t *request = tsk_null; + const tsip_header_RSeq_t* RSeq; + + if(!self || !r1xx || !r1xx->CSeq) { + TSK_DEBUG_ERROR("Invalid parameter"); + goto bail; + } + + + /* RFC 3262 - 4 UAC Behavior + The UAC MUST maintain a sequence number that indicates the most recently + received in-order reliable provisional response for the initial request. + */ + if((RSeq = (const tsip_header_RSeq_t*)tsip_message_get_header(r1xx, tsip_htype_RSeq))) { + + /* RFC 3262 - 4 UAC Behavior + If the UAC receives another reliable provisional + response to the same request, and its RSeq value is not one higher + than the value of the sequence number, that response MUST NOT be + acknowledged with a PRACK, and MUST NOT be processed further by the + UAC. An implementation MAY discard the response, or MAY cache the + response in the hopes of receiving the missing responses. + */ + if(self->rseq && (RSeq->seq <= self->rseq)) { + TSK_DEBUG_WARN("1xx.RSeq value is not one higher than lastINVITE.RSeq."); + ret = 0; /* Not error */ + goto bail; + } + self->rseq = RSeq->seq; + } + + /* RFC 3262 - 4 UAC Behavior + Assuming the response is to be transmitted reliably, the UAC MUST + create a new request with method PRACK. + */ + if(!(request = tsip_dialog_request_new(TSIP_DIALOG(self), "PRACK"))) { + goto bail; + } + + /* RFC 3262 - 7.2 RAck + The first number is the value from the RSeq header in the provisional + response that is being acknowledged. The next number, and the + method, are copied from the CSeq in the response that is being + acknowledged. The method name in the RAck header is case sensitive. + */ + TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_RACK_VA_ARGS(self->rseq, r1xx->CSeq->seq, r1xx->CSeq->method)); + //TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_DUMMY_VA_ARGS("Test", "value")); + + /* Initial INVITE was a bodiless request and 100rel is supported (I'm Alice) + 1. Alice sends an initial INVITE without offer + 2. Bob's answer is sent in the first reliable provisional response, in this case it's a 1xx INVITE response + 3. Alice's answer is sent in the PRACK response + */ + if(self->is_client && (self->last_oInvite && !TSIP_MESSAGE_HAS_CONTENT(self->last_oInvite))) { + const tsdp_message_t* sdp_lo; + char* sdp; + if((sdp_lo = tmedia_session_mgr_get_lo(self->msession_mgr)) && (sdp = tsdp_message_tostring(sdp_lo))) { + tsip_message_add_content(request, "application/sdp", sdp, tsk_strlen(sdp)); + TSK_FREE(sdp); + } + } + + // Send request + ret = tsip_dialog_request_send(TSIP_DIALOG(self), request); + +bail: + TSK_OBJECT_SAFE_FREE(request); + return ret; +} + +// Send CANCEL +int send_CANCEL(tsip_dialog_invite_t *self) +{ + int ret = -1; + if(!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + goto bail; + } + /* RFC 3261 - 9 Canceling a Request + If the request being cancelled contains a Route header field, the + CANCEL request MUST include that Route header field's values. + ==> up to tsip_dialog_request_new() + */ + + /* RFC 3261 - 9 Canceling a Request + Once the CANCEL is constructed, the client SHOULD check whether it + has received any response (provisional or final) for the request + being cancelled (herein referred to as the "original request"). + + If no provisional response has been received, the CANCEL request MUST + NOT be sent; rather, the client MUST wait for the arrival of a + provisional response before sending the request. + ==> up to the caller to check that we are not in the initial state and the FSM + is in Trying state. + */ + + /* RFC 3261 - 9 Canceling a Request + The following procedures are used to construct a CANCEL request. The + Request-URI, Call-ID, To, the numeric part of CSeq, and From header + fields in the CANCEL request MUST be identical to those in the + request being cancelled, including tags. A CANCEL constructed by a + client MUST have only a single Via header field value matching the + top Via value in the request being cancelled. Using the same values + for these header fields allows the CANCEL to be matched with the + request it cancels (Section 9.2 indicates how such matching occurs). + However, the method part of the CSeq header field MUST have a value + of CANCEL. This allows it to be identified and processed as a + transaction in its own right (See Section 17) + */ + if(self->last_oInvite) { + /* to avoid concurrent access, take a reference to the request */ + tsip_request_t* last_oInvite = tsk_object_ref(self->last_oInvite); + tsip_request_t* cancel; + + if((cancel = tsip_request_create("CANCEL", last_oInvite->line.request.uri))) { + const tsk_list_item_t* item; + const tsip_header_t* header; + + tsip_message_add_headers(cancel, + TSIP_HEADER_CSEQ_VA_ARGS(last_oInvite->CSeq->seq, "CANCEL"), + TSIP_HEADER_MAX_FORWARDS_VA_ARGS(TSIP_HEADER_MAX_FORWARDS_DEFAULT), + TSIP_HEADER_CONTENT_LENGTH_VA_ARGS(0), + tsk_null); + + cancel->Call_ID = tsk_object_ref(last_oInvite->Call_ID); + cancel->To = tsk_object_ref(last_oInvite->To); + cancel->From = tsk_object_ref(last_oInvite->From); + cancel->firstVia = tsk_object_ref(last_oInvite->firstVia); + cancel->sigcomp_id = tsk_strdup(TSIP_DIALOG_GET_SS(self)->sigcomp_id); + + // Copy Authorizations, Routes and Proxy-Auth + tsk_list_foreach(item, last_oInvite->headers) { + if(!(header = TSIP_HEADER(item->data))) { + continue; + } + + switch(header->type) { + case tsip_htype_Route: + case tsip_htype_Proxy_Authorization: + case tsip_htype_Authorization: + header = tsk_object_ref((void*)header); + if(!cancel->headers) { + cancel->headers = tsk_list_create(); + } + tsk_list_push_back_data(cancel->headers, (void**)&header); + break; + default: + break; + } + } + + ret = tsip_dialog_add_session_headers(TSIP_DIALOG(self), cancel); + ret = tsip_dialog_request_send(TSIP_DIALOG(self), cancel); + TSK_OBJECT_SAFE_FREE(cancel); + } + else { + TSK_DEBUG_ERROR("Failed to create CANCEL request"); + ret = -2; + } + + TSK_OBJECT_SAFE_FREE(last_oInvite); + return ret; + } + else { + TSK_DEBUG_WARN("There is no INVITE request to cancel"); + return 0; + } + +bail: + return ret; +} + +int tsip_dialog_invite_notify_parent(tsip_dialog_invite_t *self, const tsip_response_t* response) +{ + int ret = -1; + tsip_dialog_t* dlg_parent = tsip_dialog_layer_find_by_ssid(TSIP_DIALOG_GET_STACK(self)->layer_dialog, TSIP_DIALOG_GET_SS(self)->id_parent); + if(dlg_parent) { + tsip_action_t* action = tsip_action_create(tsip_atype_ect_lnotify, + TSIP_ACTION_SET_NULL()); + if(action) { + ret = tsip_dialog_fsm_act(dlg_parent, action->type, response, action); + TSK_OBJECT_SAFE_FREE(action); + } + else { + TSK_DEBUG_ERROR("Failed to create action object"); + } + TSK_OBJECT_SAFE_FREE(dlg_parent); + } + else { + TSK_DEBUG_ERROR("Failed to find parent with id = %llu", TSIP_DIALOG_GET_SS(self)->id_parent); + } + return ret; +} + +// Send BYE +int send_BYE(tsip_dialog_invite_t *self) +{ + int ret = -1; + tsip_request_t *bye = tsk_null; + + if(!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + goto bail; + } + /* RFC 3261 - 15.1.1 UAC Behavior + A BYE request is constructed as would any other request within a + dialog, as described in Section 12. + + Once the BYE is constructed, the UAC core creates a new non-INVITE + client transaction, and passes it the BYE request. The UAC MUST + consider the session terminated (and therefore stop sending or + listening for media) as soon as the BYE request is passed to the + client transaction. If the response for the BYE is a 481 + (Call/Transaction Does Not Exist) or a 408 (Request Timeout) or no + + response at all is received for the BYE (that is, a timeout is + returned by the client transaction), the UAC MUST consider the + session and the dialog terminated. + */ + if((bye = tsip_dialog_request_new(TSIP_DIALOG(self), "BYE"))) { + if(TSIP_DIALOG(self)->curr_action) { + tsip_dialog_apply_action(bye, TSIP_DIALOG(self)->curr_action); + } + ret = tsip_dialog_request_send(TSIP_DIALOG(self), bye); + TSK_OBJECT_SAFE_FREE(bye); + } + +bail: + return ret; +} + +// Send INFO +int send_INFO(tsip_dialog_invite_t *self, const char* content_type, const void* content_ptr, tsk_size_t content_size) +{ + int ret = -1; + tsip_request_t *info = tsk_null; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + goto bail; + } + if ((info = tsip_dialog_request_new(TSIP_DIALOG(self), "INFO"))) { + if (TSIP_DIALOG(self)->curr_action) { + ret = tsip_dialog_apply_action(info, TSIP_DIALOG(self)->curr_action); + if (ret) { + goto bail; + } + } + if (content_type && content_ptr && content_size) { + ret = tsip_message_add_content(info, content_type, content_ptr, content_size); + if (ret) { + goto bail; + } + } + ret = tsip_dialog_request_send(TSIP_DIALOG(self), info); + if (ret) { + goto bail; + } + } + +bail: + TSK_OBJECT_SAFE_FREE(info); + return ret; +} + +// Send ACK +int send_ACK(tsip_dialog_invite_t *self, const tsip_response_t* r2xxINVITE) +{ + int ret = -1; + tsip_request_t *request = tsk_null; + + if(!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + goto bail; + } + + if((request = tsip_dialog_request_new(TSIP_DIALOG(self), "ACK"))) { + + /* The initial INVITE sent by us was a bodiless request and we don't support 100rel (We are Alice) + 1. Alice sends initial INVITE without offer + 2. Bob's offer is sent in the 2xx INVITE response + 3. Alice's answer is sent in the ACK request + */ + if(self->is_client && (self->last_oInvite && !TSIP_MESSAGE_HAS_CONTENT(self->last_oInvite))) { + const tsdp_message_t* sdp_lo; + char* sdp; + if((sdp_lo = tmedia_session_mgr_get_lo(self->msession_mgr)) && (sdp = tsdp_message_tostring(sdp_lo))) { + tsip_message_add_content(request, "application/sdp", sdp, tsk_strlen(sdp)); + TSK_FREE(sdp); + } + + // Start media session if not done + if(!self->msession_mgr->started && (self->msession_mgr->sdp.lo && self->msession_mgr->sdp.ro)) { + /* Set MSRP Callback */ + if((self->msession_mgr->type & tmedia_msrp) == tmedia_msrp) { + tmedia_session_mgr_set_msrp_cb(self->msession_mgr, TSIP_DIALOG_GET_SS(self)->userdata, TSIP_DIALOG_GET_SS(self)->media.msrp.callback); + } + // starts session manager + ret = tsip_dialog_invite_msession_start(self); + } + } + + /* RFC 3261 - 13.2.2.4 2xx Responses + The UAC core MUST generate an ACK request for each 2xx received from + the transaction layer. The header fields of the ACK are constructed + in the same way as for any request sent within a dialog (see Section + 12) with the exception of the CSeq and the header fields related to + authentication. The sequence number of the CSeq header field MUST be + the same as the INVITE being acknowledged, but the CSeq method MUST + be ACK. The ACK MUST contain the same credentials as the INVITE. If + the 2xx contains an offer (based on the rules above), the ACK MUST + carry an answer in its body. If the offer in the 2xx response is not + acceptable, the UAC core MUST generate a valid answer in the ACK and + then send a BYE immediately. + ==> Credentials will be added by tsip_dialog_request_new() because they are + associated to the dialog itself. + ==> It's up to us to add/update the CSeq number. + ==> ACK requests sent here will create new client transactions, which means that + they will have there own branches. This is not the case for ACK requests sent from + the transaction layer. + */ + request->CSeq->seq = r2xxINVITE->CSeq->seq; /* As the 2xx has the same CSeq than the INVITE */ + + /* RFC 3261 - 13.2.2.4 2xx Responses + Once the ACK has been constructed, the procedures of [4] are used to + determine the destination address, port and transport. However, the + request is passed to the transport layer directly for transmission, + rather than a client transaction. This is because the UAC core + handles retransmissions of the ACK, not the transaction layer. The + ACK MUST be passed to the client transport every time a + retransmission of the 2xx final response that triggered the ACK + arrives. + */ + if(TSIP_DIALOG_GET_STACK(self)->layer_transport) { + ret = tsip_transport_layer_send(TSIP_DIALOG_GET_STACK(self)->layer_transport, tsk_null, request); + } + else { + ret = -1; + TSK_DEBUG_ERROR("Not Transport layer associated to this stack"); + } + TSK_OBJECT_SAFE_FREE(request); + } + +bail: + return ret; +} + +// Send any response +int send_RESPONSE(tsip_dialog_invite_t *self, const tsip_request_t* request, short code, const char* phrase, tsk_bool_t force_sdp) +{ + tsip_response_t *response; + int ret = -1; + + if((response = tsip_dialog_response_new(TSIP_DIALOG(self), code, phrase, request))) { + if(TSIP_REQUEST_IS_INVITE(request) || TSIP_REQUEST_IS_UPDATE(request)) { + /* Session timers (for 2xx to INVITE or UPDATE) */ + if(self->required.timer) { + tsip_message_add_headers(response, + TSIP_HEADER_REQUIRE_VA_ARGS("timer"), + TSIP_HEADER_SESSION_EXPIRES_VA_ARGS(self->stimers.timer.timeout, tsk_striequals(self->stimers.refresher, "uas")), + tsk_null + ); + } + else if(self->supported.timer) { + tsip_message_add_headers(response, + TSIP_HEADER_SUPPORTED_VA_ARGS("timer"), + TSIP_HEADER_SESSION_EXPIRES_VA_ARGS(self->stimers.timer.timeout, tsk_striequals(self->stimers.refresher, "uas")), + tsk_null + ); + } + if(self->stimers.minse) { + tsip_message_add_headers(response, + TSIP_HEADER_MIN_SE_VA_ARGS(self->stimers.minse), + tsk_null + ); + } + if(code == 422) { + tsip_message_add_headers(response, + TSIP_HEADER_DUMMY_VA_ARGS("Reason", "SIP; cause=422; text=\"Session Interval Too Small\""), + tsk_null + ); + } + + /* 180 Ringing */ + /* 183 Session in Progress */ + if(code == 180 || code == 183) { + if(self->required._100rel) { + if(self->rseq == 0) { + self->rseq = TSK_ABS((rand() ^ rand()) + 1); + } + tsip_message_add_headers(response, + TSIP_HEADER_REQUIRE_VA_ARGS("100rel"), + TSIP_HEADER_RSEQ_VA_ARGS(self->rseq), + tsk_null + ); + TSK_OBJECT_SAFE_FREE(self->last_o1xxrel); + self->last_o1xxrel = tsk_object_ref(response); + + /* No-Initial reliable 1xx will use tsip_dialog_response_send() instead of this function + * ==> can reseset timeout value and make initial schedule */ + TSIP_DIALOG_TIMER_CANCEL(100rel); + self->timer100rel.timeout = tsip_timers_getA(); + TSIP_DIALOG_INVITE_TIMER_SCHEDULE(100rel); + } + } + + /* Precondition */ + if(code == 180 || code == 183) { + if(self->required.precondition) { + tsip_message_add_headers(response, + TSIP_HEADER_REQUIRE_VA_ARGS("precondition"), + tsk_null + ); + } + } + + + /* SDP content */ + if(self->msession_mgr && force_sdp) { + const tsdp_message_t* sdp_lo; + char* sdp = tsk_null; + if((sdp_lo = tmedia_session_mgr_get_lo(self->msession_mgr)) && (sdp = tsdp_message_tostring(sdp_lo))) { + ret = tsip_message_add_content(response, "application/sdp", sdp, tsk_strlen(sdp)); + if(tsip_dialog_invite_ice_is_enabled(self)) { + ret = tsip_dialog_invite_ice_process_lo(self, sdp_lo); + } + } + TSK_FREE(sdp); + } + + /* Add Allow header */ + tsip_message_add_headers(response, + TSIP_HEADER_DUMMY_VA_ARGS("Allow", TSIP_HEADER_ALLOW_DEFAULT), + tsk_null + ); + } + else if(TSIP_REQUEST_IS_REFER(request)) { + if(self->required.norefersub) { + tsip_message_add_headers(response, + TSIP_HEADER_REQUIRE_VA_ARGS("norefersub"), + tsk_null + ); + } + if(self->supported.norefersub) { + tsip_message_add_headers(response, + TSIP_HEADER_SUPPORTED_VA_ARGS("norefersub"), + tsk_null + ); + } + } + + + ret = tsip_dialog_response_send(TSIP_DIALOG(self), response); + TSK_OBJECT_SAFE_FREE(response); + } + return ret; +} + +// Send error response +int send_ERROR(tsip_dialog_invite_t* self, const tsip_request_t* request, short code, const char* phrase, const char* reason) +{ + tsip_response_t *response; + + if(!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if((response = tsip_dialog_response_new(TSIP_DIALOG(self), code, phrase, request))) { + tsip_message_add_headers(response, + TSIP_HEADER_DUMMY_VA_ARGS("Reason", reason), + tsk_null + ); + + tsip_dialog_response_send(TSIP_DIALOG(self), response); + TSK_OBJECT_SAFE_FREE(response); + } + else { + TSK_DEBUG_ERROR("Failed to create new message"); + } + return 0; +} + +// FSM callback to signal that the dialog is in the terminated state +int tsip_dialog_invite_OnTerminated(tsip_dialog_invite_t *self) +{ + TSK_DEBUG_INFO("=== INVITE Dialog terminated ==="); + + /* Cancel all transactions associated to this dialog (will also be done when the dialog is destroyed ) + worth nothing to do it here in order to cancel in-dialog request (such as INFO, REFER...) + */ + tsip_transac_layer_cancel_by_dialog(TSIP_DIALOG_GET_STACK(self)->layer_transac, TSIP_DIALOG(self)); + + /* stop session manager */ + if(self->msession_mgr && self->msession_mgr->started) { + tmedia_session_mgr_stop(self->msession_mgr); + } + // because of C# and Java garbage collectors, the ICE context could + // be destroyed (then stoppped) very late + if(self->ice.ctx_audio) { + tnet_ice_ctx_stop(self->ice.ctx_audio); + } + if(self->ice.ctx_video) { + tnet_ice_ctx_stop(self->ice.ctx_video); + } + + /* alert the user */ + TSIP_DIALOG_SIGNAL_2(self, tsip_event_code_dialog_terminated, + TSIP_DIALOG(self)->last_error.phrase ? TSIP_DIALOG(self)->last_error.phrase : "Call Terminated", + TSIP_DIALOG(self)->last_error.message); + + /* Remove from the dialog layer. */ + return tsip_dialog_remove(TSIP_DIALOG(self)); +} + +// callback function called when media session error occures asynchronously +static int tsip_dialog_invite_msession_onerror_cb(const void* usrdata, const struct tmedia_session_s* session, const char* reason, tsk_bool_t is_fatal) +{ + tsip_dialog_t *self = (tsip_dialog_t*)usrdata; + + if(self && is_fatal) { + char* str = tsk_null; + tsip_action_t* action; + tsk_sprintf(&str, "SIP; cause=488; text=\"%s\"", (reason ? reason : "Internal error")); + action = tsip_action_create(tsip_atype_hangup, + TSIP_ACTION_SET_HEADER("Reason", str), + TSIP_ACTION_SET_NULL()); + TSK_FREE(str); + + tsip_dialog_hangup(self, action); + TSK_OBJECT_SAFE_FREE(action); + } + + return 0; +} + +// callback function called when media session events (related to rfc5168) occures asynchronously +static int tsip_dialog_invite_msession_rfc5168_cb(const void* usrdata, const struct tmedia_session_s* session, const char* reason, enum tmedia_session_rfc5168_cmd_e command) +{ + tsip_dialog_invite_t *self = (tsip_dialog_invite_t*)usrdata; + + if (self) { + if (command == tmedia_session_rfc5168_cmd_picture_fast_update) { + uint64_t now = tsk_time_now(); + if ((now - self->last_out_fastupdate_time) > TSIP_INFO_FASTUPDATE_OUT_INTERVAL_MIN) { + char* content_ptr = tsk_null; + static const char* __content_type = "application/media_control+xml"; + static const void* __content_format = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " %llu\r\n" + " \r\n" + " \r\n"; + TSK_DEBUG_INFO("Media session is asking the sigaling layer to send SIP INFO('picture_fast_update')"); + tsk_sprintf(&content_ptr, __content_format, session->id); + self->last_out_fastupdate_time = now; + return send_INFO(self, __content_type, content_ptr, tsk_strlen(content_ptr)); + } + else { + /* if too close don't update "last_fir_time" to "now" to be sure interval will increase */ + TSK_DEBUG_INFO("Outgoing SIP INFO ('picture_fast_update') requested but delay too close"); + } + } + } + return 0; +} + + + + + + + + + + + + + + + + + + + +//======================================================== +// SIP dialog INVITE object definition +// +static tsk_object_t* tsip_dialog_invite_ctor(tsk_object_t * self, va_list * app) +{ + tsip_dialog_invite_t *dialog = self; + if(dialog) { + tsip_ssession_handle_t *ss = va_arg(*app, tsip_ssession_handle_t *); + const char* call_id = va_arg(*app, const char *); + + /* Initialize base class */ + tsip_dialog_init(TSIP_DIALOG(self), tsip_dialog_INVITE, call_id, ss, _fsm_state_Started, _fsm_state_Terminated); + + /* FSM */ + TSIP_DIALOG_GET_FSM(dialog)->debug = DEBUG_STATE_MACHINE; + tsk_fsm_set_callback_terminated(TSIP_DIALOG_GET_FSM(dialog), TSK_FSM_ONTERMINATED_F(tsip_dialog_invite_OnTerminated), (const void*)dialog); + + /* default values */ + dialog->supported._100rel = ((tsip_ssession_t*)ss)->media.enable_100rel; + dialog->supported.norefersub = tsk_true; + dialog->required.ice = (((tsip_ssession_t*)ss)->media.profile == tmedia_profile_rtcweb); + dialog->supported.ice = (dialog->required.ice || ((tsip_ssession_t*)ss)->media.enable_ice); +#if 0 /* This was a patch for chrome but no longer needed when using version 23.0.1271.64 or later */ + dialog->ice.is_jingle = (((tsip_ssession_t*)ss)->media.profile == tmedia_profile_rtcweb); +#else + dialog->ice.is_jingle = tsk_false; +#endif + dialog->ice.last_sdp_ro_ver = -1; + dialog->use_rtcp = (((tsip_ssession_t*)ss)->media.profile == tmedia_profile_rtcweb) ? tsk_true : ((tsip_ssession_t*)ss)->media.enable_rtcp; + dialog->use_rtcpmux = (((tsip_ssession_t*)ss)->media.profile == tmedia_profile_rtcweb) ? tsk_true : ((tsip_ssession_t*)ss)->media.enable_rtcpmux; + + dialog->ice.last_action_id = tsk_fsm_state_none; + dialog->refersub = tsk_true; + dialog->is_conditional_ringing_enabled = ((tsip_ssession_t*)ss)->media.enable_conditional_ringing; + // ... do the same for preconditions, replaces, .... + + /* Initialize the class itself */ + tsip_dialog_invite_init(self); + } + return self; +} + +static tsk_object_t* tsip_dialog_invite_dtor(tsk_object_t * _self) +{ + tsip_dialog_invite_t *self = _self; + if(self) { + // Cancel all timers + tsip_dialog_invite_stimers_cancel(self); + tsip_dialog_invite_qos_timer_cancel(self); + TSIP_DIALOG_TIMER_CANCEL(shutdown); + TSIP_DIALOG_TIMER_CANCEL(100rel); + + // DeInitialize base class + tsip_dialog_deinit(TSIP_DIALOG(self)); + + // DeInitialize self + TSK_OBJECT_SAFE_FREE(self->ss_transf); + TSK_OBJECT_SAFE_FREE(self->msession_mgr); + + TSK_OBJECT_SAFE_FREE(self->last_oInvite); + TSK_OBJECT_SAFE_FREE(self->last_iInvite); + TSK_OBJECT_SAFE_FREE(self->last_o1xxrel); + TSK_OBJECT_SAFE_FREE(self->last_iRefer); + TSK_FREE(self->stimers.refresher); + + TSK_OBJECT_SAFE_FREE(self->ice.ctx_audio); + TSK_OBJECT_SAFE_FREE(self->ice.ctx_video); + TSK_OBJECT_SAFE_FREE(self->ice.last_action); + TSK_OBJECT_SAFE_FREE(self->ice.last_message); + //... + + TSK_DEBUG_INFO("*** INVITE Dialog destroyed ***"); + } + return self; +} + +static int tsip_dialog_invite_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + return tsip_dialog_cmp(obj1, obj2); +} + +static const tsk_object_def_t tsip_dialog_invite_def_s = { + sizeof(tsip_dialog_invite_t), + tsip_dialog_invite_ctor, + tsip_dialog_invite_dtor, + tsip_dialog_invite_cmp, +}; +const tsk_object_def_t *tsip_dialog_invite_def_t = &tsip_dialog_invite_def_s; diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.server.c b/tinySIP/src/dialogs/tsip_dialog_invite.server.c index 9082ca27..45bcc520 100755 --- a/tinySIP/src/dialogs/tsip_dialog_invite.server.c +++ b/tinySIP/src/dialogs/tsip_dialog_invite.server.c @@ -1,790 +1,859 @@ -/* -* Copyright (C) 2010-2011 Mamadou Diop. -* -* Contact: Mamadou Diop -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as publishd by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with DOUBANGO. -* -*/ - -/**@file tsip_dialog_invite.client.c - * @brief SIP dialog INVITE as per RFC 3261. - * The SOA machine is designed as per RFC 3264 and draft-ietf-sipping-sip-offeranswer-12. - * MMTel services implementation follow 3GPP TS 24.173. - * - * @author Mamadou Diop - * - - */ -#include "tinysip/dialogs/tsip_dialog_invite.h" - -#include "tinysip/dialogs/tsip_dialog_invite.common.h" - -#include "tinysip/transports/tsip_transport_layer.h" - -#include "tinysip/headers/tsip_header_Dummy.h" -#include "tinysip/headers/tsip_header_Min_SE.h" -#include "tinysip/headers/tsip_header_RAck.h" -#include "tinysip/headers/tsip_header_Require.h" -#include "tinysip/headers/tsip_header_Session_Expires.h" - -#include "tsk_debug.h" - -static const char* supported_options[] = { "100rel", "precondition", "timer" }; - -/* ======================== external functions ======================== */ -extern int tsip_dialog_invite_msession_start(tsip_dialog_invite_t *self); -extern int send_RESPONSE(tsip_dialog_invite_t *self, const tsip_request_t* request, short code, const char* phrase, tsk_bool_t force_sdp); -extern int tsip_dialog_invite_process_ro(tsip_dialog_invite_t *self, const tsip_message_t* message); -extern int tsip_dialog_invite_stimers_schedule(tsip_dialog_invite_t* self, uint64_t timeout); -extern int send_ERROR(tsip_dialog_invite_t* self, const tsip_request_t* request, short code, const char* phrase, const char* reason); - -extern int tsip_dialog_invite_timer_callback(const tsip_dialog_invite_t* self, tsk_timer_id_t timer_id); -extern tsk_bool_t tsip_dialog_invite_ice_is_enabled(const tsip_dialog_invite_t * self); -extern tsk_bool_t tsip_dialog_invite_ice_is_connected(const tsip_dialog_invite_t * self); - -/* ======================== internal functions ======================== */ -static int send_UNSUPPORTED(tsip_dialog_invite_t* self, const tsip_request_t* request, const char* option); - -/* ======================== transitions ======================== */ -static int s0000_Started_2_Terminated_X_iINVITE(va_list *app); // Failure -static int s0000_Started_2_Started_X_iINVITE(va_list *app); // Session Interval Too Small -static int s0000_Started_2_InProgress_X_iINVITE(va_list *app); // 100rel supported -static int s0000_Started_2_Ringing_X_iINVITE(va_list *app); // Neither 100rel nor QoS -static int s0000_InProgress_2_InProgress_X_iPRACK(va_list *app); // PRACK for our 18x response (with QoS) -static int s0000_InProgress_2_Ringing_X_iPRACK(va_list *app); // PRACK for our 18x response (without QoS) -static int s0000_InProgress_2_InProgress_X_iUPDATE(va_list *app); // QoS cannot resume -static int s0000_InProgress_2_Ringing_X_iUPDATE(va_list *app); // QoS can resume (do not alert user, wait for PRACK) -static int s0000_Inprogress_2_Terminated_X_iCANCEL(va_list *app); -static int s0000_Ringing_2_Ringing_X_iPRACK(va_list *app); // Alert user -static int s0000_Ringing_2_Connected_X_Accept(va_list *app); -static int s0000_Ringing_2_Terminated_X_Reject(va_list *app); -static int s0000_Ringing_2_Terminated_X_iCANCEL(va_list *app); -static int s0000_Any_2_Any_X_timer100rel(va_list *app); - -/* ======================== conds ======================== */ -static tsk_bool_t _fsm_cond_bad_extension(tsip_dialog_invite_t* self, tsip_message_t* message) -{ - const tsip_header_Require_t* requireHdr; - const tsk_list_item_t* item; - tsk_size_t i, j; - - /* Check if we support all extensions */ - for(i = 0; (requireHdr = (const tsip_header_Require_t*)tsip_message_get_headerAt(message, tsip_htype_Require, i)); i++) { - tsk_bool_t bad_extension = tsk_false; - const tsk_string_t* option = tsk_null; - tsk_list_foreach(item, requireHdr->options) { - option = item->data; - bad_extension = tsk_true; - for(j = 0; option && jvalue, supported_options[j])) { - bad_extension = tsk_false; - break; - } - } - if(bad_extension) { - break; - } - } - if(bad_extension && option) { - send_UNSUPPORTED(self, message, option->value); - return tsk_true; - } - } - - - return tsk_false; -} - -static tsk_bool_t _fsm_cond_bad_content(tsip_dialog_invite_t* self, tsip_message_t* message) -{ - int ret; - const tsdp_message_t* sdp_lo; - tsk_bool_t bodiless_INVITE = (TSIP_DIALOG(self)->state == tsip_initial && !TSIP_MESSAGE_HAS_CONTENT(message)); // Initial Bodiless INVITE - - /* Check remote offer */ - if((ret = tsip_dialog_invite_process_ro(self, message))) { - ret = send_ERROR(self, message, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); - return tsk_true; - } - /* generate local offer and check it's validity */ - if(self->msession_mgr && (sdp_lo = tmedia_session_mgr_get_lo(self->msession_mgr))) { - /* check that we have at least one valid session (Only if no bodiless initial INVITE) */ - if(!bodiless_INVITE && !tmedia_session_mgr_has_active_session(self->msession_mgr)) { - ret = send_ERROR(self, message, 488, "Not Acceptable", "SIP; cause=488; text=\"No common codecs\""); - return tsk_true; - } - // media type could change if there are zombies (medias with port equal to zero) - TSIP_DIALOG_GET_SS(self)->media.type = self->msession_mgr->type; - } - else { - ret = send_ERROR(self, message, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); - return tsk_true; - } - - return tsk_false; -} - -static tsk_bool_t _fsm_cond_toosmall(tsip_dialog_invite_t* self, tsip_message_t* message) -{ - if(TSIP_DIALOG_GET_SS(self)->media.timers.timeout && (tsip_message_supported(message, "timer") || tsip_message_required(message, "timer"))) { - const tsip_header_Session_Expires_t* Session_Expires; - if((Session_Expires = (const tsip_header_Session_Expires_t*)tsip_message_get_header(message, tsip_htype_Session_Expires))) { - if(Session_Expires->delta_seconds < TSIP_SESSION_EXPIRES_MIN_VALUE) { - self->stimers.minse = TSIP_SESSION_EXPIRES_MIN_VALUE; - send_RESPONSE(self, message, 422, "Session Interval Too Small", tsk_false); - return tsk_true; - } - else { - const tsip_header_Min_SE_t* Min_SE; - self->stimers.timer.timeout = Session_Expires->delta_seconds; - tsk_strupdate(&self->stimers.refresher, Session_Expires->refresher_uas ? "uas" : "uac"); - self->stimers.is_refresher = tsk_striequals(self->stimers.refresher, "uas"); - if((Min_SE = (const tsip_header_Min_SE_t*)tsip_message_get_header(message, tsip_htype_Min_SE))) { - self->stimers.minse = Min_SE->delta_seconds; - } - } - } - } - return tsk_false; -} - -// 100rel && (QoS or ICE) -static tsk_bool_t _fsm_cond_use_early_media(tsip_dialog_invite_t* self, tsip_message_t* message) -{ - if((tsip_message_supported(message, "100rel") && self->supported._100rel) || tsip_message_required(message, "100rel")) { - if((tsip_message_supported(message, "precondition") && self->supported.precondition) || tsip_message_required(message, "precondition")) { - return tsk_true; - } - } -#if 0 - if(tsip_dialog_invite_ice_is_enabled(self)) { - return tsk_true; - } -#endif - return tsk_false; -} - - -static tsk_bool_t _fsm_cond_prack_match(tsip_dialog_invite_t* self, tsip_message_t* message) -{ - const tsip_header_RAck_t* RAck; - - if(!self->last_o1xxrel) { - return tsk_false; - } - - if((RAck = (const tsip_header_RAck_t*)tsip_message_get_header(message, tsip_htype_RAck))) { - if((RAck->seq == self->rseq) && - (tsk_striequals(RAck->method, self->last_o1xxrel->CSeq->method)) && - (RAck->cseq == self->last_o1xxrel->CSeq->seq)) { - self->rseq++; - return tsk_true; - } - else { - TSK_DEBUG_WARN("Failed to match PRACK request"); - } - } - - return tsk_false; -} -static tsk_bool_t _fsm_cond_negociates_preconditions(tsip_dialog_invite_t* self, tsip_message_t* rPRACK) -{ - //tsip_message_supported(self->last_iInvite, "precondition") || tsip_message_required(self->last_iInvite, "precondition") - if(tsip_message_required(self->last_iInvite, "precondition") || (self->msession_mgr && self->msession_mgr->qos.strength == tmedia_qos_strength_mandatory)) { - return tsk_true; - } - return tsk_false; -} -static tsk_bool_t _fsm_cond_cannotresume(tsip_dialog_invite_t* self, tsip_message_t* rUPDATE) -{ - if(!tsip_dialog_invite_process_ro(self, rUPDATE)) { - return !tmedia_session_mgr_canresume(self->msession_mgr); - } - else { - return tsk_false; - } -} - -static tsk_bool_t _fsm_cond_initial_iack_pending(tsip_dialog_invite_t* self, tsip_message_t* rACK) -{ - return self->is_initial_iack_pending; -} - - - -/* Init FSM */ -int tsip_dialog_invite_server_init(tsip_dialog_invite_t *self) -{ - return tsk_fsm_set(TSIP_DIALOG_GET_FSM(self), - - /*======================= - * === Started === - */ - // Started -> (Bad Extendion) -> Terminated - TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iINVITE, _fsm_cond_bad_extension, _fsm_state_Terminated, s0000_Started_2_Terminated_X_iINVITE, "s0000_Started_2_Terminated_X_iINVITE"), - // Started -> (Bad content) -> Terminated - TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iINVITE, _fsm_cond_bad_content, _fsm_state_Terminated, s0000_Started_2_Terminated_X_iINVITE, "s0000_Started_2_Terminated_X_iINVITE"), - // Started -> (Session Interval Too Small) -> Started - TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iINVITE, _fsm_cond_toosmall, _fsm_state_Started, s0000_Started_2_Started_X_iINVITE, "s0000_Started_2_Started_X_iINVITE"), - // Started -> (100rel && (QoS or ICE)) -> InProgress - TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iINVITE, _fsm_cond_use_early_media, _fsm_state_InProgress, s0000_Started_2_InProgress_X_iINVITE, "s0000_Started_2_InProgress_X_iINVITE"), - // Started -> (non-100rel and non-QoS, referred to as "basic") -> Ringing - TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_iINVITE, _fsm_state_Ringing, s0000_Started_2_Ringing_X_iINVITE, "s0000_Started_2_Ringing_X_iINVITE"), - - - /*======================= - * === InProgress === - */ - // InProgress ->(iPRACK with QoS) -> InProgress - TSK_FSM_ADD(_fsm_state_InProgress, _fsm_action_iPRACK, _fsm_cond_negociates_preconditions, _fsm_state_InProgress, s0000_InProgress_2_InProgress_X_iPRACK, "s0000_InProgress_2_InProgress_X_iPRACK"), - // InProgress ->(iPRACK without QoS) -> Ringing - TSK_FSM_ADD(_fsm_state_InProgress, _fsm_action_iPRACK, _fsm_cond_prack_match, _fsm_state_Ringing, s0000_InProgress_2_Ringing_X_iPRACK, "s0000_InProgress_2_Ringing_X_iPRACK"), - // InProgress ->(iUPDATE but cannot resume) -> InProgress - TSK_FSM_ADD(_fsm_state_InProgress, _fsm_action_iUPDATE, _fsm_cond_cannotresume, _fsm_state_InProgress, s0000_InProgress_2_InProgress_X_iUPDATE, "s0000_InProgress_2_InProgress_X_iUPDATE"), - // InProgress ->(iUPDATE can resume) -> Ringing - TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_iUPDATE, _fsm_state_Ringing, s0000_InProgress_2_Ringing_X_iUPDATE, "s0000_InProgress_2_Ringing_X_iUPDATE"), - // InProgress ->(iCANCEL) -> Terminated - TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_iCANCEL, _fsm_state_Terminated, s0000_Inprogress_2_Terminated_X_iCANCEL, "s0000_Inprogress_2_Terminated_X_iCANCEL"), - - - /*======================= - * === Ringing === - */ - // Ringing -> (iPRACK) -> Ringing - TSK_FSM_ADD(_fsm_state_Ringing, _fsm_action_iPRACK, _fsm_cond_prack_match, _fsm_state_Ringing, s0000_Ringing_2_Ringing_X_iPRACK, "s0000_Ringing_2_Ringing_X_iPRACK"), - // Ringing -> (oAccept) -> Connected - TSK_FSM_ADD_ALWAYS(_fsm_state_Ringing, _fsm_action_accept, _fsm_state_Connected, s0000_Ringing_2_Connected_X_Accept, "s0000_Ringing_2_Connected_X_Accept"), - // Ringing -> (oReject) -> Terminated - TSK_FSM_ADD_ALWAYS(_fsm_state_Ringing, _fsm_action_reject, _fsm_state_Terminated, s0000_Ringing_2_Terminated_X_Reject, "s0000_Ringing_2_Terminated_X_Reject"), - // Ringing ->(iCANCEL) -> Terminated - TSK_FSM_ADD_ALWAYS(_fsm_state_Ringing, _fsm_action_iCANCEL, _fsm_state_Terminated, s0000_Ringing_2_Terminated_X_iCANCEL, "s0000_Ringing_2_Terminated_X_iCANCEL"), - - /*======================= - * === FRESH CONNECTED === - */ - // Fresh Connected [ACK is pending] ->(iCANCEL) -> Terminated - TSK_FSM_ADD(_fsm_state_Connected, _fsm_action_iCANCEL, _fsm_cond_initial_iack_pending, _fsm_state_Terminated, s0000_Ringing_2_Terminated_X_iCANCEL, "s0000_FreshConnected_2_Terminated_X_iCANCEL"), - - /*======================= - * === ANY === - */ - // Any ->(timer100rel) -> Any - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_timer100rel, tsk_fsm_state_any, s0000_Any_2_Any_X_timer100rel, "s0000_Any_2_Any_X_timer100rel"), - - - TSK_FSM_ADD_NULL()); -} - -//-------------------------------------------------------- -// == STATE MACHINE BEGIN == -//-------------------------------------------------------- - - -/* Started -> (Failure) -> Terminated */ -int s0000_Started_2_Terminated_X_iINVITE(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - /* tsip_request_t *request = va_arg(*app, tsip_request_t *); */ - - /* We are not the client */ - self->is_client = tsk_false; - - return 0; -} - -/* Started -> (Too Small) -> Started */ -int s0000_Started_2_Started_X_iINVITE(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - - /* We are not the client */ - self->is_client = tsk_false; - - return 0; -} - -/* Started -> (non-100rel and non-QoS, referred to as "basic") -> Ringing */ -int s0000_Started_2_Ringing_X_iINVITE(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - tsip_request_t *request = va_arg(*app, tsip_request_t *); - const tsip_header_Session_Expires_t* hdr_SessionExpires; - - /* we are not the client */ - self->is_client = tsk_false; - - /* update last INVITE */ - TSK_OBJECT_SAFE_FREE(self->last_iInvite); - self->last_iInvite = tsk_object_ref(request); - - // add "require:100rel" tag if the incoming INVITE contains "100rel" tag in "supported" header - if(self->last_iInvite && (tsip_message_supported(self->last_iInvite, "100rel") || tsip_message_required(self->last_iInvite, "100rel")) && self->supported._100rel) { - self->required._100rel = tsk_true; - } - - // add "require:timer" tag if incoming INVITE contains "timer" tag in "supported" header and session timers is enabled - if(TSIP_DIALOG_GET_SS(self)->media.timers.timeout) { - if((hdr_SessionExpires = (const tsip_header_Session_Expires_t*)tsip_message_get_header(request, tsip_htype_Session_Expires))) { - // "hdr_SessionExpires->delta_seconds" smallnest already checked - self->stimers.timer.timeout = hdr_SessionExpires->delta_seconds; - tsk_strupdate(&self->stimers.refresher, hdr_SessionExpires->refresher_uas ? "uas" : "uac"); - self->stimers.is_refresher = tsk_striequals(self->stimers.refresher, "uas"); - self->required.timer = tsk_true; - } - } - - /* update state */ - tsip_dialog_update_2(TSIP_DIALOG(self), request); - - /* send Ringing */ - /*if(TSIP_DIALOG_GET_STACK(self)->network.mode != tsip_stack_mode_webrtc2sip)*/{ - send_RESPONSE(self, request, 180, "Ringing", tsk_false); - } - - /* alert the user (session) */ - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_newcall, - tsip_event_code_dialog_request_incoming, "Incoming Call", request); - - return 0; -} - -/* Started -> (QoS (preconditions)) -> InProgress */ -int s0000_Started_2_InProgress_X_iINVITE(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - tsip_request_t *request = va_arg(*app, tsip_request_t *); - - /* We are not the client */ - self->is_client = tsk_false; - - /* update last INVITE */ - TSK_OBJECT_SAFE_FREE(self->last_iInvite); - self->last_iInvite = tsk_object_ref(request); - - /* Update state */ - tsip_dialog_update_2(TSIP_DIALOG(self), request); - - /* Send In Progress - RFC 3262 - 3 UAS Behavior - - The provisional response to be sent reliably is constructed by the - UAS core according to the procedures of Section 8.2.6 of RFC 3261. - In addition, it MUST contain a Require header field containing the - option tag 100rel, and MUST include an RSeq header field. The value - of the header field for the first reliable provisional response in a - transaction MUST be between 1 and 2**31 - 1. - */ - self->rseq = (rand() ^ rand()) % (0x00000001 << 31); - self->required._100rel = tsk_true; - self->required.precondition = (tsip_message_supported(self->last_iInvite, "precondition") || tsip_message_required(self->last_iInvite, "precondition")); - send_RESPONSE(self, request, 183, "Session in Progress", tsk_true); - - return 0; -} - -/* InProgress ->(iPRACK with QoS) -> InProgress */ -int s0000_InProgress_2_InProgress_X_iPRACK(va_list *app) -{ - int ret; - - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - tsip_request_t *request = va_arg(*app, tsip_request_t *); - - /* Cancel 100rel timer */ - TSIP_DIALOG_TIMER_CANCEL(100rel); - - /* In all cases: Send 2xx PRACK */ - if(!(ret = send_RESPONSE(self, request, 200, "OK", tsk_false))) { - ++self->rseq; - } - - /* - 1. Alice sends an initial INVITE without offer - 2. Bob's answer is sent in the first reliable provisional response, in this case it's a 1xx INVITE response - 3. Alice's answer is sent in the PRACK response - */ - if(!self->msession_mgr->sdp.ro) { - if(TSIP_MESSAGE_HAS_CONTENT(request)) { - if((ret = tsip_dialog_invite_process_ro(self, request))) { - /* Send Error and break the FSM */ - ret = send_ERROR(self, self->last_iInvite, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); - return -4; - } - } - else { - /* 488 INVITE */ - ret = send_ERROR(self, self->last_iInvite, 488, "Not Acceptable", "SIP; cause=488; text=\"Offer expected in the PRACK\""); - return -3; - } - } - - return ret; -} - -/* InProgress ->(iPRACK without QoS) -> Ringing */ -int s0000_InProgress_2_Ringing_X_iPRACK(va_list *app) -{ - int ret; - - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - tsip_request_t *request = va_arg(*app, tsip_request_t *); - - /* Cancel 100rel timer */ - TSIP_DIALOG_TIMER_CANCEL(100rel); - - /* In all cases: Send 2xx PRACK */ - if(!(ret = send_RESPONSE(self, request, 200, "OK", tsk_false))) { - ++self->rseq; - } - - /* - 1. Alice sends an initial INVITE without offer - 2. Bob's answer is sent in the first reliable provisional response, in this case it's a 1xx INVITE response - 3. Alice's answer is sent in the PRACK response - */ - if(self->msession_mgr && !self->msession_mgr->sdp.ro) { - if(TSIP_MESSAGE_HAS_CONTENT(request)) { - if((ret = tsip_dialog_invite_process_ro(self, request))) { - /* Send Error and break the FSM */ - ret = send_ERROR(self, self->last_iInvite, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); - return -4; - } - } - else { - /* 488 INVITE */ - ret = send_ERROR(self, self->last_iInvite, 488, "Not Acceptable", "SIP; cause=488; text=\"Offer expected in the PRACK\""); - return -3; - } - } - - /* Send Ringing */ - /*if(TSIP_DIALOG_GET_STACK(self)->network.mode != tsip_stack_mode_webrtc2sip)*/{ - ret = send_RESPONSE(self, self->last_iInvite, 180, "Ringing", tsk_false); - } - - /* Alert the user (session) */ - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_newcall, - tsip_event_code_dialog_request_incoming, "Incoming Call", request); - - return ret; -} - -/* InProgress ->(iUPDATE but cannot resume) -> InProgress */ -int s0000_InProgress_2_InProgress_X_iUPDATE(va_list *app) -{ - int ret; - - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - tsip_request_t *request = va_arg(*app, tsip_request_t *); - - if((ret = tsip_dialog_invite_process_ro(self, request))) { - /* Send Error and break the FSM */ - ret = send_ERROR(self, request, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); - return -4; - } - else { - // force SDP in 200 OK even if the request has the same SDP version - tsk_bool_t force_sdp = TSIP_MESSAGE_HAS_CONTENT(request); - ret = send_RESPONSE(self, request, 200, "OK", - (self->msession_mgr && (force_sdp || self->msession_mgr->ro_changed || self->msession_mgr->state_changed))); - } - - return ret; -} - -/* InProgress ->(iUPDATE can resume) -> Ringing */ -int s0000_InProgress_2_Ringing_X_iUPDATE(va_list *app) -{ - int ret; - - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - tsip_request_t *request = va_arg(*app, tsip_request_t *); - tsk_bool_t force_sdp; - - if((ret = tsip_dialog_invite_process_ro(self, request))) { - /* Send Error and break the FSM */ - ret = send_ERROR(self, request, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); - return -4; - } - - /* Send 200 UPDATE */ - // force SDP in 200 OK even if the request has the same SDP version - force_sdp = TSIP_MESSAGE_HAS_CONTENT(request); - ret = send_RESPONSE(self, request, 200, "OK", - (self->msession_mgr && (force_sdp || self->msession_mgr->ro_changed || self->msession_mgr->state_changed))); - - /* Send Ringing */ - /*if(TSIP_DIALOG_GET_STACK(self)->network.mode != tsip_stack_mode_webrtc2sip)*/{ - ret = send_RESPONSE(self, self->last_iInvite, 180, "Ringing", tsk_false); - } - - /* alert the user */ - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_newcall, - tsip_event_code_dialog_request_incoming, "Incoming Call", request); - - return ret; -} - -/* InProgress ->(iCANCEL) -> Terminated */ -int s0000_Inprogress_2_Terminated_X_iCANCEL(va_list *app) -{ - tsip_response_t* response; - int ret = -1; - - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - tsip_request_t *request = va_arg(*app, tsip_request_t *); - - /* Send 2xx for the CANCEL (Direct to Transport layer beacause CANCEL is a special case) */ - if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 200, "OK", request))) { - ret = tsip_transport_layer_send(TSIP_DIALOG_GET_STACK(self)->layer_transport, tsk_null, response); - TSK_OBJECT_SAFE_FREE(response); - } - - /* Send Request Cancelled */ - ret = send_ERROR(self, self->last_iInvite, 487, "Request Cancelled", "SIP; cause=487; text=\"Request Cancelled\""); - - /* set last error (or info) */ - tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Call Cancelled", tsip_event_code_dialog_terminated); - - /* alert the user */ - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, - tsip_event_code_dialog_request_incoming, "Incoming Request.", request); - - return ret; -} - -/* Ringing -> (iPRACK) -> Ringing */ -int s0000_Ringing_2_Ringing_X_iPRACK(va_list *app) -{ - int ret; - - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - tsip_request_t *request = va_arg(*app, tsip_request_t *); - - if(!self->last_iInvite) { - /* silently ignore */ - return 0; - } - - /* Cancel 100rel timer */ - TSIP_DIALOG_TIMER_CANCEL(100rel); - - /* Send 2xx PRACK */ - ret = send_RESPONSE(self, request, 200, "OK", tsk_false); - - /* alert the user */ - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, - tsip_event_code_dialog_request_incoming, "Incoming Request.", request); - - return ret; -} - -/* Ringing -> (oAccept) -> Connected */ -int s0000_Ringing_2_Connected_X_Accept(va_list *app) -{ - int ret; - - tsip_dialog_invite_t *self; - const tsip_action_t* action; - tsk_bool_t mediaType_changed; - - self = va_arg(*app, tsip_dialog_invite_t *); - va_arg(*app, const tsip_message_t *); - action = va_arg(*app, const tsip_action_t *); - - /* Determine whether the remote party support UPDATE */ - self->support_update = tsip_message_allowed(self->last_iInvite, "UPDATE"); - - /* Get Media type from the action */ - mediaType_changed = (TSIP_DIALOG_GET_SS(self)->media.type != action->media.type && action->media.type != tmedia_none); - if(self->msession_mgr && mediaType_changed) { - ret = tmedia_session_mgr_set_media_type(self->msession_mgr, action->media.type); - } - - /* Appy media params received from the user */ - if(!TSK_LIST_IS_EMPTY(action->media.params)) { - ret = tmedia_session_mgr_set_3(self->msession_mgr, action->media.params); - } - - /* set MSRP callback */ - if((self->msession_mgr->type & tmedia_msrp) == tmedia_msrp) { - ret = tmedia_session_mgr_set_msrp_cb(self->msession_mgr, TSIP_DIALOG_GET_SS(self)->userdata, TSIP_DIALOG_GET_SS(self)->media.msrp.callback); - } - - /* Cancel 100rel timer */ - TSIP_DIALOG_TIMER_CANCEL(100rel); - - /* send 2xx OK */ - ret = send_RESPONSE(self, self->last_iInvite, 200, "OK", tsk_true); - - /* say we're waiting for the incoming ACK */ - self->is_initial_iack_pending = tsk_true; - - /* do not start the session until we get the ACK message - * http://code.google.com/p/doubango/issues/detail?id=157 - */ - /* do not start the session until we get at least one remote SDP - * https://code.google.com/p/doubango/issues/detail?id=438 - */ - // FIXME: (chrome) <-RTCWeb Breaker-> (chrome) do not work if media session is not started on i200 - // http://code.google.com/p/webrtc2sip/issues/detail?id=45 - if(/*TSIP_DIALOG_GET_STACK(self)->network.mode == tsip_stack_mode_webrtc2sip*/ TSIP_MESSAGE_HAS_CONTENT(self->last_iInvite)) { - ret = tsip_dialog_invite_msession_start(self); - } - - /* Session Timers */ - if(self->stimers.timer.timeout) { - if(self->stimers.is_refresher) { - /* RFC 4028 - 9. UAS Behavior - It is RECOMMENDED that this refresh be sent oncehalf the session interval has elapsed. - Additional procedures for this refresh are described in Section 10. - */ - tsip_dialog_invite_stimers_schedule(self, (self->stimers.timer.timeout*1000)/2); - } - else { - tsip_dialog_invite_stimers_schedule(self, (self->stimers.timer.timeout*1000)); - } - } - - /* alert the user (dialog) */ - TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connected, "Dialog connected"); - - return ret; -} - -/* Ringing -> (oReject) -> Terminated */ -int s0000_Ringing_2_Terminated_X_Reject(va_list *app) -{ - int ret; - short code; - const char* phrase; - char* reason = tsk_null; - - tsip_dialog_invite_t *self; - const tsip_action_t* action; - - self = va_arg(*app, tsip_dialog_invite_t *); - va_arg(*app, const tsip_message_t *); - action = va_arg(*app, const tsip_action_t *); - - /* Cancel 100rel timer */ - TSIP_DIALOG_TIMER_CANCEL(100rel); - - /* Send Reject */ - code = action->line_resp.code>=300 ? action->line_resp.code : 603; - phrase = action->line_resp.phrase ? action->line_resp.phrase : "Decline"; - tsk_sprintf(&reason, "SIP; cause=%hi; text=\"%s\"", code, phrase); - ret = send_ERROR(self, self->last_iInvite, code, phrase, reason); - TSK_FREE(reason); - - /* set last error (or info) */ - tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Call Terminated", tsip_event_code_dialog_terminated); - - return ret; -} - -/* Ringing ->(iCANCEL) -> Terminated */ -int s0000_Ringing_2_Terminated_X_iCANCEL(va_list *app) -{ - int ret; - tsip_response_t* response; - - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - tsip_request_t *request = va_arg(*app, tsip_request_t *); - - if(!self->last_iInvite) { - /* silently ignore */ - return 0; - } - - /* Send 2xx for the CANCEL (Direct to Transport layer beacause CANCEL is a special case) */ - if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 200, "OK", request))) { - ret = tsip_transport_layer_send(TSIP_DIALOG_GET_STACK(self)->layer_transport, tsk_null, response); - TSK_OBJECT_SAFE_FREE(response); - } - - /* Send Request Cancelled */ - ret = send_ERROR(self, self->last_iInvite, 487, "Request Cancelled", "SIP; cause=487; text=\"Request Cancelled\""); - - /* set last error (or info) */ - tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Call Cancelled", tsip_event_code_dialog_terminated); - - /* alert the user */ - TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, - tsip_event_code_dialog_request_incoming, "Incoming Request.", request); - - return ret; -} - -/* Any ->(timer 100rel) -> Any */ -int s0000_Any_2_Any_X_timer100rel(va_list *app) -{ - tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); - - int ret; - - if(!self->last_o1xxrel) { - /* silently ignore */ - return 0; - } - - /* resync timer */ - if((self->timer100rel.timeout *= 2) >= (64 * tsip_timers_getA())) { - TSK_DEBUG_ERROR("Sending reliable 1xx failed"); - return -2; - } - - /* resend reliable 1xx */ - if((ret = tsip_dialog_response_send(TSIP_DIALOG(self), self->last_o1xxrel))) { - return ret; - } - else { - /* schedule timer */ - TSIP_DIALOG_INVITE_TIMER_SCHEDULE(100rel); - } - - return ret; -} - -//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// == STATE MACHINE END == -//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -int send_UNSUPPORTED(tsip_dialog_invite_t* self, const tsip_request_t* request, const char* option) -{ - tsip_response_t *response; - - if(!self || !option) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 420, "Bad Extension", request))) { - // Add UnSupported header - tsip_message_add_headers(response, - TSIP_HEADER_DUMMY_VA_ARGS("Unsupported", option), - TSIP_HEADER_DUMMY_VA_ARGS("Reason", "SIP; cause=420; text=\"Bad Extension\""), - tsk_null - ); - - tsip_dialog_response_send(TSIP_DIALOG(self), response); - TSK_OBJECT_SAFE_FREE(response); - } - return 0; -} - - +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as publishd by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tsip_dialog_invite.client.c + * @brief SIP dialog INVITE as per RFC 3261. + * The SOA machine is designed as per RFC 3264 and draft-ietf-sipping-sip-offeranswer-12. + * MMTel services implementation follow 3GPP TS 24.173. + * + * @author Mamadou Diop + * + + */ +#include "tinysip/dialogs/tsip_dialog_invite.h" + +#include "tinysip/dialogs/tsip_dialog_invite.common.h" + +#include "tinysip/transports/tsip_transport_layer.h" + +#include "tinysip/headers/tsip_header_Dummy.h" +#include "tinysip/headers/tsip_header_Min_SE.h" +#include "tinysip/headers/tsip_header_RAck.h" +#include "tinysip/headers/tsip_header_Require.h" +#include "tinysip/headers/tsip_header_Session_Expires.h" + +#include "tsk_debug.h" + +static const char* supported_options[] = { "100rel", "precondition", "timer" }; + +/* ======================== external functions ======================== */ +extern int tsip_dialog_invite_msession_start(tsip_dialog_invite_t *self); +extern int send_RESPONSE(tsip_dialog_invite_t *self, const tsip_request_t* request, short code, const char* phrase, tsk_bool_t force_sdp); +extern int tsip_dialog_invite_process_ro(tsip_dialog_invite_t *self, const tsip_message_t* message); +extern int tsip_dialog_invite_stimers_schedule(tsip_dialog_invite_t* self, uint64_t timeout); +extern int send_ERROR(tsip_dialog_invite_t* self, const tsip_request_t* request, short code, const char* phrase, const char* reason); + +extern int tsip_dialog_invite_timer_callback(const tsip_dialog_invite_t* self, tsk_timer_id_t timer_id); +extern tsk_bool_t tsip_dialog_invite_ice_is_enabled(const tsip_dialog_invite_t * self); +extern tsk_bool_t tsip_dialog_invite_ice_is_connected(const tsip_dialog_invite_t * self); + +/* ======================== internal functions ======================== */ +static int send_UNSUPPORTED(tsip_dialog_invite_t* self, const tsip_request_t* request, const char* option); + +/* ======================== transitions ======================== */ +static int s0000_Started_2_Terminated_X_iINVITE(va_list *app); // Failure +static int s0000_Started_2_Started_X_iINVITE(va_list *app); // Session Interval Too Small +static int s0000_Started_2_PreChecking_X_iINVITE(va_list *app); // Conditional ringing (or any pre-chechecking) enabled +static int s0000_Started_2_InProgress_X_iINVITE(va_list *app); // 100rel supported +static int s0000_Started_2_Ringing_X_iINVITE(va_list *app); // Neither 100rel nor QoS +static int s0000_PreChecking_2_Terminated_X_Reject(va_list *app); // User rejected the incoming call after pre-checking +static int s0000_PreChecking_2_Terminated_X_iCANCEL(va_list *app); +static int s0000_PreChecking_2_InProgress_X_Accept(va_list *app); // User accepted the incoming call after pre-checking - 100rel supported +static int s0000_PreChecking_2_Ringing_X_Accept(va_list *app); // User accepted the incoming call after pre-checking - neither 100rel nor QoS +static int s0000_InProgress_2_InProgress_X_iPRACK(va_list *app); // PRACK for our 18x response (with QoS) +static int s0000_InProgress_2_Ringing_X_iPRACK(va_list *app); // PRACK for our 18x response (without QoS) +static int s0000_InProgress_2_InProgress_X_iUPDATE(va_list *app); // QoS cannot resume +static int s0000_InProgress_2_Ringing_X_iUPDATE(va_list *app); // QoS can resume (do not alert user, wait for PRACK) +static int s0000_Inprogress_2_Terminated_X_iCANCEL(va_list *app); +static int s0000_Ringing_2_Ringing_X_iPRACK(va_list *app); // Alert user +static int s0000_Ringing_2_Connected_X_Accept(va_list *app); +static int s0000_Ringing_2_Terminated_X_Reject(va_list *app); +static int s0000_Ringing_2_Terminated_X_iCANCEL(va_list *app); +static int s0000_Any_2_Any_X_timer100rel(va_list *app); + +/* ======================== conds ======================== */ +static tsk_bool_t _fsm_cond_bad_extension(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + const tsip_header_Require_t* requireHdr; + const tsk_list_item_t* item; + tsk_size_t i, j; + + /* Check if we support all extensions */ + for(i = 0; (requireHdr = (const tsip_header_Require_t*)tsip_message_get_headerAt(message, tsip_htype_Require, i)); i++) { + tsk_bool_t bad_extension = tsk_false; + const tsk_string_t* option = tsk_null; + tsk_list_foreach(item, requireHdr->options) { + option = item->data; + bad_extension = tsk_true; + for(j = 0; option && jvalue, supported_options[j])) { + bad_extension = tsk_false; + break; + } + } + if(bad_extension) { + break; + } + } + if(bad_extension && option) { + send_UNSUPPORTED(self, message, option->value); + return tsk_true; + } + } + + + return tsk_false; +} + +static tsk_bool_t _fsm_cond_bad_content(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + int ret; + const tsdp_message_t* sdp_lo; + tsk_bool_t bodiless_INVITE = (TSIP_DIALOG(self)->state == tsip_initial && !TSIP_MESSAGE_HAS_CONTENT(message)); // Initial Bodiless INVITE + + /* Check remote offer */ + if((ret = tsip_dialog_invite_process_ro(self, message))) { + ret = send_ERROR(self, message, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); + return tsk_true; + } + /* generate local offer and check it's validity */ + if(self->msession_mgr && (sdp_lo = tmedia_session_mgr_get_lo(self->msession_mgr))) { + /* check that we have at least one valid session (Only if no bodiless initial INVITE) */ + if(!bodiless_INVITE && !tmedia_session_mgr_has_active_session(self->msession_mgr)) { + ret = send_ERROR(self, message, 488, "Not Acceptable", "SIP; cause=488; text=\"No common codecs\""); + return tsk_true; + } + // media type could change if there are zombies (medias with port equal to zero) + TSIP_DIALOG_GET_SS(self)->media.type = self->msession_mgr->type; + } + else { + ret = send_ERROR(self, message, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); + return tsk_true; + } + + return tsk_false; +} + +static tsk_bool_t _fsm_cond_toosmall(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + if(TSIP_DIALOG_GET_SS(self)->media.timers.timeout && (tsip_message_supported(message, "timer") || tsip_message_required(message, "timer"))) { + const tsip_header_Session_Expires_t* Session_Expires; + if((Session_Expires = (const tsip_header_Session_Expires_t*)tsip_message_get_header(message, tsip_htype_Session_Expires))) { + if(Session_Expires->delta_seconds < TSIP_SESSION_EXPIRES_MIN_VALUE) { + self->stimers.minse = TSIP_SESSION_EXPIRES_MIN_VALUE; + send_RESPONSE(self, message, 422, "Session Interval Too Small", tsk_false); + return tsk_true; + } + else { + const tsip_header_Min_SE_t* Min_SE; + self->stimers.timer.timeout = Session_Expires->delta_seconds; + tsk_strupdate(&self->stimers.refresher, Session_Expires->refresher_uas ? "uas" : "uac"); + self->stimers.is_refresher = tsk_striequals(self->stimers.refresher, "uas"); + if((Min_SE = (const tsip_header_Min_SE_t*)tsip_message_get_header(message, tsip_htype_Min_SE))) { + self->stimers.minse = Min_SE->delta_seconds; + } + } + } + } + return tsk_false; +} + +// 100rel && (QoS or ICE) +static tsk_bool_t _fsm_cond_use_early_media(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + if((tsip_message_supported(message, "100rel") && self->supported._100rel) || tsip_message_required(message, "100rel")) { + if((tsip_message_supported(message, "precondition") && self->supported.precondition) || tsip_message_required(message, "precondition")) { + return tsk_true; + } + } +#if 0 + if(tsip_dialog_invite_ice_is_enabled(self)) { + return tsk_true; + } +#endif + return tsk_false; +} + + +static tsk_bool_t _fsm_cond_prack_match(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + const tsip_header_RAck_t* RAck; + + if(!self->last_o1xxrel) { + return tsk_false; + } + + if((RAck = (const tsip_header_RAck_t*)tsip_message_get_header(message, tsip_htype_RAck))) { + if((RAck->seq == self->rseq) && + (tsk_striequals(RAck->method, self->last_o1xxrel->CSeq->method)) && + (RAck->cseq == self->last_o1xxrel->CSeq->seq)) { + self->rseq++; + return tsk_true; + } + else { + TSK_DEBUG_WARN("Failed to match PRACK request"); + } + } + + return tsk_false; +} +static tsk_bool_t _fsm_cond_negociates_preconditions(tsip_dialog_invite_t* self, tsip_message_t* rPRACK) +{ + //tsip_message_supported(self->last_iInvite, "precondition") || tsip_message_required(self->last_iInvite, "precondition") + if(tsip_message_required(self->last_iInvite, "precondition") || (self->msession_mgr && self->msession_mgr->qos.strength == tmedia_qos_strength_mandatory)) { + return tsk_true; + } + return tsk_false; +} +static tsk_bool_t _fsm_cond_cannotresume(tsip_dialog_invite_t* self, tsip_message_t* rUPDATE) +{ + if(!tsip_dialog_invite_process_ro(self, rUPDATE)) { + return !tmedia_session_mgr_canresume(self->msession_mgr); + } + else { + return tsk_false; + } +} + +static tsk_bool_t _fsm_cond_initial_iack_pending(tsip_dialog_invite_t* self, tsip_message_t* message) +{ + return self->is_initial_iack_pending; +} + +static tsk_bool_t _fsm_cond_prechecking_enabled(tsip_dialog_invite_t* self, tsip_message_t* rACK) +{ + return self->is_conditional_ringing_enabled && !self->is_client; // add here other options requiring pre-checking using OR (|) +} + + +/* Init FSM */ +int tsip_dialog_invite_server_init(tsip_dialog_invite_t *self) +{ + return tsk_fsm_set(TSIP_DIALOG_GET_FSM(self), + + /*======================= + * === Started === + */ + // Started -> (Bad Extendion) -> Terminated + TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iINVITE, _fsm_cond_bad_extension, _fsm_state_Terminated, s0000_Started_2_Terminated_X_iINVITE, "s0000_Started_2_Terminated_X_iINVITE"), + // Started -> (Bad content) -> Terminated + TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iINVITE, _fsm_cond_bad_content, _fsm_state_Terminated, s0000_Started_2_Terminated_X_iINVITE, "s0000_Started_2_Terminated_X_iINVITE"), + // Started -> (Session Interval Too Small) -> Started + TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iINVITE, _fsm_cond_toosmall, _fsm_state_Started, s0000_Started_2_Started_X_iINVITE, "s0000_Started_2_Started_X_iINVITE"), + // Started ->(Pre-Checking enabled) -> PreChecking + TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iINVITE, _fsm_cond_prechecking_enabled, _fsm_state_PreChecking, s0000_Started_2_PreChecking_X_iINVITE, "s0000_Started_2_PreChecking_X_iINVITE"), + // Started -> (100rel && (QoS or ICE)) -> InProgress + TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iINVITE, _fsm_cond_use_early_media, _fsm_state_InProgress, s0000_Started_2_InProgress_X_iINVITE, "s0000_Started_2_InProgress_X_iINVITE"), + // Started -> (non-100rel and non-QoS, referred to as "basic") -> Ringing + TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_iINVITE, _fsm_state_Ringing, s0000_Started_2_Ringing_X_iINVITE, "s0000_Started_2_Ringing_X_iINVITE"), + + /*======================= + * === PreChecking === + */ + // PreChecking ->(iCANCEL) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_PreChecking, _fsm_action_iCANCEL, _fsm_state_Terminated, s0000_PreChecking_2_Terminated_X_iCANCEL, "s0000_PreChecking_2_Terminated_X_iCancel"), + // PreChecking ->(oReject) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_PreChecking, _fsm_action_reject, _fsm_state_Terminated, s0000_PreChecking_2_Terminated_X_Reject, "s0000_PreChecking_2_Terminated_X_Reject"), + // PreChecking ->(oCANCEL) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_PreChecking, _fsm_action_oCANCEL, _fsm_state_Terminated, s0000_PreChecking_2_Terminated_X_Reject, "s0000_PreChecking_2_Terminated_X_oCANCEL"), + // PreChecking -> (oAccept + 100rel && (QoS or ICE)) -> InProgress + TSK_FSM_ADD(_fsm_state_PreChecking, _fsm_action_accept, _fsm_cond_use_early_media, _fsm_state_InProgress, s0000_PreChecking_2_InProgress_X_Accept, "s0000_PreChecking_2_InProgress_X_Accept"), + // PreChecking -> (oAccept + non-100rel and non-QoS, referred to as "basic") -> Ringing + TSK_FSM_ADD_ALWAYS(_fsm_state_PreChecking, _fsm_action_accept, _fsm_state_Ringing, s0000_PreChecking_2_Ringing_X_Accept, "s0000_PreChecking_2_Ringing_X_Accept"), + + /*======================= + * === InProgress === + */ + // InProgress ->(iPRACK with QoS) -> InProgress + TSK_FSM_ADD(_fsm_state_InProgress, _fsm_action_iPRACK, _fsm_cond_negociates_preconditions, _fsm_state_InProgress, s0000_InProgress_2_InProgress_X_iPRACK, "s0000_InProgress_2_InProgress_X_iPRACK"), + // InProgress ->(iPRACK without QoS) -> Ringing + TSK_FSM_ADD(_fsm_state_InProgress, _fsm_action_iPRACK, _fsm_cond_prack_match, _fsm_state_Ringing, s0000_InProgress_2_Ringing_X_iPRACK, "s0000_InProgress_2_Ringing_X_iPRACK"), + // InProgress ->(iUPDATE but cannot resume) -> InProgress + TSK_FSM_ADD(_fsm_state_InProgress, _fsm_action_iUPDATE, _fsm_cond_cannotresume, _fsm_state_InProgress, s0000_InProgress_2_InProgress_X_iUPDATE, "s0000_InProgress_2_InProgress_X_iUPDATE"), + // InProgress ->(iUPDATE can resume) -> Ringing + TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_iUPDATE, _fsm_state_Ringing, s0000_InProgress_2_Ringing_X_iUPDATE, "s0000_InProgress_2_Ringing_X_iUPDATE"), + // InProgress ->(iCANCEL) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_iCANCEL, _fsm_state_Terminated, s0000_Inprogress_2_Terminated_X_iCANCEL, "s0000_Inprogress_2_Terminated_X_iCANCEL"), + + + /*======================= + * === Ringing === + */ + // Ringing -> (iPRACK) -> Ringing + TSK_FSM_ADD(_fsm_state_Ringing, _fsm_action_iPRACK, _fsm_cond_prack_match, _fsm_state_Ringing, s0000_Ringing_2_Ringing_X_iPRACK, "s0000_Ringing_2_Ringing_X_iPRACK"), + // Ringing -> (oAccept) -> Connected + TSK_FSM_ADD_ALWAYS(_fsm_state_Ringing, _fsm_action_accept, _fsm_state_Connected, s0000_Ringing_2_Connected_X_Accept, "s0000_Ringing_2_Connected_X_Accept"), + // Ringing -> (oReject) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Ringing, _fsm_action_reject, _fsm_state_Terminated, s0000_Ringing_2_Terminated_X_Reject, "s0000_Ringing_2_Terminated_X_Reject"), + // Ringing ->(iCANCEL) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Ringing, _fsm_action_iCANCEL, _fsm_state_Terminated, s0000_Ringing_2_Terminated_X_iCANCEL, "s0000_Ringing_2_Terminated_X_iCANCEL"), + + /*======================= + * === FRESH CONNECTED === + */ + // Fresh Connected [ACK is pending] ->(iCANCEL) -> Terminated + TSK_FSM_ADD(_fsm_state_Connected, _fsm_action_iCANCEL, _fsm_cond_initial_iack_pending, _fsm_state_Terminated, s0000_Ringing_2_Terminated_X_iCANCEL, "s0000_FreshConnected_2_Terminated_X_iCANCEL"), + + /*======================= + * === ANY === + */ + // Any ->(timer100rel) -> Any + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_timer100rel, tsk_fsm_state_any, s0000_Any_2_Any_X_timer100rel, "s0000_Any_2_Any_X_timer100rel"), + + + TSK_FSM_ADD_NULL()); +} + +//-------------------------------------------------------- +// == STATE MACHINE BEGIN == +//-------------------------------------------------------- + + +/* Started -> (Failure) -> Terminated */ +int s0000_Started_2_Terminated_X_iINVITE(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + /* tsip_request_t *request = va_arg(*app, tsip_request_t *); */ + + /* We are not the client */ + self->is_client = tsk_false; + + return 0; +} + +/* Started -> (Too Small) -> Started */ +int s0000_Started_2_Started_X_iINVITE(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + + /* We are not the client */ + self->is_client = tsk_false; + + return 0; +} + +/* Started ->(Pre-Checking enabled) -> PreChecking */ +int s0000_Started_2_PreChecking_X_iINVITE(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t *request = va_arg(*app, tsip_request_t *); + + /* update last INVITE */ + TSK_OBJECT_SAFE_FREE(self->last_iInvite); + self->last_iInvite = tsk_object_ref(request); + + /* alert the user (session) */ + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_prechecking, + tsip_event_code_dialog_request_prechecking, "Pre-checking incoming Call", request); + + return 0; +} + +/* Started -> (non-100rel and non-QoS, referred to as "basic") -> Ringing */ +int s0000_Started_2_Ringing_X_iINVITE(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t *request = self->is_conditional_ringing_enabled // When called from pre-checking state (s0000_PreChecking_2_Ringing_X_Accept) then, there is no 'request embedded' + ? tsk_object_ref(self->last_iInvite) + : va_arg(*app, tsip_request_t *); + const tsip_header_Session_Expires_t* hdr_SessionExpires; + + /* we are not the client */ + self->is_client = tsk_false; + + /* update last INVITE */ + TSK_OBJECT_SAFE_FREE(self->last_iInvite); + self->last_iInvite = tsk_object_ref(request); + + // add "require:100rel" tag if the incoming INVITE contains "100rel" tag in "supported" header + if(self->last_iInvite && (tsip_message_supported(self->last_iInvite, "100rel") || tsip_message_required(self->last_iInvite, "100rel")) && self->supported._100rel) { + self->required._100rel = tsk_true; + } + + // add "require:timer" tag if incoming INVITE contains "timer" tag in "supported" header and session timers is enabled + if(TSIP_DIALOG_GET_SS(self)->media.timers.timeout) { + if((hdr_SessionExpires = (const tsip_header_Session_Expires_t*)tsip_message_get_header(request, tsip_htype_Session_Expires))) { + // "hdr_SessionExpires->delta_seconds" smallnest already checked + self->stimers.timer.timeout = hdr_SessionExpires->delta_seconds; + tsk_strupdate(&self->stimers.refresher, hdr_SessionExpires->refresher_uas ? "uas" : "uac"); + self->stimers.is_refresher = tsk_striequals(self->stimers.refresher, "uas"); + self->required.timer = tsk_true; + } + } + + /* update state */ + tsip_dialog_update_2(TSIP_DIALOG(self), request); + + /* send Ringing */ + /*if(TSIP_DIALOG_GET_STACK(self)->network.mode != tsip_stack_mode_webrtc2sip)*/{ + send_RESPONSE(self, request, 180, "Ringing", tsk_false); + } + + /* alert the user (session) */ + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_newcall, + tsip_event_code_dialog_request_incoming, "Incoming Call", request); + + return 0; +} + +/* Started -> (QoS (preconditions)) -> InProgress */ +int s0000_Started_2_InProgress_X_iINVITE(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t *request = self->is_conditional_ringing_enabled // When called from pre-checking state (s0000_PreChecking_2_InProgress_X_Accept) then, there is no 'request embedded' + ? tsk_object_ref(self->last_iInvite) + : va_arg(*app, tsip_request_t *); + + /* We are not the client */ + self->is_client = tsk_false; + + /* update last INVITE */ + TSK_OBJECT_SAFE_FREE(self->last_iInvite); + self->last_iInvite = tsk_object_ref(request); + + /* Update state */ + tsip_dialog_update_2(TSIP_DIALOG(self), request); + + /* Send In Progress + RFC 3262 - 3 UAS Behavior + + The provisional response to be sent reliably is constructed by the + UAS core according to the procedures of Section 8.2.6 of RFC 3261. + In addition, it MUST contain a Require header field containing the + option tag 100rel, and MUST include an RSeq header field. The value + of the header field for the first reliable provisional response in a + transaction MUST be between 1 and 2**31 - 1. + */ + self->rseq = (rand() ^ rand()) % (0x00000001 << 31); + self->required._100rel = tsk_true; + self->required.precondition = (tsip_message_supported(self->last_iInvite, "precondition") || tsip_message_required(self->last_iInvite, "precondition")); + send_RESPONSE(self, request, 183, "Session in Progress", tsk_true); + + return 0; +} + +/* PreChecking ->(oReject) -> Terminated */ +int s0000_PreChecking_2_Terminated_X_iCANCEL(va_list *app) +{ + return s0000_Ringing_2_Terminated_X_iCANCEL(app); +} + +/* PreChecking ->(oReject) -> Terminated */ +int s0000_PreChecking_2_Terminated_X_Reject(va_list *app) +{ + return s0000_Ringing_2_Terminated_X_Reject(app); +} + +// PreChecking -> (oAccept + 100rel && (QoS or ICE)) -> InProgress +int s0000_PreChecking_2_InProgress_X_Accept(va_list *app) +{ + return s0000_Started_2_InProgress_X_iINVITE(app); +} + +/* PreChecking -> (oAccept + non-100rel and non-QoS, referred to as "basic") -> Ringing */ +int s0000_PreChecking_2_Ringing_X_Accept(va_list *app) +{ + return s0000_Started_2_Ringing_X_iINVITE(app); +} + +/* InProgress ->(iPRACK with QoS) -> InProgress */ +int s0000_InProgress_2_InProgress_X_iPRACK(va_list *app) +{ + int ret; + + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t *request = va_arg(*app, tsip_request_t *); + + /* Cancel 100rel timer */ + TSIP_DIALOG_TIMER_CANCEL(100rel); + + /* In all cases: Send 2xx PRACK */ + if(!(ret = send_RESPONSE(self, request, 200, "OK", tsk_false))) { + ++self->rseq; + } + + /* + 1. Alice sends an initial INVITE without offer + 2. Bob's answer is sent in the first reliable provisional response, in this case it's a 1xx INVITE response + 3. Alice's answer is sent in the PRACK response + */ + if(!self->msession_mgr->sdp.ro) { + if(TSIP_MESSAGE_HAS_CONTENT(request)) { + if((ret = tsip_dialog_invite_process_ro(self, request))) { + /* Send Error and break the FSM */ + ret = send_ERROR(self, self->last_iInvite, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); + return -4; + } + } + else { + /* 488 INVITE */ + ret = send_ERROR(self, self->last_iInvite, 488, "Not Acceptable", "SIP; cause=488; text=\"Offer expected in the PRACK\""); + return -3; + } + } + + return ret; +} + +/* InProgress ->(iPRACK without QoS) -> Ringing */ +int s0000_InProgress_2_Ringing_X_iPRACK(va_list *app) +{ + int ret; + + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t *request = va_arg(*app, tsip_request_t *); + + /* Cancel 100rel timer */ + TSIP_DIALOG_TIMER_CANCEL(100rel); + + /* In all cases: Send 2xx PRACK */ + if(!(ret = send_RESPONSE(self, request, 200, "OK", tsk_false))) { + ++self->rseq; + } + + /* + 1. Alice sends an initial INVITE without offer + 2. Bob's answer is sent in the first reliable provisional response, in this case it's a 1xx INVITE response + 3. Alice's answer is sent in the PRACK response + */ + if(self->msession_mgr && !self->msession_mgr->sdp.ro) { + if(TSIP_MESSAGE_HAS_CONTENT(request)) { + if((ret = tsip_dialog_invite_process_ro(self, request))) { + /* Send Error and break the FSM */ + ret = send_ERROR(self, self->last_iInvite, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); + return -4; + } + } + else { + /* 488 INVITE */ + ret = send_ERROR(self, self->last_iInvite, 488, "Not Acceptable", "SIP; cause=488; text=\"Offer expected in the PRACK\""); + return -3; + } + } + + /* Send Ringing */ + /*if(TSIP_DIALOG_GET_STACK(self)->network.mode != tsip_stack_mode_webrtc2sip)*/{ + ret = send_RESPONSE(self, self->last_iInvite, 180, "Ringing", tsk_false); + } + + /* Alert the user (session) */ + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_newcall, + tsip_event_code_dialog_request_incoming, "Incoming Call", request); + + return ret; +} + +/* InProgress ->(iUPDATE but cannot resume) -> InProgress */ +int s0000_InProgress_2_InProgress_X_iUPDATE(va_list *app) +{ + int ret; + + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t *request = va_arg(*app, tsip_request_t *); + + if((ret = tsip_dialog_invite_process_ro(self, request))) { + /* Send Error and break the FSM */ + ret = send_ERROR(self, request, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); + return -4; + } + else { + // force SDP in 200 OK even if the request has the same SDP version + tsk_bool_t force_sdp = TSIP_MESSAGE_HAS_CONTENT(request); + ret = send_RESPONSE(self, request, 200, "OK", + (self->msession_mgr && (force_sdp || self->msession_mgr->ro_changed || self->msession_mgr->state_changed))); + } + + return ret; +} + +/* InProgress ->(iUPDATE can resume) -> Ringing */ +int s0000_InProgress_2_Ringing_X_iUPDATE(va_list *app) +{ + int ret; + + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t *request = va_arg(*app, tsip_request_t *); + tsk_bool_t force_sdp; + + if((ret = tsip_dialog_invite_process_ro(self, request))) { + /* Send Error and break the FSM */ + ret = send_ERROR(self, request, 488, "Not Acceptable", "SIP; cause=488; text=\"Bad content\""); + return -4; + } + + /* Send 200 UPDATE */ + // force SDP in 200 OK even if the request has the same SDP version + force_sdp = TSIP_MESSAGE_HAS_CONTENT(request); + ret = send_RESPONSE(self, request, 200, "OK", + (self->msession_mgr && (force_sdp || self->msession_mgr->ro_changed || self->msession_mgr->state_changed))); + + /* Send Ringing */ + /*if(TSIP_DIALOG_GET_STACK(self)->network.mode != tsip_stack_mode_webrtc2sip)*/{ + ret = send_RESPONSE(self, self->last_iInvite, 180, "Ringing", tsk_false); + } + + /* alert the user */ + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_newcall, + tsip_event_code_dialog_request_incoming, "Incoming Call", request); + + return ret; +} + +/* InProgress ->(iCANCEL) -> Terminated */ +int s0000_Inprogress_2_Terminated_X_iCANCEL(va_list *app) +{ + tsip_response_t* response; + int ret = -1; + + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t *request = va_arg(*app, tsip_request_t *); + + /* Send 2xx for the CANCEL (Direct to Transport layer beacause CANCEL is a special case) */ + if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 200, "OK", request))) { + ret = tsip_transport_layer_send(TSIP_DIALOG_GET_STACK(self)->layer_transport, tsk_null, response); + TSK_OBJECT_SAFE_FREE(response); + } + + /* Send Request Cancelled */ + ret = send_ERROR(self, self->last_iInvite, 487, "Request Cancelled", "SIP; cause=487; text=\"Request Cancelled\""); + + /* set last error (or info) */ + tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Call Cancelled", tsip_event_code_dialog_terminated); + + /* alert the user */ + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, + tsip_event_code_dialog_request_incoming, "Incoming Request.", request); + + return ret; +} + +/* Ringing -> (iPRACK) -> Ringing */ +int s0000_Ringing_2_Ringing_X_iPRACK(va_list *app) +{ + int ret; + + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t *request = va_arg(*app, tsip_request_t *); + + if(!self->last_iInvite) { + /* silently ignore */ + return 0; + } + + /* Cancel 100rel timer */ + TSIP_DIALOG_TIMER_CANCEL(100rel); + + /* Send 2xx PRACK */ + ret = send_RESPONSE(self, request, 200, "OK", tsk_false); + + /* alert the user */ + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, + tsip_event_code_dialog_request_incoming, "Incoming Request.", request); + + return ret; +} + +/* Ringing -> (oAccept) -> Connected */ +int s0000_Ringing_2_Connected_X_Accept(va_list *app) +{ + int ret; + + tsip_dialog_invite_t *self; + const tsip_action_t* action; + tsk_bool_t mediaType_changed; + + self = va_arg(*app, tsip_dialog_invite_t *); + va_arg(*app, const tsip_message_t *); + action = va_arg(*app, const tsip_action_t *); + + /* Determine whether the remote party support UPDATE */ + self->support_update = tsip_message_allowed(self->last_iInvite, "UPDATE"); + + /* Get Media type from the action */ + mediaType_changed = (TSIP_DIALOG_GET_SS(self)->media.type != action->media.type && action->media.type != tmedia_none); + if(self->msession_mgr && mediaType_changed) { + ret = tmedia_session_mgr_set_media_type(self->msession_mgr, action->media.type); + } + + /* Appy media params received from the user */ + if(!TSK_LIST_IS_EMPTY(action->media.params)) { + ret = tmedia_session_mgr_set_3(self->msession_mgr, action->media.params); + } + + /* set MSRP callback */ + if((self->msession_mgr->type & tmedia_msrp) == tmedia_msrp) { + ret = tmedia_session_mgr_set_msrp_cb(self->msession_mgr, TSIP_DIALOG_GET_SS(self)->userdata, TSIP_DIALOG_GET_SS(self)->media.msrp.callback); + } + + /* Cancel 100rel timer */ + TSIP_DIALOG_TIMER_CANCEL(100rel); + + /* send 2xx OK */ + ret = send_RESPONSE(self, self->last_iInvite, 200, "OK", tsk_true); + + /* say we're waiting for the incoming ACK */ + self->is_initial_iack_pending = tsk_true; + + /* do not start the session until we get the ACK message + * http://code.google.com/p/doubango/issues/detail?id=157 + */ + /* do not start the session until we get at least one remote SDP + * https://code.google.com/p/doubango/issues/detail?id=438 + */ + // FIXME: (chrome) <-RTCWeb Breaker-> (chrome) do not work if media session is not started on i200 + // http://code.google.com/p/webrtc2sip/issues/detail?id=45 + if(/*TSIP_DIALOG_GET_STACK(self)->network.mode == tsip_stack_mode_webrtc2sip*/ TSIP_MESSAGE_HAS_CONTENT(self->last_iInvite)) { + ret = tsip_dialog_invite_msession_start(self); + } + + /* Session Timers */ + if(self->stimers.timer.timeout) { + if(self->stimers.is_refresher) { + /* RFC 4028 - 9. UAS Behavior + It is RECOMMENDED that this refresh be sent oncehalf the session interval has elapsed. + Additional procedures for this refresh are described in Section 10. + */ + tsip_dialog_invite_stimers_schedule(self, (self->stimers.timer.timeout*1000)/2); + } + else { + tsip_dialog_invite_stimers_schedule(self, (self->stimers.timer.timeout*1000)); + } + } + + /* alert the user (dialog) */ + TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connected, "Dialog connected"); + + return ret; +} + +/* Ringing -> (oReject) -> Terminated */ +int s0000_Ringing_2_Terminated_X_Reject(va_list *app) +{ + int ret; + short code; + const char* phrase; + char* reason = tsk_null; + + tsip_dialog_invite_t *self; + const tsip_action_t* action; + + self = va_arg(*app, tsip_dialog_invite_t *); + va_arg(*app, const tsip_message_t *); + action = va_arg(*app, const tsip_action_t *); + + /* Cancel 100rel timer */ + TSIP_DIALOG_TIMER_CANCEL(100rel); + + /* Send Reject */ + code = action->line_resp.code>=300 ? action->line_resp.code : 603; + phrase = action->line_resp.phrase ? action->line_resp.phrase : "Decline"; + tsk_sprintf(&reason, "SIP; cause=%hi; text=\"%s\"", code, phrase); + ret = send_ERROR(self, self->last_iInvite, code, phrase, reason); + TSK_FREE(reason); + + /* set last error (or info) */ + tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Call Terminated", tsip_event_code_dialog_terminated); + + return ret; +} + +/* Ringing ->(iCANCEL) -> Terminated */ +int s0000_Ringing_2_Terminated_X_iCANCEL(va_list *app) +{ + int ret; + tsip_response_t* response; + + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + tsip_request_t *request = va_arg(*app, tsip_request_t *); + + if(!self->last_iInvite) { + /* silently ignore */ + return 0; + } + + /* Send 2xx for the CANCEL (Direct to Transport layer beacause CANCEL is a special case) */ + if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 200, "OK", request))) { + ret = tsip_transport_layer_send(TSIP_DIALOG_GET_STACK(self)->layer_transport, tsk_null, response); + TSK_OBJECT_SAFE_FREE(response); + } + + /* Send Request Cancelled */ + ret = send_ERROR(self, self->last_iInvite, 487, "Request Cancelled", "SIP; cause=487; text=\"Request Cancelled\""); + + /* set last error (or info) */ + tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Call Cancelled", tsip_event_code_dialog_terminated); + + /* alert the user */ + TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request, + tsip_event_code_dialog_request_incoming, "Incoming Request.", request); + + return ret; +} + +/* Any ->(timer 100rel) -> Any */ +int s0000_Any_2_Any_X_timer100rel(va_list *app) +{ + tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *); + + int ret; + + if(!self->last_o1xxrel) { + /* silently ignore */ + return 0; + } + + /* resync timer */ + if((self->timer100rel.timeout *= 2) >= (64 * tsip_timers_getA())) { + TSK_DEBUG_ERROR("Sending reliable 1xx failed"); + return -2; + } + + /* resend reliable 1xx */ + if((ret = tsip_dialog_response_send(TSIP_DIALOG(self), self->last_o1xxrel))) { + return ret; + } + else { + /* schedule timer */ + TSIP_DIALOG_INVITE_TIMER_SCHEDULE(100rel); + } + + return ret; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// == STATE MACHINE END == +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +int send_UNSUPPORTED(tsip_dialog_invite_t* self, const tsip_request_t* request, const char* option) +{ + tsip_response_t *response; + + if(!self || !option) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 420, "Bad Extension", request))) { + // Add UnSupported header + tsip_message_add_headers(response, + TSIP_HEADER_DUMMY_VA_ARGS("Unsupported", option), + TSIP_HEADER_DUMMY_VA_ARGS("Reason", "SIP; cause=420; text=\"Bad Extension\""), + tsk_null + ); + + tsip_dialog_response_send(TSIP_DIALOG(self), response); + TSK_OBJECT_SAFE_FREE(response); + } + return 0; +} + + diff --git a/tinySIP/src/tsip_ssession.c b/tinySIP/src/tsip_ssession.c index 8db4194f..95da112b 100755 --- a/tinySIP/src/tsip_ssession.c +++ b/tinySIP/src/tsip_ssession.c @@ -1,809 +1,810 @@ -/* -* Copyright (C) 2010-2011 Mamadou Diop. -* -* Contact: Mamadou Diop -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with DOUBANGO. -* -*/ - -/**@file tsip_ssession.c - * @brief SIP session. - * - * @author Mamadou Diop - * - - */ -#include "tinysip/tsip_ssession.h" - -#include "tinysip/tsip_action.h" -#include "tsip.h" - -#include "tinysip/parsers/tsip_parser_uri.h" - -#include "tinysip/dialogs/tsip_dialog_layer.h" -#include "tinysip/tsip_message.h" - -#include "tinymedia/tmedia_defaults.h" - -#include "tsk_debug.h" - -/**@defgroup tsip_session_group SIP sessions -*/ - -/* internal function used to create session for server dialogs */ -tsip_ssession_handle_t* tsip_ssession_create_2(const tsip_stack_t* stack, const struct tsip_message_s* message) -{ - tsip_ssession_t* ss = tsk_null; - - if(message) { - char *from = tsk_null, *to = tsk_null; - - /* From: */ - if(message->From && message->From->uri) { /* MUST be not null */ - from = tsip_uri_tostring(message->From->uri, tsk_false, tsk_false); - } - /* To: */ - if(message->To && message->To->uri) { /* MUST be not null */ - to = tsip_uri_tostring(message->To->uri, tsk_false, tsk_false); - } - /* create the "server-side-session" */ - if((ss = tsip_ssession_create((tsip_stack_handle_t*)stack, TSIP_SSESSION_SET_NULL()))) { - tsip_ssession_set(ss, - /* default values should be in conformance with the swig wrapper */ - TSIP_SSESSION_SET_FROM_STR(from), - TSIP_SSESSION_SET_TO_STR(to), - TSIP_SSESSION_SET_NULL()); - } - - /* in all cases */ - TSK_FREE(from); - TSK_FREE(to); - } - - /* as the it's a "server-side-session", you are not the owner - * The end-user should call tsip_ssession_have_ownership() to check whether he has the ownership. - * The end-user should also call tsip_ssession_take_ownership() to take the ownership. This will avoid the session to be deleted by the stack - * when the associated dialog ends. */ - if(ss) { - ss->owner = tsk_false; - } - - return ss; -} - -int __tsip_ssession_set_To(tsip_ssession_t *self, const char* value) -{ - tsip_uri_t* uri; - if(value && (uri = tsip_uri_parse(value, tsk_strlen(value)))) { - TSK_OBJECT_SAFE_FREE(self->to); - self->to = uri; - return 0; - } - else { - TSK_DEBUG_ERROR("%s is invalid as 'To' header value", value); - return -1; - } -} - -int __tsip_ssession_set_From(tsip_ssession_t *self, const char* value) -{ - tsip_uri_t* uri; - if(value && (uri = tsip_uri_parse(value, tsk_strlen(value)))) { - TSK_OBJECT_SAFE_FREE(self->from); - self->from = uri; - return 0; - } - else { - TSK_DEBUG_ERROR("%s is invalid as 'From' header value", value); - return -1; - } -} - -int __tsip_ssession_set(tsip_ssession_t *self, va_list *app) -{ - tsip_ssession_param_type_t sscurr; - tsip_msession_param_type_t mscurr; - tmedia_session_mgr_t* mgr = tsk_null; - - int ret = 0; - - if(!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - while((sscurr = va_arg(*app, tsip_ssession_param_type_t)) != sstype_null) { - switch(sscurr) { - //======= - // Sip - //======= - case sstype_header: - case sstype_caps: { - /* (const char*)NAME_STR, (const char*)VALUE_STR */ - const char* name = va_arg(*app, const char *); - const char* value = va_arg(*app, const char *); - - if(sscurr == sstype_header) { - /* whether to SET or UNSET the header */ - if(value == ((const char*)-1)) { - tsk_params_remove_param(self->headers, name); - break; - } - - /* From */ - if(value && tsk_striequals(name, "From")) { - if((ret = __tsip_ssession_set_From(self, value))) { - goto bail; - } - } - /* To */ - else if(value && tsk_striequals(name, "To")) { - if((ret = __tsip_ssession_set_To(self, value))) { - goto bail; - } - } - /* Expires */ - else if(value && tsk_striequals(name, "Expires")) { - /* should never happen ==> ...but who know? */ - } - /* Any other */ - else { - tsk_params_add_param(&self->headers, name, value); - } - } - else if(sscurr == sstype_caps) { - if(value == ((const char*)-1)) { /* UNSET */ - tsk_params_remove_param(self->caps, name); - } - else { /* SET */ - tsk_params_add_param(&self->caps, name, value); - } - } - break; - } - case sstype_userdata: { - /* (const void*)DATA_PTR */ - self->userdata = va_arg(*app, const void *); - break; - } - case sstype_to_str: { - /* (const char*)URI_STR */ - if((ret = __tsip_ssession_set_To(self, va_arg(*app, const char *)))) { - goto bail; - } - break; - } - case sstype_from_str: { - /* (const char*)URI_STR*/ - if((ret = __tsip_ssession_set_From(self, va_arg(*app, const char *)))) { - goto bail; - } - break; - } - case sstype_to_obj: { - /* (const tsip_uri_t*)URI_OBJ */ - const tsip_uri_t* URI_OBJ = va_arg(*app, const tsip_uri_t *); - if(URI_OBJ) { - TSK_OBJECT_SAFE_FREE(self->to); - self->to = tsk_object_ref((void*)URI_OBJ); - } - break; - } - case sstype_from_obj: { - /* (const char*)URI_OBJ*/ - const tsip_uri_t* URI_OBJ = va_arg(*app, const tsip_uri_t *); - if(URI_OBJ) { - TSK_OBJECT_SAFE_FREE(self->from); - self->from = tsk_object_ref((void*)URI_OBJ); - } - break; - } - case sstype_nocontact: { - /* (tsk_bool_t)ENABLED_BOOL */ - self->no_contact = va_arg(*app, tsk_bool_t); - break; - } - case sstype_expires: { - /* (unsigned)VALUE_UINT */ - self->expires = (((int64_t)va_arg(*app, unsigned)) * 1000) /* milliseconds */; - break; - } - case sstype_silent_hangup: { - /* sstype_silent_hangup, (tsk_bool_t)ENABLED_BOOL */ - self->silent_hangup = va_arg(*app, tsk_bool_t); - break; - } - case sstype_sigcomp_id: { - /* (const char*)COMPARTMENT_ID_STR */ - const char* COMPARTMENT_ID_STR = va_arg(*app, const char*); - if(COMPARTMENT_ID_STR == (const char*)-1) { - TSK_FREE(self->sigcomp_id); - } - else { - tsk_strupdate(&self->sigcomp_id, COMPARTMENT_ID_STR); - } - break; - } - case sstype_auth_ha1: { - /* (const char*)AUTH_HA1_STR */ - const char* AUTH_HA1_STR = va_arg(*app, const char*); - tsk_strupdate(&self->auth_ha1, AUTH_HA1_STR); - break; - } - case sstype_auth_impi: { - /* (const char*)AUTH_IMPI_STR */ - const char* AUTH_IMPI_STR = va_arg(*app, const char*); - tsk_strupdate(&self->auth_impi, AUTH_IMPI_STR); - break; - } - case sstype_parent_id: { - /* ((tsip_ssession_id_t)PARENT_ID_SSID) */ - self->id_parent = va_arg(*app, tsip_ssession_id_t); - break; - } - case sstype_ws_src: { - /* (const char*)SRC_HOST_STR, (int32_t)SRC_PORT_INT, (const char*)SRC_PROTO_STR */ - const char* SRC_HOST_STR = va_arg(*app, const char*); - int32_t SRC_PORT_INT = va_arg(*app, int32_t); - const char* SRC_PROTO_STR = va_arg(*app, const char*); - tsk_strupdate(&self->ws.src.host, SRC_HOST_STR); - tsk_itoa(SRC_PORT_INT, &self->ws.src.port); - tsk_strupdate(&self->ws.src.proto, SRC_PROTO_STR); - break; - } - case sstype_media: { - //========= - // Media - //========= - if (!mgr) { - mgr = tsip_session_get_mediamgr(self); - } - while((mscurr = va_arg(*app, tsip_msession_param_type_t)) != mstype_null) { - switch(mscurr) { - case mstype_set_profile: - // (tmedia_profile_t)PROFILE_ENUM - self->media.profile = va_arg(*app, tmedia_profile_t); - break; - case mstype_set_srtp_mode: - // (tmedia_srtp_mode_t)SRTP_MODE_ENUM - self->media.srtp_mode = va_arg(*app, tmedia_srtp_mode_t); - break; - case mstype_set_avpf_mode: - // (tmedia_mode_t)MEDIA_MODE_ENUM - self->media.avpf_mode = va_arg(*app, tmedia_mode_t); - break; - case mstype_set_100rel: - self->media.enable_100rel = va_arg(*app, tsk_bool_t); - break; - case mstype_set_ice: - self->media.enable_ice = va_arg(*app, tsk_bool_t); - break; - case mstype_set_ice_stun: - self->media.enable_icestun = va_arg(*app, tsk_bool_t); - break; - case mstype_set_ice_turn: - self->media.enable_iceturn = va_arg(*app, tsk_bool_t); - break; - case mstype_set_rtcp: - self->media.enable_rtcp = va_arg(*app, tsk_bool_t); - break; - case mstype_set_rtcpmux: - self->media.enable_rtcpmux = va_arg(*app, tsk_bool_t); - break; - case mstype_set_qos: { - /* (tmedia_qos_stype_t)TYPE_ENUM, (tmedia_qos_strength_t)STRENGTH_ENUM */ - self->media.qos.type = va_arg(*app, tmedia_qos_stype_t); - self->media.qos.strength = va_arg(*app, tmedia_qos_strength_t); - break; - } - case mstype_unset_qos: { - /* */ - self->media.qos.type = tmedia_qos_stype_none; - self->media.qos.strength = tmedia_qos_strength_none; - break; - } - case mstype_set_timers: { - /* (unsigned)TIMEOUT_UINT, (const char*)REFRESHER_STR */ - /* set values */ - self->media.timers.timeout = va_arg(*app, unsigned); - tsk_strupdate(&self->media.timers.refresher, va_arg(*app, const char*)); - break; - } - case mstype_unset_timers: { - /* */ - /* unset values */ - self->media.timers.timeout = 0; - TSK_FREE(self->media.timers.refresher); - break; - } - case mstype_set_codecs: { - /* (signed)CODECS_INT */ - self->media.codecs = va_arg(*app, signed); - if(mgr) { // apply now - tmedia_session_mgr_set_codecs_supported(mgr, self->media.codecs); - } - break; - } - case mstype_set_bypass_encoding: { - /* (tsk_bool_t)ENABLED_BOOL */ - self->media.bypass_encoding = va_arg(*app, tsk_bool_t); - break; - } - case mstype_set_bypass_decoding: { - /* (tsk_bool_t)ENABLED_BOOL */ - self->media.bypass_decoding = va_arg(*app, tsk_bool_t); - break; - } - case mstype_set_rtp_ssrc: { - /* (tmedia_type_t)MEDIA_ENUM, (uint32_t)SSRC_UINT */ - tmedia_type_t MEDIA_ENUM = va_arg(*app, tmedia_type_t); - uint32_t SSRC_UINT = va_arg(*app, uint32_t); - switch(MEDIA_ENUM) { - case tmedia_audio: - self->media.rtp.ssrc.audio = SSRC_UINT; - break; - case tmedia_video: - self->media.rtp.ssrc.video = SSRC_UINT; - break; - default: - break; - } - break; - } - case mstype_set_msrp_cb: { - /* (tmedia_session_msrp_cb_f)TMEDIA_SESSION_MSRP_CB_F */ - self->media.msrp.callback = va_arg(*app, tmedia_session_msrp_cb_f); - break; - } - case mstype_set_stun_server: { - /* (const char*)HOSTNAME, (uint16_t)PORT */ - const char* HOSTNAME = va_arg(*app, const char*); - uint16_t PORT = tsk_va_arg_u16(*app); - tsk_strupdate(&self->media.stun.hostname, HOSTNAME); - self->media.stun.port = PORT; - break; - } - case mstype_set_stun_cred: { - /* (const char*)USERNAME, (const char*)PASSWORD */ - const char* USERNAME = va_arg(*app, const char*); - const char* PASSWORD = va_arg(*app, const char*); - tsk_strupdate(&self->media.stun.username, USERNAME); - tsk_strupdate(&self->media.stun.password, PASSWORD); - break; - } - case mstype_set_video_fps: { - /* (signed)FPS_INT */ - self->media.video_fps = va_arg(*app, signed); - break; - } - case mstype_set_video_bw_up: { - /* (signed)BW_INT */ - self->media.video_bw_up = va_arg(*app, signed); - break; - } - case mstype_set_video_bw_down: { - /* (signed)BW_INT */ - self->media.video_bw_down = va_arg(*app, signed); - break; - } - case mstype_set_video_prefsize: { - /* (tmedia_pref_video_size_t)PREFSIZE_ENUM */ - self->media.video_pref_size = va_arg(*app, tmedia_pref_video_size_t); - break; - } - default: { - /* va_list will be unsafe => exit */ - TSK_DEBUG_ERROR("%d NOT a valid MEDIA pname", mscurr); - goto bail; - } - } /* switch */ - } /* while */ - - break; - } /* case */ - - default: { - /* va_list will be unsafe => exit */ - TSK_DEBUG_ERROR("%d NOT a valid SIP pname", sscurr); - goto bail; - } - - } /* switch */ - } /* while */ - -bail: - TSK_OBJECT_SAFE_FREE(mgr); - - return ret; -} - - -tsip_ssession_handle_t* tsip_ssession_create(tsip_stack_handle_t *stack, ...) -{ - tsip_ssession_t* ss = tsk_null; - va_list ap; - tsip_stack_t* _stack = stack; - - if(!_stack) { - TSK_DEBUG_ERROR("Invalid Parameter."); - goto bail; - } - - if(!(ss = tsk_object_new(tsip_ssession_def_t, stack))) { - TSK_DEBUG_ERROR("Failed to create new SIP Session."); - return tsk_null; - } - - va_start(ap, stack); - if(__tsip_ssession_set(ss, &ap)) { - TSK_DEBUG_ERROR("Failed to set user's parameters."); - TSK_OBJECT_SAFE_FREE(ss); - va_end(ap); - goto bail; - } - va_end(ap); - - /* from */ - if(!ss->from && _stack->identity.impu) { - ss->from = tsip_uri_clone(_stack->identity.impu, tsk_false, tsk_false); - } - /* to */ - /* To value will be set by the dialog (whether to use as Request-URI). */ - -bail: - return ss; -} - -int tsip_ssession_set(tsip_ssession_handle_t *self, ...) -{ - if(self) { - int ret; - va_list ap; - - tsip_ssession_t *ss = self; - - if(ss->id == TSIP_SSESSION_INVALID_ID) { - return -2; - } - - va_start(ap, self); - ret = __tsip_ssession_set(ss, &ap); - va_end(ap); - return ret; - } - else { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } -} - -tsip_ssession_id_t tsip_ssession_get_id(const tsip_ssession_handle_t *self) -{ - if(self) { - const tsip_ssession_t *ss = self; - return ss->id; - } - return TSIP_SSESSION_INVALID_ID; -} - -tsip_ssession_id_t tsip_ssession_get_id_parent(const tsip_ssession_handle_t *self) -{ - if(self) { - const tsip_ssession_t *ss = self; - return ss->id_parent; - } - return TSIP_SSESSION_INVALID_ID; -} - -int tsip_ssession_take_ownership(tsip_ssession_handle_t *self) -{ - if(self) { - tsip_ssession_t *ss = self; - if(!ss->owner) { - ss->owner = tsk_true; - /* before: only the dialog had a reference to the SIP session */ - ss = tsk_object_ref(ss); - /* after: both the end-user and the dialog have their references */ - return 0; - } - return -2; - } - else { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } -} - -tsk_bool_t tsip_ssession_have_ownership(const tsip_ssession_handle_t *self) -{ - if(self) { - const tsip_ssession_t *ss = self; - return ss->owner; - } - return tsk_false; -} - -int tsip_ssession_respond(const tsip_ssession_handle_t *self, short status, const char* phrase, const void* payload, tsk_size_t size, const struct tsip_message_s* request, ...) -{ - tsip_response_t *response = tsk_null; - tsip_dialog_t* dialog = tsk_null; - const tsip_ssession_t *ss = self; - int ret = -1; - - if(!ss || !request) { - goto bail; - } - - if(!(dialog = tsip_dialog_layer_find_by_ss(ss->stack->layer_dialog, ss))) { - goto bail; - } - - if(!(response = tsip_dialog_response_new(TSIP_DIALOG(self), status, phrase, request))) { - goto bail; - } - - if(payload && size) { - if((ret = tsip_message_add_content(response, tsk_null, payload, size))) { - goto bail; - } - } - ret = tsip_dialog_response_send(TSIP_DIALOG(self), response); - -bail: - TSK_OBJECT_SAFE_FREE(response); - TSK_OBJECT_SAFE_FREE(dialog); - - return ret; -} - -const void* tsip_ssession_get_userdata(const tsip_ssession_handle_t *self) -{ - const tsip_ssession_t* ss = (const tsip_ssession_t*)self; - if(ss) { - return ss->userdata; - } - else { - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } -} - -tmedia_type_t tsip_ssession_get_mediatype(const tsip_ssession_handle_t *self) -{ - if(self) { - return ((const tsip_ssession_t*)self)->media.type; - } - else { - TSK_DEBUG_ERROR("Invalid parameter"); - return tmedia_none; - } -} - -#include "tinysip/dialogs/tsip_dialog_invite.h" -tmedia_session_mgr_t* tsip_session_get_mediamgr(const tsip_ssession_handle_t *self) -{ - tmedia_session_mgr_t* mgr = tsk_null; - - if(self) { - const tsip_ssession_t *ss = self; - tsip_dialog_t* dialog; - - if((dialog = tsip_dialog_layer_find_by_ss(ss->stack->layer_dialog, self))) { - if(dialog->type == tsip_dialog_INVITE) { - mgr = tsk_object_ref(TSIP_DIALOG_INVITE(dialog)->msession_mgr); - } - tsk_object_unref(dialog); - } - } - else { - TSK_DEBUG_ERROR("Invalid parameter"); - } - - return mgr; -} - -const tsip_stack_handle_t* tsip_ssession_get_stack(const tsip_ssession_handle_t *self) -{ - if(self) { - return ((const tsip_ssession_t*)self)->stack; - } - else { - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } -} - -tmedia_codec_id_t tsip_ssession_get_codecs_neg(tsip_ssession_handle_t *self) -{ - int32_t codecs_neg = (int32_t)tmedia_codec_id_none; - if(self) { - tmedia_session_mgr_t* mgr = tsip_session_get_mediamgr(self); - if(mgr) { - (tmedia_session_mgr_get(mgr, - TMEDIA_SESSION_GET_INT32(mgr->type, "codecs-negotiated", &codecs_neg), - TMEDIA_SESSION_GET_NULL())); - TSK_OBJECT_SAFE_FREE(mgr); - } - } - return (tmedia_codec_id_t)codecs_neg; -} - -int tsip_ssession_handle(const tsip_ssession_t *self, const struct tsip_action_s* action) -{ - int ret = -1; - - if(self && self->stack && action) { - tsip_dialog_t *dialog; - - if((dialog = tsip_dialog_layer_find_by_ss(self->stack->layer_dialog, self))) { - switch(action->type) { - case tsip_atype_hangup: { - /* hang-up is an special case (==> hangup/cancel/nothing) */ - ret = tsip_dialog_hangup(dialog, action); - break; - } - default: { - /* All other cases */ - ret = tsip_dialog_fsm_act(dialog, action->type, tsk_null, action); - break; - } - } - /* unref */ - tsk_object_unref(dialog); - } - else { - TSK_DEBUG_ERROR("Failed to find dialog with this opid [%lld]", self->id); - } - } - else { - TSK_DEBUG_ERROR("Invalid parameter"); - } - - return ret; -} - - - - - -//======================================================== -// SIP Session object definition -// -static tsk_object_t* tsip_ssession_ctor(tsk_object_t * self, va_list * app) -{ - tsip_ssession_t *ss = self; - static tsip_ssession_id_t unique_id = 0; - if(ss) { - ss->stack = va_arg(*app, const tsip_stack_t*); - ss->caps = tsk_list_create(); - ss->headers = tsk_list_create(); - - /* unique identifier */ - ss->id = ++unique_id; - // default: you are the owner - ss->owner = tsk_true; - // default expires value - ss->expires = TSIP_SSESSION_EXPIRES_DEFAULT; - // default parentid: not parent -> no pending transfer - ss->id_parent = TSIP_SSESSION_INVALID_ID; - // default SigComp compId (will be updated by session_set()) - if (ss->stack->sigcomp.handle) { - ss->sigcomp_id = tsk_strdup(tsip_sigcomp_handler_fixme_getcompid(ss->stack->sigcomp.handle)); - } - // default media values - ss->media.profile = tmedia_defaults_get_profile(); - ss->media.srtp_mode = tmedia_defaults_get_srtp_mode(); - ss->media.avpf_mode = tmedia_defaults_get_avpf_mode(); - ss->media.enable_100rel = tmedia_defaults_get_100rel_enabled(); - ss->media.enable_ice = tmedia_defaults_get_ice_enabled(); - ss->media.enable_icestun = tmedia_defaults_get_icestun_enabled(); - ss->media.enable_iceturn = tmedia_defaults_get_iceturn_enabled(); - ss->media.enable_rtcp = tmedia_defaults_get_rtcp_enabled(); - ss->media.enable_rtcpmux = tmedia_defaults_get_rtcpmux_enabled(); - ss->media.type = tmedia_none; - ss->media.qos.type = tmedia_qos_stype_none; - ss->media.qos.strength = tmedia_qos_strength_none; - ss->media.timers.refresher = tsk_strdup(tmedia_defaults_get_inv_session_refresher()); - ss->media.timers.timeout = tmedia_defaults_get_inv_session_expires(); - ss->media.codecs = tmedia_codec_id_all; - ss->media.bypass_encoding = tmedia_defaults_get_bypass_encoding(); - ss->media.bypass_decoding = tmedia_defaults_get_bypass_decoding(); - ss->media.video_fps = tmedia_defaults_get_video_fps(); - ss->media.video_bw_down = tmedia_defaults_get_bandwidth_video_download_max(); - ss->media.video_bw_up = tmedia_defaults_get_bandwidth_video_upload_max(); - ss->media.video_pref_size = tmedia_defaults_get_pref_video_size(); - { - const char *stun_hostname, *stun_username, *stun_password; - uint16_t stun_port; - if(tmedia_defaults_get_stun_server(&stun_hostname, &stun_port) == 0) { - ss->media.stun.hostname = tsk_strdup(stun_hostname); - ss->media.stun.port = stun_port; - } - if(tmedia_defaults_get_stun_cred(&stun_username, &stun_password) == 0) { - ss->media.stun.username = tsk_strdup(stun_username); - ss->media.stun.password = tsk_strdup(stun_password); - } - } - - /* add the session to the stack */ - if(ss->stack) { - tsk_list_push_back_data(ss->stack->ssessions, (void**)&ss); - } - } - - return self; -} - -static tsk_object_t* tsip_ssession_dtor(tsk_object_t * self) -{ - tsip_ssession_t *ss = self; - if(ss) { - /* remove from the stack */ - if(ss->stack) { - tsk_list_remove_item_by_data(ss->stack->ssessions, ss); - } - - //======= - // SIP - //======= - TSK_OBJECT_SAFE_FREE(ss->caps); - TSK_OBJECT_SAFE_FREE(ss->headers); - - TSK_OBJECT_SAFE_FREE(ss->from); - TSK_OBJECT_SAFE_FREE(ss->to); - - TSK_FREE(ss->sigcomp_id); - TSK_FREE(ss->auth_ha1); - TSK_FREE(ss->auth_impi); - - //======= - // Media - //======= - TSK_FREE(ss->media.timers.refresher); - TSK_FREE(ss->media.stun.username); - TSK_FREE(ss->media.stun.password); - TSK_FREE(ss->media.stun.hostname); - - //======= - // WebSocket - //======= - TSK_FREE(ss->ws.src.host); - TSK_FREE(ss->ws.src.proto); - - TSK_DEBUG_INFO("*** SIP Session destroyed ***"); - } - return self; -} - -static int tsip_ssession_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) -{ - const tsip_ssession_t *ss1 = obj1; - const tsip_ssession_t *ss2 = obj2; - - if(ss1 && ss2) { - return (int)(ss1->id-ss2->id); - } - return -1; -} - -static const tsk_object_def_t tsip_ssession_def_s = { - sizeof(tsip_ssession_t), - tsip_ssession_ctor, - tsip_ssession_dtor, - tsip_ssession_cmp, -}; -const tsk_object_def_t *tsip_ssession_def_t = &tsip_ssession_def_s; +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tsip_ssession.c + * @brief SIP session. + * + * @author Mamadou Diop + * + + */ +#include "tinysip/tsip_ssession.h" + +#include "tinysip/tsip_action.h" +#include "tsip.h" + +#include "tinysip/parsers/tsip_parser_uri.h" + +#include "tinysip/dialogs/tsip_dialog_layer.h" +#include "tinysip/tsip_message.h" + +#include "tinymedia/tmedia_defaults.h" + +#include "tsk_debug.h" + +/**@defgroup tsip_session_group SIP sessions +*/ + +/* internal function used to create session for server dialogs */ +tsip_ssession_handle_t* tsip_ssession_create_2(const tsip_stack_t* stack, const struct tsip_message_s* message) +{ + tsip_ssession_t* ss = tsk_null; + + if(message) { + char *from = tsk_null, *to = tsk_null; + + /* From: */ + if(message->From && message->From->uri) { /* MUST be not null */ + from = tsip_uri_tostring(message->From->uri, tsk_false, tsk_false); + } + /* To: */ + if(message->To && message->To->uri) { /* MUST be not null */ + to = tsip_uri_tostring(message->To->uri, tsk_false, tsk_false); + } + /* create the "server-side-session" */ + if((ss = tsip_ssession_create((tsip_stack_handle_t*)stack, TSIP_SSESSION_SET_NULL()))) { + tsip_ssession_set(ss, + /* default values should be in conformance with the swig wrapper */ + TSIP_SSESSION_SET_FROM_STR(from), + TSIP_SSESSION_SET_TO_STR(to), + TSIP_SSESSION_SET_NULL()); + } + + /* in all cases */ + TSK_FREE(from); + TSK_FREE(to); + } + + /* as the it's a "server-side-session", you are not the owner + * The end-user should call tsip_ssession_have_ownership() to check whether he has the ownership. + * The end-user should also call tsip_ssession_take_ownership() to take the ownership. This will avoid the session to be deleted by the stack + * when the associated dialog ends. */ + if(ss) { + ss->owner = tsk_false; + } + + return ss; +} + +int __tsip_ssession_set_To(tsip_ssession_t *self, const char* value) +{ + tsip_uri_t* uri; + if(value && (uri = tsip_uri_parse(value, tsk_strlen(value)))) { + TSK_OBJECT_SAFE_FREE(self->to); + self->to = uri; + return 0; + } + else { + TSK_DEBUG_ERROR("%s is invalid as 'To' header value", value); + return -1; + } +} + +int __tsip_ssession_set_From(tsip_ssession_t *self, const char* value) +{ + tsip_uri_t* uri; + if(value && (uri = tsip_uri_parse(value, tsk_strlen(value)))) { + TSK_OBJECT_SAFE_FREE(self->from); + self->from = uri; + return 0; + } + else { + TSK_DEBUG_ERROR("%s is invalid as 'From' header value", value); + return -1; + } +} + +int __tsip_ssession_set(tsip_ssession_t *self, va_list *app) +{ + tsip_ssession_param_type_t sscurr; + tsip_msession_param_type_t mscurr; + tmedia_session_mgr_t* mgr = tsk_null; + + int ret = 0; + + if(!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + while((sscurr = va_arg(*app, tsip_ssession_param_type_t)) != sstype_null) { + switch(sscurr) { + //======= + // Sip + //======= + case sstype_header: + case sstype_caps: { + /* (const char*)NAME_STR, (const char*)VALUE_STR */ + const char* name = va_arg(*app, const char *); + const char* value = va_arg(*app, const char *); + + if(sscurr == sstype_header) { + /* whether to SET or UNSET the header */ + if(value == ((const char*)-1)) { + tsk_params_remove_param(self->headers, name); + break; + } + + /* From */ + if(value && tsk_striequals(name, "From")) { + if((ret = __tsip_ssession_set_From(self, value))) { + goto bail; + } + } + /* To */ + else if(value && tsk_striequals(name, "To")) { + if((ret = __tsip_ssession_set_To(self, value))) { + goto bail; + } + } + /* Expires */ + else if(value && tsk_striequals(name, "Expires")) { + /* should never happen ==> ...but who know? */ + } + /* Any other */ + else { + tsk_params_add_param(&self->headers, name, value); + } + } + else if(sscurr == sstype_caps) { + if(value == ((const char*)-1)) { /* UNSET */ + tsk_params_remove_param(self->caps, name); + } + else { /* SET */ + tsk_params_add_param(&self->caps, name, value); + } + } + break; + } + case sstype_userdata: { + /* (const void*)DATA_PTR */ + self->userdata = va_arg(*app, const void *); + break; + } + case sstype_to_str: { + /* (const char*)URI_STR */ + if((ret = __tsip_ssession_set_To(self, va_arg(*app, const char *)))) { + goto bail; + } + break; + } + case sstype_from_str: { + /* (const char*)URI_STR*/ + if((ret = __tsip_ssession_set_From(self, va_arg(*app, const char *)))) { + goto bail; + } + break; + } + case sstype_to_obj: { + /* (const tsip_uri_t*)URI_OBJ */ + const tsip_uri_t* URI_OBJ = va_arg(*app, const tsip_uri_t *); + if(URI_OBJ) { + TSK_OBJECT_SAFE_FREE(self->to); + self->to = tsk_object_ref((void*)URI_OBJ); + } + break; + } + case sstype_from_obj: { + /* (const char*)URI_OBJ*/ + const tsip_uri_t* URI_OBJ = va_arg(*app, const tsip_uri_t *); + if(URI_OBJ) { + TSK_OBJECT_SAFE_FREE(self->from); + self->from = tsk_object_ref((void*)URI_OBJ); + } + break; + } + case sstype_nocontact: { + /* (tsk_bool_t)ENABLED_BOOL */ + self->no_contact = va_arg(*app, tsk_bool_t); + break; + } + case sstype_expires: { + /* (unsigned)VALUE_UINT */ + self->expires = (((int64_t)va_arg(*app, unsigned)) * 1000) /* milliseconds */; + break; + } + case sstype_silent_hangup: { + /* sstype_silent_hangup, (tsk_bool_t)ENABLED_BOOL */ + self->silent_hangup = va_arg(*app, tsk_bool_t); + break; + } + case sstype_sigcomp_id: { + /* (const char*)COMPARTMENT_ID_STR */ + const char* COMPARTMENT_ID_STR = va_arg(*app, const char*); + if(COMPARTMENT_ID_STR == (const char*)-1) { + TSK_FREE(self->sigcomp_id); + } + else { + tsk_strupdate(&self->sigcomp_id, COMPARTMENT_ID_STR); + } + break; + } + case sstype_auth_ha1: { + /* (const char*)AUTH_HA1_STR */ + const char* AUTH_HA1_STR = va_arg(*app, const char*); + tsk_strupdate(&self->auth_ha1, AUTH_HA1_STR); + break; + } + case sstype_auth_impi: { + /* (const char*)AUTH_IMPI_STR */ + const char* AUTH_IMPI_STR = va_arg(*app, const char*); + tsk_strupdate(&self->auth_impi, AUTH_IMPI_STR); + break; + } + case sstype_parent_id: { + /* ((tsip_ssession_id_t)PARENT_ID_SSID) */ + self->id_parent = va_arg(*app, tsip_ssession_id_t); + break; + } + case sstype_ws_src: { + /* (const char*)SRC_HOST_STR, (int32_t)SRC_PORT_INT, (const char*)SRC_PROTO_STR */ + const char* SRC_HOST_STR = va_arg(*app, const char*); + int32_t SRC_PORT_INT = va_arg(*app, int32_t); + const char* SRC_PROTO_STR = va_arg(*app, const char*); + tsk_strupdate(&self->ws.src.host, SRC_HOST_STR); + tsk_itoa(SRC_PORT_INT, &self->ws.src.port); + tsk_strupdate(&self->ws.src.proto, SRC_PROTO_STR); + break; + } + case sstype_media: { + //========= + // Media + //========= + if (!mgr) { + mgr = tsip_session_get_mediamgr(self); + } + while((mscurr = va_arg(*app, tsip_msession_param_type_t)) != mstype_null) { + switch(mscurr) { + case mstype_set_profile: + // (tmedia_profile_t)PROFILE_ENUM + self->media.profile = va_arg(*app, tmedia_profile_t); + break; + case mstype_set_srtp_mode: + // (tmedia_srtp_mode_t)SRTP_MODE_ENUM + self->media.srtp_mode = va_arg(*app, tmedia_srtp_mode_t); + break; + case mstype_set_avpf_mode: + // (tmedia_mode_t)MEDIA_MODE_ENUM + self->media.avpf_mode = va_arg(*app, tmedia_mode_t); + break; + case mstype_set_100rel: + self->media.enable_100rel = va_arg(*app, tsk_bool_t); + break; + case mstype_set_ice: + self->media.enable_ice = va_arg(*app, tsk_bool_t); + break; + case mstype_set_ice_stun: + self->media.enable_icestun = va_arg(*app, tsk_bool_t); + break; + case mstype_set_ice_turn: + self->media.enable_iceturn = va_arg(*app, tsk_bool_t); + break; + case mstype_set_rtcp: + self->media.enable_rtcp = va_arg(*app, tsk_bool_t); + break; + case mstype_set_rtcpmux: + self->media.enable_rtcpmux = va_arg(*app, tsk_bool_t); + break; + case mstype_set_qos: { + /* (tmedia_qos_stype_t)TYPE_ENUM, (tmedia_qos_strength_t)STRENGTH_ENUM */ + self->media.qos.type = va_arg(*app, tmedia_qos_stype_t); + self->media.qos.strength = va_arg(*app, tmedia_qos_strength_t); + break; + } + case mstype_unset_qos: { + /* */ + self->media.qos.type = tmedia_qos_stype_none; + self->media.qos.strength = tmedia_qos_strength_none; + break; + } + case mstype_set_timers: { + /* (unsigned)TIMEOUT_UINT, (const char*)REFRESHER_STR */ + /* set values */ + self->media.timers.timeout = va_arg(*app, unsigned); + tsk_strupdate(&self->media.timers.refresher, va_arg(*app, const char*)); + break; + } + case mstype_unset_timers: { + /* */ + /* unset values */ + self->media.timers.timeout = 0; + TSK_FREE(self->media.timers.refresher); + break; + } + case mstype_set_codecs: { + /* (signed)CODECS_INT */ + self->media.codecs = va_arg(*app, signed); + if(mgr) { // apply now + tmedia_session_mgr_set_codecs_supported(mgr, self->media.codecs); + } + break; + } + case mstype_set_bypass_encoding: { + /* (tsk_bool_t)ENABLED_BOOL */ + self->media.bypass_encoding = va_arg(*app, tsk_bool_t); + break; + } + case mstype_set_bypass_decoding: { + /* (tsk_bool_t)ENABLED_BOOL */ + self->media.bypass_decoding = va_arg(*app, tsk_bool_t); + break; + } + case mstype_set_rtp_ssrc: { + /* (tmedia_type_t)MEDIA_ENUM, (uint32_t)SSRC_UINT */ + tmedia_type_t MEDIA_ENUM = va_arg(*app, tmedia_type_t); + uint32_t SSRC_UINT = va_arg(*app, uint32_t); + switch(MEDIA_ENUM) { + case tmedia_audio: + self->media.rtp.ssrc.audio = SSRC_UINT; + break; + case tmedia_video: + self->media.rtp.ssrc.video = SSRC_UINT; + break; + default: + break; + } + break; + } + case mstype_set_msrp_cb: { + /* (tmedia_session_msrp_cb_f)TMEDIA_SESSION_MSRP_CB_F */ + self->media.msrp.callback = va_arg(*app, tmedia_session_msrp_cb_f); + break; + } + case mstype_set_stun_server: { + /* (const char*)HOSTNAME, (uint16_t)PORT */ + const char* HOSTNAME = va_arg(*app, const char*); + uint16_t PORT = tsk_va_arg_u16(*app); + tsk_strupdate(&self->media.stun.hostname, HOSTNAME); + self->media.stun.port = PORT; + break; + } + case mstype_set_stun_cred: { + /* (const char*)USERNAME, (const char*)PASSWORD */ + const char* USERNAME = va_arg(*app, const char*); + const char* PASSWORD = va_arg(*app, const char*); + tsk_strupdate(&self->media.stun.username, USERNAME); + tsk_strupdate(&self->media.stun.password, PASSWORD); + break; + } + case mstype_set_video_fps: { + /* (signed)FPS_INT */ + self->media.video_fps = va_arg(*app, signed); + break; + } + case mstype_set_video_bw_up: { + /* (signed)BW_INT */ + self->media.video_bw_up = va_arg(*app, signed); + break; + } + case mstype_set_video_bw_down: { + /* (signed)BW_INT */ + self->media.video_bw_down = va_arg(*app, signed); + break; + } + case mstype_set_video_prefsize: { + /* (tmedia_pref_video_size_t)PREFSIZE_ENUM */ + self->media.video_pref_size = va_arg(*app, tmedia_pref_video_size_t); + break; + } + default: { + /* va_list will be unsafe => exit */ + TSK_DEBUG_ERROR("%d NOT a valid MEDIA pname", mscurr); + goto bail; + } + } /* switch */ + } /* while */ + + break; + } /* case */ + + default: { + /* va_list will be unsafe => exit */ + TSK_DEBUG_ERROR("%d NOT a valid SIP pname", sscurr); + goto bail; + } + + } /* switch */ + } /* while */ + +bail: + TSK_OBJECT_SAFE_FREE(mgr); + + return ret; +} + + +tsip_ssession_handle_t* tsip_ssession_create(tsip_stack_handle_t *stack, ...) +{ + tsip_ssession_t* ss = tsk_null; + va_list ap; + tsip_stack_t* _stack = stack; + + if(!_stack) { + TSK_DEBUG_ERROR("Invalid Parameter."); + goto bail; + } + + if(!(ss = tsk_object_new(tsip_ssession_def_t, stack))) { + TSK_DEBUG_ERROR("Failed to create new SIP Session."); + return tsk_null; + } + + va_start(ap, stack); + if(__tsip_ssession_set(ss, &ap)) { + TSK_DEBUG_ERROR("Failed to set user's parameters."); + TSK_OBJECT_SAFE_FREE(ss); + va_end(ap); + goto bail; + } + va_end(ap); + + /* from */ + if(!ss->from && _stack->identity.impu) { + ss->from = tsip_uri_clone(_stack->identity.impu, tsk_false, tsk_false); + } + /* to */ + /* To value will be set by the dialog (whether to use as Request-URI). */ + +bail: + return ss; +} + +int tsip_ssession_set(tsip_ssession_handle_t *self, ...) +{ + if(self) { + int ret; + va_list ap; + + tsip_ssession_t *ss = self; + + if(ss->id == TSIP_SSESSION_INVALID_ID) { + return -2; + } + + va_start(ap, self); + ret = __tsip_ssession_set(ss, &ap); + va_end(ap); + return ret; + } + else { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } +} + +tsip_ssession_id_t tsip_ssession_get_id(const tsip_ssession_handle_t *self) +{ + if(self) { + const tsip_ssession_t *ss = self; + return ss->id; + } + return TSIP_SSESSION_INVALID_ID; +} + +tsip_ssession_id_t tsip_ssession_get_id_parent(const tsip_ssession_handle_t *self) +{ + if(self) { + const tsip_ssession_t *ss = self; + return ss->id_parent; + } + return TSIP_SSESSION_INVALID_ID; +} + +int tsip_ssession_take_ownership(tsip_ssession_handle_t *self) +{ + if(self) { + tsip_ssession_t *ss = self; + if(!ss->owner) { + ss->owner = tsk_true; + /* before: only the dialog had a reference to the SIP session */ + ss = tsk_object_ref(ss); + /* after: both the end-user and the dialog have their references */ + return 0; + } + return -2; + } + else { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } +} + +tsk_bool_t tsip_ssession_have_ownership(const tsip_ssession_handle_t *self) +{ + if(self) { + const tsip_ssession_t *ss = self; + return ss->owner; + } + return tsk_false; +} + +int tsip_ssession_respond(const tsip_ssession_handle_t *self, short status, const char* phrase, const void* payload, tsk_size_t size, const struct tsip_message_s* request, ...) +{ + tsip_response_t *response = tsk_null; + tsip_dialog_t* dialog = tsk_null; + const tsip_ssession_t *ss = self; + int ret = -1; + + if(!ss || !request) { + goto bail; + } + + if(!(dialog = tsip_dialog_layer_find_by_ss(ss->stack->layer_dialog, ss))) { + goto bail; + } + + if(!(response = tsip_dialog_response_new(TSIP_DIALOG(self), status, phrase, request))) { + goto bail; + } + + if(payload && size) { + if((ret = tsip_message_add_content(response, tsk_null, payload, size))) { + goto bail; + } + } + ret = tsip_dialog_response_send(TSIP_DIALOG(self), response); + +bail: + TSK_OBJECT_SAFE_FREE(response); + TSK_OBJECT_SAFE_FREE(dialog); + + return ret; +} + +const void* tsip_ssession_get_userdata(const tsip_ssession_handle_t *self) +{ + const tsip_ssession_t* ss = (const tsip_ssession_t*)self; + if(ss) { + return ss->userdata; + } + else { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } +} + +tmedia_type_t tsip_ssession_get_mediatype(const tsip_ssession_handle_t *self) +{ + if(self) { + return ((const tsip_ssession_t*)self)->media.type; + } + else { + TSK_DEBUG_ERROR("Invalid parameter"); + return tmedia_none; + } +} + +#include "tinysip/dialogs/tsip_dialog_invite.h" +tmedia_session_mgr_t* tsip_session_get_mediamgr(const tsip_ssession_handle_t *self) +{ + tmedia_session_mgr_t* mgr = tsk_null; + + if(self) { + const tsip_ssession_t *ss = self; + tsip_dialog_t* dialog; + + if((dialog = tsip_dialog_layer_find_by_ss(ss->stack->layer_dialog, self))) { + if(dialog->type == tsip_dialog_INVITE) { + mgr = tsk_object_ref(TSIP_DIALOG_INVITE(dialog)->msession_mgr); + } + tsk_object_unref(dialog); + } + } + else { + TSK_DEBUG_ERROR("Invalid parameter"); + } + + return mgr; +} + +const tsip_stack_handle_t* tsip_ssession_get_stack(const tsip_ssession_handle_t *self) +{ + if(self) { + return ((const tsip_ssession_t*)self)->stack; + } + else { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } +} + +tmedia_codec_id_t tsip_ssession_get_codecs_neg(tsip_ssession_handle_t *self) +{ + int32_t codecs_neg = (int32_t)tmedia_codec_id_none; + if(self) { + tmedia_session_mgr_t* mgr = tsip_session_get_mediamgr(self); + if(mgr) { + (tmedia_session_mgr_get(mgr, + TMEDIA_SESSION_GET_INT32(mgr->type, "codecs-negotiated", &codecs_neg), + TMEDIA_SESSION_GET_NULL())); + TSK_OBJECT_SAFE_FREE(mgr); + } + } + return (tmedia_codec_id_t)codecs_neg; +} + +int tsip_ssession_handle(const tsip_ssession_t *self, const struct tsip_action_s* action) +{ + int ret = -1; + + if(self && self->stack && action) { + tsip_dialog_t *dialog; + + if((dialog = tsip_dialog_layer_find_by_ss(self->stack->layer_dialog, self))) { + switch(action->type) { + case tsip_atype_hangup: { + /* hang-up is an special case (==> hangup/cancel/nothing) */ + ret = tsip_dialog_hangup(dialog, action); + break; + } + default: { + /* All other cases */ + ret = tsip_dialog_fsm_act(dialog, action->type, tsk_null, action); + break; + } + } + /* unref */ + tsk_object_unref(dialog); + } + else { + TSK_DEBUG_ERROR("Failed to find dialog with this opid [%lld]", self->id); + } + } + else { + TSK_DEBUG_ERROR("Invalid parameter"); + } + + return ret; +} + + + + + +//======================================================== +// SIP Session object definition +// +static tsk_object_t* tsip_ssession_ctor(tsk_object_t * self, va_list * app) +{ + tsip_ssession_t *ss = self; + static tsip_ssession_id_t unique_id = 0; + if(ss) { + ss->stack = va_arg(*app, const tsip_stack_t*); + ss->caps = tsk_list_create(); + ss->headers = tsk_list_create(); + + /* unique identifier */ + ss->id = ++unique_id; + // default: you are the owner + ss->owner = tsk_true; + // default expires value + ss->expires = TSIP_SSESSION_EXPIRES_DEFAULT; + // default parentid: not parent -> no pending transfer + ss->id_parent = TSIP_SSESSION_INVALID_ID; + // default SigComp compId (will be updated by session_set()) + if (ss->stack->sigcomp.handle) { + ss->sigcomp_id = tsk_strdup(tsip_sigcomp_handler_fixme_getcompid(ss->stack->sigcomp.handle)); + } + // default media values + ss->media.profile = tmedia_defaults_get_profile(); + ss->media.srtp_mode = tmedia_defaults_get_srtp_mode(); + ss->media.avpf_mode = tmedia_defaults_get_avpf_mode(); + ss->media.enable_conditional_ringing = tmedia_defaults_get_conditional_ringing_enabled(); + ss->media.enable_100rel = tmedia_defaults_get_100rel_enabled(); + ss->media.enable_ice = tmedia_defaults_get_ice_enabled(); + ss->media.enable_icestun = tmedia_defaults_get_icestun_enabled(); + ss->media.enable_iceturn = tmedia_defaults_get_iceturn_enabled(); + ss->media.enable_rtcp = tmedia_defaults_get_rtcp_enabled(); + ss->media.enable_rtcpmux = tmedia_defaults_get_rtcpmux_enabled(); + ss->media.type = tmedia_none; + ss->media.qos.type = tmedia_qos_stype_none; + ss->media.qos.strength = tmedia_qos_strength_none; + ss->media.timers.refresher = tsk_strdup(tmedia_defaults_get_inv_session_refresher()); + ss->media.timers.timeout = tmedia_defaults_get_inv_session_expires(); + ss->media.codecs = tmedia_codec_id_all; + ss->media.bypass_encoding = tmedia_defaults_get_bypass_encoding(); + ss->media.bypass_decoding = tmedia_defaults_get_bypass_decoding(); + ss->media.video_fps = tmedia_defaults_get_video_fps(); + ss->media.video_bw_down = tmedia_defaults_get_bandwidth_video_download_max(); + ss->media.video_bw_up = tmedia_defaults_get_bandwidth_video_upload_max(); + ss->media.video_pref_size = tmedia_defaults_get_pref_video_size(); + { + const char *stun_hostname, *stun_username, *stun_password; + uint16_t stun_port; + if(tmedia_defaults_get_stun_server(&stun_hostname, &stun_port) == 0) { + ss->media.stun.hostname = tsk_strdup(stun_hostname); + ss->media.stun.port = stun_port; + } + if(tmedia_defaults_get_stun_cred(&stun_username, &stun_password) == 0) { + ss->media.stun.username = tsk_strdup(stun_username); + ss->media.stun.password = tsk_strdup(stun_password); + } + } + + /* add the session to the stack */ + if(ss->stack) { + tsk_list_push_back_data(ss->stack->ssessions, (void**)&ss); + } + } + + return self; +} + +static tsk_object_t* tsip_ssession_dtor(tsk_object_t * self) +{ + tsip_ssession_t *ss = self; + if(ss) { + /* remove from the stack */ + if(ss->stack) { + tsk_list_remove_item_by_data(ss->stack->ssessions, ss); + } + + //======= + // SIP + //======= + TSK_OBJECT_SAFE_FREE(ss->caps); + TSK_OBJECT_SAFE_FREE(ss->headers); + + TSK_OBJECT_SAFE_FREE(ss->from); + TSK_OBJECT_SAFE_FREE(ss->to); + + TSK_FREE(ss->sigcomp_id); + TSK_FREE(ss->auth_ha1); + TSK_FREE(ss->auth_impi); + + //======= + // Media + //======= + TSK_FREE(ss->media.timers.refresher); + TSK_FREE(ss->media.stun.username); + TSK_FREE(ss->media.stun.password); + TSK_FREE(ss->media.stun.hostname); + + //======= + // WebSocket + //======= + TSK_FREE(ss->ws.src.host); + TSK_FREE(ss->ws.src.proto); + + TSK_DEBUG_INFO("*** SIP Session destroyed ***"); + } + return self; +} + +static int tsip_ssession_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + const tsip_ssession_t *ss1 = obj1; + const tsip_ssession_t *ss2 = obj2; + + if(ss1 && ss2) { + return (int)(ss1->id-ss2->id); + } + return -1; +} + +static const tsk_object_def_t tsip_ssession_def_s = { + sizeof(tsip_ssession_t), + tsip_ssession_ctor, + tsip_ssession_dtor, + tsip_ssession_cmp, +}; +const tsk_object_def_t *tsip_ssession_def_t = &tsip_ssession_def_s; diff --git a/tinySIP/vs_android/tinySIP.vcxproj b/tinySIP/vs_android/tinySIP.vcxproj old mode 100644 new mode 100755 diff --git a/tinySIP/vs_android/tinySIP.vcxproj.filters b/tinySIP/vs_android/tinySIP.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinySMS/vs_android/tinySMS.vcxproj b/tinySMS/vs_android/tinySMS.vcxproj old mode 100644 new mode 100755 diff --git a/tinySMS/vs_android/tinySMS.vcxproj.filters b/tinySMS/vs_android/tinySMS.vcxproj.filters old mode 100644 new mode 100755 diff --git a/tinyXCAP/vs_android/tinyXCAP.vcxproj b/tinyXCAP/vs_android/tinyXCAP.vcxproj old mode 100644 new mode 100755 diff --git a/tinyXCAP/vs_android/tinyXCAP.vcxproj.filters b/tinyXCAP/vs_android/tinyXCAP.vcxproj.filters old mode 100644 new mode 100755