#if HAVE_CRT #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #endif //HAVE_CRT /* * 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. * */ /**@file tmsrp_message.c * @brief MSRP message. * * @author Mamadou Diop <diopmamadou(at)doubango.org> * */ #include "tinymsrp/tmsrp_message.h" #include "tsk_string.h" #include "tsk_memory.h" #include "tsk_debug.h" /*== Predicate function to find tmsrp_header_t object by type. */ static int pred_find_header_by_type(const tsk_list_item_t *item, const void *tmsrp_htype) { if(item && item->data){ tmsrp_header_t *header = item->data; tmsrp_header_type_t htype = *((tmsrp_header_type_t*)tmsrp_htype); return (header->type - htype); } return -1; } /*== Predicate function to find tmsrp_header_t object by name. */ static int pred_find_header_by_name(const tsk_list_item_t *item, const void *name) { if(item && item->data && name){ tmsrp_header_t *header = item->data; return tsk_stricmp(tmsrp_header_get_nameex(header), (const char*)name); } return -1; } tmsrp_message_t* tmsrp_message_create(tmsrp_message_type_t type, const char* tid, const char* method, short status, const char* comment) { tmsrp_message_t* message; if((message = tsk_object_new(tmsrp_message_def_t))){ message->type = type; message->tid = tsk_strdup(tid); if(message->type == tmsrp_response){ message->line.response.status = status; message->line.response.comment = tsk_strdup(comment); } else{ message->line.request.method = tsk_strdup(method); message->line.request.type = tmsrp_request_get_type(method); } message->headers = tsk_list_create(); message->end_line.tid = tsk_strdup(message->tid); message->end_line.cflag = '$'; } return message; } tmsrp_message_t* tmsrp_request_create(const char* tid, const char* method) { return tmsrp_message_create(tmsrp_request, tid, method, 0, tsk_null); } tmsrp_message_t* tmsrp_response_create(const char* tid, short status, const char* comment) { return tmsrp_message_create(tmsrp_response, tid, tsk_null, status, comment); } tmsrp_message_t* tmsrp_message_create_null() { return tmsrp_message_create(tmsrp_unknown, tsk_null, tsk_null, 0, tsk_null); } int tmsrp_message_add_header(tmsrp_message_t *self, const tmsrp_header_t *hdr) { #define ADD_HEADER(type, field) \ case tmsrp_htype_##type: \ { \ if(!self->field) \ { \ self->field = (tmsrp_header_##type##_t*)header; \ return 0; \ } \ break; \ } if(self && hdr) { tmsrp_header_t *header = tsk_object_ref((void*)hdr); switch(header->type) { ADD_HEADER(To_Path, To); ADD_HEADER(From_Path, From); ADD_HEADER(Message_ID, MessageID); ADD_HEADER(Byte_Range, ByteRange); ADD_HEADER(Failure_Report, FailureReport); ADD_HEADER(Success_Report, SuccessReport); ADD_HEADER(Status, Status); ADD_HEADER(Content_Type, ContentType); default: break; } tsk_list_push_back_data(self->headers, (void**)&header); return 0; } return -1; } int tmsrp_message_add_headers(tmsrp_message_t *self, ...) { const tsk_object_def_t* objdef; tmsrp_header_t *header; va_list ap; if(!self){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } va_start(ap, self); while((objdef = va_arg(ap, const tsk_object_def_t*))){ if((header = tsk_object_new_2(objdef, &ap))){ tmsrp_message_add_header(self, header); TSK_OBJECT_SAFE_FREE(header); } } va_end(ap); return 0; } tmsrp_request_type_t tmsrp_request_get_type(const char* method) { if(method){ if(tsk_strequals(method, "SEND")){ return tmsrp_SEND; } else if(tsk_strequals(method, "REPORT")){ return tmsrp_REPORT; } else if(tsk_strequals(method, "AUTH")){ return tmsrp_AUTH; } } return tmsrp_NONE; } const tmsrp_header_t *tmsrp_message_get_headerAt(const tmsrp_message_t *self, tmsrp_header_type_t type, tsk_size_t index) { tsk_size_t pos = 0; tsk_list_item_t *item; const tmsrp_header_t* hdr = 0; if(self) { switch(type) { case tmsrp_htype_To_Path: if(index == 0){ hdr = (const tmsrp_header_t*)self->To; goto bail; }else pos++; break; case tmsrp_htype_From_Path: if(index == 0){ hdr = (const tmsrp_header_t*)self->From; goto bail; }else pos++; break; case tmsrp_htype_Message_ID: if(index == 0){ hdr = (const tmsrp_header_t*)self->MessageID; goto bail; break; }else pos++; case tmsrp_htype_Byte_Range: if(index == 0){ hdr = (const tmsrp_header_t*)self->ByteRange; goto bail; }else pos++; break; case tmsrp_htype_Failure_Report: if(index == 0){ hdr = (const tmsrp_header_t*)self->FailureReport; goto bail; }else pos++; break; case tmsrp_htype_Success_Report: if(index == 0){ hdr = (const tmsrp_header_t*)self->SuccessReport; goto bail; }else pos++; break; case tmsrp_htype_Status: if(index == 0){ hdr = (const tmsrp_header_t*)self->Status; goto bail; }else pos++; break; case tmsrp_htype_Content_Type: if(index == 0){ hdr = (const tmsrp_header_t*)self->ContentType; goto bail; }else pos++; break; default: break; } tsk_list_foreach(item, self->headers){ if(!pred_find_header_by_type(item, &type)){ if(pos++ >= index){ hdr = item->data; break; } } } } bail: return hdr; } const tmsrp_header_t *tmsrp_message_get_header(const tmsrp_message_t *self, tmsrp_header_type_t type) { return tmsrp_message_get_headerAt(self, type, 0); } const tmsrp_header_t *tmsrp_message_get_headerByName(const tmsrp_message_t *self, const char* name) { if(self && self->headers){ const tsk_list_item_t* item; if((item = tsk_list_find_item_by_pred(self->headers, pred_find_header_by_name, name))){ return item->data; } } return tsk_null; } int tmsrp_message_add_content(tmsrp_message_t *self, const char* content_type, const void* content, tsk_size_t size) { if(self) { if(content_type){ TSK_OBJECT_SAFE_FREE(self->ContentType); } TSK_OBJECT_SAFE_FREE(self->Content); if(content_type){ TMSRP_MESSAGE_ADD_HEADER(self, TMSRP_HEADER_CONTENT_TYPE_VA_ARGS(content_type)); } self->Content = tsk_buffer_create(content, size); return 0; } return -1; } int tmsrp_message_serialize(const tmsrp_message_t *self, tsk_buffer_t *output) { if(!self || !output){ return -1; } if(TMSRP_MESSAGE_IS_REQUEST(self)){ /* pMSRP SP transact-id SP method CRLF */ tsk_buffer_append_2(output, "MSRP %s %s\r\n", self->tid, self->line.request.method); } else{ /* pMSRP SP transact-id SP status-code [SP comment] CRLF */ tsk_buffer_append_2(output, "MSRP %s %3hi%s%s\r\n", self->tid, self->line.response.status, self->line.response.comment ? " " : "", self->line.response.comment ? self->line.response.comment : "" ); } /* To-Path */ if(self->To){ tmsrp_header_serialize(TMSRP_HEADER(self->To), output); } /* From-Path */ if(self->From){ tmsrp_header_serialize(TMSRP_HEADER(self->From), output); } /* Message-Id */ if(self->MessageID){ tmsrp_header_serialize(TMSRP_HEADER(self->MessageID), output); } /* Byte-Range */ if(self->ByteRange){ tmsrp_header_serialize(TMSRP_HEADER(self->ByteRange), output); } /* Failure-Report */ if(self->FailureReport){ tmsrp_header_serialize(TMSRP_HEADER(self->FailureReport), output); } /* Success-Report */ if(self->SuccessReport){ tmsrp_header_serialize(TMSRP_HEADER(self->SuccessReport), output); } /* Status */ if(self->Status){ tmsrp_header_serialize(TMSRP_HEADER(self->Status), output); } /* All other headers (Other-Mime-headers) - Should be empty if no content is added (see below) but ... */ { tsk_list_item_t *item; tsk_list_foreach(item, self->headers){ tmsrp_header_t *hdr = item->data; tmsrp_header_serialize(hdr, output); } } /* RFC 4975 - 7.1. Constructing Requests A request with no body MUST NOT include a Content-Type or any other MIME-specific header fields. A request without a body MUST contain an end-line after the final header field. No extra CRLF will be present between the header section and the end-line. */ /* CONTENT */ if(TMSRP_MESSAGE_HAS_CONTENT(self)){ /* Content-Type */ if(self->ContentType){ tmsrp_header_serialize(TMSRP_HEADER(self->ContentType), output); } tsk_buffer_append(output, "\r\n", 2); tsk_buffer_append(output, TSK_BUFFER_TO_STRING(self->Content), TSK_BUFFER_SIZE(self->Content)); tsk_buffer_append(output, "\r\n", 2); } /* END LINE */ tsk_buffer_append_2(output, "-------%s%c\r\n", self->end_line.tid, self->end_line.cflag); return 0; } char* tmsrp_message_tostring(const tmsrp_message_t *self) { tsk_buffer_t* output; char* ret = tsk_null; if((output = tsk_buffer_create_null())){ if(!tmsrp_message_serialize(self, output)){ ret = tsk_strndup(output->data, output->size); } TSK_OBJECT_SAFE_FREE(output); } return ret; } //================================================================================================= // MSRP object definition // static void* tmsrp_message_ctor(tsk_object_t * self, va_list * app) { tmsrp_message_t *message = self; if(message){ } return self; } static void* tmsrp_message_dtor(tsk_object_t * self) { tmsrp_message_t *message = self; if(message){ TSK_FREE(message->tid); // request if(TMSRP_MESSAGE_IS_REQUEST(message)){ TSK_FREE(message->line.request.method); } // response if(TMSRP_MESSAGE_IS_RESPONSE(message)){ TSK_FREE(message->line.response.comment); } // Very common headers TSK_OBJECT_SAFE_FREE(message->To); TSK_OBJECT_SAFE_FREE(message->From); TSK_OBJECT_SAFE_FREE(message->MessageID); TSK_OBJECT_SAFE_FREE(message->ByteRange); TSK_OBJECT_SAFE_FREE(message->FailureReport); TSK_OBJECT_SAFE_FREE(message->SuccessReport); TSK_OBJECT_SAFE_FREE(message->Status); // all other headers TSK_OBJECT_SAFE_FREE(message->headers); // content TSK_OBJECT_SAFE_FREE(message->ContentType); TSK_OBJECT_SAFE_FREE(message->Content); // end-line TSK_FREE(message->end_line.tid); } return self; } static int tmsrp_message_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) { return -1; } static const tsk_object_def_t tmsrp_message_def_s = { sizeof(tmsrp_message_t), tmsrp_message_ctor, tmsrp_message_dtor, tmsrp_message_cmp, }; const tsk_object_def_t *tmsrp_message_def_t = &tmsrp_message_def_s;