LibSip_Steps.ttcn 110 KB
Newer Older
	  v_params := {{id:=c_expiresId, paramValue:=c_shortRegistration}};
	  vc_contact.contactBody.contactAddresses[0].contactParams := v_params;
	  }
	  
	  vc_firstREGISTER_sent := true;//f_setHeaders_Register is called in deREGISTER function
	  
	  vc_authorization := 
	  {
		fieldName := AUTHORIZATION_E,
		body := {f_calculatecCredentials_empty(vc_userprofile)}
	  }

      
	}// end function setHeaders_REGISTER

	/**
	 * 
	 * @desc function sets via, cseq and authorization header for the next outgoing (protected) REGISTER
	 * @verdict 
	 */
 	function f_setHeaders_2ndREGISTER() runs on SipComponent
	{
	  var CommaParam_List v_challenge;
		
	  vc_branch := c_branchCookie & f_getRndTag();

	  vc_via_REG :={
		fieldName := VIA_E,
		viaBody 	 := {valueof(m_ViaBody_currIpaddr(vc_branch, vc_userprofile))}
	  };
	  
	  // Extract challenge and calculate credentials for a response.
	  v_challenge := vc_response.msgHeader.wwwAuthenticate.challenge.otherChallenge.authParams;
	            
	  // Increment CSeq sequence number 
	  vc_cSeq.seqNumber := vc_cSeq.seqNumber + 1;
      
      // Prepair right answer      
	  vc_authorization := 
		   {
			 fieldName := AUTHORIZATION_E,
			 body := {f_calculatecCredentials(vc_userprofile, "REGISTER", v_challenge)}
		   }
				
	}// end function f_setHeaders_2ndREGISTER

	/**
	 * 
	 * @desc function sets via, cseq and authorization header for the next outgoing (protected) REGISTER
	 * NO response in Authorization header to cause an error
	 * @verdict 
	 */
	function f_setHeaders_2ndREGISTER_wo_response() runs on SipComponent
	{
	  var CommaParam_List v_challenge;
		
	  vc_branch := c_branchCookie & f_getRndTag();

	  vc_via_REG :={
		fieldName := VIA_E,
		viaBody 	 := {valueof(m_ViaBody_currIpaddr(vc_branch, vc_userprofile))}
	  };
	  
	  // Extract challenge and calculate credentials for a response.
	  v_challenge := vc_response.msgHeader.wwwAuthenticate.challenge.otherChallenge.authParams;
	            
	  // Increment CSeq sequence number 
	  vc_cSeq.seqNumber := vc_cSeq.seqNumber + 1;
      
	  // Prepair right answer      
	  vc_authorization := 
		   {
			 fieldName := AUTHORIZATION_E,
			 body := {f_calculatecCredentials_wo_response(vc_userprofile, "REGISTER", v_challenge)}
		   }
				
	}// end function f_setHeaders_2ndREGISTER_wo_response

	/**
	 * 
	 * @desc function sets via, cseq and authorization header with different private name for the next outgoing (protected) REGISTER
	 * @verdict 
	 */
 	function f_setHeaders_2ndREGISTER_authorizationWithDifferentUserName() runs on SipComponent
	{
	  var CommaParam_List v_challenge;
		
	  vc_branch := c_branchCookie & f_getRndTag();

	  vc_via_REG :={
		fieldName := VIA_E,
		viaBody 	 := {valueof(m_ViaBody_currIpaddr(vc_branch, vc_userprofile))}
	  };
	  
	  // Extract challenge and calculate credentials for a response.
	  v_challenge := vc_response.msgHeader.wwwAuthenticate.challenge.otherChallenge.authParams;
	            
	  // Increment CSeq sequence number 
	  vc_cSeq.seqNumber := vc_cSeq.seqNumber + 1;
      
      // Prepair right answer      
	  vc_authorization := 
		   {
			 fieldName := AUTHORIZATION_E,
			 body := {f_calculatecCredentialsAndChangeUserName(vc_userprofile, "REGISTER", v_challenge)}
		   }
				
	}// end function f_setHeaders_2ndREGISTER_authorizationWithDifferentUserName


	/**
	 * 
	 * @desc function sets header fields for the next outgoing REGISTER (de-registration)
	 * @param p_cSeq_s cSeq to be used
	 * @verdict 
	 */
 	function f_setHeaders_deREGISTER(inout CSeq p_cSeq_s) runs on SipComponent
	{
	f_setHeaders_REGISTER(p_cSeq_s);
	vc_contact := 
		{
		  fieldName := CONTACT_E,
		  contactBody := {wildcard := "*" } 
		};
	} // end function f_setHeaders_deREGISTER


	/**
	 * 
	 * @desc setting of general and basic Invite header fields
	 * 		in additon to the addresses (To, From, ReqUri)
	 * @param p_cSeq_s
	 */
	function f_setHeadersINVITE(inout CSeq p_cSeq_s) runs on SipComponent
	{      
	  f_setHeadersGeneral(p_cSeq_s, "INVITE"); // cseq, contact, branch, via

	  vc_callId := { fieldName:=CALL_ID_E, callid:=f_getRndCallId(p_cSeq_s) & c_AT & vc_userprofile.currIpaddr };

	  vc_cancel_To := vc_to;
	  vc_caller_To := vc_to;
      
	  vc_caller_From := vc_from;
            
	  vc_reqHostPort := vc_requestUri.hostPort;
      
	}// end function f_setHeadersINVITE

	/**
	 * 
	 * @desc setting of general and basic Message header fields
	 * 		in additon to the addresses (To, From, ReqUri)
	 * @param p_cSeq_s
	 */
	function f_setHeadersMESSAGE(inout CSeq p_cSeq_s) runs on SipComponent
	{      
	  f_setHeadersGeneral(p_cSeq_s, "MESSAGE"); // cseq, contact, branch, via

	  vc_callId := { fieldName:=CALL_ID_E, callid:=f_getRndCallId(p_cSeq_s) & c_AT & vc_userprofile.currIpaddr };

	  vc_cancel_To := vc_to;
	  vc_caller_To := vc_to;
      
	  vc_caller_From := vc_from;
            
	  vc_reqHostPort := vc_requestUri.hostPort;
      
	}// end function f_setHeadersMESSAGE

	/**
	 * 
	 * @desc function sets header field for the next outgoing SUBSCRIBE message
	 * @param p_cSeq_s CSeq parameter to be applied
	 */
 	function f_setHeaders_SUBSCRIBE(inout CSeq p_cSeq_s) runs on SipComponent
	{
	  f_setHeadersGeneral(p_cSeq_s, "SUBSCRIBE"); // cseq, contact, branch, via
  
  	  vc_requestUri:=valueof(m_SipUrl_currDomain(vc_userprofile))
      
	}// end function setHeaders_SUBSCRIBE
	
	
	/**
	 * 
	 * @desc setting of general and basic REFER header fields
	 * 		in additon to the addresses (To, From, ReqUri)
	 * @param p_cSeq_s
	 */
	function f_setHeadersREFER(inout CSeq p_cSeq_s) runs on SipComponent
	{      
	  f_setHeadersGeneral(p_cSeq_s, "REFER"); // cseq, contact, branch, via

	  vc_callId := { fieldName:=CALL_ID_E, callid:=f_getRndCallId(p_cSeq_s) & c_AT & vc_userprofile.currIpaddr };

	  vc_cancel_To := vc_to;
	  vc_caller_To := vc_to;
      
	  vc_caller_From := vc_from;
            
	  vc_reqHostPort := vc_requestUri.hostPort;
      
	}// end function f_setHeadersREFER

	/**
	 * 
	 * @desc This function reads all necessary headers from the received REGISTER message and generate the tag for the answer
	 * @param p_Request REGISTER that has been received
	 */
	function f_setHeadersOnReceiptOfREGISTER(Request p_Request)
	runs on SipComponent {

	  f_setHeadersOnReceiptOfRequest(p_Request);
	
	  vc_callId := p_Request.msgHeader.callId;
	  vc_caller_From := vc_from;
	  f_addTagInTo(vc_to);
	  vc_caller_To := vc_to;
	  vc_requestUri := p_Request.requestLine.requestUri;
      
	  vc_cancel_To := p_Request.msgHeader.toField;
      
	  if (ispresent(p_Request.msgHeader.contact)) {
		vc_reqHostPort := f_getContactAddr(p_Request.msgHeader.contact.contactBody.contactAddresses[0]);
	  	}
      
	  // update callee information and pick up tag if the call need to be canceled
	  vc_callee_To := {fieldName := TO_E,
		addressField := vc_caller_From.addressField,
		toParams := vc_caller_From.fromParams};
      
	  vc_callee_From := {fieldName := FROM_E,
		addressField := vc_caller_To.addressField,
		fromParams := vc_caller_To.toParams};
      
	} // end f_setHeadersOnReceiptOfREGISTER

	/**
	 * 
	 * @desc This function reads all necessary headers from the received SUBSCRIBE message and generate the tag for the answer
	 * @param p_Request SUBSCRIBE that has been received
	 */
	function f_setHeadersOnReceiptOfSUBSCRIBE(Request p_Request)
	runs on SipComponent {

	  f_setHeadersOnReceiptOfRequest(p_Request);
	
	  vc_callId := p_Request.msgHeader.callId;
	  vc_caller_From := vc_from;
	  f_addTagInTo(vc_to);
	  vc_caller_To := vc_to;
	  vc_requestUri := p_Request.requestLine.requestUri;
      
	  vc_cancel_To := p_Request.msgHeader.toField;
      
	  if (ispresent(p_Request.msgHeader.contact)) {
		vc_reqHostPort := f_getContactAddr(p_Request.msgHeader.contact.contactBody.contactAddresses[0]);
		}
      
	  // update callee information and pick up tag if the call need to be canceled
	  vc_callee_To := {fieldName := TO_E,
		addressField := vc_caller_From.addressField,
		toParams := vc_caller_From.fromParams};
      
	  vc_callee_From := {fieldName := FROM_E,
		addressField := vc_caller_To.addressField,
		fromParams := vc_caller_To.toParams};
      
	} // end f_setHeadersOnReceiptOfSUBSCRIBE
   
	/**
	 * 
	 * @desc function reads all necessary headers from 
	 * the received INVITE message and generate the tag for the answer
	 * @param p_Request received INVITE message
	 * @verdict 
	 */
	function f_setHeadersOnReceiptOfINVITE(Request p_Request) runs on SipComponent {

	f_setHeadersOnReceiptOfRequest(p_Request);

	vc_callId := p_Request.msgHeader.callId;

	vc_requestUri := p_Request.requestLine.requestUri;
      
	vc_cancel_To := p_Request.msgHeader.toField;
	f_addTagInTo(vc_to);
	vc_caller_From := vc_from;
	vc_caller_To := vc_to;
      
	if (ispresent(p_Request.msgHeader.contact)) {
	   	vc_reqHostPort := 
	  	f_getContactAddr(p_Request.msgHeader.contact.contactBody.contactAddresses[0]);
	 	};
      
	 // update callee information and pick up tag if the call need to be canceled
	vc_callee_To := {fieldName := TO_E,
	   addressField := vc_caller_From.addressField,
	   toParams := vc_caller_From.fromParams};
      
	vc_callee_From := {fieldName := FROM_E,
	   addressField := vc_caller_To.addressField,
	   fromParams := vc_caller_To.toParams};
        
	if (ispresent(p_Request.msgHeader.privacy)) {
		vc_privacy := p_Request.msgHeader.privacy;
		};
        
	if (ispresent(p_Request.messageBody)) { 
		//cleaning of attributes before assignment
		if (ispresent(vc_sdp_remote.media_list))
		{
				for (var integer i:=0; i<sizeof(vc_sdp_remote.media_list); i:=i+1)
				{			
						if (ispresent(vc_sdp_remote.media_list[i].attributes))
						{
							for (var integer j:=0; j<sizeof(vc_sdp_remote.media_list[i].attributes); j:=j+1)
							{		  	
					vc_sdp_remote.media_list[i].attributes[j] := omit ; 
							}
						}
				};
		}		
		
		// save SDP if present
		if ( ischosen(p_Request.messageBody.sdpMessageBody)) 
		{
			vc_sdp_remote := p_Request.messageBody.sdpMessageBody;		  
			vc_sdp_remote_is_valid := true;
   			f_prepare_SDP_answer();			 			
		};
		
		// save XML if present
		if ( ischosen(p_Request.messageBody.xmlBody))
		{
			vc_xml_remote := p_Request.messageBody.xmlBody;		 			
		}

		if ( ischosen(p_Request.messageBody.mimeMessageBody))
		{
			
			for (var integer j:=0; j<sizeof(p_Request.messageBody.mimeMessageBody.mimeEncapsulatedList); j:=j+1){
				if (match(p_Request.messageBody.mimeMessageBody.mimeEncapsulatedList[j].content_type,c_sdpAplication))
				{
					vc_sdp_remote := p_Request.messageBody.mimeMessageBody.mimeEncapsulatedList[j].mime_encapsulated_part.sdpMessageBody;
					vc_sdp_remote_is_valid := true;
					f_prepare_SDP_answer();
				};
				if (match(p_Request.messageBody.mimeMessageBody.mimeEncapsulatedList[j].content_type,c_xmlAplication))
				{
					vc_xml_remote := p_Request.messageBody.mimeMessageBody.mimeEncapsulatedList[j].mime_encapsulated_part.xmlBody;
				};			
			}	
		}	
	};
	   
	if (ispresent(p_Request.msgHeader.supported.optionsTags)) {
		for (var integer i := sizeof(p_Request.msgHeader.supported.optionsTags); i>0; i:=i-1)
		   {
			   if (p_Request.msgHeader.supported.optionsTags[i-1]=="100rel")
			   { vc_supported_100rel := true };
			   if (p_Request.msgHeader.supported.optionsTags[i-1]=="precondition")
			   { vc_supported_precondition := true }
		   }
	   	};
      
   } // end f_setHeadersOnReceiptOfINVITE

	/**
	 * 
	 * @desc function reads header field of a received BYE message 
	 * @param p_Request received BYE
	 */
   function f_setHeadersOnReceiptOfBYE(Request p_BYE_Request)
   runs on SipComponent
   {
   	
	 f_setHeadersOnReceiptOfRequest(p_BYE_Request);
	 vc_callId := p_BYE_Request.msgHeader.callId;

   } // end f_setHeadersOnReceiptOfBYE

	/**
	 * 
	 * @desc function reads header field from an incoming Request message
	 * @param p_Request received Request message
	 */
	function f_setHeadersOnReceiptOfRequest(Request p_Request) runs on SipComponent {
     vc_request := p_Request;
	 vc_cSeq := p_Request.msgHeader.cSeq;
	 vc_iut_CSeq  := p_Request.msgHeader.cSeq;
	 vc_from := p_Request.msgHeader.fromField;
	 vc_to := p_Request.msgHeader.toField;
	 vc_via := p_Request.msgHeader.via;
	 // update sent_label according to received via header field
	 f_getViaReplyAddr(vc_via.viaBody, vc_sent_label);
	  
	 // Catch route
	 vc_boo_recordRoute:=false;
	  
	 if (ispresent(p_Request.msgHeader.recordRoute))
	 {
	   vc_boo_recordRoute:=true;
	   vc_recordRoute := p_Request.msgHeader.recordRoute;
	 }
   	} // end f_setHeadersOnReceiptOfRequest

	/**
	 * 
	 * @desc functions reads header fields from an incoming Response message
	 * @param p_cSeq
	 * @param p_response received response message
	 * @verdict 
	 */
    function f_setHeadersOnReceiptOfResponse(inout CSeq p_cSeq, Response p_response) runs on SipComponent
   {
	 var integer v_i, v_j, v_nbroute;
	 var Contact v_contact; //only for local purpose
	       
	 vc_response := p_response;
	 //vc_cSeq := p_cSeq; //must not save global c_seq because it can overwrite temporary cSeq
	 vc_to :=p_response.msgHeader.toField;
	 vc_from :=p_response.msgHeader.fromField;
	 vc_caller_To := vc_to;
	 vc_caller_From := vc_from;
      
	 if (ispresent(p_response.msgHeader.contact))
	 {
	   v_contact := p_response.msgHeader.contact;
	   if (ischosen(v_contact.contactBody.contactAddresses))
	   {
		 vc_reqHostPort := f_getContactAddr(v_contact.contactBody.contactAddresses[0]);
	   }
	 }
	 else
	 {
	   if (ischosen(vc_to.addressField.nameAddr))
	   {
		 vc_reqHostPort := vc_to.addressField.nameAddr.addrSpec.hostPort;
	   }
	   else
	   {
		 vc_reqHostPort := vc_to.addressField.addrSpecUnion.hostPort;
	   }
	 }
      
	 vc_callee_To:={fieldName := TO_E,
	   addressField := vc_caller_From.addressField,
	   toParams := vc_caller_From.fromParams};
      
	 vc_callee_From:= {fieldName := FROM_E,
	   addressField := vc_caller_To.addressField,
	   fromParams := vc_caller_To.toParams};
	 
	 vc_via:= p_response.msgHeader.via;
      
	 // Route Management
	 if (ispresent(p_response.msgHeader.recordRoute))
	 {
	   vc_recordRoute := p_response.msgHeader.recordRoute;
	   v_nbroute := sizeof(vc_recordRoute.routeBody);
	   // copy and reverse the order of the routes in route header
	   for (v_i:=0; v_i<=(v_nbroute - 1); v_i:=v_i+1)
	   {
		 v_j:= v_nbroute - 1 - v_i;
		 vc_route.routeBody[v_j]:=vc_recordRoute.routeBody[v_i];
	   }
	   vc_route.fieldName := ROUTE_E;
	   vc_boo_recordRoute := true;
	   vc_boo_route := true;
	 }
	 else
	 {
	   vc_boo_recordRoute := false;
	   vc_boo_route := false;
	 };


	 // extentions due to new fields in PRACK and UPDATE messages
	 if (ispresent(p_response.msgHeader.rSeq)) {
	 	vc_rAck := 
			 { fieldName := RACK_E, 
			   responseNum := valueof(p_response.msgHeader.rSeq.responseNum),
			   seqNumber := valueof(p_response.msgHeader.cSeq.seqNumber),
			   method := valueof(p_response.msgHeader.cSeq.method)
			 };
		 };

	 // extentions due to new HistoryInfo fields 180 or 200OK messages
	 if (ispresent(p_response.msgHeader.historyInfo)) {
	 	vc_historyInfoList := valueof(p_response.msgHeader.historyInfo.historyInfoList);
		vc_history_is_valid := true
		}
	   else {vc_history_is_valid := false};

	 //sdpMessageBody answer
	 if (ispresent(p_response.messageBody)) { 
		if ( ischosen(p_response.messageBody.sdpMessageBody))
		{
			vc_sdp_remote := p_response.messageBody.sdpMessageBody;		  
			vc_sdp_remote_is_valid := true;
	 	}

		if ( ischosen(p_response.messageBody.xmlBody))
		{
			vc_xml_remote := p_response.messageBody.xmlBody;		 			
		}

		if ( ischosen(p_response.messageBody.mimeMessageBody))
		{
			
			for (var integer j:=0; j<sizeof(p_response.messageBody.mimeMessageBody.mimeEncapsulatedList); j:=j+1){
				if (match(p_response.messageBody.mimeMessageBody.mimeEncapsulatedList[j].content_type,c_sdpAplication))
				{
					vc_sdp_remote := p_response.messageBody.mimeMessageBody.mimeEncapsulatedList[j].mime_encapsulated_part.sdpMessageBody;
				};
				if (match(p_response.messageBody.mimeMessageBody.mimeEncapsulatedList[j].content_type,c_xmlAplication))
				{
					vc_xml_remote := p_response.messageBody.mimeMessageBody.mimeEncapsulatedList[j].mime_encapsulated_part.xmlBody;
				};			
			}	
		}
	 };

   }// end function f_setHeadersOnReceiptOfResponse

   /**
	* 
	* @desc functions reads ServiceRoute header field from an incoming 200 Response message in registration
	* @param p_cSeq
	* @param p_response received response message
	*/
    function f_getServiceRouteMapIntoRouteInRegistration(inout CSeq p_cSeq, Response p_response) runs on SipComponent
   {
	 var integer v_i, v_j, v_nbroute;
	 var ServiceRoute v_serviceRoute;
	       
	 // Route Management
	 if (ispresent(p_response.msgHeader.serviceRoute))
	 {
	   v_serviceRoute := p_response.msgHeader.serviceRoute;
	   v_nbroute := sizeof(v_serviceRoute.routeBody);
	   // copy and reverse the order of the routes in route header
	   for (v_i:=0; v_i<=(v_nbroute - 1); v_i:=v_i+1)
	   {
		 v_j:= v_nbroute - 1 - v_i;
		 vc_route.routeBody[v_j]:=v_serviceRoute.routeBody[v_i];
	   }
	   vc_route.fieldName := ROUTE_E;
	 }

   }// end function f_getServiceRouteMapIntoRouteInRegistration


} // end group SetHeaders
   
} // end group FieldOperations

group SDPOperations{
	
	/** 
	*  @desc check if message body include SDP attribute (2nd parameter)
	*        for any media 
	*		 
	*/
	function f_check_attribute(in SDP_Message p_sdp, in template SDP_attribute p_attribute) runs on SipComponent return boolean {
		
    	if (ispresent(p_sdp.media_list)) {
    		for (var integer j:=0; j<sizeof(p_sdp.media_list); j:=j+1){			
    			if (ispresent(p_sdp.media_list[j].attributes)) {
    				for (var integer i:=0; i<sizeof(p_sdp.media_list[j].attributes); i:=i+1){			
    					if (match(p_sdp.media_list[j].attributes[i],p_attribute)) 
    						{return(true);};
    					};
    			}
    			};
    	}
    	if (ispresent(p_sdp.attributes)) {
    		for (var integer j:=0; j<sizeof(p_sdp.attributes); j:=j+1){			
    			if (match(p_sdp.attributes[j],p_attribute)) {return(true);};
    			};
    	}
    	
    	return(false);
	}		
	
	/**
	 * 
	 * @desc 	identify an SDP direction attribute (session or first media attribute) in a SDP message and return its answer value
	 * @param 	p_sdp 		the SDP message that has been received
	 * @param 	p_attribute incoming SDP attribute that need to be used for the SDP direction (answer)
	 * @return 	the new attribute (to be send out) derived from the incoming SDP value
	 * @verdict 
	 */
	function f_get_attribute_answer(in SDP_Message p_sdp, in template SDP_attribute p_attribute) runs on SipComponent return SDP_attribute {

		var template SDP_attribute v_attribute := p_attribute;
		// check if the selected attribute is included in the SDP offer (session attributes)
		if (ispresent(p_sdp.attributes)) {
			for (var integer j:=0; j<sizeof(p_sdp.attributes); j:=j+1){			
				if (match(p_sdp.attributes[j],p_attribute)) {v_attribute := p_sdp.attributes[j];};
				};
		}

		// check if the selected attribute is included in the SDP offer (any of the media attributes)
		if (ispresent(p_sdp.media_list)) {
			for (var integer j:=0; j<sizeof(p_sdp.media_list); j:=j+1){			
				if (ispresent(p_sdp.media_list[j].attributes)) {
					for (var integer i:=0; i<sizeof(p_sdp.media_list[j].attributes); i:=i+1){			
						if (match(p_sdp.media_list[j].attributes[i],p_attribute)) 
							{v_attribute := p_sdp.media_list[j].attributes[i];};
						};
				}
				};
		}
		select (v_attribute)
		{
			case (mw_attribute_sendonly) {return(valueof(m_attribute_recvonly));}
			case (mw_attribute_sendrecv) {return(valueof(m_attribute_sendrecv));}//MRO	
			case (mw_attribute_inactive) {return(valueof(m_attribute_inactive));}//MRO
			case (mw_attribute_recvonly) {return(valueof(m_attribute_sendonly));}//MRO
		}
		return(valueof(m_attribute_sendrecv));//the default return value in case of missing attribute offer  
	}

	/** 
	*  @desc check if message body include SDP bandwidth (2nd parameter)
	*		 either for the session or a media description
	*/
	function f_check_bandwidth(in SDP_Message loc_sdp, in template SDP_bandwidth loc_bandw) runs on SipComponent return boolean {
	
					if (ispresent(loc_sdp.bandwidth)) {
						for (var integer j:=0; j<sizeof(loc_sdp.bandwidth); j:=j+1){			
							if (match(loc_sdp.bandwidth[j],loc_bandw)) {return(true);};
							};
					};
					if (ispresent(loc_sdp.media_list)) {
						for (var integer j:=0; j<sizeof(loc_sdp.media_list); j:=j+1){
							if (ispresent(loc_sdp.media_list[j].bandwidth)) {						
								if (match(loc_sdp.media_list[j].bandwidth,loc_bandw)) {return(true);};
								};
							};
					};
    	
		return(false);
	}

	/** 
	*  @desc check if message body include SDP media (2nd parameter)
	*		 
	*/
	function f_check_media(in SDP_Message loc_sdp, in template SDP_media_desc loc_media) runs on SipComponent return boolean {
		
    	if (ispresent(loc_sdp.media_list)) {
    		for (var integer j:=0; j<sizeof(loc_sdp.media_list); j:=j+1){			
    			if (match(loc_sdp.media_list[j].media_field.transport,loc_media.media_field.transport) and
    				match(loc_sdp.media_list[j].media_field.fmts,loc_media.media_field.fmts)) 
    				{return(true);};
    			};
    	}
    	return(false);
poglitsch's avatar
poglitsch committed
	}
	
	/**
	 * @desc
	 *     check if message body include precondition mechanism (a=des and
	 *     a=curr) retrun true, else false
	 * @param loc_sdp SDP message
	 */
	function f_check_precondition(in SDP_Message loc_sdp) runs on SipComponent return boolean {
    	if (f_check_attribute(loc_sdp, mw_attribute_des) or
    		f_check_attribute(loc_sdp, mw_attribute_curr)) 
    		{return(true);}
    		
    	return(false);
	}	
			

	/** 
	*  @desc check if message body include SDP media direction return true, else false
	*		 
	*/
	function f_check_media_direction(in SDP_Message loc_sdp) runs on SipComponent return boolean {
		
    	if (f_check_attribute(loc_sdp, mw_attribute_sendonly) or
    		f_check_attribute(loc_sdp, mw_attribute_recvonly) or
    		f_check_attribute(loc_sdp, mw_attribute_sendrecv) or
    		f_check_attribute(loc_sdp, mw_attribute_inactive)) 
    		{return(true);}
    		
    	return(false);
	}		

	/** 
	*  @desc copy media/attribute lines from remote to local SDP variable
	*		 
	*/
	function f_check_SDP(integer loc_sdp, integer loc_codec) runs on SipComponent
	return boolean
	{
		var SDP_media_desc v_media := f_prepare_media(loc_sdp,loc_codec);
		log("log0"); 
		if (vc_sdp_remote.media_list[0].media_field.media != v_media.media_field.media)
			{ log("log1"); return false };
		if (vc_sdp_remote.media_list[0].media_field.transport != v_media.media_field.transport)
			{ log("log2"); return false };
		if (vc_sdp_remote.media_list[0].media_field.fmts != v_media.media_field.fmts)
			{ log("remote:",vc_sdp_remote.media_list[0].media_field.fmts,"expect:",v_media.media_field.fmts); return false };
			
		return true
	}
	
poglitsch's avatar
poglitsch committed
	/**
	 * @desc replace the first curr media attribute with the given value.
	 * @param p_sdp SDP message to modify
	 * @param p_curr new curr attribute
	 */
	function f_replace_curr_attribute(inout SDP_Message p_sdp, in SDP_attribute_curr p_curr) {
		if(ispresent(p_sdp.media_list)) {
			var integer mn := sizeof(p_sdp.media_list[0].attributes);
			for(var integer i := 0; i<=mn; i := i+1) {
				if(ischosen(p_sdp.media_list[0].attributes[i].curr)){
					p_sdp.media_list[0].attributes[i].curr := p_curr;
					i:=mn; 
				}	
			}
		}
	}
	
	/**
	 * @desc append new media attribute to the first media description.
	 * @param p_sdp SDP message to modify
	 * @param p_att SDP attribute to appand
	 */
	function f_append_media_attribute(inout SDP_Message p_sdp, in SDP_attribute p_att) {
		if(ispresent(p_sdp.media_list)) {
			var integer mn := sizeof(p_sdp.media_list[0].attributes);
			p_sdp.media_list[0].attributes[mn] := p_att;
		}
	}
	
	
	/** 
	*  @desc append new media to the existing media list in SDP
	*		 
	*/
	function f_append_media(inout SDP_Message loc_SDP, in template SDP_media_desc loc_media)
	{
		var integer mn := sizeof(loc_SDP.media_list);
		loc_SDP.media_list[mn] := valueof(loc_media);
	}
	/** 
	*  @desc repare media/attribute lines
	*		 
	*/
	function f_prepare_media(integer loc_sdp, integer loc_codec) runs on SipComponent
	return SDP_media_desc
	{
		var charstring v_codecs[32] := {
			"PCMU/8000", "GSM/8000", "G723/8000", "DVI4/8000",
			"DVI4/16000", "LPC/8000", "PCMA/8000", "G722/8000",
			"L16/44100/2", "L16/44100", "QCELP/8000", "CN/8000",
			"MPA/90000", "G728/8000", "DVI4/11025", "DVI4/22050",
			"G729/8000", "G726-40/8000", "G726-32/8000", "G726-24/8000",
			"G726-16/8000", "G726D/8000", "G726E/8000", "GSM-EFR/8000",
			"CelB/90000", "JPEG/90000", "Nv/90000", "H261/90000",
			"MPV/90000", "MP2T/90000", "H263/90000", "H263-1998/90000"
		}
		var SDP_media_desc v_media :=
			{
				media_field := {
					media := "audio",
					ports := { 
						port_number := 10000, 
						num_of_ports:=omit },
					transport := "RTP/AVP",
					fmts := { "0" }
				}, //m=audio 8500 RTP/AVP 0
				information := omit,
				connections := omit,
				bandwidth := omit,
				key := omit,						
				attributes := omit
			};
			
		if (32<loc_codec or loc_codec<1) {
			log("Unexpected SDP variant");
			setverdict(inconc); 
			return (v_media)}

		if (loc_sdp == 1) {}
		else if (loc_sdp == 2) {
			v_media.media_field.fmts := {PX_SIP_SDP_dyn}; //{ "98", "0" };
			v_media.attributes := {{
			rtpmap := { attr_value := PX_SIP_SDP_dyn & " " & v_codecs[loc_codec-1] } // PX_SIP_SDP_dyn := 98
			}}
		} else if (loc_sdp == 3) {
			v_media.media_field.fmts := { "8" }
		} else if (loc_sdp == 4) {
			v_media.media_field.fmts := { "99", "8" };
			v_media.attributes := {{
				rtpmap := { attr_value := "99 " & v_codecs[loc_codec-1] }
						}}
		} else if (loc_sdp == 5) {
			v_media.media_field.media := "image";
			v_media.media_field.transport := "udptl";
			v_media.media_field.fmts := { "t38" }
		} else if (loc_sdp == 6) {
			v_media.media_field.media := "image";
			v_media.media_field.transport := "tcptl";
			v_media.media_field.fmts := { "t38" }
		} else {
			log("Unexpected SDP variant"); setverdict(inconc) 
		};
			
		return (v_media);
	}

	/** 
	*  @desc repare media/attribute lines
	*		 
	*/
	function f_prepare_SDP(integer loc_sdp, integer loc_codec) runs on SipComponent
	{

		vc_sdp_local.media_list := {f_prepare_media(loc_sdp,loc_codec)};
	}
	
	/**
	* 
	* @desc function that copy media/attribute lines from remote to local SDP variable
	*/
	function f_prepare_SDP_answer() runs on SipComponent
	{
		var integer mn, cn := 0, i, j, k :=0;
		var charstring v_PT, v_rtpmap := "";
		var SDP_attribute_list v_mediaAttributes := {};
		//increase session version
		vc_sdp_local.origin.session_version := 	int2str(str2int(vc_sdp_local.origin.session_version)+1);
		// if more than one codec, select the firs one
		mn:= sizeof(vc_sdp_local.media_list);
		for (i :=0;  i < mn; i := i+1)
		{
			//for every single media
			if (ispresent(vc_sdp_local.media_list[i].attributes))
			{
				cn := sizeof(vc_sdp_local.media_list[i].attributes);
			};
			if (sizeof(vc_sdp_local.media_list[i].media_field.fmts)>1) 
			{
				// select the first one
				v_PT := vc_sdp_local.media_list[i].media_field.fmts[0];
				vc_sdp_local.media_list[i].media_field.fmts := {v_PT};
				for (j :=0; j<cn; j:=j+1)
				{
					if (ischosen(vc_sdp_local.media_list[i].attributes[j].rtpmap))
					{
						if (v_PT == regexp(vc_sdp_local.media_list[i].attributes[j].rtpmap.attr_value,	"[ \t]#(0,)([/d]+)*",	0))
						{
							v_rtpmap := vc_sdp_local.media_list[i].attributes[j].
							rtpmap.attr_value;
							v_mediaAttributes[k] := {rtpmap := {attr_value := v_rtpmap}};
							k := k+1;
						} // else line is not copied
					}


					// simplified handling of status attributes (copy/keep status from peer):
					// a) copy/keep SDP_attribute_curr (invert tags if applicable)
					if (ischosen(vc_sdp_local.media_list[i].attributes[j].curr))
					{
						// invert local/remote status tags
						if (vc_sdp_local.media_list[i].attributes[j].curr.statusType == "local")
							{vc_sdp_local.media_list[i].attributes[j].curr.statusType := "remote"};
						if (vc_sdp_local.media_list[i].attributes[j].curr.statusType == "remote")
							{vc_sdp_local.media_list[i].attributes[j].curr.statusType := "local"};
						// invert send/recv direction tags
						if (vc_sdp_local.media_list[i].attributes[j].curr.direction == "send")
							{vc_sdp_local.media_list[i].attributes[j].curr.direction := "recv"};
						if (vc_sdp_local.media_list[i].attributes[j].curr.direction == "recv")
							{vc_sdp_local.media_list[i].attributes[j].curr.direction := "send"};
					}				
					// b) copy/keep SDP_attribute_des (keep strength, invert tags if applicable)	
					else if (ischosen(vc_sdp_local.media_list[i].attributes[j].des))
					{
						// invert local/remote status tags
						if (vc_sdp_local.media_list[i].attributes[j].des.statusType == "local")
							{vc_sdp_local.media_list[i].attributes[j].des.statusType := "remote"};
						if (vc_sdp_local.media_list[i].attributes[j].des.statusType == "remote")
							{vc_sdp_local.media_list[i].attributes[j].des.statusType := "local"};
						// invert send/recv direction tags
						if (vc_sdp_local.media_list[i].attributes[j].des.direction == "send")
							{vc_sdp_local.media_list[i].attributes[j].des.direction := "recv"};
						if (vc_sdp_local.media_list[i].attributes[j].des.direction == "recv")
							{vc_sdp_local.media_list[i].attributes[j].des.direction := "send"};
					}				
					// c) simplification: assume no SDP_attribute_conf	
					else if (ischosen(vc_sdp_local.media_list[i].attributes[j].conf))
					{
						// todo: handle SDP_attribute_conf
					}					
						
					 
					else 
					{
						// simple copy of attribute
						v_mediaAttributes[k] := vc_sdp_local.media_list[i].attributes[j];
						k := k+1;
					}
				}
				vc_sdp_local.media_list[i].attributes := v_mediaAttributes;
			}
		}
		// add handling of prenegotiation, change ports if required etc.
		//if prenegotiation...
	}

	/** 
	*  @desc reject SDP offer by setting media ports to 0
	*		 
	*/
	function f_reject_SDP_offer() runs on SipComponent
	{
		var integer mn, i;
		f_copy_SDP(); // TO BE DONE with more details!
		//increase session version
		vc_sdp_local.origin.session_version := int2str(str2int(vc_sdp_local.origin.session_version)+1);
		// if more than one codec, select the firs one
		mn:= sizeof(vc_sdp_local.media_list);
		for (i :=0;  i < mn; i := i+1)
		{
			vc_sdp_local.media_list[i].media_field.ports := {0, omit};
			vc_sdp_local.media_list[i].attributes := omit; //{};
		};
	}

rennoch's avatar
rennoch committed
	/**
	 * 
	 * @desc 	copies SDP message elements from remote to local component variable: 
	 * 				- bandwidth
	 * 				- session version (will be incremented)
	 * 				- media list
	 * 			modify the direction attribute of an SDP media list entry within an SDP message (vc_sdp_local)
	 * @param 	p_medianum 		list position number of the media (if value 0 identifies first media list element)
	 * @param 	p_direction		the new direction attribute to be included in the media entry
	 * @verdict 
	 */
	function f_SIP_modMediaDirection(integer p_medianum, template SDP_attribute p_direction) runs on SipComponent
	{
rennoch's avatar
rennoch committed
		var boolean v_set_direction; // flag indicates if direction attribute has been modified
		var integer v_mn := 0; 		 // length of media list (number of entries)
		var integer v_cn := 0; 		 // number of attributes of a media entry
		var integer i, j, k := 0;
		var SDP_attribute_list v_mediaAttributes := {}; // collect the media attributes (to be assigned at end of function)
		
		f_copy_SDP(); // copy SDP session bandwidth and media list from remote to local component variable
		
		// increment session version
		vc_sdp_local.origin.session_version := int2str(str2int(vc_sdp_local.origin.session_version)+1);
rennoch's avatar
rennoch committed
		
		// if more than one codec, select the first one
		v_mn:= sizeof(vc_sdp_local.media_list);

		if (p_medianum == 0) //specific media requested
rennoch's avatar
rennoch committed
    		{
    			p_medianum := 1; // start checking from first media
    		};
		if (p_medianum > 0) //specific media requested
rennoch's avatar
rennoch committed
    		{
    			if (not(p_medianum > v_mn)) 
    				{v_mn := p_medianum}
    		};
    		
		// handling of media list elements
		for (i :=0;  i < v_mn; i := i+1)
		{
rennoch's avatar
rennoch committed
			v_cn := 0; // initialize the number of attributes of the media list entry
rennoch's avatar
rennoch committed
			if (ispresent(vc_sdp_local.media_list)) //media_list is optional
			{						
//				log("vc_sdp_local.media_list[i] ",vc_sdp_local.media_list[i]);
				if (ispresent(vc_sdp_local.media_list[i].attributes))
				{
					v_cn := sizeof(vc_sdp_local.media_list[i].attributes);
				};
				
				v_set_direction := false;
				//if (sizeof(vc_sdp_local.media_list[i].media_field.fmts)>1) 
				// select the first one
rennoch's avatar
rennoch committed
				for (j :=0; j<v_cn; j:=j+1)
				{ 
					if (ischosen(vc_sdp_local.media_list[i].attributes[j].recvonly)
							or ischosen(vc_sdp_local.media_list[i].attributes[j].sendonly)
							or ischosen(vc_sdp_local.media_list[i].attributes[j].inactive)
							or ischosen(vc_sdp_local.media_list[i].attributes[j].sendrecv))
						{
							v_mediaAttributes[k] := valueof(p_direction);