doubango/tinyMSRP/ragel/tmsrp_parser_message.rl
c732d49e
 /*
 * Copyright (C) 2009-2015 Mamadou DIOP.
 *	
 * This file is part of Open Source Doubango Framework.
 *
 * DOUBANGO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *	
 * DOUBANGO is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *	
 * You should have received a copy of the GNU General Public License
 * along with DOUBANGO.
 *
 */
 /**@file tmsrp_machine_message.rl
  * @brief Ragel file.
  */
 #include "tinymsrp/parsers/tmsrp_parser_message.h"
 
 #include "tinymsrp/headers/tmsrp_header_Dummy.h"
 #include "tinymsrp/headers/tmsrp_header_Expires.h"
 #include "tinymsrp/headers/tmsrp_header_Max-Expires.h"
 #include "tinymsrp/headers/tmsrp_header_Min-Expires.h"
 #include "tinymsrp/headers/tmsrp_header_Use-Path.h"
 #include "tinymsrp/headers/tmsrp_header_WWW-Authenticate.h"
 
 #include "tsk_string.h"
 #include "tsk_memory.h"
 #include "tsk_debug.h"
 
 static tsk_bool_t parse_payload(tmsrp_message_t* msrp_msg, const char* tag_start, const char** p, const char* pe, tsk_bool_t* payload_parsed);
 static void set_payload(tmsrp_message_t* msrp_msg, const void* ptr, tsk_size_t len);
 
 #define TMSRP_MSG_PARSER_ADD_HEADER(name) \
 	if((header = (tmsrp_header_t*)tmsrp_header_##name##_parse(tag_start, (p - tag_start)))){ \
 		tmsrp_message_add_header(msrp_msg, header); \
 		header = tsk_object_unref(header); \
 	}
 %%{
 	machine tmsrp_machine_message;
 	
 	###########################################
 	#	Includes
 	###########################################
 	include tmsrp_machine_utils "./ragel/tmsrp_machine_utils.rl";
 	
 	action tag{
 		tag_start = p;
 	}
 
 	###########################################
 	#	Actions
 	###########################################
 	action parse_Authentication_Info{
 		//FIXME: TMSRP_MSG_PARSER_ADD_HEADER(Authentication_Info);
 		TSK_DEBUG_WARN("Authentication_Info Not implemented");
 	}
 	
 	action parse_Authorization{
 		//FIXME: TMSRP_MSG_PARSER_ADD_HEADER(Authorization);
 		TSK_DEBUG_WARN("Authorization Not implemented");
 	}
 	
 	action parse_Byte_Range{
 		TMSRP_MSG_PARSER_ADD_HEADER(Byte_Range);
 	}
 	
 	action parse_Content_Type{
 		TMSRP_MSG_PARSER_ADD_HEADER(Content_Type);
 	}
 	
 	action parse_Expires{
 		TMSRP_MSG_PARSER_ADD_HEADER(Expires);
 	}
 	
 	action parse_Failure_Report{
 		TMSRP_MSG_PARSER_ADD_HEADER(Failure_Report);
 	}
 	
 	action parse_From_Path{
 		TMSRP_MSG_PARSER_ADD_HEADER(From_Path);
 	}
 	
 	action parse_Max_Expires{
 		TMSRP_MSG_PARSER_ADD_HEADER(Max_Expires);
 	}
 	
 	action parse_Message_ID{
 		TMSRP_MSG_PARSER_ADD_HEADER(Message_ID);
 	}
 	
 	action parse_Min_Expires{
 		TMSRP_MSG_PARSER_ADD_HEADER(Min_Expires);
 	}
 	
 	action parse_Status{
 		TMSRP_MSG_PARSER_ADD_HEADER(Status);
 	}
 	
 	action parse_Success_Report{
 		TMSRP_MSG_PARSER_ADD_HEADER(Success_Report);
 	}
 	
 	action parse_To_Path{
 		TMSRP_MSG_PARSER_ADD_HEADER(To_Path);
 	}
 	
 	action parse_Use_Path{
 		TMSRP_MSG_PARSER_ADD_HEADER(Use_Path);
 	}
 	
 	action parse_WWW_Authenticate{
 		TMSRP_MSG_PARSER_ADD_HEADER(WWW_Authenticate);
 	}
 	
 	action parse_Dummy{
 		TMSRP_MSG_PARSER_ADD_HEADER(Dummy);
 	}
 	
 	action parse_tid{
 		TSK_PARSER_SET_STRING(msrp_msg->tid);
 	}
 
 	action parse_method{
 		if(msrp_msg->type == tmsrp_unknown){
 			msrp_msg->type = tmsrp_request;
 			TSK_PARSER_SET_STRING(msrp_msg->line.request.method);
 			 msrp_msg->line.request.type = tmsrp_request_get_type(msrp_msg->line.request.method);
 		}
 		else{
 			//cs = %%{ write first_final; }%%;
 			cs = tmsrp_machine_message_error;
 			TSK_DEBUG_ERROR("Message type already defined.");
 		}
 	}
 
 
 	action parse_status_code{
 		if(msrp_msg->type == tmsrp_unknown){
 			msrp_msg->type = tmsrp_response;
 			TSK_PARSER_SET_INT(msrp_msg->line.response.status);
 		}
 		else{
 			//cs = %%{ write first_final; }%%;
 			cs = tmsrp_machine_message_error;
 			TSK_DEBUG_ERROR("Message type already defined.");
 		}
 	}
 	action parse_comment{
 		TSK_PARSER_SET_STRING(msrp_msg->line.response.comment);
 	}
 
 	
 	action try_parse_data{
 		parse_payload(msrp_msg, tag_start, &p, pe, &payload_parsed); // will update "p"
 	}
 
 	action parse_data{
 		// if the msrp message contain a valid content-type, then gob it otherwise continue until we reach the endline
 		int len;
 		if(parse_payload(msrp_msg, tag_start, &p, pe, &payload_parsed)){ // will update "p"
 			// (This space left deliberately blank)
 		}
 		else if((len = (int)(p  - tag_start))>0){
 			set_payload(msrp_msg, tag_start, (tsk_size_t)len);
 		}
 	}
 
 	action parse_endtid{
 		TSK_PARSER_SET_STRING(msrp_msg->end_line.tid);
 	}
 
 	action parse_cflag{
 		if(tag_start){
 			msrp_msg->end_line.cflag = *tag_start;
 		}
 		else msrp_msg->end_line.cflag = '#';
 	}
 
 	action outside_endline{
 		*msg_size = (p - (const char*)input) + 1;
 	}
 	
 	action into_endline{
 		into_endline = tsk_true;
 	}
 	action endtid_match{
 		( into_endline || (((pe-p) >7/*seven hyphens*/) && (msrp_msg->tid) && tsk_strniequals(msrp_msg->tid, (p+7), tsk_strlen(msrp_msg->tid))) )
 	}
 
 
 	###########################################
 	#	Headers
 	###########################################
 
 	Authentication_Info = "Authentication-Info:"i SP any* :>CRLF %parse_Authentication_Info;
 	Authorization		= "Authorization:"i SP any* :>CRLF %parse_Authorization;
 	Byte_Range			= "Byte-Range:"i SP any* :>CRLF %parse_Byte_Range;
 	Content_Type		= "Content-Type:"i SP any* :>CRLF %parse_Content_Type;
 	Expires				= "Expires:"i SP any* :>CRLF %parse_Expires;
 	Failure_Report		= "Failure-Report:"i SP any* :>CRLF %parse_Failure_Report ;
 	From_Path			= "From-Path:"i SP any* :>CRLF %parse_From_Path ;
 	Max_Expires			= "Max-Expires:"i SP any* :>CRLF %parse_Max_Expires;
 	Message_ID			= "Message-ID:"i SP any* :>CRLF %parse_Message_ID;
 	Min_Expires			= "Min-Expires:"i SP any* :>CRLF %parse_Min_Expires;
 	Status				= "Status:"i SP any* :>CRLF %parse_Status;
 	Success_Report		= "Success-Report:"i SP any* :>CRLF %parse_Success_Report;
 	To_Path				= "To-Path:"i SP any* :>CRLF %parse_To_Path;
 	Use_Path			= "Use-Path:"i SP any* :>CRLF %parse_Use_Path;
 	WWW_Authenticate	= "WWW-Authenticate:"i SP any* :>CRLF %parse_WWW_Authenticate;
 	
 	Dummy				= hname ":" SP hval :>CRLF %parse_Dummy;
 	
 	header = (Authentication_Info | Authorization | Byte_Range | Content_Type | Expires | Failure_Report | From_Path | Max_Expires | Message_ID | Min_Expires | Status | Success_Report | To_Path | Use_Path | WWW_Authenticate)>tag @10 | (Dummy>tag) @0;
 
 	#headers = To_Path From_Path ( header )*;
 	headers = ( header )*;
 
 	###########################################
 	#	Utils
 	###########################################
 	transact_id	= ident;
 	method = UPALPHA*;
 	status_code	= DIGIT{3};
 	comment	= utf8text;
 	
 	continuation_flag = "+" | "$" | "#";
 	end_line = "-------" transact_id>tag %parse_endtid continuation_flag>tag %parse_cflag CRLF;
 
 	Other_Mime_header = Dummy;
 
 	data = any*;
 
 	###########################################
 	#	Request
 	###########################################
 	req_start = "MSRP" SP transact_id>tag %parse_tid SP method>tag %parse_method CRLF;
 	#content_stuff = (Other_Mime_header)* CRLF data>tag %parse_data :>CRLF;
 	content_stuff = data>tag >try_parse_data %parse_data;
 	msrp_request = req_start headers>10 (CRLF content_stuff CRLF)?>5 :>end_line when endtid_match >into_endline;
 
 	###########################################
 	#	Response
 	###########################################
 	resp_start = "MSRP" SP transact_id>tag %parse_tid SP status_code>tag %parse_status_code (SP comment>tag %parse_comment)? CRLF;
 	msrp_response = resp_start headers end_line;
 
 	###########################################
 	#	Message
 	###########################################
 	msrp_req_or_resp = (msrp_request | msrp_response)>1 @outside_endline any*>0;
 
 	###########################################
 	#	Entry Point
 	###########################################
 	main := msrp_req_or_resp;
 }%%
 
 TSK_RAGEL_DISABLE_WARNINGS_BEGIN()
 /* Ragel data */
 %% write data;
 TSK_RAGEL_DISABLE_WARNINGS_END()
 
 tmsrp_message_t* tmsrp_message_parse(const void *input, tsk_size_t size)
 {
 	tsk_size_t msg_size;
 	return tmsrp_message_parse_2(input, size, &msg_size);
 }
 
 tmsrp_message_t* tmsrp_message_parse_2(const void *input, tsk_size_t size, tsk_size_t* msg_size)
 {
 	tmsrp_message_t* msrp_msg = tsk_null;
 	const char* tag_start = tsk_null;
 	tmsrp_header_t* header = tsk_null;
 	tsk_bool_t into_endline = tsk_false;
 	tsk_bool_t payload_parsed = tsk_false;
 	
 	/* Ragel variables */
 	int cs = 0;
 	const char* p = input;
 	const char* pe = p + size;
 	const char* eof = tsk_null;
 
 	(void)(eof);
 	(void)(tmsrp_machine_message_first_final);
 	(void)(tmsrp_machine_message_error);
 	(void)(tmsrp_machine_message_en_main);
 
 	*msg_size = 0;
 
 	if(!input || !size){
 		//TSK_DEBUG_ERROR("Null or empty buffer."); // --> very common case(stream): do not bother us...
 		goto bail;
 	}
 
 	if(!(msrp_msg = tmsrp_message_create_null())){
 		goto bail;
 	}
 
 	TSK_RAGEL_DISABLE_WARNINGS_BEGIN()
 	/* Ragel init */
 	%% write init;
 
 	/* Ragel execute */
 	%% write exec;
 	TSK_RAGEL_DISABLE_WARNINGS_END()
 
 	/* Check result */
 	if( cs < %%{ write first_final; }%% ){
 		//TSK_DEBUG_ERROR("Failed to parse MSRP message."); --> very common case(stream): do not bother us...
 		TSK_OBJECT_SAFE_FREE(msrp_msg);
 		goto bail;
 	}
 	
 bail:
 	return msrp_msg;
 }
 
 static tsk_bool_t parse_payload(tmsrp_message_t* msrp_msg, const char* tag_start, const char** p, const char* pe, tsk_bool_t* payload_parsed)
 {
 	int64_t payload_len, endline_len;
 	tsk_bool_t can_parse_payload;
 
 	if(*payload_parsed){
 		TSK_DEBUG_INFO("payload already parsed");
 		return tsk_true;
 	}
 
 	if(pe && p && *p && msrp_msg && (can_parse_payload = TMSRP_HEADER_BYTE_RANGE_IS_VALID(msrp_msg->ByteRange))){
 		payload_len = (msrp_msg->ByteRange->end - msrp_msg->ByteRange->start) + 1;
 		endline_len = 2/*CRLF*/ + 7/*hyphens*/ + tsk_strlen(msrp_msg->tid) + 2/*CRLF*/;
 		can_parse_payload = (pe - tag_start) > (payload_len + endline_len);
 		if(can_parse_payload){
 			set_payload(msrp_msg, tag_start, (tsk_size_t)payload_len);
 			*p = ((tag_start + payload_len) - 1);
 			*payload_parsed = tsk_true;
 			return tsk_true;
 		}
 	}
 	return tsk_false;
 }
 
 static void set_payload(tmsrp_message_t* msrp_msg, const void* ptr, tsk_size_t len)
 {
 	if(msrp_msg->Content){
 		tsk_buffer_cleanup(msrp_msg->Content);
 		tsk_buffer_append(msrp_msg->Content, ptr, len);
 	}
 	else{
 		msrp_msg->Content = tsk_buffer_create(ptr, len);
 	}
 }