diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-iface-modem-messaging.c | 48 | ||||
-rw-r--r-- | src/mm-iface-modem-messaging.h | 5 | ||||
-rw-r--r-- | src/mm-sms-list.c | 29 | ||||
-rw-r--r-- | src/mm-sms-list.h | 4 | ||||
-rw-r--r-- | src/mm-sms-part.c | 19 | ||||
-rw-r--r-- | src/mm-sms-part.h | 3 | ||||
-rw-r--r-- | src/mm-sms.c | 357 |
7 files changed, 320 insertions, 145 deletions
diff --git a/src/mm-iface-modem-messaging.c b/src/mm-iface-modem-messaging.c index 7a4c1f31..5861b373 100644 --- a/src/mm-iface-modem-messaging.c +++ b/src/mm-iface-modem-messaging.c @@ -31,6 +31,54 @@ static GQuark storage_context_quark; /*****************************************************************************/ +guint8 +mm_iface_modem_messaging_get_local_multipart_reference (MMIfaceModemMessaging *self, + const gchar *number, + GError **error) +{ + MMSmsList *list = NULL; + guint8 reference; + guint8 first; + + /* Start by looking for a random number */ + reference = g_random_int_range (1,255); + + /* Then, look for the given reference in user-created messages */ + g_object_get (self, + MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, + NULL); + if (!list) + return reference; + + first = reference; + do { + if (!mm_sms_list_has_local_multipart_reference (list, number, reference)) { + g_object_unref (list); + return reference; + } + + if (reference == 255) + reference = 1; + else + reference++; + } + while (reference != first); + + g_object_unref (list); + + /* We were not able to find a new valid multipart reference :/ + * return an error */ + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_TOO_MANY, + "Cannot create multipart SMS: No valid multipart reference " + "available for destination number '%s'", + number); + return 0; +} + +/*****************************************************************************/ + void mm_iface_modem_messaging_bind_simple_status (MMIfaceModemMessaging *self, MMSimpleStatus *status) diff --git a/src/mm-iface-modem-messaging.h b/src/mm-iface-modem-messaging.h index c2b5d859..0cbe16c8 100644 --- a/src/mm-iface-modem-messaging.h +++ b/src/mm-iface-modem-messaging.h @@ -176,4 +176,9 @@ gboolean mm_iface_modem_messaging_is_storage_supported_for_receiving (MMIfaceMod /* SMS creation */ MMSms *mm_iface_modem_messaging_create_sms (MMIfaceModemMessaging *self); +/* Look for a new valid multipart reference */ +guint8 mm_iface_modem_messaging_get_local_multipart_reference (MMIfaceModemMessaging *self, + const gchar *number, + GError **error); + #endif /* MM_IFACE_MODEM_MESSAGING_H */ diff --git a/src/mm-sms-list.c b/src/mm-sms-list.c index 393101ba..a34bd679 100644 --- a/src/mm-sms-list.c +++ b/src/mm-sms-list.c @@ -54,6 +54,35 @@ struct _MMSmsListPrivate { /*****************************************************************************/ +gboolean +mm_sms_list_has_local_multipart_reference (MMSmsList *self, + const gchar *number, + guint8 reference) +{ + GList *l; + + /* No one should look for multipart reference 0, which isn't valid */ + g_assert (reference != 0); + + for (l = self->priv->list; l; l = g_list_next (l)) { + MMSms *sms = MM_SMS (l->data); + + if (mm_sms_is_multipart (sms) && + mm_gdbus_sms_get_pdu_type (MM_GDBUS_SMS (sms)) == MM_SMS_PDU_TYPE_SUBMIT && + mm_sms_get_storage (sms) != MM_SMS_STORAGE_UNKNOWN && + mm_sms_get_multipart_reference (sms) == reference && + g_str_equal (mm_gdbus_sms_get_number (MM_GDBUS_SMS (sms)), number)) { + /* Yes, the SMS list has an SMS with the same destination number + * and multipart reference */ + return TRUE; + } + } + + return FALSE; +} + +/*****************************************************************************/ + guint mm_sms_list_get_count (MMSmsList *self) { diff --git a/src/mm-sms-list.h b/src/mm-sms-list.h index 16f39e51..b98e71fb 100644 --- a/src/mm-sms-list.h +++ b/src/mm-sms-list.h @@ -82,4 +82,8 @@ gboolean mm_sms_list_delete_sms_finish (MMSmsList *self, GAsyncResult *res, GError **error); +gboolean mm_sms_list_has_local_multipart_reference (MMSmsList *self, + const gchar *number, + guint8 reference); + #endif /* MM_SMS_LIST_H */ diff --git a/src/mm-sms-part.c b/src/mm-sms-part.c index c779f235..7cac9b88 100644 --- a/src/mm-sms-part.c +++ b/src/mm-sms-part.c @@ -1255,7 +1255,8 @@ mm_sms_part_util_split_text (const gchar *text, } GByteArray ** -mm_sms_part_util_split_data (const GByteArray *data) +mm_sms_part_util_split_data (const guint8 *data, + gsize data_len) { GByteArray **out; @@ -1267,25 +1268,25 @@ mm_sms_part_util_split_data (const GByteArray *data) * bytes each, as we need place for the UDH header. */ - if (data->len <= 140) { + if (data_len <= 140) { out = g_new0 (GByteArray *, 2); - out[0] = g_byte_array_append (g_byte_array_sized_new (data->len), - data->data, - data->len); + out[0] = g_byte_array_append (g_byte_array_sized_new (data_len), + data, + data_len); } else { guint n_chunks; guint i; guint j; - n_chunks = data->len / 134; - if (data->len % 134 != 0) + n_chunks = data_len / 134; + if (data_len % 134 != 0) n_chunks ++; out = g_new0 (GByteArray *, n_chunks + 1); for (i = 0, j = 0; i < n_chunks; i++, j+= 134) { out[i] = g_byte_array_append (g_byte_array_sized_new (134), - &data->data[j], - MIN (data->len - j, 134)); + &data[j], + MIN (data_len - j, 134)); } } diff --git a/src/mm-sms-part.h b/src/mm-sms-part.h index 9b75ef14..4517a7a7 100644 --- a/src/mm-sms-part.h +++ b/src/mm-sms-part.h @@ -138,6 +138,7 @@ guint mm_sms_part_encode_address (const gchar *address, gchar **mm_sms_part_util_split_text (const gchar *text, MMSmsEncoding *encoding); -GByteArray **mm_sms_part_util_split_data (const GByteArray *data); +GByteArray **mm_sms_part_util_split_data (const guint8 *data, + gsize data_len); #endif /* MM_SMS_PART_H */ diff --git a/src/mm-sms.c b/src/mm-sms.c index 20ed2f90..26a6eadc 100644 --- a/src/mm-sms.c +++ b/src/mm-sms.c @@ -71,6 +71,128 @@ struct _MMSmsPrivate { }; /*****************************************************************************/ + +static gboolean +generate_submit_pdus (MMSms *self, + GError **error) +{ + guint i; + guint n_parts; + + const gchar *text; + GVariant *data_variant; + const guint8 *data; + gsize data_len = 0; + + MMSmsEncoding encoding; + gchar **split_text = NULL; + GByteArray **split_data = NULL; + + g_assert (self->priv->parts == NULL); + + text = mm_gdbus_sms_get_text (MM_GDBUS_SMS (self)); + data_variant = mm_gdbus_sms_get_data (MM_GDBUS_SMS (self)); + data = (data_variant ? + g_variant_get_fixed_array (data_variant, + &data_len, + sizeof (guchar)) : + NULL); + + g_assert (text != NULL || data != NULL); + g_assert (!(text != NULL && data != NULL)); + + if (text) { + split_text = mm_sms_part_util_split_text (text, &encoding); + if (!split_text) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Cannot create SMS: cannot process input text"); + return FALSE; + } + n_parts = g_strv_length (split_text); + } else if (data) { + encoding = MM_SMS_ENCODING_8BIT; + split_data = mm_sms_part_util_split_data (data, data_len); + g_assert (split_data != NULL); + /* noop within the for */ + for (n_parts = 0; split_data[n_parts]; n_parts++); + } else + g_assert_not_reached (); + + g_assert (split_text != NULL || split_data != NULL); + g_assert (!(split_text != NULL && split_data != NULL)); + + /* Loop text/data chunks */ + i = 0; + while (1) { + MMSmsPart *part; + gchar *part_text = NULL; + GByteArray *part_data = NULL; + + if (split_text) { + if (!split_text[i]) + break; + part_text = split_text[i]; + mm_dbg (" Processing chunk '%u' of text with '%u' bytes", + i, (guint) strlen (part_text)); + } else if (split_data) { + if (!split_data[i]) + break; + part_data = split_data[i]; + mm_dbg (" Processing chunk '%u' of data with '%u' bytes", + i, part_data->len); + + } else + g_assert_not_reached (); + + /* Create new part */ + part = mm_sms_part_new (SMS_PART_INVALID_INDEX, MM_SMS_PDU_TYPE_SUBMIT); + mm_sms_part_take_text (part, part_text); + mm_sms_part_take_data (part, part_data); + mm_sms_part_set_encoding (part, encoding); + mm_sms_part_set_number (part, mm_gdbus_sms_get_number (MM_GDBUS_SMS (self))); + mm_sms_part_set_smsc (part, mm_gdbus_sms_get_smsc (MM_GDBUS_SMS (self))); + mm_sms_part_set_validity (part, mm_gdbus_sms_get_validity (MM_GDBUS_SMS (self))); + mm_sms_part_set_class (part, mm_gdbus_sms_get_class (MM_GDBUS_SMS (self))); + mm_sms_part_set_delivery_report_request (part, mm_gdbus_sms_get_delivery_report_request (MM_GDBUS_SMS (self))); + + if (n_parts > 1) { + mm_sms_part_set_concat_reference (part, 0); /* We don't set a concat reference here */ + mm_sms_part_set_concat_sequence (part, i + 1); + mm_sms_part_set_concat_max (part, n_parts); + + mm_dbg ("Created SMS part '%u' for multipart SMS ('%u' parts expected)", + i + 1, n_parts); + } else { + mm_dbg ("Created SMS part for singlepart SMS"); + } + + /* Add to the list of parts */ + self->priv->parts = g_list_append (self->priv->parts, part); + + i++; + } + + /* Free array (not contents, which were taken for the part) */ + if (split_text) + g_free (split_text); + if (split_data) + g_free (split_data); + + /* Set additional multipart specific properties */ + if (n_parts > 1) { + self->priv->is_multipart = TRUE; + self->priv->max_parts = n_parts; + } + + /* No more parts are expected */ + self->priv->is_assembled = TRUE; + + return TRUE; +} + +/*****************************************************************************/ /* Store SMS (DBus call handling) */ typedef struct { @@ -111,6 +233,40 @@ handle_store_ready (MMSms *self, handle_store_context_free (ctx); } +static gboolean +prepare_sms_to_be_stored (MMSms *self, + GError **error) +{ + GList *l; + guint8 reference; + + g_assert (self->priv->parts == NULL); + + /* Look for a valid multipart reference to use. When storing, we need to + * check whether we have already stored multipart SMS with the same + * reference and destination number */ + reference = (mm_iface_modem_messaging_get_local_multipart_reference ( + MM_IFACE_MODEM_MESSAGING (self->priv->modem), + mm_gdbus_sms_get_number (MM_GDBUS_SMS (self)), + error)); + if (!reference || + !generate_submit_pdus (self, error)) { + g_prefix_error (error, "Cannot prepare SMS to be stored: "); + return FALSE; + } + + /* If the message is a multipart message, we need to set a proper + * multipart reference. When sending a message which wasn't stored + * yet, we can just get a random multipart reference. */ + self->priv->multipart_reference = reference; + for (l = self->priv->parts; l; l = g_list_next (l)) { + mm_sms_part_set_concat_reference ((MMSmsPart *)l->data, + self->priv->multipart_reference); + } + + return TRUE; +} + static void handle_store_auth_ready (MMBaseModem *modem, GAsyncResult *res, @@ -151,6 +307,13 @@ handle_store_auth_ready (MMBaseModem *modem, return; } + /* Prepare the SMS to be stored, creating the PDU list if required */ + if (!prepare_sms_to_be_stored (ctx->self, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_store_context_free (ctx); + return; + } + /* If not stored, check if we do support doing it */ if (!MM_SMS_GET_CLASS (ctx->self)->store || !MM_SMS_GET_CLASS (ctx->self)->store_finish) { @@ -245,6 +408,32 @@ handle_send_ready (MMSms *self, handle_send_context_free (ctx); } +static gboolean +prepare_sms_to_be_sent (MMSms *self, + GError **error) +{ + GList *l; + + if (self->priv->parts) + return TRUE; + + if (!generate_submit_pdus (self, error)) { + g_prefix_error (error, "Cannot prepare SMS to be sent: "); + return FALSE; + } + + /* If the message is a multipart message, we need to set a proper + * multipart reference. When sending a message which wasn't stored + * yet, we can just get a random multipart reference. */ + self->priv->multipart_reference = g_random_int_range (1,255); + for (l = self->priv->parts; l; l = g_list_next (l)) { + mm_sms_part_set_concat_reference ((MMSmsPart *)l->data, + self->priv->multipart_reference); + } + + return TRUE; +} + static void handle_send_auth_ready (MMBaseModem *modem, GAsyncResult *res, @@ -281,6 +470,13 @@ handle_send_auth_ready (MMBaseModem *modem, return; } + /* Prepare the SMS to be sent, creating the PDU list if required */ + if (!prepare_sms_to_be_sent (ctx->self, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_send_context_free (ctx); + return; + } + /* Check if we do support doing it */ if (!MM_SMS_GET_CLASS (ctx->self)->send || !MM_SMS_GET_CLASS (ctx->self)->send_finish) { @@ -1439,13 +1635,11 @@ mm_sms_new_from_properties (MMBaseModem *modem, MMSmsProperties *properties, GError **error) { - MMSmsPart *part; - guint n_parts; + MMSms *self; const gchar *text; - MMSmsEncoding encoding; - gchar **split_text = NULL; GByteArray *data; - GByteArray **split_data = NULL; + + g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); text = mm_sms_properties_get_text (properties); data = mm_sms_properties_peek_data_bytearray (properties); @@ -1471,138 +1665,31 @@ mm_sms_new_from_properties (MMBaseModem *modem, return NULL; } - if (text) { - split_text = mm_sms_part_util_split_text (text, &encoding); - if (!split_text) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Cannot create SMS: cannot process input text"); - return NULL; - } - n_parts = g_strv_length (split_text); - } else if (data) { - encoding = MM_SMS_ENCODING_8BIT; - split_data = mm_sms_part_util_split_data (data); - g_assert (split_data != NULL); - /* noop within the for */ - for (n_parts = 0; split_data[n_parts]; n_parts++); - } else - g_assert_not_reached (); - - if (n_parts > 1) { - MMSms *sms = NULL; - guint i = 0; - /* wtf... is this really the way to go? */ - guint reference = g_random_int_range (1,255); - - g_assert (split_text != NULL || split_data != NULL); - g_assert (!(split_text != NULL && split_data != NULL)); - - /* Loop text/data chunks */ - while (1) { - gchar *part_text = NULL; - GByteArray *part_data = NULL; - - if (split_text) { - if (!split_text[i]) - break; - part_text = split_text[i]; - split_text[i] = NULL; - mm_dbg (" Processing chunk '%u' of text with '%u' bytes", - i, (guint) strlen (part_text)); - } else if (split_data) { - if (!split_data[i]) - break; - part_data = split_data[i]; - split_data[i] = NULL; - mm_dbg (" Processing chunk '%u' of data with '%u' bytes", - i, part_data->len); - - } else - g_assert_not_reached (); - - /* Create new part */ - part = mm_sms_part_new (SMS_PART_INVALID_INDEX, - MM_SMS_PDU_TYPE_SUBMIT); - mm_sms_part_take_text (part, part_text); - mm_sms_part_take_data (part, part_data); - mm_sms_part_set_encoding (part, encoding); - mm_sms_part_set_number (part, mm_sms_properties_get_number (properties)); - mm_sms_part_set_smsc (part, mm_sms_properties_get_smsc (properties)); - mm_sms_part_set_validity (part, mm_sms_properties_get_validity (properties)); - mm_sms_part_set_class (part, mm_sms_properties_get_class (properties)); - mm_sms_part_set_delivery_report_request (part, mm_sms_properties_get_delivery_report_request (properties)); - mm_sms_part_set_concat_reference (part, reference); - mm_sms_part_set_concat_sequence (part, i + 1); - mm_sms_part_set_concat_max (part, n_parts); - - if (!sms) { - mm_dbg ("Building user-created multipart SMS... (%u parts expected)", n_parts); - sms = mm_sms_multipart_new ( - modem, - MM_SMS_STATE_UNKNOWN, - MM_SMS_STORAGE_UNKNOWN, /* not stored anywhere yet */ - reference, - n_parts, - part, - error); - if (!sms) - break; - } else if (!mm_sms_multipart_take_part (sms, part, error)) { - g_clear_object (&sms); - break; - } - - mm_dbg (" Added part '%u' to multipart SMS...", i + 1); - i++; - } - - /* Rewalk the arrays and remove any remaining text/data not set after an error */ - if (split_text) { - for (i = 0; i < n_parts; i++) { - if (split_text[i]) - g_free (split_text[i]); - } - - g_free (split_text); - } - else if (split_data) { - for (i = 0; i < n_parts; i++) { - if (split_data[i]) - g_byte_array_unref (split_data[i]); - } - g_free (split_data); - } - - return sms; - } - - /* Single part it will be */ - part = mm_sms_part_new (SMS_PART_INVALID_INDEX, - MM_SMS_PDU_TYPE_SUBMIT); + /* Create an SMS object as defined by the interface */ + self = mm_iface_modem_messaging_create_sms (MM_IFACE_MODEM_MESSAGING (modem)); + g_object_set (self, + "state", MM_SMS_STATE_UNKNOWN, + "storage", MM_SMS_STORAGE_UNKNOWN, + "number", mm_sms_properties_get_number (properties), + "pdu-type", MM_SMS_PDU_TYPE_SUBMIT, + "text", text, + "data", (data ? + g_variant_new_from_data (G_VARIANT_TYPE ("ay"), + data->data, + data->len * sizeof (guint8), + TRUE, + (GDestroyNotify) g_byte_array_unref, + g_byte_array_ref (data)) : + NULL), + "smsc", mm_sms_properties_get_smsc (properties), + "class", mm_sms_properties_get_class (properties), + "delivery-report-request", mm_sms_properties_get_delivery_report_request (properties), + NULL); - if (split_text) { - mm_sms_part_take_text (part, split_text[0]); - g_free (split_text); - } else if (split_data) { - mm_sms_part_take_data (part, split_data[0]); - g_free (split_data); - } else - g_assert_not_reached (); + /* Only export once properly created */ + mm_sms_export (self); - mm_sms_part_set_encoding (part, encoding); - mm_sms_part_set_number (part, mm_sms_properties_get_number (properties)); - mm_sms_part_set_smsc (part, mm_sms_properties_get_smsc (properties)); - mm_sms_part_set_validity (part, mm_sms_properties_get_validity (properties)); - mm_sms_part_set_class (part, mm_sms_properties_get_class (properties)); - mm_sms_part_set_delivery_report_request (part, mm_sms_properties_get_delivery_report_request (properties)); - - return mm_sms_singlepart_new (modem, - MM_SMS_STATE_UNKNOWN, - MM_SMS_STORAGE_UNKNOWN, /* not stored anywhere yet */ - part, - error); + return self; } /*****************************************************************************/ |