diff options
author | Dan Williams <dcbw@redhat.com> | 2012-02-02 21:12:44 -0600 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2012-02-02 21:12:44 -0600 |
commit | 4706ea488eb227d048a9a4021a875d87e8e7131b (patch) | |
tree | e9ee092de98bc1fb4c577575eeff4d97bcf0ed39 /src | |
parent | 76c9b29415a89a1804fcc17be737a2b09fe1109f (diff) |
sms: handle text mode SMS listing
Undoubtedly we'll need to adjust the regex but this works for now.
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-generic-gsm.c | 243 | ||||
-rw-r--r-- | src/mm-sms-utils.c | 152 | ||||
-rw-r--r-- | src/mm-sms-utils.h | 8 |
3 files changed, 288 insertions, 115 deletions
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 3a1c62eb..098ffa2e 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -1489,9 +1489,8 @@ sms_cache_lookup_full (MMModem *modem, mpm = g_hash_table_lookup (priv->sms_parts, GUINT_TO_POINTER (refnum)); if (mpm == NULL) { - *error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Internal error - no multipart structure for multipart SMS"); + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Internal error - no multipart structure for multipart SMS"); return NULL; } @@ -1508,19 +1507,17 @@ sms_cache_lookup_full (MMModem *modem, part = g_hash_table_lookup (priv->sms_contents, GUINT_TO_POINTER (mpm->parts[i])); if (part == NULL) { - *error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Internal error - part %d (index %d) is missing", - i, mpm->parts[i]); + g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Internal error - part %d (index %d) is missing", + i, mpm->parts[i]); g_free (textparts); return NULL; } text = g_hash_table_lookup (part, "text"); if (text == NULL) { - *error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Internal error - part %d (index %d) has no text element", - i, mpm->parts[i]); + g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Internal error - part %d (index %d) has no text element", + i, mpm->parts[i]); g_free (textparts); return NULL; } @@ -4882,8 +4879,7 @@ sms_get_done (MMAtSerialPort *port, goto out; } - g_hash_table_insert (properties, "index", - simple_uint_value (idx)); + g_hash_table_insert (properties, "index", simple_uint_value (idx)); sms_cache_insert (info->modem, properties, idx); look_for_complete = GPOINTER_TO_UINT (mm_callback_info_get_data(info, @@ -5125,6 +5121,168 @@ sms_delete (MMModemGsmSms *modem, user_data); } +static gboolean +pdu_parse_cmgl (MMGenericGsm *self, const char *response, GError **error) +{ + int rv, status, tpdu_len, offset; + GHashTable *properties; + + while (*response) { + int idx; + char pdu[SMS_MAX_PDU_LEN + 1]; + + rv = sscanf (response, "+CMGL: %d,%d,,%d %344s %n", + &idx, &status, &tpdu_len, pdu, &offset); + if (4 != rv) { + g_set_error (error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse CMGL response: expected 4 results got %d", rv); + mm_err("Couldn't parse response to SMS LIST (%d)", rv); + return FALSE; + } + response += offset; + + properties = sms_parse_pdu (pdu, NULL); + if (properties) { + g_hash_table_insert (properties, "index", simple_uint_value (idx)); + sms_cache_insert (MM_MODEM (self), properties, idx); + /* The cache holds a reference, so we don't need it anymore */ + g_hash_table_unref (properties); + } + } + + return TRUE; +} + +static gboolean +get_match_uint (GMatchInfo *m, guint match_index, guint *out_val) +{ + char *s; + unsigned long num; + + g_return_val_if_fail (out_val != NULL, FALSE); + + s = g_match_info_fetch (m, match_index); + g_return_val_if_fail (s != NULL, FALSE); + + errno = 0; + num = strtoul (s, NULL, 10); + g_free (s); + + if (num <= 1000 && errno == 0) { + *out_val = (guint) num; + return TRUE; + } + return FALSE; +} + +static char * +get_match_string_unquoted (GMatchInfo *m, guint match_index) +{ + char *s, *p, *q, *ret = NULL; + + q = s = g_match_info_fetch (m, match_index); + g_return_val_if_fail (s != NULL, FALSE); + + /* remove quotes */ + if (*q == '"') + q++; + p = strchr (q, '"'); + if (p) + *p = '\0'; + if (*q) + ret = g_strdup (q); + g_free (s); + return ret; +} + +static gboolean +text_parse_cmgl (MMGenericGsm *self, const char *response, GError **error) +{ + GRegex *r; + GMatchInfo *match_info = NULL; + + /* +CMGL: <index>,<stat>,<oa/da>,[alpha],<scts><CR><LF><data><CR><LF> */ + r = g_regex_new ("\\+CMGL:\\s*(\\d+)\\s*,\\s*([^,]*),\\s*([^,]*),\\s*([^,]*),\\s*([^\\r\\n]*)\\r\\n(.*)\\r\\n", 0, 0, NULL); + g_assert (r); + + if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL)) { + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Failed to parse CMGL response"); + mm_err("Couldn't parse response to SMS LIST"); + g_regex_unref (r); + return FALSE; + } + + while (g_match_info_matches (match_info)) { + GHashTable *properties; + guint matches, idx; + char *number = NULL, *timestamp, *text, *ucs2_text; + gsize ucs2_len = 0; + GByteArray *data; + + matches = g_match_info_get_match_count (match_info); + if (matches != 7) { + mm_dbg ("Failed to match entire CMGL response (count %d)", matches); + goto next; + } + + if (!get_match_uint (match_info, 1, &idx)) { + mm_dbg ("Failed to convert message index"); + goto next; + } + + /* <stat is ignored for now> */ + + number = get_match_string_unquoted (match_info, 3); + if (!number) { + mm_dbg ("Failed to get message sender number"); + goto next; + } + + timestamp = get_match_string_unquoted (match_info, 5); + + text = g_match_info_fetch (match_info, 6); + /* FIXME: Text is going to be in the character set we've set with +CSCS */ + + /* The raw SMS data can only be GSM, UCS2, or unknown (8-bit), so we + * need to convert to UCS2 here. + */ + ucs2_text = g_convert (text, -1, "UCS-2BE//TRANSLIT", "UTF-8", NULL, &ucs2_len, NULL); + g_assert (ucs2_text); + data = g_byte_array_sized_new (ucs2_len); + g_byte_array_append (data, (const guint8 *) ucs2_text, ucs2_len); + g_free (ucs2_text); + + properties = sms_properties_hash_new (NULL, + number, + timestamp, + text, + data, + 2, /* DCS = UCS2 */ + 0); /* class */ + g_assert (properties); + + g_free (number); + g_free (timestamp); + g_free (text); + g_byte_array_free (data, TRUE); + + g_hash_table_insert (properties, "index", simple_uint_value (idx)); + sms_cache_insert (MM_MODEM (self), properties, idx); + /* The cache holds a reference, so we don't need it anymore */ + g_hash_table_unref (properties); + +next: + g_match_info_next (match_info, NULL); + } + g_match_info_free (match_info); + + g_regex_unref (r); + return TRUE; +} + static void free_list_results (gpointer data) { @@ -5141,12 +5299,12 @@ sms_list_done (MMAtSerialPort *port, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); - GPtrArray *results = NULL; - int rv, status, tpdu_len, offset; - char *rstr; + MMGenericGsm *self = MM_GENERIC_GSM (info->modem); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); GHashTableIter iter; GHashTable *properties = NULL; + GPtrArray *results = NULL; + gboolean success; /* If the modem has already been removed, return without * scheduling callback */ @@ -5159,46 +5317,27 @@ sms_list_done (MMAtSerialPort *port, return; } - results = g_ptr_array_new (); - rstr = response->str; - while (*rstr) { - GError *local; - int idx; - char pdu[SMS_MAX_PDU_LEN + 1]; + if (priv->sms_pdu_mode) + success = pdu_parse_cmgl (self, response->str, &info->error); + else + success = text_parse_cmgl (self, response->str, &info->error); - rv = sscanf (rstr, "+CMGL: %d,%d,,%d %344s %n", - &idx, &status, &tpdu_len, pdu, &offset); - if (4 != rv) { - mm_err("Couldn't parse response to SMS LIST (%d)", rv); - break; - } - rstr += offset; + if (success) { + results = g_ptr_array_new (); - properties = sms_parse_pdu (pdu, &local); - if (properties) { - g_hash_table_insert (properties, "index", - simple_uint_value (idx)); - sms_cache_insert (info->modem, properties, idx); - /* The cache holds a reference, so we don't need it anymore */ - g_hash_table_unref (properties); - } else { - /* Ignore the error */ - g_clear_error(&local); + /* Add all the complete messages to the results */ + g_hash_table_iter_init (&iter, priv->sms_contents); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &properties)) { + g_hash_table_ref (properties); + g_clear_error (&info->error); + properties = sms_cache_lookup_full (info->modem, properties, NULL); + if (properties) + g_ptr_array_add (results, properties); } - } - /* Add all the complete messages to the results */ - properties = NULL; - g_hash_table_iter_init (&iter, priv->sms_contents); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &properties)) { - g_hash_table_ref (properties); - g_clear_error (&info->error); - properties = sms_cache_lookup_full (info->modem, properties, &info->error); - if (properties) - g_ptr_array_add (results, properties); + if (results) + mm_callback_info_set_data (info, "list-sms", results, free_list_results); } - if (results) - mm_callback_info_set_data (info, "list-sms", results, free_list_results); mm_callback_info_schedule (info); } diff --git a/src/mm-sms-utils.c b/src/mm-sms-utils.c index ddb35994..4e52ec49 100644 --- a/src/mm-sms-utils.c +++ b/src/mm-sms-utils.c @@ -346,6 +346,39 @@ byte_array_value (const GByteArray *array) } GHashTable * +sms_properties_hash_new (const char *smsc, + const char *number, + const char *timestamp, + const char *text, + const GByteArray *data, + guint data_coding_scheme, + guint *class) +{ + GHashTable *properties; + + g_return_val_if_fail (number != NULL, NULL); + g_return_val_if_fail (text != NULL, NULL); + g_return_val_if_fail (data != NULL, NULL); + + properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, simple_free_gvalue); + g_hash_table_insert (properties, "number", simple_string_value (number)); + g_hash_table_insert (properties, "data", byte_array_value (data)); + g_hash_table_insert (properties, "data-coding-scheme", simple_uint_value (data_coding_scheme)); + g_hash_table_insert (properties, "text", simple_string_value (text)); + + if (smsc) + g_hash_table_insert (properties, "smsc", simple_string_value (smsc)); + + if (timestamp) + g_hash_table_insert (properties, "timestamp", simple_string_value (timestamp)); + + if (class) + g_hash_table_insert (properties, "class", simple_uint_value (*class)); + + return properties; +} + +GHashTable * sms_parse_pdu (const char *hexpdu, GError **error) { GHashTable *properties; @@ -358,13 +391,14 @@ sms_parse_pdu (const char *hexpdu, GError **error) char *smsc_addr, *sender_addr, *sc_timestamp, *msg_text; SmsEncoding user_data_encoding; GByteArray *pdu_data; + guint concat_ref = 0, concat_max = 0, concat_seq = 0, msg_class = 0; + gboolean multipart = FALSE, class_valid = FALSE; /* Convert PDU from hex to binary */ pdu = (guint8 *) utils_hexstr2bin (hexpdu, &pdu_len); if (!pdu) { - *error = g_error_new_literal (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Couldn't parse PDU of SMS GET response from hex"); + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Couldn't parse PDU of SMS GET response from hex"); return NULL; } @@ -372,10 +406,10 @@ sms_parse_pdu (const char *hexpdu, GError **error) smsc_addr_num_octets = pdu[0]; variable_length_items = smsc_addr_num_octets; if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) { - *error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "PDU too short (1): %zd vs %d", pdu_len, - variable_length_items + SMS_MIN_PDU_LEN); + g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "PDU too short (1): %zd vs %d", + pdu_len, + variable_length_items + SMS_MIN_PDU_LEN); g_free (pdu); return NULL; } @@ -390,10 +424,10 @@ sms_parse_pdu (const char *hexpdu, GError **error) sender_addr_num_octets = (sender_addr_num_digits + 1) >> 1; variable_length_items += sender_addr_num_octets; if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) { - *error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "PDU too short (2): %zd vs %d", pdu_len, - variable_length_items + SMS_MIN_PDU_LEN); + g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "PDU too short (2): %zd vs %d", + pdu_len, + variable_length_items + SMS_MIN_PDU_LEN); g_free (pdu); return NULL; } @@ -410,43 +444,23 @@ sms_parse_pdu (const char *hexpdu, GError **error) else variable_length_items += user_data_len; if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) { - *error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "PDU too short (3): %zd vs %d", pdu_len, - variable_length_items + SMS_MIN_PDU_LEN); + g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "PDU too short (3): %zd vs %d", + pdu_len, + variable_length_items + SMS_MIN_PDU_LEN); g_free (pdu); return NULL; } /* Only handle SMS-DELIVER */ if ((pdu[msg_start_offset] & SMS_TP_MTI_MASK) != SMS_TP_MTI_SMS_DELIVER) { - *error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Unhandled message type: 0x%02x", - pdu[msg_start_offset]); + g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Unhandled message type: 0x%02x", + pdu[msg_start_offset]); g_free (pdu); return NULL; } - properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, - simple_free_gvalue); - - smsc_addr = sms_decode_address (&pdu[1], 2 * (pdu[0] - 1)); - g_hash_table_insert (properties, "smsc", - simple_string_value (smsc_addr)); - g_free (smsc_addr); - - sender_addr = sms_decode_address (&pdu[msg_start_offset + 2], - pdu[msg_start_offset + 1]); - g_hash_table_insert (properties, "number", - simple_string_value (sender_addr)); - g_free (sender_addr); - - sc_timestamp = sms_decode_timestamp (&pdu[tp_dcs_offset + 1]); - g_hash_table_insert (properties, "timestamp", - simple_string_value (sc_timestamp)); - g_free (sc_timestamp); - bit_offset = 0; if (pdu[msg_start_offset] & SMS_TP_UDHI) { int udhl, end, offset; @@ -470,12 +484,10 @@ sms_parse_pdu (const char *hexpdu, GError **error) pdu[offset + 2] > pdu[offset + 1]) break; - g_hash_table_insert (properties, "concat-reference", - simple_uint_value (pdu[offset])); - g_hash_table_insert (properties, "concat-max", - simple_uint_value (pdu[offset + 1])); - g_hash_table_insert (properties, "concat-sequence", - simple_uint_value (pdu[offset + 2])); + concat_ref = pdu[offset]; + concat_max = pdu[offset + 1]; + concat_seq = pdu[offset + 2]; + multipart = TRUE; break; case 0x08: /* Concatenated short message, 16-bit reference */ @@ -483,14 +495,10 @@ sms_parse_pdu (const char *hexpdu, GError **error) pdu[offset + 3] > pdu[offset + 2]) break; - g_hash_table_insert (properties, "concat-reference", - simple_uint_value ( - (pdu[offset] << 8) - | pdu[offset + 1])); - g_hash_table_insert (properties, "concat-max", - simple_uint_value (pdu[offset + 2])); - g_hash_table_insert (properties, "concat-sequence", - simple_uint_value (pdu[offset + 3])); + concat_ref = (pdu[offset] << 8) | pdu[offset + 1]; + concat_max = pdu[offset + 2]; + concat_seq = pdu[offset + 3]; + multipart = TRUE; break; } @@ -525,21 +533,39 @@ sms_parse_pdu (const char *hexpdu, GError **error) user_data_encoding, bit_offset); g_warn_if_fail (msg_text != NULL); } - g_hash_table_insert (properties, "text", simple_string_value (msg_text)); - g_free (msg_text); - /* Add the raw PDU data */ + /* Raw PDU data */ pdu_data = g_byte_array_sized_new (user_data_len); g_byte_array_append (pdu_data, &pdu[user_data_offset], user_data_len); - g_hash_table_insert (properties, "data", byte_array_value (pdu_data)); - g_byte_array_free (pdu_data, TRUE); - g_hash_table_insert (properties, "data-coding-scheme", - simple_uint_value (pdu[tp_dcs_offset] & 0xFF)); - if (pdu[tp_dcs_offset] & SMS_DCS_CLASS_VALID) - g_hash_table_insert (properties, "class", - simple_uint_value (pdu[tp_dcs_offset] & - SMS_DCS_CLASS_MASK)); + if (pdu[tp_dcs_offset] & SMS_DCS_CLASS_VALID) { + msg_class = pdu[tp_dcs_offset] & SMS_DCS_CLASS_MASK; + class_valid = TRUE; + } + + smsc_addr = sms_decode_address (&pdu[1], 2 * (pdu[0] - 1)); + sender_addr = sms_decode_address (&pdu[msg_start_offset + 2], pdu[msg_start_offset + 1]); + sc_timestamp = sms_decode_timestamp (&pdu[tp_dcs_offset + 1]); + + properties = sms_properties_hash_new (smsc_addr, + sender_addr, + sc_timestamp, + msg_text, + pdu_data, + pdu[tp_dcs_offset] & 0xFF, + class_valid ? &msg_class : NULL); + g_assert (properties); + if (multipart) { + g_hash_table_insert (properties, "concat-reference", simple_uint_value (concat_ref)); + g_hash_table_insert (properties, "concat-max", simple_uint_value (concat_max)); + g_hash_table_insert (properties, "concat-sequence", simple_uint_value (concat_seq)); + } + + g_free (smsc_addr); + g_free (sender_addr); + g_free (sc_timestamp); + g_free (msg_text); + g_byte_array_free (pdu_data, TRUE); g_free (pdu); return properties; diff --git a/src/mm-sms-utils.h b/src/mm-sms-utils.h index 5c1c026a..46f475b1 100644 --- a/src/mm-sms-utils.h +++ b/src/mm-sms-utils.h @@ -31,6 +31,14 @@ guint8 *sms_create_submit_pdu (const char *number, guint *out_msgstart, GError **error); +GHashTable *sms_properties_hash_new (const char *smsc, + const char *number, + const char *timestamp, + const char *text, + const GByteArray *data, + guint data_coding_scheme, + guint *class); + /* For testcases only */ guint sms_encode_address (const char *address, guint8 *buf, |