*** chan_sip.c.original	2012-11-08 10:26:04.470095160 +0700
--- chan_sip.c	2012-11-10 06:49:32.346095225 +0700
***************
*** 268,273 ****
--- 268,274 ----
  #include "asterisk/aoc.h"
  #include "asterisk/message.h"
  #include "sip/include/sip.h"
+ #include "sip/include/sip_isup.h"
  #include "sip/include/globals.h"
  #include "sip/include/config_parser.h"
  #include "sip/include/reqresp_parser.h"
***************
*** 1375,1380 ****
--- 1375,1385 ----
  static int sip_sipredirect(struct sip_pvt *p, const char *dest);
  static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod method);
  
+ /* SIP ISUP Handling */
+ static int find_isup(struct sip_request *req, char *original_buffer, struct ast_sockaddr *addr);
+ static int process_isup(struct sip_request *req, struct sip_pvt *p, struct sip_isup_buffer isup_buffer);
+ static int decode_parm(struct sip_pvt *p, struct sip_isup_buffer, int parm, unsigned char *parmbuf, int maxlen, int parmtype, int tx);
+ 
  /*--- Codec handling / SDP */
  static void try_suggested_sip_codec(struct sip_pvt *p);
  static const char *get_sdp_iterate(int* start, struct sip_request *req, const char *name);
***************
*** 6894,6899 ****
--- 6899,7147 ----
  	return 0;
  }
  
+ /*! \ Find ISUP & process message */
+ static int find_isup(struct sip_request *req, char *originalbuffer, struct ast_sockaddr *addr)
+ {
+         const char *content_type;
+         const char *content_length;
+         const char *search;
+ 	const char *isup_header;
+ 	const char *last_isup_header;
+ 	struct sip_pvt *p;
+ 	struct sip_isup_buffer isup_buffer;	
+ 	char *boundary_label;	
+ 
+ 	char * pch;
+ 	char * isup_offset;
+ 	char * last_pstr;
+ 	int isup_found=FALSE; 
+ 
+         unsigned int x;
+ 
+         content_length = sip_get_header(req, "Content-Length");
+         if (!ast_strlen_zero(content_length)) {
+                 if (sscanf(content_length, "%30u", &x) != 1) {
+                         ast_log(LOG_WARNING, "Invalid Content-Length: %s\n", content_length);
+                         return 0;
+                 }
+ 
+                 /* Content-Length of zero means there can't possibly be an
+                    ISUP here, even if the Content-Type says there is */
+                 if (x == 0)
+                         return 0;
+         }
+ 
+         content_type = sip_get_header(req, "Content-Type");
+         /* if it's not multipart/mixed, there cannot be an ISUP */
+         if (strncasecmp(content_type, "multipart/mixed", 15))
+                 return 0;
+ 
+ 	/* if there is no boundary marker, it's invalid */
+         if ((search = strcasestr(content_type, ";boundary=")))
+         {	search += 10;
+ 		
+ 	}
+         else if ((search = strcasestr(content_type, "; boundary=")))
+ 	{
+                 search += 11;
+ 	}
+         else
+                 return 0;
+ 
+         if (ast_strlen_zero(search))
+                 return 0;
+ 
+ 	boundary_label=malloc(strlen(search)+4); /* alloc boundary label with -- -- */
+ 	/* make boundary label copy */
+ 	strcpy(boundary_label,"--");
+ 	strcat(boundary_label,search);
+ 	strcat(boundary_label,"--");
+ 	boundary_label[strlen(search)+4]='\0';
+ 
+ 	for (x = 0; x < (req->lines); x++) {
+                 const char *line = REQ_OFFSET_TO_STR(req, line[x]);
+                 if (!strncasecmp(line, "Content-Type: application/isup",30))
+ 		{	
+ 			isup_found = TRUE;
+ 			if (strcasestr(line, "version=itu")) isup_buffer.version=1;
+ 			else if(strcasestr(line, "version=ansi")) isup_buffer.version=2;
+ 			else if(strcasestr(line, "version=etsi")) isup_buffer.version=3;
+ 			else if(strcasestr(line, "version=iso")) isup_buffer.version=4;
+ 			else isup_buffer.version=0;
+ 			isup_header=last_isup_header=line;
+ 			pch = strstr (originalbuffer,isup_header); /* check in original buffer */
+ 		}
+ 		if(isup_found)
+ 		{	if (ast_strlen_zero(line))
+ 				break;
+ 			last_isup_header=line;
+ 		}
+         }
+ 	if (!isup_found) return 0; /* No ISUP multipart, exit */
+ 
+ 	/* Find next ISUP Header in original buffer, req untrusted for binary */
+ 	while (pch!=NULL)
+ 	{	last_pstr=pch; /* OK, found */
+ 		pch=strstr(pch+1,last_isup_header);
+ 	}
+ 	pch=last_pstr;
+ 
+ 	/* ISUP encapsulation start here with 4 byte CR+LF before */
+ 	isup_offset=pch+strlen(last_isup_header)+4;
+ 	/* Find end of ISUP encapsulation, cause this binary must one by one */
+ 	for (x = 0;x <= (65536-(pch-originalbuffer)); x++)
+ 	{	pch=strstr(isup_offset+x,boundary_label);
+ 		if (pch!=NULL)	{
+ 			isup_buffer.message_length=pch-isup_offset-2; /* CR + LF*/;
+ 			isup_buffer.message=malloc(isup_buffer.message_length);
+ 			memcpy(isup_buffer.message,isup_offset,isup_buffer.message_length);
+ 			break;
+ 		}
+ 	}
+ 	if (pch!=NULL)
+ 	{	p = find_call(req, addr, req->method);  /* returns p with a reference only. _NOT_ locked */
+ 		process_isup(req, p, isup_buffer); 
+ 		return TRUE;
+ 	}
+ 	else  
+ 		return FALSE;
+ }
+ static int process_isup(struct sip_request *req, struct sip_pvt *p, struct sip_isup_buffer isup_buffer)
+ {	int *parms = NULL;
+ 	unsigned char *opt_ptr = NULL;
+ 	int message_index = -1;
+ 	int fixedparams = 0, varparams = 0, optparams = 0;
+ 	int offset = 1; /* offset 0, Message_Type */
+ 	int res, x;
+ 	unsigned char message_type=isup_buffer.message[0];
+ 	int len=isup_buffer.message_length;
+ 
+ 	/* Find us in the message list */
+ 	for (x = 0; x < sizeof(sip_isup_messages)/sizeof(struct sip_isup_message_data); x++)
+ 		if (sip_isup_messages[x].messagetype == message_type)
+ 			message_index = x;
+ 
+ 
+ 	if (message_index < 0) {
+ 		ast_verbose("!! Unable to handle message of type 0x%x\n", message_type);
+ 		return -1;
+ 	}
+ 
+ 	fixedparams = sip_isup_messages[message_index].mand_fixed_params;
+ 	varparams = sip_isup_messages[message_index].mand_var_params;
+ 	parms = sip_isup_messages[message_index].param_list;
+ 	optparams = sip_isup_messages[message_index].opt_params;
+ 
+ 	if (isup_buffer.version == SIP_ISUP_ANSI) {
+ 	/* Check for the ANSI IAM exception */
+ 	if (sip_isup_messages[message_index].messagetype == SIP_IAM) {
+ 		/* Stupid ANSI SS7?, yup */
+ 		fixedparams = 3;
+ 		varparams = 2;
+ 		parms = sip_ansi_iam_params;
+ 	} else if (sip_isup_messages[message_index].messagetype == SIP_RLC) {
+ 		optparams = 0;
+ 		}
+ 	}
+ 
+ 	/* Parse fixed parms */
+ 	for (x = 0; x < fixedparams; x++) {
+ 		res = decode_parm(p, isup_buffer, parms[x], (void *) isup_buffer.message + offset, len, PARM_TYPE_FIXED, 0);
+ 		if (res < 0) {
+ 			ast_verbose("!! Unable to parse mandatory fixed parameter '%s'\n", param2str(parms[x]));
+ 			return -1;
+ 		}
+ 
+ 		len -= res;
+ 		offset += res;
+ 	}
+ 
+ 	if (varparams) {
+ 		offset += varparams; /* add one for the optionals */
+ 		res -= varparams;
+ 	}
+ 	if (optparams) {
+ 		/* ANSI doesn't have optional parameters on RLC */
+ 		opt_ptr = isup_buffer.message+(offset++);
+ 	}
+ 
+ 	for (; (x - fixedparams) < varparams; x++) {
+ 		res = decode_parm(p, isup_buffer, parms[x], (void *) isup_buffer.message + offset, len, PARM_TYPE_VARIABLE, 0);
+ 
+ 		if (res < 0) {
+ 			ast_verbose("!! Unable to parse mandatory variable parameter '%s'\n", param2str(parms[x]));
+ 			return -1;
+ 		}
+ 
+ 		len -= res;
+ 		offset += res;
+ 	}
+ 
+ 	/* Optional paramter parsing code */
+ 	if (optparams && *opt_ptr) {
+ 		while ((len > 0) && (isup_buffer.message[offset] != 0)) {
+ 			struct sip_isup_parm_opt *optparm = (struct sip_isup_parm_opt *) (isup_buffer.message+offset);
+ 			res = decode_parm(p, isup_buffer, isup_buffer.message[offset], (void *) isup_buffer.message + offset, len, PARM_TYPE_OPTIONAL, 0);
+ 			if (res < 0) {
+ 				ast_verbose("Unhandled optional parameter 0x%x '%s'\n", optparm->type, param2str(optparm->type));
+ 				sip_isup_dump_buffer(optparm->data, optparm->len);
+ 				res = optparm->len + 2;
+ 			}
+ 
+ 			len -= res;
+ 			offset += res;
+ 		}
+ 	}
+ 	return TRUE;
+ }
+ static int decode_parm(struct sip_pvt *p, struct sip_isup_buffer isup_buffer, int parm, unsigned char *parmbuf, int maxlen, int parmtype, int tx)
+ {	struct sip_isup_parm_opt *optparm = NULL;
+ 	int x;
+ 	int len=0;
+ 	char *parmname = "Unknown Parm";
+ 	int totalparms = sizeof(parms)/sizeof(struct parm_func);
+ 	for (x = 0; x < totalparms; x++) {
+ 		if (parms[x].parm == parm) {
+ 			if (parms[x].name)
+ 				parmname = parms[x].name;
+ //	                ast_verbose("\t\t%s:\n", parms[x].name ? parms[x].name : "Unknown");
+ 	                if (parms[x].decode) {
+                         switch (parmtype) {
+                                 case PARM_TYPE_FIXED:
+                                       len = parms[x].decode(p, isup_buffer, isup_buffer.version, parmbuf, maxlen);
+                                         break;
+                                 case PARM_TYPE_VARIABLE:
+                                       	parms[x].decode(p, isup_buffer, isup_buffer.version, parmbuf + 1, parmbuf[0]);
+                                         len = 1 + parmbuf[0];
+                                         break;
+                                 case PARM_TYPE_OPTIONAL:
+                                         optparm = (struct sip_isup_parm_opt *)parmbuf;
+                                       parms[x].decode(p, isup_buffer, isup_buffer.version, optparm->data, optparm->len);
+                                         len = 2 + optparm->len;
+                                         break;
+ 				}
+ 			} else {
+ 				switch (parmtype) {
+ 					case PARM_TYPE_VARIABLE:
+ 						len = parmbuf[0] + 1;
+ 						break;
+ 					case PARM_TYPE_OPTIONAL:
+ 						optparm = (struct sip_isup_parm_opt *)parmbuf;
+ 						len = optparm->len + 2;
+ 						break;
+ 				}
+ 			}
+ 			return len;
+ 
+ 		}
+ 		
+ 	}
+ 	/* This is if we don't find it....  */
+ 	optparm = (struct sip_isup_parm_opt *)parmbuf;
+ 	ast_verbose("\t\tUnknown Parameter (0x%x):\n", optparm->type);
+ 	return optparm->len + 2;
+ }
+ 
  /*! \brief Try setting codec suggested by the SIP_CODEC channel variable */
  static void try_suggested_sip_codec(struct sip_pvt *p)
  {
***************
*** 27747,27752 ****
--- 27995,28001 ----
  	req.socket.port = htons(ast_sockaddr_port(&bindaddr));
  
  	handle_request_do(&req, &addr);
+ 	find_isup(&req, readbuf, &addr);
  	deinit_req(&req);
  
  	return 1;
