>From 8aab1f8f31275fff6a61ae580e4677543729bf05 Mon Sep 17 00:00:00 2001
From: Dennis Wehrle <dennis.wehrle@rz.uni-freiburg.de>
Date: Wed, 27 Jul 2011 15:12:36 +0200
Subject: [PATCH] Implement multipart sms from the VTY.
 Therefore a new struct gsm_sms_ud_header inside the gsm_sms struct was created.
 The following functions were modified / created:
  - sms_from_text (gsm_04_11.c): split the text into several sms
  - gsm_add_user_data_header (gsm_04_11.c): prepend the header to the user data
  - _send_sms_str (vty_interface_layer3.c): store the various sms parts to the db

---
 openbsc/include/openbsc/gsm_04_11.h       |    5 +
 openbsc/include/openbsc/gsm_data.h        |   15 +++
 openbsc/src/libmsc/gsm_04_11.c            |  139 +++++++++++++++++++++++++---
 openbsc/src/libmsc/vty_interface_layer3.c |   23 ++++--
 4 files changed, 160 insertions(+), 22 deletions(-)

diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h
index 2abe3e2..8a4ca7f 100644
--- a/openbsc/include/openbsc/gsm_04_11.h
+++ b/openbsc/include/openbsc/gsm_04_11.h
@@ -4,6 +4,10 @@
 #include <osmocom/gsm/protocol/gsm_04_11.h>
 
 #define UM_SAPI_SMS 3	/* See GSM 04.05/04.06 */
+#define UD_LEN 140		/* Number of Bytes => 140 Bytes = 160 Septets */
+#define UD_PAYLOAD_SIZE UD_LEN * sizeof(uint8_t)
+/* Add 'plus 1' to account for the 'user data header length'-field */
+#define UD_HEADER_SIZE(x) (x+1) * sizeof(uint8_t)
 
 /* SMS deliver PDU */
 struct sms_deliver {
@@ -30,6 +34,7 @@ int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn, struct msgb *msg);
 struct gsm_sms *sms_alloc(void);
 void sms_free(struct gsm_sms *sms);
 struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, int dcs, const char *text);
+void gsm_add_user_data_header(struct gsm_sms *sms);
 
 void _gsm411_sms_trans_free(struct gsm_trans *trans);
 int gsm411_send_sms_subscr(struct gsm_subscriber *subscr,
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 715ff1b..bd8ab19 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -293,6 +293,20 @@ struct gsm_network {
 #define SMS_HDR_SIZE	128
 #define SMS_TEXT_SIZE	256
 struct gsm_sms {
+	struct gsm_sms_ud_header{
+		uint8_t udh_length;	/* user data header length */
+		uint8_t iei; 		/* information element identifier:
+					 * 0 = Concatenated short messages,
+					 * 8-bit reference number (see table from 03.40 section 9.2.3.24).
+					 * If we use 8-bit reference number we have to use padding bits
+					*/
+		uint8_t length;		/* element length */
+		uint8_t parts;		/* number of parts */
+		uint8_t part;		/* current part */
+		uint8_t mi;	 	/* message identifier */
+		uint8_t padding;	/* filling bits */
+	} ud_header;
+
 	unsigned long long id;
 	struct gsm_subscriber *sender;
 	struct gsm_subscriber *receiver;
@@ -309,6 +323,7 @@ struct gsm_sms {
 	uint8_t user_data_len;
 	uint8_t user_data[SMS_TEXT_SIZE];
 
+	struct gsm_sms *next;	/* next sms part */
 	char text[SMS_TEXT_SIZE];
 };
 
diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c
index 07ad393..58f1409 100644
--- a/openbsc/src/libmsc/gsm_04_11.c
+++ b/openbsc/src/libmsc/gsm_04_11.c
@@ -117,31 +117,129 @@ void sms_free(struct gsm_sms *sms)
 	talloc_free(sms);
 }
 
+void gsm_add_user_data_header(struct gsm_sms *sms){
+	uint8_t *gen_user_data = calloc(UD_LEN, sizeof(uint8_t));
+
+	gen_user_data[0] = sms->ud_header.udh_length;
+	gen_user_data[1] = sms->ud_header.iei;
+	gen_user_data[2] = sms->ud_header.length;
+	gen_user_data[3] = sms->ud_header.mi;
+	gen_user_data[4] = sms->ud_header.parts;
+	gen_user_data[5] = sms->ud_header.part;
+
+	memcpy(gen_user_data + UD_HEADER_SIZE(sms->ud_header.udh_length),
+			sms->user_data, UD_PAYLOAD_SIZE - UD_HEADER_SIZE(sms->ud_header.udh_length));
+	memset(sms->user_data, 0, sizeof(sms->user_data));
+	memcpy(sms->user_data, gen_user_data, UD_PAYLOAD_SIZE);
+
+	free(gen_user_data);
+}
+
 struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, int dcs, const char *text)
 {
+	int sms_part = 1;
+	int start_copy = 0;
+	int end_copy = 0;
+	int copySize = 0;
+	int udh_bits = 0;
+
+	int number_of_octets;
+	int number_of_septets;
+
+	uint8_t mi = rand();
+
+	/* prepare for the worst case, every character expanding to two bytes */
+	int septet_data_max_len = strlen(text) * 2;
+	uint8_t *septet_data = calloc(septet_data_max_len, sizeof(uint8_t));
+	uint8_t *tmp_data = calloc(160, sizeof(uint8_t));
 	struct gsm_sms *sms = sms_alloc();
 
-	if (!sms)
+	if (!septet_data || !tmp_data || !sms)
 		return NULL;
 
-	sms->receiver = subscr_get(receiver);
-	strncpy(sms->text, text, sizeof(sms->text)-1);
+	number_of_septets = gsm_septet_encode(septet_data, text);
+
+	struct gsm_sms *sms_ptr = sms;
+
+	int sms_parts = number_of_septets / 160;
+	if (number_of_septets % 160 != 0)
+		sms_parts++;
+
+	do {
+		if (sms_part < sms_parts) {
+			sms_ptr->next = sms_alloc();
+		}
+
+		sms_ptr->receiver = subscr_get(receiver);
+
+		/* FIXME: don't use ID 1 static */
+		sms_ptr->sender = subscr_get_by_id(receiver->net, 1);
+		sms_ptr->reply_path_req = 0;
+		sms_ptr->status_rep_req = 0;
+		sms_ptr->protocol_id = 0; /* implicit */
+		sms_ptr->data_coding_scheme = dcs;
+		strncpy(sms_ptr->dest_addr, receiver->extension, sizeof(sms_ptr->dest_addr)-1);
+
+		/* single sms */
+		if (sms_parts <= 1) {
+			sms_ptr->ud_hdr_ind = 0;
+			sms_ptr->user_data_len = number_of_septets;
+			number_of_octets = gsm_septets2octets(sms_ptr->user_data, septet_data, number_of_septets, 0);
+		}
+
+		/* concatenated sms */
+		if (sms_parts > 1) {
+			sms_ptr->ud_hdr_ind = 1;
+
+			/*
+			 * ATM: We use a fixed UDH.
+			 * Therefore we can set the udh_length,
+			 * iei, length and the padding bits.
+			 */
+			sms_ptr->ud_header.udh_length = 5;
+			sms_ptr->ud_header.iei = 0;
+			sms_ptr->ud_header.length = 3;
+			sms_ptr->ud_header.mi = mi;
+			sms_ptr->ud_header.part = sms_part++;
+			sms_ptr->ud_header.parts = sms_parts;
+
+			udh_bits = (sms_ptr->ud_header.udh_length + 1) * 8;
+			sms_ptr->ud_header.padding = 7 - (udh_bits % 7);
+
+			/*
+			 * Copy a maximum of 153 septets (or the remaining septets).
+			 * Note: The number '152' represents the 153. spetet
+			 * at the 'septet_data'-array
+			 */
+			end_copy = (start_copy + 152) < number_of_septets ?
+					(start_copy + 152) : (number_of_septets - 1);
+			copySize = (end_copy - start_copy) + 1;
+
+			/* don't split a 2 byte extension character */
+			if (septet_data[end_copy] == 0x1b)
+				copySize--;
+
+			memset(tmp_data, 0, 160);
+			memcpy(tmp_data, septet_data + start_copy, copySize);
+
+			/* user_data_len = copySize + ud_header_size */
+			sms_ptr->user_data_len = copySize + ((udh_bits + sms_ptr->ud_header.padding) / 7);
+
+			number_of_octets = gsm_septets2octets(sms_ptr->user_data, tmp_data, copySize, sms_ptr->ud_header.padding);
+
+			gsm_add_user_data_header(sms_ptr);
+
+			start_copy += copySize;
+		}
+		sms_ptr = sms_ptr->next;
+	} while (sms_ptr != NULL);
 
-	/* FIXME: don't use ID 1 static */
-	sms->sender = subscr_get_by_id(receiver->net, 1);
-	sms->reply_path_req = 0;
-	sms->status_rep_req = 0;
-	sms->ud_hdr_ind = 0;
-	sms->protocol_id = 0; /* implicit */
-	sms->data_coding_scheme = dcs;
-	strncpy(sms->dest_addr, receiver->extension, sizeof(sms->dest_addr)-1);
-	/* Generate user_data */
-	sms->user_data_len = gsm_7bit_encode(sms->user_data, sms->text);
+	free(tmp_data);
+	free(septet_data);
 
 	return sms;
 }
 
-
 static void send_signal(int sig_no,
 			struct gsm_trans *trans,
 			struct gsm_sms *sms,
@@ -627,6 +725,16 @@ static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *m
 		octet_len = gsm_get_octet_len(gsms->user_data_len);
 		memcpy(gsms->user_data, smsp, octet_len);
 
+		if (gsms->ud_hdr_ind) {
+			gsms->ud_header.udh_length = gsms->user_data[0];
+			gsms->ud_header.iei = gsms->user_data[1];
+			gsms->ud_header.length = gsms->user_data[2];
+			gsms->ud_header.mi = gsms->user_data[3];
+			gsms->ud_header.parts = gsms->user_data[4];
+			gsms->ud_header.part = gsms->user_data[5];
+			gsms->ud_header.padding = 7 - (((gsms->ud_header.udh_length + 1) * 8) % 7);
+		}
+
 		switch (sms_alphabet) {
 		case DCS_7BIT_DEFAULT:
 			gsm_7bit_decode_hdr(gsms->text, smsp, gsms->user_data_len, gsms->ud_hdr_ind);
@@ -642,10 +750,11 @@ static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *m
 
 	LOGP(DSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, "
 	     "MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, "
+	     "ud_hdr_ind: 0x%02x, "
 	     "UserDataLength: 0x%02x, UserData: \"%s\"\n",
 	     subscr_name(gsms->sender), sms_mti, sms_vpf, gsms->msg_ref,
 	     gsms->protocol_id, gsms->data_coding_scheme, gsms->dest_addr,
-	     gsms->user_data_len,
+	     gsms->ud_hdr_ind, gsms->user_data_len,
 			sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
 				osmo_hexdump(gsms->user_data, octet_len));
 
diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c
index f31f05b..57d91fa 100644
--- a/openbsc/src/libmsc/vty_interface_layer3.c
+++ b/openbsc/src/libmsc/vty_interface_layer3.c
@@ -146,16 +146,25 @@ static int _send_sms_str(struct gsm_subscriber *receiver, char *str,
 	struct gsm_sms *sms;
 
 	sms = sms_from_text(receiver, 0, str);
-	sms->protocol_id = tp_pid;
+	struct gsm_sms *sms_ptr = sms;
 
-	/* store in database for the queue */
-	if (db_sms_store(sms) != 0) {
-		LOGP(DSMS, LOGL_ERROR, "Failed to store SMS in Database\n");
+	do {
+		sms_ptr->protocol_id = tp_pid;
+
+		if (db_sms_store(sms_ptr) != 0) {
+			LOGP(DSMS, LOGL_ERROR, "Failed to store SMS %i \\ %i in Database\n",
+					sms_ptr->ud_header.part ? sms_ptr->ud_header.part : 1,
+							sms_ptr->ud_header.parts ? sms_ptr->ud_header.parts : 1);
+			sms_free(sms_ptr);
+			return CMD_WARNING;
+		}
+
+		sms = sms_ptr;
+		sms_ptr = sms_ptr->next;
 		sms_free(sms);
-		return CMD_WARNING;
-	}
 
-	sms_free(sms);
+	} while(sms_ptr != NULL);
+
 	sms_queue_trigger(receiver->net->sms_queue);
 	return CMD_SUCCESS;
 }
-- 
1.7.1

