Re: [ModemManager] [PATCH 1/4] Split SMS parsing out into a separate file



This time with the actual patch.

On Thu, Jun 30, 2011 at 5:31 PM, Nathan Williams <njw google com> wrote:
> First of four patches that improve SMS handling. This one follows
> Dan's suggestion some time ago and splits SMS PDU parsing into a
> separate file, making it accessible to unit tests and to other code
> that might need it.
>
>    - Nathan
>
From 4c482e8f98b07867849195a9f2d7244787666119 Mon Sep 17 00:00:00 2001
From: Nathan Williams <njw chromium org>
Date: Fri, 24 Jun 2011 21:07:24 -0400
Subject: [PATCH 1/4] Split SMS parsing out into a separate file, in preparation
 for adding some tests.

Change-Id: If1ebd0fdd6e7470c21538042ab1735357649155c
---
 src/Makefile.am      |    4 +-
 src/mm-generic-gsm.c |  266 +----------------------------------------
 src/mm-sms-utils.c   |  320 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/mm-sms-utils.h   |   25 ++++
 4 files changed, 355 insertions(+), 260 deletions(-)
 create mode 100644 src/mm-sms-utils.c
 create mode 100644 src/mm-sms-utils.h

diff --git a/src/Makefile.am b/src/Makefile.am
index e813e7e..71008c9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,7 +23,9 @@ libmodem_helpers_la_SOURCES = \
 	mm-charsets.c \
 	mm-charsets.h \
 	mm-utils.c \
-	mm-utils.h
+	mm-utils.h \
+	mm-sms-utils.c \
+	mm-sms-utils.h
 
 libserial_la_CPPFLAGS = \
 	$(MM_CFLAGS) \
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 6cc01a6..6bbd69b 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -37,6 +37,7 @@
 #include "mm-properties-changed-signal.h"
 #include "mm-utils.h"
 #include "mm-modem-location.h"
+#include "mm-sms-utils.h"
 
 static void modem_init (MMModem *modem_class);
 static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class);
@@ -177,7 +178,6 @@ static void cmti_received (MMAtSerialPort *port,
 #define GS_HASH_TAG "get-sms"
 static GValue *simple_string_value (const char *str);
 static GValue *simple_uint_value (guint32 i);
-static GValue *simple_boolean_value (gboolean b);
 static void simple_free_gvalue (gpointer data);
 
 MMModem *
@@ -4171,35 +4171,6 @@ mm_generic_gsm_get_charset (MMGenericGsm *self)
 /* MMModemGsmSms interface */
 
 
-#define SMS_TP_MTI_MASK	              0x03
-#define  SMS_TP_MTI_SMS_DELIVER       0x00
-#define  SMS_TP_MTI_SMS_SUBMIT_REPORT 0x01
-#define  SMS_TP_MTI_SMS_STATUS_REPORT 0x02
-
-#define SMS_NUMBER_TYPE_MASK          0x70
-#define SMS_NUMBER_TYPE_UNKNOWN       0x00
-#define SMS_NUMBER_TYPE_INTL          0x10
-#define SMS_NUMBER_TYPE_ALPHA         0x50
-
-#define SMS_NUMBER_PLAN_MASK          0x0f
-#define SMS_NUMBER_PLAN_TELEPHONE     0x01
-
-#define SMS_TP_MMS                    0x04
-#define SMS_TP_SRI                    0x20
-#define SMS_TP_UDHI                   0x40
-#define SMS_TP_RP                     0x80
-
-#define SMS_DCS_CODING_MASK           0xec
-#define  SMS_DCS_CODING_DEFAULT       0x00
-#define  SMS_DCS_CODING_8BIT          0x04
-#define  SMS_DCS_CODING_UCS2          0x08
-
-#define SMS_DCS_CLASS_VALID           0x10
-#define SMS_DCS_CLASS_MASK            0x03
-
-#define SMS_TIMESTAMP_LEN 7
-#define SMS_MIN_PDU_LEN (7 + SMS_TIMESTAMP_LEN)
-#define SMS_MAX_PDU_LEN 344
 
 static void
 sms_send_done (MMAtSerialPort *port,
@@ -4250,219 +4221,7 @@ sms_send (MMModemGsmSms *modem,
     g_free (command);
 }
 
-static char sms_bcd_chars[] = "0123456789*#abc\0\0";
-
-static void
-sms_semi_octets_to_bcd_string (char *dest, const guint8 *octets, int num_octets)
-{
-    int i;
-
-    for (i = 0 ; i < num_octets; i++) {
-        *dest++ = sms_bcd_chars[octets[i] & 0xf];
-        *dest++ = sms_bcd_chars[(octets[i] >> 4) & 0xf];
-    }
-    *dest++ = '\0';
-}
-
-/* len is in semi-octets */
-static char *
-sms_decode_address (const guint8 *address, int len)
-{
-    guint8 addrtype, addrplan;
-    char *utf8;
-
-    addrtype = address[0] & SMS_NUMBER_TYPE_MASK;
-    addrplan = address[0] & SMS_NUMBER_PLAN_MASK;
-    address++;
-
-    if (addrtype == SMS_NUMBER_TYPE_ALPHA) {
-        guint8 *unpacked;
-        guint32 unpacked_len;
-        unpacked = gsm_unpack (address, (len * 4) / 7, 0, &unpacked_len);
-        utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 (unpacked,
-                                                        unpacked_len);
-        g_free(unpacked);
-    } else if (addrtype == SMS_NUMBER_TYPE_INTL &&
-               addrplan == SMS_NUMBER_PLAN_TELEPHONE) {
-        /* International telphone number, format as "+1234567890" */
-        utf8 = g_malloc (len + 3); /* '+' + digits + possible trailing 0xf + NUL */
-        utf8[0] = '+';
-        sms_semi_octets_to_bcd_string (utf8 + 1, address, (len + 1) / 2);
-    } else {
-        /*
-         * All non-alphanumeric types and plans are just digits, but
-         * don't apply any special formatting if we don't know the
-         * format.
-         */
-        utf8 = g_malloc (len + 2); /* digits + possible trailing 0xf + NUL */
-        sms_semi_octets_to_bcd_string (utf8, address, (len + 1) / 2);
-    }
-
-    return utf8;
-}
-
 
-static char *
-sms_decode_timestamp (const guint8 *timestamp)
-{
-    /* YYMMDDHHMMSS+ZZ */
-    char *timestr;
-    int quarters, hours;
-
-    timestr = g_malloc0 (16);
-    sms_semi_octets_to_bcd_string (timestr, timestamp, 6);
-    quarters = ((timestamp[6] & 0x7) * 10) + ((timestamp[6] >> 4) & 0xf);
-    hours = quarters / 4;
-    if (timestamp[6] & 0x08)
-        timestr[12] = '-';
-    else
-        timestr[12] = '+';
-    timestr[13] = (hours / 10) + '0';
-    timestr[14] = (hours % 10) + '0';
-    /* TODO(njw): Change timestamp rep to something that includes quarter-hours */
-    return timestr;
-}
-
-static char *
-sms_decode_text (const guint8 *text, int len, int dcs, int bit_offset)
-{
-    char *utf8;
-    guint8 coding = dcs & SMS_DCS_CODING_MASK;
-    guint8 *unpacked;
-    guint32 unpacked_len;
-
-    if (coding == SMS_DCS_CODING_DEFAULT) {
-        unpacked = gsm_unpack ((const guint8 *) text, len, bit_offset, &unpacked_len);
-        utf8 = (char *) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len);
-        g_free (unpacked);
-    } else if (coding == SMS_DCS_CODING_UCS2)
-        utf8 = g_convert ((char *) text, len, "UTF8", "UCS-2BE", NULL, NULL, NULL);
-    else if (coding == SMS_DCS_CODING_8BIT)
-        utf8 = g_strndup ((const char *)text, len);
-    else
-        utf8 = g_strdup ("");
-
-    return utf8;
-}
-
-
-static GHashTable *
-sms_parse_pdu (const char *hexpdu)
-{
-    GHashTable *properties;
-    gsize pdu_len;
-    guint8 *pdu;
-    int smsc_addr_num_octets, variable_length_items, msg_start_offset,
-            sender_addr_num_digits, sender_addr_num_octets,
-            tp_pid_offset, tp_dcs_offset, user_data_offset, user_data_len,
-            user_data_len_offset, user_data_dcs, bit_offset;
-    char *smsc_addr, *sender_addr, *sc_timestamp, *msg_text;
-
-    /* Convert PDU from hex to binary */
-    pdu = (guint8 *) utils_hexstr2bin (hexpdu, &pdu_len);
-    if (!pdu) {
-        mm_err("Couldn't parse PDU of SMS GET response from hex");
-        return NULL;
-    }
-
-    /* SMSC, in address format, precedes the TPDU */
-    smsc_addr_num_octets = pdu[0];
-    variable_length_items = smsc_addr_num_octets;
-    if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) {
-        mm_err ("PDU too short (1): %zd vs %d", pdu_len,
-                variable_length_items + SMS_MIN_PDU_LEN);
-        g_free (pdu);
-        return NULL;
-    }
-
-    /* where in the PDU the actual SMS protocol message begins */
-    msg_start_offset = 1 + smsc_addr_num_octets;
-    sender_addr_num_digits = pdu[msg_start_offset + 1];
-    /*
-     * round the sender address length up to an even number of
-     * semi-octets, and thus an integral number of octets
-     */
-    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) {
-        mm_err ("PDU too short (2): %zd vs %d", pdu_len,
-                variable_length_items + SMS_MIN_PDU_LEN);
-        g_free (pdu);
-        return NULL;
-    }
-
-    tp_pid_offset = msg_start_offset + 3 + sender_addr_num_octets;
-    tp_dcs_offset = tp_pid_offset + 1;
-
-    user_data_len_offset = tp_dcs_offset + 1 + SMS_TIMESTAMP_LEN;
-    user_data_offset = user_data_len_offset + 1;
-    user_data_len = pdu[user_data_len_offset];
-    user_data_dcs = pdu[tp_dcs_offset];
-    if ((user_data_dcs & SMS_DCS_CODING_MASK) == SMS_DCS_CODING_DEFAULT)
-        variable_length_items += (7 * (user_data_len + 1 )) / 8;
-    else
-        variable_length_items += user_data_len;
-    if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) {
-        mm_err ("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) {
-        mm_err ("Unhandled message type: 0x%02x", pdu[msg_start_offset]);
-        g_free (pdu);
-        return NULL;
-    }
-
-    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]);
-    bit_offset = 0;
-    if (pdu[msg_start_offset] & SMS_TP_UDHI) {
-        /*
-         * Skip over 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_dcs & SMS_DCS_CODING_MASK) == SMS_DCS_CODING_DEFAULT) {
-            bit_offset = 7 - (udhl * 8) % 7;
-            user_data_len -= (udhl * 8 + bit_offset) / 7;
-        } else
-            user_data_len -= udhl;
-    }
-
-    msg_text = sms_decode_text (&pdu[user_data_offset], user_data_len,
-                                user_data_dcs, 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));
-    if (user_data_dcs & SMS_DCS_CLASS_VALID)
-        g_hash_table_insert (properties, "class",
-                             simple_uint_value (user_data_dcs &
-                                                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;
-}
 
 static void
 sms_get_done (MMAtSerialPort *port,
@@ -4496,11 +4255,8 @@ sms_get_done (MMAtSerialPort *port,
         goto out;
     }
 
-    properties = sms_parse_pdu (pdu);
+    properties = sms_parse_pdu (pdu, &info->error);
     if (!properties) {
-        info->error = g_error_new_literal (MM_MODEM_ERROR,
-                                           MM_MODEM_ERROR_GENERAL,
-                                           "Failed to parse SMS PDU");
         goto out;
     }
 
@@ -4613,6 +4369,7 @@ sms_list_done (MMAtSerialPort *port,
 
         while (*rstr) {
             GHashTable *properties;
+            GError *error;
             int idx;
             char pdu[SMS_MAX_PDU_LEN + 1];
 
@@ -4624,11 +4381,14 @@ sms_list_done (MMAtSerialPort *port,
             }
             rstr += offset;
 
-            properties = sms_parse_pdu (pdu);
+            properties = sms_parse_pdu (pdu, &error);
             if (properties) {
                 g_hash_table_insert (properties, "index",
                                      simple_uint_value (idx));
                 g_ptr_array_add (results, properties);
+            } else {
+                /* Ignore the error */
+                g_clear_error(&error);
             }
         }
         /*
@@ -5228,18 +4988,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;
diff --git a/src/mm-sms-utils.c b/src/mm-sms-utils.c
new file mode 100644
index 0000000..c7de0e1
--- /dev/null
+++ b/src/mm-sms-utils.c
@@ -0,0 +1,320 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ */
+
+#include <glib.h>
+
+#include "mm-charsets.h"
+#include "mm-errors.h"
+#include "mm-utils.h"
+#include "mm-sms-utils.h"
+
+#define SMS_TP_MTI_MASK               0x03
+#define  SMS_TP_MTI_SMS_DELIVER       0x00
+#define  SMS_TP_MTI_SMS_SUBMIT_REPORT 0x01
+#define  SMS_TP_MTI_SMS_STATUS_REPORT 0x02
+
+#define SMS_NUMBER_TYPE_MASK          0x70
+#define SMS_NUMBER_TYPE_UNKNOWN       0x00
+#define SMS_NUMBER_TYPE_INTL          0x10
+#define SMS_NUMBER_TYPE_ALPHA         0x50
+
+#define SMS_NUMBER_PLAN_MASK          0x0f
+#define SMS_NUMBER_PLAN_TELEPHONE     0x01
+
+#define SMS_TP_MMS                    0x04
+#define SMS_TP_SRI                    0x20
+#define SMS_TP_UDHI                   0x40
+#define SMS_TP_RP                     0x80
+
+#define SMS_DCS_CODING_MASK           0xec
+#define  SMS_DCS_CODING_DEFAULT       0x00
+#define  SMS_DCS_CODING_8BIT          0x04
+#define  SMS_DCS_CODING_UCS2          0x08
+
+#define SMS_DCS_CLASS_VALID           0x10
+#define SMS_DCS_CLASS_MASK            0x03
+
+#define SMS_TIMESTAMP_LEN 7
+#define SMS_MIN_PDU_LEN (7 + SMS_TIMESTAMP_LEN)
+
+static char sms_bcd_chars[] = "0123456789*#abc\0\0";
+
+static void
+sms_semi_octets_to_bcd_string (char *dest, const guint8 *octets, int num_octets)
+{
+    int i;
+
+    for (i = 0 ; i < num_octets; i++) {
+        *dest++ = sms_bcd_chars[octets[i] & 0xf];
+        *dest++ = sms_bcd_chars[(octets[i] >> 4) & 0xf];
+    }
+    *dest++ = '\0';
+}
+
+/* len is in semi-octets */
+static char *
+sms_decode_address (const guint8 *address, int len)
+{
+    guint8 addrtype, addrplan;
+    char *utf8;
+
+    addrtype = address[0] & SMS_NUMBER_TYPE_MASK;
+    addrplan = address[0] & SMS_NUMBER_PLAN_MASK;
+    address++;
+
+    if (addrtype == SMS_NUMBER_TYPE_ALPHA) {
+        guint8 *unpacked;
+        guint32 unpacked_len;
+        unpacked = gsm_unpack (address, (len * 4) / 7, 0, &unpacked_len);
+        utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 (unpacked,
+                                                        unpacked_len);
+        g_free(unpacked);
+    } else if (addrtype == SMS_NUMBER_TYPE_INTL &&
+               addrplan == SMS_NUMBER_PLAN_TELEPHONE) {
+        /* International telphone number, format as "+1234567890" */
+        utf8 = g_malloc (len + 3); /* '+' + digits + possible trailing 0xf + NUL */
+        utf8[0] = '+';
+        sms_semi_octets_to_bcd_string (utf8 + 1, address, (len + 1) / 2);
+    } else {
+        /*
+         * All non-alphanumeric types and plans are just digits, but
+         * don't apply any special formatting if we don't know the
+         * format.
+         */
+        utf8 = g_malloc (len + 2); /* digits + possible trailing 0xf + NUL */
+        sms_semi_octets_to_bcd_string (utf8, address, (len + 1) / 2);
+    }
+
+    return utf8;
+}
+
+
+static char *
+sms_decode_timestamp (const guint8 *timestamp)
+{
+    /* YYMMDDHHMMSS+ZZ */
+    char *timestr;
+    int quarters, hours;
+
+    timestr = g_malloc0 (16);
+    sms_semi_octets_to_bcd_string (timestr, timestamp, 6);
+    quarters = ((timestamp[6] & 0x7) * 10) + ((timestamp[6] >> 4) & 0xf);
+    hours = quarters / 4;
+    if (timestamp[6] & 0x08)
+        timestr[12] = '-';
+    else
+        timestr[12] = '+';
+    timestr[13] = (hours / 10) + '0';
+    timestr[14] = (hours % 10) + '0';
+    /* TODO(njw): Change timestamp rep to something that includes quarter-hours */
+    return timestr;
+}
+
+static char *
+sms_decode_text (const guint8 *text, int len, int dcs, int bit_offset)
+{
+    char *utf8;
+    guint8 coding = dcs & SMS_DCS_CODING_MASK;
+    guint8 *unpacked;
+    guint32 unpacked_len;
+
+    if (coding == SMS_DCS_CODING_DEFAULT) {
+        unpacked = gsm_unpack ((const guint8 *) text, len, bit_offset, &unpacked_len);
+        utf8 = (char *) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len);
+        g_free (unpacked);
+    } else if (coding == SMS_DCS_CODING_UCS2)
+        utf8 = g_convert ((char *) text, len, "UTF8", "UCS-2BE", NULL, NULL, NULL);
+    else if (coding == SMS_DCS_CODING_8BIT)
+        utf8 = g_strndup ((const char *)text, len);
+    else
+        utf8 = g_strdup ("");
+
+    return utf8;
+}
+
+static void
+simple_free_gvalue (gpointer data)
+{
+    g_value_unset ((GValue *) data);
+    g_slice_free (GValue, data);
+}
+
+
+
+static GValue *
+simple_uint_value (guint32 i)
+{
+    GValue *val;
+
+    val = g_slice_new0 (GValue);
+    g_value_init (val, G_TYPE_UINT);
+    g_value_set_uint (val, i);
+
+    return val;
+}
+
+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;
+
+    val = g_slice_new0 (GValue);
+    g_value_init (val, G_TYPE_STRING);
+    g_value_set_string (val, str);
+
+    return val;
+}
+
+GHashTable *
+sms_parse_pdu (const char *hexpdu, GError **error)
+{
+    GHashTable *properties;
+    gsize pdu_len;
+    guint8 *pdu;
+    int smsc_addr_num_octets, variable_length_items, msg_start_offset,
+            sender_addr_num_digits, sender_addr_num_octets,
+            tp_pid_offset, tp_dcs_offset, user_data_offset, user_data_len,
+            user_data_len_offset, user_data_dcs, bit_offset;
+    char *smsc_addr, *sender_addr, *sc_timestamp, *msg_text;
+
+    /* 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");
+        return NULL;
+    }
+
+    /* SMSC, in address format, precedes the TPDU */
+    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_free (pdu);
+        return NULL;
+    }
+
+    /* where in the PDU the actual SMS protocol message begins */
+    msg_start_offset = 1 + smsc_addr_num_octets;
+    sender_addr_num_digits = pdu[msg_start_offset + 1];
+    /*
+     * round the sender address length up to an even number of
+     * semi-octets, and thus an integral number of octets
+     */
+    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_free (pdu);
+        return NULL;
+    }
+
+    tp_pid_offset = msg_start_offset + 3 + sender_addr_num_octets;
+    tp_dcs_offset = tp_pid_offset + 1;
+
+    user_data_len_offset = tp_dcs_offset + 1 + SMS_TIMESTAMP_LEN;
+    user_data_offset = user_data_len_offset + 1;
+    user_data_len = pdu[user_data_len_offset];
+    user_data_dcs = pdu[tp_dcs_offset];
+    if ((user_data_dcs & SMS_DCS_CODING_MASK) == SMS_DCS_CODING_DEFAULT)
+        variable_length_items += (7 * (user_data_len + 1 )) / 8;
+    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_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_free (pdu);
+        return NULL;
+    }
+
+    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]);
+    bit_offset = 0;
+    if (pdu[msg_start_offset] & SMS_TP_UDHI) {
+        /*
+         * Skip over 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_dcs & SMS_DCS_CODING_MASK) == SMS_DCS_CODING_DEFAULT) {
+            bit_offset = 7 - (udhl * 8) % 7;
+            user_data_len -= (udhl * 8 + bit_offset) / 7;
+        } else
+            user_data_len -= udhl;
+    }
+
+    msg_text = sms_decode_text (&pdu[user_data_offset], user_data_len,
+                                user_data_dcs, 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));
+    if (user_data_dcs & SMS_DCS_CLASS_VALID)
+        g_hash_table_insert (properties, "class",
+                             simple_uint_value (user_data_dcs &
+                                                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;
+}
diff --git a/src/mm-sms-utils.h b/src/mm-sms-utils.h
new file mode 100644
index 0000000..26d9829
--- /dev/null
+++ b/src/mm-sms-utils.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_SMS_UTILS_H
+#define MM_SMS_UTILS_H
+
+#include <glib.h>
+
+#define SMS_MAX_PDU_LEN 344
+
+GHashTable *sms_parse_pdu (const char *hexpdu, GError **error);
+
+#endif /* MM_SMS_UTILS_H */
-- 
1.7.3.1



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]