Hello

Here it is. After some delay (sorry!) i've finished the sms-patch. As already mentioned, i have changed the database-format for the SMS-table.
If you already have a database, you have to _drop_ the SMS-table!

If you have questions, please feel free to ask me.

Best Regards
Dennis Wehrle


On 18.03.2011 23:01, Dennis Wehrle wrote:
Hello

Our goal was to send status sms via the vty interface. But all of our sms were cropped. In contrast sms from one MS to another MS are displayed correctly (despite the fact, that the text at the database contains several '@' at the end / the user_data contains several zero-octets). Therefore i have inspect the code and found several bugs.

The main problem is that the "user_data_len" is not correctly used. As per GSM 03.40, 9.2.3.16 TP‑User‑Data‑Length (TP‑UDL): "If the TP‑User‑Data is coded using the GSM 7 bit default alphabet, the TP‑User‑Data‑Length field gives an integer representation of the number of septets within the TP‑User‑Data field to follow."

Currently the "user_data_len" contains the number of octets (returned from gsm_7bit_encode(...) at gsm_utils.c (libosmocore)).
The big problem here is that this information is not unique, e.g.:
1.) 46 non-extension characters + 1 extension character => (46 * 7 bit + (1 * (2 * 7 bit))) / 8 bit = 42 octets 2.) 47 non-extension characters => (47 * 7 bit) / 8 bit = 41,125 = 42 octets
3.) 48 non-extension characters => (48 * 7 bit) / 8 bit = 42 octects

But the MS has to know the correct "user_data_len" to decode the correct number of characters. For this reason i updated the gsm_7bit_encode() function to return the correct number of septets. However sometimes it is needed to know the correct number of octets (e.g. at gsm_04_11.c: gsm340_gen_tpdu(...)) => i added a function to gsm_utils.c named:
uint8_t get_octet_len(const uint8_t sept_len)


I have also fixed the problem, that the sms are wrongly stored / displayed on the database. But the solution on the function *sms_from_result(...) (at db.c) is not really "beautiful". This is because there exists no "user_data_len" field at the database. To store the right value for "user_data_len" (which is further needed) i have to get the length from the "text" field. Unfortunately this is not enough. If the text contains extension characters like {[]} etc. then the "user_data_len" has to be bigger because these characters needs two septets. Therefore i use a switch statement so search for these characters. A better solution for that is to store the right "user_data_len" to the database (on the encoding / decoding procedure). But i don't know if this is a suitable solution for all of you (because you have to change your database structure etc.).


Best Regards
Dennis Wehrle



>From 81b7619144a526971a3b19c7bf7c263e133081d1 Mon Sep 17 00:00:00 2001
From: Dennis Wehrle <[email protected]>
Date: Thu, 7 Jul 2011 18:56:33 +0200
Subject: [PATCH] sms: Due to a misuse of user_data_len the sms where cropped (if sent from vty) and wrongly stored on the database.
     Additionally it wasn't possible to send concatenated sms from the vty.
     To send multiple sms, it is necessary to use padding bits and add a user_data_header.
     Therefore the gsm_7bit_encode function was splitted to gsm_7bit_encode and gsm_septets2octets.
     gsm_septets2octets: this is the old gsm_7bit_encode function + additional padding parameter

    Additionally the gsm_7bit_decode function was modified to take account for the user_data_header.
    With the new gsm_get_octet_len function you can get the octet length for a given septet length.

    I also added several sms tests.
---
 include/osmocom/gsm/gsm_utils.h |    6 +-
 include/osmocom/vty/command.h   |    2 +-
 src/gsm/gsm0480.c               |    2 +-
 src/gsm/gsm_utils.c             |  109 ++++++++++++++-----
 tests/sms/sms_test.c            |  226 ++++++++++++++++++++++++++++++++++++---
 5 files changed, 298 insertions(+), 47 deletions(-)

diff --git a/include/osmocom/gsm/gsm_utils.h b/include/osmocom/gsm/gsm_utils.h
index 405dfe3..bdf982e 100644
--- a/include/osmocom/gsm/gsm_utils.h
+++ b/include/osmocom/gsm/gsm_utils.h
@@ -56,9 +56,13 @@ enum gsm_band {
 const char *gsm_band_name(enum gsm_band band);
 enum gsm_band gsm_band_parse(const char *mhz);
 
-int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length);
+int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length, uint8_t ud_hdr_ind);
 int gsm_7bit_encode(uint8_t *result, const char *data);
 
+int gsm_septets2octets(uint8_t *result, uint8_t *rdata, uint8_t septet_len, uint8_t padding);
+int gsm_septet_encode(uint8_t *result, const char *data);
+uint8_t gsm_get_octet_len(const uint8_t sept_len);
+
 unsigned int ms_class_gmsk_dbm(enum gsm_band band, int class);
 
 int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h
index caf0414..c13912f 100644
--- a/include/osmocom/vty/command.h
+++ b/include/osmocom/vty/command.h
@@ -134,7 +134,7 @@ struct desc {
 #define CMD_SUCCESS_DAEMON      10
 
 /* Argc max counts. */
-#define CMD_ARGC_MAX   25
+#define CMD_ARGC_MAX   256
 
 /* Turn off these macros when uisng cpp with extract.pl */
 #ifndef VTYSH_EXTRACT_PL
diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c
index b9b3ed9..74d091c 100644
--- a/src/gsm/gsm0480.c
+++ b/src/gsm/gsm0480.c
@@ -402,7 +402,7 @@ static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
 				if (num_chars > MAX_LEN_USSD_STRING)
 					num_chars = MAX_LEN_USSD_STRING;
 				gsm_7bit_decode(req->text,
-						&(uss_req_data[7]), num_chars);
+						&(uss_req_data[7]), num_chars, 0);
 				rc = 1;
 			}
 		}
diff --git a/src/gsm/gsm_utils.c b/src/gsm/gsm_utils.c
index 5da713c..8ac0d07 100644
--- a/src/gsm/gsm_utils.c
+++ b/src/gsm/gsm_utils.c
@@ -73,28 +73,44 @@ static int gsm_septet_lookup(uint8_t ch)
 	return -1;
 }
 
+/* Compute the number of octets from the number of septets, for instance: 47 septets needs 41,125 = 42 octets */
+uint8_t gsm_get_octet_len(const uint8_t sept_len){
+	int octet_len = (sept_len * 7) / 8;
+	if ((sept_len * 7) % 8 != 0)
+		octet_len++;
+
+	return octet_len;
+}
+
 /* GSM 03.38 6.2.1 Character unpacking */
-int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t length)
+int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind)
 {
 	int i = 0;
-	int l = 0;
-	int septet_l = (length * 8) / 7;
+	int shift = 0;
+
 	uint8_t *rtext = calloc(septet_l, sizeof(uint8_t));
 	uint8_t tmp;
 
-	/* FIXME: We need to account for user data headers here */
-	i += l;
-	for (; i < septet_l; i++){
+	/* skip the user data header */
+	if(ud_hdr_ind){
+		/* get user data header length + 1 (for the 'user data header length'-field) */
+		shift = ((user_data[0] + 1) * 8) / 7;
+		if((((user_data[0] + 1) * 8) % 7) != 0)
+			shift++;
+		septet_l = septet_l - shift;
+	}
+
+	for (i = 0; i < septet_l; i++){
 		rtext[i] =
-			((user_data[(i * 7 + 7) >> 3] <<
-			  (7 - ((i * 7 + 7) & 7))) |
-			 (user_data[(i * 7) >> 3] >>
-			  ((i * 7) & 7))) & 0x7f;
+			((user_data[((i + shift) * 7 + 7) >> 3] <<
+			  (7 - (((i + shift) * 7 + 7) & 7))) |
+			 (user_data[((i + shift) * 7) >> 3] >>
+			  (((i + shift) * 7) & 7))) & 0x7f;
 	}
 
 	for(i = 0; i < septet_l; i++){
 		/* this is an extension character */
-		if(rtext[i] == 0x1b && i + 1 < length){
+		if(rtext[i] == 0x1b && i + 1 < septet_l){
 			tmp = rtext[i+1];
 			*(text++) = gsm_7bit_alphabet[0x7f + tmp];
 			i++;
@@ -104,6 +120,9 @@ int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t length)
 		*(text++) = gsm_septet_lookup(rtext[i]);
 	}
 
+	if(ud_hdr_ind){
+		i += shift;
+	}
 	*text = '\0';
 	free(rtext);
 
@@ -111,7 +130,7 @@ int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t length)
 }
 
 /* GSM 03.38 6.2.1 Prepare character packing */
-static int gsm_septet_encode(uint8_t *result, const char *data)
+int gsm_septet_encode(uint8_t *result, const char *data)
 {
 	int i, y = 0;
 	uint8_t ch;
@@ -139,38 +158,68 @@ static int gsm_septet_encode(uint8_t *result, const char *data)
 	return y;
 }
 
-/* GSM 03.38 6.2.1 Character packing */
-int gsm_7bit_encode(uint8_t *result, const char *data)
-{
-	int i,y,z = 0;
-	/* prepare for the worst case, every character expanding to two bytes */
-	uint8_t *rdata = calloc(strlen(data) * 2, sizeof(uint8_t));
+/* 7bit to octet packing */
+int gsm_septets2octets(uint8_t *result, uint8_t *rdata, uint8_t septet_len, uint8_t padding){
+	int i = 0, z = 0;
 	uint8_t cb, nb;
 	int shift = 0;
-
-	y = gsm_septet_encode(rdata, data);
-
-	for(i = 0; i < y; i++) {
-		if(shift == 7 && i + 1 < y){
-			shift = 0;
-			continue;
+	uint8_t *data = calloc(septet_len + 1, sizeof(uint8_t));
+
+	if(padding){
+		shift = 7 - padding;
+		/* the first zero is needed for padding */
+		memcpy(data + 1, rdata, septet_len);
+		septet_len++;
+	}else
+		memcpy(data, rdata, septet_len);
+
+	for(i = 0; i < septet_len; i++) {
+		if(shift == 7){
+			if(i + 1 < septet_len){
+				shift = 0;
+				continue;
+			}
+		/* special end case. This is necessary if the last septet fits into the previous octet. */
+		/* E.g. 48 non-extension characters: ....ag ( a = 1100001, g = 1100111) */
+		/* result[40] = 100001 XX */
+		/* result[41] = 1100111 1 */
+			else if (i + 1 == septet_len)
+				break;
 		}
 
-		cb = (rdata[i] & 0x7f) >> shift;
-		if(i + 1 < y){
-			nb = (rdata[i + 1] & 0x7f) << (7 - shift);
+		cb = (data[i] & 0x7f) >> shift;
+		if(i + 1 < septet_len){
+			nb = (data[i + 1] & 0x7f) << (7 - shift);
 			cb = cb | nb;
 		}
 
 		result[z++] = cb;
-
 		shift++;
 	}
 
-	free(rdata);
+	free(data);
+
 	return z;
 }
 
+/* GSM 03.38 6.2.1 Character packing */
+int gsm_7bit_encode(uint8_t *result, const char *data)
+{
+	int y = 0, z = 0;
+	/* prepare for the worst case, every character expanding to two bytes */
+	uint8_t *rdata = calloc(strlen(data) * 2, sizeof(uint8_t));
+	y = gsm_septet_encode(rdata, data);
+	z = gsm_septets2octets(result, rdata, y, 0);
+
+	free(rdata);
+
+	/* we don't care about the number of octets (z), because they are not unique. E.g.: */
+	/* 1.) 46 non-extension characters + 1 extension character => (46 * 7 bit + (1 * (2 * 7 bit))) / 8 bit =  42 octets */
+	/* 2.) 47 non-extension characters => (47 * 7 bit) / 8 bit = 41,125 = 42 octets */
+	/* 3.) 48 non-extension characters => (48 * 7 bit) / 8 bit = 42 octects */
+	return y;
+}
+
 /* convert power class to dBm according to GSM TS 05.05 */
 unsigned int ms_class_gmsk_dbm(enum gsm_band band, int class)
 {
diff --git a/tests/sms/sms_test.c b/tests/sms/sms_test.c
index b4ed631..afb00ad 100644
--- a/tests/sms/sms_test.c
+++ b/tests/sms/sms_test.c
@@ -31,15 +31,19 @@ struct test_case {
 	const uint16_t input_length;
 
 	const uint8_t *expected;
-	const uint16_t expected_length;
+	const uint16_t expected_octet_length;
+	const uint16_t expected_septet_length;
+	const uint8_t ud_hdr_ind;
 };
 
 static const char simple_text[] = "test text";
+#define simple_septet_length 9
 static const uint8_t simple_enc[] = {
 	0xf4, 0xf2, 0x9c, 0x0e, 0xa2, 0x97, 0xf1, 0x74
 };
 
-static const char escape_text[] = "!$ a more#^- complicated test@@?_\%! case";
+static const char escape_text[] = "!$ a more#^- complicated test@@?_%! case";
+#define escape_septet_length 41 /* note: the ^ counts as two, because it is a extension character */
 static const uint8_t escape_enc[] = {
 	0x21, 0x01, 0x28, 0x0c, 0x6a, 0xbf, 0xe5, 0xe5, 0xd1,
 	0x86, 0xd2, 0x02, 0x8d, 0xdf, 0x6d, 0x38, 0x3b, 0x3d,
@@ -47,17 +51,115 @@ static const uint8_t escape_enc[] = {
 	0x00, 0xbf, 0x48, 0x29, 0x04, 0x1a, 0x87, 0xe7, 0x65,
 };
 
+static const char enhanced_text[] = "enhanced ^ {][} test |+~ ^ test";
+#define enhanced_septet_length 39 /* note: the characters { } [ ] ^ | ~ count as two (each of them), because they are extension characters */
+static const uint8_t enhanced_enc[] = {
+	0x65, 0x37, 0x3A, 0xEC, 0x1E, 0x97, 0xC9, 0xA0, 0x0D,
+	0x05, 0xB4, 0x41, 0x6D, 0x7C, 0x1B, 0xDE, 0x26, 0x05,
+	0xA2, 0x97, 0xE7, 0x74, 0xD0, 0x06, 0xB8, 0xDA, 0xF4,
+	0x40, 0x1B, 0x0A, 0x88, 0x5E, 0x9E, 0xD3, 0x01,
+};
+
+static const char enhancedV2_text[] = "enhanced ^ {][} test |+~ ^ tests";
+#define enhancedV2_septet_length 40 /* note: number of octets are equal to the enhanced_text! */
+static const uint8_t enhancedV2_enc[] = {
+	0x65, 0x37, 0x3A, 0xEC, 0x1E, 0x97, 0xC9, 0xA0, 0x0D,
+	0x05, 0xB4, 0x41, 0x6D, 0x7C, 0x1B, 0xDE, 0x26, 0x05,
+	0xA2, 0x97, 0xE7, 0x74, 0xD0, 0x06, 0xB8, 0xDA, 0xF4,
+	0x40, 0x1B, 0x0A, 0x88, 0x5E, 0x9E, 0xD3, 0xE7,
+};
+
+
+
+static const char concatenated_text[] =
+		"this is a testmessage. this is a testmessage. this is a testmessage. this is a testmessage. "
+		"this is a testmessage. this is a testmessage. cut here .....: this is a second testmessage. end here.";
+
+static const char splitted_text_part1[] =
+		"this is a testmessage. this is a testmessage. this is a testmessage. this is a testmessage. "
+		"this is a testmessage. this is a testmessage. cut here .....:";
+#define concatenated_part1_septet_length_with_header 160
+#define concatenated_part1_septet_length 153
+static const uint8_t concatenated_part1_enc[] = {
+		0x05, 0x00, 0x03, 0x6f, 0x02, 0x01,
+		0xe8, 0xe8, 0xf4, 0x1c, 0x94, 0x9e, 0x83, 0xc2,
+		0x20, 0x7a, 0x79, 0x4e, 0x6f, 0x97, 0xe7, 0xf3,
+		0xf0, 0xb9, 0xec, 0x02, 0xd1, 0xd1, 0xe9, 0x39,
+		0x28, 0x3d, 0x07, 0x85, 0x41, 0xf4, 0xf2, 0x9c,
+		0xde, 0x2e, 0xcf, 0xe7, 0xe1, 0x73, 0xd9, 0x05,
+		0xa2, 0xa3, 0xd3, 0x73, 0x50, 0x7a, 0x0e, 0x0a,
+		0x83, 0xe8, 0xe5, 0x39, 0xbd, 0x5d, 0x9e, 0xcf,
+		0xc3, 0xe7, 0xb2, 0x0b, 0x44, 0x47, 0xa7, 0xe7,
+		0xa0, 0xf4, 0x1c, 0x14, 0x06, 0xd1, 0xcb, 0x73,
+		0x7a, 0xbb, 0x3c, 0x9f, 0x87, 0xcf, 0x65, 0x17,
+		0x88, 0x8e, 0x4e, 0xcf, 0x41, 0xe9, 0x39, 0x28,
+		0x0c, 0xa2, 0x97, 0xe7, 0xf4, 0x76, 0x79, 0x3e,
+		0x0f, 0x9f, 0xcb, 0x2e, 0x10, 0x1d, 0x9d, 0x9e,
+		0x83, 0xd2, 0x73, 0x50, 0x18, 0x44, 0x2f, 0xcf,
+		0xe9, 0xed, 0xf2, 0x7c, 0x1e, 0x3e, 0x97, 0x5d,
+		0xa0, 0x71, 0x9d, 0x0e, 0x42, 0x97, 0xe5, 0x65,
+		0x90, 0xcb, 0xe5, 0x72, 0xb9, 0x74,
+};
+
+static const char splitted_text_part2[] = " this is a second testmessage. end here.";
+#define concatenated_part2_septet_length_with_header 47
+#define concatenated_part2_septet_length 40
+static const uint8_t concatenated_part2_enc[] = {
+		0x05, 0x00, 0x03, 0x6f, 0x02, 0x02,
+		0x40, 0x74, 0x74, 0x7a, 0x0e, 0x4a, 0xcf, 0x41,
+		0x61, 0xd0, 0xbc, 0x3c, 0x7e, 0xbb, 0xc9, 0x20,
+		0x7a, 0x79, 0x4e, 0x6f, 0x97, 0xe7, 0xf3, 0xf0,
+		0xb9, 0xec, 0x02, 0x95, 0xdd, 0x64, 0x10, 0xba,
+		0x2c, 0x2f, 0xbb, 0x00,
+};
+
+static const struct test_case test_multiple_encode[] =
+{
+	{
+		.input = concatenated_text,
+		.expected = concatenated_part1_enc,
+		.expected_octet_length = sizeof(concatenated_part1_enc),
+		.expected_septet_length = concatenated_part1_septet_length,
+		.ud_hdr_ind = 1,
+	},
+	{
+		.input = concatenated_text,
+		.expected = concatenated_part2_enc,
+		.expected_octet_length = sizeof(concatenated_part2_enc),
+		.expected_septet_length = concatenated_part2_septet_length,
+		.ud_hdr_ind = 1,
+	},
+};
+
 static const struct test_case test_encode[] =
 {
 	{
 		.input = simple_text,
 		.expected = simple_enc,
-		.expected_length = sizeof(simple_enc),
+		.expected_octet_length = sizeof(simple_enc),
+		.expected_septet_length = simple_septet_length,
+		.ud_hdr_ind = 0,
 	},
 	{
 		.input = escape_text,
 		.expected = escape_enc,
-		.expected_length = sizeof(escape_enc),
+		.expected_octet_length = sizeof(escape_enc),
+		.expected_septet_length = escape_septet_length,
+		.ud_hdr_ind = 0,
+	},
+	{
+		.input = enhanced_text,
+		.expected = enhanced_enc,
+		.expected_octet_length = sizeof(enhanced_enc),
+		.expected_septet_length = enhanced_septet_length,
+		.ud_hdr_ind = 0,
+	},
+	{
+		.input = enhancedV2_text,
+		.expected = enhancedV2_enc,
+		.expected_octet_length = sizeof(enhancedV2_enc),
+		.expected_septet_length = enhancedV2_septet_length,
+		.ud_hdr_ind = 0,
 	},
 };
 
@@ -67,11 +169,43 @@ static const struct test_case test_decode[] =
 		.input = simple_enc,
 		.input_length = sizeof(simple_enc),
 		.expected = simple_text,
+		.expected_septet_length = simple_septet_length,
+		.ud_hdr_ind = 0,
 	},
 	{
 		.input = escape_enc,
 		.input_length = sizeof(escape_enc),
 		.expected = escape_text,
+		.expected_septet_length = escape_septet_length,
+		.ud_hdr_ind = 0,
+	},
+	{
+		.input = enhanced_enc,
+		.input_length = sizeof(enhanced_enc),
+		.expected = enhanced_text,
+		.expected_septet_length = enhanced_septet_length,
+		.ud_hdr_ind = 0,
+	},
+	{
+		.input = enhancedV2_enc,
+		.input_length = sizeof(enhancedV2_enc),
+		.expected = enhancedV2_text,
+		.expected_septet_length = enhancedV2_septet_length,
+		.ud_hdr_ind = 0,
+	},
+	{
+		.input = concatenated_part1_enc,
+		.input_length = sizeof(concatenated_part1_enc),
+		.expected = splitted_text_part1,
+		.expected_septet_length = concatenated_part1_septet_length_with_header,
+		.ud_hdr_ind = 1,
+	},
+	{
+		.input = concatenated_part2_enc,
+		.input_length = sizeof(concatenated_part2_enc),
+		.expected = splitted_text_part2,
+		.expected_septet_length = concatenated_part2_septet_length_with_header,
+		.ud_hdr_ind = 1,
 	},
 };
 
@@ -79,41 +213,105 @@ int main(int argc, char** argv)
 {
 	printf("SMS testing\n");
 	struct msgb *msg;
-	uint8_t *sms;
 	uint8_t i;
 
-	uint8_t length;
+	uint8_t octet_length;
+	uint8_t septet_length;
+	uint8_t gsm_septet_length;
 	uint8_t coded[256];
+	uint8_t tmp[160];
+	uint8_t septet_data[256];
+	uint8_t ud_header[6];
 	char result[256];
 
 	/* test 7-bit encoding */
 	for (i = 0; i < ARRAY_SIZE(test_encode); ++i) {
 		memset(coded, 0x42, sizeof(coded));
-		length = gsm_7bit_encode(coded, test_encode[i].input);
+		septet_length = gsm_7bit_encode(coded, test_encode[i].input);
+		octet_length = gsm_get_octet_len(septet_length);
+		if (octet_length != test_encode[i].expected_octet_length) {
+			fprintf(stderr, "Encode case %d: Octet length failure. Got %d, expected %d\n",
+				i, octet_length, test_encode[i].expected_octet_length);
+			return -1;
+		}
 
-		if (length != test_encode[i].expected_length) {
-			fprintf(stderr, "Failed to encode case %d. Got %d, expected %d\n",
-				i, length, test_encode[i].expected_length);
+		if (septet_length != test_encode[i].expected_septet_length){
+			fprintf(stderr, "Encode case %d: Septet length failure. Got %d, expected %d\n",
+				i, septet_length, test_encode[i].expected_septet_length);
 			return -1;
 		}
 
-		if (memcmp(coded, test_encode[i].expected, length) != 0) {
-			fprintf(stderr, "Encoded content does not match for %d\n",
+		if (memcmp(coded, test_encode[i].expected, octet_length) != 0) {
+			fprintf(stderr, "Encoded content does not match for case %d\n",
 				i);
 			return -1;
 		}
 	}
 
+
+	/* Test: encode multiple SMS */
+	int number_of_septets = gsm_septet_encode(septet_data, test_multiple_encode[0].input);
+
+	/* SMS part 1 */
+	memset(tmp, 0x42, sizeof(tmp));
+	memset(coded, 0x42, sizeof(coded));
+	memcpy(tmp, septet_data, concatenated_part1_septet_length);
+
+	/* In our case: test_multiple_decode[0].ud_hdr_ind equals number of padding bits*/
+	octet_length = gsm_septets2octets(coded, tmp, concatenated_part1_septet_length, test_multiple_encode[0].ud_hdr_ind);
+
+	/* copy header */
+	memset(tmp, 0x42, sizeof(tmp));
+	int udh_length = test_multiple_encode[0].expected[0] + 1;
+	memcpy(tmp, test_multiple_encode[0].expected, udh_length);
+	memcpy(tmp + udh_length, coded, octet_length);
+	memset(coded, 0x42, sizeof(coded));
+	memcpy(coded, tmp, octet_length + 6);
+
+	if (memcmp(coded, test_multiple_encode[0].expected, octet_length) != 0) {
+		fprintf(stderr, "Multiple-SMS encoded content does not match for part 1\n");
+		return -1;
+	}
+
+
+	/* SMS part 2 */
+	memset(tmp, 0x42, sizeof(tmp));
+	memset(coded, 0x42, sizeof(coded));
+	memcpy(tmp, septet_data + concatenated_part1_septet_length, concatenated_part2_septet_length);
+
+	/* In our case: test_multiple_decode[1].ud_hdr_ind equals number of padding bits*/
+	octet_length = gsm_septets2octets(coded, tmp, concatenated_part2_septet_length, test_multiple_encode[1].ud_hdr_ind);
+
+	/* copy header */
+	memset(tmp, 0x42, sizeof(tmp));
+	udh_length = test_multiple_encode[1].expected[0] + 1;
+	memcpy(tmp, test_multiple_encode[1].expected, udh_length);
+	memcpy(tmp + udh_length, coded, octet_length);
+	memset(coded, 0x42, sizeof(coded));
+	memcpy(coded, tmp, octet_length + 6);
+
+	if (memcmp(coded, test_multiple_encode[1].expected, octet_length) != 0) {
+		fprintf(stderr, "Multiple-SMS encoded content does not match for part 2\n");
+		return -1;
+	}
+
+
+
 	/* test 7-bit decoding */
 	for (i = 0; i < ARRAY_SIZE(test_decode); ++i) {
 		memset(result, 0x42, sizeof(coded));
-		gsm_7bit_decode(result, test_decode[i].input,
-				test_decode[i].input_length);
+		septet_length = gsm_7bit_decode(result, test_decode[i].input,
+				test_decode[i].expected_septet_length, test_decode[i].ud_hdr_ind);
 
 		if (strcmp(result, test_decode[i].expected) != 0) {
 			fprintf(stderr, "Test case %d failed to decode.\n", i);
 			return -1;
 		}
+		if (septet_length != test_decode[i].expected_septet_length){
+			fprintf(stderr, "Decode case %d: Septet length failure. Got %d, expected %d\n",
+				i, septet_length, test_decode[i].expected_septet_length);
+			return -1;
+		}
 	}
 
 	printf("OK\n");
-- 
1.7.1

>From 28a5e8371f8b8a9766188f8a8d2563aa2434b0f0 Mon Sep 17 00:00:00 2001
From: Dennis Wehrle <[email protected]>
Date: Thu, 7 Jul 2011 18:54:09 +0200
Subject: [PATCH] sms: Due to a misuse of user_data_len the sms where cropped (if sent from vty) and wrongly stored on the database.
     Additionally it wasn't possible to send concatenated sms from the vty.
     Therefore several changes was made.

    To solve the user_data_len problem and for privacy reasons, the database structure was changed as follows:
    Deleted:
      - sender_id : as already discussed, use the extension
      - receiver_id : as already discussed, use the extension
      - header : not used
      - text : removed for privacy reason
    Added:
      - source_add : extension from sender
      - dest_addr : extension from receiver
      - ud_length : important identifier for user data

    To make it possible to send concatenated sms from the vty the sms_from_text-function (on gsm_04_11.c) was rewritten.
---
 openbsc/include/openbsc/gsm_04_11.h       |    4 +
 openbsc/include/openbsc/gsm_data.h        |   14 +++-
 openbsc/src/libmsc/db.c                   |   82 ++++++++---------
 openbsc/src/libmsc/gsm_04_11.c            |  145 ++++++++++++++++++++++++-----
 openbsc/src/libmsc/gsm_04_80.c            |    4 +-
 openbsc/src/libmsc/vty_interface_layer3.c |   23 +++--
 6 files changed, 198 insertions(+), 74 deletions(-)

diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h
index 2abe3e2..ca09380 100644
--- a/openbsc/include/openbsc/gsm_04_11.h
+++ b/openbsc/include/openbsc/gsm_04_11.h
@@ -4,6 +4,9 @@
 #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)
+#define UD_HEADER_SIZE(x) (x+1) * sizeof(uint8_t)	/* Add 'plus 1' to account for the 'user data header length'-field */
 
 /* SMS deliver PDU */
 struct sms_deliver {
@@ -30,6 +33,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..0febe18 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -293,6 +293,18 @@ 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;
@@ -308,7 +320,7 @@ struct gsm_sms {
 				 * BCD == 20 bytes string */
 	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/db.c b/openbsc/src/libmsc/db.c
index 1ddd3fd..f54ae49 100644
--- a/openbsc/src/libmsc/db.c
+++ b/openbsc/src/libmsc/db.c
@@ -92,21 +92,19 @@ static char *create_stmts[] = {
 		"id INTEGER PRIMARY KEY AUTOINCREMENT, "
 		"created TIMESTAMP NOT NULL, "
 		"sent TIMESTAMP, "
-		"sender_id INTEGER NOT NULL, "
-		"receiver_id INTEGER NOT NULL, "
 		"deliver_attempts INTEGER NOT NULL DEFAULT 0, "
 		/* data directly copied/derived from SMS */
+		"source_addr TEXT NOT NULL, "
+		"dest_addr TEXT, "
 		"valid_until TIMESTAMP, "
 		"reply_path_req INTEGER NOT NULL, "
 		"status_rep_req INTEGER NOT NULL, "
 		"protocol_id INTEGER NOT NULL, "
 		"data_coding_scheme INTEGER NOT NULL, "
 		"ud_hdr_ind INTEGER NOT NULL, "
-		"dest_addr TEXT, "
 		"user_data BLOB, "	/* TP-UD */
-		/* additional data, interpreted from SMS */
-		"header BLOB, "		/* UD Header */
-		"text TEXT "		/* decoded UD after UDH */
+		/* user data length. Note: equals number of septets */
+		"ud_length INTEGER NOT NULL "
 		")",
 	"CREATE TABLE IF NOT EXISTS VLR ("
 		"id INTEGER PRIMARY KEY AUTOINCREMENT, "
@@ -988,31 +986,32 @@ int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IM
 int db_sms_store(struct gsm_sms *sms)
 {
 	dbi_result result;
-	char *q_text, *q_daddr;
+	char *q_saddr, *q_daddr;
 	unsigned char *q_udata;
 	char *validity_timestamp = "2222-2-2";
+	uint8_t octet_len = 0;
 
 	/* FIXME: generate validity timestamp based on validity_minutes */
 
-	dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
+	dbi_conn_quote_string_copy(conn, (char *)sms->sender->extension, &q_saddr);
 	dbi_conn_quote_string_copy(conn, (char *)sms->dest_addr, &q_daddr);
-	dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len,
+	octet_len = gsm_get_octet_len(sms->user_data_len);
+	dbi_conn_quote_binary_copy(conn, sms->user_data, octet_len,
 				   &q_udata);
 	/* FIXME: correct validity period */
 	result = dbi_conn_queryf(conn,
 		"INSERT INTO SMS "
-		"(created, sender_id, receiver_id, valid_until, "
+		"(created, source_addr, dest_addr, valid_until, "
 		 "reply_path_req, status_rep_req, protocol_id, "
-		 "data_coding_scheme, ud_hdr_ind, dest_addr, "
-		 "user_data, text) VALUES "
-		"(datetime('now'), %llu, %llu, %u, "
-		 "%u, %u, %u, %u, %u, %s, %s, %s)",
-		sms->sender->id,
-		sms->receiver ? sms->receiver->id : 0, validity_timestamp,
+		 "data_coding_scheme, ud_hdr_ind, "
+		 "user_data, ud_length) VALUES "
+		 "(datetime('now'), %s, %s, %u, "
+		 "%u, %u, %u, %u, %u, %s, %u)",
+		q_saddr, q_daddr, validity_timestamp,
 		sms->reply_path_req, sms->status_rep_req, sms->protocol_id,
 		sms->data_coding_scheme, sms->ud_hdr_ind,
-		q_daddr, q_udata, q_text);
-	free(q_text);
+		q_udata, sms->user_data_len);
+	free(q_saddr);
 	free(q_daddr);
 	free(q_udata);
 
@@ -1026,20 +1025,25 @@ int db_sms_store(struct gsm_sms *sms)
 static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result)
 {
 	struct gsm_sms *sms = sms_alloc();
-	long long unsigned int sender_id, receiver_id;
-	const char *text, *daddr;
+	const char *daddr, *saddr;
 	const unsigned char *user_data;
+	int octet_len;
 
 	if (!sms)
 		return NULL;
 
 	sms->id = dbi_result_get_ulonglong(result, "id");
 
-	sender_id = dbi_result_get_ulonglong(result, "sender_id");
-	sms->sender = subscr_get_by_id(net, sender_id);
+	saddr = dbi_result_get_string(result, "source_addr");
+	sms->sender = subscr_get_by_extension(net, saddr);
 
-	receiver_id = dbi_result_get_ulonglong(result, "receiver_id");
-	sms->receiver = subscr_get_by_id(net, receiver_id);
+	daddr = dbi_result_get_string(result, "dest_addr");
+	sms->receiver = subscr_get_by_extension(net, daddr);
+
+	if (daddr) {
+			strncpy(sms->dest_addr, daddr, sizeof(sms->dest_addr));
+			sms->dest_addr[sizeof(sms->dest_addr)-1] = '\0';
+	}
 
 	/* FIXME: validity */
 	/* FIXME: those should all be get_uchar, but sqlite3 is braindead */
@@ -1051,23 +1055,15 @@ static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result resul
 						  "data_coding_scheme");
 	/* sms->msg_ref is temporary and not stored in DB */
 
-	daddr = dbi_result_get_string(result, "dest_addr");
-	if (daddr) {
-		strncpy(sms->dest_addr, daddr, sizeof(sms->dest_addr));
-		sms->dest_addr[sizeof(sms->dest_addr)-1] = '\0';
-	}
-
-	sms->user_data_len = dbi_result_get_field_length(result, "user_data");
+	sms->user_data_len = dbi_result_get_uint(result, "ud_length");
 	user_data = dbi_result_get_binary(result, "user_data");
+
 	if (sms->user_data_len > sizeof(sms->user_data))
 		sms->user_data_len = (uint8_t) sizeof(sms->user_data);
-	memcpy(sms->user_data, user_data, sms->user_data_len);
 
-	text = dbi_result_get_string(result, "text");
-	if (text) {
-		strncpy(sms->text, text, sizeof(sms->text));
-		sms->text[sizeof(sms->text)-1] = '\0';
-	}
+	octet_len = gsm_get_octet_len(sms->user_data_len);
+	memcpy(sms->user_data, user_data, octet_len);
+
 	return sms;
 }
 
@@ -1102,7 +1098,7 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long mi
 	result = dbi_conn_queryf(conn,
 		"SELECT SMS.* "
 			"FROM SMS JOIN Subscriber ON "
-				"SMS.receiver_id = Subscriber.id "
+				"SMS.dest_addr = Subscriber.extension "
 			"WHERE SMS.id >= %llu AND SMS.sent IS NULL "
 				"AND Subscriber.lac > 0 "
 			"ORDER BY SMS.id LIMIT 1",
@@ -1132,10 +1128,10 @@ struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net,
 	result = dbi_conn_queryf(conn,
 		"SELECT SMS.* "
 			"FROM SMS JOIN Subscriber ON "
-				"SMS.receiver_id = Subscriber.id "
-			"WHERE SMS.receiver_id >= %llu AND SMS.sent IS NULL "
+				"SMS.dest_addr = Subscriber.extension "
+			"WHERE Subscriber.id >= %llu AND SMS.sent IS NULL "
 				"AND Subscriber.lac > 0 AND SMS.deliver_attempts < %u "
-			"ORDER BY SMS.receiver_id, SMS.id LIMIT 1",
+			"ORDER BY SMS.dest_addr, SMS.id LIMIT 1",
 		min_subscr_id, failed);
 	if (!result)
 		return NULL;
@@ -1161,8 +1157,8 @@ struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
 	result = dbi_conn_queryf(conn,
 		"SELECT SMS.* "
 			"FROM SMS JOIN Subscriber ON "
-				"SMS.receiver_id = Subscriber.id "
-			"WHERE SMS.receiver_id = %llu AND SMS.sent IS NULL "
+				"SMS.dest_addr = Subscriber.extension "
+			"WHERE Subscriber.id = %llu AND SMS.sent IS NULL "
 				"AND Subscriber.lac > 0 "
 			"ORDER BY SMS.id LIMIT 1",
 		subscr->id);
diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c
index ba72c37..f309507 100644
--- a/openbsc/src/libmsc/gsm_04_11.c
+++ b/openbsc/src/libmsc/gsm_04_11.c
@@ -117,31 +117,121 @@ 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);
 
-	/* 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);
+			/* 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);
+
+	free(tmp_data);
+	free(septet_data);
 
 	return sms;
 }
 
-
 static void send_signal(int sig_no,
 			struct gsm_trans *trans,
 			struct gsm_sms *sms,
@@ -526,9 +616,8 @@ static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
 	/* generate TP-UD */
 	switch (gsm338_get_sms_alphabet(sms->data_coding_scheme)) {
 	case DCS_7BIT_DEFAULT:
-		octet_len = sms->user_data_len*7/8;
-		if (sms->user_data_len*7%8 != 0)
-			octet_len++;
+		octet_len = gsm_get_octet_len(sms->user_data_len);
+
 		/* Warning, user_data_len indicates the amount of septets
 		 * (characters), we need amount of octets occupied */
 		smsp = msgb_put(msg, octet_len);
@@ -558,6 +647,7 @@ static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *m
 	uint8_t *sms_vp;
 	uint8_t da_len_bytes;
 	uint8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
+	uint8_t octet_len;
 	int rc = 0;
 
 	osmo_counter_inc(conn->bts->network->stats.sms.submitted);
@@ -623,11 +713,22 @@ static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *m
 	}
 	gsms->user_data_len = *smsp++;
 	if (gsms->user_data_len) {
-		memcpy(gsms->user_data, smsp, gsms->user_data_len);
+		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(gsms->text, smsp, gsms->user_data_len);
+			gsm_7bit_decode(gsms->text, smsp, gsms->user_data_len, gsms->ud_hdr_ind);
 			break;
 		case DCS_8BIT_DATA:
 		case DCS_UCS2:
@@ -640,13 +741,13 @@ 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, gsms->user_data_len));
-
+				osmo_hexdump(gsms->user_data, octet_len));
 	gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
 
 	/* FIXME: This looks very wrong */
diff --git a/openbsc/src/libmsc/gsm_04_80.c b/openbsc/src/libmsc/gsm_04_80.c
index 39738a5..c7dad43 100644
--- a/openbsc/src/libmsc/gsm_04_80.c
+++ b/openbsc/src/libmsc/gsm_04_80.c
@@ -68,12 +68,14 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh;
 	uint8_t *ptr8;
+	uint8_t octet_len;
 	int response_len;
 
 	/* First put the payload text into the message */
 	ptr8 = msgb_put(msg, 0);
 	response_len = gsm_7bit_encode(ptr8, response_text);
-	msgb_put(msg, response_len);
+	octet_len = gsm_get_octet_len(response_len);
+	msgb_put(msg, octet_len);
 
 	/* Then wrap it as an Octet String */
 	msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG);
diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c
index f31f05b..3e2abea 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;
 
-	/* store in database for the queue */
-	if (db_sms_store(sms) != 0) {
-		LOGP(DSMS, LOGL_ERROR, "Failed to store SMS in Database\n");
+	struct gsm_sms *sms_ptr = sms;
+	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

Reply via email to