aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mm-base-cbm.c1
-rw-r--r--src/mm-cbm-part.c120
-rw-r--r--src/mm-cbm-part.h3
-rw-r--r--src/tests/test-cbm-part.c59
4 files changed, 173 insertions, 10 deletions
diff --git a/src/mm-base-cbm.c b/src/mm-base-cbm.c
index fe566882..71828284 100644
--- a/src/mm-base-cbm.c
+++ b/src/mm-base-cbm.c
@@ -173,6 +173,7 @@ initialize_cbm (MMBaseCbm *self)
"channel", mm_cbm_part_get_channel (part),
"update", CBM_SERIAL_MESSAGE_CODE_UPDATE (serial),
"message-code", CBM_SERIAL_MESSAGE_CODE (serial),
+ "language", mm_cbm_part_get_language (part),
NULL);
}
diff --git a/src/mm-cbm-part.c b/src/mm-cbm-part.c
index 8ac92759..18df7c17 100644
--- a/src/mm-cbm-part.c
+++ b/src/mm-cbm-part.c
@@ -27,9 +27,17 @@
#include "mm-sms-part-3gpp.h"
#define CBS_DATA_CODING_GROUP_MASK 0b11110000
+#define CBS_DATA_CODING_LANG_MASK 0b00001111
#define CBS_DATA_CODING_LANG_GSM7 0b00000000
#define CBS_DATA_CODING_GSM7 0b00010000
#define CBS_DATA_CODING_UCS2 0b00010001
+#define CBS_DATA_CODING_OTHER 0b00100000
+#define CBS_DATA_CODING_OTHER_CZECH 0b00100000
+#define CBS_DATA_CODING_OTHER_HEBREW 0b00100001
+#define CBS_DATA_CODING_OTHER_ARABIC 0b00100010
+#define CBS_DATA_CODING_OTHER_RUSSIAN 0b00100011
+#define CBS_DATA_CODING_OTHER_ICELANDIC 0b00100100
+#define CBS_DATA_CODING_UNSPECIFIED 0b00110000
#define CBS_DATA_CODING_GENERAL_NO_CLASS 0b01000000
#define CBS_DATA_CODING_GENERAL_CLASS 0b01010000
#define CBS_DATA_CODING_GENERAL_CHARSET_MASK 0b00001100
@@ -46,10 +54,48 @@ struct _MMCbmPart {
guint8 part_num;
gchar *text;
+ gchar *language;
MMSmsEncoding encoding;
};
+static gchar *
+mm_cbm_part_coding_group_to_language (guint8 group)
+{
+ switch (group & CBS_DATA_CODING_LANG_MASK) {
+ case 0b0000:
+ return g_strdup ("de");
+ case 0b0001:
+ return g_strdup ("en");
+ case 0b0010:
+ return g_strdup ("it");
+ case 0b0011:
+ return g_strdup ("fr");
+ case 0b0100:
+ return g_strdup ("es");
+ case 0b0101:
+ return g_strdup ("nl");
+ case 0b0110:
+ return g_strdup ("sv");
+ case 0b0111:
+ return g_strdup ("dk");
+ case 0b1000:
+ return g_strdup ("pt");
+ case 0b1001:
+ return g_strdup ("fi");
+ case 0b1010:
+ return g_strdup ("no");
+ case 0b1011:
+ return g_strdup ("el");
+ case 0b1110:
+ return g_strdup ("pl");
+ case 0b1111:
+ default:
+ return NULL;
+ };
+}
+
+
MMCbmPart *
mm_cbm_part_new_from_pdu (const gchar *hexpdu,
gpointer log_object,
@@ -74,12 +120,13 @@ mm_cbm_part_new_from_binary_pdu (const guint8 *pdu,
gpointer log_object,
GError **error)
{
- MMCbmPart *cbm_part;
+ g_autoptr (MMCbmPart) cbm_part = NULL;
MMCbmGeoScope scope;
guint offset = 0;
guint16 serial, group;
int len;
g_autofree gchar *text = NULL;
+ gboolean has_lang = FALSE, has_7bit_lang = FALSE;
cbm_part = mm_cbm_part_new ();
@@ -94,7 +141,6 @@ mm_cbm_part_new_from_binary_pdu (const guint8 *pdu,
check_descr_str, \
pdu_len, \
required_size); \
- mm_cbm_part_free (cbm_part); \
return NULL; \
}
@@ -116,7 +162,6 @@ mm_cbm_part_new_from_binary_pdu (const guint8 *pdu,
mm_obj_dbg (log_object, " immediate cell cbm scope");
break;
default:
- mm_cbm_part_free (cbm_part);
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
@@ -137,14 +182,40 @@ mm_cbm_part_new_from_binary_pdu (const guint8 *pdu,
/* Order matches 3GPP TS 23.038 Chapter 5 */
if (group == CBS_DATA_CODING_LANG_GSM7) {
cbm_part->encoding = MM_SMS_ENCODING_GSM7;
+ cbm_part->language = mm_cbm_part_coding_group_to_language (pdu[offset]);
} else if (pdu[offset] == CBS_DATA_CODING_GSM7) {
- PDU_SIZE_CHECK (offset + 4, "cannot skip lang");
- offset += 3;
+ has_lang = TRUE;
cbm_part->encoding = MM_SMS_ENCODING_GSM7;
} else if (pdu[offset] == CBS_DATA_CODING_UCS2) {
- PDU_SIZE_CHECK (offset + 3, "cannot skip lang");
- offset += 2;
+ has_7bit_lang = TRUE;
cbm_part->encoding = MM_SMS_ENCODING_UCS2;
+ } else if (group == CBS_DATA_CODING_OTHER) {
+ switch (group) {
+ case CBS_DATA_CODING_OTHER_CZECH:
+ cbm_part->encoding = MM_SMS_ENCODING_GSM7;
+ cbm_part->language = g_strdup ("cz");
+ break;
+ case CBS_DATA_CODING_OTHER_HEBREW:
+ cbm_part->encoding = MM_SMS_ENCODING_UCS2;
+ cbm_part->language = g_strdup ("he");
+ break;
+ case CBS_DATA_CODING_OTHER_ARABIC:
+ cbm_part->encoding = MM_SMS_ENCODING_UCS2;
+ cbm_part->language = g_strdup ("ar");
+ break;
+ case CBS_DATA_CODING_OTHER_RUSSIAN:
+ cbm_part->encoding = MM_SMS_ENCODING_UCS2;
+ cbm_part->language = g_strdup ("ru");
+ break;
+ case CBS_DATA_CODING_OTHER_ICELANDIC:
+ cbm_part->encoding = MM_SMS_ENCODING_GSM7;
+ cbm_part->language = g_strdup ("is");
+ break;
+ default:
+ cbm_part->encoding = MM_SMS_ENCODING_GSM7;
+ }
+ } else if (group == CBS_DATA_CODING_UNSPECIFIED) {
+ cbm_part->encoding = MM_SMS_ENCODING_GSM7;
} else if ((group == CBS_DATA_CODING_GENERAL_CLASS) ||
(group == CBS_DATA_CODING_GENERAL_NO_CLASS) ||
(group == CBS_DATA_CODING_UDH)) {
@@ -155,7 +226,6 @@ mm_cbm_part_new_from_binary_pdu (const guint8 *pdu,
else if (charset == CBS_DATA_CODING_GENERAL_UCS2)
cbm_part->encoding = MM_SMS_ENCODING_UCS2;
} else {
- mm_cbm_part_free (cbm_part);
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
@@ -170,6 +240,35 @@ mm_cbm_part_new_from_binary_pdu (const guint8 *pdu,
cbm_part->part_num = (pdu[offset] & 0xF0) >> 4;
offset++;
+ if (has_lang) {
+ PDU_SIZE_CHECK (offset + 4, "cannot read lang");
+ if (pdu[offset+2] != '\r') {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse lang");
+ return NULL;
+ }
+ cbm_part->language = g_strdup_printf ("%c%c", pdu[offset], pdu[offset+1]);
+ offset += 3;
+ } else if (has_7bit_lang) {
+ PDU_SIZE_CHECK (offset + 3, "cannot read 7bit lang");
+ cbm_part->language = mm_sms_decode_text (&pdu[offset],
+ 2,
+ MM_SMS_ENCODING_GSM7,
+ 0,
+ log_object,
+ NULL);
+ if (!cbm_part->language) {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse lang");
+ return NULL;
+ }
+ offset += 2;
+ }
+
switch (cbm_part->encoding) {
case MM_SMS_ENCODING_GSM7:
len = ((pdu_len - offset) * 8) / 7;
@@ -190,12 +289,11 @@ mm_cbm_part_new_from_binary_pdu (const guint8 *pdu,
log_object,
error);
if (!text) {
- mm_cbm_part_free (cbm_part);
return NULL;
}
cbm_part->text = g_steal_pointer (&text);
- return cbm_part;
+ return g_steal_pointer (&cbm_part);
}
MMCbmPart *
@@ -207,6 +305,7 @@ mm_cbm_part_new (void)
void
mm_cbm_part_free (MMCbmPart *part)
{
+ g_clear_pointer (&part->language, g_free);
g_clear_pointer (&part->text, g_free);
g_slice_free (MMCbmPart, part);
}
@@ -223,3 +322,4 @@ PART_GET_FUNC (guint, num_parts)
PART_GET_FUNC (const char *, text)
PART_GET_FUNC (guint16, channel)
PART_GET_FUNC (guint16, serial)
+PART_GET_FUNC (const char *, language)
diff --git a/src/mm-cbm-part.h b/src/mm-cbm-part.h
index f942ab0c..3e0dabf9 100644
--- a/src/mm-cbm-part.h
+++ b/src/mm-cbm-part.h
@@ -59,11 +59,14 @@ MMCbmPart *mm_cbm_part_new_from_binary_pdu (const guint8 *pdu,
MMCbmPart *mm_cbm_part_new (void);
void mm_cbm_part_free (MMCbmPart *part);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCbmPart, mm_cbm_part_free)
+
guint mm_cbm_part_get_part_num (MMCbmPart *part);
guint mm_cbm_part_get_num_parts (MMCbmPart *part);
const char *mm_cbm_part_get_text (MMCbmPart *part);
guint16 mm_cbm_part_get_serial (MMCbmPart *part);
guint16 mm_cbm_part_get_channel (MMCbmPart *part);
+const char *mm_cbm_part_get_language (MMCbmPart *part);
#endif
diff --git a/src/tests/test-cbm-part.c b/src/tests/test-cbm-part.c
index 7981018b..89fb4768 100644
--- a/src/tests/test-cbm-part.c
+++ b/src/tests/test-cbm-part.c
@@ -68,6 +68,8 @@ test_cbm_ca (void)
g_assert_cmpstr (mm_cbm_part_get_text (part), ==,
"This is a test of the Ontario Alert Ready System. There is no danger to your health or safety" );
+
+ g_assert_null (mm_cbm_part_get_language (part));
}
static void
@@ -109,6 +111,8 @@ test_cbm_ucs2 (void)
g_assert_cmpstr (mm_cbm_part_get_text (part), ==,
" Протягом дня є висока імовірність ракетнᘁ");
+
+ g_assert_null (mm_cbm_part_get_language (part));
}
@@ -153,6 +157,8 @@ test_cbm_eu (void)
g_assert_cmpstr (mm_cbm_part_get_text (part), ==,
"TEST ALERT, NATIONWIDE ALERT DAY 2022 Thu 2022/12/08 - 10:59 am - Test alert - for Deutschlan");
+
+ g_assert_cmpstr (mm_cbm_part_get_language (part), ==, "en");
}
static void
@@ -194,6 +200,7 @@ test_cbm_nl_2023 (void)
g_assert_cmpstr (mm_cbm_part_get_text (part), ==,
"NL-Alert 04-12-2023 12:00: TESTBERICHT. De overheid waarschuwt je tijdens noodsituaties via N");
mm_cbm_part_free (part);
+ g_assert_null (mm_cbm_part_get_language (part));
parse_cbm ("\r\n+CBM: 88\r\n46A011130523CC56905D96D35D206519C42E97E7741039EC06DDC37490BA0C6ABFCB7410F95D7683CA6ED03D1C9683D46550BB5C9683D26EF35BDE0ED3D365D03AEC06D9D36E72D9ED02A9542A10B538A5829AC5E9347804\r\n", &part);
serial = mm_cbm_part_get_serial (part);
@@ -208,6 +215,7 @@ test_cbm_nl_2023 (void)
g_assert_cmpstr (mm_cbm_part_get_text (part), ==,
"L-Alert. Je leest dan wat je moet doen en waar je meer informatie kan vinden. *** TEST MESSAG");
mm_cbm_part_free (part);
+ g_assert_null (mm_cbm_part_get_language (part));
parse_cbm ("\r\n+CBM: 65\r\n46A0111305334590B34C4797E5ECB09B3C071DDFF6B2DCDD2EBBE920685DCC4E8F41D7B0DC9D769F41D3FC9C5E6EBB40CE37283CA6A7DF6E90BC1CAFA7E565B2AB\r\n", &part);
serial = mm_cbm_part_get_serial (part);
@@ -222,8 +230,55 @@ test_cbm_nl_2023 (void)
g_assert_cmpstr (mm_cbm_part_get_text (part), ==,
"E Netherlands Government Public Warning System. No action required." );
mm_cbm_part_free (part);
+ g_assert_null (mm_cbm_part_get_language (part));
}
+
+static void
+test_cbm_gsm7bit_with_lang (void)
+{
+ g_autoptr(GError) err = NULL;
+ MMCbmPart *part;
+
+ static const guint8 pdu[] = {
+ 0x40, 0xC0, 0x11, 0x1F, 0x10 /* GSM 7Bit with language */, 0x13,
+ 0x64 /* d */, 0x65 /* e */, 0x0D /* \r */, 0x0A, 0x0A, 0x32, 0x8B, 0x52,
+ 0x2A, 0x0B, 0xE4, 0x0C, 0x52, 0x93, 0x4F, 0xE7,
+ };
+
+ part = mm_cbm_part_new_from_binary_pdu (pdu, G_N_ELEMENTS (pdu), NULL, &err);
+ g_assert_no_error (err);
+ g_assert_nonnull (part);
+
+ g_assert_cmpuint (mm_cbm_part_get_channel (part), ==, 4383);
+ g_assert_cmpuint (mm_cbm_part_get_num_parts (part), ==, 3);
+ g_assert_cmpuint (mm_cbm_part_get_part_num (part), ==, 1);
+ g_assert_cmpstr (mm_cbm_part_get_language (part), ==, "de");
+}
+
+
+static void
+test_cbm_ucs2_with_7bit_lang (void)
+{
+ g_autoptr(GError) err = NULL;
+ MMCbmPart *part;
+
+ static const guint8 pdu [] = {
+ 0x63, 0x40, 0x00, 0x32, 0x11 /* UCS2 with 7Bit language */, 0x14,
+ 0xF2 /* ru …*/ , 0x7A /* … in GSM 7bit */, 0x04, 0x1f, 0x04, 0x40, 0x04, 0x3e,
+ };
+
+ part = mm_cbm_part_new_from_binary_pdu (pdu, G_N_ELEMENTS (pdu), NULL, &err);
+ g_assert_no_error (err);
+ g_assert_nonnull (part);
+
+ g_assert_cmpuint (mm_cbm_part_get_channel (part), ==, 50);
+ g_assert_cmpuint (mm_cbm_part_get_num_parts (part), ==, 4);
+ g_assert_cmpuint (mm_cbm_part_get_part_num (part), ==, 1);
+ g_assert_cmpstr (mm_cbm_part_get_language (part), ==, "ru");
+}
+
+
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
@@ -242,6 +297,10 @@ int main (int argc, char **argv)
/* 2023 NL alert: */
/* https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/253#note_2192474 */
g_test_add_func ("/MM/CBM/PDU-Parser/CBM-NL", test_cbm_nl_2023);
+ /* GSM7 bit encoding with language in the first 3 bytes of the message */
+ g_test_add_func ("/MM/CBM/PDU-Parser/has-lang", test_cbm_gsm7bit_with_lang);
+ /* UCS2 encoding with 7bit language in first 2 bytes of the message */
+ g_test_add_func ("/MM/CBM/PDU-Parser/has-7bit-lang", test_cbm_ucs2_with_7bit_lang);
return g_test_run ();
}