sim: add common helpers to parse operator name and mnc length
This commit is contained in:
committed by
Aleksander Morgado
parent
257839c66e
commit
a030eaef76
+2
-29
@@ -2050,7 +2050,6 @@ parse_mnc_length (const gchar *response,
|
||||
(sw1 == 0x92) ||
|
||||
(sw1 == 0x9f)) {
|
||||
gsize buflen = 0;
|
||||
guint32 mnc_len;
|
||||
g_autofree guint8 *bin = NULL;
|
||||
|
||||
/* Convert hex string to binary */
|
||||
@@ -2059,20 +2058,8 @@ parse_mnc_length (const gchar *response,
|
||||
g_prefix_error (error, "SIM returned malformed response '%s': ", hex);
|
||||
return 0;
|
||||
}
|
||||
if (buflen < 4) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"SIM returned malformed response '%s': too short", hex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* MNC length is byte 4 of this SIM file */
|
||||
mnc_len = bin[3];
|
||||
if (mnc_len == 2 || mnc_len == 3)
|
||||
return mnc_len;
|
||||
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_len);
|
||||
return 0;
|
||||
return mm_sim_validate_mnc_length (bin, buflen, error);
|
||||
}
|
||||
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
@@ -2157,7 +2144,6 @@ parse_spn (const gchar *response,
|
||||
(sw1 == 0x91) ||
|
||||
(sw1 == 0x92) ||
|
||||
(sw1 == 0x9f)) {
|
||||
g_autoptr(GByteArray) bin_array = NULL;
|
||||
g_autofree guint8 *bin = NULL;
|
||||
gsize binlen = 0;
|
||||
|
||||
@@ -2168,20 +2154,7 @@ parse_spn (const gchar *response,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove the FF filler at the end */
|
||||
while (binlen > 1 && bin[binlen - 1] == 0xff)
|
||||
binlen--;
|
||||
if (binlen <= 1) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"SIM returned empty response '%s'", hex);
|
||||
return NULL;
|
||||
}
|
||||
/* Setup as bytearray.
|
||||
* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
|
||||
bin_array = g_byte_array_sized_new (binlen - 1);
|
||||
g_byte_array_append (bin_array, bin + 1, binlen - 1);
|
||||
|
||||
return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error);
|
||||
return mm_sim_convert_spn_to_utf8 (bin, binlen, error);
|
||||
}
|
||||
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
|
||||
@@ -5329,6 +5329,56 @@ mm_sim_parse_cpol_test_response (const gchar *response,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gchar *
|
||||
mm_sim_convert_spn_to_utf8 (const guint8 *bin,
|
||||
gsize binlen,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GByteArray) bin_array = NULL;
|
||||
|
||||
/* Remove the FF filler at the end */
|
||||
while (binlen > 1 && bin[binlen - 1] == 0xff)
|
||||
binlen--;
|
||||
if (binlen <= 1) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"SIM returned empty response");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Setup as bytearray.
|
||||
* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
|
||||
bin_array = g_byte_array_sized_new (binlen - 1);
|
||||
g_byte_array_append (bin_array, bin + 1, binlen - 1);
|
||||
|
||||
return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error);
|
||||
}
|
||||
|
||||
guint
|
||||
mm_sim_validate_mnc_length (const guint8 *bin,
|
||||
gsize binlen,
|
||||
GError **error)
|
||||
{
|
||||
guint mnc_len;
|
||||
|
||||
if (binlen < 4) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"SIM returned too short response of length %lu (should be 4)",
|
||||
binlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* MNC length is byte 4 of this SIM file */
|
||||
mnc_len = bin[3];
|
||||
if (mnc_len != 2 && mnc_len != 3) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"SIM returned invalid MNC length %u (should be either 2 or 3)",
|
||||
mnc_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mnc_len;
|
||||
}
|
||||
|
||||
#define EID_BYTE_LENGTH 16
|
||||
|
||||
gchar *
|
||||
|
||||
@@ -571,6 +571,15 @@ gboolean mm_sim_parse_cpol_test_response (const gchar *response,
|
||||
guint *out_max_index,
|
||||
GError **error);
|
||||
|
||||
/* Parse operator name and mnc length */
|
||||
gchar *mm_sim_convert_spn_to_utf8 (const guint8 *bin,
|
||||
gsize len,
|
||||
GError **error);
|
||||
|
||||
guint mm_sim_validate_mnc_length (const guint8 *bin,
|
||||
gsize len,
|
||||
GError **error);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Useful when clamp-ing an unsigned integer with implicit low limit set to 0,
|
||||
|
||||
+11
-32
@@ -1067,20 +1067,15 @@ common_read_binary_operator_id_ready (MMSimMbim *self,
|
||||
GByteArray *value;
|
||||
|
||||
value = common_read_binary_finish (self, res, &error);
|
||||
if (!value) {
|
||||
mm_obj_dbg (self, "failed reading operator ID using MBIM: %s", error->message);
|
||||
} else if (value->len != 4) {
|
||||
mm_obj_dbg (self, "failed reading operator ID using MBIM: unexpected field size");
|
||||
} else {
|
||||
guint mnc_len = value->data[3];
|
||||
|
||||
if (mnc_len == 2 || mnc_len == 3) {
|
||||
if (value) {
|
||||
guint mnc_len = mm_sim_validate_mnc_length (value->data, value->len, &error);;
|
||||
if (mnc_len) {
|
||||
g_task_return_pointer (task, g_strndup (self->priv->imsi, 3 + mnc_len), g_free);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
mm_obj_dbg (self, "failed reading operator ID using MBIM: unexpected MNC length: %u", mnc_len);
|
||||
}
|
||||
mm_obj_dbg (self, "failed reading operator ID using MBIM: %s", error->message);
|
||||
|
||||
/* Fallback to parent implementation if possible */
|
||||
MM_BASE_SIM_CLASS (mm_sim_mbim_parent_class)->load_operator_identifier (MM_BASE_SIM (self),
|
||||
@@ -1145,31 +1140,15 @@ common_read_binary_operator_name_ready (MMSimMbim *self,
|
||||
GByteArray *value;
|
||||
|
||||
value = common_read_binary_finish (self, res, &error);
|
||||
if (!value) {
|
||||
mm_obj_dbg (self, "failed reading operator name using MBIM: %s", error->message);
|
||||
} else {
|
||||
gsize len = value->len;
|
||||
|
||||
while (len > 1 && value->data[len - 1] == 0xff)
|
||||
len--;
|
||||
if (len <= 1) {
|
||||
mm_obj_dbg (self, "failed reading operator name using MBIM: value is empty");
|
||||
} else {
|
||||
g_autoptr(GByteArray) array = NULL;
|
||||
gchar *name;
|
||||
|
||||
/* Remove the first metadata byte and convert remainder to UTF8 string */
|
||||
array = g_byte_array_sized_new (len - 1);
|
||||
g_byte_array_append (array, value->data + 1, len - 1);
|
||||
name = mm_modem_charset_bytearray_to_utf8 (array, MM_MODEM_CHARSET_GSM, FALSE, &error);
|
||||
if (name) {
|
||||
g_task_return_pointer (task, name, g_free);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
mm_obj_dbg (self, "failed reading operator name using MBIM: %s", error->message);
|
||||
if (value) {
|
||||
gchar *name = mm_sim_convert_spn_to_utf8 (value->data, value->len, &error);
|
||||
if (name) {
|
||||
g_task_return_pointer (task, name, g_free);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mm_obj_dbg (self, "failed reading operator name using MBIM: %s", error->message);
|
||||
|
||||
/* Fallback to parent implementation if possible */
|
||||
MM_BASE_SIM_CLASS (mm_sim_mbim_parent_class)->load_operator_name (MM_BASE_SIM (self),
|
||||
|
||||
+5
-40
@@ -679,20 +679,11 @@ uim_read_efad_ready (QmiClientUim *client,
|
||||
return;
|
||||
}
|
||||
|
||||
if (read_result->len < 4) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"Unexpected response length reading EFad: %u", read_result->len);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* MNC length is byte 4 of this SIM file */
|
||||
mnc_length = read_result->data[3];
|
||||
if (mnc_length == 2 || mnc_length == 3) {
|
||||
g_task_return_pointer (task, g_strndup (self->priv->imsi, 3 + mnc_length), g_free);
|
||||
mnc_length = mm_sim_validate_mnc_length ((const guint8 *) read_result->data, read_result->len, &error);
|
||||
if (!mnc_length) {
|
||||
g_task_return_error (task, error);
|
||||
} else {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_length);
|
||||
g_task_return_pointer (task, g_strndup (self->priv->imsi, 3 + mnc_length), g_free);
|
||||
}
|
||||
g_object_unref (task);
|
||||
}
|
||||
@@ -738,31 +729,6 @@ load_operator_name_finish (MMBaseSim *self,
|
||||
return g_task_propagate_pointer (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
parse_spn (const guint8 *bin,
|
||||
gsize len,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GByteArray) bin_array = NULL;
|
||||
gsize binlen;
|
||||
|
||||
/* Remove the FF filler at the end */
|
||||
binlen = len;
|
||||
while (binlen > 1 && bin[binlen - 1] == 0xff)
|
||||
binlen--;
|
||||
if (binlen <= 1) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM returned empty spn");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Setup as bytearray.
|
||||
* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
|
||||
bin_array = g_byte_array_sized_new (binlen - 1);
|
||||
g_byte_array_append (bin_array, bin + 1, binlen - 1);
|
||||
|
||||
return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error);
|
||||
}
|
||||
|
||||
static void
|
||||
uim_read_efspn_ready (QmiClientUim *client,
|
||||
GAsyncResult *res,
|
||||
@@ -779,8 +745,7 @@ uim_read_efspn_ready (QmiClientUim *client,
|
||||
return;
|
||||
}
|
||||
|
||||
spn = parse_spn ((const guint8 *) read_result->data, read_result->len, &error);
|
||||
|
||||
spn = mm_sim_convert_spn_to_utf8 ((const guint8 *) read_result->data, read_result->len, &error);
|
||||
if (!spn) {
|
||||
g_task_return_error (task, error);
|
||||
} else {
|
||||
|
||||
@@ -4689,6 +4689,89 @@ test_mm_split_string_groups (void)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
const guint8 bin_array[10];
|
||||
gsize bin_len;
|
||||
const gchar *expected_name;
|
||||
gboolean expected_error;
|
||||
} TestSpnData;
|
||||
|
||||
static const TestSpnData test_spn_data[] = {
|
||||
{ { 0, 'T', 'e', 's', 't', 0xff, 0xff, 0xff, 0xff, 0xff }, 10, "Test", FALSE },
|
||||
{ { 'T', 'e', 's', 't', 0xff, 0xff }, 6, "est", FALSE },
|
||||
{ { 0, 0, '$', 'T', 'e', 's', 't' }, 7, "@¤Test", FALSE },
|
||||
{ { 0 }, 0, "", TRUE },
|
||||
{ { 0, 0xff }, 2, "", TRUE },
|
||||
{ { 0xff, 0xff }, 2, "", TRUE },
|
||||
};
|
||||
|
||||
static void
|
||||
test_spn_to_utf8 (void)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (test_spn_data); i++) {
|
||||
gchar *result = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
result = mm_sim_convert_spn_to_utf8 (test_spn_data[i].bin_array,
|
||||
test_spn_data[i].bin_len,
|
||||
&error);
|
||||
|
||||
if (test_spn_data[i].expected_error) {
|
||||
g_assert (!result);
|
||||
g_assert (error);
|
||||
g_error_free (error);
|
||||
} else {
|
||||
g_assert (result);
|
||||
g_assert_no_error (error);
|
||||
g_assert_cmpstr (result, ==, test_spn_data[i].expected_name);
|
||||
g_free (result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const guint8 bin_array[4];
|
||||
gsize bin_len;
|
||||
guint expected_length;
|
||||
gboolean expected_error;
|
||||
} TestMncData;
|
||||
|
||||
static const TestMncData test_mnc_data[] = {
|
||||
{ { 0 }, 0, 0, TRUE },
|
||||
{ { 0, 0, 0, 2 }, 3, 0, TRUE },
|
||||
{ { 0, 0, 0, 2 }, 5, 2, FALSE },
|
||||
{ { 0, 0, 0, 2 }, 4, 2, FALSE },
|
||||
{ { 0, 0, 0, 3 }, 4, 3, FALSE },
|
||||
{ { 0, 0, 0, 4 }, 4, 0, TRUE },
|
||||
};
|
||||
|
||||
static void
|
||||
test_mnc_length (void)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (test_mnc_data); i++) {
|
||||
guint result;
|
||||
GError *error = NULL;
|
||||
|
||||
result = mm_sim_validate_mnc_length (test_mnc_data[i].bin_array,
|
||||
test_mnc_data[i].bin_len,
|
||||
&error);
|
||||
|
||||
if (test_mnc_data[i].expected_error) {
|
||||
g_assert (error);
|
||||
g_error_free (error);
|
||||
} else {
|
||||
g_assert_no_error (error);
|
||||
}
|
||||
g_assert_cmpuint (result, ==, test_mnc_data[i].expected_length);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (GTestFixtureFunc) t, NULL)
|
||||
|
||||
int main (int argc, char **argv)
|
||||
@@ -4929,6 +5012,9 @@ int main (int argc, char **argv)
|
||||
|
||||
g_test_suite_add (suite, TESTCASE (test_mm_split_string_groups, NULL));
|
||||
|
||||
g_test_suite_add (suite, TESTCASE (test_spn_to_utf8, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_mnc_length, NULL));
|
||||
|
||||
result = g_test_run ();
|
||||
|
||||
reg_test_data_free (reg_data);
|
||||
|
||||
Reference in New Issue
Block a user