Supplementary Services (de)activation, interrogation added

These patches enhance the Supplementary Service (SS) processing from
only being able to handle USSD to other SS, specifically activation,
deactivation and interrogation of those SS. Registration is not yet
implemented.

include/osmocom/gsm/protocol/gsm_09_02.h has been added with needed
values for SS.

Modified by Harald Welte to keep the old ussd-only functiosn for API/ABI
stability.
diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c
index dbacefc..952604b 100644
--- a/src/gsm/gsm0480.c
+++ b/src/gsm/gsm0480.c
@@ -192,23 +192,29 @@
 }
 
 /* Forward declarations */
-static int parse_ussd(const struct gsm48_hdr *hdr,
-		      uint16_t len, struct ussd_request *req);
-static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len,
-					struct ussd_request *req);
+static int parse_ss(const struct gsm48_hdr *hdr,
+		    uint16_t len, struct ss_request *req);
+static int parse_ss_info_elements(const uint8_t *ussd_ie, uint16_t len,
+				  struct ss_request *req);
 static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length,
-					struct ussd_request *req);
+			     struct ss_request *req);
 static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
-					struct ussd_request *req);
+					struct ss_request *req);
 static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
-					struct ussd_request *req);
+					struct ss_request *req);
+static int parse_ss_for_bs_req(const uint8_t *ss_req_data,
+				     uint16_t length,
+				     struct ss_request *req);
 
 /* Decode a mobile-originated USSD-request message */
 int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len,
 				struct ussd_request *req)
 {
+	struct ss_request ss;
 	int rc = 0;
 
+	memset(&ss, 0, sizeof(ss));
+
 	if (len < sizeof(*hdr) + 2) {
 		LOGP(0, LOGL_DEBUG, "USSD Request is too short.\n");
 		return 0;
@@ -216,7 +222,19 @@
 
 	if ((hdr->proto_discr & 0x0f) == GSM48_PDISC_NC_SS) {
 		req->transaction_id = hdr->proto_discr & 0x70;
-		rc = parse_ussd(hdr, len, req);
+
+		ss.transaction_id = req->transaction_id;
+		rc = parse_ss(hdr, len, &ss);
+
+		/* convert from ss_request to legacy ussd_request */
+		req->transaction_id = ss.transaction_id;
+		req->invoke_id = ss.invoke_id;
+		if (ss.ussd_text[0] == 0xFF)
+			req->text[0] = '\0';
+		else {
+			memcpy(req->text, ss.ussd_text, sizeof(req->text));
+			req->text[sizeof(req->text)-1] = '\0';
+		}
 	}
 
 	if (!rc)
@@ -225,20 +243,42 @@
 	return rc;
 }
 
-static int parse_ussd(const struct gsm48_hdr *hdr, uint16_t len, struct ussd_request *req)
+/* Decode a mobile-originated SS request message */
+int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len,
+				struct ss_request *req)
+{
+	int rc = 0;
+
+	if (len < sizeof(*hdr) + 2) {
+		LOGP(0, LOGL_DEBUG, "SS Request is too short.\n");
+		return 0;
+	}
+
+	if ((hdr->proto_discr & 0x0f) == GSM48_PDISC_NC_SS) {
+		req->transaction_id = hdr->proto_discr & 0x70;
+		rc = parse_ss(hdr, len, req);
+	}
+
+	if (!rc)
+		LOGP(0, LOGL_DEBUG, "Error occurred while parsing received SS!\n");
+
+	return rc;
+}
+
+static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *req)
 {
 	int rc = 1;
 	uint8_t msg_type = hdr->msg_type & 0xBF;  /* message-type - section 3.4 */
 
 	switch (msg_type) {
 	case GSM0480_MTYPE_RELEASE_COMPLETE:
-		LOGP(0, LOGL_DEBUG, "USS Release Complete\n");
+		LOGP(0, LOGL_DEBUG, "SS Release Complete\n");
 		/* could also parse out the optional Cause/Facility data */
-		req->text[0] = '\0';
+		req->ussd_text[0] = 0xFF;
 		break;
 	case GSM0480_MTYPE_REGISTER:
 	case GSM0480_MTYPE_FACILITY:
-		rc &= parse_ussd_info_elements(&hdr->data[0], len - sizeof(*hdr), req);
+		rc &= parse_ss_info_elements(&hdr->data[0], len - sizeof(*hdr), req);
 		break;
 	default:
 		LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 message-type field 0x%02x\n",
@@ -250,16 +290,16 @@
 	return rc;
 }
 
-static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len,
-				    struct ussd_request *req)
+static int parse_ss_info_elements(const uint8_t *ss_ie, uint16_t len,
+				  struct ss_request *req)
 {
 	int rc = -1;
 	/* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */
 	uint8_t iei;
 	uint8_t iei_length;
 
-	iei = ussd_ie[0];
-	iei_length = ussd_ie[1];
+	iei = ss_ie[0];
+	iei_length = ss_ie[1];
 
 	/* If the data does not fit, report an error */
 	if (len - 2 < iei_length)
@@ -269,7 +309,7 @@
 	case GSM48_IE_CAUSE:
 		break;
 	case GSM0480_IE_FACILITY:
-		rc = parse_facility_ie(ussd_ie+2, iei_length, req);
+		rc = parse_facility_ie(ss_ie + 2, iei_length, req);
 		break;
 	case GSM0480_IE_SS_VERSION:
 		break;
@@ -284,7 +324,7 @@
 }
 
 static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length,
-						struct ussd_request *req)
+			     struct ss_request *req)
 {
 	int rc = 1;
 	uint8_t offset = 0;
@@ -303,8 +343,8 @@
 		switch (component_type) {
 		case GSM0480_CTYPE_INVOKE:
 			rc &= parse_ss_invoke(facility_ie+2,
-						component_length,
-						req);
+					      component_length,
+					      req);
 			break;
 		case GSM0480_CTYPE_RETURN_RESULT:
 			break;
@@ -326,7 +366,7 @@
 
 /* Parse an Invoke component - see table 3.3 */
 static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
-						struct ussd_request *req)
+			   struct ss_request *req)
 {
 	int rc = 1;
 	uint8_t offset;
@@ -337,7 +377,7 @@
 	/* mandatory part */
 	if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) {
 		LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag "
-			"0x%02x (expecting Invoke ID tag)\n", invoke_data[0]);
+		     "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]);
 	}
 
 	offset = invoke_data[1] + 2;
@@ -356,12 +396,20 @@
 		if (offset + 2 > length)
 			return 0;
 		uint8_t operation_code = invoke_data[offset+2];
+		req->opcode = operation_code;
 		switch (operation_code) {
 		case GSM0480_OP_CODE_PROCESS_USS_REQ:
 			rc = parse_process_uss_req(invoke_data + offset + 3,
 						   length - offset - 3,
 						   req);
 			break;
+		case GSM0480_OP_CODE_ACTIVATE_SS:
+		case GSM0480_OP_CODE_DEACTIVATE_SS:
+		case GSM0480_OP_CODE_INTERROGATE_SS:
+			rc = parse_ss_for_bs_req(invoke_data + offset + 3,
+						 length - offset - 3,
+						 req);
+			break;
 		default:
 			LOGP(0, LOGL_DEBUG, "GSM 04.80 operation code 0x%02x "
 				"is not yet handled\n", operation_code);
@@ -380,7 +428,7 @@
 
 /* Parse the parameters of a Process UnstructuredSS Request */
 static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
-					struct ussd_request *req)
+				 struct ss_request *req)
 {
 	int rc = 0;
 	int num_chars;
@@ -398,8 +446,12 @@
 			if ((dcs == 0x0F) &&
 			    (uss_req_data[5] == ASN1_OCTET_STRING_TAG)) {
 				num_chars = (uss_req_data[6] * 8) / 7;
-				gsm_7bit_decode_n_ussd(req->text, sizeof(req->text),
-					&(uss_req_data[7]), num_chars);
+				/* Prevent a mobile-originated buffer-overrun! */
+				if (num_chars > MAX_LEN_USSD_STRING)
+					num_chars = MAX_LEN_USSD_STRING;
+				gsm_7bit_decode_n_ussd((char *)req->ussd_text,
+							sizeof(req->ussd_text),
+							&(uss_req_data[7]), num_chars);
 				rc = 1;
 			}
 		}
@@ -407,6 +459,30 @@
 	return rc;
 }
 
+/* Parse the parameters of a Interrogate/Activate/DeactivateSS Request */
+static int parse_ss_for_bs_req(const uint8_t *ss_req_data,
+			       uint16_t length,
+			       struct ss_request *req)
+{
+	int rc = 0;
+
+
+	/* we need at least that much */
+	if (length < 5)
+		return 0;
+
+
+	if (ss_req_data[0] == GSM_0480_SEQUENCE_TAG) {
+		if ((ss_req_data[2] == ASN1_OCTET_STRING_TAG) &&
+			ss_req_data[3] == 1) {
+			req->ss_code = ss_req_data[4];
+
+			rc = 1;
+		}
+	}
+	return rc;
+}
+
 struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text)
 {
 	struct msgb *msg;
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 02c8884..b7c5158 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -44,6 +44,7 @@
 gsm0480_create_unstructuredSS_Notify;
 gsm0480_create_ussd_resp;
 gsm0480_decode_ussd_request;
+gsm0480_decode_ss_request;
 gsm0480_wrap_facility;
 gsm0480_wrap_invoke;