summaryrefslogtreecommitdiffstats
path: root/tinySIP/src
diff options
context:
space:
mode:
authorMamadou DIOP <bossiel@yahoo.fr>2015-08-17 01:56:35 +0200
committerMamadou DIOP <bossiel@yahoo.fr>2015-08-17 01:56:35 +0200
commit631fffee8a28b1bec5ed1f1d26a20e0135967f99 (patch)
tree74afe3bf3efe15aa82bcd0272b2b0f4d48c2d837 /tinySIP/src
parent7908865936604036e6f200f1b5e069f8752f3a3a (diff)
downloaddoubango-631fffee8a28b1bec5ed1f1d26a20e0135967f99.zip
doubango-631fffee8a28b1bec5ed1f1d26a20e0135967f99.tar.gz
-
Diffstat (limited to 'tinySIP/src')
-rw-r--r--tinySIP/src/api/tsip_api_common.c134
-rw-r--r--tinySIP/src/api/tsip_api_info.c134
-rw-r--r--tinySIP/src/api/tsip_api_invite.c363
-rw-r--r--tinySIP/src/api/tsip_api_message.c134
-rw-r--r--tinySIP/src/api/tsip_api_options.c133
-rw-r--r--tinySIP/src/api/tsip_api_publish.c159
-rw-r--r--tinySIP/src/api/tsip_api_register.c166
-rw-r--r--tinySIP/src/api/tsip_api_subscribe.c160
-rw-r--r--tinySIP/src/authentication/tsip_challenge.c477
-rw-r--r--tinySIP/src/authentication/tsip_milenage.c350
-rw-r--r--tinySIP/src/authentication/tsip_rijndael.c480
-rw-r--r--tinySIP/src/dialogs/tsip_dialog.c1354
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_info.c556
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_invite.c1942
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_invite.cdiv.c0
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_invite.client.c333
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_invite.conf.c1
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_invite.ect.c487
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_invite.hold.c252
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_invite.ice.c617
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_invite.qos.c92
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_invite.server.c790
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_invite.timers.c302
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_layer.c776
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_message.c552
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_options.c578
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_publish.client.c701
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_register.c507
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_register.client.c424
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_register.server.c237
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_subscribe.client.c764
-rw-r--r--tinySIP/src/dialogs/tsip_dialog_subscribe.server.c1
-rw-r--r--tinySIP/src/headers/tsip_header.c337
-rw-r--r--tinySIP/src/headers/tsip_header_Accept_Contact.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Accept_Encoding.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Accept_Language.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Accept_Resource_Priority.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Alert_Info.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Allow.c365
-rw-r--r--tinySIP/src/headers/tsip_header_Allow_Events.c352
-rw-r--r--tinySIP/src/headers/tsip_header_Authentication_Info.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Authorization.c180
-rw-r--r--tinySIP/src/headers/tsip_header_CSeq.c344
-rw-r--r--tinySIP/src/headers/tsip_header_Call_ID.c340
-rw-r--r--tinySIP/src/headers/tsip_header_Call_Info.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Contact.c702
-rw-r--r--tinySIP/src/headers/tsip_header_Content_Disposition.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Content_Encoding.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Content_Language.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Content_Length.c325
-rw-r--r--tinySIP/src/headers/tsip_header_Content_Type.c425
-rw-r--r--tinySIP/src/headers/tsip_header_Date.c466
-rw-r--r--tinySIP/src/headers/tsip_header_Dummy.c331
-rw-r--r--tinySIP/src/headers/tsip_header_Error_Info.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Event.c462
-rw-r--r--tinySIP/src/headers/tsip_header_Expires.c312
-rw-r--r--tinySIP/src/headers/tsip_header_From.c581
-rw-r--r--tinySIP/src/headers/tsip_header_History_Info.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Identity.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Identity_Info.c1
-rw-r--r--tinySIP/src/headers/tsip_header_In_Reply_To.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Join.c1
-rw-r--r--tinySIP/src/headers/tsip_header_MIME_Version.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Max_Forwards.c321
-rw-r--r--tinySIP/src/headers/tsip_header_Min_Expires.c326
-rw-r--r--tinySIP/src/headers/tsip_header_Min_SE.c449
-rw-r--r--tinySIP/src/headers/tsip_header_Organization.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_Access_Network_Info.c345
-rw-r--r--tinySIP/src/headers/tsip_header_P_Answer_State.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_Asserted_Identity.c1429
-rw-r--r--tinySIP/src/headers/tsip_header_P_Associated_URI.c558
-rw-r--r--tinySIP/src/headers/tsip_header_P_Called_Party_ID.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_Charging_Function_Addresses.c749
-rw-r--r--tinySIP/src/headers/tsip_header_P_Charging_Vector.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_DCS_Billing_Info.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_DCS_LAES.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_DCS_OSPS.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_DCS_Redirect.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_DCS_Trace_Party_ID.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_Early_Media.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_Media_Authorization.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_Preferred_Identity.c669
-rw-r--r--tinySIP/src/headers/tsip_header_P_Profile_Key.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_User_Database.c1
-rw-r--r--tinySIP/src/headers/tsip_header_P_Visited_Network_ID.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Path.c543
-rw-r--r--tinySIP/src/headers/tsip_header_Priority.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Privacy.c489
-rw-r--r--tinySIP/src/headers/tsip_header_Proxy_Authenticate.c161
-rw-r--r--tinySIP/src/headers/tsip_header_Proxy_Authorization.c182
-rw-r--r--tinySIP/src/headers/tsip_header_Proxy_Require.c364
-rw-r--r--tinySIP/src/headers/tsip_header_RAck.c355
-rw-r--r--tinySIP/src/headers/tsip_header_RSeq.c313
-rw-r--r--tinySIP/src/headers/tsip_header_Reason.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Record_Route.c554
-rw-r--r--tinySIP/src/headers/tsip_header_Refer_Sub.c471
-rw-r--r--tinySIP/src/headers/tsip_header_Refer_To.c837
-rw-r--r--tinySIP/src/headers/tsip_header_Referred_By.c1372
-rw-r--r--tinySIP/src/headers/tsip_header_Reject_Contact.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Replaces.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Reply_To.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Request_Disposition.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Require.c354
-rw-r--r--tinySIP/src/headers/tsip_header_Resource_Priority.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Retry_After.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Route.c545
-rw-r--r--tinySIP/src/headers/tsip_header_SIP_ETag.c333
-rw-r--r--tinySIP/src/headers/tsip_header_SIP_If_Match.c338
-rw-r--r--tinySIP/src/headers/tsip_header_Security_Client.c969
-rw-r--r--tinySIP/src/headers/tsip_header_Security_Server.c949
-rw-r--r--tinySIP/src/headers/tsip_header_Security_Verify.c948
-rw-r--r--tinySIP/src/headers/tsip_header_Server.c316
-rw-r--r--tinySIP/src/headers/tsip_header_Service_Route.c552
-rw-r--r--tinySIP/src/headers/tsip_header_Session_Expires.c563
-rw-r--r--tinySIP/src/headers/tsip_header_Subject.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Subscription_State.c700
-rw-r--r--tinySIP/src/headers/tsip_header_Supported.c361
-rw-r--r--tinySIP/src/headers/tsip_header_Target_Dialog.c1
-rw-r--r--tinySIP/src/headers/tsip_header_Timestamp.c1
-rw-r--r--tinySIP/src/headers/tsip_header_To.c953
-rw-r--r--tinySIP/src/headers/tsip_header_Unsupported.c1
-rw-r--r--tinySIP/src/headers/tsip_header_User_Agent.c319
-rw-r--r--tinySIP/src/headers/tsip_header_Via.c1417
-rw-r--r--tinySIP/src/headers/tsip_header_WWW_Authenticate.c160
-rw-r--r--tinySIP/src/headers/tsip_header_Warning.c554
-rw-r--r--tinySIP/src/headers/tsip_header_accept.c30
-rw-r--r--tinySIP/src/parsers/tsip_parser_header.c6357
-rw-r--r--tinySIP/src/parsers/tsip_parser_message.c533
-rw-r--r--tinySIP/src/parsers/tsip_parser_uri.c685
-rw-r--r--tinySIP/src/sigcomp/tsip_sigcomp.c530
-rw-r--r--tinySIP/src/transactions/tsip_transac.c274
-rw-r--r--tinySIP/src/transactions/tsip_transac_ict.c927
-rw-r--r--tinySIP/src/transactions/tsip_transac_ist.c823
-rw-r--r--tinySIP/src/transactions/tsip_transac_layer.c352
-rw-r--r--tinySIP/src/transactions/tsip_transac_nict.c731
-rw-r--r--tinySIP/src/transactions/tsip_transac_nist.c583
-rw-r--r--tinySIP/src/transports/tsip_transport.c1098
-rw-r--r--tinySIP/src/transports/tsip_transport_ipsec.c537
-rw-r--r--tinySIP/src/transports/tsip_transport_layer.c1403
-rw-r--r--tinySIP/src/transports/tsip_transport_tls.c0
-rw-r--r--tinySIP/src/tsip.c1235
-rw-r--r--tinySIP/src/tsip_action.c283
-rw-r--r--tinySIP/src/tsip_event.c163
-rw-r--r--tinySIP/src/tsip_message.c654
-rw-r--r--tinySIP/src/tsip_ssession.c791
-rw-r--r--tinySIP/src/tsip_timers.c250
-rw-r--r--tinySIP/src/tsip_uri.c345
147 files changed, 58923 insertions, 0 deletions
diff --git a/tinySIP/src/api/tsip_api_common.c b/tinySIP/src/api/tsip_api_common.c
new file mode 100644
index 0000000..4261fd5
--- /dev/null
+++ b/tinySIP/src/api/tsip_api_common.c
@@ -0,0 +1,134 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_common.c
+ * @brief Public common functions.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/api/tsip_api_common.h"
+
+#include "tinysip/tsip_action.h"
+
+#include "tsk_runnable.h"
+#include "tsk_debug.h"
+
+/* Internal functions */
+extern tsip_action_t* _tsip_action_create(tsip_action_type_t type, va_list* app);
+/* Local functions */
+int _tsip_api_common_any(const tsip_ssession_handle_t *ss, tsip_action_type_t type, va_list* app);
+
+/* internal function used to execute any user action
+* can only handle session with dialogs */
+int _tsip_api_common_any(const tsip_ssession_handle_t *ss, tsip_action_type_t type, va_list* app)
+{
+ int ret = -1;
+ tsip_action_t* action;
+ const tsip_ssession_t* _ss;
+
+ /* Checks for validity */
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ /* execute action */
+ if((action = _tsip_action_create(type, app))){
+ ret = tsip_ssession_handle(_ss, action);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ return ret;
+}
+
+/**@ingroup tsip_action_group
+* Rejects an incoming request.
+* @param ss The SIP Session managing the dialog on which the request has been received.
+* @param ... Any TSIP_ACTION_SET_*() macros. e.g. @ref TSIP_ACTION_SET_HEADER().
+* MUST always ends with @ref TSIP_ACTION_SET_NULL().
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tsip_api_common_reject(const tsip_ssession_handle_t *ss, ...)
+{
+ int ret = -1;
+ va_list ap;
+
+ va_start(ap, ss);
+ if((ret = _tsip_api_common_any(ss, tsip_atype_reject, &ap))){
+ TSK_DEBUG_ERROR("Reject() failed.");
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+/**@ingroup tsip_action_group
+* Hangs up a session.
+* @param ss The SIP Session to hang-up. Will send an unREGISTER or unSUBSCRIBE or unPUBLISH or
+* BYE etc depending on the type of the SIP dialog managed by the session.
+* @param ... Any TSIP_ACTION_SET_*() macros. e.g. @ref TSIP_ACTION_SET_HEADER().
+* MUST always ends with @ref TSIP_ACTION_SET_NULL().
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tsip_api_common_hangup(const tsip_ssession_handle_t *ss, ...)
+{
+ int ret = -1;
+ va_list ap;
+
+ va_start(ap, ss);
+ if((ret = _tsip_api_common_any(ss, tsip_atype_hangup, &ap))){
+ TSK_DEBUG_ERROR("Hang-up() failed.");
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+/**@ingroup tsip_action_group
+* Accepts an incoming request.
+* @param ss The SIP Session managing the dialog on which the request has been received.
+* @param ... Any TSIP_ACTION_SET_*() macros. e.g. @ref TSIP_ACTION_SET_HEADER().
+* MUST always ends with @ref TSIP_ACTION_SET_NULL().
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tsip_api_common_accept(const tsip_ssession_handle_t *ss, ...)
+{
+ int ret = -1;
+ va_list ap;
+
+ va_start(ap, ss);
+ if((ret = _tsip_api_common_any(ss, tsip_atype_accept, &ap))){
+ TSK_DEBUG_ERROR("Accept() failed.");
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+
diff --git a/tinySIP/src/api/tsip_api_info.c b/tinySIP/src/api/tsip_api_info.c
new file mode 100644
index 0000000..ec20b8b
--- /dev/null
+++ b/tinySIP/src/api/tsip_api_info.c
@@ -0,0 +1,134 @@
+/* Copyright (C) 2011 Doubango Telecom <http://www.doubango.org>
+* Copyright (C) 2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango(dot)org>
+*
+* 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_info.c
+ * @brief Public short messaging (MESSAGE) functions.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/api/tsip_api_info.h"
+
+#include "tinysip/dialogs/tsip_dialog_layer.h"
+#include "tinysip/dialogs/tsip_dialog_message.h"
+
+#include "tsip.h"
+
+#include "tsk_runnable.h"
+#include "tsk_debug.h"
+
+#define TSIP_INFO_EVENT_CREATE( type) tsk_object_new(tsip_info_event_def_t, type)
+
+extern tsip_action_t* _tsip_action_create(tsip_action_type_t type, va_list* app);
+
+int tsip_info_event_signal(tsip_info_event_type_t type, tsip_ssession_handle_t* ss, short status_code, const char *phrase, const tsip_message_t* sipmessage)
+{
+ tsip_info_event_t* sipevent = TSIP_INFO_EVENT_CREATE(type);
+ tsip_event_init(TSIP_EVENT(sipevent), ss, status_code, phrase, sipmessage, tsip_event_info);
+
+ TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(TSIP_SSESSION(ss)->stack), sipevent);
+
+ return 0;
+}
+
+int tsip_api_info_send_info(const tsip_ssession_handle_t *ss, ...)
+{
+ const tsip_ssession_t* _ss;
+ va_list ap;
+ tsip_action_t* action;
+ tsip_dialog_t* dialog;
+ int ret = -1;
+
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ /* action */
+ va_start(ap, ss);
+ if((action = _tsip_action_create(tsip_atype_info_send, &ap))){
+ if(!(dialog = tsip_dialog_layer_find_by_ss(_ss->stack->layer_dialog, ss))){
+ dialog = tsip_dialog_layer_new(_ss->stack->layer_dialog, tsip_dialog_INFO, ss);
+ }
+ ret = tsip_dialog_fsm_act(dialog, action->type, tsk_null, action);
+
+ tsk_object_unref(dialog);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP MESSAGE event object definition
+//
+static tsk_object_t* tsip_info_event_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_info_event_t *sipevent = self;
+ if(sipevent){
+ sipevent->type = va_arg(*app, tsip_info_event_type_t);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_info_event_dtor(tsk_object_t * self)
+{
+ tsip_info_event_t *sipevent = self;
+ if(sipevent){
+ tsip_event_deinit(TSIP_EVENT(sipevent));
+ }
+ return self;
+}
+
+static int tsip_info_event_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
+{
+ return -1;
+}
+
+static const tsk_object_def_t tsip_info_event_def_s =
+{
+ sizeof(tsip_info_event_t),
+ tsip_info_event_ctor,
+ tsip_info_event_dtor,
+ tsip_info_event_cmp,
+};
+const tsk_object_def_t *tsip_info_event_def_t = &tsip_info_event_def_s;
diff --git a/tinySIP/src/api/tsip_api_invite.c b/tinySIP/src/api/tsip_api_invite.c
new file mode 100644
index 0000000..530eb67
--- /dev/null
+++ b/tinySIP/src/api/tsip_api_invite.c
@@ -0,0 +1,363 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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.c
+ * @brief Public short messaging (INVITE) functions.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/api/tsip_api_invite.h"
+
+#include "tinysip/dialogs/tsip_dialog_layer.h"
+#include "tinysip/dialogs/tsip_dialog_invite.h"
+
+#include "tinysip/tsip_action.h"
+#include "tsip.h"
+
+#include "tsk_runnable.h"
+#include "tsk_debug.h"
+
+#define TSIP_INVITE_EVENT_CREATE( type) tsk_object_new(tsip_invite_event_def_t, type)
+
+extern tsip_action_t* _tsip_action_create(tsip_action_type_t type, va_list* app);
+extern int _tsip_api_common_any(const tsip_ssession_handle_t *ss, tsip_action_type_t type, va_list* app);
+
+int tsip_invite_event_signal(tsip_invite_event_type_t type, tsip_ssession_handle_t* ss, short status_code, const char *phrase, const tsip_message_t* sipmessage)
+{
+ tsip_invite_event_t* sipevent = TSIP_INVITE_EVENT_CREATE(type);
+ tsip_event_init(TSIP_EVENT(sipevent), ss, status_code, phrase, sipmessage, tsip_event_invite);
+
+ TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(TSIP_SSESSION(ss)->stack), sipevent);
+
+ return 0;
+}
+
+int tsip_api_invite_send_invite(const tsip_ssession_handle_t *ss, tmedia_type_t type, ...)
+{
+ const tsip_ssession_t* _ss;
+ va_list ap;
+ tsip_action_t* action;
+ tsip_dialog_t* dialog;
+ int ret = -1;
+
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ va_start(ap, type);
+ if((action = _tsip_action_create(tsip_atype_invite, &ap))){
+ tsk_bool_t new_dialog = tsk_false;
+ /* Media type */
+ action->media.type = type;
+
+ if(!(dialog = tsip_dialog_layer_find_by_ss(_ss->stack->layer_dialog, ss))){
+ dialog = tsip_dialog_layer_new(_ss->stack->layer_dialog, tsip_dialog_INVITE, ss);
+ new_dialog = tsk_true;
+ }
+ if(!(ret = tsip_dialog_fsm_act(dialog, action->type, tsk_null, action))){
+ if(new_dialog){ // otherwise we are trying to refresh the media type and the type will be updated if 200 OK
+ TSIP_SSESSION(_ss)->media.type = type; // Update Session Media Type
+ }
+ }
+
+ tsk_object_unref(dialog);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_invite_send_info(const tsip_ssession_handle_t *ss, ...)
+{
+ int ret = -1;
+ tsip_action_t* action;
+ const tsip_ssession_t* _ss;
+ va_list ap;
+
+ /* Checks for validity */
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ va_start(ap, ss);
+ /* execute action */
+ if((action = _tsip_action_create(tsip_atype_info_send, &ap))){
+ /* Perform action */
+ ret = tsip_ssession_handle(_ss, action);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_invite_send_hold(const tsip_ssession_handle_t *ss, tmedia_type_t type, ...)
+{
+ int ret = -1;
+ tsip_action_t* action;
+ const tsip_ssession_t* _ss;
+ va_list ap;
+
+ /* Checks for validity */
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ va_start(ap, type);
+ /* execute action */
+ if((action = _tsip_action_create(tsip_atype_hold, &ap))){
+ /* Media type */
+ action->media.type = type;
+ /* Perform action */
+ ret = tsip_ssession_handle(_ss, action);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_invite_send_resume(const tsip_ssession_handle_t *ss, tmedia_type_t type, ...)
+{
+ int ret = -1;
+ tsip_action_t* action;
+ const tsip_ssession_t* _ss;
+ va_list ap;
+
+ /* Checks for validity */
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ va_start(ap, type);
+ /* execute action */
+ if((action = _tsip_action_create(tsip_atype_resume, &ap))){
+ /* Media type */
+ action->media.type = type;
+ /* Perform action */
+ ret = tsip_ssession_handle(_ss, action);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_invite_send_large_message(const tsip_ssession_handle_t *ss, ...)
+{
+ int ret = -1;
+ va_list ap;
+
+ va_start(ap, ss);
+ if((ret = _tsip_api_common_any(ss, tsip_atype_lmessage, &ap))){
+ TSK_DEBUG_ERROR("Failed to send MSRP message");
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_invite_send_ect(const tsip_ssession_handle_t *ss, const char* toUri, ...)
+{
+ int ret = -1;
+ tsip_action_t* action;
+ const tsip_ssession_t* _ss;
+ va_list ap;
+
+ /* Checks for validity */
+ if(!(_ss = ss) || !_ss->stack || !toUri){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ va_start(ap, toUri);
+ /* execute action */
+ if((action = _tsip_action_create(tsip_atype_ect, &ap))){
+ /* Refer-To */
+ action->ect.to = tsk_strdup(toUri);
+ /* Perform action */
+ ret = tsip_ssession_handle(_ss, action);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_invite_send_ect_accept(const tsip_ssession_handle_t *ss, ...)
+{
+ int ret = -1;
+ va_list ap;
+
+ va_start(ap, ss);
+ if((ret = _tsip_api_common_any(ss, tsip_atype_ect_accept, &ap))){
+ TSK_DEBUG_ERROR("Failed to accept incoming ECT");
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_invite_send_ect_reject(const tsip_ssession_handle_t *ss, ...)
+{
+ int ret = -1;
+ va_list ap;
+
+ va_start(ap, ss);
+ if((ret = _tsip_api_common_any(ss, tsip_atype_ect_reject, &ap))){
+ TSK_DEBUG_ERROR("Failed to reject incoming ECT");
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_invite_send_dtmf(const tsip_ssession_handle_t *ss, int event, ...)
+{
+ int ret = -1;
+ tsip_action_t* action;
+ const tsip_ssession_t* _ss;
+ va_list ap;
+
+ /* Checks for validity */
+ if(!(_ss = ss) || !_ss->stack || (event <0 || event>15)){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ va_start(ap, event);
+ /* execute action */
+ if((action = _tsip_action_create(tsip_atype_dtmf_send, &ap))){
+ /* Event */
+ action->dtmf.event = event;
+ /* Perform action */
+ ret = tsip_ssession_handle(_ss, action);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_invite_send_bye(const tsip_ssession_handle_t *ss, ...)
+{
+ int ret = -1;
+ va_list ap;
+
+ va_start(ap, ss);
+ if((ret = _tsip_api_common_any(ss, tsip_atype_bye, &ap))){
+ TSK_DEBUG_ERROR("Bye() failed.");
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP INVITE event object definition
+//
+static tsk_object_t* tsip_invite_event_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_invite_event_t *sipevent = self;
+ if(sipevent){
+ sipevent->type = va_arg(*app, tsip_invite_event_type_t);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_invite_event_dtor(tsk_object_t * self)
+{
+ tsip_invite_event_t *sipevent = self;
+ if(sipevent){
+ tsip_event_deinit(TSIP_EVENT(sipevent));
+ }
+ return self;
+}
+
+static int tsip_invite_event_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
+{
+ return -1;
+}
+
+static const tsk_object_def_t tsip_invite_event_def_s =
+{
+ sizeof(tsip_invite_event_t),
+ tsip_invite_event_ctor,
+ tsip_invite_event_dtor,
+ tsip_invite_event_cmp,
+};
+const tsk_object_def_t *tsip_invite_event_def_t = &tsip_invite_event_def_s;
diff --git a/tinySIP/src/api/tsip_api_message.c b/tinySIP/src/api/tsip_api_message.c
new file mode 100644
index 0000000..232e9c6
--- /dev/null
+++ b/tinySIP/src/api/tsip_api_message.c
@@ -0,0 +1,134 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_message.c
+ * @brief Public short messaging (MESSAGE) functions.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/api/tsip_api_message.h"
+
+#include "tinysip/dialogs/tsip_dialog_layer.h"
+#include "tinysip/dialogs/tsip_dialog_message.h"
+
+#include "tsip.h"
+
+#include "tsk_runnable.h"
+#include "tsk_debug.h"
+
+#define TSIP_MESSAGE_EVENT_CREATE( type) tsk_object_new(tsip_message_event_def_t, type)
+
+extern tsip_action_t* _tsip_action_create(tsip_action_type_t type, va_list* app);
+
+int tsip_message_event_signal(tsip_message_event_type_t type, tsip_ssession_handle_t* ss, short status_code, const char *phrase, const tsip_message_t* sipmessage)
+{
+ tsip_message_event_t* sipevent = TSIP_MESSAGE_EVENT_CREATE(type);
+ tsip_event_init(TSIP_EVENT(sipevent), ss, status_code, phrase, sipmessage, tsip_event_message);
+
+ TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(TSIP_SSESSION(ss)->stack), sipevent);
+
+ return 0;
+}
+
+int tsip_api_message_send_message(const tsip_ssession_handle_t *ss, ...)
+{
+ const tsip_ssession_t* _ss;
+ va_list ap;
+ tsip_action_t* action;
+ tsip_dialog_t* dialog;
+ int ret = -1;
+
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ /* action */
+ va_start(ap, ss);
+ if((action = _tsip_action_create(tsip_atype_message_send, &ap))){
+ if(!(dialog = tsip_dialog_layer_find_by_ss(_ss->stack->layer_dialog, ss))){
+ dialog = tsip_dialog_layer_new(_ss->stack->layer_dialog, tsip_dialog_MESSAGE, ss);
+ }
+ ret = tsip_dialog_fsm_act(dialog, action->type, tsk_null, action);
+
+ tsk_object_unref(dialog);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP MESSAGE event object definition
+//
+static tsk_object_t* tsip_message_event_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_message_event_t *sipevent = self;
+ if(sipevent){
+ sipevent->type = va_arg(*app, tsip_message_event_type_t);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_message_event_dtor(tsk_object_t * self)
+{
+ tsip_message_event_t *sipevent = self;
+ if(sipevent){
+ tsip_event_deinit(TSIP_EVENT(sipevent));
+ }
+ return self;
+}
+
+static int tsip_message_event_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
+{
+ return -1;
+}
+
+static const tsk_object_def_t tsip_message_event_def_s =
+{
+ sizeof(tsip_message_event_t),
+ tsip_message_event_ctor,
+ tsip_message_event_dtor,
+ tsip_message_event_cmp,
+};
+const tsk_object_def_t *tsip_message_event_def_t = &tsip_message_event_def_s;
diff --git a/tinySIP/src/api/tsip_api_options.c b/tinySIP/src/api/tsip_api_options.c
new file mode 100644
index 0000000..df87a4d
--- /dev/null
+++ b/tinySIP/src/api/tsip_api_options.c
@@ -0,0 +1,133 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_options.c
+ * @brief Public functions to handle OPTIONS.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/api/tsip_api_options.h"
+
+#include "tinysip/dialogs/tsip_dialog_layer.h"
+#include "tinysip/dialogs/tsip_dialog_options.h"
+
+#include "tsip.h"
+
+#include "tsk_runnable.h"
+#include "tsk_debug.h"
+
+#define TSIP_OPTIONS_EVENT_CREATE( type) tsk_object_new(tsip_options_event_def_t, type)
+
+extern tsip_action_t* _tsip_action_create(tsip_action_type_t type, va_list* app);
+
+int tsip_options_event_signal(tsip_options_event_type_t type, tsip_ssession_handle_t* ss, short status_code, const char *phrase, const tsip_message_t* sipmessage)
+{
+ tsip_options_event_t* sipevent = TSIP_OPTIONS_EVENT_CREATE(type);
+ tsip_event_init(TSIP_EVENT(sipevent), ss, status_code, phrase, sipmessage, tsip_event_options);
+
+ TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(TSIP_SSESSION(ss)->stack), sipevent);
+
+ return 0;
+}
+
+int tsip_api_options_send_options(const tsip_ssession_handle_t *ss, ...)
+{
+ const tsip_ssession_t* _ss;
+ va_list ap;
+ tsip_action_t* action;
+ tsip_dialog_t* dialog;
+ int ret = -1;
+
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ va_start(ap, ss);
+ if((action = _tsip_action_create(tsip_atype_options_send, &ap))){
+ if(!(dialog = tsip_dialog_layer_find_by_ss(_ss->stack->layer_dialog, ss))){
+ dialog = tsip_dialog_layer_new(_ss->stack->layer_dialog, tsip_dialog_OPTIONS, ss);
+ }
+ ret = tsip_dialog_fsm_act(dialog, action->type, tsk_null, action);
+
+ tsk_object_unref(dialog);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP OPTIONS event object definition
+//
+static tsk_object_t* tsip_options_event_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_options_event_t *sipevent = self;
+ if(sipevent){
+ sipevent->type = va_arg(*app, tsip_options_event_type_t);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_options_event_dtor(tsk_object_t * self)
+{
+ tsip_options_event_t *sipevent = self;
+ if(sipevent){
+ tsip_event_deinit(TSIP_EVENT(sipevent));
+ }
+ return self;
+}
+
+static int tsip_options_event_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
+{
+ return -1;
+}
+
+static const tsk_object_def_t tsip_options_event_def_s =
+{
+ sizeof(tsip_options_event_t),
+ tsip_options_event_ctor,
+ tsip_options_event_dtor,
+ tsip_options_event_cmp,
+};
+const tsk_object_def_t *tsip_options_event_def_t = &tsip_options_event_def_s;
diff --git a/tinySIP/src/api/tsip_api_publish.c b/tinySIP/src/api/tsip_api_publish.c
new file mode 100644
index 0000000..916ed6f
--- /dev/null
+++ b/tinySIP/src/api/tsip_api_publish.c
@@ -0,0 +1,159 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_publish.c
+ * @brief Public subscription (PUBLISH) functions.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/api/tsip_api_publish.h"
+
+#include "tinysip/dialogs/tsip_dialog_layer.h"
+#include "tinysip/dialogs/tsip_dialog_publish.h"
+
+#include "tsip.h"
+
+#include "tsk_runnable.h"
+#include "tsk_debug.h"
+
+#define TSIP_PUBLISH_EVENT_CREATE( type) tsk_object_new(tsip_publish_event_def_t, type)
+
+extern tsip_action_t* _tsip_action_create(tsip_action_type_t type, va_list* app);
+
+int tsip_publish_event_signal(tsip_publish_event_type_t type, tsip_ssession_handle_t* ss, short status_code, const char *phrase, const tsip_message_t* sipmessage)
+{
+ tsip_publish_event_t* sipevent = TSIP_PUBLISH_EVENT_CREATE(type);
+ tsip_event_init(TSIP_EVENT(sipevent), ss, status_code, phrase, sipmessage, tsip_event_publish);
+
+ TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(TSIP_SSESSION(ss)->stack), sipevent);
+
+ return 0;
+}
+
+int tsip_api_publish_send_publish(const tsip_ssession_handle_t *ss, ...)
+{
+ const tsip_ssession_t* _ss;
+ va_list ap;
+ tsip_action_t* action;
+ tsip_dialog_t* dialog;
+ int ret = -1;
+
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ va_start(ap, ss);
+ if((action = _tsip_action_create(tsip_atype_publish, &ap))){
+ if(!(dialog = tsip_dialog_layer_find_by_ss(_ss->stack->layer_dialog, ss))){
+ dialog = tsip_dialog_layer_new(_ss->stack->layer_dialog, tsip_dialog_PUBLISH, ss);
+ }
+ ret = tsip_dialog_fsm_act(dialog, action->type, tsk_null, action);
+
+ tsk_object_unref(dialog);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_publish_send_unpublish(const tsip_ssession_handle_t *ss, ...)
+{
+ const tsip_ssession_t* _ss;
+ va_list ap;
+ tsip_action_t* action;
+ int ret = -1;
+
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack is running */
+ if(!TSK_RUNNABLE(_ss->stack)->running){
+ TSK_DEBUG_ERROR("Stack not running.");
+ return -2;
+ }
+
+ va_start(ap, ss);
+ if((action = _tsip_action_create(tsip_atype_unpublish, &ap))){
+ ret = tsip_ssession_handle(ss, action);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP PUBLISH event object definition
+//
+static tsk_object_t* tsip_publish_event_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_publish_event_t *sipevent = self;
+ if(sipevent){
+ sipevent->type = va_arg(*app, tsip_publish_event_type_t);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_publish_event_dtor(tsk_object_t * self)
+{
+ tsip_publish_event_t *sipevent = self;
+ if(sipevent){
+ tsip_event_deinit(TSIP_EVENT(sipevent));
+ }
+ return self;
+}
+
+static int tsip_publish_event_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
+{
+ return -1;
+}
+
+static const tsk_object_def_t tsip_publish_event_def_s =
+{
+ sizeof(tsip_publish_event_t),
+ tsip_publish_event_ctor,
+ tsip_publish_event_dtor,
+ tsip_publish_event_cmp,
+};
+const tsk_object_def_t *tsip_publish_event_def_t = &tsip_publish_event_def_s;
diff --git a/tinySIP/src/api/tsip_api_register.c b/tinySIP/src/api/tsip_api_register.c
new file mode 100644
index 0000000..4d61632
--- /dev/null
+++ b/tinySIP/src/api/tsip_api_register.c
@@ -0,0 +1,166 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_register.c
+ * @brief Public registration (REGISTER) functions.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/api/tsip_api_register.h"
+
+#include "tinysip/dialogs/tsip_dialog_layer.h"
+#include "tinysip/dialogs/tsip_dialog_register.h"
+
+#include "tsip.h"
+#include "tinysip/tsip_action.h"
+
+#include "tsk_runnable.h"
+#include "tsk_debug.h"
+
+#define TSIP_REGISTER_EVENT_CREATE( type) tsk_object_new(tsip_register_event_def_t, type)
+
+/* Internal functions */
+extern tsip_action_t* _tsip_action_create(tsip_action_type_t type, va_list* app);
+extern int _tsip_api_common_any(const tsip_ssession_handle_t *ss, tsip_action_type_t type, va_list* app);
+
+/* internal function used to signal evant from REGISTER dialog to user app */
+int tsip_register_event_signal(tsip_register_event_type_t type, tsip_ssession_t* ss, short status_code, const char *phrase, const tsip_message_t* sipmessage)
+{
+ tsip_register_event_t* sipevent = TSIP_REGISTER_EVENT_CREATE(type);
+ tsip_event_init(TSIP_EVENT(sipevent), ss, status_code, phrase, sipmessage, tsip_event_register);
+
+ TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(ss->stack), sipevent);
+
+ return 0;
+}
+
+/**@ingroup tsip_action_group
+* Sends SIP REGISTER request. If the session is already established, the same dialog will
+* be used (refresh).
+* @param ss The SIP Session managing the REGISTER dialog.
+* @param ... Any TSIP_ACTION_SET_*() macros. e.g. @ref TSIP_ACTION_SET_HEADER().
+* MUST always ends with @ref TSIP_ACTION_SET_NULL().
+* @retval Zero if succeed and non-zero error code otherwise.
+* @sa @ref tsip_api_register_send_unregister
+*/
+int tsip_api_register_send_register(const tsip_ssession_handle_t *ss, ...)
+{
+ const tsip_ssession_t* _ss;
+ va_list ap;
+ tsip_action_t* action;
+ tsip_dialog_t* dialog;
+ int ret = -1;
+
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ /* performs action */
+ va_start(ap, ss);
+ if((action = _tsip_action_create(tsip_atype_register, &ap))){
+ if(!(dialog = tsip_dialog_layer_find_by_ss(_ss->stack->layer_dialog, _ss))){
+ dialog = tsip_dialog_layer_new(_ss->stack->layer_dialog, tsip_dialog_REGISTER, _ss);
+ }
+ ret = tsip_dialog_fsm_act(dialog, action->type, tsk_null, action);
+
+ TSK_OBJECT_SAFE_FREE(dialog);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+/**@ingroup tsip_action_group
+* Sends SIP unREGISTER request (expires=0). The session should be already established.
+* @param ss The SIP Session managing the REGISTER dialog.
+* @param ... Any TSIP_ACTION_SET_*() macros. e.g. @ref TSIP_ACTION_SET_HEADER().
+* MUST always ends with @ref TSIP_ACTION_SET_NULL().
+* @retval Zero if succeed and non-zero error code otherwise.
+* @sa @ref tsip_api_register_send_register
+*/
+int tsip_api_register_send_unregister(const tsip_ssession_handle_t *ss, ...)
+{
+ int ret = -1;
+ va_list ap;
+
+ va_start(ap, ss);
+ if((ret = _tsip_api_common_any(ss, tsip_atype_unregister, &ap))){
+ TSK_DEBUG_ERROR("unREGISTER() failed.");
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP REGISTER event object definition
+//
+static tsk_object_t* tsip_register_event_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_register_event_t *sipevent = self;
+ if(sipevent){
+ sipevent->type = va_arg(*app, tsip_register_event_type_t);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_register_event_dtor(tsk_object_t * self)
+{
+ tsip_register_event_t *sipevent = self;
+ if(sipevent){
+ tsip_event_deinit(TSIP_EVENT(sipevent));
+ }
+ return self;
+}
+
+static int tsip_register_event_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
+{
+ return -1;
+}
+
+static const tsk_object_def_t tsip_register_event_def_s =
+{
+ sizeof(tsip_register_event_t),
+ tsip_register_event_ctor,
+ tsip_register_event_dtor,
+ tsip_register_event_cmp,
+};
+const tsk_object_def_t *tsip_register_event_def_t = &tsip_register_event_def_s;
diff --git a/tinySIP/src/api/tsip_api_subscribe.c b/tinySIP/src/api/tsip_api_subscribe.c
new file mode 100644
index 0000000..867b258
--- /dev/null
+++ b/tinySIP/src/api/tsip_api_subscribe.c
@@ -0,0 +1,160 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_subscribe.c
+ * @brief Public subscription (SUBSCRIBE) functions.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/api/tsip_api_subscribe.h"
+
+#include "tinysip/dialogs/tsip_dialog_layer.h"
+#include "tinysip/dialogs/tsip_dialog_subscribe.h"
+
+#include "tinysip/tsip_action.h"
+#include "tsip.h"
+
+#include "tsk_runnable.h"
+#include "tsk_debug.h"
+
+#define TSIP_SUBSCRIBE_EVENT_CREATE( type) tsk_object_new(tsip_subscribe_event_def_t, type)
+
+extern tsip_action_t* _tsip_action_create(tsip_action_type_t type, va_list* app);
+
+int tsip_subscribe_event_signal(tsip_subscribe_event_type_t type, tsip_ssession_t* ss, short status_code, const char *phrase, const tsip_message_t* sipmessage)
+{
+ tsip_subscribe_event_t* sipevent = TSIP_SUBSCRIBE_EVENT_CREATE(type);
+ tsip_event_init(TSIP_EVENT(sipevent), ss, status_code, phrase, sipmessage, tsip_event_subscribe);
+
+ TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(TSIP_SSESSION(ss)->stack), sipevent);
+
+ return 0;
+}
+
+int tsip_api_subscribe_send_subscribe(const tsip_ssession_handle_t *ss, ...)
+{
+ const tsip_ssession_t* _ss;
+ va_list ap;
+ tsip_action_t* action;
+ tsip_dialog_t* dialog;
+ int ret = -1;
+
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack has been started */
+ if(!TSK_RUNNABLE(_ss->stack)->started){
+ TSK_DEBUG_ERROR("Stack not started.");
+ return -2;
+ }
+
+ va_start(ap, ss);
+ if((action = _tsip_action_create(tsip_atype_subscribe, &ap))){
+ if(!(dialog = tsip_dialog_layer_find_by_ss(_ss->stack->layer_dialog, ss))){
+ dialog = tsip_dialog_layer_new(_ss->stack->layer_dialog, tsip_dialog_SUBSCRIBE, ss);
+ }
+ ret = tsip_dialog_fsm_act(dialog, action->type, tsk_null, action);
+
+ tsk_object_unref(dialog);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+int tsip_api_subscribe_send_unsubscribe(const tsip_ssession_handle_t *ss, ...)
+{
+ const tsip_ssession_t* _ss;
+ va_list ap;
+ tsip_action_t* action;
+ int ret = -1;
+
+ if(!(_ss = ss) || !_ss->stack){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return ret;
+ }
+
+ /* Checks if the stack is running */
+ if(!TSK_RUNNABLE(_ss->stack)->running){
+ TSK_DEBUG_ERROR("Stack not running.");
+ return -2;
+ }
+
+ va_start(ap, ss);
+ if((action = _tsip_action_create(tsip_atype_unsubscribe, &ap))){
+ ret = tsip_ssession_handle(ss, action);
+ TSK_OBJECT_SAFE_FREE(action);
+ }
+ va_end(ap);
+
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP SUBSCRIBE event object definition
+//
+static tsk_object_t* tsip_subscribe_event_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_subscribe_event_t *sipevent = self;
+ if(sipevent){
+ sipevent->type = va_arg(*app, tsip_subscribe_event_type_t);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_subscribe_event_dtor(tsk_object_t * self)
+{
+ tsip_subscribe_event_t *sipevent = self;
+ if(sipevent){
+ tsip_event_deinit(TSIP_EVENT(sipevent));
+ }
+ return self;
+}
+
+static int tsip_subscribe_event_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
+{
+ return -1;
+}
+
+static const tsk_object_def_t tsip_subscribe_event_def_s =
+{
+ sizeof(tsip_subscribe_event_t),
+ tsip_subscribe_event_ctor,
+ tsip_subscribe_event_dtor,
+ tsip_subscribe_event_cmp,
+};
+const tsk_object_def_t *tsip_subscribe_event_def_t = &tsip_subscribe_event_def_s;
diff --git a/tinySIP/src/authentication/tsip_challenge.c b/tinySIP/src/authentication/tsip_challenge.c
new file mode 100644
index 0000000..bfe39ee
--- /dev/null
+++ b/tinySIP/src/authentication/tsip_challenge.c
@@ -0,0 +1,477 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_challenge.c
+ * @brief SIP authentication challenge.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/authentication/tsip_challenge.h"
+
+#include "tinysip/headers/tsip_header_Authorization.h"
+#include "tinysip/headers/tsip_header_Proxy_Authorization.h"
+
+#include "tsk_string.h"
+#include "tsk_debug.h"
+#include "tsk_memory.h"
+#include "tsk_base64.h"
+#include "tsk_hmac.h"
+
+#include <string.h>
+
+#define TSIP_CHALLENGE_IS_DIGEST(self) ((self) ? tsk_striequals((self)->scheme, "Digest") : 0)
+#define TSIP_CHALLENGE_IS_AKAv1(self) ((self) ? tsk_striequals((self)->algorithm, "AKAv1-MD5") : 0)
+#define TSIP_CHALLENGE_IS_AKAv2(self) ((self) ? tsk_striequals((self)->algorithm, "AKAv2-MD5") : 0)
+
+#define TSIP_CHALLENGE_STACK(self) (TSIP_STACK((self)->stack))
+#define TSIP_CHALLENGE_USERNAME(self) (self)->username
+#define TSIP_CHALLENGE_PASSWORD(self) TSIP_CHALLENGE_STACK(self)->identity.password
+
+
+/** Creates new challenge object. */
+tsip_challenge_t* tsip_challenge_create(tsip_stack_t* stack, tsk_bool_t isproxy, const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm, const char* qop)
+{
+ return tsk_object_new(tsip_challenge_def_t, stack, isproxy,scheme, realm, nonce, opaque, algorithm, qop);
+}
+
+/** Creates new challenge object (with default values). */
+tsip_challenge_t* tsip_challenge_create_null(tsip_stack_t* stack)
+{
+ return tsip_challenge_create(stack, tsk_false, tsk_null, tsk_null, tsk_null, tsk_null, tsk_null, tsk_null);
+}
+
+
+int tsip_challenge_reset_cnonce(tsip_challenge_t *self)
+{
+ if(self){
+ if(self->qop) /* client nonce is only used if qop=auth, auth-int or both */
+ {
+#if 0
+ memcpy(self->cnonce, "ecb1d3f6931803ce7ae68099cb946594", 32);
+#else
+ tsk_istr_t istr;
+
+ tsk_strrandom(&istr);
+ tsk_md5compute(istr, tsk_strlen(istr), &self->cnonce);
+#endif
+ self->nc = 1;
+ }
+ }
+ return -1;
+}
+
+//3GPP TS 35.205/6/7/8/9 and RFC 3310
+int tsip_challenge_get_akares(tsip_challenge_t *self, char const *password, char** result)
+{
+#define SQN_XOR_AK() (AUTN + 0)
+#define SERVER_DATA() (nonce + AKA_RAND_SIZE + AKA_AUTN_SIZE)
+
+ // § ==> XOR
+ // || ==> append
+
+ AKA_RES_T akares;
+
+ int ret = -1;
+ tsk_size_t n;
+ char *nonce = tsk_null;
+
+ AKA_XXX_DECLARE(RAND);
+ AKA_XXX_DECLARE(AK);
+ AKA_XXX_DECLARE(AMF);
+ AKA_XXX_DECLARE(CK);
+ AKA_XXX_DECLARE(IK);
+ AKA_XXX_DECLARE(K);
+ AKA_XXX_DECLARE(SQN);
+ AKA_XXX_DECLARE(MAC_A);
+ AKA_XXX_DECLARE(AUTN);
+
+ AKA_XXX_BZERO(RAND);
+ AKA_XXX_BZERO(AK);
+ AKA_XXX_BZERO(AMF);
+ AKA_XXX_BZERO(CK);
+ AKA_XXX_BZERO(IK);
+ AKA_XXX_BZERO(K);
+ AKA_XXX_BZERO(SQN);
+ AKA_XXX_BZERO(MAC_A);
+ AKA_XXX_BZERO(AUTN);
+
+ /* RFC 3310 subclause 3.2: nonce = base64(RAND || AUTN || SERV_DATA) */
+ n = tsk_base64_decode((const uint8_t*)self->nonce, tsk_strlen(self->nonce), &nonce);
+ if(n > TSK_MD5_STRING_SIZE){
+ TSK_DEBUG_ERROR("The IMS CORE returned an invalid nonce.");
+ goto bail;
+ }
+ if(n < AKA_RAND_SIZE + AKA_AUTN_SIZE){
+ TSK_DEBUG_ERROR("The nonce returned by the IMS CORE is too short to contain both [RAND] and [AUTHN]");
+ goto bail;
+ }
+ else{
+ /* Get RAND and AUTN */
+ memcpy(RAND, nonce, AKA_RAND_SIZE);
+ memcpy(AUTN, (nonce + AKA_RAND_SIZE), AKA_AUTN_SIZE);
+ }
+
+ /* Secret key */
+ memcpy(K, password, (tsk_strlen(password) > AKA_K_SIZE ? AKA_K_SIZE : tsk_strlen(password)));
+
+ /* 3GPP TS 35.205: AUTN = SQN[§AK] || AMF || MAC-A */
+ memcpy(AMF, (AUTN + AKA_SQN_SIZE), AKA_AMF_SIZE);
+ memcpy(MAC_A, (AUTN + AKA_SQN_SIZE + AKA_AMF_SIZE), AKA_MAC_A_SIZE);
+
+ /* compute OP */
+ ComputeOP(TSIP_CHALLENGE_STACK(self)->security.operator_id);
+
+ /* Checks that we hold the same AMF */
+ for(n=0; n<AKA_AMF_SIZE; n++){
+ if(AMF[n] != TSIP_CHALLENGE_STACK(self)->security.amf[n]){
+ TSK_DEBUG_ERROR("IMS-AKA error: AMF <> XAMF");
+ goto bail;
+ }
+ }
+
+ /* Calculate CK, IK and AK */
+ f2345(K, RAND, akares, CK, IK, AK);
+
+ /* Calculate SQN from SQN_XOR_AK */
+ for(n=0; n<AKA_SQN_SIZE; n++){
+ SQN[n] = (uint8_t) (SQN_XOR_AK()[n] ^ AK[n]);
+ }
+
+ /* Calculate XMAC_A */
+ {
+ AKA_MAC_A_T XMAC_A;
+ memset(XMAC_A, '\0', sizeof(XMAC_A));
+
+ f1(K, RAND, SQN, AMF, XMAC_A);
+ if(!tsk_strnequals(MAC_A, XMAC_A, AKA_MAC_A_SIZE)){
+ TSK_DEBUG_ERROR("IMS-AKA error: XMAC_A [%s] <> MAC_A[%s]", XMAC_A, MAC_A);
+ goto bail;
+ }
+ }
+
+ /* RFC 4169 subclause 3
+ The HTTP Digest password is derived from base64 encoded PRF(RES || IK||CK, "http-digest-akav2-password")
+ or
+ PRF(XRES||IK||CK, "http-digest-akav2-password") instead of (RES) or (XRES) respectively.
+ Where PRF ==> HMAC_MD5 function.
+ */
+ if(TSIP_CHALLENGE_IS_AKAv2(self)){
+ uint8_t res_ik_ck[AKA_RES_SIZE + AKA_IK_SIZE + AKA_CK_SIZE];
+ tsk_md5digest_t md5_digest;
+
+ memcpy(res_ik_ck, akares, AKA_RES_SIZE);
+ memcpy((res_ik_ck + AKA_RES_SIZE), IK, AKA_IK_SIZE);
+ memcpy((res_ik_ck + AKA_RES_SIZE + AKA_IK_SIZE), CK, AKA_CK_SIZE);
+
+ if((ret = hmac_md5digest_compute((const uint8_t*)"http-digest-akav2-password", 26, (const char*)res_ik_ck, sizeof(res_ik_ck), md5_digest))){/* PRF(RES||IK||CK, ...) */
+ TSK_DEBUG_ERROR("hmac_md5digest_compute() failed. AKAv2 response will be invalid.");
+
+ ret = -3;
+ goto bail;
+ }
+ else{/* b64(PRF(...)) */
+ if(!tsk_base64_encode(md5_digest, sizeof(md5_digest), result)){
+ TSK_DEBUG_ERROR("tsk_base64_encode() failed. AKAv2 response will be invalid.");
+
+ ret = -4;
+ goto bail;
+ }
+ }
+ }
+ else{
+ *result = tsk_calloc(1, AKA_RES_SIZE + 1);
+ memcpy(*result, akares, AKA_RES_SIZE);
+
+ ret = 0;
+ }
+
+ /* Copy CK and IK */
+ memcpy(self->ck, CK, AKA_CK_SIZE);
+ memcpy(self->ik, IK, AKA_IK_SIZE);
+
+bail:
+ TSK_FREE(nonce);
+ return ret;
+
+#undef SQN_XOR_AK
+#undef SERVER_DATA
+}
+
+int tsip_challenge_get_response(tsip_challenge_t *self, const char* method, const char* uristring, const tsk_buffer_t* entity_body, tsk_md5string_t* response)
+{
+ if(TSIP_CHALLENGE_IS_DIGEST(self) && self->stack){
+ tsk_md5string_t ha1, ha2;
+ nonce_count_t nc;
+
+ /* ===
+ Calculate HA1 = MD5(A1) = M5(username:realm:secret)
+ In case of AKAv1-MD5 and AKAv2-MD5 the secret must be computed as per RFC 3310 + 3GPP TS 206/7/8/9.
+ The resulting AKA RES parameter is treated as a "password"/"secret" when calculating the response directive of RFC 2617.
+ */
+ if(TSIP_CHALLENGE_IS_AKAv1(self) || TSIP_CHALLENGE_IS_AKAv2(self)){
+ char* akaresult = tsk_null;
+ tsip_challenge_get_akares(self, TSIP_CHALLENGE_STACK(self)->identity.password, &akaresult);
+ if(thttp_auth_digest_HA1(TSIP_CHALLENGE_USERNAME(self), self->realm, akaresult, &ha1)){
+ // return -1;
+ }
+ TSK_FREE(akaresult);
+ }
+ else{
+ if(!tsk_strnullORempty(self->ha1_hexstr)){
+ // use HA1 provide be the user (e.g. webrtc2sip server will need this to authenticate INVITEs when acting as b2bua)
+ memset(ha1, 0, sizeof(tsk_md5string_t));
+ memcpy(ha1, self->ha1_hexstr, (TSK_MD5_DIGEST_SIZE << 1));
+ }
+ else{
+ thttp_auth_digest_HA1(TSIP_CHALLENGE_USERNAME(self), self->realm, TSIP_CHALLENGE_STACK(self)->identity.password, &ha1);
+ }
+ }
+
+ /* ===
+ HA2
+ */
+ thttp_auth_digest_HA2(method,
+ uristring,
+ entity_body,
+ self->qop,
+ &ha2);
+
+ /* RESPONSE */
+ if(self->nc){
+ THTTP_NCOUNT_2_STRING(self->nc, nc);
+ }
+ thttp_auth_digest_response((const tsk_md5string_t *)&ha1,
+ self->nonce,
+ nc,
+ self->cnonce,
+ self->qop,
+ (const tsk_md5string_t *)&ha2,
+ response);
+
+ if(self->qop){
+ self->nc++;
+ }
+
+ return 0;
+ }
+ return -1;
+}
+
+int tsip_challenge_update(tsip_challenge_t *self, const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm, const char* qop)
+{
+ if(self){
+ int noncechanged = !tsk_striequals(self->nonce, nonce);
+
+ tsk_strupdate(&self->scheme, scheme);
+ tsk_strupdate(&self->realm, realm);
+ tsk_strupdate(&self->nonce, nonce);
+ tsk_strupdate(&self->opaque, opaque);
+ tsk_strupdate(&self->algorithm, algorithm);
+ if(qop){
+ self->qop = tsk_strcontains(qop, tsk_strlen(qop), "auth-int") ? "auth-int" :
+ (tsk_strcontains(qop, tsk_strlen(qop), "auth") ? "auth" : tsk_null);
+ }
+
+ if(noncechanged && self->qop){
+ tsip_challenge_reset_cnonce(self);
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int tsip_challenge_set_cred(tsip_challenge_t *self, const char* username, const char* ha1_hexstr)
+{
+ if(!self || tsk_strlen(ha1_hexstr) != (TSK_MD5_DIGEST_SIZE << 1)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ tsk_strupdate(&self->username, username);
+ tsk_strupdate(&self->ha1_hexstr, ha1_hexstr);
+ return 0;
+}
+
+tsip_header_t *tsip_challenge_create_header_authorization(tsip_challenge_t *self, const tsip_request_t *request)
+{
+ tsk_md5string_t response;
+ nonce_count_t nc;
+ char *uristring = tsk_null;
+ tsip_header_t *header = tsk_null;
+
+ if(!self || !self->stack || !request){
+ goto bail;
+ }
+
+ if(!(uristring = tsip_uri_tostring(request->line.request.uri, tsk_true, tsk_false))){
+ TSK_DEBUG_ERROR("Failed to parse URI: %s", uristring);
+ goto bail;
+ }
+
+ /* We compute the nc here because @ref tsip_challenge_get_response function will increment it's value. */
+ if(self->nc){
+ THTTP_NCOUNT_2_STRING(self->nc, nc);
+ }
+
+ /* entity_body ==> request-content */
+ if(tsip_challenge_get_response(self, request->line.request.method, uristring, request->Content, &response)){
+ goto bail;
+ }
+
+
+#define TSIP_AUTH_COPY_VALUES(hdr) \
+ hdr->username = tsk_strdup(TSIP_CHALLENGE_USERNAME(self)); \
+ hdr->scheme = tsk_strdup(self->scheme); \
+ hdr->realm = tsk_strdup(self->realm); \
+ hdr->nonce = tsk_strdup(self->nonce); \
+ hdr->qop = tsk_strdup(self->qop); \
+ hdr->opaque = tsk_strdup(self->opaque); \
+ hdr->algorithm = self->algorithm ? tsk_strdup(self->algorithm) : tsk_strdup("MD5"); \
+ hdr->cnonce = self->nc? tsk_strdup(self->cnonce) : tsk_null; \
+ hdr->uri = tsk_strdup(uristring); \
+ hdr->nc = self->nc? tsk_strdup(nc) : 0; \
+ hdr->response = tsk_strdup(response); \
+
+ if(self->isproxy){
+ tsip_header_Proxy_Authorization_t *proxy_auth = tsip_header_Proxy_Authorization_create();
+ TSIP_AUTH_COPY_VALUES(proxy_auth);
+ header = TSIP_HEADER(proxy_auth);
+ }
+ else{
+ tsip_header_Authorization_t *auth = tsip_header_Authorization_create();
+ TSIP_AUTH_COPY_VALUES(auth);
+ header = TSIP_HEADER(auth);
+ }
+
+bail:
+ TSK_FREE(uristring);
+
+ return header;
+
+#undef TSIP_AUTH_COPY_VALUES
+}
+
+tsip_header_t *tsip_challenge_create_empty_header_authorization(const char* username, const char* realm, const char* uristring)
+{
+ tsip_header_Authorization_t *header = tsip_header_Authorization_create();
+
+ if(header){
+ header->scheme = tsk_strdup("Digest");
+ header->username = tsk_strdup(username);
+ header->realm = tsk_strdup(realm);
+ header->nonce = tsk_strdup("");
+ header->response = tsk_strdup("");
+ header->uri = tsk_strdup(uristring);
+ }
+
+ return TSIP_HEADER(header);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP challenge object definition
+//
+
+/**@ingroup tsip_challenge_group
+*/
+static tsk_object_t* tsip_challenge_ctor(tsk_object_t *self, va_list * app)
+{
+ tsip_challenge_t *challenge = self;
+ if(challenge){
+ const char* qop;
+
+ challenge->stack = va_arg(*app, const tsip_stack_handle_t *);
+ challenge->isproxy = va_arg(*app, tsk_bool_t);
+ challenge->username = tsk_strdup(((const struct tsip_stack_s*)challenge->stack)->identity.impi);
+ challenge->scheme = tsk_strdup(va_arg(*app, const char*));
+ challenge->realm = tsk_strdup(va_arg(*app, const char*));
+ challenge->nonce = tsk_strdup(va_arg(*app, const char*));
+ challenge->opaque = tsk_strdup(va_arg(*app, const char*));
+ challenge->algorithm = tsk_strdup(va_arg(*app, const char*));
+ qop = va_arg(*app, const char*);
+ if(qop){
+ challenge->qop = tsk_strcontains(qop, tsk_strlen(qop), "auth-int") ? "auth-int" :
+ (tsk_strcontains(qop, tsk_strlen(qop), "auth") ? "auth" : tsk_null);
+ }
+
+ if(challenge->qop){
+ tsip_challenge_reset_cnonce(challenge);
+ }
+ }
+ else TSK_DEBUG_ERROR("Failed to create new sip challenge object.");
+
+ return self;
+}
+
+/**@ingroup tsip_challenge_group
+*/
+static tsk_object_t* tsip_challenge_dtor(tsk_object_t *self)
+{
+ tsip_challenge_t *challenge = self;
+ if(challenge){
+ TSK_FREE(challenge->username);
+ TSK_FREE(challenge->scheme);
+ TSK_FREE(challenge->realm);
+ TSK_FREE(challenge->nonce);
+ TSK_FREE(challenge->opaque);
+ TSK_FREE(challenge->algorithm);
+ TSK_FREE(challenge->ha1_hexstr);
+ }
+ else{
+ TSK_DEBUG_ERROR("Null SIP challenge object.");
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tsip_challenge_def_s =
+{
+ sizeof(tsip_challenge_t),
+ tsip_challenge_ctor,
+ tsip_challenge_dtor,
+ tsk_null
+};
+const tsk_object_def_t *tsip_challenge_def_t = &tsip_challenge_def_s;
diff --git a/tinySIP/src/authentication/tsip_milenage.c b/tinySIP/src/authentication/tsip_milenage.c
new file mode 100644
index 0000000..3fd19e2
--- /dev/null
+++ b/tinySIP/src/authentication/tsip_milenage.c
@@ -0,0 +1,350 @@
+/*
+* Partial Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_milenage.c
+* @brief 3GPP authentication and key agreement functions f1, f1*, f2, f3, f4, f5 and f5*.
+*
+* @section DESCRIPTION
+*
+* @sa 3G Security
+* <a href="http://www.3gpp.org/ftp/Specs/html-info/35205.htm"> 3GPP TS 35.205 </a>
+* <a href="http://www.3gpp.org/ftp/Specs/html-info/35206.htm"> 3GPP TS 35.206 </a>
+* <a href="http://www.3gpp.org/ftp/Specs/html-info/35207.htm"> 3GPP TS 35.207 </a>
+* <a href="http://www.3gpp.org/ftp/Specs/html-info/35208.htm"> 3GPP TS 35.208 </a>
+* <a href="http://www.3gpp.org/ftp/Specs/html-info/35909.htm"> 3GPP TS 35.909 </a>
+*-------------------------------------------------------------------
+* Example algorithms f1, f1*, f2, f3, f4, f5, f5*
+*-------------------------------------------------------------------
+*
+* A sample implementation of the example 3GPP authentication and
+* key agreement functions f1, f1*, f2, f3, f4, f5 and f5*. This is
+* a byte-oriented implementation of the functions, and of the block
+* cipher kernel function Rijndael.
+*
+* This has been coded for clarity, not necessarily for efficiency.
+*
+* The functions f2, f3, f4 and f5 share the same inputs and have
+* been coded together as a single function. f1, f1* and f5* are
+* all coded separately.
+*
+*-----------------------------------------------------------------
+*
+*/
+
+#include "tinysip/authentication/tsip_milenage.h"
+#include "tinysip/authentication/tsip_rijndael.h"
+
+/*--------- Operator Variant Algorithm Configuration Field --------*/
+
+/*------- Insert your value of OP here -------*/
+//uint8_t OP[16] = {0x63, 0xbf, 0xa5, 0x0e, 0xe6, 0x52, 0x33, 0x65,
+// 0xff, 0x14, 0xc1, 0xf4, 0x5f, 0x88, 0x73, 0x7d};
+/*------- Insert your value of OP here -------*/
+uint8_t OP[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+/*-------------------------------------------------------------------
+* Algorithm f1
+*-------------------------------------------------------------------
+*
+* Computes network authentication code MAC-A from key K, random
+* challenge RAND, sequence number SQN and authentication management
+* field AMF.
+*
+*-----------------------------------------------------------------*/
+
+void f1 ( uint8_t k[16], uint8_t rand[16], uint8_t sqn[6], uint8_t amf[2],
+ uint8_t mac_a[8] )
+{
+ uint8_t op_c[16];
+ uint8_t temp[16];
+ uint8_t in1[16];
+ uint8_t out1[16];
+ uint8_t rijndaelInput[16];
+ uint8_t i;
+
+ RijndaelKeySchedule( k );
+
+ ComputeOPc( op_c );
+
+ for (i=0; i<16; i++){
+ rijndaelInput[i] = rand[i] ^ op_c[i];
+ }
+ RijndaelEncrypt( rijndaelInput, temp );
+
+ for (i=0; i<6; i++){
+ in1[i] = sqn[i];
+ in1[i+8] = sqn[i];
+ }
+
+ for (i=0; i<2; i++){
+ in1[i+6] = amf[i];
+ in1[i+14] = amf[i];
+ }
+
+ /* XOR op_c and in1, rotate by r1=64, and XOR *
+ * on the constant c1 (which is all zeroes) */
+
+ for (i=0; i<16; i++){
+ rijndaelInput[(i+8) % 16] = in1[i] ^ op_c[i];
+ }
+
+ /* XOR on the value temp computed before */
+
+ for (i=0; i<16; i++){
+ rijndaelInput[i] ^= temp[i];
+ }
+
+ RijndaelEncrypt( rijndaelInput, out1 );
+ for (i=0; i<16; i++){
+ out1[i] ^= op_c[i];
+ }
+
+ for (i=0; i<8; i++){
+ mac_a[i] = out1[i];
+ }
+
+ return;
+} /* end of function f1 */
+
+
+
+/*-------------------------------------------------------------------
+* Algorithms f2-f5
+*-------------------------------------------------------------------
+*
+* Takes key K and random challenge RAND, and returns response RES,
+* confidentiality key CK, integrity key IK and anonymity key AK.
+*
+*-----------------------------------------------------------------*/
+
+void f2345 ( uint8_t k[16], uint8_t rand[16],
+ uint8_t res[8], uint8_t ck[16], uint8_t ik[16], uint8_t ak[6] )
+{
+ uint8_t op_c[16];
+ uint8_t temp[16];
+ uint8_t out[16];
+ uint8_t rijndaelInput[16];
+ uint8_t i;
+
+ RijndaelKeySchedule( k );
+
+ ComputeOPc( op_c );
+
+ for (i=0; i<16; i++){
+ rijndaelInput[i] = rand[i] ^ op_c[i];
+ }
+ RijndaelEncrypt( rijndaelInput, temp );
+
+ /* To obtain output block OUT2: XOR OPc and TEMP, *
+ * rotate by r2=0, and XOR on the constant c2 (which *
+ * is all zeroes except that the last bit is 1). */
+
+ for (i=0; i<16; i++){
+ rijndaelInput[i] = temp[i] ^ op_c[i];
+ }
+ rijndaelInput[15] ^= 1;
+
+ RijndaelEncrypt( rijndaelInput, out );
+ for (i=0; i<16; i++){
+ out[i] ^= op_c[i];
+ }
+
+ for (i=0; i<8; i++){
+ res[i] = out[i+8];
+ }
+ for (i=0; i<6; i++){
+ ak[i] = out[i];
+ }
+
+ /* To obtain output block OUT3: XOR OPc and TEMP, *
+ * rotate by r3=32, and XOR on the constant c3 (which *
+ * is all zeroes except that the next to last bit is 1). */
+
+ for (i=0; i<16; i++){
+ rijndaelInput[(i+12) % 16] = temp[i] ^ op_c[i];
+ }
+ rijndaelInput[15] ^= 2;
+
+ RijndaelEncrypt( rijndaelInput, out );
+ for (i=0; i<16; i++){
+ out[i] ^= op_c[i];
+ }
+
+ for (i=0; i<16; i++){
+ ck[i] = out[i];
+ }
+
+ /* To obtain output block OUT4: XOR OPc and TEMP, *
+ * rotate by r4=64, and XOR on the constant c4 (which *
+ * is all zeroes except that the 2nd from last bit is 1). */
+
+ for (i=0; i<16; i++){
+ rijndaelInput[(i+8) % 16] = temp[i] ^ op_c[i];
+ }
+ rijndaelInput[15] ^= 4;
+
+ RijndaelEncrypt( rijndaelInput, out );
+ for (i=0; i<16; i++){
+ out[i] ^= op_c[i];
+ }
+
+ for (i=0; i<16; i++){
+ ik[i] = out[i];
+ }
+
+ return;
+} /* end of function f2345 */
+
+
+/*-------------------------------------------------------------------
+* Algorithm f1*
+*-------------------------------------------------------------------
+*
+* Computes resynch authentication code MAC-S from key K, random
+* challenge RAND, sequence number SQN and authentication management
+* field AMF.
+*
+*-----------------------------------------------------------------*/
+
+void f1star( uint8_t k[16], uint8_t rand[16], uint8_t sqn[6], uint8_t amf[2],
+ uint8_t mac_s[8] )
+{
+ uint8_t op_c[16];
+ uint8_t temp[16];
+ uint8_t in1[16];
+ uint8_t out1[16];
+ uint8_t rijndaelInput[16];
+ uint8_t i;
+
+ RijndaelKeySchedule( k );
+
+ ComputeOPc( op_c );
+
+ for (i=0; i<16; i++){
+ rijndaelInput[i] = rand[i] ^ op_c[i];
+ }
+ RijndaelEncrypt( rijndaelInput, temp );
+
+ for (i=0; i<6; i++){
+ in1[i] = sqn[i];
+ in1[i+8] = sqn[i];
+ }
+ for (i=0; i<2; i++){
+ in1[i+6] = amf[i];
+ in1[i+14] = amf[i];
+ }
+
+ /* XOR op_c and in1, rotate by r1=64, and XOR *
+ * on the constant c1 (which is all zeroes) */
+
+ for (i=0; i<16; i++){
+ rijndaelInput[(i+8) % 16] = in1[i] ^ op_c[i];
+ }
+
+ /* XOR on the value temp computed before */
+
+ for (i=0; i<16; i++){
+ rijndaelInput[i] ^= temp[i];
+ }
+
+ RijndaelEncrypt( rijndaelInput, out1 );
+ for (i=0; i<16; i++){
+ out1[i] ^= op_c[i];
+ }
+
+ for (i=0; i<8; i++){
+ mac_s[i] = out1[i+8];
+ }
+
+ return;
+} /* end of function f1star */
+
+
+/*-------------------------------------------------------------------
+* Algorithm f5*
+*-------------------------------------------------------------------
+*
+* Takes key K and random challenge RAND, and returns resynch
+* anonymity key AK.
+*
+*-----------------------------------------------------------------*/
+
+void f5star( uint8_t k[16], uint8_t rand[16],
+ uint8_t ak[6] )
+{
+ uint8_t op_c[16];
+ uint8_t temp[16];
+ uint8_t out[16];
+ uint8_t rijndaelInput[16];
+ uint8_t i;
+
+ RijndaelKeySchedule( k );
+
+ ComputeOPc( op_c );
+
+ for (i=0; i<16; i++)
+ rijndaelInput[i] = rand[i] ^ op_c[i];
+ RijndaelEncrypt( rijndaelInput, temp );
+
+ /* To obtain output block OUT5: XOR OPc and TEMP, *
+ * rotate by r5=96, and XOR on the constant c5 (which *
+ * is all zeroes except that the 3rd from last bit is 1). */
+
+ for (i=0; i<16; i++)
+ rijndaelInput[(i+4) % 16] = temp[i] ^ op_c[i];
+ rijndaelInput[15] ^= 8;
+
+ RijndaelEncrypt( rijndaelInput, out );
+ for (i=0; i<16; i++)
+ out[i] ^= op_c[i];
+
+ for (i=0; i<6; i++)
+ ak[i] = out[i];
+
+ return;
+} /* end of function f5star */
+
+
+/*-------------------------------------------------------------------
+* Function to compute OPc from OP and K. Assumes key schedule has
+already been performed.
+*-----------------------------------------------------------------*/
+
+void ComputeOPc( uint8_t op_c[16] )
+{
+ uint8_t i;
+
+ RijndaelEncrypt( OP, op_c );
+ for (i=0; i<16; i++){
+ op_c[i] ^= OP[i];
+ }
+
+ return;
+} /* end of function ComputeOPc */
+
+void ComputeOP( uint8_t op[16] ){
+ int i;
+ for(i=0;i<16;i++){
+ OP[i]=op[i];
+ }
+}
diff --git a/tinySIP/src/authentication/tsip_rijndael.c b/tinySIP/src/authentication/tsip_rijndael.c
new file mode 100644
index 0000000..ddcd89f
--- /dev/null
+++ b/tinySIP/src/authentication/tsip_rijndael.c
@@ -0,0 +1,480 @@
+/*
+* Partial Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_rijndael.c
+ * @brief Rijndael Implementation.
+ *
+
+ * @section DESCRIPTION
+ *
+ * @sa 3G Security
+ * <a href="http://www.3gpp.org/ftp/Specs/html-info/35205.htm"> 3GPP TS 35.205 </a>
+ * <a href="http://www.3gpp.org/ftp/Specs/html-info/35206.htm"> 3GPP TS 35.206 </a>
+ * <a href="http://www.3gpp.org/ftp/Specs/html-info/35207.htm"> 3GPP TS 35.208 </a>
+ * <a href="http://www.3gpp.org/ftp/Specs/html-info/35208.htm"> 3GPP TS 35.208 </a>
+ * <a href="http://www.3gpp.org/ftp/Specs/html-info/35909.htm"> 3GPP TS 35.909 </a>
+ *-------------------------------------------------------------------
+ * Rijndael Implementation
+ *-------------------------------------------------------------------
+ *
+ * A sample 32-bit orientated implementation of Rijndael, the
+ * suggested kernel for the example 3GPP authentication and key
+ * agreement functions.
+ *
+ * This implementation draws on the description in section 5.2 of
+ * the AES proposal and also on the implementation by
+ * Dr B. R. Gladman <brg@gladman.uk.net> 9th October 2000.
+ * It uses a number of large (4k) lookup tables to implement the
+ * algorithm in an efficient manner.
+ *
+ * Note: in this implementation the State is stored in four 32-bit
+ * words, one per column of the State, with the top byte of the
+ * column being the _least_ significant byte of the word.
+ *
+ *-----------------------------------------------------------------
+ *
+ */
+#include "tinysip/authentication/tsip_rijndael.h"
+
+#include "tnet_endianness.h"
+
+/* Circular byte rotates of 32 bit values */
+
+#define rot1(x) ((x << 8) | (x >> 24))
+#define rot2(x) ((x << 16) | (x >> 16))
+#define rot3(x) ((x << 24) | (x >> 8))
+
+/* Extract a byte from a 32-bit uint32_t */
+
+#define byte0(x) ((uint8_t)(x))
+#define byte1(x) ((uint8_t)(x >> 8))
+#define byte2(x) ((uint8_t)(x >> 16))
+#define byte3(x) ((uint8_t)(x >> 24))
+
+
+/* Put or get a 32 bit uint32_t (v) in machine order from a byte *
+ * address in (x) */
+
+#if defined(_MSC_VER)
+# define __INLINE __forceinline
+#elif defined(__GNUC__) && !defined(__APPLE__)
+# define __INLINE __inline
+#else
+# define __INLINE
+#endif
+
+__INLINE uint32_t byte_swap(uint32_t x){
+ return rot1(x) & 0x00ff00ff | rot3(x) & 0xff00ff00;
+}
+
+__INLINE uint32_t u32_in(const uint8_t x[]){
+ if(tnet_is_BE()){
+ return byte_swap(*(uint32_t*)x);
+ }
+ else{
+ return (*(uint32_t*)(x));
+ }
+}
+
+__INLINE void u32_out(uint8_t x[], const uint32_t v){
+ if(tnet_is_BE()){
+ *(uint32_t*)x = byte_swap(v);
+ }
+ else{
+ (*(uint32_t*)(x) = v);
+ }
+}
+
+/*--------------- The lookup tables ----------------------------*/
+
+static uint32_t rnd_con[10] =
+{
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
+};
+
+static uint32_t ft_tab[4][256] =
+{
+ {
+ 0xA56363C6,0x847C7CF8,0x997777EE,0x8D7B7BF6,0x0DF2F2FF,0xBD6B6BD6,0xB16F6FDE,0x54C5C591,
+ 0x50303060,0x03010102,0xA96767CE,0x7D2B2B56,0x19FEFEE7,0x62D7D7B5,0xE6ABAB4D,0x9A7676EC,
+ 0x45CACA8F,0x9D82821F,0x40C9C989,0x877D7DFA,0x15FAFAEF,0xEB5959B2,0xC947478E,0x0BF0F0FB,
+ 0xECADAD41,0x67D4D4B3,0xFDA2A25F,0xEAAFAF45,0xBF9C9C23,0xF7A4A453,0x967272E4,0x5BC0C09B,
+ 0xC2B7B775,0x1CFDFDE1,0xAE93933D,0x6A26264C,0x5A36366C,0x413F3F7E,0x02F7F7F5,0x4FCCCC83,
+ 0x5C343468,0xF4A5A551,0x34E5E5D1,0x08F1F1F9,0x937171E2,0x73D8D8AB,0x53313162,0x3F15152A,
+ 0x0C040408,0x52C7C795,0x65232346,0x5EC3C39D,0x28181830,0xA1969637,0x0F05050A,0xB59A9A2F,
+ 0x0907070E,0x36121224,0x9B80801B,0x3DE2E2DF,0x26EBEBCD,0x6927274E,0xCDB2B27F,0x9F7575EA,
+ 0x1B090912,0x9E83831D,0x742C2C58,0x2E1A1A34,0x2D1B1B36,0xB26E6EDC,0xEE5A5AB4,0xFBA0A05B,
+ 0xF65252A4,0x4D3B3B76,0x61D6D6B7,0xCEB3B37D,0x7B292952,0x3EE3E3DD,0x712F2F5E,0x97848413,
+ 0xF55353A6,0x68D1D1B9,0000000000,0x2CEDEDC1,0x60202040,0x1FFCFCE3,0xC8B1B179,0xED5B5BB6,
+ 0xBE6A6AD4,0x46CBCB8D,0xD9BEBE67,0x4B393972,0xDE4A4A94,0xD44C4C98,0xE85858B0,0x4ACFCF85,
+ 0x6BD0D0BB,0x2AEFEFC5,0xE5AAAA4F,0x16FBFBED,0xC5434386,0xD74D4D9A,0x55333366,0x94858511,
+ 0xCF45458A,0x10F9F9E9,0x06020204,0x817F7FFE,0xF05050A0,0x443C3C78,0xBA9F9F25,0xE3A8A84B,
+ 0xF35151A2,0xFEA3A35D,0xC0404080,0x8A8F8F05,0xAD92923F,0xBC9D9D21,0x48383870,0x04F5F5F1,
+ 0xDFBCBC63,0xC1B6B677,0x75DADAAF,0x63212142,0x30101020,0x1AFFFFE5,0x0EF3F3FD,0x6DD2D2BF,
+ 0x4CCDCD81,0x140C0C18,0x35131326,0x2FECECC3,0xE15F5FBE,0xA2979735,0xCC444488,0x3917172E,
+ 0x57C4C493,0xF2A7A755,0x827E7EFC,0x473D3D7A,0xAC6464C8,0xE75D5DBA,0x2B191932,0x957373E6,
+ 0xA06060C0,0x98818119,0xD14F4F9E,0x7FDCDCA3,0x66222244,0x7E2A2A54,0xAB90903B,0x8388880B,
+ 0xCA46468C,0x29EEEEC7,0xD3B8B86B,0x3C141428,0x79DEDEA7,0xE25E5EBC,0x1D0B0B16,0x76DBDBAD,
+ 0x3BE0E0DB,0x56323264,0x4E3A3A74,0x1E0A0A14,0xDB494992,0x0A06060C,0x6C242448,0xE45C5CB8,
+ 0x5DC2C29F,0x6ED3D3BD,0xEFACAC43,0xA66262C4,0xA8919139,0xA4959531,0x37E4E4D3,0x8B7979F2,
+ 0x32E7E7D5,0x43C8C88B,0x5937376E,0xB76D6DDA,0x8C8D8D01,0x64D5D5B1,0xD24E4E9C,0xE0A9A949,
+ 0xB46C6CD8,0xFA5656AC,0x07F4F4F3,0x25EAEACF,0xAF6565CA,0x8E7A7AF4,0xE9AEAE47,0x18080810,
+ 0xD5BABA6F,0x887878F0,0x6F25254A,0x722E2E5C,0x241C1C38,0xF1A6A657,0xC7B4B473,0x51C6C697,
+ 0x23E8E8CB,0x7CDDDDA1,0x9C7474E8,0x211F1F3E,0xDD4B4B96,0xDCBDBD61,0x868B8B0D,0x858A8A0F,
+ 0x907070E0,0x423E3E7C,0xC4B5B571,0xAA6666CC,0xD8484890,0x05030306,0x01F6F6F7,0x120E0E1C,
+ 0xA36161C2,0x5F35356A,0xF95757AE,0xD0B9B969,0x91868617,0x58C1C199,0x271D1D3A,0xB99E9E27,
+ 0x38E1E1D9,0x13F8F8EB,0xB398982B,0x33111122,0xBB6969D2,0x70D9D9A9,0x898E8E07,0xA7949433,
+ 0xB69B9B2D,0x221E1E3C,0x92878715,0x20E9E9C9,0x49CECE87,0xFF5555AA,0x78282850,0x7ADFDFA5,
+ 0x8F8C8C03,0xF8A1A159,0x80898909,0x170D0D1A,0xDABFBF65,0x31E6E6D7,0xC6424284,0xB86868D0,
+ 0xC3414182,0xB0999929,0x772D2D5A,0x110F0F1E,0xCBB0B07B,0xFC5454A8,0xD6BBBB6D,0x3A16162C
+ },
+ {
+ 0x6363C6A5,0x7C7CF884,0x7777EE99,0x7B7BF68D,0xF2F2FF0D,0x6B6BD6BD,0x6F6FDEB1,0xC5C59154,
+ 0x30306050,0x01010203,0x6767CEA9,0x2B2B567D,0xFEFEE719,0xD7D7B562,0xABAB4DE6,0x7676EC9A,
+ 0xCACA8F45,0x82821F9D,0xC9C98940,0x7D7DFA87,0xFAFAEF15,0x5959B2EB,0x47478EC9,0xF0F0FB0B,
+ 0xADAD41EC,0xD4D4B367,0xA2A25FFD,0xAFAF45EA,0x9C9C23BF,0xA4A453F7,0x7272E496,0xC0C09B5B,
+ 0xB7B775C2,0xFDFDE11C,0x93933DAE,0x26264C6A,0x36366C5A,0x3F3F7E41,0xF7F7F502,0xCCCC834F,
+ 0x3434685C,0xA5A551F4,0xE5E5D134,0xF1F1F908,0x7171E293,0xD8D8AB73,0x31316253,0x15152A3F,
+ 0x0404080C,0xC7C79552,0x23234665,0xC3C39D5E,0x18183028,0x969637A1,0x05050A0F,0x9A9A2FB5,
+ 0x07070E09,0x12122436,0x80801B9B,0xE2E2DF3D,0xEBEBCD26,0x27274E69,0xB2B27FCD,0x7575EA9F,
+ 0x0909121B,0x83831D9E,0x2C2C5874,0x1A1A342E,0x1B1B362D,0x6E6EDCB2,0x5A5AB4EE,0xA0A05BFB,
+ 0x5252A4F6,0x3B3B764D,0xD6D6B761,0xB3B37DCE,0x2929527B,0xE3E3DD3E,0x2F2F5E71,0x84841397,
+ 0x5353A6F5,0xD1D1B968,0000000000,0xEDEDC12C,0x20204060,0xFCFCE31F,0xB1B179C8,0x5B5BB6ED,
+ 0x6A6AD4BE,0xCBCB8D46,0xBEBE67D9,0x3939724B,0x4A4A94DE,0x4C4C98D4,0x5858B0E8,0xCFCF854A,
+ 0xD0D0BB6B,0xEFEFC52A,0xAAAA4FE5,0xFBFBED16,0x434386C5,0x4D4D9AD7,0x33336655,0x85851194,
+ 0x45458ACF,0xF9F9E910,0x02020406,0x7F7FFE81,0x5050A0F0,0x3C3C7844,0x9F9F25BA,0xA8A84BE3,
+ 0x5151A2F3,0xA3A35DFE,0x404080C0,0x8F8F058A,0x92923FAD,0x9D9D21BC,0x38387048,0xF5F5F104,
+ 0xBCBC63DF,0xB6B677C1,0xDADAAF75,0x21214263,0x10102030,0xFFFFE51A,0xF3F3FD0E,0xD2D2BF6D,
+ 0xCDCD814C,0x0C0C1814,0x13132635,0xECECC32F,0x5F5FBEE1,0x979735A2,0x444488CC,0x17172E39,
+ 0xC4C49357,0xA7A755F2,0x7E7EFC82,0x3D3D7A47,0x6464C8AC,0x5D5DBAE7,0x1919322B,0x7373E695,
+ 0x6060C0A0,0x81811998,0x4F4F9ED1,0xDCDCA37F,0x22224466,0x2A2A547E,0x90903BAB,0x88880B83,
+ 0x46468CCA,0xEEEEC729,0xB8B86BD3,0x1414283C,0xDEDEA779,0x5E5EBCE2,0x0B0B161D,0xDBDBAD76,
+ 0xE0E0DB3B,0x32326456,0x3A3A744E,0x0A0A141E,0x494992DB,0x06060C0A,0x2424486C,0x5C5CB8E4,
+ 0xC2C29F5D,0xD3D3BD6E,0xACAC43EF,0x6262C4A6,0x919139A8,0x959531A4,0xE4E4D337,0x7979F28B,
+ 0xE7E7D532,0xC8C88B43,0x37376E59,0x6D6DDAB7,0x8D8D018C,0xD5D5B164,0x4E4E9CD2,0xA9A949E0,
+ 0x6C6CD8B4,0x5656ACFA,0xF4F4F307,0xEAEACF25,0x6565CAAF,0x7A7AF48E,0xAEAE47E9,0x08081018,
+ 0xBABA6FD5,0x7878F088,0x25254A6F,0x2E2E5C72,0x1C1C3824,0xA6A657F1,0xB4B473C7,0xC6C69751,
+ 0xE8E8CB23,0xDDDDA17C,0x7474E89C,0x1F1F3E21,0x4B4B96DD,0xBDBD61DC,0x8B8B0D86,0x8A8A0F85,
+ 0x7070E090,0x3E3E7C42,0xB5B571C4,0x6666CCAA,0x484890D8,0x03030605,0xF6F6F701,0x0E0E1C12,
+ 0x6161C2A3,0x35356A5F,0x5757AEF9,0xB9B969D0,0x86861791,0xC1C19958,0x1D1D3A27,0x9E9E27B9,
+ 0xE1E1D938,0xF8F8EB13,0x98982BB3,0x11112233,0x6969D2BB,0xD9D9A970,0x8E8E0789,0x949433A7,
+ 0x9B9B2DB6,0x1E1E3C22,0x87871592,0xE9E9C920,0xCECE8749,0x5555AAFF,0x28285078,0xDFDFA57A,
+ 0x8C8C038F,0xA1A159F8,0x89890980,0x0D0D1A17,0xBFBF65DA,0xE6E6D731,0x424284C6,0x6868D0B8,
+ 0x414182C3,0x999929B0,0x2D2D5A77,0x0F0F1E11,0xB0B07BCB,0x5454A8FC,0xBBBB6DD6,0x16162C3A
+ },
+ {
+ 0x63C6A563,0x7CF8847C,0x77EE9977,0x7BF68D7B,0xF2FF0DF2,0x6BD6BD6B,0x6FDEB16F,0xC59154C5,
+ 0x30605030,0x01020301,0x67CEA967,0x2B567D2B,0xFEE719FE,0xD7B562D7,0xAB4DE6AB,0x76EC9A76,
+ 0xCA8F45CA,0x821F9D82,0xC98940C9,0x7DFA877D,0xFAEF15FA,0x59B2EB59,0x478EC947,0xF0FB0BF0,
+ 0xAD41ECAD,0xD4B367D4,0xA25FFDA2,0xAF45EAAF,0x9C23BF9C,0xA453F7A4,0x72E49672,0xC09B5BC0,
+ 0xB775C2B7,0xFDE11CFD,0x933DAE93,0x264C6A26,0x366C5A36,0x3F7E413F,0xF7F502F7,0xCC834FCC,
+ 0x34685C34,0xA551F4A5,0xE5D134E5,0xF1F908F1,0x71E29371,0xD8AB73D8,0x31625331,0x152A3F15,
+ 0x04080C04,0xC79552C7,0x23466523,0xC39D5EC3,0x18302818,0x9637A196,0x050A0F05,0x9A2FB59A,
+ 0x070E0907,0x12243612,0x801B9B80,0xE2DF3DE2,0xEBCD26EB,0x274E6927,0xB27FCDB2,0x75EA9F75,
+ 0x09121B09,0x831D9E83,0x2C58742C,0x1A342E1A,0x1B362D1B,0x6EDCB26E,0x5AB4EE5A,0xA05BFBA0,
+ 0x52A4F652,0x3B764D3B,0xD6B761D6,0xB37DCEB3,0x29527B29,0xE3DD3EE3,0x2F5E712F,0x84139784,
+ 0x53A6F553,0xD1B968D1,0000000000,0xEDC12CED,0x20406020,0xFCE31FFC,0xB179C8B1,0x5BB6ED5B,
+ 0x6AD4BE6A,0xCB8D46CB,0xBE67D9BE,0x39724B39,0x4A94DE4A,0x4C98D44C,0x58B0E858,0xCF854ACF,
+ 0xD0BB6BD0,0xEFC52AEF,0xAA4FE5AA,0xFBED16FB,0x4386C543,0x4D9AD74D,0x33665533,0x85119485,
+ 0x458ACF45,0xF9E910F9,0x02040602,0x7FFE817F,0x50A0F050,0x3C78443C,0x9F25BA9F,0xA84BE3A8,
+ 0x51A2F351,0xA35DFEA3,0x4080C040,0x8F058A8F,0x923FAD92,0x9D21BC9D,0x38704838,0xF5F104F5,
+ 0xBC63DFBC,0xB677C1B6,0xDAAF75DA,0x21426321,0x10203010,0xFFE51AFF,0xF3FD0EF3,0xD2BF6DD2,
+ 0xCD814CCD,0x0C18140C,0x13263513,0xECC32FEC,0x5FBEE15F,0x9735A297,0x4488CC44,0x172E3917,
+ 0xC49357C4,0xA755F2A7,0x7EFC827E,0x3D7A473D,0x64C8AC64,0x5DBAE75D,0x19322B19,0x73E69573,
+ 0x60C0A060,0x81199881,0x4F9ED14F,0xDCA37FDC,0x22446622,0x2A547E2A,0x903BAB90,0x880B8388,
+ 0x468CCA46,0xEEC729EE,0xB86BD3B8,0x14283C14,0xDEA779DE,0x5EBCE25E,0x0B161D0B,0xDBAD76DB,
+ 0xE0DB3BE0,0x32645632,0x3A744E3A,0x0A141E0A,0x4992DB49,0x060C0A06,0x24486C24,0x5CB8E45C,
+ 0xC29F5DC2,0xD3BD6ED3,0xAC43EFAC,0x62C4A662,0x9139A891,0x9531A495,0xE4D337E4,0x79F28B79,
+ 0xE7D532E7,0xC88B43C8,0x376E5937,0x6DDAB76D,0x8D018C8D,0xD5B164D5,0x4E9CD24E,0xA949E0A9,
+ 0x6CD8B46C,0x56ACFA56,0xF4F307F4,0xEACF25EA,0x65CAAF65,0x7AF48E7A,0xAE47E9AE,0x08101808,
+ 0xBA6FD5BA,0x78F08878,0x254A6F25,0x2E5C722E,0x1C38241C,0xA657F1A6,0xB473C7B4,0xC69751C6,
+ 0xE8CB23E8,0xDDA17CDD,0x74E89C74,0x1F3E211F,0x4B96DD4B,0xBD61DCBD,0x8B0D868B,0x8A0F858A,
+ 0x70E09070,0x3E7C423E,0xB571C4B5,0x66CCAA66,0x4890D848,0x03060503,0xF6F701F6,0x0E1C120E,
+ 0x61C2A361,0x356A5F35,0x57AEF957,0xB969D0B9,0x86179186,0xC19958C1,0x1D3A271D,0x9E27B99E,
+ 0xE1D938E1,0xF8EB13F8,0x982BB398,0x11223311,0x69D2BB69,0xD9A970D9,0x8E07898E,0x9433A794,
+ 0x9B2DB69B,0x1E3C221E,0x87159287,0xE9C920E9,0xCE8749CE,0x55AAFF55,0x28507828,0xDFA57ADF,
+ 0x8C038F8C,0xA159F8A1,0x89098089,0x0D1A170D,0xBF65DABF,0xE6D731E6,0x4284C642,0x68D0B868,
+ 0x4182C341,0x9929B099,0x2D5A772D,0x0F1E110F,0xB07BCBB0,0x54A8FC54,0xBB6DD6BB,0x162C3A16
+ },
+ {
+ 0xC6A56363,0xF8847C7C,0xEE997777,0xF68D7B7B,0xFF0DF2F2,0xD6BD6B6B,0xDEB16F6F,0x9154C5C5,
+ 0x60503030,0x02030101,0xCEA96767,0x567D2B2B,0xE719FEFE,0xB562D7D7,0x4DE6ABAB,0xEC9A7676,
+ 0x8F45CACA,0x1F9D8282,0x8940C9C9,0xFA877D7D,0xEF15FAFA,0xB2EB5959,0x8EC94747,0xFB0BF0F0,
+ 0x41ECADAD,0xB367D4D4,0x5FFDA2A2,0x45EAAFAF,0x23BF9C9C,0x53F7A4A4,0xE4967272,0x9B5BC0C0,
+ 0x75C2B7B7,0xE11CFDFD,0x3DAE9393,0x4C6A2626,0x6C5A3636,0x7E413F3F,0xF502F7F7,0x834FCCCC,
+ 0x685C3434,0x51F4A5A5,0xD134E5E5,0xF908F1F1,0xE2937171,0xAB73D8D8,0x62533131,0x2A3F1515,
+ 0x080C0404,0x9552C7C7,0x46652323,0x9D5EC3C3,0x30281818,0x37A19696,0x0A0F0505,0x2FB59A9A,
+ 0x0E090707,0x24361212,0x1B9B8080,0xDF3DE2E2,0xCD26EBEB,0x4E692727,0x7FCDB2B2,0xEA9F7575,
+ 0x121B0909,0x1D9E8383,0x58742C2C,0x342E1A1A,0x362D1B1B,0xDCB26E6E,0xB4EE5A5A,0x5BFBA0A0,
+ 0xA4F65252,0x764D3B3B,0xB761D6D6,0x7DCEB3B3,0x527B2929,0xDD3EE3E3,0x5E712F2F,0x13978484,
+ 0xA6F55353,0xB968D1D1,0000000000,0xC12CEDED,0x40602020,0xE31FFCFC,0x79C8B1B1,0xB6ED5B5B,
+ 0xD4BE6A6A,0x8D46CBCB,0x67D9BEBE,0x724B3939,0x94DE4A4A,0x98D44C4C,0xB0E85858,0x854ACFCF,
+ 0xBB6BD0D0,0xC52AEFEF,0x4FE5AAAA,0xED16FBFB,0x86C54343,0x9AD74D4D,0x66553333,0x11948585,
+ 0x8ACF4545,0xE910F9F9,0x04060202,0xFE817F7F,0xA0F05050,0x78443C3C,0x25BA9F9F,0x4BE3A8A8,
+ 0xA2F35151,0x5DFEA3A3,0x80C04040,0x058A8F8F,0x3FAD9292,0x21BC9D9D,0x70483838,0xF104F5F5,
+ 0x63DFBCBC,0x77C1B6B6,0xAF75DADA,0x42632121,0x20301010,0xE51AFFFF,0xFD0EF3F3,0xBF6DD2D2,
+ 0x814CCDCD,0x18140C0C,0x26351313,0xC32FECEC,0xBEE15F5F,0x35A29797,0x88CC4444,0x2E391717,
+ 0x9357C4C4,0x55F2A7A7,0xFC827E7E,0x7A473D3D,0xC8AC6464,0xBAE75D5D,0x322B1919,0xE6957373,
+ 0xC0A06060,0x19988181,0x9ED14F4F,0xA37FDCDC,0x44662222,0x547E2A2A,0x3BAB9090,0x0B838888,
+ 0x8CCA4646,0xC729EEEE,0x6BD3B8B8,0x283C1414,0xA779DEDE,0xBCE25E5E,0x161D0B0B,0xAD76DBDB,
+ 0xDB3BE0E0,0x64563232,0x744E3A3A,0x141E0A0A,0x92DB4949,0x0C0A0606,0x486C2424,0xB8E45C5C,
+ 0x9F5DC2C2,0xBD6ED3D3,0x43EFACAC,0xC4A66262,0x39A89191,0x31A49595,0xD337E4E4,0xF28B7979,
+ 0xD532E7E7,0x8B43C8C8,0x6E593737,0xDAB76D6D,0x018C8D8D,0xB164D5D5,0x9CD24E4E,0x49E0A9A9,
+ 0xD8B46C6C,0xACFA5656,0xF307F4F4,0xCF25EAEA,0xCAAF6565,0xF48E7A7A,0x47E9AEAE,0x10180808,
+ 0x6FD5BABA,0xF0887878,0x4A6F2525,0x5C722E2E,0x38241C1C,0x57F1A6A6,0x73C7B4B4,0x9751C6C6,
+ 0xCB23E8E8,0xA17CDDDD,0xE89C7474,0x3E211F1F,0x96DD4B4B,0x61DCBDBD,0x0D868B8B,0x0F858A8A,
+ 0xE0907070,0x7C423E3E,0x71C4B5B5,0xCCAA6666,0x90D84848,0x06050303,0xF701F6F6,0x1C120E0E,
+ 0xC2A36161,0x6A5F3535,0xAEF95757,0x69D0B9B9,0x17918686,0x9958C1C1,0x3A271D1D,0x27B99E9E,
+ 0xD938E1E1,0xEB13F8F8,0x2BB39898,0x22331111,0xD2BB6969,0xA970D9D9,0x07898E8E,0x33A79494,
+ 0x2DB69B9B,0x3C221E1E,0x15928787,0xC920E9E9,0x8749CECE,0xAAFF5555,0x50782828,0xA57ADFDF,
+ 0x038F8C8C,0x59F8A1A1,0x09808989,0x1A170D0D,0x65DABFBF,0xD731E6E6,0x84C64242,0xD0B86868,
+ 0x82C34141,0x29B09999,0x5A772D2D,0x1E110F0F,0x7BCBB0B0,0xA8FC5454,0x6DD6BBBB,0x2C3A1616
+ }
+};
+
+static uint32_t fl_tab[4][256] =
+{
+ {
+ 0x00000063,0x0000007C,0x00000077,0x0000007B,0x000000F2,0x0000006B,0x0000006F,0x000000C5,
+ 0x00000030,0x00000001,0x00000067,0x0000002B,0x000000FE,0x000000D7,0x000000AB,0x00000076,
+ 0x000000CA,0x00000082,0x000000C9,0x0000007D,0x000000FA,0x00000059,0x00000047,0x000000F0,
+ 0x000000AD,0x000000D4,0x000000A2,0x000000AF,0x0000009C,0x000000A4,0x00000072,0x000000C0,
+ 0x000000B7,0x000000FD,0x00000093,0x00000026,0x00000036,0x0000003F,0x000000F7,0x000000CC,
+ 0x00000034,0x000000A5,0x000000E5,0x000000F1,0x00000071,0x000000D8,0x00000031,0x00000015,
+ 0x00000004,0x000000C7,0x00000023,0x000000C3,0x00000018,0x00000096,0x00000005,0x0000009A,
+ 0x00000007,0x00000012,0x00000080,0x000000E2,0x000000EB,0x00000027,0x000000B2,0x00000075,
+ 0x00000009,0x00000083,0x0000002C,0x0000001A,0x0000001B,0x0000006E,0x0000005A,0x000000A0,
+ 0x00000052,0x0000003B,0x000000D6,0x000000B3,0x00000029,0x000000E3,0x0000002F,0x00000084,
+ 0x00000053,0x000000D1,0x00000000,0x000000ED,0x00000020,0x000000FC,0x000000B1,0x0000005B,
+ 0x0000006A,0x000000CB,0x000000BE,0x00000039,0x0000004A,0x0000004C,0x00000058,0x000000CF,
+ 0x000000D0,0x000000EF,0x000000AA,0x000000FB,0x00000043,0x0000004D,0x00000033,0x00000085,
+ 0x00000045,0x000000F9,0x00000002,0x0000007F,0x00000050,0x0000003C,0x0000009F,0x000000A8,
+ 0x00000051,0x000000A3,0x00000040,0x0000008F,0x00000092,0x0000009D,0x00000038,0x000000F5,
+ 0x000000BC,0x000000B6,0x000000DA,0x00000021,0x00000010,0x000000FF,0x000000F3,0x000000D2,
+ 0x000000CD,0x0000000C,0x00000013,0x000000EC,0x0000005F,0x00000097,0x00000044,0x00000017,
+ 0x000000C4,0x000000A7,0x0000007E,0x0000003D,0x00000064,0x0000005D,0x00000019,0x00000073,
+ 0x00000060,0x00000081,0x0000004F,0x000000DC,0x00000022,0x0000002A,0x00000090,0x00000088,
+ 0x00000046,0x000000EE,0x000000B8,0x00000014,0x000000DE,0x0000005E,0x0000000B,0x000000DB,
+ 0x000000E0,0x00000032,0x0000003A,0x0000000A,0x00000049,0x00000006,0x00000024,0x0000005C,
+ 0x000000C2,0x000000D3,0x000000AC,0x00000062,0x00000091,0x00000095,0x000000E4,0x00000079,
+ 0x000000E7,0x000000C8,0x00000037,0x0000006D,0x0000008D,0x000000D5,0x0000004E,0x000000A9,
+ 0x0000006C,0x00000056,0x000000F4,0x000000EA,0x00000065,0x0000007A,0x000000AE,0x00000008,
+ 0x000000BA,0x00000078,0x00000025,0x0000002E,0x0000001C,0x000000A6,0x000000B4,0x000000C6,
+ 0x000000E8,0x000000DD,0x00000074,0x0000001F,0x0000004B,0x000000BD,0x0000008B,0x0000008A,
+ 0x00000070,0x0000003E,0x000000B5,0x00000066,0x00000048,0x00000003,0x000000F6,0x0000000E,
+ 0x00000061,0x00000035,0x00000057,0x000000B9,0x00000086,0x000000C1,0x0000001D,0x0000009E,
+ 0x000000E1,0x000000F8,0x00000098,0x00000011,0x00000069,0x000000D9,0x0000008E,0x00000094,
+ 0x0000009B,0x0000001E,0x00000087,0x000000E9,0x000000CE,0x00000055,0x00000028,0x000000DF,
+ 0x0000008C,0x000000A1,0x00000089,0x0000000D,0x000000BF,0x000000E6,0x00000042,0x00000068,
+ 0x00000041,0x00000099,0x0000002D,0x0000000F,0x000000B0,0x00000054,0x000000BB,0x00000016
+ },
+ {
+ 0x00006300,0x00007C00,0x00007700,0x00007B00,0x0000F200,0x00006B00,0x00006F00,0x0000C500,
+ 0x00003000,0x00000100,0x00006700,0x00002B00,0x0000FE00,0x0000D700,0x0000AB00,0x00007600,
+ 0x0000CA00,0x00008200,0x0000C900,0x00007D00,0x0000FA00,0x00005900,0x00004700,0x0000F000,
+ 0x0000AD00,0x0000D400,0x0000A200,0x0000AF00,0x00009C00,0x0000A400,0x00007200,0x0000C000,
+ 0x0000B700,0x0000FD00,0x00009300,0x00002600,0x00003600,0x00003F00,0x0000F700,0x0000CC00,
+ 0x00003400,0x0000A500,0x0000E500,0x0000F100,0x00007100,0x0000D800,0x00003100,0x00001500,
+ 0x00000400,0x0000C700,0x00002300,0x0000C300,0x00001800,0x00009600,0x00000500,0x00009A00,
+ 0x00000700,0x00001200,0x00008000,0x0000E200,0x0000EB00,0x00002700,0x0000B200,0x00007500,
+ 0x00000900,0x00008300,0x00002C00,0x00001A00,0x00001B00,0x00006E00,0x00005A00,0x0000A000,
+ 0x00005200,0x00003B00,0x0000D600,0x0000B300,0x00002900,0x0000E300,0x00002F00,0x00008400,
+ 0x00005300,0x0000D100,0000000000,0x0000ED00,0x00002000,0x0000FC00,0x0000B100,0x00005B00,
+ 0x00006A00,0x0000CB00,0x0000BE00,0x00003900,0x00004A00,0x00004C00,0x00005800,0x0000CF00,
+ 0x0000D000,0x0000EF00,0x0000AA00,0x0000FB00,0x00004300,0x00004D00,0x00003300,0x00008500,
+ 0x00004500,0x0000F900,0x00000200,0x00007F00,0x00005000,0x00003C00,0x00009F00,0x0000A800,
+ 0x00005100,0x0000A300,0x00004000,0x00008F00,0x00009200,0x00009D00,0x00003800,0x0000F500,
+ 0x0000BC00,0x0000B600,0x0000DA00,0x00002100,0x00001000,0x0000FF00,0x0000F300,0x0000D200,
+ 0x0000CD00,0x00000C00,0x00001300,0x0000EC00,0x00005F00,0x00009700,0x00004400,0x00001700,
+ 0x0000C400,0x0000A700,0x00007E00,0x00003D00,0x00006400,0x00005D00,0x00001900,0x00007300,
+ 0x00006000,0x00008100,0x00004F00,0x0000DC00,0x00002200,0x00002A00,0x00009000,0x00008800,
+ 0x00004600,0x0000EE00,0x0000B800,0x00001400,0x0000DE00,0x00005E00,0x00000B00,0x0000DB00,
+ 0x0000E000,0x00003200,0x00003A00,0x00000A00,0x00004900,0x00000600,0x00002400,0x00005C00,
+ 0x0000C200,0x0000D300,0x0000AC00,0x00006200,0x00009100,0x00009500,0x0000E400,0x00007900,
+ 0x0000E700,0x0000C800,0x00003700,0x00006D00,0x00008D00,0x0000D500,0x00004E00,0x0000A900,
+ 0x00006C00,0x00005600,0x0000F400,0x0000EA00,0x00006500,0x00007A00,0x0000AE00,0x00000800,
+ 0x0000BA00,0x00007800,0x00002500,0x00002E00,0x00001C00,0x0000A600,0x0000B400,0x0000C600,
+ 0x0000E800,0x0000DD00,0x00007400,0x00001F00,0x00004B00,0x0000BD00,0x00008B00,0x00008A00,
+ 0x00007000,0x00003E00,0x0000B500,0x00006600,0x00004800,0x00000300,0x0000F600,0x00000E00,
+ 0x00006100,0x00003500,0x00005700,0x0000B900,0x00008600,0x0000C100,0x00001D00,0x00009E00,
+ 0x0000E100,0x0000F800,0x00009800,0x00001100,0x00006900,0x0000D900,0x00008E00,0x00009400,
+ 0x00009B00,0x00001E00,0x00008700,0x0000E900,0x0000CE00,0x00005500,0x00002800,0x0000DF00,
+ 0x00008C00,0x0000A100,0x00008900,0x00000D00,0x0000BF00,0x0000E600,0x00004200,0x00006800,
+ 0x00004100,0x00009900,0x00002D00,0x00000F00,0x0000B000,0x00005400,0x0000BB00,0x00001600
+ },
+ {
+ 0x00630000,0x007C0000,0x00770000,0x007B0000,0x00F20000,0x006B0000,0x006F0000,0x00C50000,
+ 0x00300000,0x00010000,0x00670000,0x002B0000,0x00FE0000,0x00D70000,0x00AB0000,0x00760000,
+ 0x00CA0000,0x00820000,0x00C90000,0x007D0000,0x00FA0000,0x00590000,0x00470000,0x00F00000,
+ 0x00AD0000,0x00D40000,0x00A20000,0x00AF0000,0x009C0000,0x00A40000,0x00720000,0x00C00000,
+ 0x00B70000,0x00FD0000,0x00930000,0x00260000,0x00360000,0x003F0000,0x00F70000,0x00CC0000,
+ 0x00340000,0x00A50000,0x00E50000,0x00F10000,0x00710000,0x00D80000,0x00310000,0x00150000,
+ 0x00040000,0x00C70000,0x00230000,0x00C30000,0x00180000,0x00960000,0x00050000,0x009A0000,
+ 0x00070000,0x00120000,0x00800000,0x00E20000,0x00EB0000,0x00270000,0x00B20000,0x00750000,
+ 0x00090000,0x00830000,0x002C0000,0x001A0000,0x001B0000,0x006E0000,0x005A0000,0x00A00000,
+ 0x00520000,0x003B0000,0x00D60000,0x00B30000,0x00290000,0x00E30000,0x002F0000,0x00840000,
+ 0x00530000,0x00D10000,0000000000,0x00ED0000,0x00200000,0x00FC0000,0x00B10000,0x005B0000,
+ 0x006A0000,0x00CB0000,0x00BE0000,0x00390000,0x004A0000,0x004C0000,0x00580000,0x00CF0000,
+ 0x00D00000,0x00EF0000,0x00AA0000,0x00FB0000,0x00430000,0x004D0000,0x00330000,0x00850000,
+ 0x00450000,0x00F90000,0x00020000,0x007F0000,0x00500000,0x003C0000,0x009F0000,0x00A80000,
+ 0x00510000,0x00A30000,0x00400000,0x008F0000,0x00920000,0x009D0000,0x00380000,0x00F50000,
+ 0x00BC0000,0x00B60000,0x00DA0000,0x00210000,0x00100000,0x00FF0000,0x00F30000,0x00D20000,
+ 0x00CD0000,0x000C0000,0x00130000,0x00EC0000,0x005F0000,0x00970000,0x00440000,0x00170000,
+ 0x00C40000,0x00A70000,0x007E0000,0x003D0000,0x00640000,0x005D0000,0x00190000,0x00730000,
+ 0x00600000,0x00810000,0x004F0000,0x00DC0000,0x00220000,0x002A0000,0x00900000,0x00880000,
+ 0x00460000,0x00EE0000,0x00B80000,0x00140000,0x00DE0000,0x005E0000,0x000B0000,0x00DB0000,
+ 0x00E00000,0x00320000,0x003A0000,0x000A0000,0x00490000,0x00060000,0x00240000,0x005C0000,
+ 0x00C20000,0x00D30000,0x00AC0000,0x00620000,0x00910000,0x00950000,0x00E40000,0x00790000,
+ 0x00E70000,0x00C80000,0x00370000,0x006D0000,0x008D0000,0x00D50000,0x004E0000,0x00A90000,
+ 0x006C0000,0x00560000,0x00F40000,0x00EA0000,0x00650000,0x007A0000,0x00AE0000,0x00080000,
+ 0x00BA0000,0x00780000,0x00250000,0x002E0000,0x001C0000,0x00A60000,0x00B40000,0x00C60000,
+ 0x00E80000,0x00DD0000,0x00740000,0x001F0000,0x004B0000,0x00BD0000,0x008B0000,0x008A0000,
+ 0x00700000,0x003E0000,0x00B50000,0x00660000,0x00480000,0x00030000,0x00F60000,0x000E0000,
+ 0x00610000,0x00350000,0x00570000,0x00B90000,0x00860000,0x00C10000,0x001D0000,0x009E0000,
+ 0x00E10000,0x00F80000,0x00980000,0x00110000,0x00690000,0x00D90000,0x008E0000,0x00940000,
+ 0x009B0000,0x001E0000,0x00870000,0x00E90000,0x00CE0000,0x00550000,0x00280000,0x00DF0000,
+ 0x008C0000,0x00A10000,0x00890000,0x000D0000,0x00BF0000,0x00E60000,0x00420000,0x00680000,
+ 0x00410000,0x00990000,0x002D0000,0x000F0000,0x00B00000,0x00540000,0x00BB0000,0x00160000
+ },
+ {
+ 0x63000000,0x7C000000,0x77000000,0x7B000000,0xF2000000,0x6B000000,0x6F000000,0xC5000000,
+ 0x30000000,0x01000000,0x67000000,0x2B000000,0xFE000000,0xD7000000,0xAB000000,0x76000000,
+ 0xCA000000,0x82000000,0xC9000000,0x7D000000,0xFA000000,0x59000000,0x47000000,0xF0000000,
+ 0xAD000000,0xD4000000,0xA2000000,0xAF000000,0x9C000000,0xA4000000,0x72000000,0xC0000000,
+ 0xB7000000,0xFD000000,0x93000000,0x26000000,0x36000000,0x3F000000,0xF7000000,0xCC000000,
+ 0x34000000,0xA5000000,0xE5000000,0xF1000000,0x71000000,0xD8000000,0x31000000,0x15000000,
+ 0x04000000,0xC7000000,0x23000000,0xC3000000,0x18000000,0x96000000,0x05000000,0x9A000000,
+ 0x07000000,0x12000000,0x80000000,0xE2000000,0xEB000000,0x27000000,0xB2000000,0x75000000,
+ 0x09000000,0x83000000,0x2C000000,0x1A000000,0x1B000000,0x6E000000,0x5A000000,0xA0000000,
+ 0x52000000,0x3B000000,0xD6000000,0xB3000000,0x29000000,0xE3000000,0x2F000000,0x84000000,
+ 0x53000000,0xD1000000,0000000000,0xED000000,0x20000000,0xFC000000,0xB1000000,0x5B000000,
+ 0x6A000000,0xCB000000,0xBE000000,0x39000000,0x4A000000,0x4C000000,0x58000000,0xCF000000,
+ 0xD0000000,0xEF000000,0xAA000000,0xFB000000,0x43000000,0x4D000000,0x33000000,0x85000000,
+ 0x45000000,0xF9000000,0x02000000,0x7F000000,0x50000000,0x3C000000,0x9F000000,0xA8000000,
+ 0x51000000,0xA3000000,0x40000000,0x8F000000,0x92000000,0x9D000000,0x38000000,0xF5000000,
+ 0xBC000000,0xB6000000,0xDA000000,0x21000000,0x10000000,0xFF000000,0xF3000000,0xD2000000,
+ 0xCD000000,0x0C000000,0x13000000,0xEC000000,0x5F000000,0x97000000,0x44000000,0x17000000,
+ 0xC4000000,0xA7000000,0x7E000000,0x3D000000,0x64000000,0x5D000000,0x19000000,0x73000000,
+ 0x60000000,0x81000000,0x4F000000,0xDC000000,0x22000000,0x2A000000,0x90000000,0x88000000,
+ 0x46000000,0xEE000000,0xB8000000,0x14000000,0xDE000000,0x5E000000,0x0B000000,0xDB000000,
+ 0xE0000000,0x32000000,0x3A000000,0x0A000000,0x49000000,0x06000000,0x24000000,0x5C000000,
+ 0xC2000000,0xD3000000,0xAC000000,0x62000000,0x91000000,0x95000000,0xE4000000,0x79000000,
+ 0xE7000000,0xC8000000,0x37000000,0x6D000000,0x8D000000,0xD5000000,0x4E000000,0xA9000000,
+ 0x6C000000,0x56000000,0xF4000000,0xEA000000,0x65000000,0x7A000000,0xAE000000,0x08000000,
+ 0xBA000000,0x78000000,0x25000000,0x2E000000,0x1C000000,0xA6000000,0xB4000000,0xC6000000,
+ 0xE8000000,0xDD000000,0x74000000,0x1F000000,0x4B000000,0xBD000000,0x8B000000,0x8A000000,
+ 0x70000000,0x3E000000,0xB5000000,0x66000000,0x48000000,0x03000000,0xF6000000,0x0E000000,
+ 0x61000000,0x35000000,0x57000000,0xB9000000,0x86000000,0xC1000000,0x1D000000,0x9E000000,
+ 0xE1000000,0xF8000000,0x98000000,0x11000000,0x69000000,0xD9000000,0x8E000000,0x94000000,
+ 0x9B000000,0x1E000000,0x87000000,0xE9000000,0xCE000000,0x55000000,0x28000000,0xDF000000,
+ 0x8C000000,0xA1000000,0x89000000,0x0D000000,0xBF000000,0xE6000000,0x42000000,0x68000000,
+ 0x41000000,0x99000000,0x2D000000,0x0F000000,0xB0000000,0x54000000,0xBB000000,0x16000000
+ }
+};
+
+/*----------------- The workspace ------------------------------*/
+
+static uint32_t Ekey[44]; /* The expanded key */
+
+/*------ The round Function. 4 table lookups and 4 Exors ------*/
+#define f_rnd(x, n) \
+ ( ft_tab[0][byte0(x[n])] \
+ ^ ft_tab[1][byte1(x[(n + 1) & 3])] \
+ ^ ft_tab[2][byte2(x[(n + 2) & 3])] \
+ ^ ft_tab[3][byte3(x[(n + 3) & 3])] )
+
+#define f_round(bo, bi, k) \
+ bo[0] = f_rnd(bi, 0) ^ k[0]; \
+ bo[1] = f_rnd(bi, 1) ^ k[1]; \
+ bo[2] = f_rnd(bi, 2) ^ k[2]; \
+ bo[3] = f_rnd(bi, 3) ^ k[3]; \
+ k += 4
+
+/*--- The S Box lookup used in constructing the Key schedule ---*/
+#define ls_box(x) \
+ ( fl_tab[0][byte0(x)] \
+ ^ fl_tab[1][byte1(x)] \
+ ^ fl_tab[2][byte2(x)] \
+ ^ fl_tab[3][byte3(x)] )
+
+/*------------ The last round function (no MixColumn) ----------*/
+#define lf_rnd(x, n) \
+ ( fl_tab[0][byte0(x[n])] \
+ ^ fl_tab[1][byte1(x[(n + 1) & 3])] \
+ ^ fl_tab[2][byte2(x[(n + 2) & 3])] \
+ ^ fl_tab[3][byte3(x[(n + 3) & 3])] )
+
+
+/*-----------------------------------------------------------
+ * RijndaelKeySchedule
+ * Initialise the key schedule from a supplied key
+ */
+void RijndaelKeySchedule(uint8_t key[16])
+{
+ uint32_t t;
+ uint32_t *ek=Ekey, /* pointer to the expanded key */
+ *rc=rnd_con; /* pointer to the round constant */
+
+ Ekey[0] = u32_in(key );
+ Ekey[1] = u32_in(key + 4);
+ Ekey[2] = u32_in(key + 8);
+ Ekey[3] = u32_in(key + 12);
+
+ while(ek < Ekey + 40)
+ {
+ t = rot3(ek[3]);
+ ek[4] = ek[0] ^ ls_box(t) ^ *rc++;
+ ek[5] = ek[1] ^ ek[4];
+ ek[6] = ek[2] ^ ek[5];
+ ek[7] = ek[3] ^ ek[6];
+ ek += 4;
+ }
+}
+
+/*-----------------------------------------------------------
+ * RijndaelEncrypt
+ * Encrypt an input block
+ */
+void RijndaelEncrypt(uint8_t in[16], uint8_t out[16])
+{
+ uint32_t b0[4], b1[4], *kp = Ekey;
+
+ b0[0] = u32_in(in ) ^ *kp++;
+ b0[1] = u32_in(in + 4) ^ *kp++;
+ b0[2] = u32_in(in + 8) ^ *kp++;
+ b0[3] = u32_in(in + 12) ^ *kp++;
+
+ f_round(b1, b0, kp);
+ f_round(b0, b1, kp);
+ f_round(b1, b0, kp);
+ f_round(b0, b1, kp);
+ f_round(b1, b0, kp);
+ f_round(b0, b1, kp);
+ f_round(b1, b0, kp);
+ f_round(b0, b1, kp);
+ f_round(b1, b0, kp);
+
+ u32_out(out, lf_rnd(b1, 0) ^ kp[0]);
+ u32_out(out + 4, lf_rnd(b1, 1) ^ kp[1]);
+ u32_out(out + 8, lf_rnd(b1, 2) ^ kp[2]);
+ u32_out(out + 12, lf_rnd(b1, 3) ^ kp[3]);
+}
diff --git a/tinySIP/src/dialogs/tsip_dialog.c b/tinySIP/src/dialogs/tsip_dialog.c
new file mode 100644
index 0000000..13dcdf4
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog.c
@@ -0,0 +1,1354 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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.c
+ * @brief SIP dialog base class as per RFC 3261 subclause 17.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog.h"
+
+#include "tinysip/dialogs/tsip_dialog_layer.h"
+#include "tinysip/transactions/tsip_transac_layer.h"
+#include "tinysip/transports/tsip_transport_layer.h"
+
+#include "tinysip/transactions/tsip_transac_nict.h"
+
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tinysip/headers/tsip_header_Authorization.h"
+#include "tinysip/headers/tsip_header_Contact.h"
+#include "tinysip/headers/tsip_header_Dummy.h"
+#include "tinysip/headers/tsip_header_Expires.h"
+#include "tinysip/headers/tsip_header_P_Preferred_Identity.h"
+#include "tinysip/headers/tsip_header_Proxy_Authenticate.h"
+#include "tinysip/headers/tsip_header_Proxy_Authorization.h"
+#include "tinysip/headers/tsip_header_Record_Route.h"
+#include "tinysip/headers/tsip_header_Route.h"
+#include "tinysip/headers/tsip_header_Subscription_State.h"
+#include "tinysip/headers/tsip_header_WWW_Authenticate.h"
+
+#include "tsk_debug.h"
+#include "tsk_time.h"
+
+int tsip_dialog_update_challenges(tsip_dialog_t *self, const tsip_response_t* response, tsk_bool_t acceptNewVector);
+int tsip_dialog_add_session_headers(const tsip_dialog_t *self, tsip_request_t* request);
+int tsip_dialog_add_common_headers(const tsip_dialog_t *self, tsip_request_t* request);
+
+extern tsip_uri_t* tsip_stack_get_pcscf_uri(const tsip_stack_t *self, tnet_socket_type_t type, tsk_bool_t lr);
+extern tsip_uri_t* tsip_stack_get_contacturi(const tsip_stack_t *self, const char* protocol);
+
+#define TSIP_DIALOG_ADD_HEADERS(headers) {\
+ const tsk_list_item_t* item;\
+ tsk_list_foreach(item, headers){ \
+ if(!TSK_PARAM(item->data)->tag){ \
+ /* 'Route' is special header as it's used to find next destination address */ \
+ if(tsk_striequals(TSK_PARAM(item->data)->name, "route")){ \
+ tsip_uri_t* route_uri; \
+ char* route_uri_str = tsk_strdup(TSK_PARAM(item->data)->value); \
+ tsk_strunquote_2(&route_uri_str, '<', '>'); \
+ route_uri = tsip_uri_parse(route_uri_str, tsk_strlen(route_uri_str)); \
+ if(route_uri){ \
+ tsip_message_add_headers(request, \
+ TSIP_HEADER_ROUTE_VA_ARGS(route_uri), \
+ tsk_null); \
+ TSK_OBJECT_SAFE_FREE(route_uri); \
+ } \
+ TSK_FREE(route_uri_str); \
+ } \
+ else{ \
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_DUMMY_VA_ARGS(TSK_PARAM(item->data)->name, TSK_PARAM(item->data)->value)); \
+ } \
+ } \
+ }\
+ }
+
+
+tsip_request_t *tsip_dialog_request_new(const tsip_dialog_t *self, const char* method)
+{
+ tsip_request_t *request = tsk_null;
+ tsip_uri_t *to_uri, *from_uri, *request_uri;
+ const char *call_id;
+ int copy_routes_start = -1; /* NONE */
+ const tsk_list_item_t* item;
+
+ /*
+ RFC 3261 - 12.2.1.1 Generating the Request
+
+ The Call-ID of the request MUST be set to the Call-ID of the dialog.
+ */
+ call_id = self->callid;
+
+ /*
+ RFC 3261 - 12.2.1.1 Generating the Request
+
+ Requests within a dialog MUST contain strictly monotonically
+ increasing and contiguous CSeq sequence numbers (increasing-by-one)
+ in each direction (excepting ACK and CANCEL of course, whose numbers
+ equal the requests being acknowledged or cancelled). Therefore, if
+ the local sequence number is not empty, the value of the local
+ sequence number MUST be incremented by one, and this value MUST be
+ placed into the CSeq header field.
+ */
+ /*if(!tsk_striequals(method, "ACK") && !tsk_striequals(method, "CANCEL"))
+ {
+ TSIP_DIALOG(self)->cseq_value +=1;
+ }
+ ===> See send method (cseq will be incremented before sending the request)
+ */
+
+
+ /*
+ RFC 3261 - 12.2.1.1 Generating the Request
+
+ The URI in the To field of the request MUST be set to the remote URI
+ from the dialog state. The tag in the To header field of the request
+ MUST be set to the remote tag of the dialog ID. The From URI of the
+ request MUST be set to the local URI from the dialog state. The tag
+ in the From header field of the request MUST be set to the local tag
+ of the dialog ID. If the value of the remote or local tags is null,
+ the tag parameter MUST be omitted from the To or From header fields,
+ respectively.
+ */
+ to_uri = tsk_object_ref((void*)self->uri_remote);
+ from_uri = tsk_object_ref((void*)self->uri_local);
+
+
+ /*
+ RFC 3261 - 12.2.1.1 Generating the Request
+
+ If the route set is empty, the UAC MUST place the remote target URI
+ into the Request-URI. The UAC MUST NOT add a Route header field to
+ the request.
+ */
+ if(TSK_LIST_IS_EMPTY(self->record_routes)){
+ request_uri = tsk_object_ref((void*)self->uri_remote_target);
+ }
+
+ /*
+ RFC 3261 - 12.2.1.1 Generating the Request
+
+ If the route set is not empty, and the first URI in the route set
+ contains the lr parameter (see Section 19.1.1), the UAC MUST place
+ the remote target URI into the Request-URI and MUST include a Route
+ header field containing the route set values in order, including all
+ parameters.
+
+ If the route set is not empty, and its first URI does not contain the
+ lr parameter, the UAC MUST place the first URI from the route set
+ into the Request-URI, stripping any parameters that are not allowed
+ in a Request-URI. The UAC MUST add a Route header field containing
+ the remainder of the route set values in order, including all
+ parameters. The UAC MUST then place the remote target URI into the
+ Route header field as the last value.
+
+ For example, if the remote target is sip:user@remoteua and the route
+ set contains:
+
+ <sip:proxy1>,<sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>
+ */
+ else{
+ const tsip_uri_t *first_route = ((tsip_header_Record_Route_t*)TSK_LIST_FIRST_DATA(self->record_routes))->uri;
+ if(tsk_params_have_param(first_route->params, "lr")){
+ request_uri = tsk_object_ref(self->uri_remote_target);
+ copy_routes_start = 0; /* Copy all */
+ }
+ else{
+ request_uri = tsk_object_ref((void*)first_route);
+ copy_routes_start = 1; /* Copy starting at index 1. */
+ }
+ }
+
+ /*=====================================================================
+ */
+ request = tsip_request_new(method, request_uri, from_uri, to_uri, call_id, self->cseq_value);
+ request->To->tag = tsk_strdup(self->tag_remote);
+ request->From->tag = tsk_strdup(self->tag_local);
+ request->update = tsk_true; /* Now signal that the message should be updated by the transport layer (Contact, SigComp, IPSec, ...) */
+
+
+ /*
+ RFC 3261 - 12.2.1.1 Generating the Request
+
+ A UAC SHOULD include a Contact header field in any target refresh
+ requests within a dialog, and unless there is a need to change it,
+ the URI SHOULD be the same as used in previous requests within the
+ dialog. If the "secure" flag is true, that URI MUST be a SIPS URI.
+ As discussed in Section 12.2.2, a Contact header field in a target
+ refresh request updates the remote target URI. This allows a UA to
+ provide a new contact address, should its address change during the
+ duration of the dialog.
+ */
+ switch(request->line.request.request_type){
+ case tsip_MESSAGE:
+ case tsip_PUBLISH:
+ case tsip_BYE:
+ {
+ if(request->line.request.request_type == tsip_PUBLISH) {
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_EXPIRES_VA_ARGS(TSK_TIME_MS_2_S(self->expires)));
+ }
+ /* add caps in Accept-Contact headers */
+ tsk_list_foreach(item, self->ss->caps) {
+ const tsk_param_t* param = TSK_PARAM(item->data);
+ char* value = tsk_null;
+ tsk_sprintf(&value, "*;%s%s%s",
+ param->name,
+ param->value ? "=" : "",
+ param->value ? param->value : "");
+ if(value) {
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_DUMMY_VA_ARGS("Accept-Contact", value));
+ TSK_FREE(value);
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ char* contact = tsk_null;
+ tsip_header_Contacts_L_t *hdr_contacts;
+
+ if(request->line.request.request_type == tsip_OPTIONS ||
+ request->line.request.request_type == tsip_PUBLISH ||
+ request->line.request.request_type == tsip_REGISTER){
+ /**** with expires */
+ tsk_sprintf(&contact, "m: <%s:%s@%s:%d>;expires=%d\r\n",
+ "sip",
+ from_uri->user_name,
+ "127.0.0.1",
+ 5060,
+
+ TSK_TIME_MS_2_S(self->expires));
+ }
+ else{
+ /**** without expires */
+ if(request->line.request.request_type == tsip_SUBSCRIBE){
+ /* RFC 3265 - 3.1.1. Subscription Duration
+ An "expires" parameter on the "Contact" header has no semantics for SUBSCRIBE and is explicitly
+ not equivalent to an "Expires" header in a SUBSCRIBE request or response.
+ */
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_EXPIRES_VA_ARGS(TSK_TIME_MS_2_S(self->expires)));
+ }
+ tsk_sprintf(&contact, "m: <%s:%s@%s:%d%s%s%s%s%s%s%s%s%s>\r\n",
+ "sip",
+ from_uri->user_name,
+ "127.0.0.1",
+ 5060,
+
+ self->ss->ws.src.host ? ";" : "",
+ self->ss->ws.src.host ? "ws-src-ip=" : "",
+ self->ss->ws.src.host ? self->ss->ws.src.host : "",
+ self->ss->ws.src.port[0] ? ";" : "",
+ self->ss->ws.src.port[0] ? "ws-src-port=" : "",
+ self->ss->ws.src.port[0] ? self->ss->ws.src.port : "",
+ self->ss->ws.src.proto ? ";" : "",
+ self->ss->ws.src.proto ? "ws-src-proto=" : "",
+ self->ss->ws.src.proto ? self->ss->ws.src.proto : ""
+ );
+ }
+ hdr_contacts = tsip_header_Contact_parse(contact, tsk_strlen(contact));
+ if(!TSK_LIST_IS_EMPTY(hdr_contacts)){
+ request->Contact = tsk_object_ref(hdr_contacts->head->data);
+ }
+ TSK_OBJECT_SAFE_FREE(hdr_contacts);
+ TSK_FREE(contact);
+
+ /* Add capabilities as per RFC 3840 */
+ if(request->Contact) {
+ tsk_list_foreach(item, self->ss->caps){
+ tsk_params_add_param(&TSIP_HEADER(request->Contact)->params, TSK_PARAM(item->data)->name, TSK_PARAM(item->data)->value);
+ }
+ }
+
+ break;
+ }
+ }
+
+ /* Update authorizations */
+ if(self->state == tsip_initial && TSK_LIST_IS_EMPTY(self->challenges)){
+ /* 3GPP TS 33.978 6.2.3.1 Procedures at the UE
+ On sending a REGISTER request in order to indicate support for early IMS security procedures, the UE shall not
+ include an Authorization header field and not include header fields or header field values as required by RFC3329.
+ */
+ if(TSIP_REQUEST_IS_REGISTER(request) && !TSIP_DIALOG_GET_STACK(self)->security.earlyIMS){
+ /* 3GPP TS 24.229 - 5.1.1.2.2 Initial registration using IMS AKA
+ On sending a REGISTER request, the UE shall populate the header fields as follows:
+ a) an Authorization header field, with:
+ - the "username" header field parameter, set to the value of the private user identity;
+ - the "realm" header field parameter, set to the domain name of the home network;
+ - the "uri" header field parameter, set to the SIP URI of the domain name of the home network;
+ - the "nonce" header field parameter, set to an empty value; and
+ - the "response" header field parameter, set to an empty value;
+ */
+ const char* realm = TSIP_DIALOG_GET_STACK(self)->network.realm ? TSIP_DIALOG_GET_STACK(self)->network.realm->host : "(null)";
+ char* request_uri = tsip_uri_tostring(request->line.request.uri, tsk_false, tsk_false);
+ tsip_header_t* auth_hdr = tsip_challenge_create_empty_header_authorization(TSIP_DIALOG_GET_STACK(self)->identity.impi, realm, request_uri);
+ tsip_message_add_header(request, auth_hdr);
+ tsk_object_unref(auth_hdr), auth_hdr = tsk_null;
+ TSK_FREE(request_uri);
+ }
+ }
+ else if(!TSK_LIST_IS_EMPTY(self->challenges)){
+ tsip_challenge_t *challenge;
+ tsip_header_t* auth_hdr;
+ tsk_list_foreach(item, self->challenges){
+ challenge = item->data;
+ auth_hdr = tsip_challenge_create_header_authorization(challenge, request);
+ if(auth_hdr){
+ tsip_message_add_header(request, auth_hdr);
+ tsk_object_unref(auth_hdr), auth_hdr = tsk_null;
+ }
+ }
+ }
+
+ /* Update CSeq */
+ /* RFC 3261 - 13.2.2.4 2xx Responses
+ Generating ACK: 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.
+ ==> CSeq number will be added/updated by the caller of this function,
+ credentials were added above.
+ */
+ if(!TSIP_REQUEST_IS_ACK(request) && !TSIP_REQUEST_IS_CANCEL(request)){
+ request->CSeq->seq = ++(TSIP_DIALOG(self)->cseq_value);
+ }
+
+ /* Route generation
+ * ==> http://betelco.blogspot.com/2008/11/proxy-and-service-route-discovery-in.html
+ * The dialog Routes have been copied above.
+
+ 3GPP TS 24.229 - 5.1.2A.1 UE-originating case
+
+ The UE shall build a proper preloaded Route header field value for all new dialogs and standalone transactions. The UE
+ shall build a list of Route header field values made out of the following, in this order:
+ a) the P-CSCF URI containing the IP address or the FQDN learnt through the P-CSCF discovery procedures; and
+ b) the P-CSCF port based on the security mechanism in use:
+
+ - if IMS AKA or SIP digest with TLS is in use as a security mechanism, the protected server port learnt during
+ the registration procedure;
+ - if SIP digest without TLS, NASS-IMS bundled authentciation or GPRS-IMS-Bundled authentication is in
+ use as a security mechanism, the unprotected server port used during the registration procedure;
+ c) and the values received in the Service-Route header field saved from the 200 (OK) response to the last
+ registration or re-registration of the public user identity with associated contact address.
+ */
+ if(!TSIP_REQUEST_IS_REGISTER(request))
+ { // According to the above link ==> Initial/Re/De registration do not have routes.
+ if(copy_routes_start != -1)
+ { /* The dialog already have routes ==> copy them. */
+ if(self->state == tsip_early || self->state == tsip_established){
+ int32_t index = -1;
+ tsk_list_foreach(item, self->record_routes){
+ tsip_header_Record_Route_t *record_Route = ((tsip_header_Record_Route_t*)item->data);
+ const tsip_uri_t* uri = record_Route->uri;
+ tsip_header_Route_t *route = tsk_null;
+ if(++index < copy_routes_start || !uri){
+ continue;
+ }
+
+ if((route = tsip_header_Route_create(uri))){
+ // copy parameters: see http://code.google.com/p/imsdroid/issues/detail?id=52
+ if(!TSK_LIST_IS_EMPTY(TSIP_HEADER_PARAMS(record_Route))){
+ if(!TSIP_HEADER_PARAMS(route)){
+ TSIP_HEADER_PARAMS(route) = tsk_list_create();
+ }
+ tsk_list_pushback_list(TSIP_HEADER_PARAMS(route), TSIP_HEADER_PARAMS(record_Route));
+ }
+
+ tsip_message_add_header(request, TSIP_HEADER(route));
+ TSK_OBJECT_SAFE_FREE(route);
+ }
+ }
+ }
+ }
+ else
+ { /* No routes associated to this dialog. */
+ if(self->state == tsip_initial || self->state == tsip_early){
+ /* GPP TS 24.229 section 5.1.2A [Generic procedures applicable to all methods excluding the REGISTER method]:
+ The UE shall build a proper preloaded Route header field value for all new dialogs and standalone transactions. The UE
+ shall build a list of Route header field values made out of the following, in this order:
+ a) the P-CSCF URI containing the IP address or the FQDN learnt through the P-CSCF discovery procedures; and
+ b) the P-CSCF port based on the security mechanism in use:
+ - if IMS AKA or SIP digest with TLS is in use as a security mechanism, the protected server port learnt during
+ the registration procedure;
+ - if SIP digest without TLS, NASS-IMS bundled authentciation or GPRS-IMS-Bundled authentication is in
+ use as a security mechanism, the unprotected server port used during the registration procedure;
+ c) and the values received in the Service-Route header field saved from the 200 (OK) response to the last
+ registration or re-registration of the public user identity with associated contact address.
+ */
+#if _DEBUG && defined(SDS_HACK)/* FIXME: remove this */
+ /* Ericsson SDS hack (INVITE with Proxy-CSCF as First route fail) */
+#elif 0
+ tsip_uri_t *uri = tsip_stack_get_pcscf_uri(TSIP_DIALOG_GET_STACK(self), tsk_true);
+ // Proxy-CSCF as first route
+ if(uri){
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_ROUTE_VA_ARGS(uri));
+ TSK_OBJECT_SAFE_FREE(uri);
+ }
+#endif
+ // Service routes
+ tsk_list_foreach(item, TSIP_DIALOG_GET_STACK(self)->service_routes){
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_ROUTE_VA_ARGS(item->data));
+ }
+ }
+ }
+ }
+
+ /* Add headers associated to the session */
+ tsip_dialog_add_session_headers(self, request);
+
+ /* Add headers associated to the dialog's stack */
+ TSIP_DIALOG_ADD_HEADERS(self->ss->stack->headers);
+
+ /* Add common headers */
+ tsip_dialog_add_common_headers(self, request);
+
+ /* SigComp */
+ if(self->ss->sigcomp_id){
+ /* should be added in this field instead of 'Contact' or 'Via' headers
+ * it's up to the transport layer to copy it to these headers */
+ request->sigcomp_id = tsk_strdup(self->ss->sigcomp_id);
+ }
+
+ /* Remote Address: Used if "Server mode" otherwise Proxy-CSCF will be used */
+ request->remote_addr = self->remote_addr;
+ /* Connected FD */
+ if(request->local_fd <= 0) {
+ request->local_fd = self->connected_fd;
+ }
+
+ TSK_OBJECT_SAFE_FREE(request_uri);
+ TSK_OBJECT_SAFE_FREE(from_uri);
+ TSK_OBJECT_SAFE_FREE(to_uri);
+
+ return request;
+}
+
+
+/** Sends a SIP/IMS request. This function is responsible for transaction creation.
+ *
+ * @param self The parent dialog. All callback events will be notified to this dialog.
+ * @param request The request to send.
+ *
+ * @return Zero if succeed and no-zero error code otherwise.
+**/
+int tsip_dialog_request_send(const tsip_dialog_t *self, tsip_request_t* request)
+{
+ int ret = -1;
+
+ if(self && TSIP_DIALOG_GET_STACK(self)){
+ const tsip_transac_layer_t *layer = TSIP_DIALOG_GET_STACK(self)->layer_transac;
+ if(layer){
+ /* Create new transaction. The new transaction will be added to the transaction layer.
+ The transaction has all information to create the right transaction type (NICT or ICT).
+ As this is an outgoing request ==> It shall be a client transaction (NICT or ICT).
+ For server transactions creation see @ref tsip_dialog_response_send.
+ */
+ static const tsk_bool_t isCT = tsk_true;
+ tsip_transac_t* transac;
+ tsip_transac_dst_t* dst;
+
+
+ if(TSIP_STACK_MODE_IS_CLIENT(TSIP_DIALOG_GET_STACK(self))){
+ const tsip_transport_t* transport = tsip_transport_layer_find_by_idx(TSIP_DIALOG_GET_STACK(self)->layer_transport, TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default);
+ if(!transport){
+ TSK_DEBUG_ERROR("Failed to find a valid default transport [%d]", TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default);
+ }
+ else{
+ request->dst_net_type = transport->type;
+ }
+ }
+ dst = tsip_transac_dst_dialog_create(TSIP_DIALOG(self));
+ transac = tsip_transac_layer_new(
+ layer,
+ isCT,
+ request,
+ dst
+ );
+ TSK_OBJECT_SAFE_FREE(dst);
+
+ /* Set the transaction's dialog. All events comming from the transaction (timeouts, errors ...) will be signaled to this dialog */
+ if(transac){
+ switch(transac->type)
+ {
+ case tsip_transac_type_ict:
+ case tsip_transac_type_nict:
+ {
+ /* Start the newly create IC/NIC transaction */
+ ret = tsip_transac_start(transac, request);
+ break;
+ }
+ default: break;
+ }
+ TSK_OBJECT_SAFE_FREE(transac);
+ }
+ }
+ }
+ return ret;
+}
+
+tsip_response_t *tsip_dialog_response_new(tsip_dialog_t *self, short status, const char* phrase, const tsip_request_t* request)
+{
+ /* Reponse is created as per RFC 3261 subclause 8.2.6 and (headers+tags) are copied
+ * as per subclause 8.2.6.2.
+ */
+ tsip_response_t* response;
+ if((response = tsip_response_new(status, phrase, request))){
+ switch(request->line.request.request_type){
+ case tsip_MESSAGE:
+ case tsip_PUBLISH:
+ break;
+ default:
+ /* Is there a To tag? */
+ if(response->To && !response->To->tag){
+ response->To->tag = tsk_strdup(self->tag_local);
+ }
+ /* Contact Header (for 101-299 reponses) */
+ if(self->uri_local && TSIP_RESPONSE_CODE(response) >= 101 && TSIP_RESPONSE_CODE(response) <= 299){
+ char* contact = tsk_null;
+ tsip_header_Contacts_L_t *hdr_contacts;
+
+ tsk_sprintf(&contact, "m: <%s:%s@%s:%d>\r\n", "sip", self->uri_local->user_name, "127.0.0.1", 5060);
+ hdr_contacts = tsip_header_Contact_parse(contact, tsk_strlen(contact));
+ if(!TSK_LIST_IS_EMPTY(hdr_contacts)){
+ response->Contact = tsk_object_ref(hdr_contacts->head->data);
+ response->update = tsk_true; /* Now signal that the message should be updated by the transport layer (Contact header) */
+ }
+ TSK_OBJECT_SAFE_FREE(hdr_contacts);
+ TSK_FREE(contact);
+ }
+ break;
+ }
+
+ /* SigComp */
+ if(self->ss->sigcomp_id){
+ /* should be added in this field instead of 'Contact' or 'Via' headers
+ * it's up to the transport layer to copy it to these headers */
+ response->sigcomp_id = tsk_strdup(self->ss->sigcomp_id);
+ }
+ /* Connected FD */
+ if(response->local_fd <= 0) {
+ response->local_fd = self->connected_fd;
+ }
+ /* Remote Addr: used to send requests if "Server Mode" otherwise Proxy-CSCF address will be used */
+ self->remote_addr = request->remote_addr;
+ }
+ return response;
+}
+
+int tsip_dialog_response_send(const tsip_dialog_t *self, tsip_response_t* response)
+{
+ int ret = -1;
+
+ if(self && TSIP_DIALOG_GET_STACK(self)){
+ const tsip_transac_layer_t *layer = TSIP_DIALOG_GET_STACK(self)->layer_transac;
+ if(layer){
+ /* As this is a response ...then use the associate server transaction */
+ tsip_transac_t *transac = tsip_transac_layer_find_server(layer, response);
+ if(transac){
+ ret = transac->callback(transac, tsip_transac_outgoing_msg, response);
+ tsk_object_unref(transac);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to find associated server transaction.");
+ // Send "408 Request Timeout" (should be done by the transaction layer)?
+ }
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid parameter");
+ }
+ return ret;
+}
+
+int tsip_dialog_apply_action(tsip_message_t* message, const tsip_action_t* action)
+{
+ const tsk_list_item_t* item;
+
+ if(!message || !action){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* SIP headers */
+ tsk_list_foreach(item, action->headers){
+ TSIP_MESSAGE_ADD_HEADER(message, TSIP_HEADER_DUMMY_VA_ARGS(TSK_PARAM(item->data)->name, TSK_PARAM(item->data)->value));
+ }
+ /* Payload */
+ if(action->payload){
+ tsip_message_add_content(message, tsk_null, TSK_BUFFER_DATA(action->payload), TSK_BUFFER_SIZE(action->payload));
+ }
+
+ return 0;
+}
+
+/**
+ * Gets the number of milliseconds to wait before retransmission.
+ * e.g. ==> delay before refreshing registrations (REGISTER), subscribtions (SUBSCRIBE), publication (PUBLISH) ...
+ *
+ *
+ * @param [in,out] self The calling dialog.
+ * @param [in,out] response The SIP/IMS response containing the new delay (expires, subscription-state ...).
+ *
+ * @return Zero if succeed and no-zero error code otherwise.
+**/
+int64_t tsip_dialog_get_newdelay(tsip_dialog_t *self, const tsip_message_t* message)
+{
+ int64_t expires = self->expires;
+ int64_t newdelay = expires; /* default value */
+ const tsip_header_t* hdr;
+ tsk_size_t i;
+
+ /*== NOTIFY with subscription-state header with expires parameter.
+ */
+ if(TSIP_REQUEST_IS_NOTIFY(message)){
+ const tsip_header_Subscription_State_t *hdr_state;
+ if((hdr_state = (const tsip_header_Subscription_State_t*)tsip_message_get_header(message, tsip_htype_Subscription_State))){
+ if(hdr_state->expires >0){
+ expires = TSK_TIME_S_2_MS(hdr_state->expires);
+ goto compute;
+ }
+ }
+ }
+
+ /*== Expires header.
+ */
+ if((hdr = tsip_message_get_header(message, tsip_htype_Expires))){
+ expires = TSK_TIME_S_2_MS(((const tsip_header_Expires_t*)hdr)->delta_seconds);
+ goto compute;
+ }
+
+ /*== Contact header.
+ */
+ for(i=0; (hdr = tsip_message_get_headerAt(message, tsip_htype_Contact, i)); i++){
+ const tsip_header_Contact_t* contact = (const tsip_header_Contact_t*)hdr;
+ if(contact && contact->uri)
+ {
+ const char* transport = tsk_params_get_param_value(contact->uri->params, "transport");
+ tsip_uri_t* contactUri = tsip_stack_get_contacturi(TSIP_DIALOG_GET_STACK(self), transport ? transport : "udp");
+ if(contactUri)
+ {
+ if(tsk_strequals(contact->uri->user_name, contactUri->user_name)
+ && tsk_strequals(contact->uri->host, contactUri->host)
+ && contact->uri->port == contactUri->port)
+ {
+ if(contact->expires>=0){ /* No expires parameter ==> -1*/
+ expires = TSK_TIME_S_2_MS(contact->expires);
+
+ TSK_OBJECT_SAFE_FREE(contactUri);
+ goto compute;
+ }
+ }
+ TSK_OBJECT_SAFE_FREE(contactUri);
+ }
+ }
+ }
+
+ /*
+ * 3GPP TS 24.229 -
+ *
+ * The UE shall reregister the public user identity either 600 seconds before the expiration time if the initial
+ * registration was for greater than 1200 seconds, or when half of the time has expired if the initial registration
+ * was for 1200 seconds or less.
+ */
+compute:
+ expires = TSK_TIME_MS_2_S(expires);
+ newdelay = (expires > 1200) ? (expires - 600) : (expires/2);
+
+ return TSK_TIME_S_2_MS(newdelay);
+}
+
+/**
+ *
+ * Updates the dialog state:
+ * - Authorizations (using challenges from the @a response message)
+ * - State (early, established, disconnected, ...)
+ * - Routes (and Service-Route)
+ * - Target (remote)
+ * - ...
+ *
+ * @param [in,out] self The calling dialog.
+ * @param [in,out] response The SIP/IMS response from which to get the new information.
+ *
+ * @return Zero if succeed and no-zero error code otherwise.
+**/
+int tsip_dialog_update(tsip_dialog_t *self, const tsip_response_t* response)
+{
+ if(self && TSIP_MESSAGE_IS_RESPONSE(response) && response->To){
+ short code = TSIP_RESPONSE_CODE(response);
+ const char *tag = response->To->tag;
+
+ /*
+ * 1xx (!100) or 2xx
+ */
+ /*
+ * 401 or 407 or 421 or 494
+ */
+ if(code == 401 || code == 407 || code == 421 || code == 494)
+ {
+ tsk_bool_t acceptNewVector;
+
+ /* 3GPP IMS - Each authentication vector is used only once.
+ * ==> Re-registration/De-registration ==> Allow 401/407 challenge.
+ */
+ acceptNewVector = (TSIP_RESPONSE_IS_TO_REGISTER(response) && self->state == tsip_established);
+ return tsip_dialog_update_challenges(self, response, acceptNewVector);
+ }
+ else if(100 < code && code < 300)
+ {
+ tsip_dialog_state_t state = self->state;
+
+ /* 1xx */
+ if(code <= 199){
+ if(tsk_strnullORempty(response->To->tag)){
+ TSK_DEBUG_WARN("Invalid tag parameter");
+ return 0;
+ }
+ state = tsip_early;
+ }
+ /* 2xx */
+ else{
+ state = tsip_established;
+ }
+
+ /* Remote target */
+ {
+ /* RFC 3261 12.2.1.2 Processing the Responses
+ When a UAC receives a 2xx response to a target refresh request, it
+ MUST replace the dialog's remote target URI with the URI from the
+ Contact header field in that response, if present.
+
+ FIXME: Because PRACK/UPDATE sent before the session is established MUST have
+ the rigth target URI to be delivered to the UAS ==> Do not not check that we are connected
+ */
+ if(!TSIP_RESPONSE_IS_TO_REGISTER(response) && response->Contact && response->Contact->uri){
+ TSK_OBJECT_SAFE_FREE(self->uri_remote_target);
+ self->uri_remote_target = tsip_uri_clone(response->Contact->uri, tsk_true, tsk_false);
+ }
+ }
+
+ /* Route sets */
+ {
+ tsk_size_t index;
+ const tsip_header_Record_Route_t *recordRoute;
+ tsip_header_Record_Route_t *route;
+
+ TSK_OBJECT_SAFE_FREE(self->record_routes);
+
+ for(index = 0; (recordRoute = (const tsip_header_Record_Route_t *)tsip_message_get_headerAt(response, tsip_htype_Record_Route, index)); index++){
+ if(!self->record_routes){
+ self->record_routes = tsk_list_create();
+ }
+ if((route = tsk_object_ref((void*)recordRoute))){
+ tsk_list_push_front_data(self->record_routes, (void**)&route); /* Copy reversed. */
+ }
+ }
+ }
+
+
+ /* cseq + tags + ... */
+ if(self->state == tsip_established && tsk_striequals(self->tag_remote, tag)){
+ return 0;
+ }
+ else{
+ if(!TSIP_RESPONSE_IS_TO_REGISTER(response) && !TSIP_RESPONSE_IS_TO_PUBLISH(response)){ /* REGISTER and PUBLISH don't establish dialog */
+ tsk_strupdate(&self->tag_remote, tag);
+ }
+#if 0 // PRACK and BYE will have same CSeq value ==> Let CSeq value to be incremented by "tsip_dialog_request_new()"
+ self->cseq_value = response->CSeq ? response->CSeq->seq : self->cseq_value;
+#endif
+ }
+
+ self->state = state;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+int tsip_dialog_update_2(tsip_dialog_t *self, const tsip_request_t* invite)
+{
+ if(!self || !invite){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* Remote target */
+ if(invite->Contact && invite->Contact->uri){
+ TSK_OBJECT_SAFE_FREE(self->uri_remote_target);
+ self->uri_remote_target = tsip_uri_clone(invite->Contact->uri, tsk_true, tsk_false);
+ }
+
+ /* cseq + tags + remote-uri */
+ tsk_strupdate(&self->tag_remote, invite->From?invite->From->tag:"doubango");
+ /* self->cseq_value = invite->CSeq ? invite->CSeq->seq : self->cseq_value; */
+ if(invite->From && invite->From->uri){
+ TSK_OBJECT_SAFE_FREE(self->uri_remote);
+ self->uri_remote = tsk_object_ref(invite->From->uri);
+ }
+
+ /* Route sets */
+ {
+ tsk_size_t index;
+ const tsip_header_Record_Route_t *recordRoute;
+ tsip_header_Record_Route_t* route;
+
+ TSK_OBJECT_SAFE_FREE(self->record_routes);
+
+ for(index = 0; (recordRoute = (const tsip_header_Record_Route_t *)tsip_message_get_headerAt(invite, tsip_htype_Record_Route, index)); index++){
+ if(!self->record_routes){
+ self->record_routes = tsk_list_create();
+ }
+ if((route = tsk_object_ref((void*)recordRoute))){
+ tsk_list_push_back_data(self->record_routes, (void**)&route); /* Copy non-reversed. */
+ }
+ }
+ }
+
+ self->state = tsip_established;
+
+ return 0;
+}
+
+int tsip_dialog_getCKIK(tsip_dialog_t *self, AKA_CK_T *ck, AKA_IK_T *ik)
+{
+ tsk_list_item_t *item;
+ tsip_challenge_t *challenge;
+
+ if(!self){
+ return -1;
+ }
+
+ tsk_list_foreach(item, self->challenges)
+ {
+ if((challenge = item->data)){
+ memcpy(*ck, challenge->ck, AKA_CK_SIZE);
+ memcpy(*ik, challenge->ik, AKA_IK_SIZE);
+ return 0;
+ }
+ }
+ TSK_DEBUG_ERROR("No challenge found. Fail to set IK and CK.");
+ return -2;
+}
+
+int tsip_dialog_update_challenges(tsip_dialog_t *self, const tsip_response_t* response, int acceptNewVector)
+{
+ int ret = -1;
+ tsk_size_t i;
+
+ tsk_list_item_t *item;
+
+ tsip_challenge_t *challenge;
+
+ const tsip_header_WWW_Authenticate_t *WWW_Authenticate;
+ const tsip_header_Proxy_Authenticate_t *Proxy_Authenticate;
+
+ /* RFC 2617 - HTTP Digest Session
+
+ * (A) The client response to a WWW-Authenticate challenge for a protection
+ space starts an authentication session with that protection space.
+ The authentication session lasts until the client receives another
+ WWW-Authenticate challenge from any server in the protection space.
+
+ (B) The server may return a 401 response with a new nonce value, causing the client
+ to retry the request; by specifying stale=TRUE with this response,
+ the server tells the client to retry with the new nonce, but without
+ prompting for a new username and password.
+ */
+ /* RFC 2617 - 1.2 Access Authentication Framework
+ The realm directive (case-insensitive) is required for all authentication schemes that issue a challenge.
+ */
+
+ /* FIXME: As we perform the same task ==> Use only one loop.
+ */
+
+ for(i =0; (WWW_Authenticate = (const tsip_header_WWW_Authenticate_t*)tsip_message_get_headerAt(response, tsip_htype_WWW_Authenticate, i)); i++){
+ tsk_bool_t isnew = tsk_true;
+
+ tsk_list_foreach(item, self->challenges){
+ challenge = item->data;
+ if(challenge->isproxy) continue;
+
+ if(tsk_striequals(challenge->realm, WWW_Authenticate->realm) && (WWW_Authenticate->stale || acceptNewVector)){
+ /*== (B) ==*/
+ if((ret = tsip_challenge_update(challenge,
+ WWW_Authenticate->scheme,
+ WWW_Authenticate->realm,
+ WWW_Authenticate->nonce,
+ WWW_Authenticate->opaque,
+ WWW_Authenticate->algorithm,
+ WWW_Authenticate->qop)))
+ {
+ return ret;
+ }
+ else{
+ isnew = tsk_false;
+ continue;
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to handle new challenge");
+ return -1;
+ }
+ }
+
+ if(isnew){
+ if((challenge = tsip_challenge_create(TSIP_DIALOG_GET_STACK(self),
+ tsk_false,
+ WWW_Authenticate->scheme,
+ WWW_Authenticate->realm,
+ WWW_Authenticate->nonce,
+ WWW_Authenticate->opaque,
+ WWW_Authenticate->algorithm,
+ WWW_Authenticate->qop)))
+ {
+ if(TSIP_DIALOG_GET_SS(self)->auth_ha1 && TSIP_DIALOG_GET_SS(self)->auth_impi){
+ tsip_challenge_set_cred(challenge, TSIP_DIALOG_GET_SS(self)->auth_impi, TSIP_DIALOG_GET_SS(self)->auth_ha1);
+ }
+ tsk_list_push_back_data(self->challenges, (void**)&challenge);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to handle new challenge");
+ return -1;
+ }
+ }
+ }
+
+ for(i=0; (Proxy_Authenticate = (const tsip_header_Proxy_Authenticate_t*)tsip_message_get_headerAt(response, tsip_htype_Proxy_Authenticate, i)); i++){
+ tsk_bool_t isnew = tsk_true;
+
+ tsk_list_foreach(item, self->challenges){
+ challenge = item->data;
+ if(!challenge->isproxy){
+ continue;
+ }
+
+ if(tsk_striequals(challenge->realm, Proxy_Authenticate->realm) && (Proxy_Authenticate->stale || acceptNewVector)){
+ /*== (B) ==*/
+ if((ret = tsip_challenge_update(challenge,
+ Proxy_Authenticate->scheme,
+ Proxy_Authenticate->realm,
+ Proxy_Authenticate->nonce,
+ Proxy_Authenticate->opaque,
+ Proxy_Authenticate->algorithm,
+ Proxy_Authenticate->qop)))
+ {
+ return ret;
+ }
+ else{
+ isnew = tsk_false;
+ continue;
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to handle new challenge");
+ return -1;
+ }
+ }
+
+ if(isnew){
+ if((challenge = tsip_challenge_create(TSIP_DIALOG_GET_STACK(self),
+ tsk_true,
+ Proxy_Authenticate->scheme,
+ Proxy_Authenticate->realm,
+ Proxy_Authenticate->nonce,
+ Proxy_Authenticate->opaque,
+ Proxy_Authenticate->algorithm,
+ Proxy_Authenticate->qop)))
+ {
+ if(TSIP_DIALOG_GET_SS(self)->auth_ha1 && TSIP_DIALOG_GET_SS(self)->auth_impi){
+ tsip_challenge_set_cred(challenge, TSIP_DIALOG_GET_SS(self)->auth_impi, TSIP_DIALOG_GET_SS(self)->auth_ha1);
+ }
+ tsk_list_push_back_data(self->challenges, (void**)&challenge);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to handle new challenge");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+int tsip_dialog_add_session_headers(const tsip_dialog_t *self, tsip_request_t* request)
+{
+ if(!self || !request){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ TSIP_DIALOG_ADD_HEADERS(self->ss->headers);
+ return 0;
+}
+
+int tsip_dialog_add_common_headers(const tsip_dialog_t *self, tsip_request_t* request)
+{
+ tsk_bool_t earlyIMS = tsk_false;
+ const tsip_uri_t* preferred_identity = tsk_null;
+ const char* netinfo = tsk_null;
+
+ if(!self || !request){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ earlyIMS = TSIP_DIALOG_GET_STACK(self)->security.earlyIMS;
+ preferred_identity = TSIP_DIALOG_GET_STACK(self)->identity.preferred;
+
+ //
+ // P-Preferred-Identity
+ //
+ if(preferred_identity && TSIP_STACK_MODE_IS_CLIENT(TSIP_DIALOG_GET_STACK(self))){
+ /* 3GPP TS 33.978 6.2.3.1 Procedures at the UE
+ The UE shall use the temporary public user identity (IMSI-derived IMPU, cf. section 6.1.2) only in registration
+ messages (i.e. initial registration, re-registration or de-registration), but not in any other type of SIP requests.
+ */
+ switch(request->line.request.request_type){
+ case tsip_BYE:
+ case tsip_INVITE:
+ case tsip_OPTIONS:
+ case tsip_SUBSCRIBE:
+ case tsip_NOTIFY:
+ case tsip_REFER:
+ case tsip_MESSAGE:
+ case tsip_PUBLISH:
+ case tsip_REGISTER:
+ {
+ if(!earlyIMS || (earlyIMS && TSIP_REQUEST_IS_REGISTER(request))){
+ TSIP_MESSAGE_ADD_HEADER(request,
+ TSIP_HEADER_P_PREFERRED_IDENTITY_VA_ARGS(preferred_identity)
+ );
+ }
+ break;
+ }
+ default:break;
+ }
+ }
+
+ //
+ // P-Access-Network-Info
+ //
+ if(netinfo)
+ {
+ switch(request->line.request.request_type){
+ case tsip_BYE:
+ case tsip_INVITE:
+ case tsip_OPTIONS:
+ case tsip_REGISTER:
+ case tsip_SUBSCRIBE:
+ case tsip_NOTIFY:
+ case tsip_PRACK:
+ case tsip_INFO:
+ case tsip_UPDATE:
+ case tsip_REFER:
+ case tsip_MESSAGE:
+ case tsip_PUBLISH:
+ {
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_P_ACCESS_NETWORK_INFO_VA_ARGS(netinfo));
+ break;
+ }
+ default: break;
+ }
+ }
+
+ return 0;
+}
+
+int tsip_dialog_init(tsip_dialog_t *self, tsip_dialog_type_t type, const char* call_id, tsip_ssession_t* ss, tsk_fsm_state_id curr, tsk_fsm_state_id term)
+{
+ static tsip_dialog_id_t unique_id = 0;
+ if(self){
+ if(self->initialized){
+ TSK_DEBUG_WARN("Dialog already initialized.");
+ return -2;
+ }
+
+ self->state = tsip_initial;
+ self->type = type;
+ self->id = ++unique_id;
+ self->connected_fd = TNET_INVALID_FD;
+ if(!self->record_routes){
+ self->record_routes = tsk_list_create();
+ }
+ if(!self->challenges){
+ self->challenges = tsk_list_create();
+ }
+
+ /* Sets some defalt values */
+ self->expires = TSIP_SSESSION_EXPIRES_DEFAULT;
+
+ if(call_id){
+ /* "server-side" session */
+ tsk_strupdate(&self->callid, call_id);
+ }
+ else{
+ tsk_uuidstring_t uuid; /* Call-id is a random UUID */
+ tsip_header_Call_ID_random(&uuid);
+ tsk_strupdate(&self->callid, uuid);
+ }
+
+ /* ref SIP session */
+ self->ss = tsk_object_ref(ss);
+
+ /* Local tag */{
+ tsk_istr_t tag;
+ tsk_strrandom(&tag);
+ tsk_strupdate(&self->tag_local, tag);
+ }
+
+ /* CSeq */
+ self->cseq_value = (rand() + 1);
+
+ /* FSM */
+ self->fsm = tsk_fsm_create(curr, term);
+
+ /*=== SIP Session ===*/
+ if(self->ss != TSIP_SSESSION_INVALID_HANDLE){
+
+ /* Expires */
+ self->expires = ss->expires;
+
+ /* From */
+ self->uri_local = tsk_object_ref(call_id/* "server-side" */ ? ss->to : ss->from);
+
+ /* To */
+ if(ss->to){
+ self->uri_remote = tsk_object_ref(ss->to);
+ self->uri_remote_target = tsk_object_ref(ss->to); /* Request-URI. */
+ }
+ else{
+ self->uri_remote = tsk_object_ref(ss->from);
+ self->uri_remote_target = tsk_object_ref((void*)TSIP_DIALOG_GET_STACK(self)->network.realm);
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid SIP Session id.");
+ }
+
+ tsk_safeobj_init(self);
+
+ self->initialized = tsk_true;
+ return 0;
+ }
+ return -1;
+}
+
+int tsip_dialog_fsm_act(tsip_dialog_t* self, tsk_fsm_action_id action_id, const tsip_message_t* message, const tsip_action_handle_t* action)
+{
+ int ret;
+ tsip_dialog_t* copy;
+ if(!self || !self->fsm){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return -1;
+ }
+
+ tsk_safeobj_lock(self);
+ copy = tsk_object_ref(self); /* keep a copy because tsk_fsm_act() could destroy the dialog */
+ ret = tsip_dialog_set_curr_action(copy, action);
+ ret = tsk_fsm_act(copy->fsm, action_id, copy, message, copy, message, action);
+ tsk_safeobj_unlock(copy);
+ tsk_object_unref(copy);
+
+ return ret;
+}
+
+/*
+This function is used to know if we need to keep the same action handle after receiving a response to our last action.
+*/
+tsk_bool_t tsip_dialog_keep_action(const tsip_dialog_t* self, const tsip_response_t *response)
+{
+ if(self && response){
+ const short code = TSIP_RESPONSE_CODE(response);
+ return
+ TSIP_RESPONSE_IS_1XX(response) ||
+ (code == 401 || code == 407 || code == 421 || code == 494) ||
+ (code == 422 || code == 423);
+ }
+ return tsk_false;
+}
+
+int tsip_dialog_set_connected_fd(tsip_dialog_t* self, tnet_fd_t fd)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ self->connected_fd = fd;
+ return 0;
+}
+
+int tsip_dialog_set_curr_action(tsip_dialog_t* self, const tsip_action_t* action)
+{
+ tsip_action_t* new_action;
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return -1;
+ }
+
+ new_action = tsk_object_ref((void*)action);
+ TSK_OBJECT_SAFE_FREE(self->curr_action);
+ self->curr_action = new_action;
+ return 0;
+}
+
+int tsip_dialog_set_lasterror_2(tsip_dialog_t* self, const char* phrase, short code, const tsip_message_t *message)
+{
+ if(!self || tsk_strnullORempty(phrase)){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return -1;
+ }
+
+ tsk_strupdate(&self->last_error.phrase, phrase);
+ self->last_error.code = code;
+ TSK_OBJECT_SAFE_FREE(self->last_error.message);
+ if(message){
+ self->last_error.message = (tsip_message_t*)tsk_object_ref((void*)message);
+ }
+ return 0;
+}
+
+int tsip_dialog_set_lasterror(tsip_dialog_t* self, const char* phrase, short code)
+{
+ return tsip_dialog_set_lasterror_2(self, phrase, code, tsk_null);
+}
+
+int tsip_dialog_get_lasterror(const tsip_dialog_t* self, short *code, const char** phrase, const tsip_message_t **message)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter.");
+ return -1;
+ }
+
+ if(code){
+ *code = self->last_error.code;
+ }
+ if(phrase){
+ *phrase = self->last_error.phrase;
+ }
+
+ if(message){
+ *message = self->last_error.message;
+ }
+
+ return 0;
+}
+
+int tsip_dialog_hangup(tsip_dialog_t *self, const tsip_action_t* action)
+{
+ if(self){
+ // CANCEL should only be sent for INVITE dialog
+ if(self->type != tsip_dialog_INVITE || self->state == tsip_established){
+ return tsip_dialog_fsm_act(self, tsip_atype_hangup, tsk_null, action);
+ }
+ else{
+ return tsip_dialog_fsm_act(self, tsip_atype_cancel, tsk_null, action);
+ }
+ }
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+}
+
+int tsip_dialog_shutdown(tsip_dialog_t *self, const tsip_action_t* action)
+{
+ if(self){
+ return tsip_dialog_fsm_act(self, tsip_atype_shutdown, tsk_null, action);
+ }
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+}
+
+int tsip_dialog_signal_transport_error(tsip_dialog_t *self)
+{
+ if(self){
+ return tsip_dialog_fsm_act(self, tsip_atype_transport_error, tsk_null, tsk_null);
+ }
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+}
+
+int tsip_dialog_remove(const tsip_dialog_t* self)
+{
+ return tsip_dialog_layer_remove(TSIP_DIALOG_GET_STACK(self)->layer_dialog, TSIP_DIALOG(self));
+}
+
+int tsip_dialog_cmp(const tsip_dialog_t *d1, const tsip_dialog_t *d2)
+{
+ if(d1 && d2){
+ if(
+ tsk_strequals(d1->callid, d2->callid)
+ && (tsk_strequals(d1->tag_local, d2->tag_local))
+ && (tsk_strequals(d1->tag_remote, d2->tag_remote))
+ )
+ {
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int tsip_dialog_deinit(tsip_dialog_t *self)
+{
+ if(self){
+ if(!self->initialized){
+ TSK_DEBUG_WARN("Dialog not initialized.");
+ return -2;
+ }
+
+ /* Cancel all transactions associated to this dialog (do it here before the dialog becomes unsafe) */
+ tsip_transac_layer_cancel_by_dialog(TSIP_DIALOG_GET_STACK(self)->layer_transac, self);
+
+ /* Remove the dialog from the Stream peers */
+ tsip_dialog_layer_remove_callid_from_stream_peers(TSIP_DIALOG_GET_STACK(self)->layer_dialog, self->callid);
+
+ TSK_OBJECT_SAFE_FREE(self->ss);
+ TSK_OBJECT_SAFE_FREE(self->curr_action);
+
+ TSK_OBJECT_SAFE_FREE(self->uri_local);
+ TSK_FREE(self->tag_local);
+ TSK_OBJECT_SAFE_FREE(self->uri_remote);
+ TSK_FREE(self->tag_remote);
+
+ TSK_OBJECT_SAFE_FREE(self->uri_remote_target);
+
+ TSK_FREE(self->cseq_method);
+ TSK_FREE(self->callid);
+
+ TSK_FREE(self->last_error.phrase);
+ TSK_OBJECT_SAFE_FREE(self->last_error.message);
+
+ TSK_OBJECT_SAFE_FREE(self->record_routes);
+ TSK_OBJECT_SAFE_FREE(self->challenges);
+
+ TSK_OBJECT_SAFE_FREE(self->fsm);
+
+ tsk_safeobj_deinit(self);
+
+ self->initialized = 0;
+
+ return 0;
+ }
+ return -1;
+}
+
diff --git a/tinySIP/src/dialogs/tsip_dialog_info.c b/tinySIP/src/dialogs/tsip_dialog_info.c
new file mode 100644
index 0000000..0ff2536
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_info.c
@@ -0,0 +1,556 @@
+/* Copyright (C) 2011 Doubango Telecom <http://www.doubango.org>
+* Copyright (C) 2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango(dot)org>
+*
+* 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_info.c
+ * @brief SIP dialog message (Client side).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_info.h"
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tinysip/api/tsip_api_info.h"
+
+#include "tinysip/headers/tsip_header_Dummy.h"
+#include "tinysip/headers/tsip_header_Min_Expires.h"
+
+#include "tinysip/transactions/tsip_transac_layer.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+#include "tsk_time.h"
+
+#define DEBUG_STATE_MACHINE 1
+#define TSIP_DIALOG_INFO_SIGNAL(self, type, code, phrase, message) \
+ tsip_info_event_signal(type, TSIP_DIALOG(self)->ss, code, phrase, message)
+
+/* ======================== internal functions ======================== */
+static int send_INFO(tsip_dialog_info_t *self);
+static int tsip_dialog_info_OnTerminated(tsip_dialog_info_t *self);
+
+/* ======================== transitions ======================== */
+static int tsip_dialog_info_Started_2_Sending_X_sendINFO(va_list *app);
+static int tsip_dialog_info_Started_2_Receiving_X_recvINFO(va_list *app);
+static int tsip_dialog_info_Sending_2_Sending_X_1xx(va_list *app);
+static int tsip_dialog_info_Sending_2_Terminated_X_2xx(va_list *app);
+static int tsip_dialog_info_Sending_2_Sending_X_401_407_421_494(va_list *app);
+static int tsip_dialog_info_Sending_2_Terminated_X_300_to_699(va_list *app);
+static int tsip_dialog_info_Sending_2_Terminated_X_cancel(va_list *app);
+static int tsip_dialog_info_Receiving_2_Terminated_X_accept(va_list *app);
+static int tsip_dialog_info_Receiving_2_Terminated_X_reject(va_list *app);
+static int tsip_dialog_info_Any_2_Terminated_X_transportError(va_list *app);
+static int tsip_dialog_info_Any_2_Terminated_X_Error(va_list *app);
+
+/* ======================== conds ======================== */
+
+/* ======================== actions ======================== */
+typedef enum _fsm_action_e
+{
+ _fsm_action_sendINFO = tsip_atype_info_send,
+ _fsm_action_accept = tsip_atype_accept,
+ _fsm_action_reject = tsip_atype_reject,
+ _fsm_action_cancel = tsip_atype_cancel,
+ _fsm_action_shutdown = tsip_atype_shutdown,
+ _fsm_action_transporterror = tsip_atype_transport_error,
+
+ _fsm_action_receiveINFO = 0xFF,
+ _fsm_action_1xx,
+ _fsm_action_2xx,
+ _fsm_action_401_407_421_494,
+ _fsm_action_300_to_699,
+ _fsm_action_error,
+}
+_fsm_action_t;
+
+/* ======================== states ======================== */
+typedef enum _fsm_state_e
+{
+ _fsm_state_Started,
+ _fsm_state_Sending,
+ _fsm_state_Receiving,
+ _fsm_state_Terminated
+}
+_fsm_state_t;
+
+
+static int tsip_dialog_info_event_callback(const tsip_dialog_info_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))
+ {
+ if(TSIP_RESPONSE_IS_1XX(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_1xx, msg, tsk_null);
+ }
+ else if(TSIP_RESPONSE_IS_2XX(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_2xx, msg, tsk_null);
+ }
+ else if(TSIP_RESPONSE_CODE(msg) == 401 || TSIP_RESPONSE_CODE(msg) == 407 || TSIP_RESPONSE_CODE(msg) == 421 || TSIP_RESPONSE_CODE(msg) == 494){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_401_407_421_494, msg, tsk_null);
+ }
+ else if(TSIP_RESPONSE_IS_3456(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_300_to_699, msg, tsk_null);
+ }
+ else{ /* Should never happen */
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_error, msg, tsk_null);
+ }
+ }
+ else if (TSIP_REQUEST_IS_INFO(msg)){ /* have been checked by dialog layer...but */
+ // REQUEST ==> Incoming INFO
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_receiveINFO, msg, tsk_null);
+ }
+ }
+ break;
+ }
+
+ case tsip_dialog_canceled:
+ {
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_cancel, msg, tsk_null);
+ break;
+ }
+
+ case tsip_dialog_terminated:
+ case tsip_dialog_timedout:
+ 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;
+}
+
+tsip_dialog_info_t* tsip_dialog_info_create(const tsip_ssession_handle_t* ss)
+{
+ return tsk_object_new(tsip_dialog_info_def_t, ss);
+}
+
+int tsip_dialog_info_init(tsip_dialog_info_t *self)
+{
+ //const tsk_param_t* param;
+
+ /* Initialize the state machine. */
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Started ===
+ */
+ // Started -> (send) -> Sending
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_sendINFO, _fsm_state_Sending, tsip_dialog_info_Started_2_Sending_X_sendINFO, "tsip_dialog_info_Started_2_Sending_X_sendINFO"),
+ // Started -> (receive) -> Receiving
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_receiveINFO, _fsm_state_Receiving, tsip_dialog_info_Started_2_Receiving_X_recvINFO, "tsip_dialog_info_Started_2_Receiving_X_recvINFO"),
+ // Started -> (Any) -> Started
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_dialog_info_Started_2_Started_X_any"),
+
+
+ /*=======================
+ * === Sending ===
+ */
+ // Sending -> (1xx) -> Sending
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_1xx, _fsm_state_Sending, tsip_dialog_info_Sending_2_Sending_X_1xx, "tsip_dialog_info_Sending_2_Sending_X_1xx"),
+ // Sending -> (2xx) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_2xx, _fsm_state_Terminated, tsip_dialog_info_Sending_2_Terminated_X_2xx, "tsip_dialog_info_Sending_2_Terminated_X_2xx"),
+ // Sending -> (401/407/421/494) -> Sending
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_401_407_421_494, _fsm_state_Sending, tsip_dialog_info_Sending_2_Sending_X_401_407_421_494, "tsip_dialog_info_Sending_2_Sending_X_401_407_421_494"),
+ // Sending -> (300_to_699) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_300_to_699, _fsm_state_Terminated, tsip_dialog_info_Sending_2_Terminated_X_300_to_699, "tsip_dialog_info_Sending_2_Terminated_X_300_to_699"),
+ // Sending -> (cancel) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_cancel, _fsm_state_Terminated, tsip_dialog_info_Sending_2_Terminated_X_cancel, "tsip_dialog_info_Sending_2_Terminated_X_cancel"),
+ // Sending -> (shutdown) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_shutdown, _fsm_state_Terminated, tsk_null, "tsip_dialog_info_Sending_2_Terminated_X_shutdown"),
+ // Sending -> (Any) -> Sending
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Sending, "tsip_dialog_info_Sending_2_Sending_X_any"),
+
+ /*=======================
+ * === Receiving ===
+ */
+ // Receiving -> (accept) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Receiving, _fsm_action_accept, _fsm_state_Terminated, tsip_dialog_info_Receiving_2_Terminated_X_accept, "tsip_dialog_info_Receiving_2_Terminated_X_accept"),
+ // Receiving -> (rejected) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Receiving, _fsm_action_reject, _fsm_state_Terminated, tsip_dialog_info_Receiving_2_Terminated_X_reject, "tsip_dialog_info_Receiving_2_Terminated_X_reject"),
+ // Receiving -> (Any) -> Receiving
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Receiving, "tsip_dialog_info_Receiving_2_Receiving_X_any"),
+
+ /*=======================
+ * === Any ===
+ */
+ // Any -> (transport error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, tsip_dialog_info_Any_2_Terminated_X_transportError, "tsip_dialog_info_Any_2_Terminated_X_transportError"),
+ // Any -> (transport error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_error, _fsm_state_Terminated, tsip_dialog_info_Any_2_Terminated_X_Error, "tsip_dialog_info_Any_2_Terminated_X_Error"),
+
+ TSK_FSM_ADD_NULL());
+
+ TSIP_DIALOG(self)->callback = TSIP_DIALOG_EVENT_CALLBACK_F(tsip_dialog_info_event_callback);
+
+ return 0;
+}
+
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+
+/* Started -> (sendINFO) -> Sending
+*/
+int tsip_dialog_info_Started_2_Sending_X_sendINFO(va_list *app)
+{
+ tsip_dialog_info_t *self;
+ const tsip_action_t* action;
+
+ self = va_arg(*app, tsip_dialog_info_t *);
+ va_arg(*app, tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ TSIP_DIALOG(self)->running = tsk_true;
+
+ return send_INFO(self);
+}
+
+/* Started -> (recvINFO) -> Receiving
+*/
+int tsip_dialog_info_Started_2_Receiving_X_recvINFO(va_list *app)
+{
+ tsip_dialog_info_t *self = va_arg(*app, tsip_dialog_info_t *);
+ const tsip_request_t *request = va_arg(*app, const tsip_request_t *);
+
+ /* Alert the user. */
+ TSIP_DIALOG_INFO_SIGNAL(self, tsip_i_info,
+ tsip_event_code_dialog_request_incoming, "Incoming Request.", request);
+
+ /* Update last incoming INFO */
+ TSK_OBJECT_SAFE_FREE(self->last_iMessage);
+ self->last_iMessage = tsk_object_ref((void*)request);
+
+ return 0;
+}
+
+/* Sending -> (1xx) -> Sending
+*/
+int tsip_dialog_info_Sending_2_Sending_X_1xx(va_list *app)
+{
+ /*tsip_dialog_info_t *self = va_arg(*app, tsip_dialog_info_t *);*/
+ /*const tsip_response_t *response = va_arg(*app, const tsip_response_t *);*/
+
+ return 0;
+}
+
+/* Sending -> (2xx) -> Sending
+*/
+int tsip_dialog_info_Sending_2_Terminated_X_2xx(va_list *app)
+{
+ tsip_dialog_info_t *self = va_arg(*app, tsip_dialog_info_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* Alert the user. */
+ TSIP_DIALOG_INFO_SIGNAL(self, tsip_ao_info,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ /* Reset curr action */
+ tsip_dialog_set_curr_action(TSIP_DIALOG(self), tsk_null);
+
+ return 0;
+}
+
+/* Sending -> (401/407/421/494) -> Sending
+*/
+int tsip_dialog_info_Sending_2_Sending_X_401_407_421_494(va_list *app)
+{
+ tsip_dialog_info_t *self = va_arg(*app, tsip_dialog_info_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_INFO_SIGNAL(self, tsip_ao_info,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return ret;
+ }
+
+ return send_INFO(self);
+}
+
+/* Sending -> (300 to 699) -> Terminated
+*/
+int tsip_dialog_info_Sending_2_Terminated_X_300_to_699(va_list *app)
+{
+ tsip_dialog_info_t *self = va_arg(*app, tsip_dialog_info_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* set last error (or info) */
+ tsip_dialog_set_lasterror(TSIP_DIALOG(self), TSIP_RESPONSE_PHRASE(response), TSIP_RESPONSE_CODE(response));
+
+ /* Alert the user. */
+ TSIP_DIALOG_INFO_SIGNAL(self, tsip_ao_info,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return 0;
+}
+
+/* Sending -> (cancel) -> Terminated
+*/
+int tsip_dialog_info_Sending_2_Terminated_X_cancel(va_list *app)
+{
+ tsip_dialog_info_t *self = va_arg(*app, tsip_dialog_info_t *);
+ /* const tsip_message_t *message = va_arg(*app, const tsip_message_t *); */
+
+ /* RFC 3261 - 9.1 Client Behavior
+ A CANCEL request SHOULD NOT be sent to cancel a request other than INVITE.
+ */
+
+ /* Cancel all transactions associated to this dialog (will also be done when the dialog is destroyed (worth nothing)) */
+ tsip_transac_layer_cancel_by_dialog(TSIP_DIALOG_GET_STACK(self)->layer_transac, TSIP_DIALOG(self));
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_request_cancelled, "INFO cancelled");
+
+ return 0;
+}
+
+/* Receiving -> (accept) -> Terminated
+*/
+int tsip_dialog_info_Receiving_2_Terminated_X_accept(va_list *app)
+{
+ tsip_dialog_info_t *self;
+ const tsip_action_t* action;
+
+ self = va_arg(*app, tsip_dialog_info_t *);
+ va_arg(*app, tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ if(!self->last_iMessage){
+ TSK_DEBUG_ERROR("There is non INFO to accept()");
+ /* Not an error ...but do not update current action */
+ }
+ else{
+ tsip_response_t *response;
+ int ret;
+
+ /* curr_action is only used for outgoing requests */
+ /* tsip_dialog_set_curr_action(TSIP_DIALOG(self), action); */
+
+ /* send 200 OK */
+ if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 200, "OK", self->last_iMessage))){
+ tsip_dialog_apply_action(response, action); /* apply action params to "this" response */
+ if((ret = tsip_dialog_response_send(TSIP_DIALOG(self), response))){
+ TSK_DEBUG_ERROR("Failed to send SIP response.");
+ TSK_OBJECT_SAFE_FREE(response);
+ return ret;
+ }
+ TSK_OBJECT_SAFE_FREE(response);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create SIP response.");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Receiving -> (reject) -> Terminated
+*/
+int tsip_dialog_info_Receiving_2_Terminated_X_reject(va_list *app)
+{
+ tsip_dialog_info_t *self;
+ const tsip_action_t* action;
+
+ self = va_arg(*app, tsip_dialog_info_t *);
+ va_arg(*app, tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ if(!self->last_iMessage){
+ TSK_DEBUG_ERROR("There is non INFO to reject()");
+ /* Not an error ...but do not update current action */
+ }
+ else{
+ tsip_response_t *response;
+ int ret;
+
+ /* curr_action is only used for outgoing requests */
+ /* tsip_dialog_set_curr_action(TSIP_DIALOG(self), action); */
+
+ /* send 486 Rejected */
+ if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 486, "Rejected", self->last_iMessage))){
+ tsip_dialog_apply_action(response, action); /* apply action params to "this" response */
+ if((ret = tsip_dialog_response_send(TSIP_DIALOG(self), response))){
+ TSK_DEBUG_ERROR("Failed to send SIP response.");
+ TSK_OBJECT_SAFE_FREE(response);
+ return ret;
+ }
+ TSK_OBJECT_SAFE_FREE(response);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create SIP response.");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Any -> (transport error) -> Terminated
+*/
+int tsip_dialog_info_Any_2_Terminated_X_transportError(va_list *app)
+{
+ /*tsip_dialog_info_t *self = va_arg(*app, tsip_dialog_info_t *);*/
+ /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/
+
+ return 0;
+}
+
+/* Any -> (error) -> Terminated
+*/
+int tsip_dialog_info_Any_2_Terminated_X_Error(va_list *app)
+{
+ /*tsip_dialog_info_t *self = va_arg(*app, tsip_dialog_info_t *);*/
+ /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/
+
+ return 0;
+}
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+int send_INFO(tsip_dialog_info_t *self)
+{
+ tsip_request_t* request = tsk_null;
+ int ret = -1;
+
+ if(!self){
+ return -1;
+ }
+
+ if(!(request = tsip_dialog_request_new(TSIP_DIALOG(self), "INFO"))){
+ return -2;
+ }
+
+ /* apply action params to the request */
+ if(TSIP_DIALOG(self)->curr_action){
+ tsip_dialog_apply_action(request, TSIP_DIALOG(self)->curr_action);
+ }
+
+ ret = tsip_dialog_request_send(TSIP_DIALOG(self), request);
+ TSK_OBJECT_SAFE_FREE(request);
+
+ return ret;
+}
+
+
+int tsip_dialog_info_OnTerminated(tsip_dialog_info_t *self)
+{
+ TSK_DEBUG_INFO("=== INFO Dialog terminated ===");
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminated,
+ TSIP_DIALOG(self)->last_error.phrase ? TSIP_DIALOG(self)->last_error.phrase : "Dialog terminated");
+
+ /* Remove from the dialog layer. */
+ return tsip_dialog_remove(TSIP_DIALOG(self));
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP dialog INFO object definition
+//
+static tsk_object_t* tsip_dialog_info_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_dialog_info_t *dialog = self;
+ if(dialog){
+ tsip_ssession_handle_t *ss = va_arg(*app, tsip_ssession_handle_t *);
+
+ /* Initialize base class */
+ tsip_dialog_init(TSIP_DIALOG(self), tsip_dialog_INFO, tsk_null, ss, _fsm_state_Started, _fsm_state_Terminated);
+
+ /* FSM */
+ TSIP_DIALOG_GET_FSM(self)->debug = DEBUG_STATE_MACHINE;
+ tsk_fsm_set_callback_terminated(TSIP_DIALOG_GET_FSM(self), TSK_FSM_ONTERMINATED_F(tsip_dialog_info_OnTerminated), (const void*)dialog);
+
+ /* Initialize the class itself */
+ tsip_dialog_info_init(self);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_dialog_info_dtor(tsk_object_t * self)
+{
+ tsip_dialog_info_t *dialog = self;
+ if(dialog){
+ /* DeInitialize base class (will cancel all transactions) */
+ tsip_dialog_deinit(TSIP_DIALOG(self));
+
+ /* DeInitialize self */
+ TSK_OBJECT_SAFE_FREE(dialog->last_iMessage);
+
+ TSK_DEBUG_INFO("*** INFO Dialog destroyed ***");
+ }
+ return self;
+}
+
+static int tsip_dialog_info_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_info_def_s =
+{
+ sizeof(tsip_dialog_info_t),
+ tsip_dialog_info_ctor,
+ tsip_dialog_info_dtor,
+ tsip_dialog_info_cmp,
+};
+const tsk_object_def_t *tsip_dialog_info_def_t = &tsip_dialog_info_def_s;
diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.c b/tinySIP/src/dialogs/tsip_dialog_invite.c
new file mode 100644
index 0000000..ed7b3d5
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_invite.c
@@ -0,0 +1,1942 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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 <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#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 <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#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 =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
+ " <media_control>\r\n"
+ " <vc_primitive>\r\n"
+ " <to_encoder>\r\n"
+ " <picture_fast_update>\r\n"
+ " </picture_fast_update>\r\n"
+ " </to_encoder>\r\n"
+ " <stream_id>%llu</stream_id>\r\n"
+ " </vc_primitive>\r\n"
+ " </media_control>\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;
diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.cdiv.c b/tinySIP/src/dialogs/tsip_dialog_invite.cdiv.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_invite.cdiv.c
diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.client.c b/tinySIP/src/dialogs/tsip_dialog_invite.client.c
new file mode 100644
index 0000000..c1be3c5
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_invite.client.c
@@ -0,0 +1,333 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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 <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_invite.h"
+
+#include "tinysip/dialogs/tsip_dialog_invite.common.h"
+
+#include "tinysip/headers/tsip_header_Session_Expires.h"
+
+#include "tinysdp/parsers/tsdp_parser_message.h"
+
+#include "tnet_transport.h"
+
+#include "tsk_debug.h"
+
+/* ======================== external functions ======================== */
+extern int send_INVITEorUPDATE(tsip_dialog_invite_t *self, tsk_bool_t is_INVITE, tsk_bool_t force_sdp);
+extern int send_ACK(tsip_dialog_invite_t *self, const tsip_response_t* r2xxINVITE);
+extern int send_CANCEL(tsip_dialog_invite_t *self);
+extern int send_BYE(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_msession_configure(tsip_dialog_invite_t *self);
+extern int tsip_dialog_invite_process_ro(tsip_dialog_invite_t *self, const tsip_message_t* message);
+extern int tsip_dialog_invite_stimers_handle(tsip_dialog_invite_t* self, const tsip_message_t* message);
+extern int tsip_dialog_invite_notify_parent(tsip_dialog_invite_t *self, const tsip_response_t* response);
+
+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 int x0000_Any_2_Any_X_i1xx(va_list *app);
+
+/* ======================== transitions ======================== */
+static int c0000_Started_2_Outgoing_X_oINVITE(va_list *app);
+static int c0000_Outgoing_2_Outgoing_X_iINVITEorUPDATE(va_list *app);
+static int c0000_Outgoing_2_Connected_X_i2xxINVITE(va_list *app);
+static int c0000_Outgoing_2_Terminated_X_i300_to_i699INVITE(va_list *app);
+static int c0000_Outgoing_2_Cancelling_X_oCANCEL(va_list *app);
+static int c0000_Cancelling_2_Terminated_X_i300_to_699(va_list *app); /* 487 INVITE (To have more chances, any 300-699) */
+
+/* ======================== 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_resp2CANCEL(tsip_dialog_invite_t* self, tsip_message_t* message)
+{
+ return TSIP_RESPONSE_IS_TO_CANCEL(message);
+}
+
+/* Init FSM */
+int tsip_dialog_invite_client_init(tsip_dialog_invite_t *self)
+{
+ return tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Started ===
+ */
+ // Started -> (send INVITE) -> Outgoing
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_oINVITE, _fsm_state_Outgoing, c0000_Started_2_Outgoing_X_oINVITE, "c0000_Started_2_Outgoing_X_oINVITE"),
+
+ /*=======================
+ * === Outgoing ===
+ */
+ // Outgoing -> (i2xx INVITE) -> Connected
+ TSK_FSM_ADD(_fsm_state_Outgoing, _fsm_action_i2xx, _fsm_cond_is_resp2INVITE, _fsm_state_Connected, c0000_Outgoing_2_Connected_X_i2xxINVITE, "c0000_Outgoing_2_Connected_X_i2xxINVITE"),
+ // Outgoing -> (iINVITE ) -> Outgoing
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Outgoing, _fsm_action_iINVITE, _fsm_state_Outgoing, c0000_Outgoing_2_Outgoing_X_iINVITEorUPDATE, "c0000_Outgoing_2_Outgoing_X_iINVITEorUPDATE"),
+ // Outgoing -> (iUPDATE) -> Outgoing
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Outgoing, _fsm_action_iUPDATE, _fsm_state_Outgoing, c0000_Outgoing_2_Outgoing_X_iINVITEorUPDATE, "c0000_Outgoing_2_Outgoing_X_iINVITEorUPDATE"),
+ // Outgoing -> (oCANCEL) -> Cancelling
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Outgoing, _fsm_action_oCANCEL, _fsm_state_Cancelling, c0000_Outgoing_2_Cancelling_X_oCANCEL, "c0000_Outgoing_2_Cancelling_X_oCANCEL"),
+ // Cancelling -> (any response to cancel CANCEL) -> Cancelling
+ TSK_FSM_ADD(_fsm_state_Cancelling, _fsm_action_i300_to_i699, _fsm_cond_is_resp2CANCEL, _fsm_state_Terminated, tsk_null, "c0000_Cancelling_2_Terminated_X_i300_to_699"),
+ TSK_FSM_ADD(_fsm_state_Cancelling, _fsm_action_i2xx, _fsm_cond_is_resp2CANCEL, _fsm_state_Cancelling, tsk_null, "c0000_Cancelling_2_Cancelling_X_i2xx"),
+ // Cancelling -> (i300-699 INVITE) -> Terminated
+ TSK_FSM_ADD(_fsm_state_Cancelling, _fsm_action_i300_to_i699, _fsm_cond_is_resp2INVITE, _fsm_state_Terminated, c0000_Cancelling_2_Terminated_X_i300_to_699, "c0000_Cancelling_2_Terminated_X_i300_to_699"),
+ // Outgoing -> (300-699 INVITE) -> Terminated
+ TSK_FSM_ADD(_fsm_state_Outgoing, _fsm_action_i300_to_i699, _fsm_cond_is_resp2INVITE, _fsm_state_Terminated, c0000_Outgoing_2_Terminated_X_i300_to_i699INVITE, "c0000_Outgoing_2_Terminated_X_i300_to_i699INVITE"),
+
+ TSK_FSM_ADD_NULL());
+}
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+/* Started -> (oINVITE) -> Outgoing
+*/
+int c0000_Started_2_Outgoing_X_oINVITE(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 *);
+
+ /* This is the first FSM transaction when you try to make an audio/video/msrp call */
+ if(!self->msession_mgr){
+ int32_t transport_idx = TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default;
+ self->msession_mgr = tmedia_session_mgr_create(action ? action->media.type : tmedia_all,
+ 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]), tsk_true);
+ if(TSIP_DIALOG_GET_STACK(self)->natt.ctx){
+ ret = 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);
+ ret = tsip_dialog_invite_msession_configure(self);
+ }
+
+ /* We are the client */
+ self->is_client = tsk_true;
+ /* Whether it's a client dialog for call transfer */
+ self->is_transf = (TSIP_DIALOG_GET_SS(self)->id_parent != TSIP_SSESSION_INVALID_ID);
+
+ /* Get Media type from the action */
+ TSIP_DIALOG_GET_SS(self)->media.type = action->media.type;
+ /* Appy media params received from the user */
+ if(!TSK_LIST_IS_EMPTY(action->media.params)){
+ tmedia_session_mgr_set_3(self->msession_mgr, action->media.params);
+ }
+
+ /* RFC 4028 - 7.1. Generating an Initial Session Refresh Request
+
+ A UAC MAY include a Session-Expires header field in an initial
+ session refresh request if it wants a session timer applied to the
+ session. The value of this header field indicates the session
+ interval desired by the UAC. If a Min-SE header is included in the
+ initial session refresh request, the value of the Session-Expires
+ MUST be greater than or equal to the value in Min-SE.
+
+ The UAC MAY include the refresher parameter with value 'uac' if it
+ wants to perform the refreshes. However, it is RECOMMENDED that the
+ parameter be omitted so that it can be selected by the negotiation
+ mechanisms described below.
+ */
+ if(TSIP_DIALOG_GET_SS(self)->media.timers.timeout){
+ self->stimers.timer.timeout = TSIP_DIALOG_GET_SS(self)->media.timers.timeout;
+ tsk_strupdate(&self->stimers.refresher, TSIP_DIALOG_GET_SS(self)->media.timers.refresher);
+ self->stimers.is_refresher = tsk_striequals(self->stimers.refresher, "uac");
+ self->supported.timer = tsk_true;
+ }
+
+ /* QoS
+ * One Voice Profile - 5.4.1 SIP Precondition Considerations
+ * The UE shall use the Supported header, and not the Require header, to indicate the support of precondition in
+ * accordance with Section 5.1.3.1 of 3GPP TS 24.229.
+ */
+ self->qos.type = TSIP_DIALOG_GET_SS(self)->media.qos.type;
+ self->qos.strength = TSIP_DIALOG_GET_SS(self)->media.qos.strength;
+ tmedia_session_mgr_set_qos(self->msession_mgr, self->qos.type, self->qos.strength);
+ self->supported.precondition = (self->qos.strength == tmedia_qos_strength_optional);
+ self->required.precondition = (self->qos.strength == tmedia_qos_strength_mandatory);
+
+ /* send the request */
+ ret = send_INVITE(self, tsk_false);
+
+ /* alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");
+
+ return ret;
+}
+
+/* Outgoing -> (i2xx INVITE) -> Connected
+*/
+int c0000_Outgoing_2_Connected_X_i2xxINVITE(va_list *app)
+{
+ int ret;
+
+ tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *);
+ const tsip_response_t *r2xxINVITE = va_arg(*app, const tsip_response_t *);
+ /* const tsip_action_t* action = */ va_arg(*app, const tsip_action_t *);
+
+ /* Update the dialog state */
+ if((ret = tsip_dialog_update(TSIP_DIALOG(self), r2xxINVITE))){
+ return ret;
+ }
+
+ /* Process remote offer */
+ if((ret = tsip_dialog_invite_process_ro(self, r2xxINVITE))){
+ send_BYE(self);
+ return ret;
+ }
+ else{
+ /* send ACK */
+ ret = send_ACK(self, r2xxINVITE);
+ }
+
+ /* Determine whether the remote party support UPDATE */
+ self->support_update = tsip_message_allowed(r2xxINVITE, "UPDATE");
+
+ /* Session Timers */
+ if(self->stimers.timer.timeout){
+ tsip_dialog_invite_stimers_handle(self, r2xxINVITE);
+ }
+
+ // 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));
+ }
+
+ /* Alert the user (session) */
+ ret = TSIP_DIALOG_INVITE_SIGNAL(self, tsip_ao_request,
+ TSIP_RESPONSE_CODE(r2xxINVITE), TSIP_RESPONSE_PHRASE(r2xxINVITE), r2xxINVITE);
+ /* Alert the user (dialog) */
+ ret = TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connected, "Dialog connected");
+
+ if(self->is_transf){
+ ret = tsip_dialog_invite_notify_parent(self, r2xxINVITE);
+ self->is_transf = tsk_false;//final response -> no longer need to notify the parent
+ }
+
+ /* MSRP File Transfer */
+ /*if(TSIP_DIALOG(self)->curr_action && ((TSIP_DIALOG(self)->curr_action->media.type & tmedia_msrp) == tmedia_msrp)){
+ // FIXME
+ tmedia_session_mgr_send_file(self->msession_mgr, "C:\\avatar.png",
+ TMEDIA_SESSION_SET_NULL());
+ }*/
+
+ return ret;
+}
+
+/* Outgoing -> (iINVITE or iINVITE) -> Outgoing
+*/
+int c0000_Outgoing_2_Outgoing_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 *);
+ tsk_bool_t force_sdp;
+ int ret = 0;
+
+ /* process remote offer */
+ if((ret = tsip_dialog_invite_process_ro(self, rINVITEorUPDATE))){
+ /* Send error */
+ return ret;
+ }
+
+ /* Send 200 OK */
+ // force SDP in 200 OK even if the request has the same SDP version
+ force_sdp = TSIP_MESSAGE_HAS_CONTENT(rINVITEorUPDATE);
+ ret = send_RESPONSE(self, rINVITEorUPDATE, 200, "OK",
+ (self->msession_mgr && (force_sdp || self->msession_mgr->ro_changed || self->msession_mgr->state_changed)));
+
+ /* alert the user */
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_request,
+ tsip_event_code_dialog_request_incoming, "Incoming Request.", rINVITEorUPDATE);
+
+
+ return ret;
+}
+
+/* Outgoing -> (i300-i699 INVITE) -> Terminated
+*/
+int c0000_Outgoing_2_Terminated_X_i300_to_i699INVITE(va_list *app)
+{
+ int ret;
+ tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* set last error (or info) */
+ tsip_dialog_set_lasterror_2(TSIP_DIALOG(self),
+ TSIP_RESPONSE_PHRASE(response), TSIP_RESPONSE_CODE(response), response);
+
+ /* alert the user */
+ ret = TSIP_DIALOG_INVITE_SIGNAL(self, tsip_ao_request,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ if(self->is_transf){
+ ret = tsip_dialog_invite_notify_parent(self, response);
+ self->is_transf = tsk_false;//final response -> no longer need to notify the parent
+ }
+
+ return ret;
+}
+
+/* Outgoing -> (oCANCEL ) -> Cancelling
+*/
+int c0000_Outgoing_2_Cancelling_X_oCANCEL(va_list *app)
+{
+ tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *);
+
+ self->is_cancelling = tsk_true;
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog");
+
+ return send_CANCEL(self);
+}
+
+int c0000_Cancelling_2_Terminated_X_i300_to_699(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 *);
+
+ /* set last error (or info) */
+ tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Request cancelled", TSIP_RESPONSE_CODE(response));
+
+ return 0;
+}
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ \ No newline at end of file
diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.conf.c b/tinySIP/src/dialogs/tsip_dialog_invite.conf.c
new file mode 100644
index 0000000..4ced2f8
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_invite.conf.c
@@ -0,0 +1 @@
+//http://www.quintillion.co.jp/3GPP/Specs/24147-900.pdf \ No newline at end of file
diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.ect.c b/tinySIP/src/dialogs/tsip_dialog_invite.ect.c
new file mode 100644
index 0000000..8c21dd2
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_invite.ect.c
@@ -0,0 +1,487 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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.ect.c
+ * @brief Explicit Communication Transfer (ECT) using IP Multimedia (IM) Core Network (CN) subsystem (3GPP TS 24.629)
+ * The Explicit Communication transfer (ECT) service provides a party involved in a communication to transfer that
+ * communication to a third party.
+ * This code inplement Consultative transfer mode (A.2).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_invite.h"
+
+#include "tinysip/dialogs/tsip_dialog_invite.common.h"
+
+#include "tinysip/parsers/tsip_parser_message.h"
+
+#include "tinysip/headers/tsip_header_Refer_To.h"
+#include "tinysip/headers/tsip_header_Referred_By.h"
+#include "tinysip/headers/tsip_header_Refer_Sub.h"
+#include "tinysip/headers/tsip_header_Supported.h"
+
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tinymedia/tmedia_defaults.h"
+
+#include "tsk_debug.h"
+
+static int send_NOTIFY(tsip_dialog_invite_t *self, short code, const char* phrase);
+static int send_REFER(tsip_dialog_invite_t *self, const char* to);
+static short get_SipFragResponseCode(const tsip_request_t* notify);
+static tsip_response_t * get_SipFragMessage(const tsip_request_t* notify);
+
+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 send_ERROR(tsip_dialog_invite_t* self, const tsip_request_t* request, short code, const char* phrase, const char* reason);
+extern int send_BYE(tsip_dialog_invite_t *self);
+
+/* ======================== transitions ======================== */
+static int x0400_Connected_2_oECTing_X_oECT(va_list *app);
+static int x0400_oECTing_2_oECTing_X_i2xx(va_list *app); // should be 202?
+static int x0400_oECTing_2_Connected_X_i3456(va_list *app);
+static int x0400_oECTing_2_oECTing_X_iNOTIFY(va_list *app); // Notify 1xx
+static int x0400_oECTing_2_Connected_X_iNOTIFY(va_list *app); // Notify 200_to_699
+
+static int x0400_Connected_2_iECTreq_X_iREFER(va_list *app);
+static int x0400_iECTreq_2_Connected_X_reject(va_list *app);
+static int x0400_iECTreq_2_iECTing_X_accept(va_list *app);
+static int x0400_iECTing_2_iECTing_X_1xxfNOTIFY(va_list *app);
+static int x0400_iECTing_2_Connected_X_23456fNOTIFY(va_list *app);
+#define x0400_Connected_2_Connected_X_fREFER tsk_null
+
+/* ======================== conds ======================== */
+static tsk_bool_t _fsm_cond_is_resp2REFER(tsip_dialog_invite_t* self, tsip_message_t* message)
+{
+ return TSIP_RESPONSE_IS_TO_REFER(message);
+}
+static tsk_bool_t _fsm_cond_is_1xxNOTIFY(tsip_dialog_invite_t* self, tsip_request_t* notify)
+{
+ short code = get_SipFragResponseCode(notify);
+ return (code >= 100 && code <= 199);
+}
+static tsk_bool_t _fsm_cond_is_23456NOTIFY(tsip_dialog_invite_t* self, tsip_request_t* notify)
+{
+ short code = get_SipFragResponseCode(notify);
+ return (code >= 200 && code <= 699);
+}
+static tsk_bool_t _fsm_cond_is_fREFER(tsip_dialog_invite_t* self, tsip_request_t* refer)
+{
+ const tsip_header_Refer_To_t* Refer_To = (const tsip_header_Refer_To_t*)tsip_message_get_header(refer, tsip_htype_Refer_To);
+ return (!Refer_To || !Refer_To->uri);
+}
+static tsk_bool_t _fsm_cond_is_1xxfNOTIFY(tsip_dialog_invite_t* self, tsip_response_t* response)
+{
+ short code = TSIP_RESPONSE_CODE(response);
+ return (code >= 100 && code <= 199);
+}
+static tsk_bool_t _fsm_cond_is_23456fNOTIFY(tsip_dialog_invite_t* self, tsip_response_t* response)
+{
+ short code = TSIP_RESPONSE_CODE(response);
+ return (code >= 200 && code <= 699);
+}
+
+int tsip_dialog_invite_ect_init(tsip_dialog_invite_t *self)
+{
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Outgoing Transfer ===
+ */
+ // Connected -> (oREFER) -> oECTing
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_oECT, _fsm_state_oECTing, x0400_Connected_2_oECTing_X_oECT, "x0400_Connected_2_oECTing_X_oECT"),
+ // oECTing -> (i2xx REFER) -> oECTing
+ TSK_FSM_ADD(_fsm_state_oECTing, _fsm_action_i2xx, _fsm_cond_is_resp2REFER, _fsm_state_oECTing, x0400_oECTing_2_oECTing_X_i2xx, "x0400_oECTing_2_oECTing_X_i2xx"),
+ // oECTing -> (i300-699 REFER) -> Connected
+ TSK_FSM_ADD(_fsm_state_oECTing, _fsm_action_i300_to_i699, _fsm_cond_is_resp2REFER, _fsm_state_Connected, x0400_oECTing_2_Connected_X_i3456, "x0400_ECTing_2_Connected_X_i36"),
+ // oECTing -> (iNotify 1xx sipfrag) -> oECTing
+ TSK_FSM_ADD(_fsm_state_oECTing, _fsm_action_iNOTIFY, _fsm_cond_is_1xxNOTIFY, _fsm_state_oECTing, x0400_oECTing_2_oECTing_X_iNOTIFY, "x0400_oECTing_2_oECTing_X_iNOTIFY"),
+ // oECTing -> (iNotify 23456 sipfrag) -> Connected
+ TSK_FSM_ADD(_fsm_state_oECTing, _fsm_action_iNOTIFY, _fsm_cond_is_23456NOTIFY, _fsm_state_Connected, x0400_oECTing_2_Connected_X_iNOTIFY, "x0400_oECTing_2_Connected_X_iNOTIFY"),
+
+ /*=======================
+ * === Incoming Transfer ===
+ */
+ // Connected -> (iREFER invalid) -> Connected
+ TSK_FSM_ADD(_fsm_state_Connected, _fsm_action_iREFER, _fsm_cond_is_fREFER, _fsm_state_Connected, x0400_Connected_2_Connected_X_fREFER, "x0400_Connected_2_Connected_X_fREFER"),
+ // Connected -> (iREFER) -> iECTreq
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_iREFER, _fsm_state_iECTreq, x0400_Connected_2_iECTreq_X_iREFER, "x0400_Connected_2_iECTreq_X_iREFER"),
+ // iECTreq -> (reject) -> Connected
+ TSK_FSM_ADD_ALWAYS(_fsm_state_iECTreq, _fsm_action_iECT_REJECT, _fsm_state_Connected, x0400_iECTreq_2_Connected_X_reject, "x0400_iECTreq_2_Connected_X_reject"),
+ // iECTreq -> (accept) -> iECTing
+ TSK_FSM_ADD_ALWAYS(_fsm_state_iECTreq, _fsm_action_iECT_ACCEPT, _fsm_state_iECTing, x0400_iECTreq_2_iECTing_X_accept, "x0400_iECTreq_2_iECTing_X_accept"),
+ // iECTing -> (1xx lnotify) -> iECTing
+ TSK_FSM_ADD(_fsm_state_iECTing, _fsm_action_iECT_lNOTIFY, _fsm_cond_is_1xxfNOTIFY, _fsm_state_iECTing, x0400_iECTing_2_iECTing_X_1xxfNOTIFY, "x0400_iECTing_2_iECTing_X_1xxfNOTIFY"),
+ // iECTing -> (23456 lnotify) -> Connected
+ TSK_FSM_ADD(_fsm_state_iECTing, _fsm_action_iECT_lNOTIFY, _fsm_cond_is_23456fNOTIFY, _fsm_state_Connected, x0400_iECTing_2_Connected_X_23456fNOTIFY, "x0400_iECTing_2_Connected_X_23456fNOTIFY"),
+
+ TSK_FSM_ADD_NULL());
+
+ return 0;
+}
+
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+// Connected -> (oREFER) -> oECTing
+static int x0400_Connected_2_oECTing_X_oECT(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 *);
+
+ ret = send_REFER(self, action->ect.to);
+
+ if(ret == 0){
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_o_ect_trying,
+ tsip_event_code_dialog_request_sent, "Call Transfer Initiated", tsk_null);
+ }
+ else; //Must never happen
+
+ return ret;
+}
+
+// ECTing -> (i2xx REFER) -> oECTing
+static int x0400_oECTing_2_oECTing_X_i2xx(va_list *app)
+{
+ tsip_dialog_invite_t *self;
+ const tsip_response_t* response;
+ const tsip_header_Refer_Sub_t* Refer_Sub;
+
+ self = va_arg(*app, tsip_dialog_invite_t *);
+ response = va_arg(*app, const tsip_message_t *);
+ Refer_Sub = (const tsip_header_Refer_Sub_t*)tsip_message_get_header(response, tsip_htype_Refer_Sub);
+ if(Refer_Sub){
+ self->refersub = Refer_Sub->sub;
+ }
+ if(tsip_message_required(response, "norefersub")){
+ self->required.norefersub = tsk_true;
+ }
+
+ return TSIP_DIALOG_INVITE_SIGNAL(self, tsip_o_ect_accepted,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+}
+
+// oECTing -> (i300-699 REFER) -> Connected
+static int x0400_oECTing_2_Connected_X_i3456(va_list *app)
+{
+ tsip_dialog_invite_t *self;
+ const tsip_response_t* response;
+
+ self = va_arg(*app, tsip_dialog_invite_t *);
+ response = va_arg(*app, const tsip_message_t *);
+
+ return TSIP_DIALOG_INVITE_SIGNAL(self, tsip_o_ect_failed,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+}
+
+// oECTing -> (iNotify 1xx sipfrag) -> oECTing
+static int x0400_oECTing_2_oECTing_X_iNOTIFY(va_list *app)
+{
+ int ret = 0;
+ tsip_dialog_invite_t *self;
+ const tsip_request_t* notify;
+ tsip_response_t *sipfrag = tsk_null;
+
+ self = va_arg(*app, tsip_dialog_invite_t *);
+ notify = va_arg(*app, const tsip_message_t *);
+
+ sipfrag = get_SipFragMessage(notify);
+ if(sipfrag){
+ send_RESPONSE(self, notify, 200, "Ok", tsk_false);
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_o_ect_notify,
+ TSIP_RESPONSE_CODE(sipfrag), TSIP_RESPONSE_PHRASE(sipfrag), notify);
+ }
+
+ TSK_OBJECT_SAFE_FREE(sipfrag);
+
+ return ret;
+}
+
+// oECTing -> (iNotify 23456 sipfrag) -> Connected
+static int x0400_oECTing_2_Connected_X_iNOTIFY(va_list *app)
+{
+ int ret = 0;
+ tsip_dialog_invite_t *self;
+ const tsip_request_t* notify;
+ tsip_response_t *sipfrag = tsk_null;
+
+ self = va_arg(*app, tsip_dialog_invite_t *);
+ notify = va_arg(*app, const tsip_message_t *);
+
+ sipfrag = get_SipFragMessage(notify);
+ if(sipfrag){
+ send_RESPONSE(self, notify, 200, "Ok", tsk_false);
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_o_ect_notify,
+ TSIP_RESPONSE_CODE(sipfrag), TSIP_RESPONSE_PHRASE(sipfrag), notify);
+ }
+
+ TSK_OBJECT_SAFE_FREE(sipfrag);
+
+ return ret;
+}
+
+
+// Connected -> (iREFER) -> iECTreq
+static int x0400_Connected_2_iECTreq_X_iREFER(va_list *app)
+{
+ int ret;
+ tsip_dialog_invite_t *self;
+ const tsip_request_t* refer;
+
+ self = va_arg(*app, tsip_dialog_invite_t *);
+ refer = va_arg(*app, const tsip_message_t *);
+
+ TSK_OBJECT_SAFE_FREE(self->last_iRefer);
+ self->last_iRefer = tsk_object_ref((tsk_object_t*)refer);
+
+ ret = send_RESPONSE(self, self->last_iRefer, 100, "Asking for Transfer", tsk_false);
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_ect_requested,
+ tsip_event_code_dialog_request_incoming, "Incoming Request", self->last_iRefer);
+
+ return ret;
+}
+
+// iECTreq -> (reject) -> Connected
+static int x0400_iECTreq_2_Connected_X_reject(va_list *app)
+{
+ int ret;
+ tsip_dialog_invite_t *self;
+ const tsip_action_t* action;
+ short code;
+ const char* phrase;
+ char* reason = tsk_null;
+
+ self = va_arg(*app, tsip_dialog_invite_t *);
+ va_arg(*app, const tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ // Send Reject
+ code = action->line_resp.code>=300 ? action->line_resp.code : 603;
+ phrase = action->line_resp.phrase ? action->line_resp.phrase : "Decline Transfer";
+ tsk_sprintf(&reason, "SIP; cause=%hi; text=\"%s\"", code, phrase);
+ ret = send_ERROR(self, self->last_iRefer, code, phrase, reason);
+ TSK_FREE(reason);
+
+ return ret;
+}
+
+// iECTreq -> (accept) -> iECTing
+static int x0400_iECTreq_2_iECTing_X_accept(va_list *app)
+{
+ int ret;
+ tsip_dialog_invite_t *self;
+ const tsip_header_Refer_To_t* Refer_To;
+ 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 *);
+
+ // Send 200 OK
+ ret = send_RESPONSE(self, self->last_iRefer, 200, "Transfering", tsk_false);
+ Refer_To = (const tsip_header_Refer_To_t*)tsip_message_get_header(self->last_iRefer, tsip_htype_Refer_To); // Not null: already checked
+ // Make call to the referToUri
+ TSK_OBJECT_SAFE_FREE(self->ss_transf);
+ self->ss_transf = tsip_ssession_create(TSIP_DIALOG_GET_STACK(self),
+ TSIP_SSESSION_SET_PARENT_ID(TSIP_DIALOG_GET_SS(self)->id),
+ TSIP_SSESSION_SET_NULL());
+#if TSIP_UNDER_WINDOWS // because of DirectShow Attach()
+ self->ss_transf->media.type = tmedia_audio;
+#else
+ self->ss_transf->media.type = self->msession_mgr ? self->msession_mgr->type : tmedia_defaults_get_media_type();
+#endif
+ self->ss_transf->owner = tsk_false;// not owned by the end-user -> will be destroyed as soon as the dialog dtor is called
+
+ if(ret == 0){
+ ret = tsip_invite_event_signal(tsip_i_ect_newcall, self->ss_transf,
+ tsip_event_code_dialog_request_outgoing, "ECTing", self->last_iRefer);
+ }
+
+ ret = tsip_ssession_set(self->ss_transf,
+ TSIP_SSESSION_SET_TO_OBJ(Refer_To->uri),
+ TSIP_SSESSION_SET_NULL());
+ ret = tsip_api_invite_send_invite(self->ss_transf, self->ss_transf->media.type,
+ TSIP_ACTION_SET_NULL());
+
+ return ret;
+}
+
+
+// iECTing -> (1xx lnotify) -> iECTing
+static int x0400_iECTing_2_iECTing_X_1xxfNOTIFY(va_list *app)
+{
+ int ret;
+ tsip_dialog_invite_t *self;
+ const tsip_response_t* response;
+
+ self = va_arg(*app, tsip_dialog_invite_t *);
+ response = va_arg(*app, const tsip_message_t *);
+
+ // send NOTIFY (event if norefersub enabled) and alert user
+ ret = send_NOTIFY(self, TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response));
+
+ return ret;
+}
+
+// iECTing -> (23456 lnotify) -> Connected
+static int x0400_iECTing_2_Connected_X_23456fNOTIFY(va_list *app)
+{
+ int ret;
+ tsip_dialog_invite_t *self;
+ const tsip_response_t* response;
+ short code;
+
+ self = va_arg(*app, tsip_dialog_invite_t *);
+ response = va_arg(*app, const tsip_message_t *);
+ code = TSIP_RESPONSE_CODE(response);
+
+ // send NOTIFY (event if norefersub enabled) and alert user
+ ret = send_NOTIFY(self, code, TSIP_RESPONSE_PHRASE(response));
+
+ if(code >= 200 && code <= 299){
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_ect_completed,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), self->last_iRefer);
+ // hang up the call
+ ret = send_BYE(self);
+ }
+ else{
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_i_ect_failed,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), self->last_iRefer);
+ }
+
+ return ret;
+}
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+static int send_NOTIFY(tsip_dialog_invite_t *self, short code, const char* phrase)
+{
+ tsip_request_t *notify = tsk_null;
+ int ret = -1;
+
+ if((notify = tsip_dialog_request_new(TSIP_DIALOG(self), "NOTIFY"))){
+ char* sipfrag = tsk_null;
+ tsk_sprintf(&sipfrag, "%s %hi %s\r\n", TSIP_MESSAGE_VERSION_DEFAULT, code, phrase);
+ ret = tsip_message_add_content(notify, "message/sipfrag", sipfrag, tsk_strlen(sipfrag));
+ ret = tsip_dialog_request_send(TSIP_DIALOG(self), notify);
+ if(ret == 0){
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_o_ect_notify, code, phrase, notify);
+ }
+ TSK_FREE(sipfrag);
+ TSK_OBJECT_SAFE_FREE(notify);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create request");
+ }
+ return ret;
+}
+
+static int send_REFER(tsip_dialog_invite_t *self, const char* to)
+{
+ int ret = 0;
+ tsip_request_t *refer = tsk_null;
+ tsip_uri_t* toUri = tsk_null;
+
+ if(!self || !to){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!(toUri = tsip_uri_parse(to, tsk_strlen(to)))){
+ TSK_DEBUG_ERROR("Failed to parse %s", to);
+ return -1;
+ }
+ else{
+ // tsk_params_add_param(&toUri->params, "method", "INVITE");
+ }
+
+ if((refer = tsip_dialog_request_new(TSIP_DIALOG(self), "REFER"))){
+ tsk_istr_t cid;
+ tsk_strrandom(&cid);
+ /* Add headers */
+ tsip_message_add_headers(refer,
+ TSIP_HEADER_REFER_TO_VA_ARGS(toUri),
+ TSIP_HEADER_REFERRED_BY_VA_ARGS(TSIP_DIALOG_GET_STACK(self)->identity.impu, cid),
+ TSIP_HEADER_REFER_SUB_VA_ARGS(self->refersub),
+ tsk_null);
+ if(self->supported.norefersub){
+ tsip_message_add_headers(refer,
+ TSIP_HEADER_SUPPORTED_VA_ARGS("norefersub"),
+ tsk_null);
+ }
+
+ ret = tsip_dialog_request_send(TSIP_DIALOG(self), refer);
+ TSK_OBJECT_SAFE_FREE(refer);
+ }
+
+ TSK_OBJECT_SAFE_FREE(toUri);
+ return ret;
+}
+
+static tsip_response_t * get_SipFragMessage(const tsip_request_t* notify)
+{
+ tsip_response_t *sipfrag = tsk_null;
+ if(TSIP_MESSAGE_HAS_CONTENT(notify) && tsk_striequals(notify->Content_Type->type, "message/sipfrag")){
+ tsk_ragel_state_t state;
+ tsk_bool_t ret;
+ char* content = tsk_null;
+
+ // sipfrag is a "tsip_message_t" with an extra \r\n
+ content = tsk_strndup(notify->Content->data, notify->Content->size);
+ if(tsk_strLastIndexOf(content, tsk_strlen(content), "\r\n") != tsk_strlen(content) - 2){//Hack for XXX buggy client
+ tsk_strcat(&content, "\r\n");
+ }
+ tsk_strcat(&content, "\r\n");
+
+ tsk_ragel_state_init(&state, content, tsk_strlen(content));
+ ret = tsip_message_parse(&state, &sipfrag, tsk_false);
+ TSK_FREE(content);
+ if(ret && TSIP_MESSAGE_IS_RESPONSE(sipfrag)){
+ return sipfrag;
+ }
+ TSK_OBJECT_SAFE_FREE(sipfrag);
+ }
+ return sipfrag;
+}
+
+static short get_SipFragResponseCode(const tsip_request_t* notify)
+{
+ tsip_response_t *sipfrag = get_SipFragMessage(notify);
+ short code = 0;
+ if(sipfrag){
+ code = TSIP_RESPONSE_CODE(sipfrag);
+ TSK_OBJECT_SAFE_FREE(sipfrag);
+ }
+ return code;
+} \ No newline at end of file
diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.hold.c b/tinySIP/src/dialogs/tsip_dialog_invite.hold.c
new file mode 100644
index 0000000..7845bab
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_invite.hold.c
@@ -0,0 +1,252 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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.hold.c
+ * @brief Communication Hold (3GPP TS 24.610)
+ * The Communication Hold supplementary service enables a user to suspend the reception of media stream(s) of an established IP multimedia session,
+ * and resume the media stream(s) at a later time.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_invite.h"
+
+#include "tinysip/dialogs/tsip_dialog_invite.common.h"
+
+#include "tsk_debug.h"
+
+/* ======================== transitions ======================== */
+static int x0100_Connected_2_Holding_X_oHold(va_list *app);
+static int x0101_Holding_2_Connected_X_ixxx(va_list *app);
+static int x0102_Connected_2_Resuming_X_oResume(va_list *app);
+static int x0103_Resuming_2_Connected_X_ixxx(va_list *app);
+
+static int x0150_Any_2_Any_X_i422(va_list *app);
+
+/* ======================== conds ======================== */
+static tsk_bool_t _fsm_cond_is_resp2INVITEorUPDATE(tsip_dialog_invite_t* self, tsip_message_t* message)
+{
+ return (TSIP_RESPONSE_IS_TO_INVITE(message) || TSIP_RESPONSE_IS_TO_UPDATE(message));
+}
+
+/* ======================== external functions ======================== */
+extern int send_INVITEorUPDATE(tsip_dialog_invite_t *self, tsk_bool_t is_INVITE, tsk_bool_t force_sdp);
+extern int tsip_dialog_invite_process_ro(tsip_dialog_invite_t *self, const tsip_message_t* message);
+extern int send_ACK(tsip_dialog_invite_t *self, const tsip_response_t* r2xxINVITE);
+
+int tsip_dialog_invite_hold_init(tsip_dialog_invite_t *self)
+{
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Hold ===
+ */
+ // Connected -> (send HOLD) -> Holding
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_oHold, _fsm_state_Holding, x0100_Connected_2_Holding_X_oHold, "x0100_Connected_2_Holding_X_oHold"),
+ // Holding -> (i2xx) -> Connected
+ TSK_FSM_ADD(_fsm_state_Holding, _fsm_action_i2xx, _fsm_cond_is_resp2INVITEorUPDATE, _fsm_state_Connected, x0101_Holding_2_Connected_X_ixxx, "x0101_Holding_2_Connected_X_ixxx"),
+ // Holding -> (i300-699) -> Connected
+ TSK_FSM_ADD(_fsm_state_Holding, _fsm_action_i300_to_i699, _fsm_cond_is_resp2INVITEorUPDATE, _fsm_state_Connected, x0101_Holding_2_Connected_X_ixxx, "x0101_Holding_2_Connected_X_ixxx"),
+
+ /*=======================
+ * === Resume ===
+ */
+ // Connected -> (send RESUME) -> Resuming
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_oResume, _fsm_state_Resuming, x0102_Connected_2_Resuming_X_oResume, "x0102_Connected_2_Resuming_X_oResume"),
+ // Resuming -> (i2xx) -> Connected
+ TSK_FSM_ADD(_fsm_state_Resuming, _fsm_action_i2xx, _fsm_cond_is_resp2INVITEorUPDATE, _fsm_state_Connected, x0103_Resuming_2_Connected_X_ixxx, "x0103_Resuming_2_Connected_X_ixxx"),
+ // Resuming -> (i300-699) -> Connected
+ TSK_FSM_ADD(_fsm_state_Resuming, _fsm_action_i300_to_i699, _fsm_cond_is_resp2INVITEorUPDATE, _fsm_state_Connected, x0103_Resuming_2_Connected_X_ixxx, "x0103_Resuming_2_Connected_X_ixxx"),
+
+ TSK_FSM_ADD_NULL());
+
+ return 0;
+}
+
+
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+// Connected -> (send HOLD) -> Holding
+int x0100_Connected_2_Holding_X_oHold(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(!self->msession_mgr){
+ TSK_DEBUG_WARN("Media Session manager is Null");
+ return 0;
+ }
+
+ /* put on hold */
+ ret = tmedia_session_mgr_hold(self->msession_mgr, action->media.type);
+
+ /* send the request */
+ if((ret = send_INVITE(self, tsk_false))){
+ // FIXME: signal error without breaking the state machine
+ }
+
+ return 0;
+}
+
+// Holding -> (ixxx) -> Connected
+int x0101_Holding_2_Connected_X_ixxx(va_list *app)
+{
+ int ret;
+
+ tsip_dialog_invite_t* self = va_arg(*app, tsip_dialog_invite_t *);
+ const tsip_response_t* response = va_arg(*app, const tsip_response_t *);
+
+ /* reset current action */
+ tsip_dialog_set_curr_action(TSIP_DIALOG(self), tsk_null);
+
+ /* Process remote offer */
+ if((ret = tsip_dialog_invite_process_ro(self, response))){
+ /* Send error */
+ return ret;
+ }
+ else if(TSIP_RESPONSE_IS_TO_INVITE(response)){
+ /* send ACK */
+ ret = send_ACK(self, response);
+ }
+
+ /* alert the user */
+ if(TSIP_RESPONSE_IS_2XX(response)){
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_m_local_hold_ok,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+ self->hold.local = tsk_true;
+ }
+ else{
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_m_local_hold_nok,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+ self->hold.local = tsk_false;
+ }
+
+ return ret;
+}
+
+// Connected -> (send RESUME) -> Resuming
+int x0102_Connected_2_Resuming_X_oResume(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(!self->msession_mgr){
+ TSK_DEBUG_WARN("Media Session manager is Null");
+ return 0;
+ }
+
+ /* Resume both */
+ ret = tmedia_session_mgr_resume(self->msession_mgr, action->media.type, tsk_true);
+ ret = tmedia_session_mgr_resume(self->msession_mgr, action->media.type, tsk_false);
+
+ /* send the request */
+ if((ret = send_INVITE(self, tsk_false))){
+ // FIXME: signal error without breaking the state machine
+ }
+
+ return 0;
+}
+
+// Resuming -> (ixxx) -> Connected
+int x0103_Resuming_2_Connected_X_ixxx(va_list *app)
+{
+ int ret;
+
+ tsip_dialog_invite_t* self = va_arg(*app, tsip_dialog_invite_t *);
+ const tsip_response_t* response = va_arg(*app, const tsip_response_t *);
+
+ /* reset current action */
+ tsip_dialog_set_curr_action(TSIP_DIALOG(self), tsk_null);
+
+ /* Process remote offer */
+ if((ret = tsip_dialog_invite_process_ro(self, response))){
+ /* Send error */
+ return ret;
+ }
+ else if(TSIP_RESPONSE_IS_TO_INVITE(response)){
+ /* send ACK */
+ ret = send_ACK(self, response);
+ }
+
+ /* alert the user */
+ if(TSIP_RESPONSE_IS_2XX(response)){
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_m_local_resume_ok,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+ self->hold.local = tsk_false;
+ }
+ else{
+ TSIP_DIALOG_INVITE_SIGNAL(self, tsip_m_local_resume_nok,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+ self->hold.local = tsk_true;
+ }
+
+ return ret;
+}
+
+/* handle requests/responses (MUST be called after set_ro()) */
+int tsip_dialog_invite_hold_handle(tsip_dialog_invite_t* self, const tsip_request_t* rINVITEorUPDATE)
+{
+ tsk_bool_t remote_hold, bodiless_invite;
+ int ret = 0;
+
+ if(!self || !rINVITEorUPDATE || !self->msession_mgr){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ remote_hold = tmedia_session_mgr_is_held(self->msession_mgr, self->msession_mgr->type, tsk_false);
+
+ // resume the call if we receive bodiless INVITE
+ bodiless_invite = !TSIP_MESSAGE_HAS_CONTENT(rINVITEorUPDATE) && TSIP_REQUEST_IS_INVITE(rINVITEorUPDATE);
+ if(bodiless_invite && remote_hold){
+ // resume remote
+ if((ret = tmedia_session_mgr_resume(self->msession_mgr, self->msession_mgr->type, tsk_false)) == 0){
+ remote_hold = tsk_false;
+ }
+ }
+
+ if(ret == 0 && (remote_hold != self->hold.remote)){
+ self->hold.remote = remote_hold;
+ TSIP_DIALOG_INVITE_SIGNAL(self, self->hold.remote ? tsip_m_remote_hold : tsip_m_remote_resume,
+ tsip_event_code_dialog_request_incoming, "Hold/Resume state changed", rINVITEorUPDATE);
+ }
+
+ return ret;
+}
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.ice.c b/tinySIP/src/dialogs/tsip_dialog_invite.ice.c
new file mode 100644
index 0000000..823e548
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_invite.ice.c
@@ -0,0 +1,617 @@
+/*
+ * Copyright (C) 2012 Doubango Telecom <http://www.doubango.org>
+ *
+ * Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+ * 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.
+ *
+ */
+#include "tinysip/dialogs/tsip_dialog_invite.h"
+
+#include "tinysip/dialogs/tsip_dialog_invite.common.h"
+
+#include "tinysdp/parsers/tsdp_parser_message.h"
+#include "tinysdp/tsdp_message.h"
+#include "tinysdp/headers/tsdp_header_S.h"
+#include "tinysdp/headers/tsdp_header_O.h"
+
+#include "stun/tnet_stun_types.h"
+#include "ice/tnet_ice_ctx.h"
+
+#include "tsk_debug.h"
+
+extern int tsip_dialog_invite_msession_start(tsip_dialog_invite_t *self);
+
+static int tsip_dialog_invite_ice_create_ctx(tsip_dialog_invite_t * self, tmedia_type_t media_type);
+static int tsip_dialog_invite_ice_audio_callback(const tnet_ice_event_t *e);
+static int tsip_dialog_invite_ice_video_callback(const tnet_ice_event_t *e);
+int tsip_dialog_invite_ice_set_media_type(tsip_dialog_invite_t * self, tmedia_type_t media_type);
+tsk_bool_t tsip_dialog_invite_ice_got_local_candidates(const tsip_dialog_invite_t * self);
+int tsip_dialog_invite_ice_process_ro(tsip_dialog_invite_t * self, const tsdp_message_t* sdp_ro, tsk_bool_t is_remote_offer);
+
+#define tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(_self) \
+tsip_dialog_invite_ice_set_sync_mode_ctx((_self), tsk_true); \
+tsip_dialog_invite_ice_set_silent_mode_ctx((_self), tsk_true); \
+tsip_dialog_invite_ice_cancel_ctx((_self)); /* "cancelled" event will not be sent and we're sure that cancel operation will be done when the function exit */ \
+tsip_dialog_invite_ice_set_sync_mode_ctx((_self), tsk_false); \
+tsip_dialog_invite_ice_set_silent_mode_ctx((_self), tsk_false); \
+
+/* ======================== transitions ======================== */
+// Use "Current" instead of "Any" to avoid priority reordering
+static int x0500_Current_2_Current_X_oINVITE(va_list *app);
+static int x0500_Current_2_Current_X_iINVITE(va_list *app);
+
+/* ======================== conds ======================== */
+static tsk_bool_t _fsm_cond_get_local_candidates(tsip_dialog_invite_t* self, tsip_message_t* message)
+{
+ if(self->supported.ice){
+ tsk_bool_t use_ice = tsk_false;
+ // "action->media.type" will be defined for locally initiated media update
+ tmedia_type_t new_media = TSIP_DIALOG(self)->curr_action ? TSIP_DIALOG(self)->curr_action->media.type : tmedia_none;
+
+ if(message && TSIP_MESSAGE_HAS_CONTENT(message) && tsk_striequals("application/sdp", TSIP_MESSAGE_CONTENT_TYPE(message))){
+ // If this code is called this means that we are the "answerer"
+ // only gets the candidates if ICE is enabled and the remote peer supports ICE
+ tsdp_message_t* sdp_ro;
+ const tsdp_header_M_t* M;
+ int index;
+ 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");
+ return tsk_false;
+ }
+
+ index = 0;
+ while((M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp_ro, tsdp_htype_M, index++))){
+ if(!tsdp_header_M_findA(M, "candidate")){
+ use_ice = tsk_false; // do not use ICE if at least on media is ICE-less (e.g. MSRP)
+ break;
+ }
+ use_ice = tsk_true; // only use ICE if there is a least one media line
+ }
+
+ new_media = tmedia_type_from_sdp(sdp_ro);
+
+ TSK_OBJECT_SAFE_FREE(sdp_ro);
+ }
+ else if(!message){
+ // we are the "offerer" -> use ICE only for audio or video medias (ignore ice for MSRP)
+ use_ice = (new_media & tmedia_audio) || (new_media & tmedia_video);
+ }
+
+ if(use_ice){
+ if(!self->ice.ctx_audio && !self->ice.ctx_video){ // First time
+ return tsk_true;
+ }
+ else{
+ if(self->ice.media_type != new_media){
+ return tsk_true;
+ }
+ return !tsip_dialog_invite_ice_got_local_candidates(self);
+ }
+ }
+ }
+ return tsk_false;
+}
+
+int tsip_dialog_invite_ice_init(tsip_dialog_invite_t *self)
+{
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+ // Current -> (oINVITE) -> Current
+ TSK_FSM_ADD(tsk_fsm_state_current, _fsm_action_oINVITE, _fsm_cond_get_local_candidates, tsk_fsm_state_current, x0500_Current_2_Current_X_oINVITE, "x0500_Current_2_Current_X_oINVITE"),
+ // Current -> (iINVITE) -> Current
+ TSK_FSM_ADD(tsk_fsm_state_current, _fsm_action_iINVITE, _fsm_cond_get_local_candidates, tsk_fsm_state_current, x0500_Current_2_Current_X_iINVITE, "x0500_Current_2_Current_X_iINVITE")
+ );
+
+ return 0;
+}
+
+int tsip_dialog_invite_ice_timers_set(tsip_dialog_invite_t *self, int64_t timeout)
+{
+ if(/*tnet_ice_ctx_is_active*/(self->ice.ctx_audio)){
+ tnet_ice_ctx_set_concheck_timeout(self->ice.ctx_audio, timeout);
+ }
+ if(/*tnet_ice_ctx_is_active*/(self->ice.ctx_video)){
+ tnet_ice_ctx_set_concheck_timeout(self->ice.ctx_video, timeout);
+ }
+ return 0;
+}
+
+static int tsip_dialog_invite_ice_create_ctx(tsip_dialog_invite_t * self, tmedia_type_t media_type)
+{
+ int32_t transport_idx;
+ int ret = 0;
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ transport_idx = TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default;
+ if (!self->ice.ctx_audio && (media_type & tmedia_audio)) {
+ self->ice.ctx_audio = tnet_ice_ctx_create(self->ice.is_jingle, TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]),
+ self->use_rtcp, tsk_false, tsip_dialog_invite_ice_audio_callback, self);
+ if (!self->ice.ctx_audio) {
+ TSK_DEBUG_ERROR("Failed to create ICE audio context");
+ return -2;
+ }
+#if 0 // @deprecated
+ ret = tnet_ice_ctx_set_stun(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.stun.hostname, TSIP_DIALOG_GET_SS(self)->media.stun.port, kStunSoftware, TSIP_DIALOG_GET_SS(self)->media.stun.username, TSIP_DIALOG_GET_SS(self)->media.stun.password);
+#else
+ ret = tnet_ice_ctx_add_server(
+ self->ice.ctx_audio,
+ "udp", // "tcp", "tls", "ws", "wss"...
+ TSIP_DIALOG_GET_SS(self)->media.stun.hostname,
+ TSIP_DIALOG_GET_SS(self)->media.stun.port,
+ TSIP_DIALOG_GET_SS(self)->media.enable_iceturn,
+ TSIP_DIALOG_GET_SS(self)->media.enable_icestun,
+ TSIP_DIALOG_GET_SS(self)->media.stun.username,
+ TSIP_DIALOG_GET_SS(self)->media.stun.password);
+#endif
+ ret = tnet_ice_ctx_set_turn_enabled(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.enable_iceturn);
+ ret = tnet_ice_ctx_set_stun_enabled(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.enable_icestun);
+ ret = tnet_ice_ctx_set_rtcpmux(self->ice.ctx_audio, self->use_rtcpmux);
+ }
+ if (!self->ice.ctx_video && (media_type & tmedia_video)) {
+ self->ice.ctx_video = tnet_ice_ctx_create(self->ice.is_jingle, TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]),
+ self->use_rtcp, tsk_true, tsip_dialog_invite_ice_video_callback, self);
+ if (!self->ice.ctx_video) {
+ TSK_DEBUG_ERROR("Failed to create ICE video context");
+ return -2;
+ }
+#if 0 // @deprecated
+ ret = tnet_ice_ctx_set_stun(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.stun.hostname, TSIP_DIALOG_GET_SS(self)->media.stun.port, kStunSoftware, TSIP_DIALOG_GET_SS(self)->media.stun.username, TSIP_DIALOG_GET_SS(self)->media.stun.password);
+#else
+ ret = tnet_ice_ctx_add_server(
+ self->ice.ctx_video,
+ "udp", // "tcp", "tls", "ws", "wss"...
+ TSIP_DIALOG_GET_SS(self)->media.stun.hostname,
+ TSIP_DIALOG_GET_SS(self)->media.stun.port,
+ TSIP_DIALOG_GET_SS(self)->media.enable_iceturn,
+ TSIP_DIALOG_GET_SS(self)->media.enable_icestun,
+ TSIP_DIALOG_GET_SS(self)->media.stun.username,
+ TSIP_DIALOG_GET_SS(self)->media.stun.password);
+#endif
+ ret = tnet_ice_ctx_set_turn_enabled(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.enable_iceturn);
+ ret = tnet_ice_ctx_set_stun_enabled(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.enable_icestun);
+ ret = tnet_ice_ctx_set_rtcpmux(self->ice.ctx_video, self->use_rtcpmux);
+ }
+
+ // set media type
+ ret = tsip_dialog_invite_ice_set_media_type(self, media_type);
+
+ // update session manager with the right ICE contexts
+ if (self->msession_mgr) {
+ ret = tmedia_session_mgr_set_ice_ctx(self->msession_mgr, self->ice.ctx_audio, self->ice.ctx_video);
+ }
+
+ return ret;
+}
+
+int tsip_dialog_invite_ice_set_media_type(tsip_dialog_invite_t * self, tmedia_type_t _media_type)
+{
+ if(self){
+ tmedia_type_t av_media_type = (_media_type & tmedia_audiovideo); // filter to keep audio and video only
+ // "none" comparison is used to exclude the "first call"
+ if(self->ice.media_type != tmedia_none && self->ice.media_type != av_media_type){
+ // cancels contexts associated to old medias
+ if(self->ice.ctx_audio && !(av_media_type & tmedia_audio)){
+ tnet_ice_ctx_cancel(self->ice.ctx_audio);
+ }
+ if(self->ice.ctx_video && !(av_media_type & tmedia_video)){
+ tnet_ice_ctx_cancel(self->ice.ctx_video);
+ }
+ // cancels contexts associated to new medias (e.g. session "remove" then "add")
+ // cancel() on newly created contexts don't have any effect
+ if(self->ice.ctx_audio && (!(av_media_type & tmedia_audio) && (self->ice.media_type & tmedia_audio))){
+ //tnet_ice_ctx_cancel(self->ice.ctx_audio);
+ }
+ if(self->ice.ctx_video && (!(av_media_type & tmedia_video) && (self->ice.media_type & tmedia_video))){
+ //tnet_ice_ctx_cancel(self->ice.ctx_video);
+ }
+ }
+ self->ice.media_type = av_media_type;
+ }
+ return 0;
+}
+
+static int tsip_dialog_invite_ice_start_ctx(tsip_dialog_invite_t * self)
+{
+ int ret = 0;
+ if(self){
+ if((self->ice.media_type & tmedia_audio)){
+ if(self->ice.ctx_audio && (ret = tnet_ice_ctx_start(self->ice.ctx_audio)) != 0){
+ return ret;
+ }
+ }
+ if((self->ice.media_type & tmedia_video)){
+ if(self->ice.ctx_video && (ret = tnet_ice_ctx_start(self->ice.ctx_video)) != 0){
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+static int tsip_dialog_invite_ice_cancel_ctx(tsip_dialog_invite_t * self)
+{
+ int ret = 0;
+ if(self){
+ if((self->ice.media_type & tmedia_audio)){
+ if(self->ice.ctx_audio && (ret = tnet_ice_ctx_cancel(self->ice.ctx_audio)) != 0){
+ return ret;
+ }
+ }
+ if((self->ice.media_type & tmedia_video)){
+ if(self->ice.ctx_video && (ret = tnet_ice_ctx_cancel(self->ice.ctx_video)) != 0){
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+static int tsip_dialog_invite_ice_set_sync_mode_ctx(tsip_dialog_invite_t * self, tsk_bool_t sync_mode)
+{
+ int ret = 0;
+ if(self){
+ if((self->ice.media_type & tmedia_audio)){
+ if(self->ice.ctx_audio && (ret = tnet_ice_ctx_set_sync_mode(self->ice.ctx_audio, sync_mode)) != 0){
+ return ret;
+ }
+ }
+ if((self->ice.media_type & tmedia_video)){
+ if(self->ice.ctx_video && (ret = tnet_ice_ctx_set_sync_mode(self->ice.ctx_video, sync_mode)) != 0){
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+static int tsip_dialog_invite_ice_set_silent_mode_ctx(tsip_dialog_invite_t * self, tsk_bool_t silent_mode)
+{
+ int ret = 0;
+ if(self){
+ if((self->ice.media_type & tmedia_audio)){
+ if(self->ice.ctx_audio && (ret = tnet_ice_ctx_set_silent_mode(self->ice.ctx_audio, silent_mode)) != 0){
+ return ret;
+ }
+ }
+ if((self->ice.media_type & tmedia_video)){
+ if(self->ice.ctx_video && (ret = tnet_ice_ctx_set_silent_mode(self->ice.ctx_video, silent_mode)) != 0){
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+tsk_bool_t tsip_dialog_invite_ice_is_enabled(const tsip_dialog_invite_t * self)
+{
+ if(self){
+ return (self->supported.ice && (tnet_ice_ctx_is_active(self->ice.ctx_audio) || tnet_ice_ctx_is_active(self->ice.ctx_video)));
+ }
+ return tsk_false;
+}
+
+tsk_bool_t tsip_dialog_invite_ice_is_connected(const tsip_dialog_invite_t * self)
+{
+ if(self){
+ return (!tnet_ice_ctx_is_active(self->ice.ctx_audio) || tnet_ice_ctx_is_connected(self->ice.ctx_audio))
+ && (!tnet_ice_ctx_is_active(self->ice.ctx_video) || tnet_ice_ctx_is_connected(self->ice.ctx_video));
+ }
+ return tsk_false;
+}
+
+tsk_bool_t tsip_dialog_invite_ice_got_local_candidates(const tsip_dialog_invite_t * self)
+{
+ if(self){
+ return (!tnet_ice_ctx_is_active(self->ice.ctx_audio) || tnet_ice_ctx_got_local_candidates(self->ice.ctx_audio))
+ && (!tnet_ice_ctx_is_active(self->ice.ctx_video) || tnet_ice_ctx_got_local_candidates(self->ice.ctx_video));
+ }
+ return tsk_false;
+}
+
+int tsip_dialog_invite_ice_save_action(tsip_dialog_invite_t * self, tsk_fsm_action_id action_id, const tsip_action_t* action, const tsip_message_t* message)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ // There are good reasons to ref() the action and message before safe_free()
+ // /!\ do not change
+
+ self->ice.last_action_id = action_id;
+ action = tsk_object_ref((tsk_object_t*)action);
+ TSK_OBJECT_SAFE_FREE(self->ice.last_action);
+ self->ice.last_action = (tsip_action_t*)action;
+
+ message = tsk_object_ref((tsk_object_t*)message);
+ TSK_OBJECT_SAFE_FREE(self->ice.last_message);
+ self->ice.last_message = (tsip_message_t*)message;
+ return 0;
+}
+
+int tsip_dialog_invite_ice_process_lo(tsip_dialog_invite_t * self, const tsdp_message_t* sdp_lo)
+{
+ const tsdp_header_M_t* M;
+ const tsdp_header_A_t *A;
+ int ret = 0, i;
+
+ if(!self || !sdp_lo){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ // cancels all ICE contexts without candidates
+ // this happens if codecs negotiations mismatch for one media out of two or three
+ for(i = 0; i < 2; ++i){
+ struct tnet_ice_ctx_s *ctx = i == 0 ? self->ice.ctx_audio : self->ice.ctx_video;
+ const char* media = i == 0 ? "audio" : "video";
+ if(tnet_ice_ctx_is_active(ctx)){
+ tsk_bool_t cancel = tsk_true;
+ if((M = tsdp_message_find_media(sdp_lo, media))){
+ if((A = tsdp_header_M_findA(M, "candidate"))){
+ cancel = tsk_false;
+ }
+ }
+ if(cancel){
+ ret = tnet_ice_ctx_cancel(ctx);
+ }
+ }
+ }
+
+ return ret;
+}
+
+int tsip_dialog_invite_ice_process_ro(tsip_dialog_invite_t * self, const tsdp_message_t* sdp_ro, tsk_bool_t is_remote_offer)
+{
+ char* ice_remote_candidates;
+ const tsdp_header_M_t* M;
+ tsk_size_t index;
+ const tsdp_header_A_t *A;
+ const tsdp_header_O_t *O;
+ const char* sess_ufrag = tsk_null;
+ const char* sess_pwd = tsk_null;
+ int ret = 0, i;
+ struct tnet_ice_ctx_s *ctx;
+
+ if(!self || !sdp_ro){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ if(!self->ice.ctx_audio && !self->ice.ctx_video){
+ return 0;
+ }
+
+ // make sure this is different SDP
+ if((O = (const tsdp_header_O_t*)tsdp_message_get_header(sdp_ro, tsdp_htype_O))){
+ if(self->ice.last_sdp_ro_ver == (int32_t)O->sess_version){
+ TSK_DEBUG_INFO("ICE: ignore processing SDP RO because version haven't changed");
+ return 0;
+ }
+ self->ice.last_sdp_ro_ver = (int32_t)O->sess_version;
+ }
+
+ // session level attributes
+
+ if((A = tsdp_message_get_headerA(sdp_ro, "ice-ufrag"))){
+ sess_ufrag = A->value;
+ }
+ if((A = tsdp_message_get_headerA(sdp_ro, "ice-pwd"))){
+ sess_pwd = A->value;
+ }
+
+#if 0 // Use RTCWeb Profile (tmedia_profile_rtcweb)
+ {
+ const tsdp_header_S_t *S;
+ if((S = (const tsdp_header_S_t *)tsdp_message_get_header(sdp_ro, tsdp_htype_S)) && S->value){
+ self->ice.is_jingle = tsk_strcontains(S->value, tsk_strlen(S->value), "webrtc");
+ }
+ }
+#endif
+
+ for(i = 0; i < 2; ++i){
+ if((M = tsdp_message_find_media(sdp_ro, i==0 ? "audio": "video"))){
+ const char *ufrag = sess_ufrag, *pwd = sess_pwd;
+ tsk_bool_t remote_use_rtcpmux = (tsdp_header_M_findA(M, "rtcp-mux") != tsk_null);
+ ctx = (i==0 ? self->ice.ctx_audio : self->ice.ctx_video);
+ ice_remote_candidates = tsk_null;
+ index = 0;
+ if((A = tsdp_header_M_findA(M, "ice-ufrag"))){
+ ufrag = A->value;
+ }
+ if((A = tsdp_header_M_findA(M, "ice-pwd"))){
+ pwd = A->value;
+ }
+
+ while((A = tsdp_header_M_findA_at(M, "candidate", index++))){
+ tsk_strcat_2(&ice_remote_candidates, "%s\r\n", A->value);
+ }
+ // ICE processing will be automatically stopped if the remote candidates are not valid
+ // ICE-CONTROLLING role if we are the offerer
+ ret = tnet_ice_ctx_set_remote_candidates_2(ctx, ice_remote_candidates, ufrag, pwd, !is_remote_offer, self->ice.is_jingle, (self->use_rtcpmux && remote_use_rtcpmux));
+ TSK_SAFE_FREE(ice_remote_candidates);
+ }
+ }
+
+ return ret;
+}
+
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+
+// Current -> (oINVITE) -> Current
+static int x0500_Current_2_Current_X_oINVITE(va_list *app)
+{
+ int ret;
+ tsip_dialog_invite_t *self;
+ const tsip_action_t* action;
+ const tsip_message_t *message;
+ tmedia_type_t media_type;
+ static const tsk_bool_t __force_restart_is_yes = tsk_true;
+
+ self = va_arg(*app, tsip_dialog_invite_t *);
+ message = va_arg(*app, const tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ media_type = (action && action->media.type != tmedia_none) ? action->media.type : TSIP_DIALOG_GET_SS(self)->media.type;
+ self->is_client = tsk_true;
+ tsip_dialog_invite_ice_save_action(self, _fsm_action_oINVITE, action, message);
+
+ // Cancel without notifying ("silent mode") and perform the operation right now ("sync mode")
+ tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(self);
+
+ // create ICE context
+ if((ret = tsip_dialog_invite_ice_create_ctx(self, media_type))){
+ TSK_DEBUG_ERROR("tsip_dialog_invite_ice_create_ctx() failed");
+ return ret;
+ }
+
+ // For now disable ICE timers until we receive the 2xx
+ ret = tsip_dialog_invite_ice_timers_set(self, -1);
+
+ // Start ICE
+ ret = tsip_dialog_invite_ice_start_ctx(self);
+
+ // alert the user only if we are in initial state which means that it's not media update
+ if(TSIP_DIALOG(self)->state == tsip_initial){
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");
+ }
+
+ return ret;
+}
+
+// Current -> (iINVITE) -> Current
+static int x0500_Current_2_Current_X_iINVITE(va_list *app)
+{
+ int ret;
+ tsip_dialog_invite_t *self;
+ const tsip_action_t* action;
+ const tsip_message_t *message;
+
+ self = va_arg(*app, tsip_dialog_invite_t *);
+ message = va_arg(*app, const tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ self->is_client = tsk_false;
+ ret = tsip_dialog_invite_ice_save_action(self, _fsm_action_iINVITE, action, message);
+
+ // Cancel without notifying ("silent mode") and perform the operation right now ("sync mode")
+ tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(self);
+
+ // set remote candidates
+ if(TSIP_MESSAGE_HAS_CONTENT(message)){
+ if(tsk_striequals("application/sdp", TSIP_MESSAGE_CONTENT_TYPE(message))){
+ tsdp_message_t* sdp_ro;
+ 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");
+ return -2;
+ }
+ // create ICE context
+ if((ret = tsip_dialog_invite_ice_create_ctx(self, tmedia_type_from_sdp(sdp_ro)))){
+ TSK_DEBUG_ERROR("tsip_dialog_invite_ice_create_ctx() failed");
+ return ret;
+ }
+ ret = tsip_dialog_invite_ice_process_ro(self, sdp_ro, tsk_true);
+ TSK_OBJECT_SAFE_FREE(sdp_ro);
+ }
+ else{
+ TSK_DEBUG_ERROR("[%s] content-type is not supportted", TSIP_MESSAGE_CONTENT_TYPE(message));
+ return -3;
+ }
+ }
+
+ // For now disable ICE timers until we send the 2xx and receive the ACK
+ ret = tsip_dialog_invite_ice_timers_set(self, -1);
+
+ // Start ICE
+ ret = tsip_dialog_invite_ice_start_ctx(self);
+
+ return ret;
+}
+
+
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+static int tsip_dialog_invite_ice_callback(const tnet_ice_event_t *e)
+{
+ int ret = 0;
+ tsip_dialog_invite_t *dialog;
+
+ TSK_DEBUG_INFO("ICE callback: %s", e->phrase);
+
+ dialog = tsk_object_ref(TSK_OBJECT(e->userdata));
+
+ // Do not lock: caller is thread safe
+
+ switch(e->type){
+ case tnet_ice_event_type_gathering_completed:
+ case tnet_ice_event_type_conncheck_succeed:
+ case tnet_ice_event_type_conncheck_failed:
+ case tnet_ice_event_type_cancelled:
+ {
+ if(dialog->ice.last_action_id != tsk_fsm_state_none){
+ if(tsip_dialog_invite_ice_got_local_candidates(dialog)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(dialog), dialog->ice.last_action_id, dialog->ice.last_message, dialog->ice.last_action);
+ dialog->ice.last_action_id = tsk_fsm_state_none;
+ }
+ }
+ if(dialog->ice.start_smgr){
+ ret = tsip_dialog_invite_msession_start(dialog);
+ }
+ break;
+ }
+ // fatal errors which discard ICE process
+ case tnet_ice_event_type_gathering_host_candidates_failed:
+ case tnet_ice_event_type_gathering_reflexive_candidates_failed:
+ case tnet_ice_event_type_gathering_relay_candidates_failed:
+ {
+ if (dialog->ice.last_action_id != tsk_fsm_state_none) {
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(dialog), dialog->ice.last_action_id, dialog->ice.last_message, dialog->ice.last_action);
+ dialog->ice.last_action_id = tsk_fsm_state_none;
+ }
+ break;
+ }
+ // TURN session disconnected while we're in call
+ case tnet_ice_event_type_turn_connection_broken:
+ {
+ ret = tsip_dialog_fsm_act_2(TSIP_DIALOG(dialog), _fsm_action_oBYE);
+ break;
+ }
+ default: break;
+ }
+
+ TSK_OBJECT_SAFE_FREE(dialog);
+
+ return ret;
+}
+
+static int tsip_dialog_invite_ice_audio_callback(const tnet_ice_event_t *e)
+{
+ return tsip_dialog_invite_ice_callback(e);
+}
+
+static int tsip_dialog_invite_ice_video_callback(const tnet_ice_event_t *e)
+{
+ return tsip_dialog_invite_ice_callback(e);
+} \ No newline at end of file
diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.qos.c b/tinySIP/src/dialogs/tsip_dialog_invite.qos.c
new file mode 100644
index 0000000..8cc7a2a
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_invite.qos.c
@@ -0,0 +1,92 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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.qos.c
+ * @brief Integration of Resource Management and Session Initiation Protocol (SIP) (RFC 3312)
+ * QoS Reservation.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_invite.h"
+
+#include "tinysip/dialogs/tsip_dialog_invite.common.h"
+
+#include "tsk_debug.h"
+
+#define TSIP_DIALOG_INVITE_QOS_RES_TIMEOUT 20
+
+/* ======================== external functions ======================== */
+extern int tsip_dialog_invite_timer_callback(const tsip_dialog_invite_t* self, tsk_timer_id_t timer_id);
+extern int send_INVITEorUPDATE(tsip_dialog_invite_t *self, tsk_bool_t is_INVITE, tsk_bool_t force_sdp);
+
+/* ======================== transitions ======================== */
+static int x0300_Any_2_Any_X_timerRSVP(va_list *app);
+
+/* Init FSM */
+int tsip_dialog_invite_qos_init(tsip_dialog_invite_t *self)
+{
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ // Any -> (timerRSVP) -> Any
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_timerRSVP, tsk_fsm_state_any, x0300_Any_2_Any_X_timerRSVP, "x0300_Any_2_Any_X_timerRSVP"),
+
+
+ TSK_FSM_ADD_NULL());
+
+ return 0;
+}
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+// Any -> (tiner RSVP) -> Any
+int x0300_Any_2_Any_X_timerRSVP(va_list *app)
+{
+ tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *);
+ return send_UPDATE(self, tsk_true);
+}
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+
+
+/* cancel the timer */
+int tsip_dialog_invite_qos_timer_cancel(tsip_dialog_invite_t* self)
+{
+ return tsk_timer_mgr_global_cancel(self->qos.timer.id);
+}
+
+/* schedule the timer */
+int tsip_dialog_invite_qos_timer_schedule(tsip_dialog_invite_t* self)
+{
+ /* To emulate bandwidth reservation (Because RSVP protocol is not supported) */
+ self->qos.timer.id = tsk_timer_mgr_global_schedule(TSIP_DIALOG_INVITE_QOS_RES_TIMEOUT, TSK_TIMER_CALLBACK_F(tsip_dialog_invite_timer_callback), self);
+
+ return 0;
+}
+
diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.server.c b/tinySIP/src/dialogs/tsip_dialog_invite.server.c
new file mode 100644
index 0000000..2a7a37c
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_invite.server.c
@@ -0,0 +1,790 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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 <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#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 && j<sizeof(supported_options)/sizeof(const char*); j++){
+ if(tsk_striequals(option->value, 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;
+}
+
+
diff --git a/tinySIP/src/dialogs/tsip_dialog_invite.timers.c b/tinySIP/src/dialogs/tsip_dialog_invite.timers.c
new file mode 100644
index 0000000..a8b279c
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_invite.timers.c
@@ -0,0 +1,302 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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.timers.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.
+ * Session Timers as per RFC 4028.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_invite.h"
+
+#include "tinysip/headers/tsip_header_Session_Expires.h"
+#include "tinysip/headers/tsip_header_Min_SE.h"
+
+#include "tinysip/dialogs/tsip_dialog_invite.common.h"
+
+#include "tsk_debug.h"
+
+/* ======================== internal functions ======================== */
+
+/* ======================== external functions ======================== */
+extern int tsip_dialog_invite_timer_callback(const tsip_dialog_invite_t* self, tsk_timer_id_t timer_id);
+extern int send_RESPONSE(tsip_dialog_invite_t *self, const tsip_request_t* request, short code, const char* phrase);
+extern int send_BYE(tsip_dialog_invite_t *self);
+extern int send_INVITEorUPDATE(tsip_dialog_invite_t *self, tsk_bool_t is_INVITE, tsk_bool_t force_sdp);
+
+/* ======================== transitions ======================== */
+static int x0200_Connected_2_Connected_X_timerRefresh(va_list *app);
+static int x0201_Connected_2_Trying_X_timerRefresh(va_list *app);
+static int x0250_Any_2_Any_X_i422(va_list *app);
+
+/* ======================== conds ======================== */
+static tsk_bool_t _fsm_cond_is_refresher(tsip_dialog_invite_t* self, tsip_message_t* message)
+{
+ return self->stimers.is_refresher;
+}
+static tsk_bool_t _fsm_cond_is_not_refresher(tsip_dialog_invite_t* self, tsip_message_t* message)
+{
+ return !_fsm_cond_is_refresher(self, message);
+}
+
+
+/* Init FSM */
+int tsip_dialog_invite_stimers_init(tsip_dialog_invite_t *self)
+{
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ // Connected -> (timerRefresh && isRefresher) -> Connected
+ TSK_FSM_ADD(_fsm_state_Connected, _fsm_action_timerRefresh, _fsm_cond_is_refresher, _fsm_state_Connected, x0200_Connected_2_Connected_X_timerRefresh, "x0200_Connected_2_Connected_X_timerRefresh"),
+ // Connected -> (timerRefresh && !isRefresher) -> Trying (because we will send BYE)
+ TSK_FSM_ADD(_fsm_state_Connected, _fsm_action_timerRefresh, _fsm_cond_is_not_refresher, _fsm_state_Trying, x0201_Connected_2_Trying_X_timerRefresh, "x0201_Connected_2_Trying_X_timerRefresh"),
+ // Any -> (i422) -> Any
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_i422, tsk_fsm_state_any, x0250_Any_2_Any_X_i422, "x0250_Any_2_Any_X_i422"),
+
+ TSK_FSM_ADD_NULL());
+
+ return 0;
+}
+
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+int x0200_Connected_2_Connected_X_timerRefresh(va_list *app)
+{
+ /* We are the refresher and the session timedout
+ ==> Refresh the session
+ */
+ tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *);
+ int ret;
+
+ /* RFC 4028 - 7.4. Generating Subsequent Session Refresh Requests
+
+ A re-INVITE generated to refresh the session is a normal re-INVITE,
+ and an UPDATE generated to refresh a session is a normal UPDATE. If
+ a UAC knows that its peer supports the UPDATE method, it is
+ RECOMMENDED that UPDATE be used instead of a re-INVITE. A UA can
+ make this determination if it has seen an Allow header field from its
+ peer with the value 'UPDATE', or through a mid-dialog OPTIONS
+ request. It is RECOMMENDED that the UPDATE request not contain an
+ offer [4], but a re-INVITE SHOULD contain one, even if the details of
+ the session have not changed
+ */
+ /* 2xx will be handled by tsip_dialog_invite_stimers_handle() */
+ ret = send_INVITEorUPDATE(self, !self->support_update, tsk_false);
+
+ return ret;
+}
+
+int x0201_Connected_2_Trying_X_timerRefresh(va_list *app)
+{
+ /* We are not the refresher and the session timedout
+ ==> send BYE
+ */
+ tsip_dialog_invite_t *self = va_arg(*app, tsip_dialog_invite_t *);
+ int ret;
+
+ /* send BYE */
+ ret = send_BYE(self);
+
+ /* alert the user that the session timedout */
+
+ return ret;
+}
+
+// Any -> (i422) -> Any
+int x0250_Any_2_Any_X_i422(va_list *app)
+{
+ tsip_dialog_invite_t* self = va_arg(*app, tsip_dialog_invite_t *);
+ const tsip_response_t* r422 = va_arg(*app, const tsip_response_t *);
+
+ const tsip_header_Min_SE_t* Min_SE;
+
+ /* RFC 4825 - 3. Overview of Operation
+ If the Session-Expires interval is too low for a proxy (i.e., lower
+ than the value of Min-SE that the proxy would wish to assert), the
+ proxy rejects the request with a 422 response. That response
+ contains a Min-SE header field identifying the minimum session
+ interval it is willing to support. The UAC will try again, this time
+ including the Min-SE header field in the request. The header field
+ contains the largest Min-SE header field it observed in all 422
+ responses previously received. This way, the minimum timer meets the
+ constraints of all proxies along the path.
+
+ RFC 4825 - 6. 422 Response Code Definition
+ The 422 response MUST contain a Min-SE header field with the minimum timer for that server.
+ */
+
+ if((Min_SE = (const tsip_header_Min_SE_t* )tsip_message_get_header(r422, tsip_htype_Min_SE))){
+ self->stimers.minse = Min_SE->delta_seconds;
+ self->stimers.timer.timeout = Min_SE->delta_seconds;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid response (422 need Min-SE header)");
+ return 0; /* Do not end the dialog */
+ }
+
+ /* send again the INVITE */
+ return send_INVITE(self, tsk_false);
+}
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+
+
+
+/* cancel the timer */
+int tsip_dialog_invite_stimers_cancel(tsip_dialog_invite_t* self)
+{
+ return tsk_timer_mgr_global_cancel(self->stimers.timer.id);
+}
+
+/* schedule the timer */
+int tsip_dialog_invite_stimers_schedule(tsip_dialog_invite_t* self, uint64_t timeout)
+{
+ /* Used in SIP requests ==> do not change the value
+ self->stimers.timer.timeout = timeout;
+ */
+ self->stimers.timer.id = tsk_timer_mgr_global_schedule(timeout, TSK_TIMER_CALLBACK_F(tsip_dialog_invite_timer_callback), self);
+
+ return 0;
+}
+
+/* handle requests/responses */
+int tsip_dialog_invite_stimers_handle(tsip_dialog_invite_t* self, const tsip_message_t* message)
+{
+ /* It's up to the caller to check that (self->stimers.timer.timeout is >0)
+ and message is INVITE or UPDATE or 2xxINVITE or 2xxUPDATE
+ */
+
+ int ret = 0;
+ const tsip_header_Session_Expires_t* hdr_SessionExpires;
+
+ if(!self || !message){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!self->stimers.timer.timeout){
+ /* guard for stupide callers */
+ return 0;
+ }
+ /* reUPDATE or reINVITE */
+ if(TSIP_MESSAGE_IS_REQUEST(message) && (TSIP_REQUEST_IS_UPDATE(message) || TSIP_REQUEST_IS_INVITE(message))){
+ if((hdr_SessionExpires = (const tsip_header_Session_Expires_t*)tsip_message_get_header(message, tsip_htype_Session_Expires))){
+ if(hdr_SessionExpires->delta_seconds < TSIP_SESSION_EXPIRES_MIN_VALUE){
+ self->stimers.minse = TSIP_SESSION_EXPIRES_MIN_VALUE;
+ ret = send_RESPONSE(self, message, 422, "Session Interval Too Small");
+ }
+ else{
+ 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");
+ }
+ }
+ }
+ /* 2xx */
+ else if(TSIP_MESSAGE_IS_RESPONSE(message) && (TSIP_RESPONSE_IS_TO_INVITE(message) || TSIP_RESPONSE_IS_TO_UPDATE(message))){
+ if(!TSIP_RESPONSE_IS_2XX(message)){
+ /* guard for stupide callers */
+ return 0;
+ }
+ /* Process the response only if it includes "Require: timer"
+
+ RFC 4028 - 7.2. Processing a 2xx Response
+ When a 2xx response to a session refresh request arrives, it may or
+ may not contain a Require header field with the value 'timer'. If it
+ does, the UAC MUST look for the Session-Expires header field to
+ process the response.
+
+ If there was a Require header field in the response with the value
+ 'timer', the Session-Expires header field will always be present.
+ UACs MUST be prepared to receive a Session-Expires header field in a
+ response, even if none were present in the request. The 'refresher'
+ parameter will be present in the Session-Expires header field,
+ indicating who will perform the refreshes. The UAC MUST set the
+ identity of the refresher to the value of this parameter. If the
+ parameter contains the value 'uac', the UAC will perform them.
+ */
+ if(tsip_message_required(message, "timer")){
+ if((hdr_SessionExpires = (const tsip_header_Session_Expires_t*)tsip_message_get_header(message, tsip_htype_Session_Expires))){
+ if(hdr_SessionExpires->delta_seconds < TSIP_SESSION_EXPIRES_MIN_VALUE){
+ self->stimers.minse = TSIP_SESSION_EXPIRES_MIN_VALUE;
+ ret = send_RESPONSE(self, message, 422, "Interval Too short");
+ }
+ else{
+ 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, "uac");
+ self->supported.timer = (self->stimers.timer.timeout != 0);
+ self->required.timer = self->supported.timer;
+ }
+ }
+ else{
+ self->stimers.timer.timeout = 0; /* turned-off */
+ self->supported.timer = tsk_false;
+ self->required.timer = tsk_false;
+ ret = send_RESPONSE(self, message, 481, "Session-Expires header is missing");
+ return 0;
+ }
+ }
+ else{
+ /*
+ RFC 4028 - 7.2. Processing a 2xx Response
+ If the 2xx response did not contain a Session-Expires header field,
+ there is no session expiration. In this case, no refreshes need to
+ be sent. A 2xx without a Session-Expires can come for both initial
+ and subsequent session refresh requests. This means that the session
+ timer can be 'turned-off' in mid dialog by receiving a response
+ without a Session-Expires header field.
+ */
+ self->stimers.timer.timeout = 0; /* turned-off */
+ self->supported.timer = tsk_false;
+ self->required.timer = tsk_false;
+ }
+ }
+
+ /* Cancel timeout */
+ tsip_dialog_invite_stimers_cancel(self);
+
+ /* schedule timer */
+ 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));
+ }
+ }
+
+ return ret;
+}
diff --git a/tinySIP/src/dialogs/tsip_dialog_layer.c b/tinySIP/src/dialogs/tsip_dialog_layer.c
new file mode 100644
index 0000000..9a33fd3
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_layer.c
@@ -0,0 +1,776 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_layer.c
+ * @brief SIP dialog layer.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_layer.h"
+
+#include "tinysip/dialogs/tsip_dialog_invite.h"
+#include "tinysip/dialogs/tsip_dialog_message.h"
+#include "tinysip/dialogs/tsip_dialog_info.h"
+#include "tinysip/dialogs/tsip_dialog_options.h"
+#include "tinysip/dialogs/tsip_dialog_publish.h"
+#include "tinysip/dialogs/tsip_dialog_register.h"
+#include "tinysip/dialogs/tsip_dialog_subscribe.h"
+
+#include "tinysip/transactions/tsip_transac_layer.h"
+#include "tinysip/transports/tsip_transport_layer.h"
+
+#include "tsk_debug.h"
+
+extern tsip_ssession_handle_t *tsip_ssession_create_2(const tsip_stack_t* stack, const struct tsip_message_s* message);
+
+/*== Predicate function to find dialog by type */
+static int pred_find_dialog_by_type(const tsk_list_item_t *item, const void *type)
+{
+ if(item && item->data){
+ tsip_dialog_t *dialog = item->data;
+ return (dialog->type - *((tsip_dialog_type_t*)type));
+ }
+ return -1;
+}
+
+/*== Predicate function to find dialog by not type */
+static int pred_find_dialog_by_not_type(const tsk_list_item_t *item, const void *type)
+{
+ if(item && item->data){
+ tsip_dialog_t *dialog = item->data;
+ if(dialog->type != *((tsip_dialog_type_t*)type)){
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/*== Predicate function to find dialog by callid */
+static int pred_find_dialog_by_callid(const tsk_list_item_t *item, const void *callid)
+{
+ if(item && item->data && callid){
+ return tsk_strcmp(((tsip_dialog_t*)item->data)->callid, ((const char*)callid));
+ }
+ return -1;
+}
+
+tsip_dialog_layer_t* tsip_dialog_layer_create(tsip_stack_t* stack)
+{
+ return tsk_object_new(tsip_dialog_layer_def_t, stack);
+}
+
+// it's up to the caller to release the returned object
+tsip_dialog_t* tsip_dialog_layer_find_by_ss(tsip_dialog_layer_t *self, const tsip_ssession_handle_t *ss)
+{
+ return tsip_dialog_layer_find_by_ssid(self, tsip_ssession_get_id(ss));
+}
+
+// it's up to the caller to release the returned object
+tsip_dialog_t* tsip_dialog_layer_find_by_ssid(tsip_dialog_layer_t *self, tsip_ssession_id_t ssid)
+{
+ tsip_dialog_t *ret = 0;
+ tsip_dialog_t *dialog;
+ tsk_list_item_t *item;
+
+ tsk_safeobj_lock(self);
+
+ tsk_list_foreach(item, self->dialogs){
+ dialog = item->data;
+ if(tsip_ssession_get_id(dialog->ss) == ssid){
+ ret = dialog;
+ break;
+ }
+ }
+
+ tsk_safeobj_unlock(self);
+
+ return tsk_object_ref(ret);
+}
+
+// it's up to the caller to release the returned object
+tsip_dialog_t* tsip_dialog_layer_find_by_callid(tsip_dialog_layer_t *self, const char* callid)
+{
+ if(!self || !callid){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+ else{
+ tsip_dialog_t *dialog = tsk_null;
+ tsk_list_item_t *item;
+ //--tsk_safeobj_lock(self);
+ tsk_list_foreach(item, self->dialogs){
+ if(tsk_striequals(TSIP_DIALOG(item->data)->callid, callid)){
+ dialog = tsk_object_ref(item->data);
+ break;
+ }
+ }
+ //--tsk_safeobj_unlock(self);
+ return dialog;
+ }
+}
+
+tsk_bool_t tsip_dialog_layer_have_dialog_with_callid(const tsip_dialog_layer_t *self, const char* callid)
+{
+ tsk_bool_t found = tsk_false;
+ if(self){
+ tsk_safeobj_lock(self);
+ if(tsk_list_find_item_by_pred(self->dialogs, pred_find_dialog_by_callid, callid) != tsk_null){
+ found = tsk_true;
+ }
+ tsk_safeobj_unlock(self);
+ }
+ return found;
+}
+
+// it's up to the caller to release the returned object
+tsip_dialog_t* tsip_dialog_layer_find(const tsip_dialog_layer_t *self, const char* callid, const char* to_tag, const char* from_tag, tsip_request_type_t type, tsk_bool_t *cid_matched)
+{
+ tsip_dialog_t *ret = tsk_null;
+ tsip_dialog_t *dialog;
+ tsk_list_item_t *item;
+
+ *cid_matched = tsk_false;
+
+ tsk_safeobj_lock(self);
+
+ tsk_list_foreach(item, self->dialogs){
+ dialog = item->data;
+ if(tsk_strequals(dialog->callid, callid)){
+ tsk_bool_t is_cancel = (type == tsip_CANCEL); // Incoming CANCEL
+ tsk_bool_t is_register = (type == tsip_REGISTER); // Incoming REGISTER
+ tsk_bool_t is_notify = (type == tsip_NOTIFY); // Incoming NOTIFY
+ *cid_matched = tsk_true;
+ /* CANCEL Request will have the same local tag than the INVITE request
+ the remote tag could be null if the CANCEL request is received immediately after a 100 Trying
+ */
+ if((is_cancel || tsk_strequals(dialog->tag_local, from_tag)) && (!dialog->tag_remote || tsk_strequals(dialog->tag_remote, to_tag))){
+ ret = tsk_object_ref(dialog);
+ break;
+ }
+ /* REGISTER is dialogless which means that each reREGISTER or unREGISTER will have empty to tag */
+ if(is_register /* Do not check tags */){
+ ret = tsk_object_ref(dialog);
+ break;
+ }
+ /* NOTIFY could arrive before the 200 SUBSCRIBE => This is why we don't try to match both tags
+
+ RFC 3265 - 3.1.4.4. Confirmation of Subscription Creation
+ Due to the potential for both out-of-order messages and forking, the
+ subscriber MUST be prepared to receive NOTIFY messages before the
+ SUBSCRIBE transaction has completed.
+ */
+ if(is_notify /* Do not check tags */){
+ ret = tsk_object_ref(dialog);
+ break;
+ }
+ }
+ }
+
+ tsk_safeobj_unlock(self);
+
+ return ret;
+}
+
+tsk_size_t tsip_dialog_layer_count_active_calls(tsip_dialog_layer_t *self)
+{
+ tsk_size_t count = 0;
+
+ tsip_dialog_t *dialog;
+ tsk_list_item_t *item;
+
+ tsk_safeobj_lock(self);
+
+ tsk_list_foreach(item, self->dialogs) {
+ if ((dialog = item->data) && dialog->type == tsip_dialog_INVITE && dialog->state != tsip_initial && dialog->state != tsip_terminated) {
+ ++count;
+ }
+ }
+
+ tsk_safeobj_unlock(self);
+
+ return count;
+}
+
+/** Hangup all dialogs starting by non-REGISTER */
+int tsip_dialog_layer_shutdownAll(tsip_dialog_layer_t *self)
+{
+ if(self){
+ tsk_bool_t wait = tsk_false;
+ tsk_list_item_t *item;
+ tsip_dialog_t *dialog;
+ tsip_dialog_type_t regtype = tsip_dialog_REGISTER;
+
+ if(!self->shutdown.inprogress){
+ self->shutdown.inprogress = tsk_true;
+ if (!self->shutdown.condwait) {
+ self->shutdown.condwait = tsk_condwait_create();
+ }
+ }
+
+ tsk_safeobj_lock(self);
+ if(tsk_list_count(self->dialogs, pred_find_dialog_by_not_type, &regtype)){
+ /* There are non-register dialogs ==> phase-1 */
+ goto phase1;
+ }
+ else if(tsk_list_count(self->dialogs, pred_find_dialog_by_type, &regtype)){
+ /* There are one or more register dialogs ==> phase-2 */
+ goto phase2;
+ }
+ else{
+ tsk_safeobj_unlock(self);
+ goto done;
+ }
+
+phase1:
+ /* Phase 1 - shutdown all except register and silent_hangup */
+ TSK_DEBUG_INFO("== Shutting down - Phase-1 started ==");
+phase1_loop:
+ tsk_list_foreach(item, self->dialogs){
+ dialog = item->data;
+ if(dialog->type != tsip_dialog_REGISTER && !dialog->ss->silent_hangup){
+ item = tsk_object_ref(item);
+ if(!tsip_dialog_shutdown(dialog, tsk_null)){
+ wait = tsk_true;
+ }
+
+ // if "tsip_dialog_shutdown()" remove the dialog, then
+ // "self->dialogs" will be unsafe
+ if(!(item = tsk_object_unref(item))){
+ goto phase1_loop;
+ }
+ }
+ }
+ tsk_safeobj_unlock(self);
+
+ /* wait until phase-1 is completed */
+ if(wait){
+ tsk_condwait_timedwait(self->shutdown.condwait, TSIP_DIALOG_SHUTDOWN_TIMEOUT);
+ }
+
+ /* lock and goto phase2 */
+ tsk_safeobj_lock(self);
+ wait = tsk_false;
+ goto phase2;
+
+phase2:
+ /* Phase 2 - unregister */
+ TSK_DEBUG_INFO("== Shutting down - Phase-2 started ==");
+ self->shutdown.phase2 = tsk_true;
+phase2_loop:
+ tsk_list_foreach(item, self->dialogs){
+ dialog = item->data;
+ if(dialog->type == tsip_dialog_REGISTER){
+ item = tsk_object_ref(item);
+ if(!tsip_dialog_shutdown(dialog, tsk_null)){
+ wait = tsk_true;
+ }
+ // if "tsip_dialog_shutdown()" remove the dialog, then
+ // "self->dialogs" will be unsafe
+ if(!(item = tsk_object_unref(item))){
+ goto phase2_loop;
+ }
+ }
+ }
+ tsk_safeobj_unlock(self);
+
+ /* wait until phase-2 is completed */
+ if(wait){
+ tsk_condwait_timedwait(self->shutdown.condwait, TSIP_DIALOG_SHUTDOWN_TIMEOUT);
+ }
+
+
+ /* Phase 3 - silenthangup (dialogs will be terminated immediately) */
+ TSK_DEBUG_INFO("== Shutting down - Phase-3 ==");
+phase3_loop:
+ tsk_list_foreach(item, self->dialogs){
+ dialog = item->data;
+ if(dialog->ss->silent_hangup){
+ item = tsk_object_ref(item);
+ tsip_dialog_shutdown(dialog, tsk_null);
+
+ // if "tsip_dialog_shutdown()" remove the dialog, then
+ // "self->dialogs" will became unsafe while looping
+ if(!(item = tsk_object_unref(item))){
+ goto phase3_loop;
+ }
+ }
+ }
+
+done:
+ TSK_DEBUG_INFO("== Shutting down - Terminated ==");
+ return 0;
+ }
+ return -1;
+}
+
+static void* TSK_STDCALL _tsip_dialog_signal_transport_error_async(void* dialog)
+{
+ tsip_dialog_signal_transport_error(TSIP_DIALOG(dialog));
+ return tsk_null;
+}
+
+int tsip_dialog_layer_signal_stack_disconnected(tsip_dialog_layer_t *self)
+{
+ tsk_list_item_t *item;
+ // use copy for lock-free code and faster code. also fix issue 172 (https://code.google.com/p/idoubs/issues/detail?id=172)
+ tsip_dialogs_L_t *dialogs_copy;
+ tsip_dialog_t *dialog;
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if (!(dialogs_copy = tsk_list_create())) {
+ TSK_DEBUG_ERROR("Failed to create list");
+ return -1;
+ }
+
+ tsk_safeobj_lock(self);
+ tsk_list_pushback_list(dialogs_copy, self->dialogs);
+ tsk_safeobj_unlock(self);
+
+ tsk_list_foreach(item, dialogs_copy){
+ if((dialog = TSIP_DIALOG(item->data))){
+ tsip_dialog_signal_transport_error(dialog);
+ }
+ }
+ TSK_OBJECT_SAFE_FREE(dialogs_copy);
+ return 0;
+}
+
+int tsip_dialog_layer_signal_peer_disconnected(tsip_dialog_layer_t *self, const struct tsip_transport_stream_peer_s* peer)
+{
+ tsip_dialog_t *dialog;
+ const tsk_list_item_t *item;
+
+ if(!self || !peer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ //!\ must not lock the entire layer
+
+ // tsk_safeobj_lock(self);
+
+ tsk_list_lock(peer->dialogs_cids);
+ tsk_list_foreach(item, peer->dialogs_cids){
+ if((dialog = tsip_dialog_layer_find_by_callid(self, TSK_STRING_STR(item->data)))){
+ tsip_dialog_signal_transport_error(dialog);
+ TSK_OBJECT_SAFE_FREE(dialog);
+ }
+ else{
+ // To avoid this WARN, you should call tsip_dialog_layer_have_dialog_with_callid() before adding a callid to a peer
+ TSK_DEBUG_WARN("Stream peer holds call-id='%s' but the dialog layer doesn't know it", TSK_STRING_STR(item->data));
+ }
+ }
+ tsk_list_unlock(peer->dialogs_cids);
+
+ // tsk_safeobj_unlock(self);
+
+ return 0;
+}
+
+int tsip_dialog_layer_remove_callid_from_stream_peers(tsip_dialog_layer_t *self, const char* callid)
+{
+ if(self){
+ return tsip_transport_layer_remove_callid_from_stream_peers(self->stack->layer_transport, callid);
+ }
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+}
+
+/* the caller of this function must unref() the returned object */
+tsip_dialog_t* tsip_dialog_layer_new(tsip_dialog_layer_t *self, tsip_dialog_type_t type, const tsip_ssession_t *ss)
+{
+ tsip_dialog_t* ret = tsk_null;
+ tsip_dialog_t* dialog;
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ goto bail;
+ }
+
+ switch(type){
+ case tsip_dialog_INVITE:
+ {
+ if((dialog = (tsip_dialog_t*)tsip_dialog_invite_create(ss, tsk_null))){
+ ret = tsk_object_ref(dialog);
+ tsk_list_push_back_data(self->dialogs, (void**)&dialog);
+ }
+ break;
+ }
+ case tsip_dialog_MESSAGE:
+ {
+ if((dialog = (tsip_dialog_t*)tsip_dialog_message_create(ss))){
+ ret = tsk_object_ref(dialog);
+ tsk_list_push_back_data(self->dialogs, (void**)&dialog);
+ }
+ break;
+ }
+ case tsip_dialog_INFO:
+ {
+ if((dialog = (tsip_dialog_t*)tsip_dialog_info_create(ss))){
+ ret = tsk_object_ref(dialog);
+ tsk_list_push_back_data(self->dialogs, (void**)&dialog);
+ }
+ break;
+ }
+ case tsip_dialog_OPTIONS:
+ {
+ if((dialog = (tsip_dialog_t*)tsip_dialog_options_create(ss))){
+ ret = tsk_object_ref(dialog);
+ tsk_list_push_back_data(self->dialogs, (void**)&dialog);
+ }
+ break;
+ }
+ case tsip_dialog_PUBLISH:
+ {
+ if((dialog = (tsip_dialog_t*)tsip_dialog_publish_create(ss))){
+ ret = tsk_object_ref(dialog);
+ tsk_list_push_back_data(self->dialogs, (void**)&dialog);
+ }
+ break;
+ }
+ case tsip_dialog_REGISTER:
+ {
+ if((dialog = (tsip_dialog_t*)tsip_dialog_register_create(ss, tsk_null))){
+ ret = tsk_object_ref(dialog);
+ tsk_list_push_back_data(self->dialogs, (void**)&dialog);
+ }
+ break;
+ }
+ case tsip_dialog_SUBSCRIBE:
+ {
+ if((dialog = (tsip_dialog_t*)tsip_dialog_subscribe_create(ss))){
+ ret = tsk_object_ref(dialog);
+ tsk_list_push_back_data(self->dialogs, (void**)&dialog);
+ }
+ break;
+ }
+
+ default:
+ {
+ TSK_DEBUG_ERROR("Dialog type not supported.");
+ break;
+ }
+ }
+
+bail:
+ return ret;
+}
+
+int tsip_dialog_layer_remove(tsip_dialog_layer_t *self, const tsip_dialog_t *dialog)
+{
+ if(dialog && self){
+ tsip_dialog_type_t regtype = tsip_dialog_REGISTER;
+ tsk_safeobj_lock(self);
+
+ /* remove the dialog */
+ tsk_list_remove_item_by_data(self->dialogs, dialog);
+
+ /* whether shutting down? */
+ if(self->shutdown.inprogress){
+ if(self->shutdown.phase2){ /* Phase 2 (all non-REGISTER and silent dialogs have been removed) */
+ if(tsk_list_count(self->dialogs, pred_find_dialog_by_type, &regtype) == 0){
+ /* alert only if there is not REGISTER dialog (ignore silents) */
+ TSK_DEBUG_INFO("== Shutting down - Phase-2 completed ==");
+ tsk_condwait_broadcast(self->shutdown.condwait);
+ }
+ }
+ else{ /* Phase 1 */
+ if(tsk_list_count(self->dialogs, pred_find_dialog_by_not_type, &regtype) == 0){
+ /* alert only if all dialogs except REGISTER have been removed */
+ TSK_DEBUG_INFO("== Shutting down - Phase-1 completed ==");
+ tsk_condwait_broadcast(self->shutdown.condwait);
+ }
+ }
+ }
+
+ tsk_safeobj_unlock(self);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+// this function is only called if no transaction match
+// for responses, the transaction will always match
+int tsip_dialog_layer_handle_incoming_msg(const tsip_dialog_layer_t *self, tsip_message_t* message)
+{
+ int ret = -1;
+ tsk_bool_t cid_matched;
+ tsip_dialog_t* dialog;
+ tsip_transac_t* transac = tsk_null;
+ const tsip_transac_layer_t *layer_transac = self->stack->layer_transac;
+
+ if(!layer_transac){
+ goto bail;
+ }
+
+ //tsk_safeobj_lock(self);
+ dialog = tsip_dialog_layer_find(self, message->Call_ID->value,
+ TSIP_MESSAGE_IS_RESPONSE(message) ? message->To->tag : message->From->tag,
+ TSIP_MESSAGE_IS_RESPONSE(message) ? message->From->tag : message->To->tag,
+ TSIP_MESSAGE_IS_REQUEST(message) ? TSIP_MESSAGE_AS_REQUEST(message)->line.request.request_type : tsip_NONE,
+ &cid_matched);
+ //tsk_safeobj_unlock(self);
+
+ if(dialog){
+ if(TSIP_REQUEST_IS_CANCEL(message) || TSIP_REQUEST_IS_ACK(message)){
+ ret = dialog->callback(dialog, tsip_dialog_i_msg, message);
+ tsk_object_unref(dialog);
+ goto bail;
+ }
+ else{
+ static tsk_bool_t isCT = tsk_false;
+ tsip_transac_dst_t* dst = tsip_transac_dst_dialog_create(dialog);
+ transac = tsip_transac_layer_new(
+ layer_transac,
+ isCT,
+ message,
+ dst
+ );
+ TSK_OBJECT_SAFE_FREE(dst);
+ TSK_OBJECT_SAFE_FREE(dialog);
+ }
+ }
+ else{
+ /* MediaProxyMode : forward all non-INVITE messages */
+ if(self->stack->network.mode == tsip_stack_mode_webrtc2sip){
+ tsk_bool_t b2bua;
+
+ if(TSIP_MESSAGE_IS_REQUEST(message)){
+ // requests received over TCP/TLS/UDP must contain "ws-src-ip" and "ws-src-port" parameters
+ if(!TNET_SOCKET_TYPE_IS_WS(message->src_net_type) && !TNET_SOCKET_TYPE_IS_WSS(message->src_net_type)){
+ const char* ws_src_ip = tsk_params_get_param_value(message->line.request.uri->params, "ws-src-ip");
+ const tnet_port_t ws_src_port = (tnet_port_t)tsk_params_get_param_value_as_int(message->line.request.uri->params, "ws-src-port");
+ if(!tsip_transport_layer_have_stream_peer_with_remote_ip(self->stack->layer_transport, ws_src_ip, ws_src_port)){
+ if(!TSIP_REQUEST_IS_ACK(message)){ // ACK do not expect response
+#if 0 // code commented because when using mjserver, rejecting the forked INVITE terminate all dialogs: have to check if it's conform to RFC 3261 or not
+ tsip_response_t* response = tsip_response_new(488, "WebSocket Peer not connected", message);
+ ret = tsip_transport_layer_send(self->stack->layer_transport, "no-branch", response);
+ TSK_OBJECT_SAFE_FREE(response);
+ return ret;
+#else
+ TSK_DEBUG_INFO("Request for peer at %s:%d cannot be delivered", ws_src_ip, ws_src_port);
+#endif
+ }
+ return 0;
+ }
+ }
+ }
+
+ // "rtcweb-breaker" parameter will be in the Contact header for outgoing request and in the request-uri for incoming requests
+ b2bua = TSIP_REQUEST_IS_INVITE(message) && message->Contact && message->Contact->uri &&
+ (tsk_striequals(tsk_params_get_param_value(message->Contact->uri->params, "rtcweb-breaker"), "yes")
+ || tsk_striequals(tsk_params_get_param_value(message->line.request.uri->params, "rtcweb-breaker"), "yes"));
+
+ if(!b2bua){
+ // forward the message
+ static tsk_bool_t isCT = tsk_true;
+ tsip_transac_dst_t* dst;
+ tsip_transac_t* transac;
+
+ TSIP_MESSAGE(message)->update = tsk_true; // update AoR and Via
+ if((dst = tsip_transac_dst_net_create(TSIP_STACK(self->stack)))){
+ if((transac = tsip_transac_layer_new(self->stack->layer_transac, isCT, message, dst))){
+ ret = tsip_transac_start(transac, message);
+ TSK_OBJECT_SAFE_FREE(transac);
+ }
+ TSK_OBJECT_SAFE_FREE(dst);
+ }
+ return ret;
+ }
+ }
+
+ if(TSIP_MESSAGE_IS_REQUEST(message)){
+ tsip_ssession_t* ss = tsk_null;
+ tsip_dialog_t* newdialog = tsk_null;
+
+ switch(message->line.request.request_type){
+ case tsip_MESSAGE:
+ { /* Server incoming MESSAGE */
+ if((ss = tsip_ssession_create_2(self->stack, message))){
+ newdialog = (tsip_dialog_t*)tsip_dialog_message_create(ss);
+ }
+ break;
+ }
+ case tsip_INFO:
+ { /* Server incoming INFO */
+ if((ss = tsip_ssession_create_2(self->stack, message))){
+ newdialog = (tsip_dialog_t*)tsip_dialog_info_create(ss);
+ }
+ break;
+ }
+ case tsip_OPTIONS:
+ { /* Server incoming OPTIONS */
+ if((ss = tsip_ssession_create_2(self->stack, message))){
+ newdialog = (tsip_dialog_t*)tsip_dialog_options_create(ss);
+ }
+ break;
+ }
+
+ case tsip_REGISTER:
+ { /* incoming REGISTER */
+ if((ss = tsip_ssession_create_2(self->stack, message))){
+ newdialog = (tsip_dialog_t*)tsip_dialog_register_create(ss, message->Call_ID ? message->Call_ID->value : tsk_null);
+ }
+ break;
+ }
+
+ case tsip_INVITE:
+ { /* incoming INVITE */
+ if((ss = tsip_ssession_create_2(self->stack, message))){
+ newdialog = (tsip_dialog_t*)tsip_dialog_invite_create(ss, message->Call_ID ? message->Call_ID->value : tsk_null);
+ }
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }//switch
+
+ // for new dialog, create a new transac and start it later
+ if(newdialog){
+ static const tsk_bool_t isCT = tsk_false;
+ tsip_transac_dst_t* dst = tsip_transac_dst_dialog_create(newdialog);
+ transac = tsip_transac_layer_new(
+ layer_transac,
+ isCT,
+ message,
+ dst
+ );
+ if(message->local_fd > 0 && TNET_SOCKET_TYPE_IS_STREAM(message->src_net_type)) {
+ tsip_dialog_set_connected_fd(newdialog, message->local_fd);
+ }
+ tsk_list_push_back_data(self->dialogs, (void**)&newdialog); /* add new dialog to the layer */
+ TSK_OBJECT_SAFE_FREE(dst);
+ }
+
+ /* The dialog will become the owner of the SIP session
+ * => when destoyed => SIP session will be destroyed, unless the user-end takes ownership() */
+ TSK_OBJECT_SAFE_FREE(ss);
+ }
+ }
+
+ if(transac){
+ ret = tsip_transac_start(transac, message);
+ tsk_object_unref(transac);
+ }
+ /* - No transaction match for the SIP request
+ - ACK do not expect any response (http://code.google.com/p/imsdroid/issues/detail?id=420)
+ */
+ else if(TSIP_MESSAGE_IS_REQUEST(message) && !TSIP_REQUEST_IS_ACK(message)){
+ const tsip_transport_layer_t *layer;
+ tsip_response_t* response = tsk_null;
+ if(!dialog && cid_matched){
+ dialog = tsip_dialog_layer_find_by_callid((tsip_dialog_layer_t *)self, message->Call_ID->value);
+ }
+
+ if((layer = self->stack->layer_transport)){
+ if(cid_matched){ /* We are receiving our own message. */
+ response = tsip_response_new(482, "Loop Detected (Check your iFCs)", message);
+ if(response && !response->To->tag){/* Early dialog? */
+ response->To->tag = tsk_strdup("doubango");
+ }
+ }
+ else{
+ switch(message->line.request.request_type){
+ case tsip_OPTIONS: // Hacked to work on Tiscali IMS networks
+ case tsip_INFO:
+ response = tsip_response_new(405, "Method Not Allowed", message);
+ break;
+ default:
+ response = tsip_response_new(481, "Dialog/Transaction Does Not Exist", message);
+ break;
+ }
+ }
+ if(response){
+ if(dialog && TSIP_DIALOG_GET_SS(dialog)){
+ tsk_strupdate(&response->sigcomp_id, TSIP_DIALOG_GET_SS(dialog)->sigcomp_id);
+ }
+ ret = tsip_transport_layer_send(layer, response->firstVia ? response->firstVia->branch : "no-branch", response);
+ TSK_OBJECT_SAFE_FREE(response);
+ }
+ }
+
+ TSK_OBJECT_SAFE_FREE(dialog);
+ }
+
+bail:
+ return ret;
+}
+
+
+
+
+
+//========================================================
+// Dialog layer object definition
+//
+static tsk_object_t* tsip_dialog_layer_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_dialog_layer_t *layer = self;
+ if(layer){
+ layer->stack = va_arg(*app, const tsip_stack_t *);
+ layer->dialogs = tsk_list_create();
+
+ tsk_safeobj_init(layer);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_dialog_layer_dtor(tsk_object_t * self)
+{
+ tsip_dialog_layer_t *layer = self;
+ if(layer){
+ TSK_OBJECT_SAFE_FREE(layer->dialogs);
+
+ /* condwait */
+ if(layer->shutdown.condwait){
+ tsk_condwait_destroy(&layer->shutdown.condwait);
+ }
+
+ tsk_safeobj_deinit(layer);
+
+ TSK_DEBUG_INFO("*** Dialog Layer destroyed ***");
+ }
+ return self;
+}
+
+static int tsip_dialog_layer_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
+{
+ return -1;
+}
+
+static const tsk_object_def_t tsip_dialog_layer_def_s =
+{
+ sizeof(tsip_dialog_layer_t),
+ tsip_dialog_layer_ctor,
+ tsip_dialog_layer_dtor,
+ tsip_dialog_layer_cmp,
+};
+const tsk_object_def_t *tsip_dialog_layer_def_t = &tsip_dialog_layer_def_s;
diff --git a/tinySIP/src/dialogs/tsip_dialog_message.c b/tinySIP/src/dialogs/tsip_dialog_message.c
new file mode 100644
index 0000000..9619eda
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_message.c
@@ -0,0 +1,552 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_message.c
+ * @brief SIP dialog message (Client side).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_message.h"
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tinysip/api/tsip_api_message.h"
+
+#include "tinysip/headers/tsip_header_Dummy.h"
+#include "tinysip/headers/tsip_header_Min_Expires.h"
+
+#include "tinysip/transactions/tsip_transac_layer.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+#include "tsk_time.h"
+
+#define DEBUG_STATE_MACHINE 1
+#define TSIP_DIALOG_MESSAGE_SIGNAL(self, type, code, phrase, message) \
+ tsip_message_event_signal(type, TSIP_DIALOG(self)->ss, code, phrase, message)
+
+/* ======================== internal functions ======================== */
+static int send_MESSAGE(tsip_dialog_message_t *self);
+static int tsip_dialog_message_OnTerminated(tsip_dialog_message_t *self);
+
+/* ======================== transitions ======================== */
+static int tsip_dialog_message_Started_2_Sending_X_sendMESSAGE(va_list *app);
+static int tsip_dialog_message_Started_2_Receiving_X_recvMESSAGE(va_list *app);
+static int tsip_dialog_message_Sending_2_Sending_X_1xx(va_list *app);
+static int tsip_dialog_message_Sending_2_Terminated_X_2xx(va_list *app);
+static int tsip_dialog_message_Sending_2_Sending_X_401_407_421_494(va_list *app);
+static int tsip_dialog_message_Sending_2_Terminated_X_300_to_699(va_list *app);
+static int tsip_dialog_message_Sending_2_Terminated_X_cancel(va_list *app);
+static int tsip_dialog_message_Receiving_2_Terminated_X_accept(va_list *app);
+static int tsip_dialog_message_Receiving_2_Terminated_X_reject(va_list *app);
+static int tsip_dialog_message_Any_2_Terminated_X_transportError(va_list *app);
+static int tsip_dialog_message_Any_2_Terminated_X_Error(va_list *app);
+
+/* ======================== conds ======================== */
+
+/* ======================== actions ======================== */
+typedef enum _fsm_action_e
+{
+ _fsm_action_sendMESSAGE = tsip_atype_message_send,
+ _fsm_action_accept = tsip_atype_accept,
+ _fsm_action_reject = tsip_atype_reject,
+ _fsm_action_cancel = tsip_atype_cancel,
+ _fsm_action_shutdown = tsip_atype_shutdown,
+ _fsm_action_transporterror = tsip_atype_transport_error,
+
+ _fsm_action_receiveMESSAGE = 0xFF,
+ _fsm_action_1xx,
+ _fsm_action_2xx,
+ _fsm_action_401_407_421_494,
+ _fsm_action_300_to_699,
+ _fsm_action_error,
+}
+_fsm_action_t;
+
+/* ======================== states ======================== */
+typedef enum _fsm_state_e
+{
+ _fsm_state_Started,
+ _fsm_state_Sending,
+ _fsm_state_Receiving,
+ _fsm_state_Terminated
+}
+_fsm_state_t;
+
+
+static int tsip_dialog_message_event_callback(const tsip_dialog_message_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)){
+ 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)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_1xx, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS_2XX(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_2xx, msg, action);
+ }
+ else if(TSIP_RESPONSE_CODE(msg) == 401 || TSIP_RESPONSE_CODE(msg) == 407 || TSIP_RESPONSE_CODE(msg) == 421 || TSIP_RESPONSE_CODE(msg) == 494){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_401_407_421_494, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS_3456(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_300_to_699, msg, action);
+ }
+ else{ /* Should never happen */
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_error, msg, action);
+ }
+ }
+ else if (TSIP_REQUEST_IS_MESSAGE(msg)){ /* have been checked by dialog layer...but */
+ // REQUEST ==> Incoming MESSAGE
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_receiveMESSAGE, msg, tsk_null);
+ }
+ }
+ break;
+ }
+
+ case tsip_dialog_canceled:
+ {
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_cancel, msg, tsk_null);
+ break;
+ }
+
+ case tsip_dialog_terminated:
+ case tsip_dialog_timedout:
+ 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;
+}
+
+tsip_dialog_message_t* tsip_dialog_message_create(const tsip_ssession_handle_t* ss)
+{
+ return tsk_object_new(tsip_dialog_message_def_t, ss);
+}
+
+int tsip_dialog_message_init(tsip_dialog_message_t *self)
+{
+ //const tsk_param_t* param;
+
+ /* Initialize the state machine. */
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Started ===
+ */
+ // Started -> (send) -> Sending
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_sendMESSAGE, _fsm_state_Sending, tsip_dialog_message_Started_2_Sending_X_sendMESSAGE, "tsip_dialog_message_Started_2_Sending_X_sendMESSAGE"),
+ // Started -> (receive) -> Receiving
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_receiveMESSAGE, _fsm_state_Receiving, tsip_dialog_message_Started_2_Receiving_X_recvMESSAGE, "tsip_dialog_message_Started_2_Receiving_X_recvMESSAGE"),
+ // Started -> (Any) -> Started
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_dialog_message_Started_2_Started_X_any"),
+
+
+ /*=======================
+ * === Sending ===
+ */
+ // Sending -> (1xx) -> Sending
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_1xx, _fsm_state_Sending, tsip_dialog_message_Sending_2_Sending_X_1xx, "tsip_dialog_message_Sending_2_Sending_X_1xx"),
+ // Sending -> (2xx) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_2xx, _fsm_state_Terminated, tsip_dialog_message_Sending_2_Terminated_X_2xx, "tsip_dialog_message_Sending_2_Terminated_X_2xx"),
+ // Sending -> (401/407/421/494) -> Sending
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_401_407_421_494, _fsm_state_Sending, tsip_dialog_message_Sending_2_Sending_X_401_407_421_494, "tsip_dialog_message_Sending_2_Sending_X_401_407_421_494"),
+ // Sending -> (300_to_699) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_300_to_699, _fsm_state_Terminated, tsip_dialog_message_Sending_2_Terminated_X_300_to_699, "tsip_dialog_message_Sending_2_Terminated_X_300_to_699"),
+ // Sending -> (cancel) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_cancel, _fsm_state_Terminated, tsip_dialog_message_Sending_2_Terminated_X_cancel, "tsip_dialog_message_Sending_2_Terminated_X_cancel"),
+ // Sending -> (shutdown) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_shutdown, _fsm_state_Terminated, tsk_null, "tsip_dialog_message_Sending_2_Terminated_X_shutdown"),
+ // Sending -> (Any) -> Sending
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Sending, "tsip_dialog_message_Sending_2_Sending_X_any"),
+
+ /*=======================
+ * === Receiving ===
+ */
+ // Receiving -> (accept) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Receiving, _fsm_action_accept, _fsm_state_Terminated, tsip_dialog_message_Receiving_2_Terminated_X_accept, "tsip_dialog_message_Receiving_2_Terminated_X_accept"),
+ // Receiving -> (rejected) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Receiving, _fsm_action_reject, _fsm_state_Terminated, tsip_dialog_message_Receiving_2_Terminated_X_reject, "tsip_dialog_message_Receiving_2_Terminated_X_reject"),
+ // Receiving -> (Any) -> Receiving
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Receiving, "tsip_dialog_message_Receiving_2_Receiving_X_any"),
+
+ /*=======================
+ * === Any ===
+ */
+ // Any -> (transport error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, tsip_dialog_message_Any_2_Terminated_X_transportError, "tsip_dialog_message_Any_2_Terminated_X_transportError"),
+ // Any -> (transport error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_error, _fsm_state_Terminated, tsip_dialog_message_Any_2_Terminated_X_Error, "tsip_dialog_message_Any_2_Terminated_X_Error"),
+
+ TSK_FSM_ADD_NULL());
+
+ TSIP_DIALOG(self)->callback = TSIP_DIALOG_EVENT_CALLBACK_F(tsip_dialog_message_event_callback);
+
+ return 0;
+}
+
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+
+/* Started -> (sendMESSAGE) -> Sending
+*/
+int tsip_dialog_message_Started_2_Sending_X_sendMESSAGE(va_list *app)
+{
+ tsip_dialog_message_t *self;
+
+ self = va_arg(*app, tsip_dialog_message_t *);
+
+ TSIP_DIALOG(self)->running = tsk_true;
+
+ return send_MESSAGE(self);
+}
+
+/* Started -> (recvMESSAGE) -> Receiving
+*/
+int tsip_dialog_message_Started_2_Receiving_X_recvMESSAGE(va_list *app)
+{
+ tsip_dialog_message_t *self = va_arg(*app, tsip_dialog_message_t *);
+ const tsip_request_t *request = va_arg(*app, const tsip_request_t *);
+
+ /* Alert the user. */
+ TSIP_DIALOG_MESSAGE_SIGNAL(self, tsip_i_message,
+ tsip_event_code_dialog_request_incoming, "Incoming Request.", request);
+
+ /* Update last incoming MESSAGE */
+ TSK_OBJECT_SAFE_FREE(self->request);
+ self->request = tsk_object_ref((void*)request);
+
+ return 0;
+}
+
+/* Sending -> (1xx) -> Sending
+*/
+int tsip_dialog_message_Sending_2_Sending_X_1xx(va_list *app)
+{
+ /*tsip_dialog_message_t *self = va_arg(*app, tsip_dialog_message_t *);*/
+ /*const tsip_response_t *response = va_arg(*app, const tsip_response_t *);*/
+
+ return 0;
+}
+
+/* Sending -> (2xx) -> Sending
+*/
+int tsip_dialog_message_Sending_2_Terminated_X_2xx(va_list *app)
+{
+ tsip_dialog_message_t *self = va_arg(*app, tsip_dialog_message_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* Alert the user. */
+ TSIP_DIALOG_MESSAGE_SIGNAL(self, tsip_ao_message,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ /* Reset curr action */
+ tsip_dialog_set_curr_action(TSIP_DIALOG(self), tsk_null);
+
+ return 0;
+}
+
+/* Sending -> (401/407/421/494) -> Sending
+*/
+int tsip_dialog_message_Sending_2_Sending_X_401_407_421_494(va_list *app)
+{
+ tsip_dialog_message_t *self = va_arg(*app, tsip_dialog_message_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_MESSAGE_SIGNAL(self, tsip_ao_message,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return ret;
+ }
+
+ return send_MESSAGE(self);
+}
+
+/* Sending -> (300 to 699) -> Terminated
+*/
+int tsip_dialog_message_Sending_2_Terminated_X_300_to_699(va_list *app)
+{
+ tsip_dialog_message_t *self = va_arg(*app, tsip_dialog_message_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* set last error (or info) */
+ tsip_dialog_set_lasterror(TSIP_DIALOG(self), TSIP_RESPONSE_PHRASE(response), TSIP_RESPONSE_CODE(response));
+
+ /* Alert the user. */
+ TSIP_DIALOG_MESSAGE_SIGNAL(self, tsip_ao_message,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return 0;
+}
+
+/* Sending -> (cancel) -> Terminated
+*/
+int tsip_dialog_message_Sending_2_Terminated_X_cancel(va_list *app)
+{
+ tsip_dialog_message_t *self = va_arg(*app, tsip_dialog_message_t *);
+ /* const tsip_message_t *message = va_arg(*app, const tsip_message_t *); */
+
+ /* RFC 3261 - 9.1 Client Behavior
+ A CANCEL request SHOULD NOT be sent to cancel a request other than INVITE.
+ */
+
+ /* Cancel all transactions associated to this dialog (will also be done when the dialog is destroyed (worth nothing)) */
+ tsip_transac_layer_cancel_by_dialog(TSIP_DIALOG_GET_STACK(self)->layer_transac, TSIP_DIALOG(self));
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_request_cancelled, "MESSAGE cancelled");
+
+ return 0;
+}
+
+/* Receiving -> (accept) -> Terminated
+*/
+int tsip_dialog_message_Receiving_2_Terminated_X_accept(va_list *app)
+{
+ tsip_dialog_message_t *self;
+ const tsip_action_t* action;
+
+ self = va_arg(*app, tsip_dialog_message_t *);
+ va_arg(*app, tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ if(!self->request){
+ TSK_DEBUG_ERROR("There is non MESSAGE to accept()");
+ /* Not an error ...but do not update current action */
+ }
+ else{
+ tsip_response_t *response;
+ int ret;
+
+ /* curr_action is only used for outgoing requests */
+ /* tsip_dialog_set_curr_action(TSIP_DIALOG(self), action); */
+
+ /* send 200 OK */
+ if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 200, "OK", self->request))){
+ tsip_dialog_apply_action(response, action); /* apply action params to "this" response */
+ if((ret = tsip_dialog_response_send(TSIP_DIALOG(self), response))){
+ TSK_DEBUG_ERROR("Failed to send SIP response.");
+ TSK_OBJECT_SAFE_FREE(response);
+ return ret;
+ }
+ TSK_OBJECT_SAFE_FREE(response);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create SIP response.");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Receiving -> (reject) -> Terminated
+*/
+int tsip_dialog_message_Receiving_2_Terminated_X_reject(va_list *app)
+{
+ tsip_dialog_message_t *self;
+ const tsip_action_t* action;
+
+ self = va_arg(*app, tsip_dialog_message_t *);
+ va_arg(*app, tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ if(!self->request){
+ TSK_DEBUG_ERROR("There is non MESSAGE to reject()");
+ /* Not an error ...but do not update current action */
+ }
+ else{
+ tsip_response_t *response;
+ int ret;
+
+ /* curr_action is only used for outgoing requests */
+ /* tsip_dialog_set_curr_action(TSIP_DIALOG(self), action); */
+
+ /* send 486 Rejected */
+ if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 486, "Rejected", self->request))){
+ tsip_dialog_apply_action(response, action); /* apply action params to "this" response */
+ if((ret = tsip_dialog_response_send(TSIP_DIALOG(self), response))){
+ TSK_DEBUG_ERROR("Failed to send SIP response.");
+ TSK_OBJECT_SAFE_FREE(response);
+ return ret;
+ }
+ TSK_OBJECT_SAFE_FREE(response);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create SIP response.");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Any -> (transport error) -> Terminated
+*/
+int tsip_dialog_message_Any_2_Terminated_X_transportError(va_list *app)
+{
+ /*tsip_dialog_message_t *self = va_arg(*app, tsip_dialog_message_t *);*/
+ /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/
+
+ return 0;
+}
+
+/* Any -> (error) -> Terminated
+*/
+int tsip_dialog_message_Any_2_Terminated_X_Error(va_list *app)
+{
+ /*tsip_dialog_message_t *self = va_arg(*app, tsip_dialog_message_t *);*/
+ /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/
+
+ return 0;
+}
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+int send_MESSAGE(tsip_dialog_message_t *self)
+{
+ tsip_request_t* request = tsk_null;
+ int ret = -1;
+
+ if(!self){
+ return -1;
+ }
+
+ if(!(request = tsip_dialog_request_new(TSIP_DIALOG(self), "MESSAGE"))){
+ return -2;
+ }
+
+ /* apply action params to the request */
+ if(TSIP_DIALOG(self)->curr_action){
+ tsip_dialog_apply_action(request, TSIP_DIALOG(self)->curr_action);
+ }
+
+ ret = tsip_dialog_request_send(TSIP_DIALOG(self), request);
+ TSK_OBJECT_SAFE_FREE(request);
+
+ return ret;
+}
+
+
+int tsip_dialog_message_OnTerminated(tsip_dialog_message_t *self)
+{
+ TSK_DEBUG_INFO("=== MESSAGE Dialog terminated ===");
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminated,
+ TSIP_DIALOG(self)->last_error.phrase ? TSIP_DIALOG(self)->last_error.phrase : "Dialog terminated");
+
+ /* Remove from the dialog layer. */
+ return tsip_dialog_remove(TSIP_DIALOG(self));
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP dialog MESSAGE object definition
+//
+static tsk_object_t* tsip_dialog_message_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_dialog_message_t *dialog = self;
+ if(dialog){
+ tsip_ssession_handle_t *ss = va_arg(*app, tsip_ssession_handle_t *);
+
+ /* Initialize base class */
+ tsip_dialog_init(TSIP_DIALOG(self), tsip_dialog_MESSAGE, tsk_null, ss, _fsm_state_Started, _fsm_state_Terminated);
+
+ /* FSM */
+ TSIP_DIALOG_GET_FSM(self)->debug = DEBUG_STATE_MACHINE;
+ tsk_fsm_set_callback_terminated(TSIP_DIALOG_GET_FSM(self), TSK_FSM_ONTERMINATED_F(tsip_dialog_message_OnTerminated), (const void*)dialog);
+
+ /* Initialize the class itself */
+ tsip_dialog_message_init(self);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_dialog_message_dtor(tsk_object_t * self)
+{
+ tsip_dialog_message_t *dialog = self;
+ if(dialog){
+ /* DeInitialize base class (will cancel all transactions) */
+ tsip_dialog_deinit(TSIP_DIALOG(self));
+
+ /* DeInitialize self */
+ TSK_OBJECT_SAFE_FREE(dialog->request);
+
+ TSK_DEBUG_INFO("*** MESSAGE Dialog destroyed ***");
+ }
+ return self;
+}
+
+static int tsip_dialog_message_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_message_def_s =
+{
+ sizeof(tsip_dialog_message_t),
+ tsip_dialog_message_ctor,
+ tsip_dialog_message_dtor,
+ tsip_dialog_message_cmp,
+};
+const tsk_object_def_t *tsip_dialog_message_def_t = &tsip_dialog_message_def_s;
diff --git a/tinySIP/src/dialogs/tsip_dialog_options.c b/tinySIP/src/dialogs/tsip_dialog_options.c
new file mode 100644
index 0000000..135be69
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_options.c
@@ -0,0 +1,578 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_options.c
+ * @brief SIP dialog OPTIONS as per RFC 3261 section 11.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_options.h"
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tinysip/api/tsip_api_options.h"
+
+#include "tinysip/headers/tsip_header_Dummy.h"
+#include "tinysip/headers/tsip_header_Min_Expires.h"
+
+#include "tinysip/transactions/tsip_transac_layer.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+#include "tsk_time.h"
+
+#define DEBUG_STATE_MACHINE 1
+#define TSIP_DIALOG_OPTIONS_SIGNAL(self, type, code, phrase, options) \
+ tsip_options_event_signal(type, TSIP_DIALOG(self)->ss, code, phrase, options)
+
+/* ======================== internal functions ======================== */
+static int send_OPTIONS(tsip_dialog_options_t *self);
+static int send_response(tsip_dialog_options_t *self, short status, const char* phrase, const tsip_request_t *request);
+static int tsip_dialog_options_OnTerminated(tsip_dialog_options_t *self);
+
+/* ======================== transitions ======================== */
+static int tsip_dialog_options_Started_2_Sending_X_sendOPTIONS(va_list *app);
+static int tsip_dialog_options_Started_2_Receiving_X_recvOPTIONS(va_list *app);
+static int tsip_dialog_options_Sending_2_Sending_X_1xx(va_list *app);
+static int tsip_dialog_options_Sending_2_Terminated_X_2xx(va_list *app);
+static int tsip_dialog_options_Sending_2_Sending_X_401_407_421_494(va_list *app);
+static int tsip_dialog_options_Sending_2_Terminated_X_300_to_699(va_list *app);
+static int tsip_dialog_options_Sending_2_Terminated_X_cancel(va_list *app);
+static int tsip_dialog_options_Receiving_2_Terminated_X_accept(va_list *app);
+static int tsip_dialog_options_Receiving_2_Terminated_X_reject(va_list *app);
+static int tsip_dialog_options_Any_2_Terminated_X_transportError(va_list *app);
+static int tsip_dialog_options_Any_2_Terminated_X_Error(va_list *app);
+
+/* ======================== conds ======================== */
+
+/* ======================== actions ======================== */
+typedef enum _fsm_action_e
+{
+ _fsm_action_sendOPTIONS = tsip_atype_options_send,
+ _fsm_action_accept = tsip_atype_accept,
+ _fsm_action_reject = tsip_atype_reject,
+ _fsm_action_cancel = tsip_atype_cancel,
+ _fsm_action_shutdown = tsip_atype_shutdown,
+ _fsm_action_transporterror = tsip_atype_transport_error,
+
+ _fsm_action_receiveOPTIONS = 0xFF,
+ _fsm_action_1xx,
+ _fsm_action_2xx,
+ _fsm_action_401_407_421_494,
+ _fsm_action_300_to_699,
+ _fsm_action_error,
+}
+_fsm_action_t;
+
+/* ======================== states ======================== */
+typedef enum _fsm_state_e
+{
+ _fsm_state_Started,
+ _fsm_state_Sending,
+ _fsm_state_Receiving,
+ _fsm_state_Terminated
+}
+_fsm_state_t;
+
+
+int tsip_dialog_options_event_callback(const tsip_dialog_options_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)){
+ 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)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_1xx, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS_2XX(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_2xx, msg, action);
+ }
+ else if(TSIP_RESPONSE_CODE(msg) == 401 || TSIP_RESPONSE_CODE(msg) == 407 || TSIP_RESPONSE_CODE(msg) == 421 || TSIP_RESPONSE_CODE(msg) == 494){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_401_407_421_494, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS_3456(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_300_to_699, msg, action);
+ }
+ else{ /* should never happen */
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_error, msg, action);
+ }
+ }
+ else if (TSIP_REQUEST_IS_OPTIONS(msg)){ /* have been checked by dialog layer...but */
+ // REQUEST ==> Incoming OPTIONS
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_receiveOPTIONS, msg, tsk_null);
+ }
+ }
+ break;
+ }
+
+ case tsip_dialog_canceled:
+ {
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_cancel, msg, tsk_null);
+ break;
+ }
+
+ case tsip_dialog_terminated:
+ case tsip_dialog_timedout:
+ 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;
+}
+
+tsip_dialog_options_t* tsip_dialog_options_create(const tsip_ssession_handle_t* ss)
+{
+ return tsk_object_new(tsip_dialog_options_def_t, ss);
+}
+
+int tsip_dialog_options_init(tsip_dialog_options_t *self)
+{
+ //const tsk_param_t* param;
+
+ /* Initialize the state machine. */
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Started ===
+ */
+ // Started -> (send) -> Sending
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_sendOPTIONS, _fsm_state_Sending, tsip_dialog_options_Started_2_Sending_X_sendOPTIONS, "tsip_dialog_options_Started_2_Sending_X_sendOPTIONS"),
+ // Started -> (receive) -> Receiving
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_receiveOPTIONS, _fsm_state_Receiving, tsip_dialog_options_Started_2_Receiving_X_recvOPTIONS, "tsip_dialog_options_Started_2_Receiving_X_recvOPTIONS"),
+ // Started -> (Any) -> Started
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_dialog_options_Started_2_Started_X_any"),
+
+
+ /*=======================
+ * === Sending ===
+ */
+ // Sending -> (1xx) -> Sending
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_1xx, _fsm_state_Sending, tsip_dialog_options_Sending_2_Sending_X_1xx, "tsip_dialog_options_Sending_2_Sending_X_1xx"),
+ // Sending -> (2xx) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_2xx, _fsm_state_Terminated, tsip_dialog_options_Sending_2_Terminated_X_2xx, "tsip_dialog_options_Sending_2_Terminated_X_2xx"),
+ // Sending -> (401/407/421/494) -> Sending
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_401_407_421_494, _fsm_state_Sending, tsip_dialog_options_Sending_2_Sending_X_401_407_421_494, "tsip_dialog_options_Sending_2_Sending_X_401_407_421_494"),
+ // Sending -> (300_to_699) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_300_to_699, _fsm_state_Terminated, tsip_dialog_options_Sending_2_Terminated_X_300_to_699, "tsip_dialog_options_Sending_2_Terminated_X_300_to_699"),
+ // Sending -> (cancel) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Sending, _fsm_action_cancel, _fsm_state_Terminated, tsip_dialog_options_Sending_2_Terminated_X_cancel, "tsip_dialog_options_Sending_2_Terminated_X_cancel"),
+ // Sending -> (Any) -> Sending
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Sending, "tsip_dialog_options_Sending_2_Sending_X_any"),
+
+ /*=======================
+ * === Receiving ===
+ */
+ // Receiving -> (accept) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Receiving, _fsm_action_accept, _fsm_state_Terminated, tsip_dialog_options_Receiving_2_Terminated_X_accept, "tsip_dialog_options_Receiving_2_Terminated_X_accept"),
+ // Receiving -> (rejected) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Receiving, _fsm_action_reject, _fsm_state_Terminated, tsip_dialog_options_Receiving_2_Terminated_X_reject, "tsip_dialog_options_Receiving_2_Terminated_X_reject"),
+ // Receiving -> (Any) -> Receiving
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Receiving, "tsip_dialog_options_Receiving_2_Receiving_X_any"),
+
+ /*=======================
+ * === Any ===
+ */
+ // Any -> (transport error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, tsip_dialog_options_Any_2_Terminated_X_transportError, "tsip_dialog_options_Any_2_Terminated_X_transportError"),
+ // Any -> (transport error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_error, _fsm_state_Terminated, tsip_dialog_options_Any_2_Terminated_X_Error, "tsip_dialog_options_Any_2_Terminated_X_Error"),
+
+ TSK_FSM_ADD_NULL());
+
+ TSIP_DIALOG(self)->callback = TSIP_DIALOG_EVENT_CALLBACK_F(tsip_dialog_options_event_callback);
+
+ return 0;
+}
+
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+
+/* Started -> (sendOPTIONS) -> Sending
+*/
+int tsip_dialog_options_Started_2_Sending_X_sendOPTIONS(va_list *app)
+{
+ tsip_dialog_options_t *self;
+
+ self = va_arg(*app, tsip_dialog_options_t *);
+
+ TSIP_DIALOG(self)->running = tsk_true;
+
+ /* alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");
+
+ return send_OPTIONS(self);
+}
+
+/* Started -> (recvOPTIONS) -> Receiving
+*/
+int tsip_dialog_options_Started_2_Receiving_X_recvOPTIONS(va_list *app)
+{
+ tsip_dialog_options_t *self = va_arg(*app, tsip_dialog_options_t *);
+ const tsip_request_t *request = va_arg(*app, const tsip_request_t *);
+
+ /* Update last incoming MESSAGE */
+ TSK_OBJECT_SAFE_FREE(self->last_iMessage);
+ self->last_iMessage = tsk_object_ref((void*)request);
+
+ /* Alert the user. */
+ TSIP_DIALOG_OPTIONS_SIGNAL(self, tsip_i_options,
+ tsip_event_code_dialog_request_incoming, "Incoming Request.", request);
+
+ return 0;
+}
+
+/* Sending -> (1xx) -> Sending
+*/
+int tsip_dialog_options_Sending_2_Sending_X_1xx(va_list *app)
+{
+ /*tsip_dialog_options_t *self = va_arg(*app, tsip_dialog_options_t *);*/
+ /*const tsip_response_t *response = va_arg(*app, const tsip_response_t *);*/
+
+ return 0;
+}
+
+/* Sending -> (2xx) -> Sending
+*/
+int tsip_dialog_options_Sending_2_Terminated_X_2xx(va_list *app)
+{
+ tsip_dialog_options_t *self = va_arg(*app, tsip_dialog_options_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* Alert the user. */
+ TSIP_DIALOG_OPTIONS_SIGNAL(self, tsip_ao_options,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ /* Reset curr action */
+ tsip_dialog_set_curr_action(TSIP_DIALOG(self), tsk_null);
+
+ return 0;
+}
+
+/* Sending -> (401/407/421/494) -> Sending
+*/
+int tsip_dialog_options_Sending_2_Sending_X_401_407_421_494(va_list *app)
+{
+ tsip_dialog_options_t *self = va_arg(*app, tsip_dialog_options_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_OPTIONS_SIGNAL(self, tsip_ao_options,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return ret;
+ }
+
+ return send_OPTIONS(self);
+}
+
+/* Sending -> (300 to 699) -> Terminated
+*/
+int tsip_dialog_options_Sending_2_Terminated_X_300_to_699(va_list *app)
+{
+ tsip_dialog_options_t *self = va_arg(*app, tsip_dialog_options_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* Alert the user. */
+ TSIP_DIALOG_OPTIONS_SIGNAL(self, tsip_ao_options,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return 0;
+}
+
+/* Sending -> (cancel) -> Terminated
+*/
+int tsip_dialog_options_Sending_2_Terminated_X_cancel(va_list *app)
+{
+ int ret;
+
+ tsip_dialog_options_t *self = va_arg(*app, tsip_dialog_options_t *);
+ /* const tsip_message_t *message = va_arg(*app, const tsip_message_t *); */
+
+ /* RFC 3261 - 9.1 Client Behavior
+ A CANCEL request SHOULD NOT be sent to cancel a request other than INVITE.
+ */
+
+ /* Cancel all transactions associated to this dialog (will also be done when the dialog is destroyed (worth nothing)) */
+ ret = tsip_transac_layer_cancel_by_dialog(TSIP_DIALOG_GET_STACK(self)->layer_transac, TSIP_DIALOG(self));
+
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_request_cancelled, "OPTIONS cancelled");
+
+ return ret;
+}
+
+/* Receiving -> (accept) -> Terminated
+*/
+int tsip_dialog_options_Receiving_2_Terminated_X_accept(va_list *app)
+{
+ tsip_dialog_options_t *self;
+ const tsip_action_t* action;
+
+ self = va_arg(*app, tsip_dialog_options_t *);
+ va_arg(*app, tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ if(!self->last_iMessage){
+ TSK_DEBUG_ERROR("There is non OPTIONS to accept()");
+ /* Not an error ...but do not update current action */
+ }
+ else{
+ tsip_response_t *response;
+ int ret;
+
+ /* curr_action is only used for outgoing requests */
+ /* tsip_dialog_set_curr_action(TSIP_DIALOG(self), action); */
+
+ /* send 200 OK */
+ if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 200, "OK", self->last_iMessage))){
+ tsip_dialog_apply_action(response, action); /* apply action params to "this" response */
+ if((ret = tsip_dialog_response_send(TSIP_DIALOG(self), response))){
+ TSK_DEBUG_ERROR("Failed to send SIP response.");
+ TSK_OBJECT_SAFE_FREE(response);
+ return ret;
+ }
+ TSK_OBJECT_SAFE_FREE(response);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create SIP response.");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Receiving -> (reject) -> Terminated
+*/
+int tsip_dialog_options_Receiving_2_Terminated_X_reject(va_list *app)
+{
+ tsip_dialog_options_t *self;
+ const tsip_action_t* action;
+
+ self = va_arg(*app, tsip_dialog_options_t *);
+ va_arg(*app, tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ if(!self->last_iMessage){
+ TSK_DEBUG_ERROR("There is non OPTIONS to reject()");
+ /* Not an error ...but do not update current action */
+ }
+ else{
+ tsip_response_t *response;
+ int ret;
+
+ /* curr_action is only used for outgoing requests */
+ /* tsip_dialog_set_curr_action(TSIP_DIALOG(self), action); */
+
+ /* send 486 Rejected */
+ if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 486, "Rejected", self->last_iMessage))){
+ tsip_dialog_apply_action(response, action); /* apply action params to "this" response */
+ if((ret = tsip_dialog_response_send(TSIP_DIALOG(self), response))){
+ TSK_DEBUG_ERROR("Failed to send SIP response.");
+ TSK_OBJECT_SAFE_FREE(response);
+ return ret;
+ }
+ TSK_OBJECT_SAFE_FREE(response);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create SIP response.");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Any -> (transport error) -> Terminated
+*/
+int tsip_dialog_options_Any_2_Terminated_X_transportError(va_list *app)
+{
+ /*tsip_dialog_options_t *self = va_arg(*app, tsip_dialog_options_t *);*/
+ /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/
+
+ return 0;
+}
+
+/* Any -> (error) -> Terminated
+*/
+int tsip_dialog_options_Any_2_Terminated_X_Error(va_list *app)
+{
+ /*tsip_dialog_options_t *self = va_arg(*app, tsip_dialog_options_t *);*/
+ /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/
+
+ return 0;
+}
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+int send_OPTIONS(tsip_dialog_options_t *self)
+{
+ tsip_request_t* request = tsk_null;
+ int ret = -1;
+
+ if(!self){
+ return -1;
+ }
+
+ if(!(request = tsip_dialog_request_new(TSIP_DIALOG(self), "OPTIONS"))){
+ return -2;
+ }
+
+ /* action parameters and payload*/
+ if(TSIP_DIALOG(self)->curr_action){
+ const tsk_list_item_t* item;
+ tsk_list_foreach(item, TSIP_DIALOG(self)->curr_action->headers){
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_DUMMY_VA_ARGS(TSK_PARAM(item->data)->name, TSK_PARAM(item->data)->value));
+ }
+ if(TSIP_DIALOG(self)->curr_action->payload){
+ tsip_message_add_content(request, tsk_null, TSK_BUFFER_DATA(TSIP_DIALOG(self)->curr_action->payload), TSK_BUFFER_SIZE(TSIP_DIALOG(self)->curr_action->payload));
+ }
+ }
+
+ ret = tsip_dialog_request_send(TSIP_DIALOG(self), request);
+ TSK_OBJECT_SAFE_FREE(request);
+
+ return ret;
+}
+
+int send_response(tsip_dialog_options_t *self, short status, const char* phrase, const tsip_request_t *request)
+{
+ tsip_response_t *response;
+ int ret = -1;
+
+ response = tsip_dialog_response_new(TSIP_DIALOG(self), status, phrase, request);
+ if(response){
+ if(response->To && !response->To->tag){
+ tsk_istr_t tag;
+ tsk_strrandom(&tag);
+ response->To->tag = tsk_strdup(tag);
+ }
+ ret = tsip_dialog_response_send(TSIP_DIALOG(self), response);
+ TSK_OBJECT_SAFE_FREE(response);
+ }
+
+ return ret;
+}
+
+
+int tsip_dialog_options_OnTerminated(tsip_dialog_options_t *self)
+{
+ TSK_DEBUG_INFO("=== OPTIONS Dialog terminated ===");
+
+ /* alert user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminated,
+ TSIP_DIALOG(self)->last_error.phrase ? TSIP_DIALOG(self)->last_error.phrase : "Dialog terminated");
+
+ /* Remove from the dialog layer. */
+ return tsip_dialog_remove(TSIP_DIALOG(self));
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP dialog OPTIONS object definition
+//
+static tsk_object_t* tsip_dialog_options_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_dialog_options_t *dialog = self;
+ if(dialog){
+ tsip_ssession_handle_t *ss = va_arg(*app, tsip_ssession_handle_t *);
+
+ /* Initialize base class */
+ tsip_dialog_init(TSIP_DIALOG(self), tsip_dialog_OPTIONS, tsk_null, ss, _fsm_state_Started, _fsm_state_Terminated);
+
+ /* FSM */
+ TSIP_DIALOG_GET_FSM(self)->debug = DEBUG_STATE_MACHINE;
+ tsk_fsm_set_callback_terminated(TSIP_DIALOG_GET_FSM(self), TSK_FSM_ONTERMINATED_F(tsip_dialog_options_OnTerminated), (const void*)dialog);
+
+ /* Initialize the class itself */
+ tsip_dialog_options_init(self);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_dialog_options_dtor(tsk_object_t * self)
+{
+ tsip_dialog_options_t *dialog = self;
+ if(dialog){
+
+ /* DeInitialize base class (will cancel all transactions) */
+ tsip_dialog_deinit(TSIP_DIALOG(self));
+
+ /* DeInitialize self */
+ TSK_OBJECT_SAFE_FREE(dialog->last_iMessage);
+
+ TSK_DEBUG_INFO("*** OPTIONS Dialog destroyed ***");
+ }
+ return self;
+}
+
+static int tsip_dialog_options_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_options_def_s =
+{
+ sizeof(tsip_dialog_options_t),
+ tsip_dialog_options_ctor,
+ tsip_dialog_options_dtor,
+ tsip_dialog_options_cmp,
+};
+const tsk_object_def_t *tsip_dialog_options_def_t = &tsip_dialog_options_def_s;
diff --git a/tinySIP/src/dialogs/tsip_dialog_publish.client.c b/tinySIP/src/dialogs/tsip_dialog_publish.client.c
new file mode 100644
index 0000000..aa22737
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_publish.client.c
@@ -0,0 +1,701 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_publish.client.c
+ * @brief SIP dialog PUBLISH as per RFC 3903.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_publish.h"
+
+#include "tinysip/headers/tsip_header_Dummy.h"
+#include "tinysip/headers/tsip_header_Min_Expires.h"
+#include "tinysip/headers/tsip_header_SIP_ETag.h"
+#include "tinysip/headers/tsip_header_SIP_If_Match.h"
+
+#include "tinysip/transactions/tsip_transac_layer.h"
+
+#include "tinysip/tsip_message.h"
+
+#include "tinysip/api/tsip_api_publish.h"
+
+#include "tsk_debug.h"
+#include "tsk_time.h"
+
+#define DEBUG_STATE_MACHINE 0
+#define TSIP_DIALOG_PUBLISH_TIMER_SCHEDULE(TX) TSIP_DIALOG_TIMER_SCHEDULE(publish, TX)
+#define TSIP_DIALOG_PUBLISH_SIGNAL(self, type, code, phrase, message) \
+ tsip_publish_event_signal(type, TSIP_DIALOG(self)->ss, code, phrase, message)
+
+/* ======================== internal functions ======================== */
+static int send_PUBLISH(tsip_dialog_publish_t *self);
+static int tsip_dialog_publish_OnTerminated(tsip_dialog_publish_t *self);
+
+/* ======================== transitions ======================== */
+static int tsip_dialog_publish_Started_2_Trying_X_publish(va_list *app);
+static int tsip_dialog_publish_Trying_2_Trying_X_1xx(va_list *app);
+static int tsip_dialog_publish_Trying_2_Terminated_X_2xx(va_list *app);
+static int tsip_dialog_publish_Trying_2_Connected_X_2xx(va_list *app);
+static int tsip_dialog_publish_Trying_2_Trying_X_401_407_421_494(va_list *app);
+static int tsip_dialog_publish_Trying_2_Trying_X_423(va_list *app);
+static int tsip_dialog_publish_Trying_2_Terminated_X_300_to_699(va_list *app);
+static int tsip_dialog_publish_Trying_2_Terminated_X_cancel(va_list *app);
+static int tsip_dialog_publish_Connected_2_Trying_X_publish(va_list *app);
+static int tsip_dialog_publish_Any_2_Trying_X_hangup(va_list *app);
+static int tsip_dialog_publish_Any_2_Trying_X_shutdown(va_list *app);
+static int tsip_dialog_publish_Any_2_Terminated_X_transportError(va_list *app);
+static int tsip_dialog_publish_Any_2_Terminated_X_Error(va_list *app);
+
+
+/* ======================== conds ======================== */
+static tsk_bool_t _fsm_cond_unpublishing(tsip_dialog_publish_t* dialog, tsip_message_t* message)
+{
+ return dialog->unpublishing;
+}
+static tsk_bool_t _fsm_cond_publishing(tsip_dialog_publish_t* dialog, tsip_message_t* message)
+{
+ return !_fsm_cond_unpublishing(dialog, message);
+}
+
+static tsk_bool_t _fsm_cond_silent_hangup(tsip_dialog_publish_t* dialog, tsip_message_t* message)
+{
+ return TSIP_DIALOG(dialog)->ss->silent_hangup;
+}
+static tsk_bool_t _fsm_cond_not_silent_hangup(tsip_dialog_publish_t* dialog, tsip_message_t* message)
+{
+ return !TSIP_DIALOG(dialog)->ss->silent_hangup;
+}
+#define _fsm_cond_silent_shutdown _fsm_cond_silent_hangup
+#define _fsm_cond_not_silent_shutdown _fsm_cond_not_silent_hangup
+
+
+/* ======================== actions ======================== */
+typedef enum _fsm_action_e
+{
+ _fsm_action_publish = tsip_atype_publish,
+ _fsm_action_cancel = tsip_atype_cancel,
+ _fsm_action_hangup = tsip_atype_unpublish,
+ _fsm_action_shutdown = tsip_atype_shutdown,
+ _fsm_action_transporterror = tsip_atype_transport_error,
+
+ _fsm_action_1xx = 0xFF,
+ _fsm_action_2xx,
+ _fsm_action_401_407_421_494,
+ _fsm_action_423,
+ _fsm_action_300_to_699,
+ _fsm_action_shutdown_timedout, /* Any -> Terminated */
+ _fsm_action_error,
+}
+_fsm_action_t;
+
+/* ======================== states ======================== */
+typedef enum _fsm_state_e
+{
+ _fsm_state_Started,
+ _fsm_state_Trying,
+ _fsm_state_Connected,
+ _fsm_state_Terminated
+}
+_fsm_state_t;
+
+
+/**
+ * Callback function called to alert the dialog for new events from the transaction/transport layers.
+ *
+ * @param [in,out] self A reference to the dialog.
+ * @param type The event type.
+ * @param [in,out] msg The incoming SIP/IMS message.
+ *
+ * @return Zero if succeed and non-zero error code otherwise.
+**/
+int tsip_dialog_publish_event_callback(const tsip_dialog_publish_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 && 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)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_1xx, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS_2XX(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_2xx, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS(msg,401) || TSIP_RESPONSE_IS(msg,407) || TSIP_RESPONSE_IS(msg,421) || TSIP_RESPONSE_IS(msg,494)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_401_407_421_494, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS(msg,423)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_423, msg, action);
+ }
+ else{
+ // Alert User
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_error, msg, action);
+ /* TSK_DEBUG_WARN("Not supported status code: %d", TSIP_RESPONSE_CODE(msg)); */
+ }
+ }
+ else{
+ //
+ // REQUEST
+ //
+ }
+ break;
+ }
+
+ case tsip_dialog_canceled:
+ {
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_cancel, msg, tsk_null);
+ break;
+ }
+
+ case tsip_dialog_terminated:
+ case tsip_dialog_timedout:
+ 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 [in,out] 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_publish_timer_callback(const tsip_dialog_publish_t* self, tsk_timer_id_t timer_id)
+{
+ int ret = -1;
+
+ if(self)
+ {
+ if(timer_id == self->timerrefresh.id){
+ tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_publish, tsk_null, tsk_null);
+ ret = 0;
+ }
+ else if(timer_id == self->timershutdown.id){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_error, tsk_null, tsk_null);
+ }
+ }
+ return ret;
+}
+
+
+tsip_dialog_publish_t* tsip_dialog_publish_create(const tsip_ssession_handle_t* ss)
+{
+ return tsk_object_new(tsip_dialog_publish_def_t, ss);
+}
+
+/**
+ * Initializes the dialog.
+ *
+ * @param [in,out] self The dialog to initialize.
+**/
+int tsip_dialog_publish_init(tsip_dialog_publish_t *self)
+{
+ /* Initialize the State Machine. */
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Started ===
+ */
+ // Started -> (PUBLISH) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_publish, _fsm_state_Trying, tsip_dialog_publish_Started_2_Trying_X_publish, "tsip_dialog_publish_Started_2_Trying_X_publish"),
+ // Started -> (Any) -> Started
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_dialog_publish_Started_2_Started_X_any"),
+
+
+ /*=======================
+ * === Trying ===
+ */
+ // Trying -> (1xx) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_1xx, _fsm_state_Trying, tsip_dialog_publish_Trying_2_Trying_X_1xx, "tsip_dialog_publish_Trying_2_Trying_X_1xx"),
+ // Trying -> (2xx) -> Terminated
+ TSK_FSM_ADD(_fsm_state_Trying, _fsm_action_2xx, _fsm_cond_unpublishing, _fsm_state_Terminated, tsip_dialog_publish_Trying_2_Terminated_X_2xx, "tsip_dialog_publish_Trying_2_Terminated_X_2xx"),
+ // Trying -> (2xx) -> Connected
+ TSK_FSM_ADD(_fsm_state_Trying, _fsm_action_2xx, _fsm_cond_publishing, _fsm_state_Connected, tsip_dialog_publish_Trying_2_Connected_X_2xx, "tsip_dialog_publish_Trying_2_Connected_X_2xx"),
+ // Trying -> (401/407/421/494) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_401_407_421_494, _fsm_state_Trying, tsip_dialog_publish_Trying_2_Trying_X_401_407_421_494, "tsip_dialog_publish_Trying_2_Trying_X_401_407_421_494"),
+ // Trying -> (423) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_423, _fsm_state_Trying, tsip_dialog_publish_Trying_2_Trying_X_423, "tsip_dialog_publish_Trying_2_Trying_X_423"),
+ // Trying -> (300_to_699) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_300_to_699, _fsm_state_Terminated, tsip_dialog_publish_Trying_2_Terminated_X_300_to_699, "tsip_dialog_publish_Trying_2_Terminated_X_300_to_699"),
+ // Trying -> (cancel) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_cancel, _fsm_state_Terminated, tsip_dialog_publish_Trying_2_Terminated_X_cancel, "tsip_dialog_publish_Trying_2_Terminated_X_cancel"),
+ // Trying -> (hangup) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_hangup, _fsm_state_Terminated, tsk_null, "tsip_dialog_publish_Trying_2_Terminated_X_hangup"),
+ // Trying -> (shutdown) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_shutdown, _fsm_state_Terminated, tsk_null, "tsip_dialog_publish_Trying_2_Terminated_X_shutdown"),
+ // Trying -> (Any) -> Trying
+ // TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Trying, "tsip_dialog_publish_Trying_2_Trying_X_any"),
+
+
+ /*=======================
+ * === Connected ===
+ */
+ // Connected -> (PUBLISH) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_publish, _fsm_state_Trying, tsip_dialog_publish_Connected_2_Trying_X_publish, "tsip_dialog_publish_Connected_2_Trying_X_publish"),
+
+ /*=======================
+ * === Any ===
+ */
+ // Any -> (transport error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, tsip_dialog_publish_Any_2_Terminated_X_transportError, "tsip_dialog_publish_Any_2_Terminated_X_transportError"),
+ // Any -> (error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_error, _fsm_state_Terminated, tsip_dialog_publish_Any_2_Terminated_X_Error, "tsip_dialog_publish_Any_2_Terminated_X_Error"),
+ // Any -> (hangup) -> Trying
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_hangup, _fsm_cond_not_silent_hangup, _fsm_state_Trying, tsip_dialog_publish_Any_2_Trying_X_hangup, "tsip_dialog_publish_Any_2_Trying_X_hangup"),
+ // Any -> (silenthangup) -> Terminated
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_hangup, _fsm_cond_silent_hangup, _fsm_state_Terminated, tsk_null, "tsip_dialog_publish_Any_2_Trying_X_silenthangup"),
+ // Any -> (shutdown) -> Trying
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_shutdown, _fsm_cond_not_silent_shutdown, _fsm_state_Trying, tsip_dialog_publish_Any_2_Trying_X_shutdown, "tsip_dialog_publish_Any_2_Trying_X_shutdown"),
+ // Any -> (silentshutdown) -> Terminated
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_shutdown, _fsm_cond_silent_shutdown, _fsm_state_Terminated, tsk_null, "tsip_dialog_publishe_Any_2_Trying_X_silentshutdown"),
+ // Any -> (shutdown timedout) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_shutdown_timedout, _fsm_state_Terminated, tsk_null, "tsip_dialog_publish_shutdown_timedout"),
+
+ TSK_FSM_ADD_NULL());
+
+ /* Sets callback function */
+ TSIP_DIALOG(self)->callback = TSIP_DIALOG_EVENT_CALLBACK_F(tsip_dialog_publish_event_callback);
+
+ /* Timers */
+ self->timerrefresh.id = TSK_INVALID_TIMER_ID;
+ self->timerrefresh.timeout = TSIP_DIALOG(self)->expires;
+ self->timershutdown.id = TSK_INVALID_TIMER_ID;
+ self->timershutdown.timeout = TSIP_DIALOG_SHUTDOWN_TIMEOUT;
+
+ return 0;
+}
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+
+/* Started -> (send) -> Trying
+*/
+int tsip_dialog_publish_Started_2_Trying_X_publish(va_list *app)
+{
+ tsip_dialog_publish_t *self;
+
+ self = va_arg(*app, tsip_dialog_publish_t *);
+
+ TSIP_DIALOG(self)->running = tsk_true;
+
+ /* alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");
+
+ return send_PUBLISH(self);
+}
+
+/* Trying -> (1xx) -> Trying
+*/
+int tsip_dialog_publish_Trying_2_Trying_X_1xx(va_list *app)
+{
+ /*tsip_dialog_publish_t *self = va_arg(*app, tsip_dialog_publish_t *);*/
+ /*const tsip_response_t *response = va_arg(*app, const tsip_response_t *);*/
+
+ return 0;
+}
+
+/* Trying -> (2xx) -> Terminated
+*/
+int tsip_dialog_publish_Trying_2_Terminated_X_2xx(va_list *app)
+{
+ tsip_dialog_publish_t *self = va_arg(*app, tsip_dialog_publish_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* Alert the user. */
+ TSIP_DIALOG_PUBLISH_SIGNAL(self, self->unpublishing ? tsip_ao_unpublish : tsip_ao_publish,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return 0;
+}
+
+/* Trying -> (2xx) -> Connected
+*/
+int tsip_dialog_publish_Trying_2_Connected_X_2xx(va_list *app)
+{
+ tsip_dialog_publish_t *self = va_arg(*app, tsip_dialog_publish_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+ int ret;
+
+ tsk_bool_t first_time_to_connect = (TSIP_DIALOG(self)->state == tsip_initial);
+
+ /* RFC 3903 - 4.1. Identification of Published Event State
+ For each successful PUBLISH request, the ESC will generate and assign
+ an entity-tag and return it in the SIP-ETag header field of the 2xx
+ response.
+ */
+ const tsip_header_SIP_ETag_t *SIP_ETag;
+ if((SIP_ETag = (const tsip_header_SIP_ETag_t*)tsip_message_get_header(response, tsip_htype_SIP_ETag))){
+ tsk_strupdate(&self->etag, SIP_ETag->value);
+ }
+
+ /* Alert the user (session)*/
+ TSIP_DIALOG_PUBLISH_SIGNAL(self, self->unpublishing ? tsip_ao_unpublish : tsip_ao_publish,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+ /* Alert the user (dialog)*/
+ if(first_time_to_connect){ /* PUBLISH not dialog oriented ...but */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connected, "Dialog connected");
+ }
+
+ /* Update the dialog state */
+ if((ret = tsip_dialog_update(TSIP_DIALOG(self), response))){
+ return ret;
+ }
+
+ /* Reset current action */
+ tsip_dialog_set_curr_action(TSIP_DIALOG(self), tsk_null);
+
+ /* Request timeout for dialog refresh (re-publish). */
+ self->timerrefresh.timeout = tsip_dialog_get_newdelay(TSIP_DIALOG(self), response);
+ TSIP_DIALOG_PUBLISH_TIMER_SCHEDULE(refresh);
+
+ return 0;
+}
+
+/* Trying -> (401/407/421/494) -> Trying
+*/
+int tsip_dialog_publish_Trying_2_Trying_X_401_407_421_494(va_list *app)
+{
+ tsip_dialog_publish_t *self = va_arg(*app, tsip_dialog_publish_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_PUBLISH_SIGNAL(self, self->unpublishing ? tsip_ao_unpublish : tsip_ao_publish,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return ret;
+ }
+
+ return send_PUBLISH(self);
+}
+
+/* Trying -> (423) -> Trying
+*/
+int tsip_dialog_publish_Trying_2_Trying_X_423(va_list *app)
+{
+ tsip_dialog_publish_t *self = va_arg(*app, tsip_dialog_publish_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ tsip_header_Min_Expires_t *hdr;
+
+ /*
+ RFC 3261 - 10.2.8 Error Responses
+
+ If a UA receives a 423 (Interval Too Brief) response, it MAY retry
+ the registration after making the expiration interval of all contact
+ addresses in the PUBLISH request equal to or greater than the
+ expiration interval within the Min-Expires header field of the 423
+ (Interval Too Brief) response.
+ */
+ hdr = (tsip_header_Min_Expires_t*)tsip_message_get_header(response, tsip_htype_Min_Expires);
+ if(hdr){
+ TSIP_DIALOG(self)->expires = TSK_TIME_S_2_MS(hdr->value);
+ send_PUBLISH(self);
+ }
+ else{
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_message_error, "Received invalid SIP response");
+
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Trying -> (300 to 699) -> Terminated
+*/
+int tsip_dialog_publish_Trying_2_Terminated_X_300_to_699(va_list *app)
+{
+ tsip_dialog_publish_t *self = va_arg(*app, tsip_dialog_publish_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* set last error (or info) */
+ tsip_dialog_set_lasterror(TSIP_DIALOG(self), TSIP_RESPONSE_PHRASE(response), TSIP_RESPONSE_CODE(response));
+
+ /* Alert the user. */
+ TSIP_DIALOG_PUBLISH_SIGNAL(self, self->unpublishing ? tsip_ao_unpublish : tsip_ao_publish,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return 0;
+}
+
+/* Trying -> (cancel) -> Terminated
+*/
+int tsip_dialog_publish_Trying_2_Terminated_X_cancel(va_list *app)
+{
+ int ret;
+ tsip_dialog_publish_t *self = va_arg(*app, tsip_dialog_publish_t *);
+ /* const tsip_message_t *message = va_arg(*app, const tsip_message_t *); */
+
+ /* Cancel all transactions associated to this dialog (will also be done when the dialog is destroyed (worth nothing)) */
+ ret = tsip_transac_layer_cancel_by_dialog(TSIP_DIALOG_GET_STACK(self)->layer_transac, TSIP_DIALOG(self));
+
+ /* RFC 3261 - 9.1 Client Behavior
+ A CANCEL request SHOULD NOT be sent to cancel a request other than INVITE.
+ */
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_request_cancelled, "Subscription cancelled");
+
+ return ret;
+}
+
+/* Connected -> (PUBLISH) -> Trying
+*/
+int tsip_dialog_publish_Connected_2_Trying_X_publish(va_list *app)
+{
+ tsip_dialog_publish_t *self;
+
+ self = va_arg(*app, tsip_dialog_publish_t *);
+
+ return send_PUBLISH(self);
+}
+
+/* Connected -> (hangup) -> Trying
+*/
+int tsip_dialog_publish_Any_2_Trying_X_hangup(va_list *app)
+{
+ tsip_dialog_publish_t *self;
+
+ self = va_arg(*app, tsip_dialog_publish_t *);
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog");
+
+ self->unpublishing = tsk_true;
+ return send_PUBLISH(self);
+}
+
+/* Any -> (shutdown) -> Trying
+*/
+int tsip_dialog_publish_Any_2_Trying_X_shutdown(va_list *app)
+{
+ tsip_dialog_publish_t *self = va_arg(*app, tsip_dialog_publish_t *);
+
+ /* schedule shutdow timeout */
+ TSIP_DIALOG_PUBLISH_TIMER_SCHEDULE(shutdown);
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog");
+
+ self->unpublishing = tsk_true;
+ return send_PUBLISH(self);
+}
+
+/* Any -> (transport error) -> Terminated
+*/
+int tsip_dialog_publish_Any_2_Terminated_X_transportError(va_list *app)
+{
+ tsip_dialog_publish_t *self = va_arg(*app, tsip_dialog_publish_t *);
+ /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/
+
+ /* Alert the user. */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_transport_error, "Transport error.");
+
+ return 0;
+}
+
+/* Any -> (Error) -> Terminated
+*/
+int tsip_dialog_publish_Any_2_Terminated_X_Error(va_list *app)
+{
+ tsip_dialog_publish_t *self = va_arg(*app, tsip_dialog_publish_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* Alert the user. */
+ if(response){
+ TSIP_DIALOG_PUBLISH_SIGNAL(self, self->unpublishing ? tsip_ao_unpublish : tsip_ao_publish,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+ }
+ else{
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_global_error, "Global error.");
+ }
+
+ return 0;
+}
+
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+/**
+ * Sends a PUBLISH request.
+**/
+int send_PUBLISH(tsip_dialog_publish_t *self)
+{
+ tsip_request_t *request = tsk_null;
+ int ret = -1;
+ const tsip_action_t* action;
+
+ if(!self){
+ return -1;
+ }
+
+ // action
+ action = TSIP_DIALOG(self)->curr_action;
+
+ if(self->unpublishing){
+ TSIP_DIALOG(self)->expires = 0;
+ }
+
+ /* RFC 3903 - 4.1. Identification of Published Event State
+ The presence of a body and the SIP-If-Match header field determine
+ the specific SSESSION that the request is performing, as described in Table 1.
+ +-----------+-------+---------------+---------------+
+ | SSESSION | Body? | SIP-If-Match? | Expires Value |
+ +-----------+-------+---------------+---------------+
+ | Initial | yes | no | > 0 |
+ | Refresh | no | yes | > 0 |
+ | Modify | yes | yes | > 0 |
+ | Remove | no | yes | 0 |
+ +-----------+-------+---------------+---------------+
+ Table 1: Publication ssessions
+ */
+ if((request = tsip_dialog_request_new(TSIP_DIALOG(self), "PUBLISH"))){
+ /*Etag. If initial then etag is null. */
+ if(self->etag){
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_SIP_IF_MATCH_VA_ARGS(self->etag));
+ }
+ /*Body*/
+ if(action && action->payload && !self->unpublishing){
+ const tsk_list_item_t* item;
+ tsk_list_foreach(item, action->headers){
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_DUMMY_VA_ARGS(TSK_PARAM(item->data)->name, TSK_PARAM(item->data)->value));
+ }
+ if(action->payload){
+ tsip_message_add_content(request, tsk_null, TSK_BUFFER_DATA(action->payload), TSK_BUFFER_SIZE(action->payload));
+ }
+ }
+
+ ret = tsip_dialog_request_send(TSIP_DIALOG(self), request);
+ TSK_OBJECT_SAFE_FREE(request);
+ }
+
+ return ret;
+}
+
+/**
+ * Callback function called by the state machine manager to signal that the final state has been reached.
+ *
+ * @param [in,out] self The state machine owner.
+**/
+int tsip_dialog_publish_OnTerminated(tsip_dialog_publish_t *self)
+{
+ TSK_DEBUG_INFO("=== PUBLISH Dialog terminated ===");
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminated,
+ TSIP_DIALOG(self)->last_error.phrase ? TSIP_DIALOG(self)->last_error.phrase : "Dialog terminated");
+
+ /* Remove from the dialog layer. */
+ return tsip_dialog_remove(TSIP_DIALOG(self));
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP dialog PUBLISH object definition
+//
+static tsk_object_t* tsip_dialog_publish_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_dialog_publish_t *dialog = self;
+ if(dialog){
+ tsip_ssession_handle_t *ss = va_arg(*app, tsip_ssession_handle_t *);
+
+ /* init base class */
+ tsip_dialog_init(TSIP_DIALOG(self), tsip_dialog_PUBLISH, tsk_null, ss, _fsm_state_Started, _fsm_state_Terminated);
+
+ /* FSM */
+ TSIP_DIALOG_GET_FSM(self)->debug = DEBUG_STATE_MACHINE;
+ tsk_fsm_set_callback_terminated(TSIP_DIALOG_GET_FSM(self), TSK_FSM_ONTERMINATED_F(tsip_dialog_publish_OnTerminated), (const void*)dialog);
+
+ /* init the class itself */
+ tsip_dialog_publish_init(self);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_dialog_publish_dtor(tsk_object_t * _self)
+{
+ tsip_dialog_publish_t *self = _self;
+ if(self){
+ TSK_DEBUG_INFO("*** PUBLISH Dialog destroyed ***");
+
+ /* Cancel all timers */
+ TSIP_DIALOG_TIMER_CANCEL(refresh);
+ TSIP_DIALOG_TIMER_CANCEL(shutdown);
+
+ /* deinit base class (will cancel all transactions) */
+ tsip_dialog_deinit(TSIP_DIALOG(self));
+
+ /* deinit self*/
+ TSK_FREE(self->etag);
+ }
+ return self;
+}
+
+static int tsip_dialog_publish_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_publish_def_s =
+{
+ sizeof(tsip_dialog_publish_t),
+ tsip_dialog_publish_ctor,
+ tsip_dialog_publish_dtor,
+ tsip_dialog_publish_cmp,
+};
+const tsk_object_def_t *tsip_dialog_publish_def_t = &tsip_dialog_publish_def_s;
diff --git a/tinySIP/src/dialogs/tsip_dialog_register.c b/tinySIP/src/dialogs/tsip_dialog_register.c
new file mode 100644
index 0000000..a1c1424
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_register.c
@@ -0,0 +1,507 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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.
+*
+*/
+#include "tinysip/dialogs/tsip_dialog_register.h"
+#include "tinysip/dialogs/tsip_dialog_register.common.h"
+
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tinysip/transports/tsip_transport_layer.h"
+
+#include "tinysip/headers/tsip_header_Dummy.h"
+#include "tinysip/headers/tsip_header_Supported.h"
+
+#include "tinysip/transactions/tsip_transac_layer.h"
+
+#include "tipsec.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+#include "tsk_time.h"
+
+
+/* ======================== internal functions ======================== */
+/*static*/ int tsip_dialog_register_send_REGISTER(tsip_dialog_register_t *self, tsk_bool_t initial);
+/*static*/ int tsip_dialog_register_send_RESPONSE(tsip_dialog_register_t *self, const tsip_request_t* request, short code, const char* phrase);
+/*static*/ int tsip_dialog_register_OnTerminated(tsip_dialog_register_t *self);
+
+/* ======================== transitions ======================== */
+static int tsip_dialog_register_Any_2_InProgress_X_hangup(va_list *app);
+static int tsip_dialog_register_Any_2_InProgress_X_shutdown(va_list *app);
+static int tsip_dialog_register_Any_2_Terminated_X_transportError(va_list *app);
+static int tsip_dialog_register_Any_2_Terminated_X_Error(va_list *app);
+
+/* ======================== conds ======================== */
+static tsk_bool_t _fsm_cond_silent_hangup(tsip_dialog_register_t* dialog, tsip_message_t* message)
+{
+ return TSIP_DIALOG(dialog)->ss->silent_hangup;
+}
+static tsk_bool_t _fsm_cond_not_silent_hangup(tsip_dialog_register_t* dialog, tsip_message_t* message)
+{
+ return !TSIP_DIALOG(dialog)->ss->silent_hangup;
+}
+
+
+/* Client-Side dialog */
+extern int tsip_dialog_register_client_init(tsip_dialog_register_t *self);
+/* Server-Side dialog */
+extern int tsip_dialog_register_server_init(tsip_dialog_register_t *self);
+
+/**
+ * @fn int tsip_dialog_register_event_callback(const tsip_dialog_register_t *self, tsip_dialog_event_type_t type,
+ * const tsip_message_t *msg)
+ *
+ * @brief Callback function called to alert the dialog for new events from the transaction/transport layers.
+ *
+ * @param [in,out] self A reference to the dialog.
+ * @param type The event type.
+ * @param [in,out] msg The incoming SIP/IMS message.
+ *
+ * @return Zero if succeed and non-zero error code otherwise.
+**/
+int tsip_dialog_register_event_callback(const tsip_dialog_register_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)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_1xx, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS_2XX(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_2xx, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS(msg,401) || TSIP_RESPONSE_IS(msg,407) || TSIP_RESPONSE_IS(msg,421) || TSIP_RESPONSE_IS(msg,494)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_401_407_421_494, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS(msg,423)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_423, msg, action);
+ }
+ else{
+ // Alert User
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_error, msg, action);
+ /* TSK_DEBUG_WARN("Not supported status code: %d", TSIP_RESPONSE_CODE(msg)); */
+ }
+ }
+ else{
+ //
+ // REQUEST
+ //
+ if(TSIP_REQUEST_IS_REGISTER(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_iREGISTER, msg, tsk_null);
+ }
+ }
+ }
+ break;
+ }
+
+ case tsip_dialog_canceled:
+ {
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_cancel, msg, tsk_null);
+ break;
+ }
+
+ case tsip_dialog_terminated:
+ case tsip_dialog_timedout:
+ 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_register_timer_callback(const tsip_dialog_register_t* self, tsk_timer_id_t timer_id)
+{
+ int ret = -1;
+
+ if(self){
+ if(timer_id == self->timerrefresh.id){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_oREGISTER, 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;
+}
+
+/** Create SIP REGISTER dialog. */
+tsip_dialog_register_t* tsip_dialog_register_create(const tsip_ssession_handle_t* ss, const char* call_id)
+{
+ return tsk_object_new(tsip_dialog_register_def_t, ss, call_id);
+}
+
+
+/** Initializes the dialog.
+ *
+ * @param [in,out] self The dialog to initialize.
+**/
+int tsip_dialog_register_init(tsip_dialog_register_t *self)
+{
+ // Initialize client side
+ tsip_dialog_register_client_init(self);
+ // initialize server side
+ tsip_dialog_register_server_init(self);
+
+ /* Initialize common side */
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Any ===
+ */
+ // Any -> (hangup) -> InProgress
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_hangup, _fsm_cond_not_silent_hangup, _fsm_state_InProgress, tsip_dialog_register_Any_2_InProgress_X_hangup, "tsip_dialog_register_Any_2_InProgress_X_hangup"),
+ // Any -> (silenthangup) -> Terminated
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_hangup, _fsm_cond_silent_hangup, _fsm_state_Terminated, tsk_null, "tsip_dialog_register_Any_2_InProgress_X_silenthangup"),
+ // Any -> (shutdown) -> InProgress
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_shutdown, _fsm_cond_not_silent_shutdown, _fsm_state_InProgress, tsip_dialog_register_Any_2_InProgress_X_shutdown, "tsip_dialog_register_Any_2_InProgress_X_shutdown"),
+ // Any -> (silentshutdown) -> Terminated
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_shutdown, _fsm_cond_silent_shutdown, _fsm_state_Terminated, tsk_null, "tsip_dialog_register_Any_2_InProgress_X_silentshutdown"),
+ // Any -> (shutdown timedout) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_shutdown_timedout, _fsm_state_Terminated, tsk_null, "tsip_dialog_register_shutdown_timedout"),
+ // Any -> (transport error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, tsip_dialog_register_Any_2_Terminated_X_transportError, "tsip_dialog_register_Any_2_Terminated_X_transportError"),
+ // Any -> (error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_error, _fsm_state_Terminated, tsip_dialog_register_Any_2_Terminated_X_Error, "tsip_dialog_register_Any_2_Terminated_X_Error"),
+
+ TSK_FSM_ADD_NULL());
+
+ /* Sets callback function */
+ TSIP_DIALOG(self)->callback = TSIP_DIALOG_EVENT_CALLBACK_F(tsip_dialog_register_event_callback);
+
+ /* Timers */
+ self->timerrefresh.id = TSK_INVALID_TIMER_ID;
+ self->timerrefresh.timeout = TSIP_DIALOG(self)->expires;
+ self->timershutdown.id = TSK_INVALID_TIMER_ID;
+ self->timershutdown.timeout = TSIP_DIALOG_SHUTDOWN_TIMEOUT;
+
+ return 0;
+}
+
+
+
+
+/* Any -> (hangup) -> InProgress
+*/
+int tsip_dialog_register_Any_2_InProgress_X_hangup(va_list *app)
+{
+ tsip_dialog_register_t *self;
+
+ self = va_arg(*app, tsip_dialog_register_t *);
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog");
+
+ self->unregistering = tsk_true;
+ return tsip_dialog_register_send_REGISTER(self, tsk_true);
+}
+
+/* Any -> (shutdown) -> InProgress
+*/
+int tsip_dialog_register_Any_2_InProgress_X_shutdown(va_list *app)
+{
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+
+ /* schedule shutdow timeout */
+ TSIP_DIALOG_REGISTER_TIMER_SCHEDULE(shutdown);
+
+ /* alert user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog");
+
+ self->unregistering = tsk_true;
+ return tsip_dialog_register_send_REGISTER(self, tsk_true);
+}
+
+/* Any -> (transport error) -> Terminated
+*/
+int tsip_dialog_register_Any_2_Terminated_X_transportError(va_list *app)
+{
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/
+
+ /* Alert the user. */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_transport_error, "Transport error.");
+
+ return 0;
+}
+
+/* Any -> (error) -> Terminated
+*/
+int tsip_dialog_register_Any_2_Terminated_X_Error(va_list *app)
+{
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* save last error */
+ tsip_dialog_set_lasterror_2(TSIP_DIALOG(self), TSIP_RESPONSE_PHRASE(response), TSIP_RESPONSE_CODE(response), response);
+
+ /* Alert the user. */
+ if(response){
+ TSIP_DIALOG_REGISTER_SIGNAL(self, self->unregistering ? tsip_ao_unregister : tsip_ao_register,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+ }
+ else{
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_global_error, "Global error.");
+ }
+
+ return 0;
+}
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+/**
+ *
+ * Sends a REGISTER request.
+ *
+ * @param [in,out] self The caller.
+ * @param [in] initial Indicates whether it's an initial (new CSeq) REGISTER or not.
+ * Initial REGISTER request will creates new IPSec temporary SAs.
+ *
+ * @return Zero if succeed and non-zero error code otherwise.
+**/
+int tsip_dialog_register_send_REGISTER(tsip_dialog_register_t *self, tsk_bool_t initial)
+{
+ tsip_request_t *request;
+ int ret = -1;
+
+ /* whether we are unregistering */
+ if(self->unregistering){
+ TSIP_DIALOG(self)->expires = 0;
+ }
+
+ /* creates REGISTER request */
+ if((request = tsip_dialog_request_new(TSIP_DIALOG(self), "REGISTER"))){
+ /* == RCS phase 2
+ */
+ /*if(TSIP_DIALOG_GET_STACK(self)->enable_gsmarcs){
+ TSIP_HEADER_ADD_PARAM(request->Contact, "+g.oma.sip-im.large-message", 0);
+ TSIP_HEADER_ADD_PARAM(request->Contact, "audio", 0);
+ TSIP_HEADER_ADD_PARAM(request->Contact, "video", 0);
+ TSIP_HEADER_ADD_PARAM(request->Contact, "+g.3gpp.cs-voice", 0);
+ TSIP_HEADER_ADD_PARAM(request->Contact, "+g.3gpp.icsi-ref", TSIP_ICSI_QUOTED_MMTEL_PSVOICE);
+ }*/
+
+ ///* mobility */
+ //if(TSIP_DIALOG_GET_STACK(self)->mobility){
+ // TSIP_HEADER_ADD_PARAM(request->Contact, "mobility", TSIP_DIALOG_GET_STACK(self)->mobility);
+ //}
+
+ ///* deviceID - FIXME: find reference. */
+ //if(TSIP_DIALOG_GET_STACK(self)->device_id){
+ // TSIP_HEADER_ADD_PARAM(request->Contact, "+deviceID", TSIP_DIALOG_GET_STACK(self)->device_id);
+ //}
+
+ ///* GSMA Image Sharing */
+ //if(TSIP_DIALOG_GET_STACK(self)->enable_gsmais){
+ // TSIP_HEADER_ADD_PARAM(request->Contact, "+g.3gpp.app_ref", TSIP_IARI_QUOTED_GSMAIS);
+ //}
+
+ ///* 3GPP TS 24.341 subclause 5.3.2.2 */
+ //if(TSIP_DIALOG_GET_STACK(self)->enable_3gppsms){
+ // TSIP_HEADER_ADD_PARAM(request->Contact, "+g.3gpp.smsip", 0);
+ //}
+
+ /* 3GPP TS 24.229 - 5.1.1.2 Initial registration */
+ if(TSIP_DIALOG(self)->state ==tsip_initial){
+ /*
+ g) the Supported header field containing the option-tag "path", and
+ 1) if GRUU is supported, the option-tag "gruu"; and
+ 2) if multiple registrations is supported, the option-tag "outbound".
+ */
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_SUPPORTED_VA_ARGS("path"));
+ //if(1==2/* gruu*/){
+ //}
+ //else if(2 == 3 /* multiple registrations */){
+ //}
+ }
+
+ /* action parameters and payload */
+ if(TSIP_DIALOG(self)->curr_action){
+ const tsk_list_item_t* item;
+ tsk_list_foreach(item, TSIP_DIALOG(self)->curr_action->headers){
+ TSIP_MESSAGE_ADD_HEADER(request, TSIP_HEADER_DUMMY_VA_ARGS(TSK_PARAM(item->data)->name, TSK_PARAM(item->data)->value));
+ }
+ if(TSIP_DIALOG(self)->curr_action->payload){
+ tsip_message_add_content(request, tsk_null, TSK_BUFFER_DATA(TSIP_DIALOG(self)->curr_action->payload), TSK_BUFFER_SIZE(TSIP_DIALOG(self)->curr_action->payload));
+ }
+ }
+
+ /* Create temorary SAs if initial register. */
+ if(TSIP_DIALOG_GET_STACK(self)->security.secagree_mech){
+ if(tsk_striequals(TSIP_DIALOG_GET_STACK(self)->security.secagree_mech, "ipsec-3gpp")){
+ if(initial){
+ tsip_transport_createTempSAs(TSIP_DIALOG_GET_STACK(self)->layer_transport);
+ }
+ else{
+ AKA_CK_T ck;
+ AKA_IK_T ik;
+ tsip_dialog_getCKIK(TSIP_DIALOG(self), &ck, &ik);
+ tsip_transport_startSAs(TSIP_DIALOG_GET_STACK(self)->layer_transport, (const tipsec_key_t*)ik, (const tipsec_key_t*)ck);
+ }
+ }
+ }
+
+ if(!(ret = tsip_dialog_request_send(TSIP_DIALOG(self), request))){
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_request_sent, "(un)REGISTER request successfully sent.");
+ }
+ else{
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_transport_error, "Transport error.");
+ }
+
+ TSK_OBJECT_SAFE_FREE(request);
+ }
+
+ return ret;
+}
+
+
+
+// Send any response
+int tsip_dialog_register_send_RESPONSE(tsip_dialog_register_t *self, const tsip_request_t* request, short code, const char* phrase)
+{
+ tsip_response_t *response;
+ int ret = -1;
+
+ if(!self || !request){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 1;
+ }
+
+ if((response = tsip_dialog_response_new(TSIP_DIALOG(self), code, phrase, request))){
+ ret = tsip_dialog_response_send(TSIP_DIALOG(self), response);
+ TSK_OBJECT_SAFE_FREE(response);
+ }
+ return ret;
+}
+
+
+
+/**
+ * @fn int tsip_dialog_register_OnTerminated(tsip_dialog_register_t *self)
+ *
+ * @brief Callback function called by the state machine manager to signal that the final state has been reached.
+ *
+ * @param [in,out] self The state machine owner.
+**/
+int tsip_dialog_register_OnTerminated(tsip_dialog_register_t *self)
+{
+ TSK_DEBUG_INFO("=== REGISTER Dialog terminated ===");
+
+ /* Cleanup IPSec SAs */
+ if(TSIP_DIALOG_GET_STACK(self)->security.secagree_mech && tsk_striequals(TSIP_DIALOG_GET_STACK(self)->security.secagree_mech, "ipsec-3gpp")){
+ tsip_transport_cleanupSAs(TSIP_DIALOG_GET_STACK(self)->layer_transport);
+ }
+ /* Reset values to avoid issues when the session is reused */
+ self->unregistering = tsk_false;
+ TSK_OBJECT_SAFE_FREE(self->last_iRegister);
+
+ /* 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 : "Dialog terminated",
+ TSIP_DIALOG(self)->last_error.message);
+
+ /* Remove from the dialog layer. */
+ return tsip_dialog_remove(TSIP_DIALOG(self));
+}
+
+
+
+
+//========================================================
+// SIP dialog REGISTER object definition
+//
+static tsk_object_t* tsip_dialog_register_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_dialog_register_t *dialog = self;
+ if(dialog){
+ tsip_ssession_t *ss = va_arg(*app, tsip_ssession_t *);
+ const char* call_id = va_arg(*app, const char *);
+
+ /* Initialize base class */
+ tsip_dialog_init(TSIP_DIALOG(self), tsip_dialog_REGISTER, call_id, ss, _fsm_state_Started, _fsm_state_Terminated);
+
+ /* create FSM */
+ TSIP_DIALOG_GET_FSM(self)->debug = DEBUG_STATE_MACHINE;
+ tsk_fsm_set_callback_terminated(TSIP_DIALOG_GET_FSM(self), TSK_FSM_ONTERMINATED_F(tsip_dialog_register_OnTerminated), (const void*)dialog);
+
+ /* Initialize the class itself */
+ tsip_dialog_register_init(self);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_dialog_register_dtor(tsk_object_t * _self)
+{
+ tsip_dialog_register_t *self = _self;
+ if(self){
+
+ /* Cancel all timers */
+ TSIP_DIALOG_TIMER_CANCEL(refresh);
+ TSIP_DIALOG_TIMER_CANCEL(shutdown);
+
+ /* DeInitialize base class (will cancel all transactions) */
+ tsip_dialog_deinit(TSIP_DIALOG(self));
+
+ // Delete resources
+ TSK_OBJECT_SAFE_FREE(self->last_iRegister);
+
+ TSK_DEBUG_INFO("*** REGISTER Dialog destroyed ***");
+ }
+ return self;
+}
+
+static int tsip_dialog_register_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_register_def_s =
+{
+ sizeof(tsip_dialog_register_t),
+ tsip_dialog_register_ctor,
+ tsip_dialog_register_dtor,
+ tsip_dialog_register_cmp,
+};
+const tsk_object_def_t *tsip_dialog_register_def_t = &tsip_dialog_register_def_s;
diff --git a/tinySIP/src/dialogs/tsip_dialog_register.client.c b/tinySIP/src/dialogs/tsip_dialog_register.client.c
new file mode 100644
index 0000000..fa5e99b
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_register.client.c
@@ -0,0 +1,424 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_register.client.c
+ * @brief SIP dialog register (Client side).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_register.h"
+#include "tinysip/dialogs/tsip_dialog_register.common.h"
+
+#include "tinysip/transports/tsip_transport_layer.h"
+#include "tinysip/transactions/tsip_transac_layer.h"
+
+#include "tinysip/headers/tsip_header_Path.h"
+#include "tinysip/headers/tsip_header_Service_Route.h"
+#include "tinysip/headers/tsip_header_P_Associated_URI.h"
+#include "tinysip/headers/tsip_header_Min_Expires.h"
+
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+/* ======================== external functions ======================== */
+extern int tsip_dialog_register_timer_callback(const tsip_dialog_register_t* self, tsk_timer_id_t timer_id);
+extern int tsip_dialog_register_send_REGISTER(tsip_dialog_register_t *self, tsk_bool_t initial);
+
+/* ======================== transitions ======================== */
+static int tsip_dialog_register_Started_2_InProgress_X_oRegister(va_list *app);
+static int tsip_dialog_register_InProgress_2_InProgress_X_1xx(va_list *app);
+static int tsip_dialog_register_InProgress_2_Terminated_X_2xx(va_list *app);
+static int tsip_dialog_register_InProgress_2_Connected_X_2xx(va_list *app);
+static int tsip_dialog_register_InProgress_2_InProgress_X_401_407_421_494(va_list *app);
+static int tsip_dialog_register_InProgress_2_InProgress_X_423(va_list *app);
+static int tsip_dialog_register_InProgress_2_Terminated_X_300_to_699(va_list *app);
+static int tsip_dialog_register_InProgress_2_Terminated_X_cancel(va_list *app);
+static int tsip_dialog_register_Connected_2_InProgress_X_oRegister(va_list *app);
+
+
+/* ======================== conds ======================== */
+static tsk_bool_t _fsm_cond_client_unregistering(tsip_dialog_register_t* dialog, tsip_message_t* message)
+{
+ return !dialog->is_server && dialog->unregistering;
+}
+static tsk_bool_t _fsm_cond_client_registering(tsip_dialog_register_t* dialog, tsip_message_t* message)
+{
+ return !_fsm_cond_client_unregistering(dialog, message);
+}
+
+
+/** Initializes the dialog.
+ *
+ * @param [in,out] self The dialog to initialize.
+**/
+int tsip_dialog_register_client_init(tsip_dialog_register_t *self)
+{
+ /* Initialize the state machine. */
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Started ===
+ */
+ // Started -> (REGISTER) -> InProgress
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_oREGISTER, _fsm_state_InProgress, tsip_dialog_register_Started_2_InProgress_X_oRegister, "tsip_dialog_register_Started_2_InProgress_X_oRegister"),
+ // Started -> (Any) -> Started
+ //TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_dialog_register_Started_2_Started_X_any"),
+
+
+ /*=======================
+ * === InProgress ===
+ */
+ // InProgress -> (1xx) -> InProgress
+ TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_1xx, _fsm_state_InProgress, tsip_dialog_register_InProgress_2_InProgress_X_1xx, "tsip_dialog_register_InProgress_2_InProgress_X_1xx"),
+ // InProgress -> (2xx) -> Terminated
+ TSK_FSM_ADD(_fsm_state_InProgress, _fsm_action_2xx, _fsm_cond_client_unregistering, _fsm_state_Terminated, tsip_dialog_register_InProgress_2_Terminated_X_2xx, "tsip_dialog_register_InProgress_2_Terminated_X_2xx"),
+ // InProgress -> (2xx) -> Connected
+ TSK_FSM_ADD(_fsm_state_InProgress, _fsm_action_2xx, _fsm_cond_client_registering, _fsm_state_Connected, tsip_dialog_register_InProgress_2_Connected_X_2xx, "tsip_dialog_register_InProgress_2_Connected_X_2xx"),
+ // InProgress -> (401/407/421/494) -> InProgress
+ TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_401_407_421_494, _fsm_state_InProgress, tsip_dialog_register_InProgress_2_InProgress_X_401_407_421_494, "tsip_dialog_register_InProgress_2_InProgress_X_401_407_421_494"),
+ // InProgress -> (423) -> InProgress
+ TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_423, _fsm_state_InProgress, tsip_dialog_register_InProgress_2_InProgress_X_423, "tsip_dialog_register_InProgress_2_InProgress_X_423"),
+ // InProgress -> (300_to_699) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_300_to_699, _fsm_state_Terminated, tsip_dialog_register_InProgress_2_Terminated_X_300_to_699, "tsip_dialog_register_InProgress_2_Terminated_X_300_to_699"),
+ // InProgress -> (cancel) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_cancel, _fsm_state_Terminated, tsip_dialog_register_InProgress_2_Terminated_X_cancel, "tsip_dialog_register_InProgress_2_Terminated_X_cancel"),
+ // InProgress -> (hangup) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_hangup, _fsm_state_Terminated, tsk_null, "tsip_dialog_register_InProgress_2_Terminated_X_hangup"),
+ // InProgress -> (shutdown) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_InProgress, _fsm_action_shutdown, _fsm_state_Terminated, tsk_null, "tsip_dialog_register_InProgress_2_Terminated_X_shutdown"),
+ // InProgress -> (Any) -> InProgress
+ //TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_InProgress, "tsip_dialog_register_InProgress_2_InProgress_X_any"),
+
+
+ /*=======================
+ * === Connected ===
+ */
+ // Connected -> (register) -> InProgress [refresh case]
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_oREGISTER, _fsm_state_InProgress, tsip_dialog_register_Connected_2_InProgress_X_oRegister, "tsip_dialog_register_Connected_2_InProgress_X_oRegister"),
+
+
+ TSK_FSM_ADD_NULL());
+
+ return 0;
+}
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+/* Started -> (REGISTER) -> InProgress
+*/
+int tsip_dialog_register_Started_2_InProgress_X_oRegister(va_list *app)
+{
+ tsip_dialog_register_t *self;
+
+ self = va_arg(*app, tsip_dialog_register_t *);
+
+ TSIP_DIALOG(self)->running = tsk_true;
+
+ /* alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");
+
+ return tsip_dialog_register_send_REGISTER(self, tsk_true);
+}
+
+/* InProgress -> (1xx) -> InProgress
+*/
+int tsip_dialog_register_InProgress_2_InProgress_X_1xx(va_list *app)
+{
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* Alert the user (session) */
+ TSIP_DIALOG_REGISTER_SIGNAL(self, tsip_ao_register,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return tsip_dialog_update(TSIP_DIALOG(self), response);
+}
+
+/* InProgress -> (2xx) -> Connected
+*/
+//#include "tsk_thread.h"
+int tsip_dialog_register_InProgress_2_Connected_X_2xx(va_list *app)
+{
+ int ret;
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ tsk_bool_t first_time_to_connect = (TSIP_DIALOG(self)->state == tsip_initial);
+
+ /* - Set P-associated-uriS
+ * - Update service-routes
+ * - Update Pats
+ */
+ {
+ tsk_size_t index;
+ const tsip_header_Path_t *hdr_Path;
+ const tsip_header_Service_Route_t *hdr_Service_Route;
+ const tsip_header_P_Associated_URI_t *hdr_P_Associated_URI_t;
+ tsip_uri_t *uri;
+
+ /* To avoid memory leaks ==> delete all concerned objects (it worth nothing) */
+ TSK_OBJECT_SAFE_FREE(TSIP_DIALOG_GET_STACK(self)->associated_uris);
+ TSK_OBJECT_SAFE_FREE(TSIP_DIALOG_GET_STACK(self)->service_routes);
+ TSK_OBJECT_SAFE_FREE(TSIP_DIALOG_GET_STACK(self)->paths);
+
+ /* Associated URIs */
+ for(index = 0; (hdr_P_Associated_URI_t = (const tsip_header_P_Associated_URI_t*)tsip_message_get_headerAt(response, tsip_htype_P_Associated_URI, index)); index++){
+ if(!TSIP_DIALOG_GET_STACK(self)->associated_uris){
+ TSIP_DIALOG_GET_STACK(self)->associated_uris = tsk_list_create();
+ }
+ uri = tsk_object_ref(hdr_P_Associated_URI_t->uri);
+ tsk_list_push_back_data(TSIP_DIALOG_GET_STACK(self)->associated_uris, (void**)&uri);
+ }
+
+ /* Service-Route (3GPP TS 24.229)
+ store the list of service route values contained in the Service-Route header field and bind the list to the contact
+ address used in registration, in order to build a proper preloaded Route header field value for new dialogs and
+ standalone transactions when using the respective contact address.
+ */
+ for(index = 0; (hdr_Service_Route = (const tsip_header_Service_Route_t*)tsip_message_get_headerAt(response, tsip_htype_Service_Route, index)); index++){
+ if(!TSIP_DIALOG_GET_STACK(self)->service_routes){
+ TSIP_DIALOG_GET_STACK(self)->service_routes = tsk_list_create();
+ }
+ uri = tsk_object_ref(hdr_Service_Route->uri);
+ tsk_list_push_back_data(TSIP_DIALOG_GET_STACK(self)->service_routes, (void**)&uri);
+ }
+
+ /* Paths */
+ for(index = 0; (hdr_Path = (const tsip_header_Path_t*)tsip_message_get_headerAt(response, tsip_htype_Path, index)); index++){
+ if(TSIP_DIALOG_GET_STACK(self)->paths == 0){
+ TSIP_DIALOG_GET_STACK(self)->paths = tsk_list_create();
+ }
+ uri = tsk_object_ref(hdr_Path->uri);
+ tsk_list_push_back_data(TSIP_DIALOG_GET_STACK(self)->paths, (void**)&uri);
+ }
+ }
+
+ /* 3GPP TS 24.229 - 5.1.1.2 Initial registration */
+ if(first_time_to_connect){
+ tsk_bool_t barred = tsk_true;
+ const tsk_list_item_t *item;
+ const tsip_uri_t *uri;
+ const tsip_uri_t *uri_first = 0;
+
+ /*
+ b) store as the default public user identity the first URI on the list of URIs present in the P-Associated-URI header
+ field and bind it to the respective contact address of the UE and the associated set of security associations or TLS
+ session;
+ NOTE 4: When using the respective contact address and associated set of security associations or TLS session, the
+ UE can utilize additional URIs contained in the P-Associated-URI header field and bound it to the
+ respective contact address of the UE and the associated set of security associations or TLS session, e.g. for
+ application purposes.
+ c) treat the identity under registration as a barred public user identity, if it is not included in the P-Associated-URI
+ header field;
+ */
+ tsk_list_foreach(item, TSIP_DIALOG_GET_STACK(self)->associated_uris){
+ uri = item->data;
+ if(item == TSIP_DIALOG_GET_STACK(self)->associated_uris->head){
+ uri_first = item->data;
+ }
+ if(!tsk_object_cmp(TSIP_DIALOG_GET_STACK(self)->identity.preferred, uri)){
+ barred = 0;
+ break;
+ }
+ }
+
+ if(barred && uri_first){
+ TSK_OBJECT_SAFE_FREE(TSIP_DIALOG_GET_STACK(self)->identity.preferred);
+ TSIP_DIALOG_GET_STACK(self)->identity.preferred = tsk_object_ref((void*)uri_first);
+ }
+ }
+
+ /* Update the dialog state */
+ if((ret = tsip_dialog_update(TSIP_DIALOG(self), response))){
+ return ret;
+ }
+
+ /* Reset current action */
+ tsip_dialog_set_curr_action(TSIP_DIALOG(self), tsk_null);
+
+ /* Request timeout for dialog refresh (re-registration). */
+ self->timerrefresh.timeout = tsip_dialog_get_newdelay(TSIP_DIALOG(self), response);
+ TSIP_DIALOG_REGISTER_TIMER_SCHEDULE(refresh);
+
+ /* Alert the user (session) */
+ TSIP_DIALOG_REGISTER_SIGNAL(self, tsip_ao_register,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+ /* Alert the user (dialog) */
+ if(first_time_to_connect){
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connected, "Dialog connected");
+ }
+
+ return ret;
+}
+
+/* InProgress -> (2xx) -> Terminated
+*/
+int tsip_dialog_register_InProgress_2_Terminated_X_2xx(va_list *app)
+{
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* save last error */
+ tsip_dialog_set_lasterror_2(TSIP_DIALOG(self), TSIP_RESPONSE_PHRASE(response), TSIP_RESPONSE_CODE(response), response);
+
+ /* Alert the user */
+ TSIP_DIALOG_REGISTER_SIGNAL(self, tsip_ao_unregister,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return 0;
+}
+
+/* InProgress --> (401/407/421/494) --> InProgress
+*/
+int tsip_dialog_register_InProgress_2_InProgress_X_401_407_421_494(va_list *app)
+{
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_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_REGISTER_SIGNAL(self, self->unregistering ? tsip_ao_unregister : tsip_ao_register,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ /* set last error (or info) */
+ tsip_dialog_set_lasterror(TSIP_DIALOG(self), "Authentication failed", TSIP_RESPONSE_CODE(response));
+
+ return ret;
+ }
+
+ /* Ensure IPSec SAs */
+ if(TSIP_DIALOG_GET_STACK(self)->security.secagree_mech && tsk_striequals(TSIP_DIALOG_GET_STACK(self)->security.secagree_mech, "ipsec-3gpp")){
+ tsip_transport_ensureTempSAs(TSIP_DIALOG_GET_STACK(self)->layer_transport, response, TSIP_DIALOG(self)->expires);
+ }
+
+ return tsip_dialog_register_send_REGISTER(self, tsk_false);
+}
+
+/* InProgress -> (423) -> InProgress
+*/
+int tsip_dialog_register_InProgress_2_InProgress_X_423(va_list *app)
+{
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ const tsip_message_t *message = va_arg(*app, const tsip_message_t *);
+
+ tsip_header_Min_Expires_t *hdr;
+ int ret = 0;
+
+ /*
+ RFC 3261 - 10.2.8 Error Responses
+
+ If a UA receives a 423 (Interval Too Brief) response, it MAY retry
+ the registration after making the expiration interval of all contact
+ addresses in the REGISTER request equal to or greater than the
+ expiration interval within the Min-Expires header field of the 423
+ (Interval Too Brief) response.
+ */
+ hdr = (tsip_header_Min_Expires_t*)tsip_message_get_header(message, tsip_htype_Min_Expires);
+ if(hdr){
+ TSIP_DIALOG(self)->expires = TSK_TIME_S_2_MS(hdr->value);
+
+ if(tsk_striequals(TSIP_DIALOG_GET_STACK(self)->security.secagree_mech, "ipsec-3gpp")){
+ tsip_transport_cleanupSAs(TSIP_DIALOG_GET_STACK(self)->layer_transport);
+ ret = tsip_dialog_register_send_REGISTER(self, tsk_true);
+ }
+ else{
+ ret = tsip_dialog_register_send_REGISTER(self, tsk_false);
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Missing header: Min_Expires");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/* InProgress -> (300-699) -> Terminated
+*/
+int tsip_dialog_register_InProgress_2_Terminated_X_300_to_699(va_list *app)
+{
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* save last error */
+ tsip_dialog_set_lasterror_2(TSIP_DIALOG(self), TSIP_RESPONSE_PHRASE(response), TSIP_RESPONSE_CODE(response), response);
+
+ /* Alert the user. */
+ TSIP_DIALOG_REGISTER_SIGNAL(self, self->unregistering ? tsip_ao_unregister : tsip_ao_register,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return 0;
+}
+
+/* InProgress -> (cancel) -> Terminated
+*/
+int tsip_dialog_register_InProgress_2_Terminated_X_cancel(va_list *app)
+{
+ int ret;
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ /* const tsip_message_t *message = va_arg(*app, const tsip_message_t *); */
+
+ /* Cancel all transactions associated to this dialog (will also be done when the dialog is destroyed (worth nothing)) */
+ ret = tsip_transac_layer_cancel_by_dialog(TSIP_DIALOG_GET_STACK(self)->layer_transac, TSIP_DIALOG(self));
+
+ /* RFC 3261 - 9.1 Client Behavior
+ A CANCEL request SHOULD NOT be sent to cancel a request other than INVITE.
+ */
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_request_cancelled, "Registration cancelled");
+
+ return ret;
+}
+
+/* Connected -> (REGISTER) -> InProgress
+*/
+int tsip_dialog_register_Connected_2_InProgress_X_oRegister(va_list *app)
+{
+ tsip_dialog_register_t *self;
+
+ self = va_arg(*app, tsip_dialog_register_t *);
+
+ return tsip_dialog_register_send_REGISTER(self, tsk_true);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tinySIP/src/dialogs/tsip_dialog_register.server.c b/tinySIP/src/dialogs/tsip_dialog_register.server.c
new file mode 100644
index 0000000..de912ff
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_register.server.c
@@ -0,0 +1,237 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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.
+*
+*/
+#include "tinysip/dialogs/tsip_dialog_register.h"
+#include "tinysip/dialogs/tsip_dialog_register.common.h"
+
+
+/* ======================== external functions ======================== */
+extern int tsip_dialog_register_send_RESPONSE(tsip_dialog_register_t *self, const tsip_request_t* request, short code, const char* phrase);
+
+/* ======================== transitions ======================== */
+static int s0000_Started_2_Terminated_X_iREGISTER(va_list *app);
+static int s0000_Started_2_Incoming_X_iREGISTER(va_list *app);
+static int s0000_Incoming_2_Connected_X_Accept(va_list *app);
+static int s0000_Incoming_2_Terminated_X_Terminates(va_list *app);
+static int s0000_Connected_2_Connected_X_iREGISTER(va_list *app);
+static int s0000_Connected_2_Terminated_X_iREGISTER(va_list *app);
+
+
+/* ======================== conds ======================== */
+static tsk_bool_t _fsm_cond_not_served_here(tsip_dialog_register_t* dialog, tsip_message_t* message)
+{
+#if 0 // FIXME: Have to disabled only when in proxy mode
+ if(message && TSIP_REQUEST_IS_REGISTER(message)){
+ if(tsk_object_cmp(TSIP_DIALOG_GET_STACK(dialog)->network.realm, message->line.request.uri) != 0){
+ tsip_dialog_register_send_RESPONSE(dialog, TSIP_MESSAGE_AS_REQUEST(message), 404, "Domain not served here");
+ return tsk_true;
+ }
+ }
+#endif
+ return tsk_false;
+}
+static tsk_bool_t _fsm_cond_server_unregistering(tsip_dialog_register_t* dialog, tsip_message_t* message)
+{
+ if(message && dialog->is_server){
+ int64_t expires = tsip_message_getExpires(message);
+ dialog->unregistering = (expires == 0);
+ return dialog->unregistering;
+ }
+ return tsk_false;
+}
+static tsk_bool_t _fsm_cond_server_registering(tsip_dialog_register_t* dialog, tsip_message_t* message)
+{
+ return !_fsm_cond_server_unregistering(dialog, message);
+}
+
+
+int tsip_dialog_register_server_init(tsip_dialog_register_t *self)
+{
+ return tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Started ===
+ */
+ // Started -> (Domain Not Served here) -> Terminated
+ TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iREGISTER, _fsm_cond_not_served_here, _fsm_state_Terminated, s0000_Started_2_Terminated_X_iREGISTER, "s0000_Started_2_Terminated_X_iREGISTER"),
+ // Started -> (All is OK and we are not unRegistering) -> Trying
+ TSK_FSM_ADD(_fsm_state_Started, _fsm_action_iREGISTER, _fsm_cond_server_registering, _fsm_state_Incoming, s0000_Started_2_Incoming_X_iREGISTER, "s0000_Started_2_Incoming_X_iREGISTER"),
+
+ /*=======================
+ * === Incoming ===
+ */
+ // Incoming -> (Accept) -> Connected
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Incoming, _fsm_action_accept, _fsm_state_Connected, s0000_Incoming_2_Connected_X_Accept, "s0000_Incoming_2_Connected_X_Accept"),
+ // Incoming -> (iRegister) -> Incoming
+ TSK_FSM_ADD(_fsm_state_Incoming, _fsm_action_iREGISTER, _fsm_cond_server_registering, _fsm_state_Incoming, tsk_null, "s0000_Incoming_2_Incoming_X_iREGISTER"),
+ // Incoming -> (iRegister, expires=0) -> Terminated
+ TSK_FSM_ADD(_fsm_state_Incoming, _fsm_action_iREGISTER, _fsm_cond_server_unregistering, _fsm_state_Terminated, tsk_null, "s0000_Incoming_2_Terminated_X_iREGISTER"),
+ // Incoming -> (Reject) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Incoming, _fsm_action_reject, _fsm_state_Terminated, s0000_Incoming_2_Terminated_X_Terminates, "s0000_Incoming_2_Terminated_X_Terminates"),
+ // Incoming -> (Hangup) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Incoming, _fsm_action_hangup, _fsm_state_Terminated, s0000_Incoming_2_Terminated_X_Terminates, "s0000_Incoming_2_Terminated_X_Terminates"),
+
+ /*=======================
+ * === Connected ===
+ */
+ // Connected -> (Register) -> Connected
+ TSK_FSM_ADD(_fsm_state_Connected, _fsm_action_iREGISTER, _fsm_cond_server_registering, _fsm_state_Connected, s0000_Connected_2_Connected_X_iREGISTER, "s0000_Connected_2_Connected_X_iREGISTER"),
+ // Connected -> (UnRegister) -> Terminated
+ TSK_FSM_ADD(_fsm_state_Connected, _fsm_action_iREGISTER, _fsm_cond_server_unregistering, _fsm_state_Terminated, s0000_Connected_2_Terminated_X_iREGISTER, "s0000_Connected_2_Terminated_X_iREGISTER"),
+ // Connected -> (TimedOut) -> Terminated
+ // Connected -> (Refresh OK) -> Connected
+ // Connected -> (Refresh NOK) -> Terminated
+
+ TSK_FSM_ADD_NULL());
+}
+
+
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+/* Started -> (Failure) -> Terminated
+*/
+int s0000_Started_2_Terminated_X_iREGISTER(va_list *app)
+{
+ return 0;
+ /*tsip_dialog_register_t *self;
+ const tsip_action_t* action;
+
+ self = va_arg(*app, tsip_dialog_register_t *);
+ va_arg(*app, const tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ TSIP_DIALOG(self)->running = tsk_true;
+ tsip_dialog_set_curr_action(TSIP_DIALOG(self), action);
+
+ // alert the user
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");
+
+ return send_REGISTER(self, tsk_true);*/
+}
+
+/* Started -> (All is OK and we are Registering) -> Incoming
+*/
+int s0000_Started_2_Incoming_X_iREGISTER(va_list *app)
+{
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ tsip_request_t *request = va_arg(*app, tsip_request_t *);
+
+ // set as server side dialog
+ TSIP_DIALOG_REGISTER(self)->is_server = tsk_true;
+
+ /* update last REGISTER */
+ TSK_OBJECT_SAFE_FREE(self->last_iRegister);
+ self->last_iRegister = tsk_object_ref(request);
+
+ /* alert the user (session) */
+ TSIP_DIALOG_REGISTER_SIGNAL(self, tsip_i_newreg,
+ tsip_event_code_dialog_request_incoming, "Incoming New Register", request);
+
+ return 0;
+}
+
+/* Incoming -> (Accept) -> Connected
+*/
+int s0000_Incoming_2_Connected_X_Accept(va_list *app)
+{
+ int ret;
+
+ tsip_dialog_register_t *self;
+
+ self = va_arg(*app, tsip_dialog_register_t *);
+
+ /* send 2xx OK */
+ if((ret = tsip_dialog_register_send_RESPONSE(self, self->last_iRegister, 200, "OK"))){
+ return ret;
+ }
+
+ /* alert the user (dialog) */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connected, "Dialog connected");
+
+ return ret;
+}
+
+/* Incoming -> (Reject) -> Terminated
+*/
+int s0000_Incoming_2_Terminated_X_Terminates(va_list *app)
+{
+ int ret;
+ short code;
+ const char* phrase;
+ char* reason = tsk_null;
+
+ tsip_dialog_register_t *self;
+ const tsip_action_t* action;
+
+ self = va_arg(*app, tsip_dialog_register_t *);
+ va_arg(*app, const tsip_message_t *);
+ action = va_arg(*app, const tsip_action_t *);
+
+ /* Send Reject */
+ code = action->line_resp.code>=300 ? action->line_resp.code : 600;
+ phrase = action->line_resp.phrase ? action->line_resp.phrase : "Not Supported";
+ tsk_sprintf(&reason, "SIP; cause=%hi; text=\"%s\"", code, phrase);
+ ret = tsip_dialog_register_send_RESPONSE(self, self->last_iRegister, code, phrase);
+ TSK_FREE(reason);
+
+ return ret;
+}
+
+/* Connected -> (register) -> Connected
+*/
+static int s0000_Connected_2_Connected_X_iREGISTER(va_list *app)
+{
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ const tsip_request_t *request = va_arg(*app, const tsip_request_t *);
+
+ TSK_OBJECT_SAFE_FREE(self->last_iRegister);
+ self->last_iRegister = tsk_object_ref((tsk_object_t*)request);
+
+ /* send 2xx OK */
+ return tsip_dialog_register_send_RESPONSE(self, self->last_iRegister, 200, "OK");
+}
+
+/* Connected -> (Unregister) -> Terminated
+*/
+int s0000_Connected_2_Terminated_X_iREGISTER(va_list *app)
+{
+ int ret;
+ tsip_dialog_register_t *self = va_arg(*app, tsip_dialog_register_t *);
+ tsip_request_t *request = va_arg(*app, tsip_request_t *);
+
+ /* update last REGISTER */
+ TSK_OBJECT_SAFE_FREE(self->last_iRegister);
+ self->last_iRegister = tsk_object_ref(request);
+
+ /* send 2xx OK */
+ if((ret = tsip_dialog_register_send_RESPONSE(self, self->last_iRegister, 200, "OK"))){
+ return ret;
+ }
+
+ /* alert the user (session) */
+ TSIP_DIALOG_REGISTER_SIGNAL(self, tsip_i_unregister,
+ tsip_event_code_dialog_request_incoming, "Incoming Request", request);
+
+ return 0;
+} \ No newline at end of file
diff --git a/tinySIP/src/dialogs/tsip_dialog_subscribe.client.c b/tinySIP/src/dialogs/tsip_dialog_subscribe.client.c
new file mode 100644
index 0000000..28f34e4
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_subscribe.client.c
@@ -0,0 +1,764 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_subscribe.client.c
+ * @brief SIP dialog SUBSCRIBE (Client side) as per RFC 3265.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/dialogs/tsip_dialog_subscribe.h"
+
+#include "tinysip/headers/tsip_header_Dummy.h"
+#include "tinysip/headers/tsip_header_Event.h"
+#include "tinysip/headers/tsip_header_Min_Expires.h"
+#include "tinysip/headers/tsip_header_Subscription_State.h"
+
+#include "tinysip/transactions/tsip_transac_layer.h"
+
+#include "tinysip/api/tsip_api_subscribe.h"
+
+#include "tsk_debug.h"
+#include "tsk_time.h"
+
+
+
+#define DEBUG_STATE_MACHINE 0
+#define TSIP_DIALOG_SUBSCRIBE_TIMER_SCHEDULE(TX) TSIP_DIALOG_TIMER_SCHEDULE(subscribe, TX)
+#define TSIP_DIALOG_SUBSCRIBE_SIGNAL(self, type, code, phrase, message) \
+ tsip_subscribe_event_signal(type, TSIP_DIALOG(self)->ss, code, phrase, message)
+
+/* ======================== internal functions ======================== */
+static int process_i_notify(tsip_dialog_subscribe_t *self, const tsip_request_t* notify);
+static int send_SUBSCRIBE(tsip_dialog_subscribe_t *self);
+static int send_200NOTIFY(tsip_dialog_subscribe_t *self, const tsip_request_t* request);
+static int tsip_dialog_subscribe_OnTerminated(tsip_dialog_subscribe_t *self);
+
+/* ======================== transitions ======================== */
+static int tsip_dialog_subscribe_Started_2_Trying_X_subscribe(va_list *app);
+static int tsip_dialog_subscribe_Trying_2_Trying_X_1xx(va_list *app);
+static int tsip_dialog_subscribe_Trying_2_Terminated_X_2xx(va_list *app);
+static int tsip_dialog_subscribe_Trying_2_Connected_X_2xx(va_list *app);
+static int tsip_dialog_subscribe_Trying_2_Trying_X_401_407_421_494(va_list *app);
+static int tsip_dialog_subscribe_Trying_2_Trying_X_423(va_list *app);
+static int tsip_dialog_subscribe_Trying_2_Terminated_X_300_to_699(va_list *app);
+static int tsip_dialog_subscribe_Trying_2_Terminated_X_cancel(va_list *app);
+static int tsip_dialog_subscribe_Trying_2_Trying_X_NOTIFY(va_list *app);
+static int tsip_dialog_subscribe_Connected_2_Trying_X_unsubscribe(va_list *app);
+static int tsip_dialog_subscribe_Connected_2_Trying_X_subscribe(va_list *app);
+static int tsip_dialog_subscribe_Connected_2_Connected_X_NOTIFY(va_list *app);
+static int tsip_dialog_subscribe_Connected_2_Terminated_X_NOTIFY(va_list *app);
+static int tsip_dialog_subscribe_Any_2_Trying_X_hangup(va_list *app);
+static int tsip_dialog_subscribe_Any_2_Trying_X_shutdown(va_list *app);
+static int tsip_dialog_subscribe_Any_2_Terminated_X_transportError(va_list *app);
+static int tsip_dialog_subscribe_Any_2_Terminated_X_Error(va_list *app);
+
+
+/* ======================== conds ======================== */
+static tsk_bool_t _fsm_cond_unsubscribing(tsip_dialog_subscribe_t* dialog, tsip_message_t* message)
+{
+ return dialog->unsubscribing;
+}
+static tsk_bool_t _fsm_cond_subscribing(tsip_dialog_subscribe_t* dialog, tsip_message_t* message)
+{
+ return !_fsm_cond_unsubscribing(dialog, message);
+}
+
+static tsk_bool_t _fsm_cond_notify_terminated(tsip_dialog_subscribe_t* dialog, tsip_message_t* message)
+{
+ const tsip_header_Subscription_State_t *hdr_state;
+ if((hdr_state = (const tsip_header_Subscription_State_t*)tsip_message_get_header(message, tsip_htype_Subscription_State)))
+ {
+ return tsk_striequals(hdr_state->state, "terminated") &&
+ (hdr_state->expires < 0 || tsk_striequals(hdr_state->reason, "rejected") || tsk_striequals(hdr_state->reason, "noresource"));
+ }
+ return tsk_false;
+}
+static tsk_bool_t _fsm_cond_notify_not_terminated(tsip_dialog_subscribe_t* dialog, tsip_message_t* message)
+{
+ return !_fsm_cond_notify_terminated(dialog, message);
+}
+
+static tsk_bool_t _fsm_cond_silent_hangup(tsip_dialog_subscribe_t* dialog, tsip_message_t* message)
+{
+ return TSIP_DIALOG(dialog)->ss->silent_hangup;
+}
+static tsk_bool_t _fsm_cond_not_silent_hangup(tsip_dialog_subscribe_t* dialog, tsip_message_t* message)
+{
+ return !TSIP_DIALOG(dialog)->ss->silent_hangup;
+}
+#define _fsm_cond_silent_shutdown _fsm_cond_silent_hangup
+#define _fsm_cond_not_silent_shutdown _fsm_cond_not_silent_hangup
+
+/* ======================== actions ======================== */
+typedef enum _fsm_action_e
+{
+ _fsm_action_subscribe = tsip_atype_subscribe,
+ _fsm_action_hangup = tsip_atype_hangup,
+ _fsm_action_cancel = tsip_atype_cancel,
+ _fsm_action_shutdown = tsip_atype_shutdown,
+ _fsm_action_transporterror = tsip_atype_transport_error,
+
+ _fsm_action_1xx = 0xFF,
+ _fsm_action_2xx,
+ _fsm_action_401_407_421_494,
+ _fsm_action_423,
+ _fsm_action_300_to_699,
+ _fsm_action_shutdown_timedout, /* Any -> Terminated */
+ _fsm_action_notify,
+ _fsm_action_error,
+}
+_fsm_action_t;
+
+/* ======================== states ======================== */
+typedef enum _fsm_state_e
+{
+ _fsm_state_Started,
+ _fsm_state_Trying,
+ _fsm_state_Connected,
+ _fsm_state_Terminated
+}
+_fsm_state_t;
+
+/**
+ * Callback function called to alert the dialog for new events from the transaction/transport layers.
+ *
+ * @param [in,out] self A reference to the dialog.
+ * @param type The event type.
+ * @param [in,out] msg The incoming SIP/IMS message.
+ *
+ * @return Zero if succeed and non-zero error code otherwise.
+**/
+int tsip_dialog_subscribe_event_callback(const tsip_dialog_subscribe_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 && 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)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_1xx, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS_2XX(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_2xx, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS(msg,401) || TSIP_RESPONSE_IS(msg,407) || TSIP_RESPONSE_IS(msg,421) || TSIP_RESPONSE_IS(msg,494)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_401_407_421_494, msg, action);
+ }
+ else if(TSIP_RESPONSE_IS(msg,423)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_423, msg, action);
+ }
+ else{
+ // Alert User
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_error, msg, action);
+ /* TSK_DEBUG_WARN("Not supported status code: %d", TSIP_RESPONSE_CODE(msg)); */
+ }
+ }
+ else{
+ //
+ // REQUEST
+ //
+ if(TSIP_REQUEST_IS_NOTIFY(msg)){
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_notify, msg, tsk_null);
+ }
+ }
+ break;
+ }
+
+ case tsip_dialog_canceled:
+ {
+ ret = tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_cancel, msg, tsk_null);
+ break;
+ }
+
+ case tsip_dialog_terminated:
+ case tsip_dialog_timedout:
+ 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 [in,out] 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_subscribe_timer_callback(const tsip_dialog_subscribe_t* self, tsk_timer_id_t timer_id)
+{
+ int ret = -1;
+
+ if(self)
+ {
+ if(timer_id == self->timerrefresh.id){
+ tsip_dialog_fsm_act(TSIP_DIALOG(self), _fsm_action_subscribe, tsk_null, tsk_null);
+ ret = 0;
+ }
+ 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_subscribe_t* tsip_dialog_subscribe_create(const tsip_ssession_handle_t* ss)
+{
+ return tsk_object_new(tsip_dialog_subscribe_def_t, ss);
+}
+
+/** Initializes the dialog.
+ *
+ * @param [in,out] self The dialog to initialize.
+**/
+int tsip_dialog_subscribe_init(tsip_dialog_subscribe_t *self)
+{
+ /* Initialize the State Machine. */
+ tsk_fsm_set(TSIP_DIALOG_GET_FSM(self),
+
+ /*=======================
+ * === Started ===
+ */
+ // Started -> (Send) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_subscribe, _fsm_state_Trying, tsip_dialog_subscribe_Started_2_Trying_X_subscribe, "tsip_dialog_subscribe_Started_2_Trying_X_subscribe"),
+ // Started -> (Any) -> Started
+ TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_dialog_subscribe_Started_2_Started_X_any"),
+
+
+ /*=======================
+ * === Trying ===
+ */
+ // Trying -> (1xx) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_1xx, _fsm_state_Trying, tsip_dialog_subscribe_Trying_2_Trying_X_1xx, "tsip_dialog_subscribe_Trying_2_Trying_X_1xx"),
+ // Trying -> (2xx) -> Terminated
+ TSK_FSM_ADD(_fsm_state_Trying, _fsm_action_2xx, _fsm_cond_unsubscribing, _fsm_state_Terminated, tsip_dialog_subscribe_Trying_2_Terminated_X_2xx, "tsip_dialog_subscribe_Trying_2_Terminated_X_2xx"),
+ // Trying -> (2xx) -> Connected
+ TSK_FSM_ADD(_fsm_state_Trying, _fsm_action_2xx, _fsm_cond_subscribing, _fsm_state_Connected, tsip_dialog_subscribe_Trying_2_Connected_X_2xx, "tsip_dialog_subscribe_Trying_2_Connected_X_2xx"),
+ // Trying -> (401/407/421/494) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_401_407_421_494, _fsm_state_Trying, tsip_dialog_subscribe_Trying_2_Trying_X_401_407_421_494, "tsip_dialog_subscribe_Trying_2_Trying_X_401_407_421_494"),
+ // Trying -> (423) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_423, _fsm_state_Trying, tsip_dialog_subscribe_Trying_2_Trying_X_423, "tsip_dialog_subscribe_Trying_2_Trying_X_423"),
+ // Trying -> (300_to_699) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_300_to_699, _fsm_state_Terminated, tsip_dialog_subscribe_Trying_2_Terminated_X_300_to_699, "tsip_dialog_subscribe_Trying_2_Terminated_X_300_to_699"),
+ // Trying -> (cancel) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_cancel, _fsm_state_Terminated, tsip_dialog_subscribe_Trying_2_Terminated_X_cancel, "tsip_dialog_subscribe_Trying_2_Terminated_X_cancel"),
+ // Trying -> (Notify) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_notify, _fsm_state_Trying, tsip_dialog_subscribe_Trying_2_Trying_X_NOTIFY, "tsip_dialog_subscribe_Trying_2_Trying_X_NOTIFY"),
+ // Trying -> (hangup) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_hangup, _fsm_state_Terminated, tsk_null, "tsip_dialog_subscribe_Trying_2_Terminated_X_hangup"),
+ // Trying -> (shutdown) -> Terminated
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_shutdown, _fsm_state_Terminated, tsk_null, "tsip_dialog_subscribe_Trying_2_Terminated_X_shutdown"),
+ // Trying -> (Any) -> Trying
+ //TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Trying, "tsip_dialog_subscribe_Trying_2_Trying_X_any"),
+
+
+ /*=======================
+ * === Connected ===
+ */
+ // Connected -> (SUBSCRIBE) -> Trying
+ TSK_FSM_ADD_ALWAYS(_fsm_state_Connected, _fsm_action_subscribe, _fsm_state_Trying, tsip_dialog_subscribe_Connected_2_Trying_X_subscribe, "tsip_dialog_subscribe_Connected_2_Trying_X_subscribe"),
+ // Connected -> (NOTIFY) -> Connected
+ TSK_FSM_ADD(_fsm_state_Connected, _fsm_action_notify, _fsm_cond_notify_not_terminated, _fsm_state_Connected, tsip_dialog_subscribe_Connected_2_Connected_X_NOTIFY, "tsip_dialog_subscribe_Connected_2_Connected_X_NOTIFY"),
+ // Connected -> (NOTIFY) -> Terminated
+ TSK_FSM_ADD(_fsm_state_Connected, _fsm_action_notify, _fsm_cond_notify_terminated, _fsm_state_Terminated, tsip_dialog_subscribe_Connected_2_Terminated_X_NOTIFY, "tsip_dialog_subscribe_Connected_2_Terminated_X_NOTIFY"),
+
+ /*=======================
+ * === Any ===
+ */
+ // Any -> (hangup) -> Trying
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_hangup, _fsm_cond_not_silent_hangup, _fsm_state_Trying, tsip_dialog_subscribe_Any_2_Trying_X_hangup, "tsip_dialog_subscribe_Any_2_Trying_X_hangup"),
+ // Any -> (silenthangup) -> Terminated
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_hangup, _fsm_cond_silent_hangup, _fsm_state_Terminated, tsk_null, "tsip_dialog_subscribe_Any_2_Trying_X_silenthangup"),
+ // Any -> (shutdown) -> Trying
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_shutdown, _fsm_cond_not_silent_shutdown, _fsm_state_Trying, tsip_dialog_subscribe_Any_2_Trying_X_shutdown, "tsip_dialog_subscribe_Any_2_Trying_X_shutdown"),
+ // Any -> (silentshutdown) -> Terminated
+ TSK_FSM_ADD(tsk_fsm_state_any, _fsm_action_shutdown, _fsm_cond_silent_shutdown, _fsm_state_Terminated, tsk_null, "tsip_dialog_subscribe_Any_2_Trying_X_silentshutdown"),
+ // Any -> (shutdown timedout) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_shutdown_timedout, _fsm_state_Terminated, tsk_null, "tsip_dialog_subscribe_shutdown_timedout"),
+ // Any -> (transport error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, tsip_dialog_subscribe_Any_2_Terminated_X_transportError, "tsip_dialog_subscribe_Any_2_Terminated_X_transportError"),
+ // Any -> (error) -> Terminated
+ TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_error, _fsm_state_Terminated, tsip_dialog_subscribe_Any_2_Terminated_X_Error, "tsip_dialog_subscribe_Any_2_Terminated_X_Error"),
+
+ TSK_FSM_ADD_NULL());
+
+ /* Sets callback function */
+ TSIP_DIALOG(self)->callback = TSIP_DIALOG_EVENT_CALLBACK_F(tsip_dialog_subscribe_event_callback);
+
+ /* Timers */
+ self->timerrefresh.id = TSK_INVALID_TIMER_ID;
+ self->timerrefresh.timeout = TSIP_DIALOG(self)->expires;
+ self->timershutdown.id = TSK_INVALID_TIMER_ID;
+ self->timershutdown.timeout = TSIP_DIALOG_SHUTDOWN_TIMEOUT;
+
+ return 0;
+}
+
+
+//--------------------------------------------------------
+// == STATE MACHINE BEGIN ==
+//--------------------------------------------------------
+
+/* Started -> (SUBSCRIBE) -> Trying
+*/
+int tsip_dialog_subscribe_Started_2_Trying_X_subscribe(va_list *app)
+{
+ tsip_dialog_subscribe_t *self;
+
+ self = va_arg(*app, tsip_dialog_subscribe_t *);
+
+ TSIP_DIALOG(self)->running = tsk_true;
+
+ /* alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting");
+
+ return send_SUBSCRIBE(self);
+}
+
+/* Trying -> (1xx) -> Trying
+*/
+int tsip_dialog_subscribe_Trying_2_Trying_X_1xx(va_list *app)
+{
+ /*tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);*/
+ /*const tsip_response_t *response = va_arg(*app, const tsip_response_t *);*/
+
+ return 0;
+}
+
+/* Trying -> (2xx) -> Terminated
+*/
+int tsip_dialog_subscribe_Trying_2_Terminated_X_2xx(va_list *app)
+{
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* Alert the user. */
+ TSIP_DIALOG_SUBSCRIBE_SIGNAL(self, self->unsubscribing ? tsip_ao_unsubscribe : tsip_ao_subscribe,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return 0;
+}
+
+/* Trying -> (2xx) -> Connected
+*/
+int tsip_dialog_subscribe_Trying_2_Connected_X_2xx(va_list *app)
+{
+ int ret;
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ tsk_bool_t first_time_to_connect = (TSIP_DIALOG(self)->state == tsip_initial);
+
+ /* Update the dialog state. */
+ if((ret = tsip_dialog_update(TSIP_DIALOG(self), response))){
+ return ret;
+ }
+
+ /* Alert the user(session) */
+ TSIP_DIALOG_SUBSCRIBE_SIGNAL(self, self->unsubscribing ? tsip_ao_unsubscribe : tsip_ao_subscribe,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+ /* Alert the user(dialog) */
+ if(first_time_to_connect){
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connected, "Dialog connected");
+ }
+
+ /* Reset current action */
+ tsip_dialog_set_curr_action(TSIP_DIALOG(self), tsk_null);
+
+ /* Request timeout for dialog refresh (re-subscribtion). */
+ self->timerrefresh.timeout = tsip_dialog_get_newdelay(TSIP_DIALOG(self), response);
+ TSIP_DIALOG_SUBSCRIBE_TIMER_SCHEDULE(refresh);
+
+ return 0;
+}
+
+/* Trying -> (401/407/421/494) -> Trying
+*/
+int tsip_dialog_subscribe_Trying_2_Trying_X_401_407_421_494(va_list *app)
+{
+ int ret;
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ if((ret = tsip_dialog_update(TSIP_DIALOG(self), response))){
+ /* Alert the user. */
+ TSIP_DIALOG_SUBSCRIBE_SIGNAL(self, self->unsubscribing ? tsip_ao_unsubscribe : tsip_ao_subscribe,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return ret;
+ }
+
+ return send_SUBSCRIBE(self);
+}
+
+/* Trying -> (423) -> Trying
+*/
+int tsip_dialog_subscribe_Trying_2_Trying_X_423(va_list *app)
+{
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ tsip_header_Min_Expires_t *hdr;
+
+ /*
+ RFC 3261 - 10.2.8 Error Responses
+
+ If a UA receives a 423 (Interval Too Brief) response, it MAY retry
+ the registration after making the expiration interval of all contact
+ addresses in the SUBSCRIBE request equal to or greater than the
+ expiration interval within the Min-Expires header field of the 423
+ (Interval Too Brief) response.
+ */
+ hdr = (tsip_header_Min_Expires_t*)tsip_message_get_header(response, tsip_htype_Min_Expires);
+ if(hdr){
+ TSIP_DIALOG(self)->expires = TSK_TIME_S_2_MS(hdr->value);
+ send_SUBSCRIBE(self);
+ }
+ else{
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_message_error, "Received invalid SIP response.");
+
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Trying -> (300-699) -> Terminated
+*/
+int tsip_dialog_subscribe_Trying_2_Terminated_X_300_to_699(va_list *app)
+{
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ /* save last error */
+ tsip_dialog_set_lasterror_2(TSIP_DIALOG(self), TSIP_RESPONSE_PHRASE(response), TSIP_RESPONSE_CODE(response), response);
+
+ /* alert the user */
+ TSIP_DIALOG_SUBSCRIBE_SIGNAL(self, self->unsubscribing ? tsip_ao_unsubscribe : tsip_ao_subscribe,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+
+ return 0;
+}
+
+/* Trying -> (cancel) -> Terminated
+*/
+int tsip_dialog_subscribe_Trying_2_Terminated_X_cancel(va_list *app)
+{
+ int ret;
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ /* const tsip_response_t *response = va_arg(*app, const tsip_response_t *); */
+
+ /* Cancel all transactions associated to this dialog (will also be done when the dialog is destroyed (worth nothing)) */
+ ret = tsip_transac_layer_cancel_by_dialog(TSIP_DIALOG_GET_STACK(self)->layer_transac, TSIP_DIALOG(self));
+
+ /* RFC 3261 - 9.1 Client Behavior
+ A CANCEL request SHOULD NOT be sent to cancel a request other than INVITE.
+ */
+
+ /* alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_request_cancelled, "Subscription cancelled");
+
+ return ret;
+}
+
+/* Trying -> (NOTIFY) -> Trying
+*/
+int tsip_dialog_subscribe_Trying_2_Trying_X_NOTIFY(va_list *app)
+{
+ int ret;
+
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ const tsip_request_t *request = va_arg(*app, const tsip_request_t *);
+
+ ret = send_200NOTIFY(self, request);
+ ret = process_i_notify(self, request);
+
+ return ret;
+}
+
+/* Connected -> (SUBSCRIBE) -> Trying
+*/
+int tsip_dialog_subscribe_Connected_2_Trying_X_subscribe(va_list *app)
+{
+ tsip_dialog_subscribe_t *self;
+
+ self = va_arg(*app, tsip_dialog_subscribe_t *);
+
+ return send_SUBSCRIBE(self);
+}
+
+/* Connected -> (NOTIFY) -> Connected
+*/
+int tsip_dialog_subscribe_Connected_2_Connected_X_NOTIFY(va_list *app)
+{
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ const tsip_request_t *request = va_arg(*app, const tsip_request_t *);
+ int ret;
+
+ ret = send_200NOTIFY(self, request);
+ ret = process_i_notify(self, request);
+
+ return ret;
+}
+
+/* Connected -> (NOTIFY) -> Terminated
+*/
+int tsip_dialog_subscribe_Connected_2_Terminated_X_NOTIFY(va_list *app)
+{
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ const tsip_request_t *request = va_arg(*app, const tsip_request_t *);
+
+ /* Alert the user */
+ TSIP_DIALOG_SUBSCRIBE_SIGNAL(self, tsip_i_notify,
+ tsip_event_code_dialog_request_incoming, "Incoming NOTIFY.", request);
+
+ return send_200NOTIFY(self, request);
+}
+
+/* Any -> (hangup) -> Trying
+*/
+int tsip_dialog_subscribe_Any_2_Trying_X_hangup(va_list *app)
+{
+ tsip_dialog_subscribe_t *self;
+
+ self = va_arg(*app, tsip_dialog_subscribe_t *);
+
+ /* Alert the user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog");
+
+ self->unsubscribing = tsk_true;
+ return send_SUBSCRIBE(self);
+}
+
+/* Any -> (shutdown) -> Trying
+*/
+int tsip_dialog_subscribe_Any_2_Trying_X_shutdown(va_list *app)
+{
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+
+ /* schedule shutdow timeout */
+ TSIP_DIALOG_SUBSCRIBE_TIMER_SCHEDULE(shutdown);
+
+ /* alert user */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_terminating, "Terminating dialog");
+
+ self->unsubscribing = tsk_true;
+ return send_SUBSCRIBE(self);
+}
+
+/* Any -> (transport error) -> Terminated
+*/
+int tsip_dialog_subscribe_Any_2_Terminated_X_transportError(va_list *app)
+{
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ /* const tsip_response_t *response = va_arg(*app, const tsip_response_t *); */
+
+ /* Alert the user. */
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_transport_error, "Transport error.");
+
+ return 0;
+}
+
+/* Any -> (error) -> Terminated
+*/
+int tsip_dialog_subscribe_Any_2_Terminated_X_Error(va_list *app)
+{
+ tsip_dialog_subscribe_t *self = va_arg(*app, tsip_dialog_subscribe_t *);
+ const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
+
+ // save last error
+ tsip_dialog_set_lasterror_2(TSIP_DIALOG(self), TSIP_RESPONSE_PHRASE(response), TSIP_RESPONSE_CODE(response), response);
+
+ /* Alert user */
+ if(response){
+ TSIP_DIALOG_SUBSCRIBE_SIGNAL(self, self->unsubscribing ? tsip_ao_unsubscribe : tsip_ao_subscribe,
+ TSIP_RESPONSE_CODE(response), TSIP_RESPONSE_PHRASE(response), response);
+ }
+ else{
+ TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_global_error, "Global error.");
+ }
+
+ return 0;
+}
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// == STATE MACHINE END ==
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+/**
+ * Sends a SUBSCRIBE request.
+ *
+ * @param [in,out] self The caller.
+ *
+ * @return Zero if succeed and non-zero error code otherwise.
+**/
+int send_SUBSCRIBE(tsip_dialog_subscribe_t *self)
+{
+ tsip_request_t *request;
+ int ret = -1;
+
+ if(self->unsubscribing){
+ TSIP_DIALOG(self)->expires = 0;
+ }
+
+ if((request = tsip_dialog_request_new(TSIP_DIALOG(self), "SUBSCRIBE"))){
+ /* apply action params to the request */
+ if(TSIP_DIALOG(self)->curr_action){
+ tsip_dialog_apply_action(request, TSIP_DIALOG(self)->curr_action);
+ }
+ /* send the request */
+ ret = tsip_dialog_request_send(TSIP_DIALOG(self), request);
+ TSK_OBJECT_SAFE_FREE(request);
+ }
+
+ return ret;
+}
+
+int send_200NOTIFY(tsip_dialog_subscribe_t *self, const tsip_request_t* request)
+{
+ tsip_response_t *response;
+ int ret = -1;
+
+ if((response = tsip_dialog_response_new(TSIP_DIALOG(self), 200, "OK", request))){
+ ret = tsip_dialog_response_send(TSIP_DIALOG(self), response);
+ TSK_OBJECT_SAFE_FREE(response);
+ }
+ return ret;
+}
+
+// process incoming notify: refresh delay and alert the user
+int process_i_notify(tsip_dialog_subscribe_t *self, const tsip_request_t* notify)
+{
+ if(!self || !notify){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* Request timeout for dialog refresh (re-registration). */
+ self->timerrefresh.timeout = tsip_dialog_get_newdelay(TSIP_DIALOG(self), notify);
+ TSIP_DIALOG_SUBSCRIBE_TIMER_SCHEDULE(refresh);
+
+ /* Alert the user */
+ TSIP_DIALOG_SUBSCRIBE_SIGNAL(self, tsip_i_notify,
+ tsip_event_code_dialog_request_incoming, "Incoming NOTIFY.", notify);
+
+ return 0;
+}
+
+/**
+ * Callback function called by the state machine manager to signal that the final state has been reached.
+ *
+ * @param [in,out] self The state machine owner.
+**/
+int tsip_dialog_subscribe_OnTerminated(tsip_dialog_subscribe_t *self)
+{
+ TSK_DEBUG_INFO("=== SUBSCRIBE Dialog terminated ===");
+
+ /* 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 : "Dialog terminated",
+ TSIP_DIALOG(self)->last_error.message);
+
+ /* Remove from the dialog layer. */
+ return tsip_dialog_remove(TSIP_DIALOG(self));
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//========================================================
+// SIP dialog SUBSCRIBE object definition
+//
+static tsk_object_t* tsip_dialog_subscribe_ctor(tsk_object_t * self, va_list * app)
+{
+ tsip_dialog_subscribe_t *dialog = self;
+ if(dialog){
+ tsip_ssession_handle_t *ss = va_arg(*app, tsip_ssession_handle_t *);
+
+ /* Initialize base class */
+ tsip_dialog_init(TSIP_DIALOG(self), tsip_dialog_SUBSCRIBE, tsk_null, ss, _fsm_state_Started, _fsm_state_Terminated);
+
+ /* FSM */
+ TSIP_DIALOG_GET_FSM(self)->debug = DEBUG_STATE_MACHINE;
+ tsk_fsm_set_callback_terminated(TSIP_DIALOG_GET_FSM(self), TSK_FSM_ONTERMINATED_F(tsip_dialog_subscribe_OnTerminated), (const void*)dialog);
+
+ /* Initialize the class itself */
+ tsip_dialog_subscribe_init(self);
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_dialog_subscribe_dtor(tsk_object_t * _self)
+{
+ tsip_dialog_subscribe_t *self = _self;
+ if(self){
+ /* Cancel all timers */
+ TSIP_DIALOG_TIMER_CANCEL(refresh);
+ TSIP_DIALOG_TIMER_CANCEL(shutdown);
+
+ /* DeInitialize base class (will cancel all transactions) */
+ tsip_dialog_deinit(TSIP_DIALOG(self));
+
+ TSK_DEBUG_INFO("*** SUBSCRIBE Dialog destroyed ***");
+ }
+ return self;
+}
+
+static int tsip_dialog_subscribe_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_subscribe_def_s =
+{
+ sizeof(tsip_dialog_subscribe_t),
+ tsip_dialog_subscribe_ctor,
+ tsip_dialog_subscribe_dtor,
+ tsip_dialog_subscribe_cmp,
+};
+const tsk_object_def_t *tsip_dialog_subscribe_def_t = &tsip_dialog_subscribe_def_s;
diff --git a/tinySIP/src/dialogs/tsip_dialog_subscribe.server.c b/tinySIP/src/dialogs/tsip_dialog_subscribe.server.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/dialogs/tsip_dialog_subscribe.server.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header.c b/tinySIP/src/headers/tsip_header.c
new file mode 100644
index 0000000..4360281
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header.c
@@ -0,0 +1,337 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_header.c
+ * @brief Defines a SIP header (field-name: field-value).
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/headers/tsip_header.h"
+
+#include "tinysip/headers/tsip_header_Dummy.h"
+
+
+#include "tsk_debug.h"
+
+/* Compact headers: http://www.cs.columbia.edu/sip/compact.html
+Abbreviation Header defined by origin (mnemonic)
+a Accept-Contact draft-ietf-sip-callerprefs --
+b Referred-By -refer- "by"
+c Content-Type RFC 3261
+d Request-Disposition
+e Content-Encoding RFC 3261
+f From RFC 3261
+i Call-ID RFC 3261
+j Reject-Contact
+k Supported RFC 3261 "know"
+l Content-Length RFC 3261
+m Contact RFC 3261 "moved"
+n Identity-Info
+o Event -event- "occurance"
+r Refer-To -refer-
+s Subject RFC 3261
+t To RFC 3261
+u Allow-Events -events- "understand"
+v Via RFC 3261
+y Identity
+*/
+
+#if TSIP_COMPACT_HEADERS
+# define _Accept_Contact "a"
+# define _Referred_By "b"
+# define _Content_Type "c"
+# define _Request_Disposition "d"
+# define _Content_Encoding "e"
+# define _From "f"
+# define _Call_ID "i"
+# define _Reject_Contact "j";
+# define _Supported "k"
+# define _Content_Length "l"
+# define _Contact "m"
+# define _Identity_Info "n"
+# define _Event "o"
+# define _Refer_To "r"
+# define _Subject "s"
+# define _To "t";
+# define _Allow_Events "u"
+# define _Via "v"
+# define _Session_Expires "x"
+# define _Identity "y"
+#else
+# define _Accept_Contact "Accept-Contact"
+# define _Referred_By "Referred-By"
+# define _Content_Type "Content-Type"
+# define _Request_Disposition "Request-Disposition"
+# define _Content_Encoding "Content-Encoding"
+# define _From "From"
+# define _Call_ID "Call-ID"
+# define _Reject_Contact "Reject-Contact"
+# define _Supported "Supported"
+# define _Content_Length "Content-Length"
+# define _Contact "Contact"
+# define _Identity_Info "Identity-Info"
+# define _Event "Event"
+# define _Refer_To "Refer-To"
+# define _Subject "Subject"
+# define _To "To"
+# define _Allow_Events "Allow-Events"
+# define _Via "Via"
+# define _Session_Expires "Session-Expires"
+# define _Identity "Identity"
+#endif
+
+/** Gets the name of the SIP header with a type equal to @a type.
+ * @param type The @a type of the header for which to retrieve the name.
+ *
+ * @return The name of the header.
+**/
+const char *tsip_header_get_name(tsip_header_type_t type)
+{
+ switch(type)
+ {
+ case tsip_htype_Accept: return "Accept";
+ case tsip_htype_Accept_Contact: return _Accept_Contact;
+ case tsip_htype_Accept_Encoding: return "Accept-Encoding";
+ case tsip_htype_Accept_Language: return "Accept-Language";
+ case tsip_htype_Accept_Resource_Priority: return "Accept-Resource-Priority";
+ case tsip_htype_Alert_Info: return "Alert-Info";
+ case tsip_htype_Allow: return "Allow";
+ case tsip_htype_Allow_Events: return _Allow_Events;
+ case tsip_htype_Authentication_Info: return "Authentication-Info";
+ case tsip_htype_Authorization: return "Authorization";
+ case tsip_htype_Call_ID: return _Call_ID;
+ case tsip_htype_Call_Info: return "Call-Info";
+ case tsip_htype_Contact: return _Contact;
+ case tsip_htype_Content_Disposition: return "Content-Disposition";
+ case tsip_htype_Content_Encoding: return _Content_Encoding;
+ case tsip_htype_Content_Language: return "Content-Language";
+ case tsip_htype_Content_Length: return _Content_Length;
+ case tsip_htype_Content_Type: return _Content_Type;
+ case tsip_htype_CSeq: return "CSeq";
+ case tsip_htype_Date: return "Date";
+ case tsip_htype_Error_Info: return "Error-Info";
+ case tsip_htype_Event: return _Event;
+ case tsip_htype_Expires: return "Expires";
+ case tsip_htype_From: return _From;
+ case tsip_htype_History_Info: return "History-Info";
+ case tsip_htype_Identity: return _Identity;
+ case tsip_htype_Identity_Info: return _Identity_Info;
+ case tsip_htype_In_Reply_To: return "In-Reply-To";
+ case tsip_htype_Join: return "Join";
+ case tsip_htype_Max_Forwards: return "Max-Forwards";
+ case tsip_htype_MIME_Version: return "MIME-Version";
+ case tsip_htype_Min_Expires: return "Min-Expires";
+ case tsip_htype_Min_SE: return "Min-SE";
+ case tsip_htype_Organization: return "Organization";
+ case tsip_htype_Path: return "Path";
+ case tsip_htype_Priority: return "Priority";
+ case tsip_htype_Privacy: return "Privacy";
+ case tsip_htype_Proxy_Authenticate: return "Proxy-Authenticate";
+ case tsip_htype_Proxy_Authorization: return "Proxy-Authorization";
+ case tsip_htype_Proxy_Require: return "Proxy-Require";
+ case tsip_htype_RAck: return "RAck";
+ case tsip_htype_Reason: return "Reason";
+ case tsip_htype_Record_Route: return "Record-Route";
+ case tsip_htype_Refer_Sub: return "Refer-Sub";
+ case tsip_htype_Refer_To: return _Refer_To;
+ case tsip_htype_Referred_By: return _Referred_By;
+ case tsip_htype_Reject_Contact: return _Reject_Contact;
+ case tsip_htype_Replaces: return "Replaces";
+ case tsip_htype_Reply_To: return "Reply-To";
+ case tsip_htype_Request_Disposition: return _Request_Disposition;
+ case tsip_htype_Require: return "Require";
+ case tsip_htype_Resource_Priority: return "Resource-Priority";
+ case tsip_htype_Retry_After: return "Retry-After";
+ case tsip_htype_Route: return "Route";
+ case tsip_htype_RSeq: return "RSeq";
+ case tsip_htype_Security_Client: return "Security-Client";
+ case tsip_htype_Security_Server: return "Security-Server";
+ case tsip_htype_Security_Verify: return "Security-Verify";
+ case tsip_htype_Server: return "Server";
+ case tsip_htype_Service_Route: return "Service-Route";
+ case tsip_htype_Session_Expires: return _Session_Expires;
+ case tsip_htype_SIP_ETag: return "SIP-ETag";
+ case tsip_htype_SIP_If_Match: return "SIP-If-Match";
+ case tsip_htype_Subject: return _Subject;
+ case tsip_htype_Subscription_State: return "Subscription-State";
+ case tsip_htype_Supported: return _Supported;
+ case tsip_htype_Target_Dialog: return "Target-Dialog";
+ case tsip_htype_Timestamp: return "Timestamp";
+ case tsip_htype_To: return _To;
+ case tsip_htype_Unsupported: return "Unsupported";
+ case tsip_htype_User_Agent: return "User-Agent";
+ case tsip_htype_Via: return _Via;
+ case tsip_htype_Warning: return "Warning";
+ case tsip_htype_WWW_Authenticate: return "WWW-Authenticate";
+ case tsip_htype_P_Access_Network_Info: return "P-Access-Network-Info";
+ case tsip_htype_P_Answer_State: return "P-Answer-State";
+ case tsip_htype_P_Asserted_Identity: return "P-Asserted-Identity";
+ case tsip_htype_P_Associated_URI: return "P-Associated-URI";
+ case tsip_htype_P_Called_Party_ID: return "P-Called-Party-ID";
+ case tsip_htype_P_Charging_Function_Addresses: return "P-Charging-Function-Addresses";
+ case tsip_htype_P_Charging_Vector: return "P-Charging-Vector";
+ case tsip_htype_P_DCS_Billing_Info: return "P-DCS-Billing-Info";
+ case tsip_htype_P_DCS_LAES: return "P-DCS-LAES";
+ case tsip_htype_P_DCS_OSPS: return "P-DCS-OSPS";
+ case tsip_htype_P_DCS_Redirect: return "P-DCS-Redirect";
+ case tsip_htype_P_DCS_Trace_Party_ID: return "P-DCS-Trace-Party-ID";
+ case tsip_htype_P_Early_Media: return "P-Early-Media";
+ case tsip_htype_P_Media_Authorization: return "P-Media-Authorization";
+ case tsip_htype_P_Preferred_Identity: return "P-Preferred-Identity";
+ case tsip_htype_P_Profile_Key: return "P-Profile-Key";
+ case tsip_htype_P_User_Database: return "P-User-Database";
+ case tsip_htype_P_Visited_Network_ID: return "P-Visited-Network-ID";
+
+ default: return "unknown-header";
+ }
+}
+
+const char *tsip_header_get_name_2(const tsip_header_t *self)
+{
+ if(self){
+ if(self->type == tsip_htype_Dummy){
+ return ((tsip_header_Dummy_t*)self)->name;
+ }
+ else{
+ return tsip_header_get_name(self->type);
+ }
+ }
+ return "unknown-header";
+}
+
+char tsip_header_get_param_separator(const tsip_header_t *self)
+{
+ if(self)
+ {
+ switch(self->type)
+ {
+ case tsip_htype_Authorization:
+ case tsip_htype_Proxy_Authorization:
+ case tsip_htype_Proxy_Authenticate:
+ case tsip_htype_WWW_Authenticate:
+ {
+ return ',';
+ }
+
+ default:
+ {
+ return ';';
+ }
+ }
+ }
+ return 0;
+}
+
+int tsip_header_serialize(const tsip_header_t *self, tsk_buffer_t *output)
+{
+ int ret = -1;
+ static const char* hname;
+ static char separator;
+
+ if(self && TSIP_HEADER(self)->serialize){
+ tsk_list_item_t *item;
+
+ hname = tsip_header_get_name_2(self);
+ ret = 0; // for empty lists
+
+ /* Header name */
+ tsk_buffer_append_2(output, "%s: ", hname);
+
+ /* Header value (likes calling tsip_header_value_serialize() ) */
+ if((ret = TSIP_HEADER(self)->serialize(self, output))){
+ // CHECK all headers return value!
+ //return ret;
+ }
+
+ /* Parameters */
+ tsk_list_foreach(item, self->params){
+ tsk_param_t* param = item->data;
+ separator = tsip_header_get_param_separator(self);
+ if((ret = tsk_buffer_append_2(output, param->value?"%c%s=%s":"%c%s", separator, param->name, param->value))){
+ return ret;
+ }
+ }
+
+ /* CRLF */
+ tsk_buffer_append(output, "\r\n", 2);
+ }
+ return ret;
+}
+
+char* tsip_header_tostring(const tsip_header_t *self)
+{
+ tsk_buffer_t *output;
+ char* ret = tsk_null;
+ if(self && (output = tsk_buffer_create_null())){
+ if(!tsip_header_serialize(self, output)){
+ ret = tsk_strndup(output->data, output->size);
+ }
+ TSK_OBJECT_SAFE_FREE(output);
+ }
+ return ret;
+}
+
+int tsip_header_value_serialize(const tsip_header_t *self, tsk_buffer_t *output)
+{
+ if(self && output){
+ return TSIP_HEADER(self)->serialize(self, output);
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+}
+
+char* tsip_header_value_tostring(const tsip_header_t *self)
+{
+ tsk_buffer_t *output;
+ char* ret = tsk_null;
+ if(self && (output = tsk_buffer_create_null())){
+ if(!tsip_header_value_serialize(self, output)){
+ ret = tsk_strndup(output->data, output->size);
+ }
+ TSK_OBJECT_SAFE_FREE(output);
+ }
+ return ret;
+}
+
+char* tsip_header_get_param_value(const tsip_header_t *self, const char* pname)
+{
+ const tsk_param_t* _param;
+ char* value = tsk_null;
+
+ if(!self || !pname){
+ return tsk_null;
+ }
+
+ if(self->get_special_param_value && (value = self->get_special_param_value(self, pname))){
+ return value;
+ }
+
+ if((_param = tsk_params_get_param_by_name(self->params, pname))){
+ return tsk_strdup(_param->value);
+ }
+
+ return tsk_null;
+}
diff --git a/tinySIP/src/headers/tsip_header_Accept_Contact.c b/tinySIP/src/headers/tsip_header_Accept_Contact.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Accept_Contact.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header_Accept_Encoding.c b/tinySIP/src/headers/tsip_header_Accept_Encoding.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Accept_Encoding.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header_Accept_Language.c b/tinySIP/src/headers/tsip_header_Accept_Language.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Accept_Language.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header_Accept_Resource_Priority.c b/tinySIP/src/headers/tsip_header_Accept_Resource_Priority.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Accept_Resource_Priority.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header_Alert_Info.c b/tinySIP/src/headers/tsip_header_Alert_Info.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Alert_Info.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header_Allow.c b/tinySIP/src/headers/tsip_header_Allow.c
new file mode 100644
index 0000000..db0930b
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Allow.c
@@ -0,0 +1,365 @@
+
+/* #line 1 "./ragel/tsip_parser_header_Allow.rl" */
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_header_Allow.c
+ * @brief SIP Allow header.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/headers/tsip_header_Allow.h"
+
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tsk_debug.h"
+#include "tsk_memory.h"
+#include <string.h>
+
+/***********************************
+* Ragel state machine.
+*/
+
+/* #line 66 "./ragel/tsip_parser_header_Allow.rl" */
+
+
+
+tsip_header_Allow_t* tsip_header_Allow_create()
+{
+ return tsk_object_new(tsip_header_Allow_def_t);
+}
+
+int tsip_header_Allow_serialize(const tsip_header_t* header, tsk_buffer_t* output)
+{
+ if(header){
+ const tsip_header_Allow_t *Allow = (const tsip_header_Allow_t *)header;
+ tsk_list_item_t *item;
+ tsk_string_t *str;
+ int ret = 0;
+
+ tsk_list_foreach(item, Allow->methods){
+ str = item->data;
+ if(item == Allow->methods->head){
+ tsk_buffer_append(output, str->value, tsk_strlen(str->value));
+ }
+ else{
+ tsk_buffer_append_2(output, ",%s", str->value);
+ }
+ }
+
+ return ret;
+ }
+
+ return -1;
+}
+
+tsip_header_Allow_t *tsip_header_Allow_parse(const char *data, tsk_size_t size)
+{
+ int cs = 0;
+ const char *p = data;
+ const char *pe = p + size;
+ const char *eof = pe;
+ tsip_header_Allow_t *hdr_allow = tsip_header_Allow_create();
+
+ const char *tag_start = tsk_null;
+
+
+/* #line 89 "./src/headers/tsip_header_Allow.c" */
+static const char _tsip_machine_parser_header_Allow_actions[] = {
+ 0, 1, 0, 1, 1, 1, 2
+};
+
+static const char _tsip_machine_parser_header_Allow_key_offsets[] = {
+ 0, 0, 2, 4, 6, 8, 10, 13,
+ 30, 31, 47, 51, 52, 54, 57, 74,
+ 75, 77, 93
+};
+
+static const char _tsip_machine_parser_header_Allow_trans_keys[] = {
+ 65, 97, 76, 108, 76, 108, 79, 111,
+ 87, 119, 9, 32, 58, 9, 13, 32,
+ 33, 37, 39, 126, 42, 43, 45, 46,
+ 48, 57, 65, 90, 95, 122, 10, 9,
+ 13, 32, 33, 37, 39, 44, 126, 42,
+ 46, 48, 57, 65, 90, 95, 122, 9,
+ 13, 32, 44, 10, 9, 32, 9, 32,
+ 44, 9, 13, 32, 33, 37, 39, 126,
+ 42, 43, 45, 46, 48, 57, 65, 90,
+ 95, 122, 10, 9, 32, 9, 32, 33,
+ 37, 39, 126, 42, 43, 45, 46, 48,
+ 57, 65, 90, 95, 122, 0
+};
+
+static const char _tsip_machine_parser_header_Allow_single_lengths[] = {
+ 0, 2, 2, 2, 2, 2, 3, 7,
+ 1, 8, 4, 1, 2, 3, 7, 1,
+ 2, 6, 0
+};
+
+static const char _tsip_machine_parser_header_Allow_range_lengths[] = {
+ 0, 0, 0, 0, 0, 0, 0, 5,
+ 0, 4, 0, 0, 0, 0, 5, 0,
+ 0, 5, 0
+};
+
+static const char _tsip_machine_parser_header_Allow_index_offsets[] = {
+ 0, 0, 3, 6, 9, 12, 15, 19,
+ 32, 34, 47, 52, 54, 57, 61, 74,
+ 76, 79, 91
+};
+
+static const char _tsip_machine_parser_header_Allow_indicies[] = {
+ 0, 0, 1, 2, 2, 1, 3, 3,
+ 1, 4, 4, 1, 5, 5, 1, 5,
+ 5, 6, 1, 6, 7, 6, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 1,
+ 9, 1, 10, 11, 10, 12, 12, 12,
+ 13, 12, 12, 12, 12, 12, 1, 14,
+ 15, 14, 16, 1, 17, 1, 18, 18,
+ 1, 18, 18, 16, 1, 16, 19, 16,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 1, 20, 1, 21, 21, 1, 21,
+ 21, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 1, 1, 0
+};
+
+static const char _tsip_machine_parser_header_Allow_trans_targs[] = {
+ 2, 0, 3, 4, 5, 6, 7, 8,
+ 9, 18, 10, 8, 9, 14, 10, 11,
+ 14, 12, 13, 15, 16, 17
+};
+
+static const char _tsip_machine_parser_header_Allow_trans_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 5, 3, 3, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+
+static const int tsip_machine_parser_header_Allow_start = 1;
+static const int tsip_machine_parser_header_Allow_first_final = 18;
+static const int tsip_machine_parser_header_Allow_error = 0;
+
+static const int tsip_machine_parser_header_Allow_en_main = 1;
+
+
+ (void)(tsip_machine_parser_header_Allow_first_final);
+
+/* #line 109 "./ragel/tsip_parser_header_Allow.rl" */
+ (void)(eof);
+ (void)(void)(tsip_machine_parser_header_Allow_first_final);
+ (void)(void)(tsip_machine_parser_header_Allow_error);
+ (void)(void)(tsip_machine_parser_header_Allow_en_main);
+
+/* #line 173 "./src/headers/tsip_header_Allow.c" */
+ {
+ cs = tsip_machine_parser_header_Allow_start;
+ }
+
+/* #line 114 "./ragel/tsip_parser_header_Allow.rl" */
+
+/* #line 180 "./src/headers/tsip_header_Allow.c" */
+ {
+ int _klen;
+ unsigned int _trans;
+ const char *_acts;
+ unsigned int _nacts;
+ const char *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( cs == 0 )
+ goto _out;
+_resume:
+ _keys = _tsip_machine_parser_header_Allow_trans_keys + _tsip_machine_parser_header_Allow_key_offsets[cs];
+ _trans = _tsip_machine_parser_header_Allow_index_offsets[cs];
+
+ _klen = _tsip_machine_parser_header_Allow_single_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + _klen - 1;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( (*p) < *_mid )
+ _upper = _mid - 1;
+ else if ( (*p) > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _tsip_machine_parser_header_Allow_range_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + (_klen<<1) - 2;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( (*p) < _mid[0] )
+ _upper = _mid - 2;
+ else if ( (*p) > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ _trans += ((_mid - _keys)>>1);
+ goto _match;
+ }
+ }
+ _trans += _klen;
+ }
+
+_match:
+ _trans = _tsip_machine_parser_header_Allow_indicies[_trans];
+ cs = _tsip_machine_parser_header_Allow_trans_targs[_trans];
+
+ if ( _tsip_machine_parser_header_Allow_trans_actions[_trans] == 0 )
+ goto _again;
+
+ _acts = _tsip_machine_parser_header_Allow_actions + _tsip_machine_parser_header_Allow_trans_actions[_trans];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 )
+ {
+ switch ( *_acts++ )
+ {
+ case 0:
+/* #line 48 "./ragel/tsip_parser_header_Allow.rl" */
+ {
+ tag_start = p;
+ }
+ break;
+ case 1:
+/* #line 53 "./ragel/tsip_parser_header_Allow.rl" */
+ {
+ TSK_PARSER_ADD_STRING(hdr_allow->methods);
+ }
+ break;
+ case 2:
+/* #line 58 "./ragel/tsip_parser_header_Allow.rl" */
+ {
+ }
+ break;
+/* #line 271 "./src/headers/tsip_header_Allow.c" */
+ }
+ }
+
+_again:
+ if ( cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ _out: {}
+ }
+
+/* #line 115 "./ragel/tsip_parser_header_Allow.rl" */
+
+ if( cs <
+/* #line 287 "./src/headers/tsip_header_Allow.c" */
+18
+/* #line 116 "./ragel/tsip_parser_header_Allow.rl" */
+ ){
+ TSK_DEBUG_ERROR("Failed to parse SIP 'Allow' header.");
+ TSK_OBJECT_SAFE_FREE(hdr_allow);
+ }
+
+ return hdr_allow;
+}
+
+tsk_bool_t tsip_header_Allow_allows(const tsip_header_Allow_t* self, const char* method)
+{
+ const tsk_list_item_t* item;
+ const tsk_string_t* string;
+
+ if(!self || !method){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_list_foreach(item, self->methods){
+ if(!(string = item->data)){
+ continue;
+ }
+ if(tsk_striequals(string->value, method)){
+ return tsk_true;
+ }
+ }
+ return tsk_false;
+}
+
+
+
+
+
+//========================================================
+// Allow header object definition
+//
+
+static tsk_object_t* tsip_header_Allow_ctor(tsk_object_t *self, va_list * app)
+{
+ tsip_header_Allow_t *Allow = self;
+ if(Allow){
+ /*const char* methods = va_arg(*app, const char *);
+ if(methods && !tsk_strempty(methods))
+ {
+ Allow->methods = tsip_header_Allow_parse(methods, tsk_strlen(methods));
+ }*/
+ TSIP_HEADER(Allow)->type = tsip_htype_Allow;
+ TSIP_HEADER(Allow)->serialize = tsip_header_Allow_serialize;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create new Allow header.");
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_header_Allow_dtor(tsk_object_t *self)
+{
+ tsip_header_Allow_t *Allow = self;
+ if(Allow){
+ TSK_OBJECT_SAFE_FREE(Allow->methods);
+ TSK_OBJECT_SAFE_FREE(TSIP_HEADER_PARAMS(Allow));
+ }
+ else TSK_DEBUG_ERROR("Null Allow header.");
+
+ return self;
+}
+
+static const tsk_object_def_t tsip_header_Allow_def_s =
+{
+ sizeof(tsip_header_Allow_t),
+ tsip_header_Allow_ctor,
+ tsip_header_Allow_dtor,
+ tsk_null
+};
+const tsk_object_def_t *tsip_header_Allow_def_t = &tsip_header_Allow_def_s;
diff --git a/tinySIP/src/headers/tsip_header_Allow_Events.c b/tinySIP/src/headers/tsip_header_Allow_Events.c
new file mode 100644
index 0000000..e86a9c4
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Allow_Events.c
@@ -0,0 +1,352 @@
+
+/* #line 1 "./ragel/tsip_parser_header_Allow_Events.rl" */
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_header_Allow_events.c
+ * @brief SIP Allow-Events/u header.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/headers/tsip_header_Allow_Events.h"
+
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tsk_debug.h"
+#include "tsk_memory.h"
+
+#include <string.h>
+
+
+/***********************************
+* Ragel state machine.
+*/
+
+/* #line 69 "./ragel/tsip_parser_header_Allow_Events.rl" */
+
+
+tsip_header_Allow_Events_t* tsip_header_Allow_Events_create()
+{
+ return tsk_object_new(tsip_header_Allow_Events_def_t);
+}
+
+int tsip_header_Allow_Events_serialize(const tsip_header_t* header, tsk_buffer_t* output)
+{
+ if(header){
+ const tsip_header_Allow_Events_t *Allow_Events = (const tsip_header_Allow_Events_t *)header;
+ tsk_list_item_t *item;
+ tsk_string_t *str;
+ int ret = 0;
+
+ tsk_list_foreach(item, Allow_Events->events){
+ str = item->data;
+ if(item == Allow_Events->events->head){
+ tsk_buffer_append(output, str->value, tsk_strlen(str->value));
+ }
+ else{
+ tsk_buffer_append_2(output, ",%s", str->value);
+ }
+ }
+
+ return ret;
+ }
+
+ return -1;
+}
+
+tsip_header_Allow_Events_t *tsip_header_Allow_Events_parse(const char *data, tsk_size_t size)
+{
+ int cs = 0;
+ const char *p = data;
+ const char *pe = p + size;
+ const char *eof = pe;
+ tsip_header_Allow_Events_t *hdr_allow_events = tsip_header_Allow_Events_create();
+
+ const char *tag_start = tsk_null;
+
+
+/* #line 90 "./src/headers/tsip_header_Allow_Events.c" */
+static const char _tsip_machine_parser_header_Allow_events_actions[] = {
+ 0, 1, 0, 1, 1, 1, 2
+};
+
+static const char _tsip_machine_parser_header_Allow_events_key_offsets[] = {
+ 0, 0, 4, 6, 8, 10, 12, 13,
+ 15, 17, 19, 21, 23, 25, 28, 44,
+ 45, 47, 62, 79, 83, 84, 86, 89,
+ 90, 103
+};
+
+static const char _tsip_machine_parser_header_Allow_events_trans_keys[] = {
+ 65, 85, 97, 117, 76, 108, 76, 108,
+ 79, 111, 87, 119, 45, 69, 101, 86,
+ 118, 69, 101, 78, 110, 84, 116, 83,
+ 115, 9, 32, 58, 9, 13, 32, 33,
+ 37, 39, 45, 126, 42, 43, 48, 57,
+ 65, 90, 95, 122, 10, 9, 32, 9,
+ 32, 33, 37, 39, 45, 126, 42, 43,
+ 48, 57, 65, 90, 95, 122, 9, 13,
+ 32, 33, 37, 39, 44, 46, 126, 42,
+ 45, 48, 57, 65, 90, 95, 122, 9,
+ 13, 32, 44, 10, 9, 32, 9, 32,
+ 44, 10, 33, 37, 39, 45, 126, 42,
+ 43, 48, 57, 65, 90, 95, 122, 0
+};
+
+static const char _tsip_machine_parser_header_Allow_events_single_lengths[] = {
+ 0, 4, 2, 2, 2, 2, 1, 2,
+ 2, 2, 2, 2, 2, 3, 8, 1,
+ 2, 7, 9, 4, 1, 2, 3, 1,
+ 5, 0
+};
+
+static const char _tsip_machine_parser_header_Allow_events_range_lengths[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 4, 0,
+ 0, 4, 4, 0, 0, 0, 0, 0,
+ 4, 0
+};
+
+static const unsigned char _tsip_machine_parser_header_Allow_events_index_offsets[] = {
+ 0, 0, 5, 8, 11, 14, 17, 19,
+ 22, 25, 28, 31, 34, 37, 41, 54,
+ 56, 59, 71, 85, 90, 92, 95, 99,
+ 101, 111
+};
+
+static const char _tsip_machine_parser_header_Allow_events_indicies[] = {
+ 0, 2, 0, 2, 1, 3, 3, 1,
+ 4, 4, 1, 5, 5, 1, 6, 6,
+ 1, 7, 1, 8, 8, 1, 9, 9,
+ 1, 10, 10, 1, 11, 11, 1, 12,
+ 12, 1, 2, 2, 1, 2, 2, 13,
+ 1, 13, 14, 13, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 1, 16, 1,
+ 17, 17, 1, 17, 17, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 1, 18,
+ 19, 18, 20, 20, 20, 21, 22, 20,
+ 20, 20, 20, 20, 1, 23, 24, 23,
+ 13, 1, 25, 1, 26, 26, 1, 26,
+ 26, 13, 1, 27, 1, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 1, 1,
+ 0
+};
+
+static const char _tsip_machine_parser_header_Allow_events_trans_targs[] = {
+ 2, 0, 13, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 14, 15, 18,
+ 16, 17, 19, 23, 18, 14, 24, 19,
+ 20, 21, 22, 25
+};
+
+static const char _tsip_machine_parser_header_Allow_events_trans_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 3, 3, 0, 3, 0, 0,
+ 0, 0, 0, 5
+};
+
+static const int tsip_machine_parser_header_Allow_events_start = 1;
+static const int tsip_machine_parser_header_Allow_events_first_final = 25;
+static const int tsip_machine_parser_header_Allow_events_error = 0;
+
+static const int tsip_machine_parser_header_Allow_events_en_main = 1;
+
+
+/* #line 111 "./ragel/tsip_parser_header_Allow_Events.rl" */
+ (void)(eof);
+ (void)(tsip_machine_parser_header_Allow_events_first_final);
+ (void)(tsip_machine_parser_header_Allow_events_error);
+ (void)(tsip_machine_parser_header_Allow_events_en_main);
+
+/* #line 184 "./src/headers/tsip_header_Allow_Events.c" */
+ {
+ cs = tsip_machine_parser_header_Allow_events_start;
+ }
+
+/* #line 116 "./ragel/tsip_parser_header_Allow_Events.rl" */
+
+/* #line 191 "./src/headers/tsip_header_Allow_Events.c" */
+ {
+ int _klen;
+ unsigned int _trans;
+ const char *_acts;
+ unsigned int _nacts;
+ const char *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( cs == 0 )
+ goto _out;
+_resume:
+ _keys = _tsip_machine_parser_header_Allow_events_trans_keys + _tsip_machine_parser_header_Allow_events_key_offsets[cs];
+ _trans = _tsip_machine_parser_header_Allow_events_index_offsets[cs];
+
+ _klen = _tsip_machine_parser_header_Allow_events_single_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + _klen - 1;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( (*p) < *_mid )
+ _upper = _mid - 1;
+ else if ( (*p) > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _tsip_machine_parser_header_Allow_events_range_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + (_klen<<1) - 2;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( (*p) < _mid[0] )
+ _upper = _mid - 2;
+ else if ( (*p) > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ _trans += ((_mid - _keys)>>1);
+ goto _match;
+ }
+ }
+ _trans += _klen;
+ }
+
+_match:
+ _trans = _tsip_machine_parser_header_Allow_events_indicies[_trans];
+ cs = _tsip_machine_parser_header_Allow_events_trans_targs[_trans];
+
+ if ( _tsip_machine_parser_header_Allow_events_trans_actions[_trans] == 0 )
+ goto _again;
+
+ _acts = _tsip_machine_parser_header_Allow_events_actions + _tsip_machine_parser_header_Allow_events_trans_actions[_trans];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 )
+ {
+ switch ( *_acts++ )
+ {
+ case 0:
+/* #line 49 "./ragel/tsip_parser_header_Allow_Events.rl" */
+ {
+ tag_start = p;
+ }
+ break;
+ case 1:
+/* #line 53 "./ragel/tsip_parser_header_Allow_Events.rl" */
+ {
+ TSK_PARSER_ADD_STRING(hdr_allow_events->events);
+ }
+ break;
+ case 2:
+/* #line 57 "./ragel/tsip_parser_header_Allow_Events.rl" */
+ {
+ }
+ break;
+/* #line 282 "./src/headers/tsip_header_Allow_Events.c" */
+ }
+ }
+
+_again:
+ if ( cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ _out: {}
+ }
+
+/* #line 117 "./ragel/tsip_parser_header_Allow_Events.rl" */
+
+ if( cs <
+/* #line 298 "./src/headers/tsip_header_Allow_Events.c" */
+25
+/* #line 118 "./ragel/tsip_parser_header_Allow_Events.rl" */
+ ){
+ TSK_DEBUG_ERROR("Failed to parse SIP 'Allow-Events' header.");
+ TSK_OBJECT_SAFE_FREE(hdr_allow_events);
+ }
+
+ return hdr_allow_events;
+}
+
+
+
+
+
+
+
+//========================================================
+// Allow_events header object definition
+//
+
+static tsk_object_t* tsip_header_Allow_Events_ctor(tsk_object_t *self, va_list * app)
+{
+ tsip_header_Allow_Events_t *Allow_events = self;
+ if(Allow_events){
+ TSIP_HEADER(Allow_events)->type = tsip_htype_Allow_Events;
+ TSIP_HEADER(Allow_events)->serialize = tsip_header_Allow_Events_serialize;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create new Allow-Events header.");
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_header_Allow_Events_dtor(tsk_object_t *self)
+{
+ tsip_header_Allow_Events_t *Allow_events = self;
+ if(Allow_events){
+ TSK_OBJECT_SAFE_FREE(Allow_events->events);
+ TSK_OBJECT_SAFE_FREE(TSIP_HEADER_PARAMS(Allow_events));
+ }
+ else{
+ TSK_DEBUG_ERROR("Null Allow-Events header.");
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tsip_header_Allow_Events_def_s =
+{
+ sizeof(tsip_header_Allow_Events_t),
+ tsip_header_Allow_Events_ctor,
+ tsip_header_Allow_Events_dtor,
+ tsk_null
+};
+const tsk_object_def_t *tsip_header_Allow_Events_def_t = &tsip_header_Allow_Events_def_s;
diff --git a/tinySIP/src/headers/tsip_header_Authentication_Info.c b/tinySIP/src/headers/tsip_header_Authentication_Info.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Authentication_Info.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header_Authorization.c b/tinySIP/src/headers/tsip_header_Authorization.c
new file mode 100644
index 0000000..92c9fdc
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Authorization.c
@@ -0,0 +1,180 @@
+
+/* #line 1 "tsip_parser_header_Authorization.rl" */
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_header_Authorization.c
+ * @brief SIP Proxy-Authenticate header.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/headers/tsip_header_Authorization.h"
+
+#include "tinyhttp/headers/thttp_header_Authorization.h"
+
+#include "tsk_debug.h"
+#include "tsk_memory.h"
+#include "tsk_time.h"
+
+#include <string.h>
+
+tsip_header_Authorization_t* tsip_header_Authorization_create()
+{
+ return tsk_object_new(tsip_header_Authorization_def_t);
+}
+
+int tsip_header_Authorization_serialize(const tsip_header_t* header, tsk_buffer_t* output)
+{
+ if(header){
+ const tsip_header_Authorization_t *Authorization = (const tsip_header_Authorization_t *)header;
+ if(Authorization && Authorization->scheme){
+ return tsk_buffer_append_2(output, "%s %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ Authorization->scheme,
+
+ Authorization->username ? "username=\"" : "",
+ Authorization->username ? Authorization->username : "",
+ Authorization->username ? "\"" : "",
+
+ Authorization->realm ? ",realm=\"" : "",
+ Authorization->realm ? Authorization->realm : "",
+ Authorization->realm ? "\"" : "",
+
+ Authorization->nonce ? ",nonce=\"" : "",
+ Authorization->nonce ? Authorization->nonce : "",
+ Authorization->nonce ? "\"" : "",
+
+ Authorization->uri ? ",uri=\"" : "",
+ Authorization->uri ? Authorization->uri : "",
+ Authorization->uri ? "\"" : "",
+
+ Authorization->response ? ",response=\"" : "",
+ Authorization->response ? Authorization->response : "",
+ Authorization->response ? "\"" : "",
+
+ Authorization->algorithm ? ",algorithm=" : "",
+ Authorization->algorithm ? Authorization->algorithm : "",
+
+ Authorization->cnonce ? ",cnonce=\"" : "",
+ Authorization->cnonce ? Authorization->cnonce : "",
+ Authorization->cnonce ? "\"" : "",
+
+ Authorization->opaque ? ",opaque=\"" : "",
+ Authorization->opaque ? Authorization->opaque : "",
+ Authorization->opaque ? "\"" : "",
+
+ Authorization->qop ? ",qop=" : "",
+ Authorization->qop ? Authorization->qop : "",
+
+ Authorization->nc ? ",nc=" : "",
+ Authorization->nc ? Authorization->nc : ""
+ );
+ }
+ }
+ return -1;
+}
+
+tsip_header_Authorization_t *tsip_header_Authorization_parse(const char *data, tsk_size_t size)
+{
+ tsip_header_Authorization_t *hdr_sip = 0;
+ thttp_header_Authorization_t* hdr_http;
+
+ if((hdr_http = thttp_header_Authorization_parse(data, size))){
+ hdr_sip = tsip_header_Authorization_create();
+
+ hdr_sip->scheme = tsk_strdup(hdr_http->scheme);
+ hdr_sip->username = tsk_strdup(hdr_http->username);
+ hdr_sip->realm = tsk_strdup(hdr_http->realm);
+ hdr_sip->nonce = tsk_strdup(hdr_http->nonce);
+ hdr_sip->uri = tsk_strdup(hdr_http->uri);
+ hdr_sip->response = tsk_strdup(hdr_http->response);
+ hdr_sip->algorithm = tsk_strdup(hdr_http->algorithm);
+ hdr_sip->cnonce = tsk_strdup(hdr_http->cnonce);
+ hdr_sip->opaque = tsk_strdup(hdr_http->opaque);
+ hdr_sip->qop = tsk_strdup(hdr_http->qop);
+ hdr_sip->nc = tsk_strdup(hdr_http->nc);
+
+ TSIP_HEADER(hdr_sip)->params = tsk_object_ref(THTTP_HEADER(hdr_http)->params);
+
+ TSK_OBJECT_SAFE_FREE(hdr_http);
+ }
+
+ return hdr_sip;
+}
+
+
+
+
+
+//========================================================
+// Authorization header object definition
+//
+
+/**@ingroup tsip_header_Authorization_group
+*/
+static tsk_object_t* tsip_header_Authorization_ctor(tsk_object_t *self, va_list * app)
+{
+ tsip_header_Authorization_t *Authorization = self;
+ if(Authorization){
+ TSIP_HEADER(Authorization)->type = tsip_htype_Authorization;
+ TSIP_HEADER(Authorization)->serialize = tsip_header_Authorization_serialize;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create new Authorization header.");
+ }
+ return self;
+}
+
+/**@ingroup tsip_header_Authorization_group
+*/
+static tsk_object_t* tsip_header_Authorization_dtor(tsk_object_t *self)
+{
+ tsip_header_Authorization_t *Authorization = self;
+ if(Authorization){
+ TSK_FREE(Authorization->scheme);
+ TSK_FREE(Authorization->username);
+ TSK_FREE(Authorization->realm);
+ TSK_FREE(Authorization->nonce);
+ TSK_FREE(Authorization->uri);
+ TSK_FREE(Authorization->response);
+ TSK_FREE(Authorization->algorithm);
+ TSK_FREE(Authorization->cnonce);
+ TSK_FREE(Authorization->opaque);
+ TSK_FREE(Authorization->qop);
+ TSK_FREE(Authorization->nc);
+
+ TSK_OBJECT_SAFE_FREE(TSIP_HEADER_PARAMS(Authorization));
+ }
+ else TSK_DEBUG_ERROR("Null Authorization header.");
+
+ return self;
+}
+
+static const tsk_object_def_t tsip_header_Authorization_def_s =
+{
+ sizeof(tsip_header_Authorization_t),
+ tsip_header_Authorization_ctor,
+ tsip_header_Authorization_dtor,
+ tsk_null
+};
+const tsk_object_def_t *tsip_header_Authorization_def_t = &tsip_header_Authorization_def_s;
diff --git a/tinySIP/src/headers/tsip_header_CSeq.c b/tinySIP/src/headers/tsip_header_CSeq.c
new file mode 100644
index 0000000..ca168ea
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_CSeq.c
@@ -0,0 +1,344 @@
+
+/* #line 1 "./ragel/tsip_parser_header_CSeq.rl" */
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_header_CSeq.c
+ * @brief SIP CSeq header.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/headers/tsip_header_CSeq.h"
+
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tsk_debug.h"
+#include "tsk_memory.h"
+
+extern tsip_request_type_t tsip_request_get_type(const char* method);
+
+/***********************************
+* Ragel state machine.
+*/
+
+/* #line 68 "./ragel/tsip_parser_header_CSeq.rl" */
+
+
+
+tsip_header_CSeq_t* tsip_header_CSeq_create(int32_t seq, const char*method)
+{
+ return tsk_object_new(TSIP_HEADER_CSEQ_VA_ARGS(seq, method));
+}
+
+int tsip_header_CSeq_serialize(const tsip_header_t* header, tsk_buffer_t* output)
+{
+ if(header){
+ const tsip_header_CSeq_t *CSeq = (const tsip_header_CSeq_t *)header;
+ return tsk_buffer_append_2(output, "%u %s", CSeq->seq, CSeq->method);
+ }
+ return -1;
+}
+
+tsip_header_CSeq_t *tsip_header_CSeq_parse(const char *data, tsk_size_t size)
+{
+ int cs = 0;
+ const char *p = data;
+ const char *pe = p + size;
+ const char *eof = pe;
+ tsip_header_CSeq_t *hdr_cseq = tsip_header_CSeq_create(TSIP_HEADER_CSEQ_NONE, tsk_null);
+
+ const char *tag_start = tsk_null;
+
+
+/* #line 75 "./src/headers/tsip_header_CSeq.c" */
+static const char _tsip_machine_parser_header_CSeq_actions[] = {
+ 0, 1, 0, 1, 1, 1, 2, 1,
+ 3
+};
+
+static const char _tsip_machine_parser_header_CSeq_key_offsets[] = {
+ 0, 0, 2, 4, 6, 8, 11, 16,
+ 17, 19, 23, 28, 45, 46, 48, 64,
+ 79, 80
+};
+
+static const char _tsip_machine_parser_header_CSeq_trans_keys[] = {
+ 67, 99, 83, 115, 69, 101, 81, 113,
+ 9, 32, 58, 9, 13, 32, 48, 57,
+ 10, 9, 32, 9, 32, 48, 57, 9,
+ 13, 32, 48, 57, 9, 13, 32, 33,
+ 37, 39, 126, 42, 43, 45, 46, 48,
+ 57, 65, 90, 95, 122, 10, 9, 32,
+ 9, 32, 33, 37, 39, 126, 42, 43,
+ 45, 46, 48, 57, 65, 90, 95, 122,
+ 13, 33, 37, 39, 126, 42, 43, 45,
+ 46, 48, 57, 65, 90, 95, 122, 10,
+ 0
+};
+
+static const char _tsip_machine_parser_header_CSeq_single_lengths[] = {
+ 0, 2, 2, 2, 2, 3, 3, 1,
+ 2, 2, 3, 7, 1, 2, 6, 5,
+ 1, 0
+};
+
+static const char _tsip_machine_parser_header_CSeq_range_lengths[] = {
+ 0, 0, 0, 0, 0, 0, 1, 0,
+ 0, 1, 1, 5, 0, 0, 5, 5,
+ 0, 0
+};
+
+static const char _tsip_machine_parser_header_CSeq_index_offsets[] = {
+ 0, 0, 3, 6, 9, 12, 16, 21,
+ 23, 26, 30, 35, 48, 50, 53, 65,
+ 76, 78
+};
+
+static const char _tsip_machine_parser_header_CSeq_indicies[] = {
+ 0, 0, 1, 2, 2, 1, 3, 3,
+ 1, 4, 4, 1, 4, 4, 5, 1,
+ 5, 6, 5, 7, 1, 8, 1, 9,
+ 9, 1, 9, 9, 7, 1, 10, 11,
+ 10, 12, 1, 13, 14, 13, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 1,
+ 16, 1, 17, 17, 1, 17, 17, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 1, 18, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 1, 20, 1, 1, 0
+};
+
+static const char _tsip_machine_parser_header_CSeq_trans_targs[] = {
+ 2, 0, 3, 4, 5, 6, 7, 10,
+ 8, 9, 11, 12, 10, 11, 12, 15,
+ 13, 14, 16, 15, 17
+};
+
+static const char _tsip_machine_parser_header_CSeq_trans_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 5, 5, 0, 0, 0, 1,
+ 0, 0, 3, 0, 7
+};
+
+static const int tsip_machine_parser_header_CSeq_start = 1;
+static const int tsip_machine_parser_header_CSeq_first_final = 17;
+static const int tsip_machine_parser_header_CSeq_error = 0;
+
+static const int tsip_machine_parser_header_CSeq_en_main = 1;
+
+
+/* #line 96 "./ragel/tsip_parser_header_CSeq.rl" */
+ (void)(eof);
+ (void)(tsip_machine_parser_header_CSeq_first_final);
+ (void)(tsip_machine_parser_header_CSeq_error);
+ (void)(tsip_machine_parser_header_CSeq_en_main);
+
+/* #line 157 "./src/headers/tsip_header_CSeq.c" */
+ {
+ cs = tsip_machine_parser_header_CSeq_start;
+ }
+
+/* #line 101 "./ragel/tsip_parser_header_CSeq.rl" */
+
+/* #line 164 "./src/headers/tsip_header_CSeq.c" */
+ {
+ int _klen;
+ unsigned int _trans;
+ const char *_acts;
+ unsigned int _nacts;
+ const char *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( cs == 0 )
+ goto _out;
+_resume:
+ _keys = _tsip_machine_parser_header_CSeq_trans_keys + _tsip_machine_parser_header_CSeq_key_offsets[cs];
+ _trans = _tsip_machine_parser_header_CSeq_index_offsets[cs];
+
+ _klen = _tsip_machine_parser_header_CSeq_single_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + _klen - 1;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( (*p) < *_mid )
+ _upper = _mid - 1;
+ else if ( (*p) > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _tsip_machine_parser_header_CSeq_range_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + (_klen<<1) - 2;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( (*p) < _mid[0] )
+ _upper = _mid - 2;
+ else if ( (*p) > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ _trans += ((_mid - _keys)>>1);
+ goto _match;
+ }
+ }
+ _trans += _klen;
+ }
+
+_match:
+ _trans = _tsip_machine_parser_header_CSeq_indicies[_trans];
+ cs = _tsip_machine_parser_header_CSeq_trans_targs[_trans];
+
+ if ( _tsip_machine_parser_header_CSeq_trans_actions[_trans] == 0 )
+ goto _again;
+
+ _acts = _tsip_machine_parser_header_CSeq_actions + _tsip_machine_parser_header_CSeq_trans_actions[_trans];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 )
+ {
+ switch ( *_acts++ )
+ {
+ case 0:
+/* #line 48 "./ragel/tsip_parser_header_CSeq.rl" */
+ {
+ tag_start = p;
+ }
+ break;
+ case 1:
+/* #line 52 "./ragel/tsip_parser_header_CSeq.rl" */
+ {
+ TSK_PARSER_SET_STRING(hdr_cseq->method);
+ }
+ break;
+ case 2:
+/* #line 56 "./ragel/tsip_parser_header_CSeq.rl" */
+ {
+ TSK_PARSER_SET_UINT(hdr_cseq->seq);
+ }
+ break;
+ case 3:
+/* #line 60 "./ragel/tsip_parser_header_CSeq.rl" */
+ {
+ }
+ break;
+/* #line 261 "./src/headers/tsip_header_CSeq.c" */
+ }
+ }
+
+_again:
+ if ( cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ _out: {}
+ }
+
+/* #line 102 "./ragel/tsip_parser_header_CSeq.rl" */
+
+ if( cs <
+/* #line 277 "./src/headers/tsip_header_CSeq.c" */
+17
+/* #line 103 "./ragel/tsip_parser_header_CSeq.rl" */
+ ){
+ TSK_DEBUG_ERROR("Failed to parse 'CSeq' header.");
+ TSK_OBJECT_SAFE_FREE(hdr_cseq);
+ }
+ else {
+ hdr_cseq->type = tsip_request_get_type(hdr_cseq->method);
+ }
+
+ return hdr_cseq;
+}
+
+
+
+
+
+
+
+//========================================================
+// CSeq header object definition
+//
+
+static tsk_object_t* tsip_header_CSeq_ctor(tsk_object_t *self, va_list * app)
+{
+ tsip_header_CSeq_t *CSeq = self;
+ if(CSeq){
+ TSIP_HEADER(CSeq)->type = tsip_htype_CSeq;
+ TSIP_HEADER(CSeq)->serialize = tsip_header_CSeq_serialize;
+ CSeq->seq = va_arg(*app, uint32_t);
+ CSeq->method = tsk_strdup(va_arg(*app, const char*));
+
+ if(!tsk_strnullORempty(CSeq->method)){
+ CSeq->type = tsip_request_get_type(CSeq->method);
+ }
+ else{
+ CSeq->type = tsip_NONE;
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create new CSeq header.");
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_header_CSeq_dtor(tsk_object_t *self)
+{
+ tsip_header_CSeq_t *CSeq = self;
+ if(CSeq){
+ TSK_FREE(CSeq->method);
+ TSK_OBJECT_SAFE_FREE(TSIP_HEADER_PARAMS(CSeq));
+ }
+ else{
+ TSK_DEBUG_ERROR("Null CSeq header.");
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tsip_header_CSeq_def_s =
+{
+ sizeof(tsip_header_CSeq_t),
+ tsip_header_CSeq_ctor,
+ tsip_header_CSeq_dtor,
+ tsk_null
+};
+const tsk_object_def_t *tsip_header_CSeq_def_t = &tsip_header_CSeq_def_s;
+
diff --git a/tinySIP/src/headers/tsip_header_Call_ID.c b/tinySIP/src/headers/tsip_header_Call_ID.c
new file mode 100644
index 0000000..3920b53
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Call_ID.c
@@ -0,0 +1,340 @@
+
+/* #line 1 "./ragel/tsip_parser_header_Call_ID.rl" */
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_header_Call_ID.c
+ * @brief SIP Call-ID/i header.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/headers/tsip_header_Call_ID.h"
+
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tsk_debug.h"
+#include "tsk_memory.h"
+#include "tsk_time.h"
+
+#include <string.h>
+
+
+
+/***********************************
+* Ragel state machine.
+*/
+
+/* #line 68 "./ragel/tsip_parser_header_Call_ID.rl" */
+
+
+
+tsip_header_Call_ID_t* tsip_header_Call_ID_create(const char* call_id)
+{
+ return tsk_object_new(TSIP_HEADER_CALL_ID_VA_ARGS(call_id));
+}
+
+int tsip_header_Call_ID_serialize(const tsip_header_t* header, tsk_buffer_t* output)
+{
+ if(header){
+ const tsip_header_Call_ID_t *Call_ID = (const tsip_header_Call_ID_t *)header;
+ if(Call_ID->value){
+ return tsk_buffer_append(output, Call_ID->value, tsk_strlen(Call_ID->value));
+ }
+ }
+ return -1;
+}
+
+int tsip_header_Call_ID_random(tsk_uuidstring_t *result)
+{
+ return tsk_uuidgenerate(result);
+}
+
+tsip_header_Call_ID_t *tsip_header_Call_ID_parse(const char *data, tsk_size_t size)
+{
+ int cs = 0;
+ const char *p = data;
+ const char *pe = p + size;
+ const char *eof = pe;
+ tsip_header_Call_ID_t *hdr_call_id = tsip_header_Call_ID_create(0);
+
+ const char *tag_start = tsk_null;
+
+
+/* #line 85 "./src/headers/tsip_header_Call_ID.c" */
+static const char _tsip_machine_parser_header_Call_ID_actions[] = {
+ 0, 1, 0, 1, 1, 1, 2
+};
+
+static const char _tsip_machine_parser_header_Call_ID_key_offsets[] = {
+ 0, 0, 4, 6, 8, 10, 11, 13,
+ 15, 18, 37, 38, 40, 58, 74, 75,
+ 91, 108
+};
+
+static const char _tsip_machine_parser_header_Call_ID_trans_keys[] = {
+ 67, 73, 99, 105, 65, 97, 76, 108,
+ 76, 108, 45, 73, 105, 68, 100, 9,
+ 32, 58, 9, 13, 32, 37, 60, 33,
+ 34, 39, 43, 45, 58, 62, 63, 65,
+ 93, 95, 123, 125, 126, 10, 9, 32,
+ 9, 32, 37, 60, 33, 34, 39, 43,
+ 45, 58, 62, 63, 65, 93, 95, 123,
+ 125, 126, 13, 37, 60, 64, 33, 34,
+ 39, 43, 45, 58, 62, 93, 95, 123,
+ 125, 126, 10, 37, 60, 33, 34, 39,
+ 43, 45, 58, 62, 63, 65, 93, 95,
+ 123, 125, 126, 13, 37, 60, 33, 34,
+ 39, 43, 45, 58, 62, 63, 65, 93,
+ 95, 123, 125, 126, 0
+};
+
+static const char _tsip_machine_parser_header_Call_ID_single_lengths[] = {
+ 0, 4, 2, 2, 2, 1, 2, 2,
+ 3, 5, 1, 2, 4, 4, 1, 2,
+ 3, 0
+};
+
+static const char _tsip_machine_parser_header_Call_ID_range_lengths[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 0, 0, 7, 6, 0, 7,
+ 7, 0
+};
+
+static const char _tsip_machine_parser_header_Call_ID_index_offsets[] = {
+ 0, 0, 5, 8, 11, 14, 16, 19,
+ 22, 26, 39, 41, 44, 56, 67, 69,
+ 79, 90
+};
+
+static const char _tsip_machine_parser_header_Call_ID_indicies[] = {
+ 0, 2, 0, 2, 1, 3, 3, 1,
+ 4, 4, 1, 5, 5, 1, 6, 1,
+ 7, 7, 1, 2, 2, 1, 2, 2,
+ 8, 1, 8, 9, 8, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 1, 11,
+ 1, 12, 12, 1, 12, 12, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 1,
+ 13, 14, 14, 15, 14, 14, 14, 14,
+ 14, 14, 1, 16, 1, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 1, 13,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 1, 1, 0
+};
+
+static const char _tsip_machine_parser_header_Call_ID_trans_targs[] = {
+ 2, 0, 8, 3, 4, 5, 6, 7,
+ 9, 10, 13, 11, 12, 14, 13, 15,
+ 17, 16
+};
+
+static const char _tsip_machine_parser_header_Call_ID_trans_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 3, 0, 0,
+ 5, 0
+};
+
+static const int tsip_machine_parser_header_Call_ID_start = 1;
+static const int tsip_machine_parser_header_Call_ID_first_final = 17;
+static const int tsip_machine_parser_header_Call_ID_error = 0;
+
+static const int tsip_machine_parser_header_Call_ID_en_main = 1;
+
+
+/* #line 103 "./ragel/tsip_parser_header_Call_ID.rl" */
+ (void)(eof);
+ (void)(tsip_machine_parser_header_Call_ID_first_final);
+ (void)(tsip_machine_parser_header_Call_ID_error);
+ (void)(tsip_machine_parser_header_Call_ID_en_main);
+
+/* #line 171 "./src/headers/tsip_header_Call_ID.c" */
+ {
+ cs = tsip_machine_parser_header_Call_ID_start;
+ }
+
+/* #line 108 "./ragel/tsip_parser_header_Call_ID.rl" */
+
+/* #line 178 "./src/headers/tsip_header_Call_ID.c" */
+ {
+ int _klen;
+ unsigned int _trans;
+ const char *_acts;
+ unsigned int _nacts;
+ const char *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( cs == 0 )
+ goto _out;
+_resume:
+ _keys = _tsip_machine_parser_header_Call_ID_trans_keys + _tsip_machine_parser_header_Call_ID_key_offsets[cs];
+ _trans = _tsip_machine_parser_header_Call_ID_index_offsets[cs];
+
+ _klen = _tsip_machine_parser_header_Call_ID_single_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + _klen - 1;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( (*p) < *_mid )
+ _upper = _mid - 1;
+ else if ( (*p) > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _tsip_machine_parser_header_Call_ID_range_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + (_klen<<1) - 2;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( (*p) < _mid[0] )
+ _upper = _mid - 2;
+ else if ( (*p) > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ _trans += ((_mid - _keys)>>1);
+ goto _match;
+ }
+ }
+ _trans += _klen;
+ }
+
+_match:
+ _trans = _tsip_machine_parser_header_Call_ID_indicies[_trans];
+ cs = _tsip_machine_parser_header_Call_ID_trans_targs[_trans];
+
+ if ( _tsip_machine_parser_header_Call_ID_trans_actions[_trans] == 0 )
+ goto _again;
+
+ _acts = _tsip_machine_parser_header_Call_ID_actions + _tsip_machine_parser_header_Call_ID_trans_actions[_trans];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 )
+ {
+ switch ( *_acts++ )
+ {
+ case 0:
+/* #line 51 "./ragel/tsip_parser_header_Call_ID.rl" */
+ {
+ tag_start = p;
+ }
+ break;
+ case 1:
+/* #line 55 "./ragel/tsip_parser_header_Call_ID.rl" */
+ {
+ TSK_PARSER_SET_STRING(hdr_call_id->value);
+ }
+ break;
+ case 2:
+/* #line 59 "./ragel/tsip_parser_header_Call_ID.rl" */
+ {
+ }
+ break;
+/* #line 269 "./src/headers/tsip_header_Call_ID.c" */
+ }
+ }
+
+_again:
+ if ( cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ _out: {}
+ }
+
+/* #line 109 "./ragel/tsip_parser_header_Call_ID.rl" */
+
+ if( cs <
+/* #line 285 "./src/headers/tsip_header_Call_ID.c" */
+17
+/* #line 110 "./ragel/tsip_parser_header_Call_ID.rl" */
+ ){
+ TSK_DEBUG_ERROR("Failed to parse SIP 'Call-ID' header.");
+ TSK_OBJECT_SAFE_FREE(hdr_call_id);
+ }
+
+ return hdr_call_id;
+}
+
+
+
+
+
+
+
+//========================================================
+// Call_ID header object definition
+//
+
+static tsk_object_t* tsip_header_Call_ID_ctor(tsk_object_t *self, va_list * app)
+{
+ tsip_header_Call_ID_t *Call_ID = self;
+ if(Call_ID){
+ Call_ID->value = tsk_strdup(va_arg(*app, const char *));
+ TSIP_HEADER(Call_ID)->type = tsip_htype_Call_ID;
+ TSIP_HEADER(Call_ID)->serialize = tsip_header_Call_ID_serialize;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create new Call-ID header.");
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_header_Call_ID_dtor(tsk_object_t *self)
+{
+ tsip_header_Call_ID_t *Call_ID = self;
+ if(Call_ID){
+ TSK_FREE(Call_ID->value);
+ TSK_OBJECT_SAFE_FREE(TSIP_HEADER_PARAMS(Call_ID));
+ }
+ else{
+ TSK_DEBUG_ERROR("Null Call-ID header.");
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tsip_header_Call_ID_def_s =
+{
+ sizeof(tsip_header_Call_ID_t),
+ tsip_header_Call_ID_ctor,
+ tsip_header_Call_ID_dtor,
+ tsk_null
+};
+const tsk_object_def_t *tsip_header_Call_ID_def_t = &tsip_header_Call_ID_def_s;
diff --git a/tinySIP/src/headers/tsip_header_Call_Info.c b/tinySIP/src/headers/tsip_header_Call_Info.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Call_Info.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header_Contact.c b/tinySIP/src/headers/tsip_header_Contact.c
new file mode 100644
index 0000000..e7abd80
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Contact.c
@@ -0,0 +1,702 @@
+
+/* #line 1 "./ragel/tsip_parser_header_Contact.rl" */
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_header_Contact.c
+ * @brief SIP Contact/m header.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/headers/tsip_header_Contact.h"
+
+#include "tinysip/parsers/tsip_parser_uri.h"
+
+#include "tsk_debug.h"
+#include "tsk_memory.h"
+
+
+
+/***********************************
+* Ragel state machine.
+*/
+
+/* #line 108 "./ragel/tsip_parser_header_Contact.rl" */
+
+
+
+tsip_header_Contact_t* tsip_header_Contact_create()
+{
+ return tsk_object_new(tsip_header_Contact_def_t);
+}
+
+int tsip_header_Contact_serialize(const tsip_header_t* header, tsk_buffer_t* output)
+{
+ if(header){
+ const tsip_header_Contact_t *Contact = (const tsip_header_Contact_t *)header;
+ int ret = 0;
+
+ /* Uri with hacked display-name*/
+ if((ret = tsip_uri_serialize(Contact->uri, tsk_true, tsk_true, output))){
+ return ret;
+ }
+
+ /* Expires */
+ if(Contact->expires >=0){
+ tsk_buffer_append_2(output, ";expires=%lld", Contact->expires);
+ }
+
+ return ret;
+ }
+
+ return -1;
+}
+
+
+tsip_header_Contacts_L_t *tsip_header_Contact_parse(const char *data, tsk_size_t size)
+{
+ int cs = 0;
+ const char *p = data;
+ const char *pe = p + size;
+ const char *eof = pe;
+ tsip_header_Contacts_L_t *hdr_contacts = tsk_list_create();
+
+ const char *tag_start = tsk_null;
+ tsip_header_Contact_t *curr_contact = 0;
+
+
+/* #line 90 "./src/headers/tsip_header_Contact.c" */
+static const char _tsip_machine_parser_header_Contact_actions[] = {
+ 0, 1, 0, 1, 1, 1, 2, 1,
+ 3, 1, 4, 1, 5, 1, 6, 1,
+ 7, 2, 1, 0, 2, 3, 6, 2,
+ 4, 6, 2, 5, 6
+};
+
+static const short _tsip_machine_parser_header_Contact_key_offsets[] = {
+ 0, 0, 4, 6, 8, 10, 12, 14,
+ 16, 19, 40, 41, 43, 64, 65, 67,
+ 71, 74, 75, 79, 91, 94, 94, 95,
+ 100, 105, 106, 108, 112, 133, 134, 136,
+ 157, 158, 160, 163, 180, 198, 202, 203,
+ 205, 213, 214, 216, 220, 226, 246, 265,
+ 270, 270, 275, 294, 295, 297, 315, 333,
+ 339, 340, 342, 347, 366, 367, 369, 388,
+ 389, 391, 394, 402, 403, 405, 410, 416,
+ 433, 440, 448, 456, 464, 466, 473, 482,
+ 484, 487, 489, 492, 494, 497, 500, 501,
+ 504, 505, 508, 509, 518, 527, 535, 543,
+ 551, 559, 561, 567, 576, 585, 594, 596,
+ 599, 602, 603, 604, 624, 644, 664, 684,
+ 704, 724, 742, 748, 749, 751, 756, 775,
+ 776, 778, 797, 804, 821, 839, 843
+};
+
+static const char _tsip_machine_parser_header_Contact_trans_keys[] = {
+ 67, 77, 99, 109, 79, 111, 78, 110,
+ 84, 116, 65, 97, 67, 99, 84, 116,
+ 9, 32, 58, 9, 13, 32, 33, 34,
+ 37, 39, 42, 43, 60, 126, 45, 46,
+ 48, 57, 65, 90, 95, 96, 97, 122,
+ 10, 9, 32, 9, 13, 32, 33, 34,
+ 37, 39, 42, 43, 60, 126, 45, 46,
+ 48, 57, 65, 90, 95, 96, 97, 122,
+ 10, 9, 32, 9, 32, 42, 60, 9,
+ 13, 32, 10, 65, 90, 97, 122, 9,
+ 32, 43, 58, 45, 46, 48, 57, 65,
+ 90, 97, 122, 9, 32, 58, 62, 9,
+ 13, 32, 44, 59, 9, 13, 32, 44,
+ 59, 10, 9, 32, 9, 32, 44, 59,
+ 9, 13, 32, 33, 34, 37, 39, 60,
+ 126, 42, 43, 45, 46, 48, 57, 65,
+ 90, 95, 96, 97, 122, 10, 9, 32,
+ 9, 13, 32, 33, 34, 37, 39, 60,
+ 126, 42, 43, 45, 46, 48, 57, 65,
+ 90, 95, 96, 97, 122, 10, 9, 32,
+ 9, 32, 60, 9, 13, 32, 33, 37,
+ 39, 126, 42, 43, 45, 46, 48, 57,
+ 65, 90, 95, 122, 9, 13, 32, 33,
+ 37, 39, 60, 126, 42, 43, 45, 46,
+ 48, 57, 65, 90, 95, 122, 9, 13,
+ 32, 60, 10, 9, 32, 13, 34, 92,
+ 127, 0, 8, 10, 31, 10, 9, 32,
+ 9, 13, 32, 60, 0, 9, 11, 12,
+ 14, 127, 9, 13, 32, 33, 37, 39,
+ 42, 43, 58, 126, 45, 46, 48, 57,
+ 65, 90, 95, 96, 97, 122, 9, 13,
+ 32, 33, 37, 39, 58, 60, 126, 42,
+ 43, 45, 46, 48, 57, 65, 90, 95,
+ 122, 9, 13, 32, 58, 60, 9, 13,
+ 32, 44, 59, 9, 13, 32, 33, 37,
+ 39, 69, 101, 126, 42, 43, 45, 46,
+ 48, 57, 65, 90, 95, 122, 10, 9,
+ 32, 9, 32, 33, 37, 39, 69, 101,
+ 126, 42, 43, 45, 46, 48, 57, 65,
+ 90, 95, 122, 9, 13, 32, 33, 37,
+ 39, 44, 59, 61, 126, 42, 46, 48,
+ 57, 65, 90, 95, 122, 9, 13, 32,
+ 44, 59, 61, 10, 9, 32, 9, 32,
+ 44, 59, 61, 9, 13, 32, 33, 34,
+ 37, 39, 91, 126, 42, 43, 45, 46,
+ 48, 57, 65, 90, 95, 122, 10, 9,
+ 32, 9, 13, 32, 33, 34, 37, 39,
+ 91, 126, 42, 43, 45, 46, 48, 57,
+ 65, 90, 95, 122, 10, 9, 32, 9,
+ 32, 34, 13, 34, 92, 127, 0, 8,
+ 10, 31, 10, 9, 32, 9, 13, 32,
+ 44, 59, 0, 9, 11, 12, 14, 127,
+ 9, 13, 32, 33, 37, 39, 44, 59,
+ 126, 42, 46, 48, 57, 65, 90, 95,
+ 122, 58, 48, 57, 65, 70, 97, 102,
+ 58, 93, 48, 57, 65, 70, 97, 102,
+ 58, 93, 48, 57, 65, 70, 97, 102,
+ 58, 93, 48, 57, 65, 70, 97, 102,
+ 58, 93, 58, 48, 57, 65, 70, 97,
+ 102, 46, 58, 93, 48, 57, 65, 70,
+ 97, 102, 48, 57, 46, 48, 57, 48,
+ 57, 46, 48, 57, 48, 57, 93, 48,
+ 57, 93, 48, 57, 93, 46, 48, 57,
+ 46, 46, 48, 57, 46, 46, 58, 93,
+ 48, 57, 65, 70, 97, 102, 46, 58,
+ 93, 48, 57, 65, 70, 97, 102, 58,
+ 93, 48, 57, 65, 70, 97, 102, 58,
+ 93, 48, 57, 65, 70, 97, 102, 58,
+ 93, 48, 57, 65, 70, 97, 102, 58,
+ 93, 48, 57, 65, 70, 97, 102, 58,
+ 93, 48, 57, 65, 70, 97, 102, 46,
+ 58, 93, 48, 57, 65, 70, 97, 102,
+ 46, 58, 93, 48, 57, 65, 70, 97,
+ 102, 46, 58, 93, 48, 57, 65, 70,
+ 97, 102, 48, 57, 46, 48, 57, 46,
+ 48, 57, 46, 58, 9, 13, 32, 33,
+ 37, 39, 44, 59, 61, 88, 120, 126,
+ 42, 46, 48, 57, 65, 90, 95, 122,
+ 9, 13, 32, 33, 37, 39, 44, 59,
+ 61, 80, 112, 126, 42, 46, 48, 57,
+ 65, 90, 95, 122, 9, 13, 32, 33,
+ 37, 39, 44, 59, 61, 73, 105, 126,
+ 42, 46, 48, 57, 65, 90, 95, 122,
+ 9, 13, 32, 33, 37, 39, 44, 59,
+ 61, 82, 114, 126, 42, 46, 48, 57,
+ 65, 90, 95, 122, 9, 13, 32, 33,
+ 37, 39, 44, 59, 61, 69, 101, 126,
+ 42, 46, 48, 57, 65, 90, 95, 122,
+ 9, 13, 32, 33, 37, 39, 44, 59,
+ 61, 83, 115, 126, 42, 46, 48, 57,
+ 65, 90, 95, 122, 9, 13, 32, 33,
+ 37, 39, 44, 59, 61, 126, 42, 46,
+ 48, 57, 65, 90, 95, 122, 9, 13,
+ 32, 44, 59, 61, 10, 9, 32, 9,
+ 32, 44, 59, 61, 9, 13, 32, 33,
+ 34, 37, 39, 91, 126, 42, 43, 45,
+ 46, 48, 57, 65, 90, 95, 122, 10,
+ 9, 32, 9, 13, 32, 33, 34, 37,
+ 39, 91, 126, 42, 43, 45, 46, 48,
+ 57, 65, 90, 95, 122, 9, 13, 32,
+ 44, 59, 48, 57, 9, 13, 32, 33,
+ 37, 39, 126, 42, 43, 45, 46, 48,
+ 57, 65, 90, 95, 122, 9, 13, 32,
+ 33, 37, 39, 60, 126, 42, 43, 45,
+ 46, 48, 57, 65, 90, 95, 122, 9,
+ 13, 32, 60, 0
+};
+
+static const char _tsip_machine_parser_header_Contact_single_lengths[] = {
+ 0, 4, 2, 2, 2, 2, 2, 2,
+ 3, 11, 1, 2, 11, 1, 2, 4,
+ 3, 1, 0, 4, 3, 0, 1, 5,
+ 5, 1, 2, 4, 9, 1, 2, 9,
+ 1, 2, 3, 7, 8, 4, 1, 2,
+ 4, 1, 2, 4, 0, 10, 9, 5,
+ 0, 5, 9, 1, 2, 8, 10, 6,
+ 1, 2, 5, 9, 1, 2, 9, 1,
+ 2, 3, 4, 1, 2, 5, 0, 9,
+ 1, 2, 2, 2, 2, 1, 3, 0,
+ 1, 0, 1, 0, 1, 1, 1, 1,
+ 1, 1, 1, 3, 3, 2, 2, 2,
+ 2, 2, 0, 3, 3, 3, 0, 1,
+ 1, 1, 1, 12, 12, 12, 12, 12,
+ 12, 10, 6, 1, 2, 5, 9, 1,
+ 2, 9, 5, 7, 8, 4, 0
+};
+
+static const char _tsip_machine_parser_header_Contact_range_lengths[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 0, 0, 5, 0, 0, 0,
+ 0, 0, 2, 4, 0, 0, 0, 0,
+ 0, 0, 0, 0, 6, 0, 0, 6,
+ 0, 0, 0, 5, 5, 0, 0, 0,
+ 2, 0, 0, 0, 3, 5, 5, 0,
+ 0, 0, 5, 0, 0, 5, 4, 0,
+ 0, 0, 0, 5, 0, 0, 5, 0,
+ 0, 0, 2, 0, 0, 0, 3, 4,
+ 3, 3, 3, 3, 0, 3, 3, 1,
+ 1, 1, 1, 1, 1, 1, 0, 1,
+ 0, 1, 0, 3, 3, 3, 3, 3,
+ 3, 0, 3, 3, 3, 3, 1, 1,
+ 1, 0, 0, 4, 4, 4, 4, 4,
+ 4, 4, 0, 0, 0, 0, 5, 0,
+ 0, 5, 1, 5, 5, 0, 0
+};
+
+static const short _tsip_machine_parser_header_Contact_index_offsets[] = {
+ 0, 0, 5, 8, 11, 14, 17, 20,
+ 23, 27, 44, 46, 49, 66, 68, 71,
+ 76, 80, 82, 85, 94, 98, 99, 101,
+ 107, 113, 115, 118, 123, 139, 141, 144,
+ 160, 162, 165, 169, 182, 196, 201, 203,
+ 206, 213, 215, 218, 223, 227, 243, 258,
+ 264, 265, 271, 286, 288, 291, 305, 320,
+ 327, 329, 332, 338, 353, 355, 358, 373,
+ 375, 378, 382, 389, 391, 394, 400, 404,
+ 418, 423, 429, 435, 441, 444, 449, 456,
+ 458, 461, 463, 466, 468, 471, 474, 476,
+ 479, 481, 484, 486, 493, 500, 506, 512,
+ 518, 524, 527, 531, 538, 545, 552, 554,
+ 557, 560, 562, 564, 581, 598, 615, 632,
+ 649, 666, 681, 688, 690, 693, 699, 714,
+ 716, 719, 734, 741, 754, 768, 773
+};
+
+static const unsigned char _tsip_machine_parser_header_Contact_indicies[] = {
+ 0, 2, 0, 2, 1, 3, 3, 1,
+ 4, 4, 1, 5, 5, 1, 6, 6,
+ 1, 7, 7, 1, 2, 2, 1, 2,
+ 2, 8, 1, 9, 10, 9, 11, 12,
+ 11, 11, 13, 11, 14, 11, 11, 11,
+ 15, 11, 15, 1, 16, 1, 17, 17,
+ 1, 18, 19, 18, 11, 12, 11, 11,
+ 13, 11, 14, 11, 11, 11, 15, 11,
+ 15, 1, 20, 1, 21, 21, 1, 21,
+ 21, 22, 23, 1, 22, 24, 22, 1,
+ 25, 1, 26, 26, 1, 27, 27, 28,
+ 29, 28, 28, 28, 28, 1, 27, 27,
+ 29, 1, 30, 31, 30, 32, 33, 32,
+ 34, 35, 1, 36, 37, 36, 38, 35,
+ 1, 39, 1, 40, 40, 1, 40, 40,
+ 38, 35, 1, 41, 42, 41, 11, 12,
+ 11, 11, 14, 11, 11, 11, 11, 15,
+ 11, 15, 1, 43, 1, 44, 44, 1,
+ 45, 46, 45, 11, 12, 11, 11, 14,
+ 11, 11, 11, 11, 15, 11, 15, 1,
+ 47, 1, 48, 48, 1, 48, 48, 23,
+ 1, 49, 50, 49, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 1, 52, 53,
+ 52, 51, 51, 51, 54, 51, 51, 51,
+ 51, 51, 51, 1, 55, 56, 55, 23,
+ 1, 57, 1, 49, 49, 1, 59, 60,
+ 61, 1, 1, 1, 58, 62, 1, 58,
+ 58, 1, 52, 53, 52, 54, 1, 58,
+ 58, 58, 1, 63, 50, 63, 51, 51,
+ 51, 51, 64, 65, 51, 64, 64, 64,
+ 51, 64, 1, 66, 53, 66, 51, 51,
+ 51, 65, 54, 51, 51, 51, 51, 51,
+ 51, 1, 67, 56, 67, 65, 23, 1,
+ 68, 69, 70, 69, 71, 72, 68, 35,
+ 73, 35, 74, 74, 74, 75, 75, 74,
+ 74, 74, 74, 74, 74, 1, 76, 1,
+ 77, 77, 1, 77, 77, 74, 74, 74,
+ 75, 75, 74, 74, 74, 74, 74, 74,
+ 1, 78, 79, 78, 80, 80, 80, 81,
+ 82, 83, 80, 80, 80, 80, 80, 1,
+ 84, 85, 84, 38, 35, 83, 1, 86,
+ 1, 87, 87, 1, 87, 87, 38, 35,
+ 83, 1, 83, 88, 83, 89, 90, 89,
+ 89, 91, 89, 89, 89, 89, 89, 89,
+ 1, 92, 1, 93, 93, 1, 93, 94,
+ 93, 89, 90, 89, 89, 91, 89, 89,
+ 89, 89, 89, 89, 1, 95, 1, 96,
+ 96, 1, 96, 96, 90, 1, 97, 98,
+ 99, 1, 1, 1, 90, 100, 1, 90,
+ 90, 1, 101, 79, 101, 81, 82, 1,
+ 90, 90, 90, 1, 101, 79, 101, 89,
+ 89, 89, 81, 82, 89, 89, 89, 89,
+ 89, 1, 103, 102, 102, 102, 1, 105,
+ 98, 104, 104, 104, 1, 105, 98, 106,
+ 106, 106, 1, 105, 98, 107, 107, 107,
+ 1, 105, 98, 1, 109, 108, 102, 102,
+ 1, 110, 105, 98, 111, 104, 104, 1,
+ 112, 1, 113, 114, 1, 115, 1, 116,
+ 117, 1, 118, 1, 98, 119, 1, 98,
+ 120, 1, 98, 1, 116, 121, 1, 116,
+ 1, 113, 122, 1, 113, 1, 110, 105,
+ 98, 123, 106, 106, 1, 110, 105, 98,
+ 107, 107, 107, 1, 125, 98, 124, 124,
+ 124, 1, 127, 98, 126, 126, 126, 1,
+ 127, 98, 128, 128, 128, 1, 127, 98,
+ 129, 129, 129, 1, 127, 98, 1, 130,
+ 124, 124, 1, 110, 127, 98, 131, 126,
+ 126, 1, 110, 127, 98, 132, 128, 128,
+ 1, 110, 127, 98, 129, 129, 129, 1,
+ 133, 1, 110, 134, 1, 110, 135, 1,
+ 110, 1, 109, 1, 78, 79, 78, 80,
+ 80, 80, 81, 82, 83, 136, 136, 80,
+ 80, 80, 80, 80, 1, 78, 79, 78,
+ 80, 80, 80, 81, 82, 83, 137, 137,
+ 80, 80, 80, 80, 80, 1, 78, 79,
+ 78, 80, 80, 80, 81, 82, 83, 138,
+ 138, 80, 80, 80, 80, 80, 1, 78,
+ 79, 78, 80, 80, 80, 81, 82, 83,
+ 139, 139, 80, 80, 80, 80, 80, 1,
+ 78, 79, 78, 80, 80, 80, 81, 82,
+ 83, 140, 140, 80, 80, 80, 80, 80,
+ 1, 78, 79, 78, 80, 80, 80, 81,
+ 82, 83, 141, 141, 80, 80, 80, 80,
+ 80, 1, 142, 79, 142, 80, 80, 80,
+ 81, 82, 143, 80, 80, 80, 80, 80,
+ 1, 144, 145, 144, 38, 35, 143, 1,
+ 146, 1, 147, 147, 1, 147, 147, 38,
+ 35, 143, 1, 143, 148, 143, 89, 90,
+ 89, 89, 91, 89, 89, 89, 149, 89,
+ 89, 1, 150, 1, 151, 151, 1, 151,
+ 94, 151, 89, 90, 89, 89, 91, 89,
+ 89, 89, 149, 89, 89, 1, 152, 153,
+ 152, 154, 156, 155, 1, 157, 24, 157,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 1, 158, 24, 158, 51, 51, 51,
+ 54, 51, 51, 51, 51, 51, 51, 1,
+ 159, 24, 159, 23, 1, 1, 0
+};
+
+static const char _tsip_machine_parser_header_Contact_trans_targs[] = {
+ 2, 0, 8, 3, 4, 5, 6, 7,
+ 9, 9, 10, 35, 40, 123, 18, 45,
+ 11, 12, 12, 13, 14, 15, 16, 18,
+ 17, 126, 19, 20, 19, 21, 22, 23,
+ 24, 17, 28, 50, 24, 25, 28, 26,
+ 27, 28, 29, 30, 31, 31, 32, 33,
+ 34, 36, 38, 35, 37, 32, 18, 37,
+ 32, 39, 40, 41, 43, 44, 42, 46,
+ 45, 48, 47, 47, 49, 24, 17, 28,
+ 50, 51, 54, 107, 52, 53, 55, 17,
+ 54, 28, 50, 59, 55, 56, 57, 58,
+ 60, 71, 66, 72, 61, 62, 63, 64,
+ 65, 67, 69, 70, 68, 24, 73, 106,
+ 74, 77, 75, 76, 78, 93, 79, 91,
+ 80, 81, 89, 82, 83, 87, 84, 85,
+ 86, 88, 90, 92, 94, 102, 95, 98,
+ 96, 97, 99, 100, 101, 103, 104, 105,
+ 108, 109, 110, 111, 112, 113, 114, 118,
+ 114, 115, 116, 117, 119, 122, 120, 121,
+ 24, 17, 28, 122, 50, 124, 125, 125
+};
+
+static const char _tsip_machine_parser_header_Contact_trans_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 17, 17, 17, 3, 17,
+ 0, 0, 3, 3, 0, 0, 0, 0,
+ 0, 15, 1, 0, 0, 0, 0, 7,
+ 13, 13, 13, 0, 0, 0, 0, 0,
+ 0, 3, 3, 0, 0, 3, 3, 0,
+ 0, 0, 0, 0, 5, 5, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 20, 20, 20,
+ 7, 0, 1, 1, 0, 0, 26, 26,
+ 0, 26, 11, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 26, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 26, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0,
+ 23, 23, 23, 0, 9, 0, 5, 0
+};
+
+static const int tsip_machine_parser_header_Contact_start = 1;
+static const int tsip_machine_parser_header_Contact_first_final = 126;
+static const int tsip_machine_parser_header_Contact_error = 0;
+
+static const int tsip_machine_parser_header_Contact_en_main = 1;
+
+
+/* #line 151 "./ragel/tsip_parser_header_Contact.rl" */
+ (void)(eof);
+ (void)(tsip_machine_parser_header_Contact_first_final);
+ (void)(tsip_machine_parser_header_Contact_error);
+ (void)(tsip_machine_parser_header_Contact_en_main);
+
+/* #line 442 "./src/headers/tsip_header_Contact.c" */
+ {
+ cs = tsip_machine_parser_header_Contact_start;
+ }
+
+/* #line 156 "./ragel/tsip_parser_header_Contact.rl" */
+
+/* #line 449 "./src/headers/tsip_header_Contact.c" */
+ {
+ int _klen;
+ unsigned int _trans;
+ const char *_acts;
+ unsigned int _nacts;
+ const char *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( cs == 0 )
+ goto _out;
+_resume:
+ _keys = _tsip_machine_parser_header_Contact_trans_keys + _tsip_machine_parser_header_Contact_key_offsets[cs];
+ _trans = _tsip_machine_parser_header_Contact_index_offsets[cs];
+
+ _klen = _tsip_machine_parser_header_Contact_single_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + _klen - 1;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( (*p) < *_mid )
+ _upper = _mid - 1;
+ else if ( (*p) > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _tsip_machine_parser_header_Contact_range_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + (_klen<<1) - 2;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( (*p) < _mid[0] )
+ _upper = _mid - 2;
+ else if ( (*p) > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ _trans += ((_mid - _keys)>>1);
+ goto _match;
+ }
+ }
+ _trans += _klen;
+ }
+
+_match:
+ _trans = _tsip_machine_parser_header_Contact_indicies[_trans];
+ cs = _tsip_machine_parser_header_Contact_trans_targs[_trans];
+
+ if ( _tsip_machine_parser_header_Contact_trans_actions[_trans] == 0 )
+ goto _again;
+
+ _acts = _tsip_machine_parser_header_Contact_actions + _tsip_machine_parser_header_Contact_trans_actions[_trans];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 )
+ {
+ switch ( *_acts++ )
+ {
+ case 0:
+/* #line 48 "./ragel/tsip_parser_header_Contact.rl" */
+ {
+ tag_start = p;
+ }
+ break;
+ case 1:
+/* #line 52 "./ragel/tsip_parser_header_Contact.rl" */
+ {
+ if(!curr_contact){
+ curr_contact = tsip_header_Contact_create();
+ }
+ }
+ break;
+ case 2:
+/* #line 58 "./ragel/tsip_parser_header_Contact.rl" */
+ {
+ if(curr_contact){
+ TSK_PARSER_SET_STRING(curr_contact->display_name);
+ tsk_strunquote(&curr_contact->display_name);
+ }
+ }
+ break;
+ case 3:
+/* #line 65 "./ragel/tsip_parser_header_Contact.rl" */
+ {
+ if(curr_contact && !curr_contact->uri){
+ int len = (int)(p - tag_start);
+ if((curr_contact->uri = tsip_uri_parse(tag_start, (tsk_size_t)len)) && curr_contact->display_name){
+ curr_contact->uri->display_name = tsk_strdup(curr_contact->display_name);
+ }
+ }
+ }
+ break;
+ case 4:
+/* #line 74 "./ragel/tsip_parser_header_Contact.rl" */
+ {
+ if(curr_contact){
+ TSK_PARSER_SET_INTEGER(curr_contact->expires);
+ }
+ }
+ break;
+ case 5:
+/* #line 80 "./ragel/tsip_parser_header_Contact.rl" */
+ {
+ if(curr_contact){
+ TSK_PARSER_ADD_PARAM(TSIP_HEADER_PARAMS(curr_contact));
+ }
+ }
+ break;
+ case 6:
+/* #line 86 "./ragel/tsip_parser_header_Contact.rl" */
+ {
+ if(curr_contact){
+ tsk_list_push_back_data(hdr_contacts, ((void**) &curr_contact));
+ }
+ }
+ break;
+ case 7:
+/* #line 92 "./ragel/tsip_parser_header_Contact.rl" */
+ {
+ }
+ break;
+/* #line 586 "./src/headers/tsip_header_Contact.c" */
+ }
+ }
+
+_again:
+ if ( cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ _out: {}
+ }
+
+/* #line 157 "./ragel/tsip_parser_header_Contact.rl" */
+
+ if( cs <
+/* #line 602 "./src/headers/tsip_header_Contact.c" */
+126
+/* #line 158 "./ragel/tsip_parser_header_Contact.rl" */
+ ){
+ TSK_DEBUG_ERROR("Failed to parse SIP 'Contact' header.");
+ TSK_OBJECT_SAFE_FREE(curr_contact);
+ TSK_OBJECT_SAFE_FREE(hdr_contacts);
+ }
+
+ return hdr_contacts;
+}
+
+
+
+
+//========================================================
+// Contact header object definition
+//
+
+static tsk_object_t* tsip_header_Contact_ctor(tsk_object_t *self, va_list * app)
+{
+ tsip_header_Contact_t *Contact = self;
+ if(Contact){
+ TSIP_HEADER(Contact)->type = tsip_htype_Contact;
+ TSIP_HEADER(Contact)->serialize = tsip_header_Contact_serialize;
+ Contact->expires = -1;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create new Contact header.");
+ }
+ return self;
+}
+
+static tsk_object_t* tsip_header_Contact_dtor(tsk_object_t *self)
+{
+ tsip_header_Contact_t *Contact = self;
+ if(Contact){
+ TSK_FREE(Contact->display_name);
+ TSK_OBJECT_SAFE_FREE(Contact->uri);
+
+ TSK_OBJECT_SAFE_FREE(TSIP_HEADER_PARAMS(Contact));
+ }
+ else{
+ TSK_DEBUG_ERROR("Null Contact header.");
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tsip_header_Contact_def_s =
+{
+ sizeof(tsip_header_Contact_t),
+ tsip_header_Contact_ctor,
+ tsip_header_Contact_dtor,
+ tsk_null
+};
+const tsk_object_def_t *tsip_header_Contact_def_t = &tsip_header_Contact_def_s;
+
+
+////========================================================
+//// Contact object definition
+////
+//
+///**@ingroup tsip_header_Contact_group
+//*/
+//static tsk_object_t* tsip_contact_ctor(tsk_object_t *self, va_list * app)
+//{
+// tsip_contact_t *contact = self;
+// if(contact)
+// {
+// contact->expires = -1;
+// }
+// else
+// {
+// TSK_DEBUG_ERROR("Failed to create new Contact object.");
+// }
+// return self;
+//}
+//
+//static tsk_object_t* tsip_contact_dtor(tsk_object_t *self)
+//{
+// tsip_contact_t *contact = self;
+// if(contact)
+// {
+// TSK_FREE(contact->display_name);
+// TSK_OBJECT_SAFE_FREE(TSIP_HEADER_PARAMS(contact));
+//
+// TSK_OBJECT_SAFE_FREE(contact->uri);
+// }
+// else TSK_DEBUG_ERROR("Null Contact object.");
+//
+// return self;
+//}
+//
+//static const tsk_object_def_t tsip_contact_def_s =
+//{
+// sizeof(tsip_contact_t),
+// tsip_contact_ctor,
+// tsip_contact_dtor,
+// 0
+//};
+//const void *tsip_contact_def_t = &tsip_contact_def_s;
diff --git a/tinySIP/src/headers/tsip_header_Content_Disposition.c b/tinySIP/src/headers/tsip_header_Content_Disposition.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Content_Disposition.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header_Content_Encoding.c b/tinySIP/src/headers/tsip_header_Content_Encoding.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Content_Encoding.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header_Content_Language.c b/tinySIP/src/headers/tsip_header_Content_Language.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Content_Language.c
@@ -0,0 +1 @@
+
diff --git a/tinySIP/src/headers/tsip_header_Content_Length.c b/tinySIP/src/headers/tsip_header_Content_Length.c
new file mode 100644
index 0000000..d209be8
--- /dev/null
+++ b/tinySIP/src/headers/tsip_header_Content_Length.c
@@ -0,0 +1,325 @@
+
+/* #line 1 "./ragel/tsip_parser_header_Content_Length.rl" */
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
+*
+* 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_header_Content_Length.c
+ * @brief SIP Content-Length/l header.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
+ *
+
+ */
+#include "tinysip/headers/tsip_header_Content_Length.h"
+
+#include "tsk_debug.h"
+#include "tsk_memory.h"
+
+
+
+/***********************************
+* Ragel state machine.
+*/
+
+/* #line 62 "./ragel/tsip_parser_header_Content_Length.rl" */
+
+
+
+tsip_header_Content_Length_t* tsip_header_Content_Length_create(uint32_t length)
+{
+ return tsk_object_new(TSIP_HEADER_CONTENT_LENGTH_VA_ARGS(length));
+}
+
+tsip_header_Content_Length_t* tsip_header_Content_Length_create_null()
+{
+ return tsip_header_Content_Length_create(0);
+}
+
+int tsip_header_Content_Length_serialize(const tsip_header_t* header, tsk_buffer_t* output)
+{
+ if(header){
+ const tsip_header_Content_Length_t *Content_Length = (const tsip_header_Content_Length_t *)header;
+ return tsk_buffer_append_2(output, "%u", Content_Length->length);
+ }
+
+ return -1;
+}
+
+tsip_header_Content_Length_t *tsip_header_Content_Length_parse(const char *data, tsk_size_t size)
+{
+ int cs = 0;
+ const char *p = data;
+ const char *pe = p + size;
+ const char *eof = pe;
+ tsip_header_Content_Length_t *hdr_clength = tsip_header_Content_Length_create(0);
+
+ const char *tag_start = tsk_null;
+
+
+/* #line 79 "./src/headers/tsip_header_Content_Length.c" */
+static const char _tsip_machine_parser_header_Content_Length_actions[] = {
+ 0, 1, 0, 1, 1, 1, 2
+};
+
+static const char _tsip_machine_parser_header_Content_Length_key_offsets[] = {
+ 0, 0, 4, 6, 8, 10, 12, 14,
+ 16, 17, 19, 21, 23, 25, 27, 29,
+ 32, 37, 38, 40, 44, 47, 48
+};
+
+static const char _tsip_machine_parser_header_Content_Length_trans_keys[] = {
+ 67, 76, 99, 108, 79, 111, 78, 110,
+ 84, 116, 69, 101, 78, 110, 84, 116,
+ 45, 76, 108, 69, 101, 78, 110, 71,
+ 103, 84, 116, 72, 104, 9, 32, 58,
+ 9, 13, 32, 48, 57, 10, 9, 32,
+ 9, 32, 48, 57, 13, 48, 57, 10,
+ 0
+};
+
+static const char _tsip_machine_parser_header_Content_Length_single_lengths[] = {
+ 0, 4, 2, 2, 2, 2, 2, 2,
+ 1, 2, 2, 2, 2, 2, 2, 3,
+ 3, 1, 2, 2, 1, 1, 0
+};
+
+static const char _tsip_machine_parser_header_Content_Length_range_lengths[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 1, 1, 0, 0
+};
+
+static const char _tsip_machine_parser_header_Content_Length_index_offsets[] = {
+ 0, 0, 5, 8, 11, 14, 17, 20,
+ 23, 25, 28, 31, 34, 37, 40, 43,
+ 47, 52, 54, 57, 61, 64, 66
+};
+
+static const char _tsip_machine_parser_header_Content_Length_indicies[] = {
+ 0, 2, 0, 2, 1, 3, 3, 1,
+ 4, 4, 1, 5, 5, 1, 6, 6,
+ 1, 7, 7, 1, 8, 8, 1, 9,
+ 1, 10, 10, 1, 11, 11, 1, 12,
+ 12, 1, 13, 13, 1, 14, 14, 1,
+ 2, 2, 1, 2, 2, 15, 1, 15,
+ 16, 15, 17, 1, 18, 1, 19, 19,
+ 1, 19, 19, 17, 1, 20, 21, 1,
+ 22, 1, 1, 0
+};
+
+static const char _tsip_machine_parser_header_Content_Length_trans_targs[] = {
+ 2, 0, 15, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 16,
+ 17, 20, 18, 19, 21, 20, 22
+};
+
+static const char _tsip_machine_parser_header_Content_Length_trans_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 3, 0, 5
+};
+
+static const int tsip_machine_parser_header_Content_Length_start = 1;
+static const int tsip_machine_parser_header_Content_Length_first_final = 22;
+static const int tsip_machine_parser_header_Content_Length_error = 0;
+
+static const int tsip_machine_parser_header_Content_Length_en_main = 1;
+
+
+/* #line 96 "./ragel/tsip_parser_header_Content_Length.rl" */
+ (void)(eof);
+ (void)(tsip_machine_parser_header_Content_Length_first_final);
+ (void)(tsip_machine_parser_header_Content_Length_error);
+ (void)(tsip_machine_parser_header_Content_Length_en_main);
+
+/* #line 155 "./src/headers/tsip_header_Content_Length.c" */
+ {
+ cs = tsip_machine_parser_header_Content_Length_start;
+ }
+
+/* #line 101 "./ragel/tsip_parser_header_Content_Length.rl" */
+
+/* #line 162 "./src/headers/tsip_header_Content_Length.c" */
+ {
+ int _klen;
+ unsigned int _trans;
+ const char *_acts;
+ unsigned int _nacts;
+ const char *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( cs == 0 )
+ goto _out;
+_resume:
+ _keys = _tsip_machine_parser_header_Content_Length_trans_keys + _tsip_machine_parser_header_Content_Length_key_offsets[cs];
+ _trans = _tsip_machine_parser_header_Content_Length_index_offsets[cs];
+
+ _klen = _tsip_machine_parser_header_Content_Length_single_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + _klen - 1;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( (*p) < *_mid )
+ _upper = _mid - 1;
+ else if ( (*p) > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _tsip_machine_parser_header_Content_Length_range_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + (_klen<<1) - 2;