diff options
-rw-r--r-- | src/mm-charsets.c | 92 | ||||
-rw-r--r-- | src/mm-charsets.h | 5 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 43 |
3 files changed, 137 insertions, 3 deletions
diff --git a/src/mm-charsets.c b/src/mm-charsets.c index f12ad30d..c2fa2298 100644 --- a/src/mm-charsets.c +++ b/src/mm-charsets.c @@ -87,6 +87,22 @@ charset_iconv_to (MMModemCharset charset) return NULL; } +static const char * +charset_iconv_from (MMModemCharset charset) +{ + CharsetEntry *iter = &charset_map[0]; + + g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL); + + while (iter->gsm_name) { + if (iter->charset == charset) + return iter->iconv_from_name; + iter++; + } + g_warn_if_reached (); + return NULL; +} + gboolean mm_modem_charset_byte_array_append (GByteArray *array, const char *string, @@ -131,3 +147,79 @@ mm_modem_charset_byte_array_append (GByteArray *array, return TRUE; } +/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */ + +static int hex2num (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +static int hex2byte (const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) + return -1; + b = hex2num(*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + +static char * +hexstr2bin (const char *hex, gsize *out_len) +{ + size_t len = strlen (hex); + size_t i; + int a; + const char * ipos = hex; + char * buf = NULL; + char * opos; + + /* Length must be a multiple of 2 */ + g_return_val_if_fail ((len % 2) == 0, NULL); + + opos = buf = g_malloc0 ((len / 2) + 1); + for (i = 0; i < len; i += 2) { + a = hex2byte (ipos); + if (a < 0) { + g_free (buf); + return NULL; + } + *opos++ = a; + ipos += 2; + } + *out_len = len / 2; + return buf; +} + +/* End from hostap */ + +char * +mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset) +{ + char *unconverted; + const char *iconv_from; + gsize unconverted_len = 0; + + g_return_val_if_fail (src != NULL, NULL); + g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL); + + iconv_from = charset_iconv_from (charset); + g_return_val_if_fail (iconv_from != NULL, FALSE); + + unconverted = hexstr2bin (src, &unconverted_len); + g_return_val_if_fail (unconverted != NULL, NULL); + + if (charset == MM_MODEM_CHARSET_UTF8 || charset == MM_MODEM_CHARSET_IRA) + return unconverted; + + return g_convert (unconverted, unconverted_len, "UTF-8//TRANSLIT", iconv_from, NULL, NULL, NULL); +} + diff --git a/src/mm-charsets.h b/src/mm-charsets.h index 41d568dd..5fa34065 100644 --- a/src/mm-charsets.h +++ b/src/mm-charsets.h @@ -43,5 +43,10 @@ gboolean mm_modem_charset_byte_array_append (GByteArray *array, gboolean quoted, MMModemCharset charset); +/* Take a string in hex representation ("00430052" or "A4BE11" for example) + * and convert it from the given character set to UTF-8. + */ +char *mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset); + #endif /* MM_CHARSETS_H */ diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index f13a7fff..f888d9d3 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -1408,8 +1408,37 @@ reg_info_updated (MMGenericGsm *self, } } +static void +convert_operator_from_ucs2 (char **operator) +{ + const char *p; + char *converted; + size_t len; + + g_return_if_fail (operator != NULL); + g_return_if_fail (*operator != NULL); + + p = *operator; + len = strlen (p); + + /* Len needs to be a multiple of 4 for UCS2 */ + if ((len < 4) && ((len % 4) != 0)) + return; + + while (*p) { + if (!isxdigit (*p++)) + return; + } + + converted = mm_modem_charset_hex_to_utf8 (*operator, MM_MODEM_CHARSET_UCS2); + if (converted) { + g_free (*operator); + *operator = converted; + } +} + static char * -parse_operator (const char *reply) +parse_operator (const char *reply, MMModemCharset cur_charset) { char *operator = NULL; @@ -1431,6 +1460,13 @@ parse_operator (const char *reply) g_regex_unref (r); } + /* Some modems (Option & HSO) return the operator name as a hexadecimal + * string of the bytes of the operator name as encoded by the current + * character set. + */ + if (operator && (cur_charset == MM_MODEM_CHARSET_UCS2)) + convert_operator_from_ucs2 (&operator); + return operator; } @@ -1444,7 +1480,7 @@ read_operator_code_done (MMAtSerialPort *port, char *oper; if (!error) { - oper = parse_operator (response->str); + oper = parse_operator (response->str, MM_MODEM_CHARSET_UNKNOWN); if (oper) reg_info_updated (self, FALSE, 0, TRUE, oper, FALSE, NULL); } @@ -1457,10 +1493,11 @@ read_operator_name_done (MMAtSerialPort *port, gpointer user_data) { MMGenericGsm *self = MM_GENERIC_GSM (user_data); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); char *oper; if (!error) { - oper = parse_operator (response->str); + oper = parse_operator (response->str, priv->cur_charset); if (oper) reg_info_updated (self, FALSE, 0, FALSE, NULL, TRUE, oper); } |