/*
* Copyright (C) 2020, University of the Basque Country (UPV/EHU)
* 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.
*
*/
#include "SipMessage.h"
#define VALUE_CONTENT_TYPE_MULTIPART "multipart/mixed"
#define VALUE_CONTENT_TYPE_MESSAGE_MBMS "application/vnd.3gpp.mcptt-mbms-usage-info+xml"
#define VALUE_CONTENT_TYPE_SDP "application/sdp"


SdpMessage::SdpMessage()
:m_pSdpMessage(tsk_null)
{
}

SdpMessage::SdpMessage(tsdp_message_t *_sdpmessage)
{
	m_pSdpMessage = (tsdp_message_t *)tsk_object_ref(_sdpmessage);
}

SdpMessage::~SdpMessage()
{
	TSK_OBJECT_SAFE_FREE(m_pSdpMessage);
}

char* SdpMessage::getSdpHeaderValue(const char* media, char name, unsigned index /*= 0*/)
{
	const tsdp_header_M_t* M;

	if((M = (const tsdp_header_M_t*)tsdp_message_get_header(m_pSdpMessage, tsdp_htype_M))){
		tsdp_header_type_t type = tsdp_htype_Dummy;
		const tsdp_header_t* header;
		switch(name){
			case 'a': type = tsdp_htype_A; break;
			case 'b': type = tsdp_htype_B; break;
			case 'c': type = tsdp_htype_C; break;
			case 'e': type = tsdp_htype_E; break;
			case 'i': type = tsdp_htype_I; break;
			case 'k': type = tsdp_htype_K; break;
			case 'm': type = tsdp_htype_M; break;
			case 'o': type = tsdp_htype_O; break;


			case 'p': type = tsdp_htype_P; break;
			case 'r': type = tsdp_htype_R; break;
			case 's': type = tsdp_htype_S; break;
			case 't': type = tsdp_htype_T; break;
			case 'u': type = tsdp_htype_U; break;
			case 'v': type = tsdp_htype_V; break;
			case 'z': type = tsdp_htype_Z; break;
		}

		if((header = tsdp_message_get_headerAt(m_pSdpMessage, type, index))){
			return tsdp_header_tostring(header);
		}
	}
		
	return tsk_null;
}

char* SdpMessage::getSdpHeaderAValue(const char* media, const char* attributeName)
{
	const tsdp_header_M_t* M;
	tsk_size_t i;

	for(i = 0; (M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(m_pSdpMessage, tsdp_htype_M, i)); i++){
		if(tsk_striequals(M->media, media)){
			const tsdp_header_A_t* A;
			if((A = tsdp_header_M_findA(M, attributeName))){
				return tsk_strdup(A->value);
			}
		}
	}
	
	return tsk_null;
}

int SdpMessage::getSdpHeaderMPort(const char* media, unsigned index /*= 0*/)
{
	const tsdp_header_M_t* M;
	tsk_size_t i;
	tsk_size_t con;
	for(i = 0,con=0; (M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(m_pSdpMessage, tsdp_htype_M, i)); i++){
		if(tsk_striequals(M->media, media)){
			if(con>=index){
				return M->port;
			}else{
				con++;
			}
			
		}
	}
	
	return -1;
}


char* SdpMessage::getSdpHeaderCAddr(const char* media, unsigned index /*= 0*/)
{
	const tsdp_header_M_t* M;
	tsk_size_t i;
	tsk_size_t con;
	for(i = 0,con=0; (M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(m_pSdpMessage, tsdp_htype_M, i)); i++){
		if(tsk_striequals(M->media, media)){
			if(con>=index){
				return tsk_strdup(M->C->addr);
			}else{
				con++;
			}
			
		}
	}
	
	return tsk_null;
}

char* SdpMessage::getSdpHeaderCAddrType(const char* media, unsigned index /*= 0*/)
{
	const tsdp_header_M_t* M;
	tsk_size_t i;
	tsk_size_t con;
	for(i = 0,con=0; (M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(m_pSdpMessage, tsdp_htype_M, i)); i++){
		if(tsk_striequals(M->media, media)){
			if(con>=index){
				return tsk_strdup(M->C->addrtype);
			}else{
				con++;
			}
			
		}
	}
	
	return tsk_null;
}

int SdpMessage::getSdpHeaderMPort(const char* media)
{
	return getSdpHeaderMPort(media,0);
}

SipMessage::SipMessage()
:m_pSipMessage(tsk_null), m_pSdpMessage(tsk_null)
{ 
}

SipMessage::SipMessage(tsip_message_t *_sipmessage)
: m_pSdpMessage(tsk_null)
{
	m_pSipMessage = (tsip_message_t *)tsk_object_ref(_sipmessage);
}

SipMessage::~SipMessage()
{
	TSK_OBJECT_SAFE_FREE(m_pSipMessage);
	if(m_pSdpMessage){
		delete m_pSdpMessage;
	}
}

bool SipMessage::isResponse()
{
	return TSIP_MESSAGE_IS_RESPONSE(m_pSipMessage);
}

tsip_request_type_t SipMessage::getRequestType()
{
	if(TSIP_MESSAGE_IS_REQUEST(m_pSipMessage)){
		return (m_pSipMessage)->line.request.request_type;
	}
	return tsip_NONE;
}

short SipMessage::getResponseCode()
{
	return TSIP_RESPONSE_CODE(m_pSipMessage);
}

const char* SipMessage::getResponsePhrase()
{
	return TSIP_RESPONSE_PHRASE(m_pSipMessage);
}

const tsip_header_t* SipMessage::getSipHeader(const char* name, unsigned index /* =0 */)
{
	/* Do not worry about calling tsk_striequals() several times because the function
	* is fully optimized.
	*/
	/* Code below comes from tsip_message_get_headerAt() */
	tsk_size_t pos = 0;
	const tsk_list_item_t *item;
	const tsip_header_t* hdr = tsk_null;
	if(!m_pSipMessage || !name){
		return tsk_null;
	}			

	if(tsk_striequals(name, "v") || tsk_striequals(name, "via")){
		if(index == 0){
			hdr = (const tsip_header_t*)m_pSipMessage->firstVia;
			goto bail;
		}else pos++; }
	if(tsk_striequals(name, "f") || tsk_striequals(name, "from")){
		if(index == 0){
			hdr = (const tsip_header_t*)m_pSipMessage->From;
			goto bail;
		}else pos++; }
	if(tsk_striequals(name, "t") || tsk_striequals(name, "to")){
		if(index == 0){
			hdr = (const tsip_header_t*)m_pSipMessage->To;
			goto bail;
		}else pos++; }
	if(tsk_striequals(name, "m") || tsk_striequals(name, "contact")){
		if(index == 0){
			hdr = (const tsip_header_t*)m_pSipMessage->Contact;
			goto bail;
		}else pos++; }
	if(tsk_striequals(name, "i") || tsk_striequals(name, "call-id")){
		if(index == 0){
			hdr = (const tsip_header_t*)m_pSipMessage->Call_ID;
			goto bail;
		}else pos++; }
	if(tsk_striequals(name, "cseq")){
		if(index == 0){
			hdr = (const tsip_header_t*)m_pSipMessage->CSeq;
			goto bail;
		}else pos++; }
	if(tsk_striequals(name, "expires")){
		if(index == 0){
			hdr = (const tsip_header_t*)m_pSipMessage->Expires;
			goto bail;
		}else pos++; }
	if(tsk_striequals(name, "c") || tsk_striequals(name, "content-type")){
		if(index == 0){
			hdr = (const tsip_header_t*)m_pSipMessage->Content_Type;
			goto bail;
		}else pos++; }
	if(tsk_striequals(name, "l") || tsk_striequals(name, "content-length")){
		if(index == 0){
			hdr = (const tsip_header_t*)m_pSipMessage->Content_Length;
			goto bail;
		}else pos++; }

	tsk_list_foreach(item, m_pSipMessage->headers){
		if(tsk_striequals(tsip_header_get_name_2(TSIP_HEADER(item->data)), name)){
			if(pos++ >= index){
				hdr = (const tsip_header_t*)item->data;
				break;
			}
		}
	}
	

bail:
	return hdr;
}

// e.g. getHeaderParamValue("content-type");
char* SipMessage::getSipHeaderValue(const char* name, unsigned index /* = 0*/)
{
	const tsip_header_t* header;
	if((header = this->getSipHeader(name, index))){

		switch(header->type){
			case tsip_htype_From:
				return tsip_uri_tostring(((const tsip_header_From_t*)header)->uri, tsk_false, tsk_false);
			case tsip_htype_To:
				return tsip_uri_tostring(((const tsip_header_To_t*)header)->uri, tsk_false, tsk_false);
				break;
			case tsip_htype_P_Asserted_Identity:
				return tsip_uri_tostring(((const tsip_header_P_Asserted_Identity_t*)header)->uri, tsk_false, tsk_false);
				break;

			default:
				return tsip_header_value_tostring(header);
		}
	}
	// SWIG: %newobject getHeaderValueAt;
	return tsk_null;
}

// e.g. getHeaderParamValue("content-type", "charset");
char* SipMessage::getSipHeaderParamValue(const char* name, const char* param, unsigned index /*=0*/)
{
	const tsip_header_t* header;

	if((header = this->getSipHeader(name, index))){
		return tsip_header_get_param_value(header, param);
	}

	// SWIG: %newobject getSipHeaderParamValue;
	return tsk_null;
}

/** Returns the content length.
*/
unsigned SipMessage::getSipContentLength()
{
	return TSIP_MESSAGE_CONTENT_DATA_LENGTH(m_pSipMessage);
}

/*
unsigned SipMessage::getSipContentMbms()
{
	return TSIP_MESSAGE_CONTENT_DATA_LENGTH(m_pSipMessage);
}
unsigned SipMessage::getSipContentSdp()
{
	return TSIP_MESSAGE_CONTENT_DATA_LENGTH(m_pSipMessage);
}
*/
/** Gets the message content
* @param output A pointer to the output buffer where to copy the data. MUST
* be allocated by the caller.
* @param maxsize The maximum number of octets to copy. Should be less than the size of the
* @a output buffer. You can use @a getSipContentLength() to get the right value to use.
* @retval The number of octet copied in the @a output buffer.
*/
unsigned SipMessage::getSipContent(void* output, unsigned maxsize)
{
	unsigned retsize = 0;
	if(output && maxsize && TSIP_MESSAGE_HAS_CONTENT(m_pSipMessage)){
		retsize = (m_pSipMessage->Content->size > maxsize) ? maxsize : m_pSipMessage->Content->size;
		memcpy(output, m_pSipMessage->Content->data, retsize);
	}
	return retsize;
}

const void* SipMessage::getSipContentPtr()
{
    if(m_pSipMessage && m_pSipMessage->Content){
        return m_pSipMessage->Content->data;
    }
    return tsk_null;
}

//MCPTT
unsigned SipMessage::getSipContentSdp(void* output, unsigned maxsize)
{
	return getSipContentContetType(output, maxsize,VALUE_CONTENT_TYPE_SDP);
}
//MCPTT MBMS
unsigned SipMessage::getSipContentMbms(void* output, unsigned maxsize)
{
	return getSipContentContetType(output, maxsize,VALUE_CONTENT_TYPE_MESSAGE_MBMS);
}


//MCPTT
unsigned SipMessage::getSipContentContetType(void* output, unsigned maxsize,char* content_type)
{
	char* boundary = tsk_null;
	tmedia_multipart_body_t* mp_body = tsk_null;
	tmedia_content_multipart_t* mp_content = tsk_null;
	tsip_header_t* content_type_data=tsk_null;
	unsigned retsize = 0;
	if(output && maxsize && TSIP_MESSAGE_HAS_CONTENT(m_pSipMessage)){
		content_type_data=(tsip_header_t*)m_pSipMessage->Content_Type;
		boundary = tsip_header_get_param_value(content_type_data, "boundary");
		if(boundary == tsk_null) {			
							TSK_DEBUG_ERROR("[%s] content-type is not supportted", TSIP_MESSAGE_CONTENT_TYPE(m_pSipMessage));
							return -3;
		}else{
			mp_body = tmedia_content_multipart_body_parse(TSIP_MESSAGE_CONTENT_DATA(m_pSipMessage), TSIP_MESSAGE_CONTENT_DATA_LENGTH(m_pSipMessage), VALUE_CONTENT_TYPE_MULTIPART, boundary);
			if(mp_body != tsk_null)
			{
				mp_content = tmedia_content_multipart_body_get_content(mp_body,content_type);
				if(mp_content != tsk_null)
				{
					retsize = (mp_content->data_size > maxsize) ? maxsize : mp_content->data_size;
					memcpy(output, mp_content->data, retsize);
				}
		
			}
		}
			
	}
	return retsize;
}

const SdpMessage* SipMessage::getSdpMessage()
{
	char* sdpString=tsk_null;
	if(!m_pSdpMessage && TSIP_MESSAGE_HAS_CONTENT(m_pSipMessage)){
		tsdp_message_t* sdp = tsdp_message_parse(m_pSipMessage->Content->data, m_pSipMessage->Content->size);
		if(sdp){
			m_pSdpMessage = new SdpMessage(sdp);
			TSK_OBJECT_SAFE_FREE(sdp);
		}else{
			#if HAVE_CRT //Debug memory
		sdpString = (char*)malloc(getSipContentLength());
			#else
		sdpString = (char*)tsk_malloc(getSipContentLength());
			#endif //HAVE_CRT
			
			int size=getSipContentSdp(sdpString,getSipContentLength());
			tsdp_message_t* sdp = tsdp_message_parse(sdpString,size);
			if(sdp){
				m_pSdpMessage = new SdpMessage(sdp);
				TSK_OBJECT_SAFE_FREE(sdp);
			}
		}
	}
	
	return m_pSdpMessage;
}
