Index: pri_facility.c
===================================================================
RCS file: /usr/cvsroot/libpri/pri_facility.c,v
retrieving revision 1.2
diff -u -r1.2 pri_facility.c
--- pri_facility.c	15 Dec 2004 20:15:28 -0000	1.2
+++ pri_facility.c	3 Feb 2005 06:18:07 -0000
@@ -16,56 +16,467 @@
 #include <stdlib.h>
 #include <string.h>
 
-int rose_invoke_decode(q931_call *call, unsigned char *data, int len)
+#undef DEBUG
+
+struct AddressingDataElements_PresentedNumberUnscreened {
+	char partyAddress[21];
+	char partySubaddress[21];
+	int  npi;
+	int  ton;
+	int  pres;
+};
+
+int redirectingreason_from_q931(struct pri *pri, int redirectingreason)
+{
+	switch(pri->switchtype) {
+	case PRI_SWITCH_QSIG:
+		switch(redirectingreason) {
+		case PRI_REDIR_UNKNOWN:
+			return QSIG_DIVERT_REASON_UNKNOWN;
+		case PRI_REDIR_FORWARD_ON_BUSY:
+			return QSIG_DIVERT_REASON_CFB;
+		case PRI_REDIR_FORWARD_ON_NO_REPLY:
+			return QSIG_DIVERT_REASON_CFNR;
+		case PRI_REDIR_UNCONDITIONAL:
+			return QSIG_DIVERT_REASON_CFU;
+		case PRI_REDIR_DEFLECTION:
+		case PRI_REDIR_DTE_OUT_OF_ORDER:
+		case PRI_REDIR_FORWARDED_BY_DTE:
+			pri_message("!! Don't know how to convert Q.931 redirection reason %d to Q.SIG\n", redirectingreason);
+			/* Fall through */
+		default:
+			return QSIG_DIVERT_REASON_UNKNOWN;
+		}
+	default:
+		switch(redirectingreason) {
+		case PRI_REDIR_UNKNOWN:
+			return Q952_DIVERT_REASON_UNKNOWN;
+		case PRI_REDIR_FORWARD_ON_BUSY:
+			return Q952_DIVERT_REASON_CFB;
+		case PRI_REDIR_FORWARD_ON_NO_REPLY:
+			return Q952_DIVERT_REASON_CFNR;
+		case PRI_REDIR_DEFLECTION:
+			return Q952_DIVERT_REASON_CD;
+		case PRI_REDIR_UNCONDITIONAL:
+			return Q952_DIVERT_REASON_CFU;
+		case PRI_REDIR_DTE_OUT_OF_ORDER:
+		case PRI_REDIR_FORWARDED_BY_DTE:
+			pri_message("!! Don't know how to convert Q.931 redirection reason %d to Q.952\n", redirectingreason);
+			/* Fall through */
+		default:
+			return Q952_DIVERT_REASON_UNKNOWN;
+		}
+	}
+}
+
+static int redirectingreason_for_q931(struct pri *pri, int redirectingreason)
+{
+	switch(pri->switchtype) {
+	case PRI_SWITCH_QSIG:
+		switch(redirectingreason) {
+		case QSIG_DIVERT_REASON_UNKNOWN:
+			return PRI_REDIR_UNKNOWN;
+		case QSIG_DIVERT_REASON_CFU:
+			return PRI_REDIR_UNCONDITIONAL;
+		case QSIG_DIVERT_REASON_CFB:
+			return PRI_REDIR_FORWARD_ON_BUSY;
+		case QSIG_DIVERT_REASON_CFNR:
+			return PRI_REDIR_FORWARD_ON_NO_REPLY;
+		default:
+			pri_message("!! Unknown Q.SIG diversion reason %d\n", redirectingreason);
+			return PRI_REDIR_UNKNOWN;
+		}
+	default:
+		switch(redirectingreason) {
+		case Q952_DIVERT_REASON_UNKNOWN:
+			return PRI_REDIR_UNKNOWN;
+		case Q952_DIVERT_REASON_CFU:
+			return PRI_REDIR_UNCONDITIONAL;
+		case Q952_DIVERT_REASON_CFB:
+			return PRI_REDIR_FORWARD_ON_BUSY;
+		case Q952_DIVERT_REASON_CFNR:
+			return PRI_REDIR_FORWARD_ON_NO_REPLY;
+		case Q952_DIVERT_REASON_CD:
+			return PRI_REDIR_DEFLECTION;
+		case Q952_DIVERT_REASON_IMMEDIATE:
+			pri_message("!! Dont' know how to convert Q.952 diversion reason IMMEDIATE to PRI analog\n");
+			return PRI_REDIR_UNKNOWN;	/* ??? */
+		default:
+			pri_message("!! Unknown Q.952 diversion reason %d\n", redirectingreason);
+			return PRI_REDIR_UNKNOWN;
+		}
+	}
+}
+
+int typeofnumber_from_q931(struct pri *pri, int ton)
+{
+	switch(ton) {
+	case PRI_TON_INTERNATIONAL:
+		return Q932_TON_INTERNATIONAL;
+	case PRI_TON_NATIONAL:
+		return Q932_TON_NATIONAL;
+	case PRI_TON_NET_SPECIFIC:
+		return Q932_TON_NET_SPECIFIC;
+	case PRI_TON_SUBSCRIBER:
+		return Q932_TON_SUBSCRIBER;
+	case PRI_TON_ABBREVIATED:
+		return Q932_TON_ABBREVIATED;
+	case PRI_TON_RESERVED:
+	default:
+		pri_message("!! Unsupported Q.931 TypeOfNumber value (%d)\n", ton);
+		/* fall through */
+	case PRI_TON_UNKNOWN:
+		return Q932_TON_UNKNOWN;
+	}
+}
+
+static int typeofnumber_for_q931(struct pri *pri, int ton)
+{
+	switch (ton) {
+	case Q932_TON_UNKNOWN:
+		return PRI_TON_UNKNOWN;
+	case Q932_TON_INTERNATIONAL:
+		return PRI_TON_INTERNATIONAL;
+	case Q932_TON_NATIONAL:
+		return PRI_TON_NATIONAL;
+	case Q932_TON_NET_SPECIFIC:
+		return PRI_TON_NET_SPECIFIC;
+	case Q932_TON_SUBSCRIBER:
+		return PRI_TON_SUBSCRIBER;
+	case Q932_TON_ABBREVIATED:
+		return PRI_TON_ABBREVIATED;
+	default:
+		pri_message("!! Invalid Q.932 TypeOfNumber %d\n", ton);
+		return PRI_TON_UNKNOWN;
+	}
+}
+
+int asn1_string_encode(unsigned char asn1_type, void *data, int len, int max_len, void *src, int src_len)
+{
+	struct rose_component *comp = NULL;
+	
+	if (len < 2 + src_len)
+		return -1;
+
+	if (max_len && (src_len > max_len))
+		src_len = max_len;
+
+	comp = (struct rose_component *)data;
+	comp->type = asn1_type;
+	comp->len = src_len;
+	memcpy(comp->data, src, src_len);
+	
+	return 2 + src_len;
+}
+
+static int rose_number_digits_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct AddressingDataElements_PresentedNumberUnscreened *value)
 {
 	int i = 0;
-	unsigned char *vdata = data;
 	struct rose_component *comp = NULL;
+	unsigned char *vdata = data;
 
-	if (len <= 0) return -1;
+	do {
+		GET_COMPONENT(comp, i, vdata, len);
+		CHECK_COMPONENT(comp, ASN1_NUMERICSTRING, "Don't know what to do with PublicPartyNumber ROSE component type 0x%x\n");
+		if(comp->len > 20) {
+			pri_message("!! Oversized NumberDigits component (%d)\n", comp->len);
+			return -1;
+		}
+		memcpy(value->partyAddress, comp->data, comp->len);
+		value->partyAddress[comp->len] = '\0';
 
-	/* Invoke ID stuff */
-	if (&vdata[i])
-		comp = (struct rose_component*)&vdata[i];
-	else return -1;
+		return 0;
+	}
+	while(0);
+	
+	return -1;
+}
+
+static int rose_public_party_number_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct AddressingDataElements_PresentedNumberUnscreened *value)
+{
+	int i = 0;
+	struct rose_component *comp = NULL;
+	unsigned char *vdata = data;
+	int ton;
 
-	if (i+1 >= len) return -1;
-	if (comp->type && comp->type != ASN1_INTEGER) {
-		pri_message("Don't know what to do if first ROSE component is of type 0x%x\n",comp->type);
+	if (len < 2)
 		return -1;
+
+	do {
+		GET_COMPONENT(comp, i, vdata, len);
+		CHECK_COMPONENT(comp, ASN1_ENUMERATED, "Don't know what to do with PublicPartyNumber ROSE component type 0x%x\n");
+		ASN1_GET_INTEGER(comp, ton);
+		NEXT_COMPONENT(comp, i);
+		ton = typeofnumber_for_q931(pri, ton);
+
+		if(rose_number_digits_decode(pri, call, &vdata[i], len-i, value))
+			return -1;
+		value->ton = ton;
+
+		return 0;
+
+	} while(0);
+	return -1;
+}
+
+static int rose_address_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct AddressingDataElements_PresentedNumberUnscreened *value)
+{
+	int i = 0;
+	struct rose_component *comp = NULL;
+	unsigned char *vdata = data;
+
+	do {
+		GET_COMPONENT(comp, i, vdata, len);
+
+		switch(comp->type) {
+		case 0xA0:	/* unknownPartyNumber */
+			if(rose_number_digits_decode(pri, call, comp->data, comp->len, value))
+				return -1;
+			value->npi = PRI_NPI_UNKNOWN;
+			value->ton = PRI_TON_UNKNOWN;
+			break;
+		case 0xA1:	/* publicPartyNumber */
+			if(rose_public_party_number_decode(pri, call, comp->data, comp->len, value) != 0)
+				return -1;
+			value->npi = PRI_NPI_E163_E164;
+			break;
+		case 0xA2:	/* nsapEncodedNumber */
+			pri_message("!! NsapEncodedNumber isn't handled\n");
+			return -1;
+		case 0xA3:	/* dataPartyNumber */
+			if(rose_number_digits_decode(pri, call, comp->data, comp->len, value))
+				return -1;
+			value->npi = PRI_NPI_X121 /* ??? */;
+			value->ton = PRI_TON_UNKNOWN /* ??? */;
+			pri_message("!! dataPartyNumber isn't handled\n");
+			return -1;
+		case 0xA4:	/* telexPartyNumber */
+			if (rose_number_digits_decode(pri, call, comp->data, comp->len, value))
+				return -1;
+			value->npi = PRI_NPI_F69 /* ??? */;
+			value->ton = PRI_TON_UNKNOWN /* ??? */;
+			pri_message("!! telexPartyNumber isn't handled\n");
+			return -1;
+		case 0xA5:	/* priavePartyNumber */
+			pri_message("!! privatePartyNumber isn't handled\n");
+			value->npi = PRI_NPI_PRIVATE;
+			return -1;
+		case 0xA8:	/* nationalStandardPartyNumber */
+			if (rose_number_digits_decode(pri, call, comp->data, comp->len, value))
+				return -1;
+			value->npi = PRI_NPI_NATIONAL;
+			value->ton = PRI_TON_NATIONAL;
+			break;
+		default:
+			pri_message("!! Unknown Party number component received 0x%X\n", comp->type);
+			return -1;
+		}
+		NEXT_COMPONENT(comp, i);
+		if(i < len)
+			pri_message("!! not all information is handled from Address component\n");
+		return 0;
 	}
-	i += comp->len + 2;
+	while (0);
+
+	return -1;
+}
+
+static int rose_presented_number_unscreened_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct AddressingDataElements_PresentedNumberUnscreened *value)
+{
+	int i = 0;
+	struct rose_component *comp = NULL;
+	unsigned char *vdata = data;
+
+	/* Fill in default values */
+	value->ton = PRI_TON_UNKNOWN;
+	value->npi = PRI_NPI_E163_E164;
+	value->pres = -1;	/* Data is not available */
+
+	do {
+		GET_COMPONENT(comp, i, vdata, len);
 
-	/* Operation Tag */
-	if (&vdata[i])
-		comp = (struct rose_component*)&vdata[i];
-	else return -1;
-
-	if (i+1 >= len) return -1;
-	if (comp->type && comp->type != ASN1_INTEGER) {
-		pri_message("Don't know what to do if second ROSE component is of type 0x%x\n",comp->type);
+		switch(comp->type) {
+		case 0xA0:		/* [0] presentationAllowedNumber */
+			value->pres = PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+			return rose_address_decode(pri, call, comp->data, comp->len, value);
+		case 0x81:		/* [1] IMPLICIT presentationRestricted */
+			if (comp->len != 0) { /* must be NULL */
+				pri_error("!! Invalid PresentationRestricted component received (len != 0)\n");
+				return -1;
+			}
+			value->pres = PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
+			return 0;
+		case 0x82:		/* [2] IMPLICIT numberNotAvailableDueToInterworking */
+			if (comp->len != 0) { /* must be NULL */
+				pri_error("!! Invalid NumberNotAvailableDueToInterworking component received (len != 0)\n");
+				return -1;
+			}
+			value->pres = PRES_NUMBER_NOT_AVAILABLE;
+			return 0;
+		case 0xA3:		/* [3] presentationRestrictedNumber */
+			value->pres = PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
+			return rose_address_decode(pri, call, comp->data, comp->len, value);
+		default:
+			pri_message("Invalid PresentedNumberUnscreened component 0x%X\n", comp->type);
+		}
 		return -1;
 	}
-	i += comp->len + 2;
+	while (0);
 
-	if (i >= len) 
-		return -1;
+	return -1;
+}
 
-	/* Arguement Tag */
-	if (&vdata[i])
-		comp = (struct rose_component*)&vdata[i];
-	else return -1;
+static int rose_diverting_leg_information2_decode(struct pri *pri, q931_call *call, unsigned char *data, int len)
+{
+	int i = 0;
+	int diversion_counter;
+	int diversion_reason;
+	struct AddressingDataElements_PresentedNumberUnscreened divertingNr;
+	struct AddressingDataElements_PresentedNumberUnscreened originalCalledNr;
+	struct rose_component *comp = NULL;
+	unsigned char *vdata = data;
 
-	if (comp->type) {
-		switch (comp->type) {
+	do {
+		/* diversionCounter stuff */
+		GET_COMPONENT(comp, i, vdata, len);
+		CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do it diversionCounter is of type 0x%x\n");
+		ASN1_GET_INTEGER(comp, diversion_counter);
+		NEXT_COMPONENT(comp, i);
+
+		/* diversionReason stuff */
+		GET_COMPONENT(comp, i, vdata, len);
+		CHECK_COMPONENT(comp, ASN1_ENUMERATED, "Invalid diversionReason type 0x%X of ROSE divertingLegInformation2 component received\n");
+		ASN1_GET_INTEGER(comp, diversion_reason);
+		NEXT_COMPONENT(comp, i);
+
+		diversion_reason = redirectingreason_for_q931(pri, diversion_reason);
+	
+#ifdef DEBUG
+		if(pri->debug)
+			pri_message("    Redirection reason: %d, total diversions: %d\n", diversion_reason, diversion_counter);
+#endif
+
+		for(; i < len; NEXT_COMPONENT(comp, i)) {
+			GET_COMPONENT(comp, i, vdata, len);
+			switch(comp->type) {
+			case 0xA1:		/* divertingNr: PresentedNumberUnscreened */
+				if(rose_presented_number_unscreened_decode(pri, call, comp->data, comp->len, &divertingNr) != 0)
+					return -1;
+#ifdef DEBUG
+				if (pri->debug) {
+					pri_message("    Received divertingNr '%s'\n", divertingNr.partyAddress);
+					pri_message("      ton = %d, pres = %d, npi = %d\n", divertingNr.ton, divertingNr.pres, divertingNr.npi);
+				}
+#endif
+				break;
+			case 0xA2:		/* originalCalledNr: PresentedNumberUnscreened */
+				if(rose_presented_number_unscreened_decode(pri, call, comp->data, comp->len, &originalCalledNr) != 0)
+					return -1;
+#ifdef DEBUG
+				if (pri->debug) {
+					pri_message("    Received originalCalledNr '%s'\n", originalCalledNr.partyAddress);
+					pri_message("      ton = %d, pres = %d, npi = %d\n", originalCalledNr.ton, originalCalledNr.pres, originalCalledNr.npi);
+				}
+#endif
+				break;
+			default:
+				pri_message("!! Invalid DivertingLegInformation2 component received 0x%X\n", comp->type);
+				return -1;
+			}
+		}
+		if (i < len)
+			return -1;	/* Aborted before */
+
+		if (divertingNr.pres >= 0) {
+			call->redirectingplan = divertingNr.npi;
+			call->redirectingpres = divertingNr.pres;
+			call->redirectingreason = diversion_reason;
+			strncpy(call->redirectingnum, divertingNr.partyAddress, sizeof(call->redirectingnum)-1);
+			call->redirectingnum[sizeof(call->redirectingnum)-1] = '\0';
+		}
+		else if (originalCalledNr.pres >= 0) {
+			call->redirectingplan = originalCalledNr.npi;
+			call->redirectingpres = originalCalledNr.pres;
+			call->redirectingreason = diversion_reason;
+			strncpy(call->redirectingnum, originalCalledNr.partyAddress, sizeof(call->redirectingnum)-1);
+			call->redirectingnum[sizeof(call->redirectingnum)-1] = '\0';
+		}
+		return 0;
+	}
+	while (0);
+
+	return -1;
+}
+
+int rose_invoke_decode(struct pri *pri, q931_call *call, unsigned char *data, int len)
+{
+	int i = 0;
+	int operation_tag;
+	unsigned char *vdata = data;
+	struct rose_component *comp = NULL, *invokeid = NULL, *operationid = NULL;
+
+	do {
+		/* Invoke ID stuff */
+		GET_COMPONENT(comp, i, vdata, len);
+		CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do if first ROSE component is of type 0x%x\n");
+		invokeid = comp;
+		NEXT_COMPONENT(comp, i);
+
+		/* Operation Tag */
+		GET_COMPONENT(comp, i, vdata, len);
+		CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do if second ROSE component is of type 0x%x\n");
+		operationid = comp;
+		ASN1_GET_INTEGER(comp, operation_tag);
+		NEXT_COMPONENT(comp, i);
+
+		/* No argument - return with error */
+		if (i >= len) 
+			return -1;
+
+		/* Arguement Tag */
+		GET_COMPONENT(comp, i, vdata, len);
+		if (!comp->type)
+			return -1;
+
+#ifdef DEBUG
+		pri_message("  [ Handling operation %d ]\n", operation_tag);
+#endif
+		switch (operation_tag) {
+		case SS_CNID_CALLINGNAME:
+#ifdef DEBUG
+			if (pri->debug)
+				pri_message("  Handle Name display operation\n");
+#endif
+			switch (comp->type) {
 			case ROSE_NAME_PRESENTATION_ALLOWED_SIMPLE:
 				memcpy(call->callername, comp->data, comp->len);
 				call->callername[comp->len] = 0;
+#ifdef DEBUG
+				if (pri->debug)
+				  pri_message("    Received caller name '%s'\n", call->callername);
+#endif
 				return 0;
 			default:
 				pri_message("Do not handle argument of type 0x%X\n", comp->type);
 				return -1;
+			}
+			break;
+		case ROSE_DIVERTING_LEG_INFORMATION2:
+#ifdef DEBUG
+			if (pri->debug)
+				pri_message("  Handle DivertingLegInformation2\n");
+#endif
+			if (comp->type != 0x30) { /* Sequence */
+				pri_message("Invalid DivertingLegInformation2Type argument\n");
+				return -1;
+			}
+			return rose_diverting_leg_information2_decode(pri, call, comp->data, comp->len);
+		default:
+			pri_message("!! Unable to handle ROSE operation %d\n", operation_tag);
+			return -1;
 		}
-	} else return -1;
+	} while(0);
+	
+	return -1;
 }
 
Index: pri_facility.h
===================================================================
RCS file: /usr/cvsroot/libpri/pri_facility.h,v
retrieving revision 1.1
diff -u -r1.1 pri_facility.h
--- pri_facility.h	27 Oct 2004 20:44:39 -0000	1.1
+++ pri_facility.h	3 Feb 2005 06:18:07 -0000
@@ -11,68 +11,161 @@
 #define _PRI_FACILITY_H
 
 /* Protocol Profile field */
-#define ROSE_NETWORK_EXTENSION 0x9F
+#define Q932_PROTOCOL_ROSE			0x11	/* X.219 & X.229 */
+#define Q932_PROTOCOL_CMIP			0x12	/* Q.941 */
+#define Q932_PROTOCOL_ACSE			0x13	/* X.217 & X.227 */
+#define Q932_PROTOCOL_GAT			0x16
+#define Q932_PROTOCOL_EXTENSIONS	0x1F
 
 /* Argument values */
-#define ROSE_NAME_PRESENTATION_ALLOWED_SIMPLE 0x80
-#define ROSE_NAME_PRESENTATION_RESTRICTED_NULL 0x87
-#define ROSE_NAME_NOT_AVAIL 0x84
-
-/* Divert arguments */
-#define ROSE_DIVERTING_LEG_INFORMATION2 0x15
+#define ROSE_NAME_PRESENTATION_ALLOWED_SIMPLE	0x80
+#define ROSE_NAME_PRESENTATION_RESTRICTED_NULL	0x87
+#define ROSE_NAME_NOT_AVAIL						0x84
 
 /* Component types */
-#define COMP_TYPE_INVOKE 0xA1
-#define COMP_TYPE_INTERPRETATION 0x8B
-#define COMP_TYPE_NETWORK_PROTOCOL_PROFILE 0x92
-#define COMP_TYPE_RETURN_RESULT 0xA2
-#define COMP_TYPE_RETURN_ERROR 0xA3
-#define COMP_TYPE_REJECT 0xA4
-#define COMP_TYPE_NFE 0xAA
-
+#define COMP_TYPE_INTERPRETATION			0x8B
+#define COMP_TYPE_NETWORK_PROTOCOL_PROFILE	0x92
+#define COMP_TYPE_INVOKE					0xA1
+#define COMP_TYPE_RETURN_RESULT				0xA2
+#define COMP_TYPE_RETURN_ERROR				0xA3
+#define COMP_TYPE_REJECT					0xA4
+#define COMP_TYPE_NFE						0xAA
+
+/* Operation ID values */
+/* Q.952 ROSE operations */
+#define ROSE_DIVERTING_LEG_INFORMATION1		18
+#define ROSE_DIVERTING_LEG_INFORMATION2		15
+#define ROSE_DIVERTING_LEG_INFORMATION3		19
+/* Q.SIG operations */
+#define SS_CNID_CALLINGNAME					0
+#define SS_DIVERTING_LEG_INFORMATION2		22
+#define SS_MWI_ACTIVATE						80
+#define SS_MWI_DEACTIVATE					81
+#define SS_MWI_INTERROGATE					82
 
 /* ROSE definitions and data structures */
-#define INVOKE_IDENTIFIER 0x02
-#define INVOKE_LINKED_IDENTIFIER 0x80
-#define INVOKE_NULL_IDENTIFIER __USE_ASN1_NULL
+#define INVOKE_IDENTIFIER			0x02
+#define INVOKE_LINKED_IDENTIFIER	0x80
+#define INVOKE_NULL_IDENTIFIER		__USE_ASN1_NULL
 
 /* ASN.1 Data types */
-#define ASN1_BOOLEAN 0x01
-#define ASN1_INTEGER 0x02
-#define ASN1_BITSTRING 0x03
-#define ASN1_OCTETSTRING 0x04
-#define ASN1_NULL 0x05
-#define ASN1_OBJECTIDENTIFIER 0x06
-#define ASN1_OBJECTDESCRIPTER 0x07
-#define ASN1_UTF8STRING 0x0c
-#define ASN1_SEQUENCE 0x10
-#define ASN1_SET 0x11
-#define ASN1_NUMERICSTRING 0x12
-#define ASN1_PRINTABLESTRING 0x13
-#define ASN1_TELETEXSTRING 0x14
-#define ASN1_IA5STRING 0x16
-#define ASN1_UTCTIME 0x17
-#define ASN1_GENERALIZEDTIME 0x18
-
-
-#define INVOKE_OPERATION_INT __USE_ASN1_INTEGER
-#define INVOKE_OBJECT_ID __USE_ASN1_OBJECTIDENTIFIER
-
-/* Divert cause */
-#define DIVERT_REASON_UNKNOWN 0x00
-#define DIVERT_REASON_CFU 0x01
-#define DIVERT_REASON_CFB 0x02
-#define DIVERT_REASON_CFNR 0x03
+#define ASN1_BOOLEAN			0x01
+#define ASN1_INTEGER			0x02
+#define ASN1_BITSTRING			0x03
+#define ASN1_OCTETSTRING		0x04
+#define ASN1_NULL				0x05
+#define ASN1_OBJECTIDENTIFIER	0x06
+#define ASN1_OBJECTDESCRIPTER	0x07
+#define ASN1_EXTERN				0x08
+#define ASN1_REAL				0x09
+#define ASN1_ENUMERATED			0x0a
+#define ASN1_EMBEDDEDPDV		0x0b
+#define ASN1_UTF8STRING			0x0c
+#define ASN1_RELATIVEOBJECTID	0x0d
+/* 0x0e & 0x0f are reserved for future ASN.1 editions */
+#define ASN1_SEQUENCE			0x10
+#define ASN1_SET				0x11
+#define ASN1_NUMERICSTRING		0x12
+#define ASN1_PRINTABLESTRING	0x13
+#define ASN1_TELETEXSTRING		0x14
+#define ASN1_IA5STRING			0x16
+#define ASN1_UTCTIME			0x17
+#define ASN1_GENERALIZEDTIME	0x18
+
+#define INVOKE_OPERATION_INT	__USE_ASN1_INTEGER
+#define INVOKE_OBJECT_ID		__USE_ASN1_OBJECTIDENTIFIER
+
+/* Q.952 Divert cause */
+#define Q952_DIVERT_REASON_UNKNOWN		0x00
+#define Q952_DIVERT_REASON_CFU			0x01
+#define Q952_DIVERT_REASON_CFB			0x02
+#define Q952_DIVERT_REASON_CFNR			0x03
+#define Q952_DIVERT_REASON_CD			0x04
+#define Q952_DIVERT_REASON_IMMEDIATE	0x05
+/* Q.SIG Divert cause. Listed in ECMA-174 */
+#define QSIG_DIVERT_REASON_UNKNOWN		0x00	/* Call forward unknown reason */
+#define QSIG_DIVERT_REASON_CFU			0x01	/* Call Forward Unconditional (other reason) */
+#define QSIG_DIVERT_REASON_CFB			0x02	/* Call Forward Busy */
+#define QSIG_DIVERT_REASON_CFNR			0x03	/* Call Forward No Reply */
+
+/* Q.932 Type of number */
+#define Q932_TON_UNKNOWN				0x00
+#define Q932_TON_INTERNATIONAL			0x01
+#define Q932_TON_NATIONAL				0x02
+#define Q932_TON_NET_SPECIFIC			0x03
+#define Q932_TON_SUBSCRIBER				0x04
+#define Q932_TON_ABBREVIATED			0x06
 
 struct rose_component {
-        u_int8_t type;
-        u_int8_t len;
-        u_int8_t data[0];
+	u_int8_t type;
+	u_int8_t len;
+	u_int8_t data[0];
 };
 
+#define GET_COMPONENT(component, idx, ptr, length) \
+	if ((idx)+2 >= (length)) \
+		break; \
+	(component) = (struct rose_component*)&((ptr)[idx]); \
+	if ((idx)+(component)->len+2 > (length)) { \
+		if ((component)->len != 128) \
+			pri_message("Length (%d) of 0x%X component is too long\n", (component)->len, (component)->type); \
+	}
+/*
+	pri_message("XX Got component %d (0x%02X), length %d\n", (component)->type, (component)->type, (component)->len); \
+	if ((component)->len > 0) { \
+		int zzz; \
+		pri_message("XX  Data:"); \
+		for (zzz = 0; zzz < (component)->len; ++zzz) \
+			pri_message(" %02X", (component)->data[zzz]); \
+		pri_message("\n"); \
+	}
+*/
+
+#define NEXT_COMPONENT(component, idx) \
+	(idx) += (component)->len + 2
+
+#define SUB_COMPONENT(component, idx) \
+	(idx) += 2
+
+#define CHECK_COMPONENT(component, comptype, message) \
+	if ((component)->type && ((component)->type&0x1f) != (comptype)) { \
+		pri_message((message), (component)->type); \
+		break; \
+	}
+	
+#define ASN1_GET_INTEGER(component, variable) \
+	{ \
+		int comp_idx; \
+		(variable) = 0; \
+		for (comp_idx = 0; comp_idx < (component)->len; ++comp_idx) \
+			(variable) = ((variable) << 8) | (component)->data[comp_idx]; \
+	}
+
+#define ASN1_ADD_SIMPLE(component, comptype, ptr, idx) \
+	(component) = (struct rose_component *)&((ptr)[(idx)]); \
+	(component)->type = (comptype); \
+	(component)->len = 0; \
+	(idx) += 2;
+
+#define ASN1_ADD_BYTECOMP(component, comptype, ptr, idx, value) \
+	(component) = (struct rose_component *)&((ptr)[(idx)]); \
+	(component)->type = (comptype); \
+	(component)->len = 1; \
+	(component)->data[0] = (value); \
+	(idx) += 3;
+
+#define ASN1_PUSH(stack, stackpointer, component) \
+	(stack)[(stackpointer)++] = (component);
+
+#define ASN1_FIXUP(stack, stackpointer, data, idx) \
+	--(stackpointer); \
+	(stack)[(stackpointer)]->len = (unsigned char *)&((data)[(idx)]) - (unsigned char *)(stack)[(stackpointer)] - 2;
 
 /* Decoder fo the invoke part of a ROSE request
    It currently only support calling name decode */
-extern int rose_invoke_decode(struct q931_call *call, unsigned char *data, int len);
+extern int rose_invoke_decode(struct pri *pri, struct q931_call *call, unsigned char *data, int len);
+extern int asn1_string_encode(unsigned char asn1_type, void *data, int len, int max_len, void *src, int src_len);
+int typeofnumber_from_q931(struct pri *pri, int ton);
+int redirectingreason_from_q931(struct pri *pri, int redirectingreason);
 
 #endif /* _PRI_FACILITY_H */
Index: pri_internal.h
===================================================================
RCS file: /usr/cvsroot/libpri/pri_internal.h,v
retrieving revision 1.15
diff -u -r1.15 pri_internal.h
--- pri_internal.h	17 Jan 2005 12:58:05 -0000	1.15
+++ pri_internal.h	3 Feb 2005 06:18:08 -0000
@@ -103,6 +103,8 @@
 	unsigned int q931_txcount;
 	unsigned int q931_rxcount;
 #endif
+
+	unsigned char last_invoke;	/* Last ROSE invoke ID */
 };
 
 struct pri_sr {
Index: q931.c
===================================================================
RCS file: /usr/cvsroot/libpri/q931.c,v
retrieving revision 1.112
diff -u -r1.112 q931.c
--- q931.c	28 Jan 2005 22:11:24 -0000	1.112
+++ q931.c	3 Feb 2005 06:18:08 -0000
@@ -1049,68 +1049,230 @@
 
 static FUNC_SEND(transmit_facility)
 {
-	int i = 0;
-	struct rose_component *comp;
+	int i = 0, j, first_i, compsp = 0;
+	struct rose_component *comp, *compstk[10];
 	unsigned char namelen = strlen(call->callername);
 
-	if (namelen > 15) 
+	if ((pri->switchtype == PRI_SWITCH_NI2) && (namelen > 15))
 		namelen = 15; /* According to GR-1367, for NI2 switches it can't be > 15 characters */
 	if ((namelen > 0) && ((pri->switchtype == PRI_SWITCH_QSIG) ||
 			((pri->switchtype == PRI_SWITCH_NI2) && (pri->localtype == PRI_NETWORK)))) {
-		ie->data[i] = ROSE_NETWORK_EXTENSION;
+		first_i = i;
+		ie->data[i] = 0x80 | Q932_PROTOCOL_EXTENSIONS;
 		i++;
 		/* Interpretation component */
-		comp = (struct rose_component*)&ie->data[i];
-		comp->type = COMP_TYPE_INTERPRETATION;
-		comp->len = 0x01;
-		comp->data[0] = 0x00; /* Discard unrecognized invokes */
+		ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, ie->data, i, 0x00 /* Discard unrecognized invokes */);
 
-		i += 3;
-		comp = (struct rose_component*)&ie->data[i];
 		/* Invoke ID */
-		comp->type = COMP_TYPE_INVOKE;
-		comp->len = 3 /* sizeof Invoke ID */
-			+ 3 /* sizeof Operation tag */
-			+ 2 /* first two bytes of the Arguement section */
-			+ namelen;
+		ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, ie->data, i);
+		ASN1_PUSH(compstk, compsp, comp);
 
-		i += 2;
-		comp = (struct rose_component*)&ie->data[i];
 		/* Invoke component contents */
 		/*	Invoke ID */
-		comp->type = ASN1_INTEGER;
-		comp->len = 0x01;
-		comp->data[0] = 0x01; /* Invoke ID value */
-		i += 3;
-		comp = (struct rose_component*)&ie->data[i];
+		ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, ie->data, i, ++pri->last_invoke);
+
 		/*	Operation Tag */
-		comp->type = ASN1_INTEGER;
-		comp->len = 0x01;
-		comp->data[0] = 0x00; /* Calling name */
-		i += 3;
-		comp = (struct rose_component*)&ie->data[i];
+		ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, ie->data, i, SS_CNID_CALLINGNAME);
+
 		/* Arugement Tag */
-		comp->type = ROSE_NAME_PRESENTATION_ALLOWED_SIMPLE;
-		comp->len = namelen;
-		i += 2;
-		memcpy(comp->data, call->callername, namelen);
-		i += namelen;
-		return i+2 /* 2 = length of IE header */;
+		j = asn1_string_encode(ROSE_NAME_PRESENTATION_ALLOWED_SIMPLE, &ie->data[i], len - i, 15, call->callername, namelen);
+		if (j < 0) {
+			i = first_i;
+			goto finish1;
+		}
+		i += j;
+
+		/* Fix length of stacked components */
+		while(compsp > 0) {
+			ASN1_FIXUP(compstk, compsp, ie->data, i);
+		}
+finish1:
 	}
+	if (/*(pri->switchtype == PRI_SWITCH_EUROISDN_E1) &&*/ call->redirectingnum && strlen(call->redirectingnum)) {
+		if (!(first_i = i)) {
+			/* Add protocol information header */
+			ie->data[i++] = 0x80 | Q932_PROTOCOL_ROSE;
+		}
+
+		/* ROSE invoke component */
+		ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, ie->data, i);
+		ASN1_PUSH(compstk, compsp, comp);
+
+		/* ROSE invokeId component */
+		ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, ie->data, i, ++pri->last_invoke);
+
+		/* ROSE operationId component */
+		ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, ie->data, i, ROSE_DIVERTING_LEG_INFORMATION2);
+
+		/* ROSE ARGUMENT component */
+		ASN1_ADD_SIMPLE(comp, 0x30, ie->data, i);
+
+		/* ROSE DivertingLegInformation2.diversionCounter component */
+		/* Always is 1 because other isn't available in the current design */
+		ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, ie->data, i, 1);
+
+		/* ROSE DivertingLegInformation2.diversionReason component */
+		ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, ie->data, i, redirectingreason_from_q931(pri, call->redirectingreason));
+
+		/* ROSE DivertingLegInformation2.divertingNr component */
+		ASN1_ADD_SIMPLE(comp, 0xA1, ie->data, i);
+		ASN1_PUSH(compstk, compsp, comp);
+
+		/* Redirecting information always not screened */
+		switch(call->redirectingpres) {
+		case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
+		case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
+			if (call->redirectingnum && strlen(call->redirectingnum)) {
+				ASN1_ADD_SIMPLE(comp, 0xA0, ie->data, i);
+				ASN1_PUSH(compstk, compsp, comp);
+
+				/* NPI of redirected number is not supported in the current design */
+				ASN1_ADD_SIMPLE(comp, 0xA1, ie->data, i);
+				ASN1_PUSH(compstk, compsp, comp);
+
+				ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, ie->data, i, typeofnumber_from_q931(pri, call->redirectingplan >> 4));
 
-	return 0;
+				j = asn1_string_encode(ASN1_NUMERICSTRING, &ie->data[i], len - i, 20, call->redirectingnum, strlen(call->redirectingnum));
+				if (j < 0) {
+					i = first_i;
+					goto finish2;
+				}
+				i += j;
+				ASN1_FIXUP(compstk, compsp, ie->data, i);
+				ASN1_FIXUP(compstk, compsp, ie->data, i);
+				break;
+			}
+			/* fall through */
+		case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
+		case PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
+			ASN1_ADD_SIMPLE(comp, 0x81, ie->data, i);
+			break;
+		/* Don't know how to handle this */
+		case PRES_ALLOWED_NETWORK_NUMBER:
+		case PRES_PROHIB_NETWORK_NUMBER:
+		case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
+		case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
+			ASN1_ADD_SIMPLE(comp, 0x81, ie->data, i);
+			break;
+		default:
+			pri_message("!! Undefined presentation value for redirecting number: %d\n", call->redirectingpres);
+		case PRES_NUMBER_NOT_AVAILABLE:
+			ASN1_ADD_SIMPLE(comp, 0x82, ie->data, i);
+			break;
+		}
+		ASN1_FIXUP(compstk, compsp, ie->data, i);
+
+		/* ROSE DivertingLegInformation2.originalCalledNr component */
+		/* This information isn't supported by current design - duplicate divertingNr */
+		ASN1_ADD_SIMPLE(comp, 0xA2, ie->data, i);
+		ASN1_PUSH(compstk, compsp, comp);
+
+		/* Redirecting information always not screened */
+		switch(call->redirectingpres) {
+		case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
+		case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
+			if (call->redirectingnum && strlen(call->redirectingnum)) {
+				ASN1_ADD_SIMPLE(comp, 0xA0, ie->data, i);
+				ASN1_PUSH(compstk, compsp, comp);
+
+				ASN1_ADD_SIMPLE(comp, 0xA1, ie->data, i);
+				ASN1_PUSH(compstk, compsp, comp);
+
+				ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, ie->data, i, typeofnumber_from_q931(pri, call->redirectingplan >> 4));
+
+				j = asn1_string_encode(ASN1_NUMERICSTRING, &ie->data[i], len - i, 20, call->redirectingnum, strlen(call->redirectingnum));
+				if (j < 0) {
+					i = first_i;
+					goto finish2;
+				}
+				i += j;
+				ASN1_FIXUP(compstk, compsp, ie->data, i);
+				ASN1_FIXUP(compstk, compsp, ie->data, i);
+				break;
+			}
+			/* fall through */
+		case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
+		case PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
+			ASN1_ADD_SIMPLE(comp, 0x81, ie->data, i);
+			break;
+		/* Don't know how to handle this */
+		case PRES_ALLOWED_NETWORK_NUMBER:
+		case PRES_PROHIB_NETWORK_NUMBER:
+		case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
+		case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
+			ASN1_ADD_SIMPLE(comp, 0x81, ie->data, i);
+			break;
+		default:
+			pri_message("!! Undefined presentation value for redirecting number: %d\n", call->redirectingpres);
+		case PRES_NUMBER_NOT_AVAILABLE:
+			ASN1_ADD_SIMPLE(comp, 0x82, ie->data, i);
+			break;
+		}
+		ASN1_FIXUP(compstk, compsp, ie->data, i);
+
+		/* Fix length of stacked components */
+		while(compsp > 0) {
+			ASN1_FIXUP(compstk, compsp, ie->data, i);
+		}
+finish2:
+	}
+	return (i ? i+2 : 0);
 }
 
 static FUNC_RECV(receive_facility)
 {
 	int i = 0;
+	int protocol, next_protocol;
 	struct rose_component *comp = NULL;
+	enum {
+		Q932_STATE_NFE,				/* Network facility extension */
+		Q932_STATE_NPP,				/* Network protocol profile */
+		Q932_STATE_INTERPRETATION,	/* Interpretation component */
+		Q932_STATE_SERVICE			/* Service component(s) */
+	} state = Q932_STATE_SERVICE;
+#define Q932_HANDLE_PROC(component, my_state, name, handler) \
+				case component: \
+					if(state > my_state) { \
+						pri_error("!! %s component received in wrong place\n"); \
+						break; \
+					} \
+					state = my_state; \
+					if (pri->debug) \
+						pri_message("Handle Q.932 %s component\n", name); \
+					(handler)(pri, call, comp->data, comp->len); \
+					break;
+#define Q932_HANDLE_NULL(component, my_state, name, handle) \
+				case component: \
+					if(state > my_state) { \
+						pri_error("!! %s component received in wrong place\n"); \
+						break; \
+					} \
+					state = my_state; \
+					if (pri->debug) \
+						pri_message("Q.932 %s component is not handled\n", name); \
+					break;
 
 	if (ie->len < 1)
 		return -1;
 
-	if(ie->data[i] != 0x9F) {
-		if (pri->debug) pri_message("!! Don't know how to handle Service Discriminator of type 0x%X\n", ie->data[i]);
+	if ((ie->data[i] & 0xe0) != 0x80) {
+		pri_error ("!! Invalid Protocol Profile field 0x%X\n", ie->data[i]);
+		return -1;
+	}
+	switch(next_protocol = protocol = (ie->data[i] & 0x1f)) {
+	case Q932_PROTOCOL_CMIP:
+	case Q932_PROTOCOL_ACSE:
+		if (pri->debug)
+			pri_message("!! Don't know how to handle Q.932 Protocol Profile of type 0x%X\n", protocol);
+		return -1;
+	case Q932_PROTOCOL_EXTENSIONS:
+		state = Q932_STATE_NFE;
+		next_protocol = Q932_PROTOCOL_ROSE;
+		break;
+	case Q932_PROTOCOL_ROSE:
+		break;
+	default:
+		pri_error("!! Invalid Q.932 Protocol Profile of type 0x%X received\n", protocol);
 		return -1;
 	}
 	i++;
@@ -1121,20 +1283,50 @@
 	while ((i+1 < ie->len) && (&ie->data[i])) {
 		comp = (struct rose_component*)&ie->data[i];
 		if (comp->type) {
-			switch (comp->type) {
-				case COMP_TYPE_INTERPRETATION:
-					if (pri->debug) pri_message("Handle ROSE interpretation component\n");
+			if (protocol == Q932_PROTOCOL_EXTENSIONS) {
+				switch (comp->type) {
+				Q932_HANDLE_NULL(COMP_TYPE_INTERPRETATION, Q932_STATE_INTERPRETATION, "Interpretation", NULL);
+				Q932_HANDLE_NULL(COMP_TYPE_NFE, Q932_STATE_NFE, "Network facility extensions", NULL);
+				Q932_HANDLE_NULL(COMP_TYPE_NETWORK_PROTOCOL_PROFILE, Q932_STATE_NPP, "Network protocol profile", NULL);
+				default:
+					protocol = next_protocol;
 					break;
-				case COMP_TYPE_INVOKE:
-					rose_invoke_decode(call, comp->data, comp->len);
+				}
+			}
+			switch (protocol) {
+			case Q932_PROTOCOL_ROSE:
+				switch (comp->type) {
+				Q932_HANDLE_PROC(COMP_TYPE_INVOKE, Q932_STATE_SERVICE, "ROSE Invoke", rose_invoke_decode);
+				Q932_HANDLE_NULL(COMP_TYPE_RETURN_RESULT, Q932_STATE_SERVICE, "ROSE return result", NULL);
+				Q932_HANDLE_NULL(COMP_TYPE_RETURN_ERROR, Q932_STATE_SERVICE, "ROSE return error", NULL);
+				Q932_HANDLE_NULL(COMP_TYPE_REJECT, Q932_STATE_SERVICE, "ROSE reject", NULL);
+				default:
+					if (pri->debug)
+						pri_message("Don't know how to handle ROSE component of type 0x%X\n", comp->type);
+					break;
+				}
+				break;
+			case Q932_PROTOCOL_CMIP:
+				switch (comp->type) {
+				default:
+					if (pri->debug)
+						pri_message("Don't know how to handle CMIP component of type 0x%X\n", comp->type);
 					break;
+				}
+				break;
+			case Q932_PROTOCOL_ACSE:
+				switch (comp->type) {
 				default:
-					if (pri->debug) pri_message("Don't know how to handle ROSE component of type 0x%X\n", comp->type);
+					if (pri->debug)
+						pri_message("Don't know how to handle ACSE component of type 0x%X\n", comp->type);
 					break;
+				}
+				break;
 			}
 		}
 		i += (comp->len + 2);
 	}
+#undef Q932_HANDLE
 
 	return 0;
 }
Index: testprilib.c
===================================================================
RCS file: /usr/cvsroot/libpri/testprilib.c,v
retrieving revision 1.3
diff -u -r1.3 testprilib.c
--- testprilib.c	15 Dec 2004 20:15:28 -0000	1.3
+++ testprilib.c	3 Feb 2005 06:18:08 -0000
@@ -75,6 +75,7 @@
 	int x;
 	static q931_call *calls[TEST_CALLS];
 	char name[256], num[256], dest[256];
+	struct pri_sr *sr;
 	switch(e->gen.e) {
 	case PRI_EVENT_DCHAN_UP:
 		printf("Network is up.  Sending blast of calls!\n");
@@ -84,11 +85,25 @@
 			sprintf(dest, "60%02d", x + 1);
 			if (!(calls[x] = pri_new_call(pri))) {
 				perror("pri_new_call");
-			} else if (pri_call(pri, calls[x], PRI_TRANS_CAP_DIGITAL, x + 1, 1, 1, num, 
+				continue;
+			}
+#if 1
+			sr = pri_sr_new();
+			pri_sr_set_channel(sr, x, 0, 0);
+			pri_sr_set_bearer(sr, 0, PRI_LAYER_1_ULAW);
+			pri_sr_set_called(sr, dest, PRI_NATIONAL_ISDN, 1);
+			pri_sr_set_caller(sr, num, name, PRI_NATIONAL_ISDN, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN);
+			pri_sr_set_redirecting(sr, num, PRI_NATIONAL_ISDN, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, PRI_REDIR_UNCONDITIONAL);
+			if (pri_setup(pri, calls[x], sr))
+				perror("pri_setup");
+			pri_sr_free(sr);
+#else
+			if (pri_call(pri, calls[x], PRI_TRANS_CAP_DIGITAL, x + 1, 1, 1, num, 
 				PRI_NATIONAL_ISDN, name, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN,
 				dest, PRI_NATIONAL_ISDN, PRI_LAYER_1_ULAW)) {
 					perror("pri_call");
 			}
+#endif
 		}
 		printf("Setup %d calls!\n", TEST_CALLS);
 		break;
