sms: create SMS parts only when storing or sending

When a user creates an SMS object, we will expose all its properties in DBus
properly, but we will not create the internal list of SMS parts.

The list of SMS parts will be created when the SMS is stored or sent, whatever
comes first. When the message is sent and it was previously stored, the list of
parts is not re-created.

If the message requires multiple parts, the multipart reference is computed as
follows:
 * If the SMS was not stored and is being sent, we just use a random number.
 * If the SMS is being stored, we will use a multipart reference which is not
   being used already in another SMS to the same destination.
This commit is contained in:
Aleksander Morgado
2012-09-13 12:00:37 +02:00
parent 2da9474d0c
commit e212f6b6ed
7 changed files with 320 additions and 145 deletions

View File

@@ -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)

View File

@@ -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 */

View File

@@ -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)
{

View File

@@ -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 */

View File

@@ -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));
}
}

View File

@@ -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 */

View File

@@ -70,6 +70,128 @@ struct _MMSmsPrivate {
gboolean is_assembled;
};
/*****************************************************************************/
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) */
@@ -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 ();
/* 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 (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);
/* Only export once properly created */
mm_sms_export (self);
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);
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 ();
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;
}
/*****************************************************************************/