aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mm-generic-gsm.c470
-rw-r--r--src/mm-sms-utils.c98
2 files changed, 512 insertions, 56 deletions
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 82e51f67..f7584ad6 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -129,6 +129,15 @@ typedef struct {
/* SMS */
GHashTable *sms_present;
+ /* Map from SMS index numbers to parsed PDUs (themselves as hash tables) */
+ GHashTable *sms_contents;
+ /*
+ * Map from multipart SMS reference numbers to SMSMultiPartMessage
+ * structures.
+ */
+ GHashTable *sms_parts;
+
+ guint sms_fetch_pending;
} MMGenericGsmPrivate;
static void get_registration_status (MMAtSerialPort *port, MMCallbackInfo *info);
@@ -1324,6 +1333,249 @@ ciev_received (MMAtSerialPort *port,
/* FIXME: handle roaming and service indicators */
}
+typedef struct {
+ /*
+ * The key index number that refers to this multipart message -
+ * usually the index number of the first part received.
+ */
+ guint index;
+
+ /* Number of parts in the complete message */
+ guint numparts;
+
+ /* Number of parts missing from the message */
+ guint missing;
+
+ /* Array of (index numbers of) message parts, in order */
+ guint *parts;
+} SMSMultiPartMessage;
+
+static void
+sms_cache_insert (MMModem *modem, GHashTable *properties, guint idx)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ GHashTable *old_properties;
+ GValue *ref;
+
+ ref = g_hash_table_lookup (properties, "concat-reference");
+ if (ref != NULL) {
+ GValue *max, *seq;
+ guint refnum, maxnum, seqnum;
+ SMSMultiPartMessage *mpm;
+
+ max = g_hash_table_lookup (properties, "concat-max");
+ seq = g_hash_table_lookup (properties, "concat-sequence");
+ if (max == NULL || seq == NULL) {
+ /* Internal error - not all required data present */
+ return;
+ }
+
+ refnum = g_value_get_uint (ref);
+ maxnum = g_value_get_uint (max);
+ seqnum = g_value_get_uint (seq);
+
+ if (seqnum > maxnum) {
+ /* Error - SMS says "part N of M", but N > M */
+ return;
+ }
+
+ mpm = g_hash_table_lookup (priv->sms_parts, GUINT_TO_POINTER (refnum));
+ if (mpm == NULL) {
+ /* Create a new one */
+ if (maxnum > 255)
+ maxnum = 255;
+ mpm = g_malloc0 (sizeof (*mpm));
+ mpm->index = idx;
+ mpm->numparts = maxnum;
+ mpm->missing = maxnum;
+ mpm->parts = g_malloc0 (maxnum * sizeof(*mpm->parts));
+ g_hash_table_insert (priv->sms_parts, GUINT_TO_POINTER (refnum),
+ mpm);
+ }
+
+ if (maxnum != mpm->numparts) {
+ /* Error - other messages with this refnum claim a different number of parts */
+ return;
+ }
+
+ if (mpm->parts[seqnum - 1] != 0) {
+ /* Error - two SMS segments have claimed to be the same part of the same message. */
+ return;
+ }
+
+ mpm->parts[seqnum - 1] = idx;
+ mpm->missing--;
+ }
+
+ old_properties = g_hash_table_lookup (priv->sms_contents, GUINT_TO_POINTER (idx));
+ if (old_properties != NULL)
+ g_hash_table_unref (old_properties);
+
+ g_hash_table_insert (priv->sms_contents, GUINT_TO_POINTER (idx),
+ g_hash_table_ref (properties));
+}
+
+/*
+ * Takes a hash table representing a (possibly partial) SMS and
+ * determines if it is the key part of a complete SMS. The complete
+ * SMS, if any, is returned. If there is no such SMS (for example, not
+ * all parts are present yet), NULL is returned. The passed-in hash
+ * table is dereferenced, and the returned hash table is referenced.
+ */
+static GHashTable *
+sms_cache_lookup_full (MMModem *modem,
+ GHashTable *properties,
+ GError **error)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ int i, refnum, indexnum;
+ SMSMultiPartMessage *mpm;
+ GHashTable *full, *part, *first;
+ GHashTableIter iter;
+ gpointer key, value;
+ char *fulltext;
+ char **textparts;
+ GValue *ref, *idx, *text;
+
+ ref = g_hash_table_lookup (properties, "concat-reference");
+ if (ref == NULL)
+ return properties;
+ refnum = g_value_get_uint (ref);
+
+ idx = g_hash_table_lookup (properties, "index");
+ if (idx == NULL) {
+ g_hash_table_unref (properties);
+ return NULL;
+ }
+
+ indexnum = g_value_get_uint (idx);
+ g_hash_table_unref (properties);
+
+ 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");
+ return NULL;
+ }
+
+ /* Check that this is the key */
+ if (indexnum != mpm->index)
+ return NULL;
+
+ if (mpm->missing != 0)
+ return NULL;
+
+ /* Complete multipart message is present. Assemble it */
+ textparts = g_malloc0((1 + mpm->numparts) * sizeof (*textparts));
+ for (i = 0 ; i < mpm->numparts ; i++) {
+ 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_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_free (textparts);
+ return NULL;
+ }
+ textparts[i] = g_value_dup_string (text);
+ }
+ textparts[i] = NULL;
+ fulltext = g_strjoinv (NULL, textparts);
+ g_strfreev (textparts);
+
+ first = g_hash_table_lookup (priv->sms_contents,
+ GUINT_TO_POINTER (mpm->parts[0]));
+ full = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
+ simple_free_gvalue);
+ g_hash_table_iter_init (&iter, first);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ const char *keystr = key;
+ if (strncmp (keystr, "concat-", 7) == 0)
+ continue;
+ if (strcmp (keystr, "text") == 0 ||
+ strcmp (keystr, "index") == 0)
+ continue;
+ if (strcmp (keystr, "class") == 0) {
+ GValue *val;
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UINT);
+ g_value_copy (value, val);
+ g_hash_table_insert (full, key, val);
+ } else {
+ GValue *val;
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_STRING);
+ g_value_copy (value, val);
+ g_hash_table_insert (full, key, val);
+ }
+ }
+
+ g_hash_table_insert (full, "index", simple_uint_value (mpm->index));
+ g_hash_table_insert (full, "text", simple_string_value (fulltext));
+ g_free (fulltext);
+
+ return full;
+}
+
+static void
+cmti_received_has_sms (MMModemGsmSms *modem,
+ GHashTable *properties,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ guint idx;
+ gboolean complete;
+ GValue *ref;
+
+ /*
+ * But how will the 'received', non-complete signal get sent?
+ * Maybe that should happen earlier.
+ */
+ if (properties == NULL)
+ return;
+
+ ref = g_hash_table_lookup (properties, "concat-reference");
+ if (ref == NULL) {
+ /* single-part message */
+ GValue *idxval = g_hash_table_lookup (properties, "index");
+ if (idxval == NULL)
+ return;
+ idx = g_value_get_uint (idxval);
+ complete = TRUE;
+ } else {
+ SMSMultiPartMessage *mpm;
+ mpm = g_hash_table_lookup (priv->sms_parts,
+ GUINT_TO_POINTER (g_value_get_uint (ref)));
+ if (mpm == NULL)
+ return;
+ idx = mpm->index;
+ complete = (mpm->missing == 0);
+ }
+
+ if (complete)
+ mm_modem_gsm_sms_completed (MM_MODEM_GSM_SMS (self), idx, TRUE);
+
+ mm_modem_gsm_sms_received (MM_MODEM_GSM_SMS (self), idx, complete);
+}
+
+static void sms_get_invoke (MMCallbackInfo *info);
+static void sms_get_done (MMAtSerialPort *port, GString *response,
+ GError *error, gpointer user_data);
+
static void
cmti_received (MMAtSerialPort *port,
GMatchInfo *info,
@@ -1331,8 +1583,9 @@ cmti_received (MMAtSerialPort *port,
{
MMGenericGsm *self = MM_GENERIC_GSM (user_data);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMCallbackInfo *cbinfo;
guint idx = 0;
- char *str;
+ char *str, *command;
str = g_match_info_fetch (info, 2);
if (str)
@@ -1346,12 +1599,20 @@ cmti_received (MMAtSerialPort *port,
/* Nothing is currently stored in the hash table - presence is all that matters. */
g_hash_table_insert (priv->sms_present, GINT_TO_POINTER (idx), NULL);
- /* todo: parse pdu to know if the sms is complete */
- mm_modem_gsm_sms_received (MM_MODEM_GSM_SMS (self),
- idx,
- TRUE);
+ /* Retrieve the message */
+ cbinfo = mm_callback_info_new_full (MM_MODEM (user_data),
+ sms_get_invoke,
+ G_CALLBACK (cmti_received_has_sms),
+ user_data);
- /* todo: send mm_modem_gsm_sms_completed if complete */
+ if (priv->sms_fetch_pending != 0) {
+ mm_err("sms_fetch_pending is %d, not 0", priv->sms_fetch_pending);
+ }
+ priv->sms_fetch_pending = idx;
+
+ command = g_strdup_printf ("+CMGR=%d", idx);
+ mm_at_serial_port_queue_command (port, command, 10, sms_get_done, cbinfo);
+ /* Don't want to signal received here before we have the contents */
}
static void
@@ -4267,8 +4528,6 @@ mm_generic_gsm_get_charset (MMGenericGsm *self)
/*****************************************************************************/
/* MMModemGsmSms interface */
-
-
static void
sms_send_done (MMAtSerialPort *port,
GString *response,
@@ -4318,8 +4577,6 @@ sms_send (MMModemGsmSms *modem,
g_free (command);
}
-
-
static void
sms_get_done (MMAtSerialPort *port,
GString *response,
@@ -4327,10 +4584,15 @@ sms_get_done (MMAtSerialPort *port,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
GHashTable *properties;
int rv, status, tpdu_len;
+ guint idx;
char pdu[SMS_MAX_PDU_LEN + 1];
+ idx = priv->sms_fetch_pending;
+ priv->sms_fetch_pending = 0;
+
/* If the modem has already been removed, return without
* scheduling callback */
if (mm_callback_info_check_modem_removed (info))
@@ -4357,8 +4619,19 @@ sms_get_done (MMAtSerialPort *port,
goto out;
}
- mm_callback_info_set_data (info, GS_HASH_TAG, properties,
- (GDestroyNotify) g_hash_table_unref);
+ g_hash_table_insert (properties, "index",
+ simple_uint_value (idx));
+ sms_cache_insert (info->modem, properties, idx);
+
+ /*
+ * If this is a standalone message, or the key part of a
+ * multipart message, pass it along, otherwise report that there's
+ * no such message.
+ */
+ properties = sms_cache_lookup_full (info->modem, properties, &info->error);
+ if (properties)
+ mm_callback_info_set_data (info, GS_HASH_TAG, properties,
+ (GDestroyNotify) g_hash_table_unref);
out:
mm_callback_info_schedule (info);
@@ -4383,6 +4656,25 @@ sms_get (MMModemGsmSms *modem,
MMCallbackInfo *info;
char *command;
MMAtSerialPort *port;
+ MMGenericGsmPrivate *priv =
+ MM_GENERIC_GSM_GET_PRIVATE (MM_GENERIC_GSM (modem));
+ GHashTable *properties;
+ GError *error = NULL;
+
+ properties = g_hash_table_lookup (priv->sms_contents, GUINT_TO_POINTER (idx));
+ if (properties != NULL) {
+ g_hash_table_ref (properties);
+ properties = sms_cache_lookup_full (MM_MODEM (modem), properties, &error);
+ if (properties == NULL) {
+ error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "No SMS found");
+ }
+ callback (modem, properties, error, user_data);
+ if (properties != NULL)
+ g_hash_table_unref (properties);
+ return;
+ }
info = mm_callback_info_new_full (MM_MODEM (modem),
sms_get_invoke,
@@ -4395,10 +4687,18 @@ sms_get (MMModemGsmSms *modem,
return;
}
- command = g_strdup_printf ("+CMGR=%d\r\n", idx);
+ command = g_strdup_printf ("+CMGR=%d", idx);
+ priv->sms_fetch_pending = idx;
mm_at_serial_port_queue_command (port, command, 10, sms_get_done, info);
}
+typedef struct {
+ MMGenericGsmPrivate *priv;
+ MMCallbackInfo *info;
+ SMSMultiPartMessage *mpm;
+ int deleting;
+} SMSDeleteProgress;
+
static void
sms_delete_done (MMAtSerialPort *port,
GString *response,
@@ -4419,6 +4719,50 @@ sms_delete_done (MMAtSerialPort *port,
}
static void
+sms_delete_multi_next (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ SMSDeleteProgress *progress = (SMSDeleteProgress *)user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (progress->info))
+ goto done;
+
+ if (error)
+ progress->info->error = g_error_copy (error);
+
+ for (progress->deleting++ ;
+ progress->deleting < progress->mpm->numparts ;
+ progress->deleting++)
+ if (progress->mpm->parts[progress->deleting] != 0)
+ break;
+ if (progress->deleting < progress->mpm->numparts) {
+ GHashTable *properties;
+ char *command;
+ guint idx;
+
+ idx = progress->mpm->parts[progress->deleting];
+ command = g_strdup_printf ("+CMGD=%d", idx);
+ mm_at_serial_port_queue_command (port, command, 10,
+ sms_delete_multi_next, progress);
+ properties = g_hash_table_lookup (progress->priv->sms_contents, GUINT_TO_POINTER (idx));
+ g_hash_table_remove (progress->priv->sms_contents, GUINT_TO_POINTER (idx));
+ g_hash_table_remove (progress->priv->sms_present, GUINT_TO_POINTER (idx));
+ g_hash_table_unref (properties);
+ return;
+ }
+
+ mm_callback_info_schedule (progress->info);
+done:
+ g_free (progress->mpm->parts);
+ g_free (progress->mpm);
+ g_free (progress);
+}
+
+static void
sms_delete (MMModemGsmSms *modem,
guint idx,
MMModemFn callback,
@@ -4428,18 +4772,92 @@ sms_delete (MMModemGsmSms *modem,
char *command;
MMAtSerialPort *port;
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (MM_GENERIC_GSM (modem));
+ GHashTable *properties;
+ MMAtSerialResponseFn next_callback;
+ GValue *ref;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- g_hash_table_remove (priv->sms_present, GINT_TO_POINTER (idx));
+
+ properties = g_hash_table_lookup (priv->sms_contents, GINT_TO_POINTER (idx));
+ if (properties == NULL) {
+ /*
+ * TODO(njw): This assumes our cache is valid. If we doubt this, we should just
+ * run the delete anyway and let that return the nonexistent-message error.
+ */
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "No SMS to delete");
+ mm_callback_info_schedule (info);
+ return;
+ }
port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
if (!port) {
mm_callback_info_schedule (info);
+ g_hash_table_remove (priv->sms_contents, GINT_TO_POINTER (idx));
+ g_hash_table_unref (properties);
return;
}
- command = g_strdup_printf ("+CMGD=%d\r\n", idx);
- mm_at_serial_port_queue_command (port, command, 10, sms_delete_done, info);
+ user_data = info;
+ next_callback = sms_delete_done;
+ ref = g_hash_table_lookup (properties, "concat-reference");
+ if (ref != NULL) {
+ SMSMultiPartMessage *mpm;
+ SMSDeleteProgress *progress;
+ guint refnum;
+
+ refnum = g_value_get_uint (ref);
+ mpm = g_hash_table_lookup (priv->sms_parts, GUINT_TO_POINTER (refnum));
+ if (mpm == NULL) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Internal error - no part array for multipart SMS");
+ mm_callback_info_schedule (info);
+ g_hash_table_remove (priv->sms_contents, GINT_TO_POINTER (idx));
+ g_hash_table_unref (properties);
+ return;
+ }
+ /* Only allow the delete operation on the main index number. */
+ if (idx != mpm->index) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "No SMS to delete");
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ g_hash_table_remove (priv->sms_parts, GUINT_TO_POINTER (refnum));
+ progress = g_malloc0 (sizeof(*progress));
+ progress->priv = priv;
+ progress->info = info;
+ progress->mpm = mpm;
+ for (progress->deleting = 0 ;
+ progress->deleting < mpm->numparts ;
+ progress->deleting++)
+ if (mpm->parts[progress->deleting] != 0)
+ break;
+ user_data = progress;
+ next_callback = sms_delete_multi_next;
+ idx = progress->mpm->parts[progress->deleting];
+ properties = g_hash_table_lookup (priv->sms_contents, GINT_TO_POINTER (idx));
+ }
+ g_hash_table_remove (priv->sms_contents, GUINT_TO_POINTER (idx));
+ g_hash_table_remove (priv->sms_present, GUINT_TO_POINTER (idx));
+ g_hash_table_unref (properties);
+
+ command = g_strdup_printf ("+CMGD=%d", idx);
+ mm_at_serial_port_queue_command (port, command, 10, next_callback,
+ user_data);
+}
+
+static void
+free_list_results (gpointer data)
+{
+ GPtrArray *results = (GPtrArray *) data;
+
+ g_ptr_array_foreach (results, (GFunc) g_hash_table_unref, NULL);
+ g_ptr_array_free (results, TRUE);
}
static void
@@ -4482,21 +4900,19 @@ sms_list_done (MMAtSerialPort *port,
if (properties) {
g_hash_table_insert (properties, "index",
simple_uint_value (idx));
- g_ptr_array_add (results, properties);
+ sms_cache_insert (info->modem, properties, idx);
+ /* Only add complete messages to the results */
+ properties = sms_cache_lookup_full (info->modem, properties, &info->error);
+ if (properties)
+ g_ptr_array_add (results, properties);
} else {
/* Ignore the error */
g_clear_error(&local);
}
}
- /*
- * todo(njw): mm_gsm_destroy_scan_data does what we want
- * (destroys a GPtrArray of g_hash_tables), but it should be
- * renamed to describe that or there should be a function
- * named for what we're doing here.
- */
if (results)
mm_callback_info_set_data (info, "list-sms", results,
- mm_gsm_destroy_scan_data);
+ free_list_results);
}
mm_callback_info_schedule (info);
@@ -4532,7 +4948,7 @@ sms_list (MMModemGsmSms *modem,
return;
}
- command = g_strdup_printf ("+CMGL=4\r\n");
+ command = g_strdup_printf ("+CMGL=4");
mm_at_serial_port_queue_command (port, command, 10, sms_list_done, info);
}
@@ -5579,6 +5995,8 @@ mm_generic_gsm_init (MMGenericGsm *self)
priv->reg_regex = mm_gsm_creg_regex_get (TRUE);
priv->roam_allowed = TRUE;
priv->sms_present = g_hash_table_new (g_direct_hash, g_direct_equal);
+ priv->sms_contents = g_hash_table_new (g_direct_hash, g_direct_equal);
+ priv->sms_parts = g_hash_table_new (g_direct_hash, g_direct_equal);
mm_properties_changed_signal_register_property (G_OBJECT (self),
MM_MODEM_GSM_NETWORK_ALLOWED_MODE,
@@ -5812,6 +6230,8 @@ finalize (GObject *object)
g_free (priv->oper_name);
g_free (priv->simid);
g_hash_table_destroy (priv->sms_present);
+ g_hash_table_destroy (priv->sms_contents);
+ g_hash_table_destroy (priv->sms_parts);
G_OBJECT_CLASS (mm_generic_gsm_parent_class)->finalize (object);
}
diff --git a/src/mm-sms-utils.c b/src/mm-sms-utils.c
index 1b32a796..df706ea7 100644
--- a/src/mm-sms-utils.c
+++ b/src/mm-sms-utils.c
@@ -22,6 +22,7 @@
#include "mm-errors.h"
#include "mm-utils.h"
#include "mm-sms-utils.h"
+#include "mm-log.h"
#define SMS_TP_MTI_MASK 0x03
#define SMS_TP_MTI_SMS_DELIVER 0x00
@@ -247,18 +248,6 @@ simple_uint_value (guint32 i)
}
static GValue *
-simple_boolean_value (gboolean b)
-{
- GValue *val;
-
- val = g_slice_new0 (GValue);
- g_value_init (val, G_TYPE_BOOLEAN);
- g_value_set_boolean (val, b);
-
- return val;
-}
-
-static GValue *
simple_string_value (const char *str)
{
GValue *val;
@@ -352,18 +341,79 @@ sms_parse_pdu (const char *hexpdu, GError **error)
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;
+ udhl = pdu[user_data_offset] + 1;
+ end = user_data_offset + udhl;
+
+ for (offset = user_data_offset + 1; offset < end;) {
+ guint8 ie_id, ie_len;
+
+ ie_id = pdu[offset++];
+ ie_len = pdu[offset++];
+
+ switch (ie_id) {
+ case 0x00:
+ /*
+ * Ignore the IE if one of the following is true:
+ * - it claims to be part 0 of M
+ * - it claims to be part N of M, N > M
+ */
+ if (pdu[offset + 2] == 0 ||
+ 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]));
+ break;
+ case 0x08:
+ /* Concatenated short message, 16-bit reference */
+ if (pdu[offset + 3] == 0 ||
+ 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]));
+ break;
+ }
+
+ offset += ie_len;
+ }
+
/*
- * Skip over the user data headers to prevent it from being
+ * Move past the user data headers to prevent it from being
* decoded into garbage text.
*/
- int udhl;
- udhl = pdu[user_data_offset] + 1;
user_data_offset += udhl;
if (user_data_encoding == MM_SMS_ENCODING_GSM7) {
/*
@@ -378,29 +428,15 @@ sms_parse_pdu (const char *hexpdu, GError **error)
msg_text = sms_decode_text (&pdu[user_data_offset], user_data_len,
user_data_encoding, bit_offset);
-
- 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 (sender_addr));
g_hash_table_insert (properties, "text",
simple_string_value (msg_text));
- g_hash_table_insert (properties, "smsc",
- simple_string_value (smsc_addr));
- g_hash_table_insert (properties, "timestamp",
- simple_string_value (sc_timestamp));
+ g_free (msg_text);
+
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));
- g_hash_table_insert (properties, "completed", simple_boolean_value (TRUE));
-
- g_free (smsc_addr);
- g_free (sender_addr);
- g_free (sc_timestamp);
- g_free (msg_text);
g_free (pdu);
-
return properties;
}