doubango/tinyDAV/src/bfcp/tdav_session_bfcp.c
c732d49e
 #if HAVE_CRT
 #define _CRTDBG_MAP_ALLOC 
 #include <stdlib.h> 
 #include <crtdbg.h>
 #endif //HAVE_CRT
 /*
74ca6d11
 * Copyright (C) 2020, University of the Basque Country (UPV/EHU)
c732d49e
 * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com>
 *
 * The original file was part of Open Source Doubango Framework
 * Copyright (C) 2010-2011 Mamadou Diop.
 * Copyright (C) 2012 Doubango Telecom <http://doubango.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 tdav_session_bfcp.c
  * @brief The The Binary Floor Control Protocol (BFCP, rfc4582) session.
  */
 #include "tinydav/bfcp/tdav_session_bfcp.h"
 
 #if !defined(HAVE_TINYBFCP) || HAVE_TINYBFCP
 
 #include "tinybfcp/tbfcp_session.h"
 #include "tinybfcp/tbfcp_pkt.h"
 #include "tinybfcp/tbfcp_utils.h"
 
 #include "tsk_string.h"
 #include "tsk_memory.h" /* TSK_FREE */
 #include "tsk_debug.h"
 
 /*
 	* https://tools.ietf.org/html/rfc4574
 	* https://tools.ietf.org/html/rfc4582
 	* https://tools.ietf.org/html/rfc4583
 	* http://tools.ietf.org/html/rfc4796
 	* https://tools.ietf.org/html/draft-ietf-bfcpbis-rfc4582bis-1
 */
 
 typedef struct tdav_session_bfcp_s
 {
 	TMEDIA_DECLARE_SESSION_BFCP;
 
 	struct tbfcp_session_s* p_bfcp_s;
 	struct tbfcp_pkt_s* p_pkt_FloorRequest;
 	struct tbfcp_pkt_s* p_pkt_FloorRelease;
 	struct tbfcp_pkt_s* p_pkt_Hello;
 
 	tsk_bool_t b_started;
 	tsk_bool_t b_use_ipv6;
 	tsk_bool_t b_revoked_handled;
 	tsk_bool_t b_conf_idf_changed;
 	tsk_bool_t b_stop_to_reconf;
 
 	char* p_local_ip;
 	//uint16_t local_port;
 
 	/* NAT Traversal context */
 	struct tnet_nat_ctx_s* p_natt_ctx;
 
 	char* p_remote_ip;
 	uint16_t u_remote_port;
 
 	// https://tools.ietf.org/html/rfc4583 attributes
 	struct {
 		char* confid;
 		char* floorid;
 		char* mstrm;
 		char* userid;
 	} rfc4583;
 }
 tdav_session_bfcp_t;
 
 static int _tdav_session_bfcp_notif(const struct tbfcp_session_event_xs *e);
 static int _tdav_session_bfcp_send_Hello(tdav_session_bfcp_t* p_bfcp);
 
 /* ============ Plugin interface ================= */
 
 static int _tdav_session_bfcp_set(tmedia_session_t* p_self, const tmedia_param_t* param)
 {
 	int ret = 0;
 	tdav_session_bfcp_t* p_bfcp;
 
 	if (!p_self) {
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 
 	TSK_DEBUG_INFO("tdav_session_bfcp_set");
 
 	p_bfcp = (tdav_session_bfcp_t*)p_self;
 
 	if (param->value_type == tmedia_pvt_pchar) {
 		if (tsk_striequals(param->key, "remote-ip")) {
 			// only if no ip associated to the "m=" line
 			if (param->value && !p_bfcp->p_remote_ip) {
 				p_bfcp->p_remote_ip = tsk_strdup(param->value);
 			}
 		}
 		else if (tsk_striequals(param->key, "local-ip")) {
 			tsk_strupdate(&p_bfcp->p_local_ip, param->value);
 		}
 		else if (tsk_striequals(param->key, "local-ipver")) {
 			p_bfcp->b_use_ipv6 = tsk_striequals(param->value, "ipv6");
 		}
 	}
 	else if (param->value_type == tmedia_pvt_pobject) {
 		if (tsk_striequals(param->key, "natt-ctx")) {
 			TSK_OBJECT_SAFE_FREE(p_bfcp->p_natt_ctx);
 			p_bfcp->p_natt_ctx = tsk_object_ref(param->value);
 		}
 	}
 	else if (param->value_type == tmedia_pvt_int32) {
 		if (tsk_striequals(param->key, "stop-to-reconf")) {
 			p_bfcp->b_stop_to_reconf = TSK_TO_INT32((uint8_t*)param->value) ? tsk_true : tsk_false;
 		}
 	}
 	
 
 	return ret;
 }
 
 static int _tdav_session_bfcp_get(tmedia_session_t* p_self, tmedia_param_t* param)
 {
 	int ret = 0;
 	tdav_session_bfcp_t* p_bfcp;
 
 	if (!p_self) {
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 
 	TSK_DEBUG_INFO("_tdav_session_bfcp_get");
 
 	p_bfcp = (tdav_session_bfcp_t*)p_self;
 
 	return ret;
 }
 
 static int _tdav_session_bfcp_prepare(tmedia_session_t* p_self)
 {
 	int ret = 0;
 	tdav_session_bfcp_t* p_bfcp;
 
 	if (!p_self) {
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 
 	TSK_DEBUG_INFO("_tdav_session_bfcp_prepare");
 	
 	p_bfcp = (tdav_session_bfcp_t*)p_self;
 
 	if (!p_bfcp->p_bfcp_s) {
 		enum tnet_socket_type_e e_socket_type = kBfcpTransportDefault;
 		if ((ret = tbfcp_session_create(e_socket_type, p_bfcp->p_local_ip, &p_bfcp->p_bfcp_s))) {
 			return ret;
 		}
 	}
 	if ((ret = tbfcp_session_set_natt_ctx(p_bfcp->p_bfcp_s, p_bfcp->p_natt_ctx))) {
 		return ret;
 	}
 	if ((ret = tbfcp_session_prepare(p_bfcp->p_bfcp_s))) {
 		return ret;
 	}
 	if ((ret = tbfcp_session_set_callback(p_bfcp->p_bfcp_s, _tdav_session_bfcp_notif, p_bfcp))) {
 		return ret;
 	}
 	TSK_OBJECT_SAFE_FREE(p_bfcp->p_pkt_Hello);
 	TSK_OBJECT_SAFE_FREE(p_bfcp->p_pkt_FloorRequest);
 	TSK_OBJECT_SAFE_FREE(p_bfcp->p_pkt_FloorRelease);
 	p_bfcp->b_revoked_handled = tsk_false;
 
 	return ret;
 }
 
 static int _tdav_session_bfcp_start(tmedia_session_t* p_self)
 {
 	int ret = 0;
 	tdav_session_bfcp_t* p_bfcp;
 
 	if (!p_self) {
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 
 	TSK_DEBUG_INFO("_tdav_session_bfcp_start");
 
 	p_bfcp = (tdav_session_bfcp_t*)p_self;
 
 	if ((ret = tbfcp_session_set_remote_address(p_bfcp->p_bfcp_s, p_bfcp->p_remote_ip, p_bfcp->u_remote_port))) {
 		return ret;
 	}
 	if ((ret = tbfcp_session_start(p_bfcp->p_bfcp_s))) {
 		return ret;
 	}
 	if ((ret = _tdav_session_bfcp_send_Hello(p_bfcp))) {
 		return ret;
 	}
 
 	p_bfcp->b_started = tsk_true;
 
 	return ret;
 }
 
 static int _tdav_session_bfcp_pause(tmedia_session_t* p_self)
 {
 	int ret = 0;
 	tdav_session_bfcp_t* p_bfcp;
 
 	if (!p_self) {
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 
 	TSK_DEBUG_INFO("_tdav_session_bfcp_pause");
 
 	p_bfcp = (tdav_session_bfcp_t*)p_self;
 
 	if (p_bfcp->p_bfcp_s && (ret = tbfcp_session_pause(p_bfcp->p_bfcp_s))) {
 		return ret;
 	}
 
 	return ret;
 }
 
 static int _tdav_session_bfcp_stop(tmedia_session_t* p_self)
 {
 	int ret = 0;
 	tdav_session_bfcp_t* p_bfcp;
 
 	if (!p_self) {
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 
 	TSK_DEBUG_INFO("_tdav_session_bfcp_stop");
 
 	p_bfcp = (tdav_session_bfcp_t*)p_self;
 
 	if (!p_bfcp->b_stop_to_reconf) { // If stop-to-reconf then do not release the FloorRequest but reuse it
 		if (p_bfcp->b_started) {
 			/*if (p_bfcp->p_bfcp_s)*/ {
 				/*if (!p_bfcp->p_pkt_FloorRelease) {
 					ret = tbfcp_session_create_pkt_FloorRelease(p_bfcp->p_bfcp_s, &p_bfcp->p_pkt_FloorRelease);
 				}
 				if (ret == 0 && p_bfcp->p_pkt_FloorRelease && (ret = tbfcp_session_send_pkt(p_bfcp->p_bfcp_s, p_bfcp->p_pkt_FloorRelease))) {
 					//!\ do not exit
 				}*/
 			}
 		}
 		tsk_strupdate(&p_bfcp->rfc4583.confid, "");
 	}
 
 	if (p_bfcp->p_bfcp_s) {
 		ret = tbfcp_session_stop(p_bfcp->p_bfcp_s);
 	}
 
 	p_bfcp->b_started = tsk_false;
 	p_bfcp->b_stop_to_reconf = tsk_false; // reset
 
 	return ret;
 }
 
 static const tsdp_header_M_t* _tdav_session_bfcp_get_lo(tmedia_session_t* p_self)
 {
 	tdav_session_bfcp_t* p_bfcp;
 	tsk_bool_t b_changed = tsk_false;
 	const char *pc_local_ip, *pc_local_profile, *pc_local_role, *pc_local_setup;
 	tnet_port_t u_local_port;
 	enum tbfcp_role_e e_local_role;
 	enum tbfcp_setup_e e_local_setup;
 	int ret;
 
 	TSK_DEBUG_INFO("_tdav_session_bfcp_get_lo");
 
 	if (!p_self || !p_self->plugin) {
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return tsk_null;
 	}
 	
 	p_bfcp = (tdav_session_bfcp_t*)p_self;
 
 	b_changed = (p_self->ro_changed || !p_self->M.lo);
 
 	if (!b_changed) {
 		TSK_DEBUG_INFO("No changes to the BFCP session...skip SDP update");
 		return p_self->M.lo;
 	}
 
 	if (b_changed && p_self->M.lo) {
 		static const char* __fields[] = { "floorctrl", "setup", "connection", "curr", "des", "conf" };
 		// remove media-level attributes
 		tsdp_header_A_removeAll_by_fields(p_self->M.lo->Attributes, __fields, sizeof(__fields)/sizeof(__fields[0]));
 		// Codec list never change and FMTs always a single star (*) value. Radvision TelePresence System reject a BFCP session whithout the single FMT (*)
 		// The Codecs and formats are never rebuilt which means we must not clear them
 #if 0
 		tsk_list_clear_items(p_self->M.lo->FMTs);
 #endif
 	}
 
 	// get local address
 	if ((ret = tbfcp_session_get_local_address(p_bfcp->p_bfcp_s, &pc_local_ip, &u_local_port))) {
 		TSK_DEBUG_ERROR("Failed to get local address from BFCP session");
 		return tsk_null;
 	}
 	// get local profile
 	if ((ret = tbfcp_session_get_profile(p_bfcp->p_bfcp_s, &pc_local_profile))) {
 		TSK_DEBUG_ERROR("Failed to get local profile from BFCP session");
 		return tsk_null;
 	}
 	// get local role
 	if ((ret = tbfcp_session_get_local_role(p_bfcp->p_bfcp_s, &e_local_role))) {
 		TSK_DEBUG_ERROR("Failed to get local role from BFCP session");
 		return tsk_null;
 	}
 	if ((ret = tbfcp_utils_get_role(e_local_role, &pc_local_role))) {
 		return tsk_null;
 	}
 	// get local setup
 	if ((ret = tbfcp_session_get_local_setup(p_bfcp->p_bfcp_s, &e_local_setup))) {
 		TSK_DEBUG_ERROR("Failed to get local setup from BFCP session");
 		return tsk_null;
 	}
 	if ((ret = tbfcp_utils_get_setup(e_local_role, &pc_local_setup))) {
 		return tsk_null;
 	}
 
 	if (!p_self->M.lo){
 		if (!(p_self->M.lo = tsdp_header_M_create(p_self->plugin->media, u_local_port, pc_local_profile))) {
 			TSK_DEBUG_ERROR("Failed to create BFCP SDP media header");
 			return tsk_null;
 		}
 		tsdp_header_M_add_headers(p_self->M.lo,
 				TSDP_FMT_VA_ARGS("*"),
 				TSDP_HEADER_A_VA_ARGS("connection", "new"),
 				TSDP_HEADER_A_VA_ARGS("lib", "tinyBFCP"),
 				tsk_null);
 		// If NATT is active, do not rely on the global IP address Connection line
 		if (p_bfcp->p_natt_ctx)  {
 			tsdp_header_M_add_headers(p_self->M.lo,
 				TSDP_HEADER_C_VA_ARGS("IN", p_bfcp->b_use_ipv6 ? "IP6" : "IP4", pc_local_ip),
 				tsk_null);
 		}
 	}
 	else {
 		p_self->M.lo->port = u_local_port;
 		tsk_strupdate(&p_self->M.lo->proto, pc_local_profile);
 	}
 
 	// add "floorctrl" and "setup" attributes
 	tsdp_header_M_add_headers(p_self->M.lo,
 				TSDP_HEADER_A_VA_ARGS("connection", "new"),
 				TSDP_HEADER_A_VA_ARGS("floorctrl", pc_local_role),
 				TSDP_HEADER_A_VA_ARGS("setup", pc_local_setup),
 				tsk_null);
 
 	return p_self->M.lo;
 }
 
 static int _tdav_session_bfcp_set_ro(tmedia_session_t* p_self, const tsdp_header_M_t* m)
 {
 	int ret = 0;
 	const tsdp_header_A_t* A;
 	tdav_session_bfcp_t* p_bfcp;
 	enum tbfcp_role_e e_remote_role = tbfcp_role_c_s;
 	uint32_t u_remote_conf_id = 0xFFFF;
 	uint16_t u_remote_user_id = 0xFFFF, u_remote_floor_id = 0xFFFF;
 
 	if (!p_self || !m) {
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 
 	TSK_DEBUG_INFO("_tdav_session_bfcp_set_ro");
 
 	p_bfcp = (tdav_session_bfcp_t*)p_self;
 
 	/* update remote offer */
 	TSK_OBJECT_SAFE_FREE(p_self->M.ro);
 	p_self->M.ro = tsk_object_ref(TSK_OBJECT(m));
 
 
 	// https://tools.ietf.org/html/rfc4583
 	{
 		p_bfcp->b_conf_idf_changed = tsk_false;
 		if ((A = tsdp_header_M_findA(m, "floorctrl"))) {
 			if ((ret = tbfcp_utils_parse_role(A->value, &e_remote_role))) {
 				return ret;
 			}
 		}
 		if ((A = tsdp_header_M_findA(m, "confid"))) {
 			p_bfcp->b_conf_idf_changed |= !tsk_striequals(p_bfcp->rfc4583.confid, A->value);
 			tsk_strupdate(&p_bfcp->rfc4583.confid, A->value);
 			u_remote_conf_id = (uint32_t)tsk_atoi64(p_bfcp->rfc4583.confid);
 		}
 		if ((A = tsdp_header_M_findA(m, "userid"))) {
 			p_bfcp->b_conf_idf_changed |= !tsk_striequals(p_bfcp->rfc4583.userid, A->value);
 			tsk_strupdate(&p_bfcp->rfc4583.userid, A->value);
 			u_remote_user_id = (uint16_t)tsk_atoi64(p_bfcp->rfc4583.userid);
 		}
 		if ((A = tsdp_header_M_findA(m, "floorid"))) {
 			char tmp_str[256];
 			if (sscanf(A->value, "%255s %*s", tmp_str) != EOF) {
 				char *pch, *saveptr;
 				p_bfcp->b_conf_idf_changed |= !tsk_striequals(p_bfcp->rfc4583.floorid, tmp_str);
 				tsk_strupdate(&p_bfcp->rfc4583.floorid, tmp_str);
 				u_remote_floor_id = (uint16_t)tsk_atoi64(p_bfcp->rfc4583.floorid);
 				pch = tsk_strtok_r(&A->value[tsk_strlen(tmp_str) + 1], " ", &saveptr);
 				while (pch) {
 					if (sscanf(pch, "mstrm: %255s", tmp_str) != EOF) {
 						p_bfcp->b_conf_idf_changed |= !tsk_striequals(p_bfcp->rfc4583.mstrm, tmp_str);
 						tsk_strupdate(&p_bfcp->rfc4583.mstrm, tmp_str);
 						break;
 					}
 					pch = tsk_strtok_r(tsk_null, " ", &saveptr);
 				}
 			}
 		}
 		// set remote role
 		if ((ret = tbfcp_session_set_remote_role(p_bfcp->p_bfcp_s, e_remote_role))) {
 			return ret;
 		}
 		if ((e_remote_role & tbfcp_role_s_only)) {
 			// local = client
 			if ((ret = tbfcp_session_set_conf_ids(p_bfcp->p_bfcp_s, u_remote_conf_id, u_remote_user_id, u_remote_floor_id))) {
 				return ret;
 			}
 		}
 		else {
 			// local = remote: Not supported yet and will never happen
 		}
 	}//end-of-rfc4583
 
 	/* get connection associated to this media line
 	* If the connnection is global, then the manager will call tdav_session_audio_set() */
 	if (m->C && m->C->addr) {
 		tsk_strupdate(&p_bfcp->p_remote_ip, m->C->addr);
 		p_bfcp->b_use_ipv6 = tsk_striequals(m->C->addrtype, "IP6");
 	}
 	/* set remote port */
 	p_bfcp->u_remote_port = m->port;
 
 	return ret;
 }
 
 static int _tdav_session_bfcp_send_Hello(tdav_session_bfcp_t* p_bfcp)
 {
 	int ret = 0;
 	if (!p_bfcp) {
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 
 	if (!p_bfcp->p_pkt_Hello && (ret = tbfcp_session_create_pkt_Hello(p_bfcp->p_bfcp_s, &p_bfcp->p_pkt_Hello))) {
 		return ret;
 	}
 	if ((ret = tbfcp_session_send_pkt(p_bfcp->p_bfcp_s, p_bfcp->p_pkt_Hello))) {
 		return ret;
 	}
 	return ret;
 }
 
 static int _tdav_session_bfcp_notif(const struct tbfcp_session_event_xs *e)
 {
 	tdav_session_bfcp_t* p_bfcp = tsk_object_ref(TSK_OBJECT(e->pc_usr_data));
 	int ret = 0;
 	static const char* kErrTextGlobalError = "Global error";
 	static const int kErrCodeGlobalError = -56;
 	static const char* kErrTextTimeout = "Timeout";
 	static const int kErrCodeTimeout = -57;
 	static const char* kErrTextUnExpectedIncomingMsg = "Unexpected incoming BFCP message";
 	static const int kErrCodeUnExpectedIncomingMsg = -58;
 	static const char* kErrTextBadRequest = "Bad Request";
 	static const int kErrCodeBadRequest = -59;
 	static const char* kInfoTextFloorReqStatus = "FloorRequestStatus";
 
 #define _RAISE_ERR_AND_GOTO_BAIL(_code, _reason) \
 		if (TMEDIA_SESSION(p_bfcp)->bfcp_cb.fun) { \
 			tmedia_session_bfcp_evt_xt e; \
 			e.type = tmedia_session_bfcp_evt_type_err; e.err.code = _code; e.reason = _reason; \
 			TMEDIA_SESSION(p_bfcp)->bfcp_cb.fun(TMEDIA_SESSION(p_bfcp)->bfcp_cb.usrdata, TMEDIA_SESSION(p_bfcp), &e); \
 		} \
 		ret = _code; goto bail;
 #define _RAISE_FLREQ(_status, _reason) \
 		if (TMEDIA_SESSION(p_bfcp)->bfcp_cb.fun) { \
 			tmedia_session_bfcp_evt_xt e; \
 			e.type = tmedia_session_bfcp_evt_type_flreq_status; e.flreq.status = _status; e.reason = _reason; \
 			TMEDIA_SESSION(p_bfcp)->bfcp_cb.fun(TMEDIA_SESSION(p_bfcp)->bfcp_cb.usrdata, TMEDIA_SESSION(p_bfcp), &e); \
 		} \
 
 	switch (e->e_type) {
 		case tbfcp_session_event_type_inf_inc_msg:
 			{
 				if (p_bfcp->p_pkt_Hello && p_bfcp->p_pkt_Hello->hdr.transac_id == e->pc_pkt->hdr.transac_id && p_bfcp->p_pkt_Hello->hdr.user_id == e->pc_pkt->hdr.user_id && p_bfcp->p_pkt_Hello->hdr.conf_id == e->pc_pkt->hdr.conf_id) {
 					TSK_OBJECT_SAFE_FREE(p_bfcp->p_pkt_Hello);
 					if (e->pc_pkt->hdr.primitive == tbfcp_primitive_HelloAck) {
 						if (!p_bfcp->p_pkt_FloorRequest) {
 							if (p_bfcp->b_conf_idf_changed || 0) {
 								// Create the "FloorRelease" for this "FloorRequest"
 								TSK_OBJECT_SAFE_FREE(p_bfcp->p_pkt_FloorRelease);
 								if ((ret = tbfcp_session_create_pkt_FloorRelease(p_bfcp->p_bfcp_s, &p_bfcp->p_pkt_FloorRelease))) {
 									goto raise_err;
 								}
 								if ((ret = tbfcp_session_create_pkt_FloorRequest(p_bfcp->p_bfcp_s, &p_bfcp->p_pkt_FloorRequest))) {
 									goto raise_err;
 								}
 								if ((ret = tbfcp_session_send_pkt(p_bfcp->p_bfcp_s, p_bfcp->p_pkt_FloorRequest))) {
 									goto raise_err;
 								}
 							}
 							else {
 								TSK_DEBUG_INFO("No change to BFCP session... do not send FloorRequest");
 							}
 						}
 					}
 					else {
 						TSK_DEBUG_ERROR("%s", kErrTextUnExpectedIncomingMsg);
 						_RAISE_ERR_AND_GOTO_BAIL(kErrCodeUnExpectedIncomingMsg, kErrTextUnExpectedIncomingMsg);
 					}
 				}
 				else if(p_bfcp->p_pkt_FloorRequest /*&& p_bfcp->p_pkt_FloorRequest->hdr.transac_id == e->pc_pkt->hdr.transac_id*/ && p_bfcp->p_pkt_FloorRequest->hdr.user_id == e->pc_pkt->hdr.user_id && p_bfcp->p_pkt_FloorRequest->hdr.conf_id == e->pc_pkt->hdr.conf_id) {
 					tsk_bool_t transac_id_matched = (p_bfcp->p_pkt_FloorRequest->hdr.transac_id == e->pc_pkt->hdr.transac_id);
 					if (e->pc_pkt->hdr.primitive == tbfcp_primitive_FloorRequestStatus || e->pc_pkt->hdr.primitive == tbfcp_primitive_FloorStatus) {
 						tsk_size_t u_index0, u_index1, u_index2, u_index3;
 						const tbfcp_attr_grouped_t *pc_attr_FloorRequestInformation = tsk_null, 
 							*pc_attr_FloorRequestStatus = tsk_null, 
 							*pc_attr_OverallRequestStatus = tsk_null;
 						const tbfcp_attr_octetstring16_t *pc_attr_RequestStatus = tsk_null;
 
 						u_index0 = 0;
 						// Find "FloorRequestInformation"
 						while ((ret = tbfcp_pkt_attr_find_at(e->pc_pkt, tbfcp_attribute_format_Grouped, u_index0++, (const tbfcp_attr_t **)&pc_attr_FloorRequestInformation)) == 0 && pc_attr_FloorRequestInformation) {
 							if (TBFCP_ATTR(pc_attr_FloorRequestInformation)->hdr.type != tbfcp_attribute_type_FLOOR_REQUEST_INFORMATION) {
 								continue;
 							}
 							// Find "FloorRequestStatus"
 							u_index1 = 0;
 							while ((ret = tbfcp_attr_grouped_find_at(pc_attr_FloorRequestInformation, tbfcp_attribute_format_Grouped, u_index1++, (const tbfcp_attr_t **)&pc_attr_FloorRequestStatus)) == 0 && pc_attr_FloorRequestStatus) {
 								if (TBFCP_ATTR(pc_attr_FloorRequestStatus)->hdr.type != tbfcp_attribute_type_FLOOR_REQUEST_STATUS) {
 									continue;
 								}
 								if (pc_attr_FloorRequestStatus->extra_hdr.FloorID != atoi(p_bfcp->rfc4583.floorid)) {
 									continue;
 								}
 								break;
 							}
 							if (!pc_attr_FloorRequestStatus) {
 								continue;
 							}
 							// Find "OverallRequestStatus"
 							u_index2 = 0;
 							while ((ret = tbfcp_attr_grouped_find_at(pc_attr_FloorRequestInformation, tbfcp_attribute_format_Grouped, u_index2++, (const tbfcp_attr_t **)&pc_attr_OverallRequestStatus)) == 0 && pc_attr_OverallRequestStatus) {
 								if (TBFCP_ATTR(pc_attr_OverallRequestStatus)->hdr.type != tbfcp_attribute_type_OVERALL_REQUEST_STATUS) {
 									continue;
 								}
 
 								// Find "RequestStatus"
 								u_index3 = 0;
 								while ((ret = tbfcp_attr_grouped_find_at(pc_attr_OverallRequestStatus, tbfcp_attribute_format_OctetString16, u_index3++, (const tbfcp_attr_t **)&pc_attr_RequestStatus)) == 0 && pc_attr_RequestStatus) {
 									if (TBFCP_ATTR(pc_attr_RequestStatus)->hdr.type != tbfcp_attribute_type_REQUEST_STATUS) {
 										continue;
 									}
 									break;
 								}
 							}
 							if (pc_attr_RequestStatus) {
 								break;
 							}
 						}
 
 						if (pc_attr_RequestStatus) { 
 							// https://tools.ietf.org/html/rfc4582#section-5.2.5
 							uint16_t u_status = pc_attr_RequestStatus->OctetString16[0] + (pc_attr_RequestStatus->OctetString16[1] << 8);
 							if (transac_id_matched) {
 								if (u_status == tbfcp_reqstatus_Revoked && !p_bfcp->b_revoked_handled) { // revoked
 									TSK_OBJECT_SAFE_FREE(p_bfcp->p_pkt_FloorRequest); // free the FloorRequest and ask new one once HelloAck is received
 									// Radvision sends a Revoke after a reINVITE to ask for new negotiation.
 									if (p_bfcp->p_pkt_FloorRelease) {
 										if ((ret = tbfcp_session_send_pkt(p_bfcp->p_bfcp_s, p_bfcp->p_pkt_FloorRelease))) {
 											goto raise_err;
 										}
 									}
 									if ((ret = _tdav_session_bfcp_send_Hello(p_bfcp))) {
 										goto raise_err;
 									}
 									p_bfcp->b_revoked_handled = tsk_true;
 								}
 								else {
 									_RAISE_FLREQ(u_status, kInfoTextFloorReqStatus);
 								}
 							}
 							else { //!transac_id_matched
 								// Status from old FloorRequest
 								tbfcp_pkt_t* p_pkt = tsk_null;
 								TSK_DEBUG_INFO("Status from old Request");
 								if (u_status == tbfcp_reqstatus_Pending || u_status == tbfcp_reqstatus_Accepted || u_status == tbfcp_reqstatus_Granted) {
 									if ((ret = tbfcp_pkt_create_FloorRelease_2(e->pc_pkt->hdr.conf_id, e->pc_pkt->hdr.transac_id, e->pc_pkt->hdr.user_id, pc_attr_FloorRequestStatus->extra_hdr.FloorID, &p_pkt))) {
 										goto raise_err;
 									}
 									ret = tbfcp_session_send_pkt(p_bfcp->p_bfcp_s, p_pkt);
 									TSK_OBJECT_SAFE_FREE(p_pkt);
 									if (ret) {
 										goto raise_err;
 									}
 								}
 							}
 						}
 						else {
 							/* /!\ No RequestStatus attribute in FloorRequestStatus */
 							TSK_OBJECT_SAFE_FREE(p_bfcp->p_pkt_FloorRequest);
 							TSK_DEBUG_ERROR("%s", kErrTextBadRequest);
 							_RAISE_ERR_AND_GOTO_BAIL(kErrCodeBadRequest, kErrTextBadRequest);
 						}						
 					}
 					else {
 						switch (e->pc_pkt->hdr.primitive) {
 							case tbfcp_primitive_Hello: break; // already handled in "_tbfcp_session_process_incoming_pkt()"
 							default:
 								{
 									TSK_DEBUG_ERROR("%s", kErrTextUnExpectedIncomingMsg);
 									_RAISE_ERR_AND_GOTO_BAIL(kErrCodeUnExpectedIncomingMsg, kErrTextUnExpectedIncomingMsg);
 									break;
 								}
 						}
 					}
 				}
 				break;
 			}
 		case tbfcp_session_event_type_err_send_timedout:
 			{
 				/* /!\ Sending BFCP message timedout */
 				TSK_DEBUG_ERROR("%s", kErrTextTimeout);
 				_RAISE_ERR_AND_GOTO_BAIL(kErrCodeTimeout, kErrTextTimeout);
 				break;
 			}
 	}
 raise_err:
 	if (ret) {
 		TSK_DEBUG_ERROR("%s", kErrTextGlobalError);
 		_RAISE_ERR_AND_GOTO_BAIL(kErrCodeGlobalError, kErrTextGlobalError);
 	}
 bail:
 
 	TSK_OBJECT_SAFE_FREE(p_bfcp);
 	return ret;
 }
 
 
 /* ============ Public functions ================= */
 
 
 
 
 
 //=================================================================================================
 //	Session MSRp Plugin object definition
 //
 /* constructor */
 static tsk_object_t* _tdav_session_bfcp_ctor(tsk_object_t * p_self, va_list * app)
 {
 	tdav_session_bfcp_t *p_session = (tdav_session_bfcp_t *)p_self;
 	if (p_session) {
 		/* init base: called by tmedia_session_create() */
 		/* init self */
 		// TMEDIA_SESSION_BFCP(session)->send_file = tdav_session_bfcp_send_file;
 		// TMEDIA_SESSION_BFCP(session)->send_message = tdav_session_bfcp_send_message;
 		
 		// session->config = tbfcp_config_create();
 		// session->setup = bfcp_setup_actpass;
 		// session->dir = tdav_bfcp_dir_none;
 	}
 	return p_self;
 }
 /* destructor */
 static tsk_object_t* _tdav_session_bfcp_dtor(tsk_object_t * p_self)
 { 
 	tdav_session_bfcp_t *p_session = (tdav_session_bfcp_t *)p_self;
 	if (p_session) {
 		/* deinit self */
 
 		TSK_OBJECT_SAFE_FREE(p_session->p_bfcp_s);
 		TSK_OBJECT_SAFE_FREE(p_session->p_pkt_FloorRequest);
 		TSK_OBJECT_SAFE_FREE(p_session->p_pkt_FloorRelease);
 		TSK_OBJECT_SAFE_FREE(p_session->p_pkt_Hello);
 
 		TSK_FREE(p_session->p_local_ip);
 		TSK_FREE(p_session->p_remote_ip);
 
 		/* rfc4583 */
 		TSK_FREE(p_session->rfc4583.confid);
 		TSK_FREE(p_session->rfc4583.floorid);
 		TSK_FREE(p_session->rfc4583.mstrm);
 		TSK_FREE(p_session->rfc4583.userid);
 
 		/* NAT Traversal context */
 		TSK_OBJECT_SAFE_FREE(p_session->p_natt_ctx);
 		
 		/* deinit base */
 		tmedia_session_deinit(p_self);
 
 		TSK_DEBUG_INFO("*** tdav_session_bfcp_t destroyed ***");
 	}
 
 	return p_self;
 }
 /* object definition */
 static const tsk_object_def_t tdav_session_bfcp_def_s = 
 {
 	sizeof(tdav_session_bfcp_t),
 	_tdav_session_bfcp_ctor, 
 	_tdav_session_bfcp_dtor,
 	tmedia_session_cmp, 
 };
 /* plugin definition*/
 static const tmedia_session_plugin_def_t tdav_session_bfcp_plugin_def_s = 
 {
 	&tdav_session_bfcp_def_s,
 	
 	tmedia_bfcp,
 	"application",
 	
 	_tdav_session_bfcp_set,
 	_tdav_session_bfcp_get,
 	_tdav_session_bfcp_prepare,
 	_tdav_session_bfcp_start,
 	_tdav_session_bfcp_pause,
 	_tdav_session_bfcp_stop,
 
 	/* Audio part */
 	{ tsk_null },
 
 	_tdav_session_bfcp_get_lo,
 	_tdav_session_bfcp_set_ro
 };
 const tmedia_session_plugin_def_t *tdav_session_bfcp_plugin_def_t = &tdav_session_bfcp_plugin_def_s;
 
 #endif /* !defined(HAVE_TINYBFCP) || HAVE_TINYBFCP */