From a29382be70ead81b31a1e7f14ad6fc0984082536 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Sun, 6 Jul 2014 16:20:47 +0200 Subject: sms: rename 'MMSms' to 'MMBaseSms' Just so that we don't have same header names in src/ and /libmm-glib. --- src/Makefile.am | 4 +- src/mm-base-sms.c | 2048 +++++++++++++++++++++++++++++++++++++++ src/mm-base-sms.h | 126 +++ src/mm-broadband-modem-mbim.c | 2 +- src/mm-broadband-modem-qmi.c | 2 +- src/mm-broadband-modem.c | 4 +- src/mm-iface-modem-messaging.c | 12 +- src/mm-iface-modem-messaging.h | 6 +- src/mm-sms-list.c | 80 +- src/mm-sms-list.h | 2 +- src/mm-sms-mbim.c | 48 +- src/mm-sms-mbim.h | 8 +- src/mm-sms-qmi.c | 66 +- src/mm-sms-qmi.h | 8 +- src/mm-sms.c | 2050 ---------------------------------------- src/mm-sms.h | 129 --- 16 files changed, 2295 insertions(+), 2300 deletions(-) create mode 100644 src/mm-base-sms.c create mode 100644 src/mm-base-sms.h delete mode 100644 src/mm-sms.c delete mode 100644 src/mm-sms.h diff --git a/src/Makefile.am b/src/Makefile.am index 15521c73..7e7693f5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -187,8 +187,8 @@ ModemManager_SOURCES = \ mm-base-modem-at.c \ mm-base-modem.h \ mm-base-modem.c \ - mm-sms.h \ - mm-sms.c \ + mm-base-sms.h \ + mm-base-sms.c \ mm-sms-list.h \ mm-sms-list.c \ mm-iface-modem.h \ diff --git a/src/mm-base-sms.c b/src/mm-base-sms.c new file mode 100644 index 00000000..bc38a1c5 --- /dev/null +++ b/src/mm-base-sms.c @@ -0,0 +1,2048 @@ +/* -*- 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) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2012 Red Hat, Inc. + * Copyright (C) 2012 Google, Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include +#define _LIBMM_INSIDE_MM +#include + +#include "mm-base-sms.h" +#include "mm-broadband-modem.h" +#include "mm-iface-modem.h" +#include "mm-iface-modem-messaging.h" +#include "mm-sms-part-3gpp.h" +#include "mm-base-modem-at.h" +#include "mm-base-modem.h" +#include "mm-log.h" +#include "mm-modem-helpers.h" + +G_DEFINE_TYPE (MMBaseSms, mm_base_sms, MM_GDBUS_TYPE_SMS_SKELETON) + +enum { + PROP_0, + PROP_PATH, + PROP_CONNECTION, + PROP_MODEM, + PROP_IS_MULTIPART, + PROP_MAX_PARTS, + PROP_MULTIPART_REFERENCE, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +struct _MMBaseSmsPrivate { + /* The connection to the system bus */ + GDBusConnection *connection; + /* The modem which owns this SMS */ + MMBaseModem *modem; + /* The path where the SMS object is exported */ + gchar *path; + + /* Multipart SMS specific stuff */ + gboolean is_multipart; + guint multipart_reference; + + /* List of SMS parts */ + guint max_parts; + GList *parts; + + /* Set to true when all needed parts were received, + * parsed and assembled */ + gboolean is_assembled; +}; + +/*****************************************************************************/ + +static guint +get_validity_relative (GVariant *tuple) +{ + guint type; + GVariant *value; + guint value_integer = 0; + + if (!tuple) + return 0; + + g_variant_get (tuple, "(uv)", &type, &value); + + if (type == MM_SMS_VALIDITY_TYPE_RELATIVE) + value_integer = g_variant_get_uint32 (value); + + g_variant_unref (value); + + return value_integer; +} + +static gboolean +generate_3gpp_submit_pdus (MMBaseSms *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_3gpp_util_split_text (text, &encoding); + if (!split_text) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Cannot generate PDUs: Error processing input text"); + return FALSE; + } + n_parts = g_strv_length (split_text); + } else if (data) { + encoding = MM_SMS_ENCODING_8BIT; + split_data = mm_sms_part_3gpp_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)); + + if (n_parts > 255) { + if (split_text) + g_strfreev (split_text); + else if (split_data) { + guint i = 0; + + while (split_data[i]) + g_byte_array_unref (split_data[i++]); + g_free (split_data); + } + + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_TOO_MANY, + "Cannot generate PDUs: Text or Data too long"); + return FALSE; + } + + /* 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_relative (part, get_validity_relative (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; +} + +static gboolean +generate_cdma_submit_pdus (MMBaseSms *self, + GError **error) +{ + const gchar *text; + GVariant *data_variant; + const guint8 *data; + gsize data_len = 0; + + MMSmsPart *part; + + 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)); + + /* Create new part */ + part = mm_sms_part_new (SMS_PART_INVALID_INDEX, MM_SMS_PDU_TYPE_CDMA_SUBMIT); + if (text) + mm_sms_part_set_text (part, text); + else if (data) { + GByteArray *part_data; + + part_data = g_byte_array_sized_new (data_len); + g_byte_array_append (part_data, data, data_len); + mm_sms_part_take_data (part, part_data); + } else + g_assert_not_reached (); + mm_sms_part_set_encoding (part, data ? MM_SMS_ENCODING_8BIT : MM_SMS_ENCODING_UNKNOWN); + mm_sms_part_set_number (part, mm_gdbus_sms_get_number (MM_GDBUS_SMS (self))); + + /* If creating a CDMA SMS part but we don't have a Teleservice ID, we default to WMT */ + if (mm_gdbus_sms_get_teleservice_id (MM_GDBUS_SMS (self)) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN) { + mm_dbg ("Defaulting to WMT teleservice ID when creating SMS part"); + mm_sms_part_set_cdma_teleservice_id (part, MM_SMS_CDMA_TELESERVICE_ID_WMT); + } else + mm_sms_part_set_cdma_teleservice_id (part, mm_gdbus_sms_get_teleservice_id (MM_GDBUS_SMS (self))); + + mm_sms_part_set_cdma_service_category (part, mm_gdbus_sms_get_service_category (MM_GDBUS_SMS (self))); + + mm_dbg ("Created SMS part for CDMA SMS"); + + /* Add to the list of parts */ + self->priv->parts = g_list_append (self->priv->parts, part); + + /* No more parts are expected */ + self->priv->is_assembled = TRUE; + + return TRUE; +} + +static gboolean +generate_submit_pdus (MMBaseSms *self, + GError **error) +{ + MMBaseModem *modem; + gboolean is_3gpp; + + /* First; decide which kind of PDU we'll generate, based on the current modem caps */ + + g_object_get (self, + MM_BASE_SMS_MODEM, &modem, + NULL); + g_assert (modem != NULL); + + is_3gpp = mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)); + g_object_unref (modem); + + /* On a 3GPP-capable modem, create always a 3GPP SMS (even if the modem is 3GPP+3GPP2) */ + if (is_3gpp) + return generate_3gpp_submit_pdus (self, error); + + /* Otherwise, create a 3GPP2 SMS */ + return generate_cdma_submit_pdus (self, error); +} + +/*****************************************************************************/ +/* Store SMS (DBus call handling) */ + +typedef struct { + MMBaseSms *self; + MMBaseModem *modem; + GDBusMethodInvocation *invocation; + MMSmsStorage storage; +} HandleStoreContext; + +static void +handle_store_context_free (HandleStoreContext *ctx) +{ + g_object_unref (ctx->invocation); + g_object_unref (ctx->modem); + g_object_unref (ctx->self); + g_free (ctx); +} + +static void +handle_store_ready (MMBaseSms *self, + GAsyncResult *res, + HandleStoreContext *ctx) +{ + GError *error = NULL; + + if (!MM_BASE_SMS_GET_CLASS (self)->store_finish (self, res, &error)) { + /* On error, clear up the parts we generated */ + g_list_free_full (self->priv->parts, (GDestroyNotify)mm_sms_part_free); + self->priv->parts = NULL; + g_dbus_method_invocation_take_error (ctx->invocation, error); + } else { + mm_gdbus_sms_set_storage (MM_GDBUS_SMS (ctx->self), ctx->storage); + + /* Transition from Unknown->Stored for SMS which were created by the user */ + if (mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_UNKNOWN) + mm_gdbus_sms_set_state (MM_GDBUS_SMS (ctx->self), MM_SMS_STATE_STORED); + + mm_gdbus_sms_complete_store (MM_GDBUS_SMS (ctx->self), ctx->invocation); + } + + handle_store_context_free (ctx); +} + +static gboolean +prepare_sms_to_be_stored (MMBaseSms *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. */ + if (self->priv->is_multipart) { + 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, + HandleStoreContext *ctx) +{ + GError *error = NULL; + + if (!mm_base_modem_authorize_finish (modem, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_store_context_free (ctx); + return; + } + + /* First of all, check if we already have the SMS stored. */ + if (mm_base_sms_get_storage (ctx->self) != MM_SMS_STORAGE_UNKNOWN) { + /* Check if SMS stored in some other storage */ + if (mm_base_sms_get_storage (ctx->self) == ctx->storage) + /* Good, same storage */ + mm_gdbus_sms_complete_store (MM_GDBUS_SMS (ctx->self), ctx->invocation); + else + g_dbus_method_invocation_return_error ( + ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "SMS is already stored in storage '%s', cannot store it in storage '%s'", + mm_sms_storage_get_string (mm_base_sms_get_storage (ctx->self)), + mm_sms_storage_get_string (ctx->storage)); + handle_store_context_free (ctx); + return; + } + + /* Check if the requested storage is allowed for storing */ + if (!mm_iface_modem_messaging_is_storage_supported_for_storing (MM_IFACE_MODEM_MESSAGING (ctx->modem), + ctx->storage, + &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_store_context_free (ctx); + 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_BASE_SMS_GET_CLASS (ctx->self)->store || + !MM_BASE_SMS_GET_CLASS (ctx->self)->store_finish) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Storing SMS is not supported by this modem"); + handle_store_context_free (ctx); + return; + } + + MM_BASE_SMS_GET_CLASS (ctx->self)->store (ctx->self, + ctx->storage, + (GAsyncReadyCallback)handle_store_ready, + ctx); +} + +static gboolean +handle_store (MMBaseSms *self, + GDBusMethodInvocation *invocation, + guint32 storage) +{ + HandleStoreContext *ctx; + + ctx = g_new0 (HandleStoreContext, 1); + ctx->self = g_object_ref (self); + ctx->invocation = g_object_ref (invocation); + g_object_get (self, + MM_BASE_SMS_MODEM, &ctx->modem, + NULL); + ctx->storage = (MMSmsStorage)storage; + + if (ctx->storage == MM_SMS_STORAGE_UNKNOWN) { + /* We'll set now the proper storage, taken from the default mem2 one */ + g_object_get (self->priv->modem, + MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, &ctx->storage, + NULL); + g_assert (ctx->storage != MM_SMS_STORAGE_UNKNOWN); + } + + mm_base_modem_authorize (ctx->modem, + invocation, + MM_AUTHORIZATION_MESSAGING, + (GAsyncReadyCallback)handle_store_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ +/* Send SMS (DBus call handling) */ + +typedef struct { + MMBaseSms *self; + MMBaseModem *modem; + GDBusMethodInvocation *invocation; +} HandleSendContext; + +static void +handle_send_context_free (HandleSendContext *ctx) +{ + g_object_unref (ctx->invocation); + g_object_unref (ctx->modem); + g_object_unref (ctx->self); + g_free (ctx); +} + +static void +handle_send_ready (MMBaseSms *self, + GAsyncResult *res, + HandleSendContext *ctx) +{ + GError *error = NULL; + + if (!MM_BASE_SMS_GET_CLASS (self)->send_finish (self, res, &error)) { + /* On error, clear up the parts we generated */ + g_list_free_full (self->priv->parts, (GDestroyNotify)mm_sms_part_free); + self->priv->parts = NULL; + g_dbus_method_invocation_take_error (ctx->invocation, error); + } else { + /* Transition from Unknown->Sent or Stored->Sent */ + if (mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_UNKNOWN || + mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_STORED) { + GList *l; + + /* Update state */ + mm_gdbus_sms_set_state (MM_GDBUS_SMS (ctx->self), MM_SMS_STATE_SENT); + /* Grab last message reference */ + l = g_list_last (mm_base_sms_get_parts (ctx->self)); + mm_gdbus_sms_set_message_reference (MM_GDBUS_SMS (ctx->self), + mm_sms_part_get_message_reference ((MMSmsPart *)l->data)); + } + mm_gdbus_sms_complete_send (MM_GDBUS_SMS (ctx->self), ctx->invocation); + } + + handle_send_context_free (ctx); +} + +static gboolean +prepare_sms_to_be_sent (MMBaseSms *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. */ + if (self->priv->is_multipart) { + 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, + HandleSendContext *ctx) +{ + MMSmsState state; + GError *error = NULL; + + if (!mm_base_modem_authorize_finish (modem, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_send_context_free (ctx); + return; + } + + /* We can only send SMS created by the user */ + state = mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)); + if (state == MM_SMS_STATE_RECEIVED || + state == MM_SMS_STATE_RECEIVING) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "This SMS was received, cannot send it"); + handle_send_context_free (ctx); + return; + } + + /* Don't allow sending the same SMS multiple times, we would lose the message reference */ + if (state == MM_SMS_STATE_SENT) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "This SMS was already sent, cannot send it again"); + handle_send_context_free (ctx); + 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_BASE_SMS_GET_CLASS (ctx->self)->send || + !MM_BASE_SMS_GET_CLASS (ctx->self)->send_finish) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Sending SMS is not supported by this modem"); + handle_send_context_free (ctx); + return; + } + + MM_BASE_SMS_GET_CLASS (ctx->self)->send (ctx->self, + (GAsyncReadyCallback)handle_send_ready, + ctx); +} + +static gboolean +handle_send (MMBaseSms *self, + GDBusMethodInvocation *invocation) +{ + HandleSendContext *ctx; + + ctx = g_new0 (HandleSendContext, 1); + ctx->self = g_object_ref (self); + ctx->invocation = g_object_ref (invocation); + g_object_get (self, + MM_BASE_SMS_MODEM, &ctx->modem, + NULL); + + mm_base_modem_authorize (ctx->modem, + invocation, + MM_AUTHORIZATION_MESSAGING, + (GAsyncReadyCallback)handle_send_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ + +void +mm_base_sms_export (MMBaseSms *self) +{ + static guint id = 0; + gchar *path; + + path = g_strdup_printf (MM_DBUS_SMS_PREFIX "/%d", id++); + g_object_set (self, + MM_BASE_SMS_PATH, path, + NULL); + g_free (path); +} + +void +mm_base_sms_unexport (MMBaseSms *self) +{ + g_object_set (self, + MM_BASE_SMS_PATH, NULL, + NULL); +} + +/*****************************************************************************/ + +static void +sms_dbus_export (MMBaseSms *self) +{ + GError *error = NULL; + + /* Handle method invocations */ + g_signal_connect (self, + "handle-store", + G_CALLBACK (handle_store), + NULL); + g_signal_connect (self, + "handle-send", + G_CALLBACK (handle_send), + NULL); + + if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self), + self->priv->connection, + self->priv->path, + &error)) { + mm_warn ("couldn't export SMS at '%s': '%s'", + self->priv->path, + error->message); + g_error_free (error); + } +} + +static void +sms_dbus_unexport (MMBaseSms *self) +{ + /* Only unexport if currently exported */ + if (g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self))) + g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self)); +} + +/*****************************************************************************/ + +const gchar * +mm_base_sms_get_path (MMBaseSms *self) +{ + return self->priv->path; +} + +MMSmsStorage +mm_base_sms_get_storage (MMBaseSms *self) +{ + return mm_gdbus_sms_get_storage (MM_GDBUS_SMS (self)); +} + +gboolean +mm_base_sms_is_multipart (MMBaseSms *self) +{ + return self->priv->is_multipart; +} + +guint +mm_base_sms_get_multipart_reference (MMBaseSms *self) +{ + g_return_val_if_fail (self->priv->is_multipart, 0); + + return self->priv->multipart_reference; +} + +gboolean +mm_base_sms_multipart_is_complete (MMBaseSms *self) +{ + return (g_list_length (self->priv->parts) == self->priv->max_parts); +} + +gboolean +mm_base_sms_multipart_is_assembled (MMBaseSms *self) +{ + return self->priv->is_assembled; +} + +/*****************************************************************************/ + +static guint +cmp_sms_part_index (MMSmsPart *part, + gpointer user_data) +{ + return (GPOINTER_TO_UINT (user_data) - mm_sms_part_get_index (part)); +} + +gboolean +mm_base_sms_has_part_index (MMBaseSms *self, + guint index) +{ + return !!g_list_find_custom (self->priv->parts, + GUINT_TO_POINTER (index), + (GCompareFunc)cmp_sms_part_index); +} + +GList * +mm_base_sms_get_parts (MMBaseSms *self) +{ + return self->priv->parts; +} + +/*****************************************************************************/ + +static gboolean +sms_get_store_or_send_command (MMSmsPart *part, + gboolean text_or_pdu, /* TRUE for PDU */ + gboolean store_or_send, /* TRUE for send */ + gchar **out_cmd, + gchar **out_msg_data, + GError **error) +{ + g_assert (out_cmd != NULL); + g_assert (out_msg_data != NULL); + + if (!text_or_pdu) { + /* Text mode */ + *out_cmd = g_strdup_printf ("+CMG%c=\"%s\"", + store_or_send ? 'S' : 'W', + mm_sms_part_get_number (part)); + *out_msg_data = g_strdup_printf ("%s\x1a", mm_sms_part_get_text (part)); + } else { + guint8 *pdu; + guint pdulen = 0; + guint msgstart = 0; + gchar *hex; + + /* AT+CMGW=[, ] PDU can be entered. / */ + + pdu = mm_sms_part_3gpp_get_submit_pdu (part, &pdulen, &msgstart, error); + if (!pdu) + /* 'error' should already be set */ + return FALSE; + + /* Convert PDU to hex */ + hex = mm_utils_bin2hexstr (pdu, pdulen); + g_free (pdu); + + if (!hex) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Not enough memory to send SMS PDU"); + return FALSE; + } + + /* CMGW/S length is the size of the PDU without SMSC information */ + *out_cmd = g_strdup_printf ("+CMG%c=%d", + store_or_send ? 'S' : 'W', + pdulen - msgstart); + *out_msg_data = g_strdup_printf ("%s\x1a", hex); + g_free (hex); + } + + return TRUE; +} + +/*****************************************************************************/ +/* Store the SMS */ + +typedef struct { + MMBaseSms *self; + MMBaseModem *modem; + GSimpleAsyncResult *result; + MMSmsStorage storage; + gboolean need_unlock; + gboolean use_pdu_mode; + GList *current; + gchar *msg_data; +} SmsStoreContext; + +static void +sms_store_context_complete_and_free (SmsStoreContext *ctx) +{ + g_simple_async_result_complete (ctx->result); + g_object_unref (ctx->result); + /* Unlock mem2 storage if we had the lock */ + if (ctx->need_unlock) + mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), FALSE, TRUE); + g_object_unref (ctx->modem); + g_object_unref (ctx->self); + g_free (ctx->msg_data); + g_free (ctx); +} + +static gboolean +sms_store_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void sms_store_next_part (SmsStoreContext *ctx); + +static void +store_msg_data_ready (MMBaseModem *modem, + GAsyncResult *res, + SmsStoreContext *ctx) +{ + const gchar *response; + GError *error = NULL; + gint rv; + gint idx; + + response = mm_base_modem_at_command_finish (modem, res, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + sms_store_context_complete_and_free (ctx); + return; + } + + /* Read the new part index from the reply */ + rv = sscanf (response, "+CMGW: %d", &idx); + if (rv != 1 || idx < 0) { + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't read index of already stored part: " + "%d fields parsed", + rv); + sms_store_context_complete_and_free (ctx); + return; + } + + /* Set the index in the part we hold */ + mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, (guint)idx); + + ctx->current = g_list_next (ctx->current); + sms_store_next_part (ctx); +} + +static void +store_ready (MMBaseModem *modem, + GAsyncResult *res, + SmsStoreContext *ctx) +{ + const gchar *response; + GError *error = NULL; + + response = mm_base_modem_at_command_finish (modem, res, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + sms_store_context_complete_and_free (ctx); + return; + } + + /* Send the actual message data */ + mm_base_modem_at_command_raw (ctx->modem, + ctx->msg_data, + 10, + FALSE, + (GAsyncReadyCallback)store_msg_data_ready, + ctx); +} + +static void +sms_store_next_part (SmsStoreContext *ctx) +{ + gchar *cmd; + GError *error = NULL; + + if (!ctx->current) { + /* Done we are */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + sms_store_context_complete_and_free (ctx); + return; + } + + if (ctx->msg_data) { + g_free (ctx->msg_data); + ctx->msg_data = NULL; + } + + if (!sms_get_store_or_send_command ((MMSmsPart *)ctx->current->data, + ctx->use_pdu_mode, + FALSE, + &cmd, + &ctx->msg_data, + &error)) { + g_simple_async_result_take_error (ctx->result, error); + sms_store_context_complete_and_free (ctx); + return; + } + + g_assert (cmd != NULL); + g_assert (ctx->msg_data != NULL); + + mm_base_modem_at_command (ctx->modem, + cmd, + 10, + FALSE, + (GAsyncReadyCallback)store_ready, + ctx); + g_free (cmd); +} + +static void +store_lock_sms_storages_ready (MMBroadbandModem *modem, + GAsyncResult *res, + SmsStoreContext *ctx) +{ + GError *error = NULL; + + if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { + g_simple_async_result_take_error (ctx->result, error); + sms_store_context_complete_and_free (ctx); + return; + } + + /* We are now locked. Whatever result we have here, we need to make sure + * we unlock the storages before finishing. */ + ctx->need_unlock = TRUE; + + /* Go on to store the parts */ + ctx->current = ctx->self->priv->parts; + sms_store_next_part (ctx); +} + +static void +sms_store (MMBaseSms *self, + MMSmsStorage storage, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SmsStoreContext *ctx; + + /* Setup the context */ + ctx = g_new0 (SmsStoreContext, 1); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + sms_store); + ctx->self = g_object_ref (self); + ctx->modem = g_object_ref (self->priv->modem); + ctx->storage = storage; + + /* Different ways to do it if on PDU or text mode */ + g_object_get (self->priv->modem, + MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, + NULL); + + /* First, lock storage to use */ + g_assert (MM_IS_BROADBAND_MODEM (self->priv->modem)); + mm_broadband_modem_lock_sms_storages ( + MM_BROADBAND_MODEM (self->priv->modem), + MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ + ctx->storage, + (GAsyncReadyCallback)store_lock_sms_storages_ready, + ctx); +} + +/*****************************************************************************/ +/* Send the SMS */ + +typedef struct { + MMBaseSms *self; + MMBaseModem *modem; + GSimpleAsyncResult *result; + gboolean need_unlock; + gboolean from_storage; + gboolean use_pdu_mode; + GList *current; + gchar *msg_data; +} SmsSendContext; + +static void +sms_send_context_complete_and_free (SmsSendContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->result); + /* Unlock mem2 storage if we had the lock */ + if (ctx->need_unlock) + mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), FALSE, TRUE); + g_object_unref (ctx->modem); + g_object_unref (ctx->self); + g_free (ctx->msg_data); + g_free (ctx); +} + +static gboolean +sms_send_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void sms_send_next_part (SmsSendContext *ctx); + +static gint +read_message_reference_from_reply (const gchar *response, + GError **error) +{ + gint rv = 0; + gint idx = -1; + + if (strstr (response, "+CMGS")) + rv = sscanf (strstr (response, "+CMGS"), "+CMGS: %d", &idx); + else if (strstr (response, "+CMSS")) + rv = sscanf (strstr (response, "+CMSS"), "+CMSS: %d", &idx); + + if (rv != 1 || idx < 0) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't read message reference: " + "%d fields parsed from response '%s'", + rv, response); + return -1; + } + + return idx; +} + +static void +send_generic_msg_data_ready (MMBaseModem *modem, + GAsyncResult *res, + SmsSendContext *ctx) +{ + GError *error = NULL; + const gchar *response; + gint message_reference; + + response = mm_base_modem_at_command_finish (modem, res, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + sms_send_context_complete_and_free (ctx); + return; + } + + message_reference = read_message_reference_from_reply (response, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + sms_send_context_complete_and_free (ctx); + return; + } + + mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, + (guint)message_reference); + + ctx->current = g_list_next (ctx->current); + sms_send_next_part (ctx); +} + +static void +send_generic_ready (MMBaseModem *modem, + GAsyncResult *res, + SmsSendContext *ctx) +{ + GError *error = NULL; + + mm_base_modem_at_command_finish (modem, res, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + sms_send_context_complete_and_free (ctx); + return; + } + + /* Send the actual message data */ + mm_base_modem_at_command_raw (ctx->modem, + ctx->msg_data, + 10, + FALSE, + (GAsyncReadyCallback)send_generic_msg_data_ready, + ctx); +} + +static void +send_from_storage_ready (MMBaseModem *modem, + GAsyncResult *res, + SmsSendContext *ctx) +{ + GError *error = NULL; + const gchar *response; + gint message_reference; + + response = mm_base_modem_at_command_finish (modem, res, &error); + if (error) { + if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { + g_simple_async_result_take_error (ctx->result, error); + sms_send_context_complete_and_free (ctx); + return; + } + + mm_dbg ("Couldn't send SMS from storage: '%s'; trying generic send...", + error->message); + g_error_free (error); + + ctx->from_storage = FALSE; + sms_send_next_part (ctx); + return; + } + + message_reference = read_message_reference_from_reply (response, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + sms_send_context_complete_and_free (ctx); + return; + } + + mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, + (guint)message_reference); + + ctx->current = g_list_next (ctx->current); + sms_send_next_part (ctx); +} + +static void +sms_send_next_part (SmsSendContext *ctx) +{ + GError *error = NULL; + gchar *cmd; + + if (!ctx->current) { + /* Done we are */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + sms_send_context_complete_and_free (ctx); + return; + } + + /* Send from storage */ + if (ctx->from_storage) { + cmd = g_strdup_printf ("+CMSS=%d", + mm_sms_part_get_index ((MMSmsPart *)ctx->current->data)); + mm_base_modem_at_command (ctx->modem, + cmd, + 30, + FALSE, + (GAsyncReadyCallback)send_from_storage_ready, + ctx); + g_free (cmd); + return; + } + + /* Generic send */ + + if (ctx->msg_data) { + g_free (ctx->msg_data); + ctx->msg_data = NULL; + } + + if (!sms_get_store_or_send_command ((MMSmsPart *)ctx->current->data, + ctx->use_pdu_mode, + TRUE, + &cmd, + &ctx->msg_data, + &error)) { + g_simple_async_result_take_error (ctx->result, error); + sms_send_context_complete_and_free (ctx); + return; + } + + g_assert (cmd != NULL); + g_assert (ctx->msg_data != NULL); + mm_base_modem_at_command (ctx->modem, + cmd, + 30, + FALSE, + (GAsyncReadyCallback)send_generic_ready, + ctx); + g_free (cmd); +} + +static void +send_lock_sms_storages_ready (MMBroadbandModem *modem, + GAsyncResult *res, + SmsSendContext *ctx) +{ + GError *error = NULL; + + if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { + g_simple_async_result_take_error (ctx->result, error); + sms_send_context_complete_and_free (ctx); + return; + } + + /* We are now locked. Whatever result we have here, we need to make sure + * we unlock the storages before finishing. */ + ctx->need_unlock = TRUE; + + /* Go on to send the parts */ + ctx->current = ctx->self->priv->parts; + sms_send_next_part (ctx); +} + +static void +sms_send (MMBaseSms *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SmsSendContext *ctx; + + /* Setup the context */ + ctx = g_new0 (SmsSendContext, 1); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + sms_send); + ctx->self = g_object_ref (self); + ctx->modem = g_object_ref (self->priv->modem); + + /* If the SMS is STORED, try to send from storage */ + ctx->from_storage = (mm_base_sms_get_storage (self) != MM_SMS_STORAGE_UNKNOWN); + if (ctx->from_storage) { + /* When sending from storage, first lock storage to use */ + g_assert (MM_IS_BROADBAND_MODEM (self->priv->modem)); + mm_broadband_modem_lock_sms_storages ( + MM_BROADBAND_MODEM (self->priv->modem), + MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ + mm_base_sms_get_storage (self), + (GAsyncReadyCallback)send_lock_sms_storages_ready, + ctx); + return; + } + + /* Different ways to do it if on PDU or text mode */ + g_object_get (self->priv->modem, + MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, + NULL); + ctx->current = self->priv->parts; + sms_send_next_part (ctx); +} + +/*****************************************************************************/ + +typedef struct { + MMBaseSms *self; + MMBaseModem *modem; + GSimpleAsyncResult *result; + gboolean need_unlock; + GList *current; + guint n_failed; +} SmsDeletePartsContext; + +static void +sms_delete_parts_context_complete_and_free (SmsDeletePartsContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->result); + /* Unlock mem1 storage if we had the lock */ + if (ctx->need_unlock) + mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), TRUE, FALSE); + g_object_unref (ctx->modem); + g_object_unref (ctx->self); + g_free (ctx); +} + +static gboolean +sms_delete_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void delete_next_part (SmsDeletePartsContext *ctx); + +static void +delete_part_ready (MMBaseModem *modem, + GAsyncResult *res, + SmsDeletePartsContext *ctx) +{ + GError *error = NULL; + + mm_base_modem_at_command_finish (modem, res, &error); + if (error) { + ctx->n_failed++; + mm_dbg ("Couldn't delete SMS part with index %u: '%s'", + mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), + error->message); + g_error_free (error); + } + + /* We reset the index, as there is no longer that part */ + mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, SMS_PART_INVALID_INDEX); + + ctx->current = g_list_next (ctx->current); + delete_next_part (ctx); +} + +static void +delete_next_part (SmsDeletePartsContext *ctx) +{ + gchar *cmd; + + /* Skip non-stored parts */ + while (ctx->current && + mm_sms_part_get_index ((MMSmsPart *)ctx->current->data) == SMS_PART_INVALID_INDEX) + ctx->current = g_list_next (ctx->current); + + /* If all removed, we're done */ + if (!ctx->current) { + if (ctx->n_failed > 0) + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't delete %u parts from this SMS", + ctx->n_failed); + else + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + + sms_delete_parts_context_complete_and_free (ctx); + return; + } + + cmd = g_strdup_printf ("+CMGD=%d", + mm_sms_part_get_index ((MMSmsPart *)ctx->current->data)); + mm_base_modem_at_command (ctx->modem, + cmd, + 10, + FALSE, + (GAsyncReadyCallback)delete_part_ready, + ctx); + g_free (cmd); +} + +static void +delete_lock_sms_storages_ready (MMBroadbandModem *modem, + GAsyncResult *res, + SmsDeletePartsContext *ctx) +{ + GError *error = NULL; + + if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { + g_simple_async_result_take_error (ctx->result, error); + sms_delete_parts_context_complete_and_free (ctx); + return; + } + + /* We are now locked. Whatever result we have here, we need to make sure + * we unlock the storages before finishing. */ + ctx->need_unlock = TRUE; + + /* Go on deleting parts */ + ctx->current = ctx->self->priv->parts; + delete_next_part (ctx); +} + +static void +sms_delete (MMBaseSms *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SmsDeletePartsContext *ctx; + + ctx = g_new0 (SmsDeletePartsContext, 1); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + sms_delete); + ctx->self = g_object_ref (self); + ctx->modem = g_object_ref (self->priv->modem); + + if (mm_base_sms_get_storage (self) == MM_SMS_STORAGE_UNKNOWN) { + mm_dbg ("Not removing parts from non-stored SMS"); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + sms_delete_parts_context_complete_and_free (ctx); + return; + } + + /* Select specific storage to delete from */ + mm_broadband_modem_lock_sms_storages ( + MM_BROADBAND_MODEM (self->priv->modem), + mm_base_sms_get_storage (self), + MM_SMS_STORAGE_UNKNOWN, /* none required for mem2 */ + (GAsyncReadyCallback)delete_lock_sms_storages_ready, + ctx); +} + +/*****************************************************************************/ + +gboolean +mm_base_sms_delete_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error) +{ + if (MM_BASE_SMS_GET_CLASS (self)->delete_finish) { + gboolean deleted; + + deleted = MM_BASE_SMS_GET_CLASS (self)->delete_finish (self, res, error); + if (deleted) + /* We do change the state of this SMS back to UNKNOWN, as it is no + * longer stored in the device */ + mm_gdbus_sms_set_state (MM_GDBUS_SMS (self), MM_SMS_STATE_UNKNOWN); + + return deleted; + } + + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +void +mm_base_sms_delete (MMBaseSms *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + if (MM_BASE_SMS_GET_CLASS (self)->delete && + MM_BASE_SMS_GET_CLASS (self)->delete_finish) { + MM_BASE_SMS_GET_CLASS (self)->delete (self, callback, user_data); + return; + } + + g_simple_async_report_error_in_idle (G_OBJECT (self), + callback, + user_data, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Deleting SMS is not supported by this modem"); +} + +/*****************************************************************************/ + +static gboolean +assemble_sms (MMBaseSms *self, + GError **error) +{ + GList *l; + guint idx; + MMSmsPart **sorted_parts; + GString *fulltext; + GByteArray *fulldata; + guint validity_relative; + + sorted_parts = g_new0 (MMSmsPart *, self->priv->max_parts); + + /* Note that sequence in multipart messages start with '1', while singlepart + * messages have '0' as sequence. */ + + if (self->priv->max_parts == 1) { + if (g_list_length (self->priv->parts) != 1) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Single part message with multiple parts (%u) found", + g_list_length (self->priv->parts)); + g_free (sorted_parts); + return FALSE; + } + + sorted_parts[0] = (MMSmsPart *)self->priv->parts->data; + } else { + /* Check if we have duplicate parts */ + for (l = self->priv->parts; l; l = g_list_next (l)) { + idx = mm_sms_part_get_concat_sequence ((MMSmsPart *)l->data); + + if (idx < 1 || idx > self->priv->max_parts) { + mm_warn ("Invalid part index (%u) found, ignoring", idx); + continue; + } + + if (sorted_parts[idx - 1]) { + mm_warn ("Duplicate part index (%u) found, ignoring", idx); + continue; + } + + /* Add the part to the proper index */ + sorted_parts[idx - 1] = (MMSmsPart *)l->data; + } + } + + fulltext = g_string_new (""); + fulldata = g_byte_array_sized_new (160 * self->priv->max_parts); + + /* Assemble text and data from all parts. Now 'idx' is the index of the + * array, so for multipart messages the real index of the part is 'idx + 1' + */ + for (idx = 0; idx < self->priv->max_parts; idx++) { + const gchar *parttext; + const GByteArray *partdata; + + if (!sorted_parts[idx]) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Cannot assemble SMS, missing part at index (%u)", + self->priv->max_parts == 1 ? idx : idx + 1); + g_string_free (fulltext, TRUE); + g_byte_array_free (fulldata, TRUE); + g_free (sorted_parts); + return FALSE; + } + + /* When the user creates the SMS, it will have either 'text' or 'data', + * not both. Also status report PDUs may not have neither text nor data. */ + parttext = mm_sms_part_get_text (sorted_parts[idx]); + partdata = mm_sms_part_get_data (sorted_parts[idx]); + + if (!parttext && !partdata && + mm_sms_part_get_pdu_type (sorted_parts[idx]) != MM_SMS_PDU_TYPE_STATUS_REPORT) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Cannot assemble SMS, part at index (%u) has neither text nor data", + self->priv->max_parts == 1 ? idx : idx + 1); + g_string_free (fulltext, TRUE); + g_byte_array_free (fulldata, TRUE); + g_free (sorted_parts); + return FALSE; + } + + if (parttext) + g_string_append (fulltext, parttext); + if (partdata) + g_byte_array_append (fulldata, partdata->data, partdata->len); + } + + /* If we got all parts, we also have the first one always */ + g_assert (sorted_parts[0] != NULL); + + /* Prepare for validity tuple */ + validity_relative = mm_sms_part_get_validity_relative (sorted_parts[0]); + + /* If we got everything, assemble the text! */ + g_object_set (self, + "pdu-type", mm_sms_part_get_pdu_type (sorted_parts[0]), + "text", fulltext->str, + "data", g_variant_new_from_data (G_VARIANT_TYPE ("ay"), + fulldata->data, + fulldata->len * sizeof (guint8), + TRUE, + (GDestroyNotify) g_byte_array_unref, + g_byte_array_ref (fulldata)), + "smsc", mm_sms_part_get_smsc (sorted_parts[0]), + "class", mm_sms_part_get_class (sorted_parts[0]), + "teleservice-id", mm_sms_part_get_cdma_teleservice_id (sorted_parts[0]), + "service-category", mm_sms_part_get_cdma_service_category (sorted_parts[0]), + "number", mm_sms_part_get_number (sorted_parts[0]), + "validity", (validity_relative ? + g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (validity_relative)) : + g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))), + "timestamp", mm_sms_part_get_timestamp (sorted_parts[0]), + "discharge-timestamp", mm_sms_part_get_discharge_timestamp (sorted_parts[0]), + "delivery-state", mm_sms_part_get_delivery_state (sorted_parts[0]), + /* delivery report request and message reference taken always from the last part */ + "message-reference", mm_sms_part_get_message_reference (sorted_parts[self->priv->max_parts - 1]), + "delivery-report-request", mm_sms_part_get_delivery_report_request (sorted_parts[self->priv->max_parts - 1]), + NULL); + + g_string_free (fulltext, TRUE); + g_byte_array_unref (fulldata); + g_free (sorted_parts); + + self->priv->is_assembled = TRUE; + + return TRUE; +} + +/*****************************************************************************/ + +static guint +cmp_sms_part_sequence (MMSmsPart *a, + MMSmsPart *b) +{ + return (mm_sms_part_get_concat_sequence (a) - mm_sms_part_get_concat_sequence (b)); +} + +gboolean +mm_base_sms_multipart_take_part (MMBaseSms *self, + MMSmsPart *part, + GError **error) +{ + if (!self->priv->is_multipart) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "This SMS is not a multipart message"); + return FALSE; + } + + if (g_list_length (self->priv->parts) >= self->priv->max_parts) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Already took %u parts, cannot take more", + g_list_length (self->priv->parts)); + return FALSE; + } + + if (g_list_find_custom (self->priv->parts, + part, + (GCompareFunc)cmp_sms_part_sequence)) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Cannot take part, sequence %u already taken", + mm_sms_part_get_concat_sequence (part)); + return FALSE; + } + + if (mm_sms_part_get_concat_sequence (part) > self->priv->max_parts) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Cannot take part with sequence %u, maximum is %u", + mm_sms_part_get_concat_sequence (part), + self->priv->max_parts); + return FALSE; + } + + /* Insert sorted by concat sequence */ + self->priv->parts = g_list_insert_sorted (self->priv->parts, + part, + (GCompareFunc)cmp_sms_part_sequence); + + /* We only populate contents when the multipart SMS is complete */ + if (mm_base_sms_multipart_is_complete (self)) { + GError *inner_error = NULL; + + if (!assemble_sms (self, &inner_error)) { + /* We DO NOT propagate the error. The part was properly taken + * so ownership passed to the MMBaseSms object. */ + mm_warn ("Couldn't assemble SMS: '%s'", + inner_error->message); + g_error_free (inner_error); + } else { + /* Completed AND assembled + * Change state RECEIVING->RECEIVED, and signal completeness */ + if (mm_gdbus_sms_get_state (MM_GDBUS_SMS (self)) == MM_SMS_STATE_RECEIVING) + mm_gdbus_sms_set_state (MM_GDBUS_SMS (self), MM_SMS_STATE_RECEIVED); + } + } + + return TRUE; +} + +MMBaseSms * +mm_base_sms_new (MMBaseModem *modem) +{ + return MM_BASE_SMS (g_object_new (MM_TYPE_BASE_SMS, + MM_BASE_SMS_MODEM, modem, + NULL)); +} + +MMBaseSms * +mm_base_sms_singlepart_new (MMBaseModem *modem, + MMSmsState state, + MMSmsStorage storage, + MMSmsPart *part, + GError **error) +{ + MMBaseSms *self; + + g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); + + /* 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", state, + "storage", storage, + NULL); + + /* Keep the single part in the list */ + self->priv->parts = g_list_prepend (self->priv->parts, part); + + if (!assemble_sms (self, error)) { + /* Note: we need to remove the part from the list, as we really didn't + * take it, and therefore the caller is responsible for freeing it. */ + self->priv->parts = g_list_remove (self->priv->parts, part); + g_clear_object (&self); + } else + /* Only export once properly created */ + mm_base_sms_export (self); + + return self; +} + +MMBaseSms * +mm_base_sms_multipart_new (MMBaseModem *modem, + MMSmsState state, + MMSmsStorage storage, + guint reference, + guint max_parts, + MMSmsPart *first_part, + GError **error) +{ + MMBaseSms *self; + + g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); + + /* If this is the first part of a RECEIVED SMS, we overwrite the state + * as RECEIVING, to indicate that it is not completed yet. */ + if (state == MM_SMS_STATE_RECEIVED) + state = MM_SMS_STATE_RECEIVING; + + /* 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, + MM_BASE_SMS_IS_MULTIPART, TRUE, + MM_BASE_SMS_MAX_PARTS, max_parts, + MM_BASE_SMS_MULTIPART_REFERENCE, reference, + "state", state, + "storage", storage, + "validity", g_variant_new ("(uv)", + MM_SMS_VALIDITY_TYPE_UNKNOWN, + g_variant_new_boolean (FALSE)), + NULL); + + if (!mm_base_sms_multipart_take_part (self, first_part, error)) + g_clear_object (&self); + + /* We do export uncomplete multipart messages, in order to be able to + * request removal of all parts of those multipart SMS that will never + * get completed. + * Only the STATE of the SMS object will be valid in the exported DBus + * interface.*/ + if (self) + mm_base_sms_export (self); + + return self; +} + +MMBaseSms * +mm_base_sms_new_from_properties (MMBaseModem *modem, + MMSmsProperties *properties, + GError **error) +{ + MMBaseSms *self; + const gchar *text; + GByteArray *data; + + g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); + + text = mm_sms_properties_get_text (properties); + data = mm_sms_properties_peek_data_bytearray (properties); + + /* Don't create SMS from properties if either (text|data) or number is missing */ + if (!mm_sms_properties_get_number (properties) || + (!text && !data)) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Cannot create SMS: mandatory parameter '%s' is missing", + (!mm_sms_properties_get_number (properties)? + "number" : "text' or 'data")); + return NULL; + } + + /* Don't create SMS from properties if both text and data are given */ + if (text && data) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Cannot create SMS: both 'text' and 'data' given"); + return NULL; + } + + /* 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_properties_get_teleservice_id (properties) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN ? + MM_SMS_PDU_TYPE_SUBMIT : + MM_SMS_PDU_TYPE_CDMA_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), + "teleservice-id", mm_sms_properties_get_teleservice_id (properties), + "service-category", mm_sms_properties_get_service_category (properties), + "delivery-report-request", mm_sms_properties_get_delivery_report_request (properties), + "delivery-state", MM_SMS_DELIVERY_STATE_UNKNOWN, + "validity", (mm_sms_properties_get_validity_type (properties) == MM_SMS_VALIDITY_TYPE_RELATIVE ? + g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (mm_sms_properties_get_validity_relative (properties))) : + g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))), + NULL); + + /* Only export once properly created */ + mm_base_sms_export (self); + + return self; +} + +/*****************************************************************************/ + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MMBaseSms *self = MM_BASE_SMS (object); + + switch (prop_id) { + case PROP_PATH: + g_free (self->priv->path); + self->priv->path = g_value_dup_string (value); + + /* Export when we get a DBus connection AND we have a path */ + if (!self->priv->path) + sms_dbus_unexport (self); + else if (self->priv->connection) + sms_dbus_export (self); + break; + case PROP_CONNECTION: + g_clear_object (&self->priv->connection); + self->priv->connection = g_value_dup_object (value); + + /* Export when we get a DBus connection AND we have a path */ + if (!self->priv->connection) + sms_dbus_unexport (self); + else if (self->priv->path) + sms_dbus_export (self); + break; + case PROP_MODEM: + g_clear_object (&self->priv->modem); + self->priv->modem = g_value_dup_object (value); + if (self->priv->modem) { + /* Bind the modem's connection (which is set when it is exported, + * and unset when unexported) to the SMS's connection */ + g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION, + self, MM_BASE_SMS_CONNECTION, + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + } + break; + case PROP_IS_MULTIPART: + self->priv->is_multipart = g_value_get_boolean (value); + break; + case PROP_MAX_PARTS: + self->priv->max_parts = g_value_get_uint (value); + break; + case PROP_MULTIPART_REFERENCE: + self->priv->multipart_reference = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MMBaseSms *self = MM_BASE_SMS (object); + + switch (prop_id) { + case PROP_PATH: + g_value_set_string (value, self->priv->path); + break; + case PROP_CONNECTION: + g_value_set_object (value, self->priv->connection); + break; + case PROP_MODEM: + g_value_set_object (value, self->priv->modem); + break; + case PROP_IS_MULTIPART: + g_value_set_boolean (value, self->priv->is_multipart); + break; + case PROP_MAX_PARTS: + g_value_set_uint (value, self->priv->max_parts); + break; + case PROP_MULTIPART_REFERENCE: + g_value_set_uint (value, self->priv->multipart_reference); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +mm_base_sms_init (MMBaseSms *self) +{ + /* Initialize private data */ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_SMS, MMBaseSmsPrivate); + /* Defaults */ + self->priv->max_parts = 1; +} + +static void +finalize (GObject *object) +{ + MMBaseSms *self = MM_BASE_SMS (object); + + g_list_free_full (self->priv->parts, (GDestroyNotify)mm_sms_part_free); + g_free (self->priv->path); + + G_OBJECT_CLASS (mm_base_sms_parent_class)->finalize (object); +} + +static void +dispose (GObject *object) +{ + MMBaseSms *self = MM_BASE_SMS (object); + + if (self->priv->connection) { + /* If we arrived here with a valid connection, make sure we unexport + * the object */ + sms_dbus_unexport (self); + g_clear_object (&self->priv->connection); + } + + g_clear_object (&self->priv->modem); + + G_OBJECT_CLASS (mm_base_sms_parent_class)->dispose (object); +} + +static void +mm_base_sms_class_init (MMBaseSmsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMBaseSmsPrivate)); + + /* Virtual methods */ + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + object_class->dispose = dispose; + + klass->store = sms_store; + klass->store_finish = sms_store_finish; + klass->send = sms_send; + klass->send_finish = sms_send_finish; + klass->delete = sms_delete; + klass->delete_finish = sms_delete_finish; + + properties[PROP_CONNECTION] = + g_param_spec_object (MM_BASE_SMS_CONNECTION, + "Connection", + "GDBus connection to the system bus.", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]); + + properties[PROP_PATH] = + g_param_spec_string (MM_BASE_SMS_PATH, + "Path", + "DBus path of the SMS", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]); + + properties[PROP_MODEM] = + g_param_spec_object (MM_BASE_SMS_MODEM, + "Modem", + "The Modem which owns this SMS", + MM_TYPE_BASE_MODEM, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); + + properties[PROP_IS_MULTIPART] = + g_param_spec_boolean (MM_BASE_SMS_IS_MULTIPART, + "Is multipart", + "Flag specifying if the SMS is multipart", + FALSE, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_IS_MULTIPART, properties[PROP_IS_MULTIPART]); + + properties[PROP_MAX_PARTS] = + g_param_spec_uint (MM_BASE_SMS_MAX_PARTS, + "Max parts", + "Maximum number of parts composing this SMS", + 1,255, 1, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_MAX_PARTS, properties[PROP_MAX_PARTS]); + + properties[PROP_MULTIPART_REFERENCE] = + g_param_spec_uint (MM_BASE_SMS_MULTIPART_REFERENCE, + "Multipart reference", + "Common reference for all parts in the multipart SMS", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_MULTIPART_REFERENCE, properties[PROP_MULTIPART_REFERENCE]); +} diff --git a/src/mm-base-sms.h b/src/mm-base-sms.h new file mode 100644 index 00000000..0d4b5bc5 --- /dev/null +++ b/src/mm-base-sms.h @@ -0,0 +1,126 @@ +/* -*- 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: + * + * Author: Aleksander Morgado + * + * Copyright (C) 2012 Google, Inc. + */ + +#ifndef MM_BASE_SMS_H +#define MM_BASE_SMS_H + +#include +#include + +#define _LIBMM_INSIDE_MM +#include + +#include "mm-sms-part.h" +#include "mm-base-modem.h" + +#define MM_TYPE_BASE_SMS (mm_base_sms_get_type ()) +#define MM_BASE_SMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_SMS, MMBaseSms)) +#define MM_BASE_SMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_SMS, MMBaseSmsClass)) +#define MM_IS_BASE_SMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BASE_SMS)) +#define MM_IS_BASE_SMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BASE_SMS)) +#define MM_BASE_SMS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_SMS, MMBaseSmsClass)) + +typedef struct _MMBaseSms MMBaseSms; +typedef struct _MMBaseSmsClass MMBaseSmsClass; +typedef struct _MMBaseSmsPrivate MMBaseSmsPrivate; + +#define MM_BASE_SMS_PATH "sms-path" +#define MM_BASE_SMS_CONNECTION "sms-connection" +#define MM_BASE_SMS_MODEM "sms-modem" +#define MM_BASE_SMS_IS_MULTIPART "sms-is-multipart" +#define MM_BASE_SMS_MAX_PARTS "sms-max-parts" +#define MM_BASE_SMS_MULTIPART_REFERENCE "sms-multipart-reference" + +struct _MMBaseSms { + MmGdbusSmsSkeleton parent; + MMBaseSmsPrivate *priv; +}; + +struct _MMBaseSmsClass { + MmGdbusSmsSkeletonClass parent; + + /* Store the SMS */ + void (* store) (MMBaseSms *self, + MMSmsStorage storage, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* store_finish) (MMBaseSms *self, + GAsyncResult *res, + GError **error); + + /* Send the SMS */ + void (* send) (MMBaseSms *self, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* send_finish) (MMBaseSms *self, + GAsyncResult *res, + GError **error); + + /* Delete the SMS */ + void (* delete) (MMBaseSms *self, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* delete_finish) (MMBaseSms *self, + GAsyncResult *res, + GError **error); +}; + +GType mm_base_sms_get_type (void); + +/* This one can be overriden by plugins */ +MMBaseSms *mm_base_sms_new (MMBaseModem *modem); +MMBaseSms *mm_base_sms_new_from_properties (MMBaseModem *modem, + MMSmsProperties *properties, + GError **error); +MMBaseSms *mm_base_sms_singlepart_new (MMBaseModem *modem, + MMSmsState state, + MMSmsStorage storage, + MMSmsPart *part, + GError **error); +MMBaseSms *mm_base_sms_multipart_new (MMBaseModem *modem, + MMSmsState state, + MMSmsStorage storage, + guint reference, + guint max_parts, + MMSmsPart *first_part, + GError **error); +gboolean mm_base_sms_multipart_take_part (MMBaseSms *self, + MMSmsPart *part, + GError **error); + +void mm_base_sms_export (MMBaseSms *self); +void mm_base_sms_unexport (MMBaseSms *self); +const gchar *mm_base_sms_get_path (MMBaseSms *self); +MMSmsStorage mm_base_sms_get_storage (MMBaseSms *self); + +gboolean mm_base_sms_has_part_index (MMBaseSms *self, + guint index); +GList *mm_base_sms_get_parts (MMBaseSms *self); + +gboolean mm_base_sms_is_multipart (MMBaseSms *self); +guint mm_base_sms_get_multipart_reference (MMBaseSms *self); +gboolean mm_base_sms_multipart_is_complete (MMBaseSms *self); +gboolean mm_base_sms_multipart_is_assembled (MMBaseSms *self); + +void mm_base_sms_delete (MMBaseSms *self, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_base_sms_delete_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error); + +#endif /* MM_BASE_SMS_H */ diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index f6589656..1a5c34b7 100644 --- a/src/mm-broadband-modem-mbim.c +++ b/src/mm-broadband-modem-mbim.c @@ -2811,7 +2811,7 @@ enable_unsolicited_events_messaging (MMIfaceModemMessaging *self, /*****************************************************************************/ /* Create SMS (Messaging interface) */ -static MMSms * +static MMBaseSms * messaging_create_sms (MMIfaceModemMessaging *self) { return mm_sms_mbim_new (MM_BASE_MODEM (self)); diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c index ef5bb569..872b0053 100644 --- a/src/mm-broadband-modem-qmi.c +++ b/src/mm-broadband-modem-qmi.c @@ -7610,7 +7610,7 @@ messaging_enable_unsolicited_events (MMIfaceModemMessaging *_self, /*****************************************************************************/ /* Create SMS (Messaging interface) */ -static MMSms * +static MMBaseSms * messaging_create_sms (MMIfaceModemMessaging *_self) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index fd4852cb..7089737c 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -6193,10 +6193,10 @@ modem_messaging_load_initial_sms_parts (MMIfaceModemMessaging *self, /*****************************************************************************/ /* Create SMS (Messaging interface) */ -static MMSms * +static MMBaseSms * modem_messaging_create_sms (MMIfaceModemMessaging *self) { - return mm_sms_new (MM_BASE_MODEM (self)); + return mm_base_sms_new (MM_BASE_MODEM (self)); } /*****************************************************************************/ diff --git a/src/mm-iface-modem-messaging.c b/src/mm-iface-modem-messaging.c index 06770f39..d2ef6e86 100644 --- a/src/mm-iface-modem-messaging.c +++ b/src/mm-iface-modem-messaging.c @@ -88,7 +88,7 @@ mm_iface_modem_messaging_bind_simple_status (MMIfaceModemMessaging *self, /*****************************************************************************/ -MMSms * +MMBaseSms * mm_iface_modem_messaging_create_sms (MMIfaceModemMessaging *self) { g_assert (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->create_sms != NULL); @@ -271,7 +271,7 @@ handle_create_auth_ready (MMBaseModem *self, MMSmsList *list = NULL; GError *error = NULL; MMSmsProperties *properties; - MMSms *sms; + MMBaseSms *sms; if (!mm_base_modem_authorize_finish (self, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); @@ -300,9 +300,9 @@ handle_create_auth_ready (MMBaseModem *self, return; } - sms = mm_sms_new_from_properties (MM_BASE_MODEM (self), - properties, - &error); + sms = mm_base_sms_new_from_properties (MM_BASE_MODEM (self), + properties, + &error); if (!sms) { g_object_unref (properties); g_dbus_method_invocation_take_error (ctx->invocation, error); @@ -330,7 +330,7 @@ handle_create_auth_ready (MMBaseModem *self, /* Complete the DBus call */ mm_gdbus_modem_messaging_complete_create (ctx->skeleton, ctx->invocation, - mm_sms_get_path (sms)); + mm_base_sms_get_path (sms)); g_object_unref (sms); g_object_unref (properties); diff --git a/src/mm-iface-modem-messaging.h b/src/mm-iface-modem-messaging.h index 7ded6645..c27e100c 100644 --- a/src/mm-iface-modem-messaging.h +++ b/src/mm-iface-modem-messaging.h @@ -23,7 +23,7 @@ #include #include "mm-sms-part.h" -#include "mm-sms.h" +#include "mm-base-sms.h" #define MM_TYPE_IFACE_MODEM_MESSAGING (mm_iface_modem_messaging_get_type ()) #define MM_IFACE_MODEM_MESSAGING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_MESSAGING, MMIfaceModemMessaging)) @@ -123,7 +123,7 @@ struct _MMIfaceModemMessaging { GError **error); /* Create SMS objects */ - MMSms * (* create_sms) (MMIfaceModemMessaging *self); + MMBaseSms * (* create_sms) (MMIfaceModemMessaging *self); }; GType mm_iface_modem_messaging_get_type (void); @@ -176,7 +176,7 @@ gboolean mm_iface_modem_messaging_is_storage_supported_for_receiving (MMIfaceMod GError **error); /* SMS creation */ -MMSms *mm_iface_modem_messaging_create_sms (MMIfaceModemMessaging *self); +MMBaseSms *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, diff --git a/src/mm-sms-list.c b/src/mm-sms-list.c index dea6fd5d..e98bd8c2 100644 --- a/src/mm-sms-list.c +++ b/src/mm-sms-list.c @@ -26,7 +26,7 @@ #include "mm-iface-modem-messaging.h" #include "mm-sms-list.h" -#include "mm-sms.h" +#include "mm-base-sms.h" #include "mm-log.h" G_DEFINE_TYPE (MMSmsList, mm_sms_list, G_TYPE_OBJECT); @@ -65,12 +65,12 @@ mm_sms_list_has_local_multipart_reference (MMSmsList *self, g_assert (reference != 0); for (l = self->priv->list; l; l = g_list_next (l)) { - MMSms *sms = MM_SMS (l->data); + MMBaseSms *sms = MM_BASE_SMS (l->data); - if (mm_sms_is_multipart (sms) && + if (mm_base_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 && + mm_base_sms_get_storage (sms) != MM_SMS_STORAGE_UNKNOWN && + mm_base_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 */ @@ -103,7 +103,7 @@ mm_sms_list_get_paths (MMSmsList *self) const gchar *path; /* Don't try to add NULL paths (not yet exported SMS objects) */ - path = mm_sms_get_path (MM_SMS (l->data)); + path = mm_base_sms_get_path (MM_BASE_SMS (l->data)); if (path) path_list[i++] = g_strdup (path); } @@ -138,21 +138,21 @@ mm_sms_list_delete_sms_finish (MMSmsList *self, } static guint -cmp_sms_by_path (MMSms *sms, +cmp_sms_by_path (MMBaseSms *sms, const gchar *path) { - return g_strcmp0 (mm_sms_get_path (sms), path); + return g_strcmp0 (mm_base_sms_get_path (sms), path); } static void -delete_ready (MMSms *sms, +delete_ready (MMBaseSms *sms, GAsyncResult *res, DeleteSmsContext *ctx) { GError *error = NULL; GList *l; - if (!mm_sms_delete_finish (sms, res, &error)) { + if (!mm_base_sms_delete_finish (sms, res, &error)) { /* We report the error */ g_simple_async_result_take_error (ctx->result, error); delete_sms_context_complete_and_free (ctx); @@ -164,14 +164,14 @@ delete_ready (MMSms *sms, ctx->path, (GCompareFunc)cmp_sms_by_path); if (l) { - g_object_unref (MM_SMS (l->data)); + g_object_unref (MM_BASE_SMS (l->data)); ctx->self->priv->list = g_list_delete_link (ctx->self->priv->list, l); } /* We don't need to unref the SMS any more, but we can use the * reference we got in the method, which is the one kept alive * during the async operation. */ - mm_sms_unexport (sms); + mm_base_sms_unexport (sms); g_signal_emit (ctx->self, signals[SIGNAL_DELETED], 0, @@ -213,33 +213,33 @@ mm_sms_list_delete_sms (MMSmsList *self, user_data, mm_sms_list_delete_sms); - mm_sms_delete (MM_SMS (l->data), - (GAsyncReadyCallback)delete_ready, - ctx); + mm_base_sms_delete (MM_BASE_SMS (l->data), + (GAsyncReadyCallback)delete_ready, + ctx); } /*****************************************************************************/ void mm_sms_list_add_sms (MMSmsList *self, - MMSms *sms) + MMBaseSms *sms) { self->priv->list = g_list_prepend (self->priv->list, g_object_ref (sms)); g_signal_emit (self, signals[SIGNAL_ADDED], 0, - mm_sms_get_path (sms), + mm_base_sms_get_path (sms), FALSE); } /*****************************************************************************/ static guint -cmp_sms_by_concat_reference (MMSms *sms, +cmp_sms_by_concat_reference (MMBaseSms *sms, gpointer user_data) { - if (!mm_sms_is_multipart (sms)) + if (!mm_base_sms_is_multipart (sms)) return -1; - return (GPOINTER_TO_UINT (user_data) - mm_sms_get_multipart_reference (sms)); + return (GPOINTER_TO_UINT (user_data) - mm_base_sms_get_multipart_reference (sms)); } typedef struct { @@ -248,11 +248,11 @@ typedef struct { } PartIndexAndStorage; static guint -cmp_sms_by_part_index_and_storage (MMSms *sms, +cmp_sms_by_part_index_and_storage (MMBaseSms *sms, PartIndexAndStorage *ctx) { - return !(mm_sms_get_storage (sms) == ctx->storage && - mm_sms_has_part_index (sms, ctx->part_index)); + return !(mm_base_sms_get_storage (sms) == ctx->storage && + mm_base_sms_has_part_index (sms, ctx->part_index)); } static gboolean @@ -262,19 +262,19 @@ take_singlepart (MMSmsList *self, MMSmsStorage storage, GError **error) { - MMSms *sms; + MMBaseSms *sms; - sms = mm_sms_singlepart_new (self->priv->modem, - state, - storage, - part, - error); + sms = mm_base_sms_singlepart_new (self->priv->modem, + state, + storage, + part, + error); if (!sms) return FALSE; self->priv->list = g_list_prepend (self->priv->list, sms); g_signal_emit (self, signals[SIGNAL_ADDED], 0, - mm_sms_get_path (sms), + mm_base_sms_get_path (sms), state == MM_SMS_STATE_RECEIVED); return TRUE; } @@ -287,7 +287,7 @@ take_multipart (MMSmsList *self, GError **error) { GList *l; - MMSms *sms; + MMBaseSms *sms; guint concat_reference; concat_reference = mm_sms_part_get_concat_reference (part); @@ -296,22 +296,22 @@ take_multipart (MMSmsList *self, (GCompareFunc)cmp_sms_by_concat_reference); if (l) /* Try to take the part */ - return mm_sms_multipart_take_part (MM_SMS (l->data), part, error); + return mm_base_sms_multipart_take_part (MM_BASE_SMS (l->data), part, error); /* Create new Multipart */ - sms = mm_sms_multipart_new (self->priv->modem, - state, - storage, - concat_reference, - mm_sms_part_get_concat_max (part), - part, - error); + sms = mm_base_sms_multipart_new (self->priv->modem, + state, + storage, + concat_reference, + mm_sms_part_get_concat_max (part), + part, + error); if (!sms) return FALSE; self->priv->list = g_list_prepend (self->priv->list, sms); g_signal_emit (self, signals[SIGNAL_ADDED], 0, - mm_sms_get_path (sms), + mm_base_sms_get_path (sms), (state == MM_SMS_STATE_RECEIVED || state == MM_SMS_STATE_RECEIVING)); diff --git a/src/mm-sms-list.h b/src/mm-sms-list.h index b98e71fb..29370657 100644 --- a/src/mm-sms-list.h +++ b/src/mm-sms-list.h @@ -72,7 +72,7 @@ gboolean mm_sms_list_take_part (MMSmsList *self, GError **error); void mm_sms_list_add_sms (MMSmsList *self, - MMSms *sms); + MMBaseSms *sms); void mm_sms_list_delete_sms (MMSmsList *self, const gchar *sms_path, diff --git a/src/mm-sms-mbim.c b/src/mm-sms-mbim.c index e91f1266..3bdd68a9 100644 --- a/src/mm-sms-mbim.c +++ b/src/mm-sms-mbim.c @@ -31,7 +31,7 @@ #include "mm-log.h" #include "mm-sms-part-3gpp.h" -G_DEFINE_TYPE (MMSmsMbim, mm_sms_mbim, MM_TYPE_SMS) +G_DEFINE_TYPE (MMSmsMbim, mm_sms_mbim, MM_TYPE_BASE_SMS) /*****************************************************************************/ @@ -44,7 +44,7 @@ peek_device (gpointer self, MMBaseModem *modem = NULL; g_object_get (G_OBJECT (self), - MM_SMS_MODEM, &modem, + MM_BASE_SMS_MODEM, &modem, NULL); g_assert (MM_IS_BASE_MODEM (modem)); @@ -74,7 +74,7 @@ peek_device (gpointer self, /* Send the SMS */ typedef struct { - MMSms *self; + MMBaseSms *self; MMBaseModem *modem; MbimDevice *device; GSimpleAsyncResult *result; @@ -93,7 +93,7 @@ sms_send_context_complete_and_free (SmsSendContext *ctx) } static gboolean -sms_send_finish (MMSms *self, +sms_send_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { @@ -180,7 +180,7 @@ sms_send_next_part (SmsSendContext *ctx) } static void -sms_send (MMSms *self, +sms_send (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { @@ -199,17 +199,17 @@ sms_send (MMSms *self, ctx->self = g_object_ref (self); ctx->device = g_object_ref (device); g_object_get (self, - MM_SMS_MODEM, &ctx->modem, + MM_BASE_SMS_MODEM, &ctx->modem, NULL); - ctx->current = mm_sms_get_parts (self);; + ctx->current = mm_base_sms_get_parts (self);; sms_send_next_part (ctx); } /*****************************************************************************/ typedef struct { - MMSms *self; + MMBaseSms *self; MMBaseModem *modem; MbimDevice *device; GSimpleAsyncResult *result; @@ -229,7 +229,7 @@ sms_delete_parts_context_complete_and_free (SmsDeletePartsContext *ctx) } static gboolean -sms_delete_finish (MMSms *self, +sms_delete_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { @@ -308,7 +308,7 @@ delete_next_part (SmsDeletePartsContext *ctx) } static void -sms_delete (MMSms *self, +sms_delete (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { @@ -326,22 +326,22 @@ sms_delete (MMSms *self, ctx->self = g_object_ref (self); ctx->device = g_object_ref (device); g_object_get (self, - MM_SMS_MODEM, &ctx->modem, + MM_BASE_SMS_MODEM, &ctx->modem, NULL); /* Go on deleting parts */ - ctx->current = mm_sms_get_parts (self); + ctx->current = mm_base_sms_get_parts (self); delete_next_part (ctx); } /*****************************************************************************/ -MMSms * +MMBaseSms * mm_sms_mbim_new (MMBaseModem *modem) { - return MM_SMS (g_object_new (MM_TYPE_SMS_MBIM, - MM_SMS_MODEM, modem, - NULL)); + return MM_BASE_SMS (g_object_new (MM_TYPE_SMS_MBIM, + MM_BASE_SMS_MODEM, modem, + NULL)); } static void @@ -352,12 +352,12 @@ mm_sms_mbim_init (MMSmsMbim *self) static void mm_sms_mbim_class_init (MMSmsMbimClass *klass) { - MMSmsClass *sms_class = MM_SMS_CLASS (klass); - - sms_class->store = NULL; - sms_class->store_finish = NULL; - sms_class->send = sms_send; - sms_class->send_finish = sms_send_finish; - sms_class->delete = sms_delete; - sms_class->delete_finish = sms_delete_finish; + MMBaseSmsClass *base_sms_class = MM_BASE_SMS_CLASS (klass); + + base_sms_class->store = NULL; + base_sms_class->store_finish = NULL; + base_sms_class->send = sms_send; + base_sms_class->send_finish = sms_send_finish; + base_sms_class->delete = sms_delete; + base_sms_class->delete_finish = sms_delete_finish; } diff --git a/src/mm-sms-mbim.h b/src/mm-sms-mbim.h index 93dc7730..f2f6f3ac 100644 --- a/src/mm-sms-mbim.h +++ b/src/mm-sms-mbim.h @@ -22,7 +22,7 @@ #define _LIBMM_INSIDE_MM #include -#include "mm-sms.h" +#include "mm-base-sms.h" #define MM_TYPE_SMS_MBIM (mm_sms_mbim_get_type ()) #define MM_SMS_MBIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SMS_MBIM, MMSmsMbim)) @@ -35,15 +35,15 @@ typedef struct _MMSmsMbim MMSmsMbim; typedef struct _MMSmsMbimClass MMSmsMbimClass; struct _MMSmsMbim { - MMSms parent; + MMBaseSms parent; }; struct _MMSmsMbimClass { - MMSmsClass parent; + MMBaseSmsClass parent; }; GType mm_sms_mbim_get_type (void); -MMSms *mm_sms_mbim_new (MMBaseModem *modem); +MMBaseSms *mm_sms_mbim_new (MMBaseModem *modem); #endif /* MM_SMS_MBIM_H */ diff --git a/src/mm-sms-qmi.c b/src/mm-sms-qmi.c index 2e1fef75..6f604844 100644 --- a/src/mm-sms-qmi.c +++ b/src/mm-sms-qmi.c @@ -33,7 +33,7 @@ #include "mm-sms-part-cdma.h" #include "mm-log.h" -G_DEFINE_TYPE (MMSmsQmi, mm_sms_qmi, MM_TYPE_SMS); +G_DEFINE_TYPE (MMSmsQmi, mm_sms_qmi, MM_TYPE_BASE_SMS) /*****************************************************************************/ @@ -49,7 +49,7 @@ ensure_qmi_client (MMSmsQmi *self, MMPortQmi *port; g_object_get (self, - MM_SMS_MODEM, &modem, + MM_BASE_SMS_MODEM, &modem, NULL); g_assert (MM_IS_BASE_MODEM (modem)); @@ -115,7 +115,7 @@ check_sms_type_support (MMSmsQmi *self, /* Store the SMS */ typedef struct { - MMSms *self; + MMBaseSms *self; MMBaseModem *modem; QmiClientWms *client; GSimpleAsyncResult *result; @@ -135,7 +135,7 @@ sms_store_context_complete_and_free (SmsStoreContext *ctx) } static gboolean -sms_store_finish (MMSms *self, +sms_store_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { @@ -177,7 +177,7 @@ store_ready (QmiClientWms *client, qmi_message_wms_raw_write_output_unref (output); /* Set the index in the part we hold */ - parts = mm_sms_get_parts (ctx->self); + parts = mm_base_sms_get_parts (ctx->self); mm_sms_part_set_index ((MMSmsPart *)parts->data, (guint)idx); /* Go on with next one */ @@ -250,7 +250,7 @@ sms_store_next_part (SmsStoreContext *ctx) } static void -sms_store (MMSms *self, +sms_store (MMBaseSms *self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data) @@ -275,10 +275,10 @@ sms_store (MMSms *self, ctx->client = g_object_ref (client); ctx->storage = storage; g_object_get (self, - MM_SMS_MODEM, &ctx->modem, + MM_BASE_SMS_MODEM, &ctx->modem, NULL); - ctx->current = mm_sms_get_parts (self); + ctx->current = mm_base_sms_get_parts (self); /* Check whether we support the given SMS type */ if (!check_sms_type_support (MM_SMS_QMI (self), ctx->modem, (MMSmsPart *)ctx->current->data, &error)) { @@ -295,7 +295,7 @@ sms_store (MMSms *self, /* Send the SMS */ typedef struct { - MMSms *self; + MMBaseSms *self; MMBaseModem *modem; QmiClientWms *client; GSimpleAsyncResult *result; @@ -315,7 +315,7 @@ sms_send_context_complete_and_free (SmsSendContext *ctx) } static gboolean -sms_send_finish (MMSms *self, +sms_send_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { @@ -534,7 +534,7 @@ sms_send_from_storage (SmsSendContext *ctx) qmi_message_wms_send_from_memory_storage_input_set_information ( input, - mm_sms_storage_to_qmi_storage_type (mm_sms_get_storage (ctx->self)), + mm_sms_storage_to_qmi_storage_type (mm_base_sms_get_storage (ctx->self)), mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data) ? QMI_WMS_MESSAGE_MODE_GSM_WCDMA : @@ -569,7 +569,7 @@ sms_send_next_part (SmsSendContext *ctx) } static void -sms_send (MMSms *self, +sms_send (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { @@ -592,13 +592,13 @@ sms_send (MMSms *self, ctx->self = g_object_ref (self); ctx->client = g_object_ref (client); g_object_get (self, - MM_SMS_MODEM, &ctx->modem, + MM_BASE_SMS_MODEM, &ctx->modem, NULL); /* If the SMS is STORED, try to send from storage */ - ctx->from_storage = (mm_sms_get_storage (self) != MM_SMS_STORAGE_UNKNOWN); + ctx->from_storage = (mm_base_sms_get_storage (self) != MM_SMS_STORAGE_UNKNOWN); - ctx->current = mm_sms_get_parts (self); + ctx->current = mm_base_sms_get_parts (self); /* Check whether we support the given SMS type */ if (!check_sms_type_support (MM_SMS_QMI (self), ctx->modem, (MMSmsPart *)ctx->current->data, &error)) { @@ -613,7 +613,7 @@ sms_send (MMSms *self, /*****************************************************************************/ typedef struct { - MMSms *self; + MMBaseSms *self; MMBaseModem *modem; QmiClientWms *client; GSimpleAsyncResult *result; @@ -633,7 +633,7 @@ sms_delete_parts_context_complete_and_free (SmsDeletePartsContext *ctx) } static gboolean -sms_delete_finish (MMSms *self, +sms_delete_finish (MMBaseSms *self, GAsyncResult *res, GError **error) { @@ -703,7 +703,7 @@ delete_next_part (SmsDeletePartsContext *ctx) input = qmi_message_wms_delete_input_new (); qmi_message_wms_delete_input_set_memory_storage ( input, - mm_sms_storage_to_qmi_storage_type (mm_sms_get_storage (ctx->self)), + mm_sms_storage_to_qmi_storage_type (mm_base_sms_get_storage (ctx->self)), NULL); qmi_message_wms_delete_input_set_memory_index ( input, @@ -725,7 +725,7 @@ delete_next_part (SmsDeletePartsContext *ctx) } static void -sms_delete (MMSms *self, +sms_delete (MMBaseSms *self, GAsyncReadyCallback callback, gpointer user_data) { @@ -746,22 +746,22 @@ sms_delete (MMSms *self, ctx->self = g_object_ref (self); ctx->client = g_object_ref (client); g_object_get (self, - MM_SMS_MODEM, &ctx->modem, + MM_BASE_SMS_MODEM, &ctx->modem, NULL); /* Go on deleting parts */ - ctx->current = mm_sms_get_parts (self); + ctx->current = mm_base_sms_get_parts (self); delete_next_part (ctx); } /*****************************************************************************/ -MMSms * +MMBaseSms * mm_sms_qmi_new (MMBaseModem *modem) { - return MM_SMS (g_object_new (MM_TYPE_SMS_QMI, - MM_SMS_MODEM, modem, - NULL)); + return MM_BASE_SMS (g_object_new (MM_TYPE_SMS_QMI, + MM_BASE_SMS_MODEM, modem, + NULL)); } static void @@ -772,12 +772,12 @@ mm_sms_qmi_init (MMSmsQmi *self) static void mm_sms_qmi_class_init (MMSmsQmiClass *klass) { - MMSmsClass *sms_class = MM_SMS_CLASS (klass); - - sms_class->store = sms_store; - sms_class->store_finish = sms_store_finish; - sms_class->send = sms_send; - sms_class->send_finish = sms_send_finish; - sms_class->delete = sms_delete; - sms_class->delete_finish = sms_delete_finish; + MMBaseSmsClass *base_sms_class = MM_BASE_SMS_CLASS (klass); + + base_sms_class->store = sms_store; + base_sms_class->store_finish = sms_store_finish; + base_sms_class->send = sms_send; + base_sms_class->send_finish = sms_send_finish; + base_sms_class->delete = sms_delete; + base_sms_class->delete_finish = sms_delete_finish; } diff --git a/src/mm-sms-qmi.h b/src/mm-sms-qmi.h index bceb300f..64a2f300 100644 --- a/src/mm-sms-qmi.h +++ b/src/mm-sms-qmi.h @@ -24,7 +24,7 @@ #define _LIBMM_INSIDE_MM #include -#include "mm-sms.h" +#include "mm-base-sms.h" #define MM_TYPE_SMS_QMI (mm_sms_qmi_get_type ()) #define MM_SMS_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SMS_QMI, MMSmsQmi)) @@ -37,15 +37,15 @@ typedef struct _MMSmsQmi MMSmsQmi; typedef struct _MMSmsQmiClass MMSmsQmiClass; struct _MMSmsQmi { - MMSms parent; + MMBaseSms parent; }; struct _MMSmsQmiClass { - MMSmsClass parent; + MMBaseSmsClass parent; }; GType mm_sms_qmi_get_type (void); -MMSms *mm_sms_qmi_new (MMBaseModem *modem); +MMBaseSms *mm_sms_qmi_new (MMBaseModem *modem); #endif /* MM_SMS_QMI_H */ diff --git a/src/mm-sms.c b/src/mm-sms.c deleted file mode 100644 index a31579ff..00000000 --- a/src/mm-sms.c +++ /dev/null @@ -1,2050 +0,0 @@ -/* -*- 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) 2008 - 2009 Novell, Inc. - * Copyright (C) 2009 - 2012 Red Hat, Inc. - * Copyright (C) 2012 Google, Inc. - */ - -#include -#include -#include -#include -#include -#include - -#include -#define _LIBMM_INSIDE_MM -#include - -#include "mm-broadband-modem.h" -#include "mm-iface-modem.h" -#include "mm-iface-modem-messaging.h" -#include "mm-sms.h" -#include "mm-sms-part-3gpp.h" -#include "mm-base-modem-at.h" -#include "mm-base-modem.h" -#include "mm-log.h" -#include "mm-modem-helpers.h" - -G_DEFINE_TYPE (MMSms, mm_sms, MM_GDBUS_TYPE_SMS_SKELETON); - -enum { - PROP_0, - PROP_PATH, - PROP_CONNECTION, - PROP_MODEM, - PROP_IS_MULTIPART, - PROP_MAX_PARTS, - PROP_MULTIPART_REFERENCE, - PROP_LAST -}; - -static GParamSpec *properties[PROP_LAST]; - -struct _MMSmsPrivate { - /* The connection to the system bus */ - GDBusConnection *connection; - /* The modem which owns this SMS */ - MMBaseModem *modem; - /* The path where the SMS object is exported */ - gchar *path; - - /* Multipart SMS specific stuff */ - gboolean is_multipart; - guint multipart_reference; - - /* List of SMS parts */ - guint max_parts; - GList *parts; - - /* Set to true when all needed parts were received, - * parsed and assembled */ - gboolean is_assembled; -}; - -/*****************************************************************************/ - -static guint -get_validity_relative (GVariant *tuple) -{ - guint type; - GVariant *value; - guint value_integer = 0; - - if (!tuple) - return 0; - - g_variant_get (tuple, "(uv)", &type, &value); - - if (type == MM_SMS_VALIDITY_TYPE_RELATIVE) - value_integer = g_variant_get_uint32 (value); - - g_variant_unref (value); - - return value_integer; -} - -static gboolean -generate_3gpp_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_3gpp_util_split_text (text, &encoding); - if (!split_text) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Cannot generate PDUs: Error processing input text"); - return FALSE; - } - n_parts = g_strv_length (split_text); - } else if (data) { - encoding = MM_SMS_ENCODING_8BIT; - split_data = mm_sms_part_3gpp_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)); - - if (n_parts > 255) { - if (split_text) - g_strfreev (split_text); - else if (split_data) { - guint i = 0; - - while (split_data[i]) - g_byte_array_unref (split_data[i++]); - g_free (split_data); - } - - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_TOO_MANY, - "Cannot generate PDUs: Text or Data too long"); - return FALSE; - } - - /* 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_relative (part, get_validity_relative (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; -} - -static gboolean -generate_cdma_submit_pdus (MMSms *self, - GError **error) -{ - const gchar *text; - GVariant *data_variant; - const guint8 *data; - gsize data_len = 0; - - MMSmsPart *part; - - 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)); - - /* Create new part */ - part = mm_sms_part_new (SMS_PART_INVALID_INDEX, MM_SMS_PDU_TYPE_CDMA_SUBMIT); - if (text) - mm_sms_part_set_text (part, text); - else if (data) { - GByteArray *part_data; - - part_data = g_byte_array_sized_new (data_len); - g_byte_array_append (part_data, data, data_len); - mm_sms_part_take_data (part, part_data); - } else - g_assert_not_reached (); - mm_sms_part_set_encoding (part, data ? MM_SMS_ENCODING_8BIT : MM_SMS_ENCODING_UNKNOWN); - mm_sms_part_set_number (part, mm_gdbus_sms_get_number (MM_GDBUS_SMS (self))); - - /* If creating a CDMA SMS part but we don't have a Teleservice ID, we default to WMT */ - if (mm_gdbus_sms_get_teleservice_id (MM_GDBUS_SMS (self)) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN) { - mm_dbg ("Defaulting to WMT teleservice ID when creating SMS part"); - mm_sms_part_set_cdma_teleservice_id (part, MM_SMS_CDMA_TELESERVICE_ID_WMT); - } else - mm_sms_part_set_cdma_teleservice_id (part, mm_gdbus_sms_get_teleservice_id (MM_GDBUS_SMS (self))); - - mm_sms_part_set_cdma_service_category (part, mm_gdbus_sms_get_service_category (MM_GDBUS_SMS (self))); - - mm_dbg ("Created SMS part for CDMA SMS"); - - /* Add to the list of parts */ - self->priv->parts = g_list_append (self->priv->parts, part); - - /* No more parts are expected */ - self->priv->is_assembled = TRUE; - - return TRUE; -} - -static gboolean -generate_submit_pdus (MMSms *self, - GError **error) -{ - MMBaseModem *modem; - gboolean is_3gpp; - - /* First; decide which kind of PDU we'll generate, based on the current modem caps */ - - g_object_get (self, - MM_SMS_MODEM, &modem, - NULL); - g_assert (modem != NULL); - - is_3gpp = mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)); - g_object_unref (modem); - - /* On a 3GPP-capable modem, create always a 3GPP SMS (even if the modem is 3GPP+3GPP2) */ - if (is_3gpp) - return generate_3gpp_submit_pdus (self, error); - - /* Otherwise, create a 3GPP2 SMS */ - return generate_cdma_submit_pdus (self, error); -} - -/*****************************************************************************/ -/* Store SMS (DBus call handling) */ - -typedef struct { - MMSms *self; - MMBaseModem *modem; - GDBusMethodInvocation *invocation; - MMSmsStorage storage; -} HandleStoreContext; - -static void -handle_store_context_free (HandleStoreContext *ctx) -{ - g_object_unref (ctx->invocation); - g_object_unref (ctx->modem); - g_object_unref (ctx->self); - g_free (ctx); -} - -static void -handle_store_ready (MMSms *self, - GAsyncResult *res, - HandleStoreContext *ctx) -{ - GError *error = NULL; - - if (!MM_SMS_GET_CLASS (self)->store_finish (self, res, &error)) { - /* On error, clear up the parts we generated */ - g_list_free_full (self->priv->parts, (GDestroyNotify)mm_sms_part_free); - self->priv->parts = NULL; - g_dbus_method_invocation_take_error (ctx->invocation, error); - } else { - mm_gdbus_sms_set_storage (MM_GDBUS_SMS (ctx->self), ctx->storage); - - /* Transition from Unknown->Stored for SMS which were created by the user */ - if (mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_UNKNOWN) - mm_gdbus_sms_set_state (MM_GDBUS_SMS (ctx->self), MM_SMS_STATE_STORED); - - mm_gdbus_sms_complete_store (MM_GDBUS_SMS (ctx->self), ctx->invocation); - } - - 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. */ - if (self->priv->is_multipart) { - 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, - HandleStoreContext *ctx) -{ - GError *error = NULL; - - if (!mm_base_modem_authorize_finish (modem, res, &error)) { - g_dbus_method_invocation_take_error (ctx->invocation, error); - handle_store_context_free (ctx); - return; - } - - /* First of all, check if we already have the SMS stored. */ - if (mm_sms_get_storage (ctx->self) != MM_SMS_STORAGE_UNKNOWN) { - /* Check if SMS stored in some other storage */ - if (mm_sms_get_storage (ctx->self) == ctx->storage) - /* Good, same storage */ - mm_gdbus_sms_complete_store (MM_GDBUS_SMS (ctx->self), ctx->invocation); - else - g_dbus_method_invocation_return_error ( - ctx->invocation, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "SMS is already stored in storage '%s', cannot store it in storage '%s'", - mm_sms_storage_get_string (mm_sms_get_storage (ctx->self)), - mm_sms_storage_get_string (ctx->storage)); - handle_store_context_free (ctx); - return; - } - - /* Check if the requested storage is allowed for storing */ - if (!mm_iface_modem_messaging_is_storage_supported_for_storing (MM_IFACE_MODEM_MESSAGING (ctx->modem), - ctx->storage, - &error)) { - g_dbus_method_invocation_take_error (ctx->invocation, error); - handle_store_context_free (ctx); - 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) { - g_dbus_method_invocation_return_error (ctx->invocation, - MM_CORE_ERROR, - MM_CORE_ERROR_UNSUPPORTED, - "Storing SMS is not supported by this modem"); - handle_store_context_free (ctx); - return; - } - - MM_SMS_GET_CLASS (ctx->self)->store (ctx->self, - ctx->storage, - (GAsyncReadyCallback)handle_store_ready, - ctx); -} - -static gboolean -handle_store (MMSms *self, - GDBusMethodInvocation *invocation, - guint32 storage) -{ - HandleStoreContext *ctx; - - ctx = g_new0 (HandleStoreContext, 1); - ctx->self = g_object_ref (self); - ctx->invocation = g_object_ref (invocation); - g_object_get (self, - MM_SMS_MODEM, &ctx->modem, - NULL); - ctx->storage = (MMSmsStorage)storage; - - if (ctx->storage == MM_SMS_STORAGE_UNKNOWN) { - /* We'll set now the proper storage, taken from the default mem2 one */ - g_object_get (self->priv->modem, - MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, &ctx->storage, - NULL); - g_assert (ctx->storage != MM_SMS_STORAGE_UNKNOWN); - } - - mm_base_modem_authorize (ctx->modem, - invocation, - MM_AUTHORIZATION_MESSAGING, - (GAsyncReadyCallback)handle_store_auth_ready, - ctx); - return TRUE; -} - -/*****************************************************************************/ -/* Send SMS (DBus call handling) */ - -typedef struct { - MMSms *self; - MMBaseModem *modem; - GDBusMethodInvocation *invocation; -} HandleSendContext; - -static void -handle_send_context_free (HandleSendContext *ctx) -{ - g_object_unref (ctx->invocation); - g_object_unref (ctx->modem); - g_object_unref (ctx->self); - g_free (ctx); -} - -static void -handle_send_ready (MMSms *self, - GAsyncResult *res, - HandleSendContext *ctx) -{ - GError *error = NULL; - - if (!MM_SMS_GET_CLASS (self)->send_finish (self, res, &error)) { - /* On error, clear up the parts we generated */ - g_list_free_full (self->priv->parts, (GDestroyNotify)mm_sms_part_free); - self->priv->parts = NULL; - g_dbus_method_invocation_take_error (ctx->invocation, error); - } else { - /* Transition from Unknown->Sent or Stored->Sent */ - if (mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_UNKNOWN || - mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_STORED) { - GList *l; - - /* Update state */ - mm_gdbus_sms_set_state (MM_GDBUS_SMS (ctx->self), MM_SMS_STATE_SENT); - /* Grab last message reference */ - l = g_list_last (mm_sms_get_parts (ctx->self)); - mm_gdbus_sms_set_message_reference (MM_GDBUS_SMS (ctx->self), - mm_sms_part_get_message_reference ((MMSmsPart *)l->data)); - } - mm_gdbus_sms_complete_send (MM_GDBUS_SMS (ctx->self), ctx->invocation); - } - - 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. */ - if (self->priv->is_multipart) { - 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, - HandleSendContext *ctx) -{ - MMSmsState state; - GError *error = NULL; - - if (!mm_base_modem_authorize_finish (modem, res, &error)) { - g_dbus_method_invocation_take_error (ctx->invocation, error); - handle_send_context_free (ctx); - return; - } - - /* We can only send SMS created by the user */ - state = mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)); - if (state == MM_SMS_STATE_RECEIVED || - state == MM_SMS_STATE_RECEIVING) { - g_dbus_method_invocation_return_error (ctx->invocation, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "This SMS was received, cannot send it"); - handle_send_context_free (ctx); - return; - } - - /* Don't allow sending the same SMS multiple times, we would lose the message reference */ - if (state == MM_SMS_STATE_SENT) { - g_dbus_method_invocation_return_error (ctx->invocation, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "This SMS was already sent, cannot send it again"); - handle_send_context_free (ctx); - 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) { - g_dbus_method_invocation_return_error (ctx->invocation, - MM_CORE_ERROR, - MM_CORE_ERROR_UNSUPPORTED, - "Sending SMS is not supported by this modem"); - handle_send_context_free (ctx); - return; - } - - MM_SMS_GET_CLASS (ctx->self)->send (ctx->self, - (GAsyncReadyCallback)handle_send_ready, - ctx); -} - -static gboolean -handle_send (MMSms *self, - GDBusMethodInvocation *invocation) -{ - HandleSendContext *ctx; - - ctx = g_new0 (HandleSendContext, 1); - ctx->self = g_object_ref (self); - ctx->invocation = g_object_ref (invocation); - g_object_get (self, - MM_SMS_MODEM, &ctx->modem, - NULL); - - mm_base_modem_authorize (ctx->modem, - invocation, - MM_AUTHORIZATION_MESSAGING, - (GAsyncReadyCallback)handle_send_auth_ready, - ctx); - return TRUE; -} - -/*****************************************************************************/ - -void -mm_sms_export (MMSms *self) -{ - static guint id = 0; - gchar *path; - - path = g_strdup_printf (MM_DBUS_SMS_PREFIX "/%d", id++); - g_object_set (self, - MM_SMS_PATH, path, - NULL); - g_free (path); -} - -void -mm_sms_unexport (MMSms *self) -{ - g_object_set (self, - MM_SMS_PATH, NULL, - NULL); -} - -/*****************************************************************************/ - -static void -mm_sms_dbus_export (MMSms *self) -{ - GError *error = NULL; - - /* Handle method invocations */ - g_signal_connect (self, - "handle-store", - G_CALLBACK (handle_store), - NULL); - g_signal_connect (self, - "handle-send", - G_CALLBACK (handle_send), - NULL); - - if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self), - self->priv->connection, - self->priv->path, - &error)) { - mm_warn ("couldn't export SMS at '%s': '%s'", - self->priv->path, - error->message); - g_error_free (error); - } -} - -static void -mm_sms_dbus_unexport (MMSms *self) -{ - /* Only unexport if currently exported */ - if (g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self))) - g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self)); -} - -/*****************************************************************************/ - -const gchar * -mm_sms_get_path (MMSms *self) -{ - return self->priv->path; -} - -MMSmsStorage -mm_sms_get_storage (MMSms *self) -{ - return mm_gdbus_sms_get_storage (MM_GDBUS_SMS (self)); -} - -gboolean -mm_sms_is_multipart (MMSms *self) -{ - return self->priv->is_multipart; -} - -guint -mm_sms_get_multipart_reference (MMSms *self) -{ - g_return_val_if_fail (self->priv->is_multipart, 0); - - return self->priv->multipart_reference; -} - -gboolean -mm_sms_multipart_is_complete (MMSms *self) -{ - return (g_list_length (self->priv->parts) == self->priv->max_parts); -} - -gboolean -mm_sms_multipart_is_assembled (MMSms *self) -{ - return self->priv->is_assembled; -} - -/*****************************************************************************/ - -static guint -cmp_sms_part_index (MMSmsPart *part, - gpointer user_data) -{ - return (GPOINTER_TO_UINT (user_data) - mm_sms_part_get_index (part)); -} - -gboolean -mm_sms_has_part_index (MMSms *self, - guint index) -{ - return !!g_list_find_custom (self->priv->parts, - GUINT_TO_POINTER (index), - (GCompareFunc)cmp_sms_part_index); -} - -GList * -mm_sms_get_parts (MMSms *self) -{ - return self->priv->parts; -} - -/*****************************************************************************/ - -static gboolean -sms_get_store_or_send_command (MMSmsPart *part, - gboolean text_or_pdu, /* TRUE for PDU */ - gboolean store_or_send, /* TRUE for send */ - gchar **out_cmd, - gchar **out_msg_data, - GError **error) -{ - g_assert (out_cmd != NULL); - g_assert (out_msg_data != NULL); - - if (!text_or_pdu) { - /* Text mode */ - *out_cmd = g_strdup_printf ("+CMG%c=\"%s\"", - store_or_send ? 'S' : 'W', - mm_sms_part_get_number (part)); - *out_msg_data = g_strdup_printf ("%s\x1a", mm_sms_part_get_text (part)); - } else { - guint8 *pdu; - guint pdulen = 0; - guint msgstart = 0; - gchar *hex; - - /* AT+CMGW=[, ] PDU can be entered. / */ - - pdu = mm_sms_part_3gpp_get_submit_pdu (part, &pdulen, &msgstart, error); - if (!pdu) - /* 'error' should already be set */ - return FALSE; - - /* Convert PDU to hex */ - hex = mm_utils_bin2hexstr (pdu, pdulen); - g_free (pdu); - - if (!hex) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Not enough memory to send SMS PDU"); - return FALSE; - } - - /* CMGW/S length is the size of the PDU without SMSC information */ - *out_cmd = g_strdup_printf ("+CMG%c=%d", - store_or_send ? 'S' : 'W', - pdulen - msgstart); - *out_msg_data = g_strdup_printf ("%s\x1a", hex); - g_free (hex); - } - - return TRUE; -} - -/*****************************************************************************/ -/* Store the SMS */ - -typedef struct { - MMSms *self; - MMBaseModem *modem; - GSimpleAsyncResult *result; - MMSmsStorage storage; - gboolean need_unlock; - gboolean use_pdu_mode; - GList *current; - gchar *msg_data; -} SmsStoreContext; - -static void -sms_store_context_complete_and_free (SmsStoreContext *ctx) -{ - g_simple_async_result_complete (ctx->result); - g_object_unref (ctx->result); - /* Unlock mem2 storage if we had the lock */ - if (ctx->need_unlock) - mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), FALSE, TRUE); - g_object_unref (ctx->modem); - g_object_unref (ctx->self); - g_free (ctx->msg_data); - g_free (ctx); -} - -static gboolean -sms_store_finish (MMSms *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void sms_store_next_part (SmsStoreContext *ctx); - -static void -store_msg_data_ready (MMBaseModem *modem, - GAsyncResult *res, - SmsStoreContext *ctx) -{ - const gchar *response; - GError *error = NULL; - gint rv; - gint idx; - - response = mm_base_modem_at_command_finish (modem, res, &error); - if (error) { - g_simple_async_result_take_error (ctx->result, error); - sms_store_context_complete_and_free (ctx); - return; - } - - /* Read the new part index from the reply */ - rv = sscanf (response, "+CMGW: %d", &idx); - if (rv != 1 || idx < 0) { - g_simple_async_result_set_error (ctx->result, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't read index of already stored part: " - "%d fields parsed", - rv); - sms_store_context_complete_and_free (ctx); - return; - } - - /* Set the index in the part we hold */ - mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, (guint)idx); - - ctx->current = g_list_next (ctx->current); - sms_store_next_part (ctx); -} - -static void -store_ready (MMBaseModem *modem, - GAsyncResult *res, - SmsStoreContext *ctx) -{ - const gchar *response; - GError *error = NULL; - - response = mm_base_modem_at_command_finish (modem, res, &error); - if (error) { - g_simple_async_result_take_error (ctx->result, error); - sms_store_context_complete_and_free (ctx); - return; - } - - /* Send the actual message data */ - mm_base_modem_at_command_raw (ctx->modem, - ctx->msg_data, - 10, - FALSE, - (GAsyncReadyCallback)store_msg_data_ready, - ctx); -} - -static void -sms_store_next_part (SmsStoreContext *ctx) -{ - gchar *cmd; - GError *error = NULL; - - if (!ctx->current) { - /* Done we are */ - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - sms_store_context_complete_and_free (ctx); - return; - } - - if (ctx->msg_data) { - g_free (ctx->msg_data); - ctx->msg_data = NULL; - } - - if (!sms_get_store_or_send_command ((MMSmsPart *)ctx->current->data, - ctx->use_pdu_mode, - FALSE, - &cmd, - &ctx->msg_data, - &error)) { - g_simple_async_result_take_error (ctx->result, error); - sms_store_context_complete_and_free (ctx); - return; - } - - g_assert (cmd != NULL); - g_assert (ctx->msg_data != NULL); - - mm_base_modem_at_command (ctx->modem, - cmd, - 10, - FALSE, - (GAsyncReadyCallback)store_ready, - ctx); - g_free (cmd); -} - -static void -store_lock_sms_storages_ready (MMBroadbandModem *modem, - GAsyncResult *res, - SmsStoreContext *ctx) -{ - GError *error = NULL; - - if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { - g_simple_async_result_take_error (ctx->result, error); - sms_store_context_complete_and_free (ctx); - return; - } - - /* We are now locked. Whatever result we have here, we need to make sure - * we unlock the storages before finishing. */ - ctx->need_unlock = TRUE; - - /* Go on to store the parts */ - ctx->current = ctx->self->priv->parts; - sms_store_next_part (ctx); -} - -static void -sms_store (MMSms *self, - MMSmsStorage storage, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SmsStoreContext *ctx; - - /* Setup the context */ - ctx = g_new0 (SmsStoreContext, 1); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - sms_store); - ctx->self = g_object_ref (self); - ctx->modem = g_object_ref (self->priv->modem); - ctx->storage = storage; - - /* Different ways to do it if on PDU or text mode */ - g_object_get (self->priv->modem, - MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, - NULL); - - /* First, lock storage to use */ - g_assert (MM_IS_BROADBAND_MODEM (self->priv->modem)); - mm_broadband_modem_lock_sms_storages ( - MM_BROADBAND_MODEM (self->priv->modem), - MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ - ctx->storage, - (GAsyncReadyCallback)store_lock_sms_storages_ready, - ctx); -} - -/*****************************************************************************/ -/* Send the SMS */ - -typedef struct { - MMSms *self; - MMBaseModem *modem; - GSimpleAsyncResult *result; - gboolean need_unlock; - gboolean from_storage; - gboolean use_pdu_mode; - GList *current; - gchar *msg_data; -} SmsSendContext; - -static void -sms_send_context_complete_and_free (SmsSendContext *ctx) -{ - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - /* Unlock mem2 storage if we had the lock */ - if (ctx->need_unlock) - mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), FALSE, TRUE); - g_object_unref (ctx->modem); - g_object_unref (ctx->self); - g_free (ctx->msg_data); - g_free (ctx); -} - -static gboolean -sms_send_finish (MMSms *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void sms_send_next_part (SmsSendContext *ctx); - -static gint -read_message_reference_from_reply (const gchar *response, - GError **error) -{ - gint rv = 0; - gint idx = -1; - - if (strstr (response, "+CMGS")) - rv = sscanf (strstr (response, "+CMGS"), "+CMGS: %d", &idx); - else if (strstr (response, "+CMSS")) - rv = sscanf (strstr (response, "+CMSS"), "+CMSS: %d", &idx); - - if (rv != 1 || idx < 0) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't read message reference: " - "%d fields parsed from response '%s'", - rv, response); - return -1; - } - - return idx; -} - -static void -send_generic_msg_data_ready (MMBaseModem *modem, - GAsyncResult *res, - SmsSendContext *ctx) -{ - GError *error = NULL; - const gchar *response; - gint message_reference; - - response = mm_base_modem_at_command_finish (modem, res, &error); - if (error) { - g_simple_async_result_take_error (ctx->result, error); - sms_send_context_complete_and_free (ctx); - return; - } - - message_reference = read_message_reference_from_reply (response, &error); - if (error) { - g_simple_async_result_take_error (ctx->result, error); - sms_send_context_complete_and_free (ctx); - return; - } - - mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, - (guint)message_reference); - - ctx->current = g_list_next (ctx->current); - sms_send_next_part (ctx); -} - -static void -send_generic_ready (MMBaseModem *modem, - GAsyncResult *res, - SmsSendContext *ctx) -{ - GError *error = NULL; - - mm_base_modem_at_command_finish (modem, res, &error); - if (error) { - g_simple_async_result_take_error (ctx->result, error); - sms_send_context_complete_and_free (ctx); - return; - } - - /* Send the actual message data */ - mm_base_modem_at_command_raw (ctx->modem, - ctx->msg_data, - 10, - FALSE, - (GAsyncReadyCallback)send_generic_msg_data_ready, - ctx); -} - -static void -send_from_storage_ready (MMBaseModem *modem, - GAsyncResult *res, - SmsSendContext *ctx) -{ - GError *error = NULL; - const gchar *response; - gint message_reference; - - response = mm_base_modem_at_command_finish (modem, res, &error); - if (error) { - if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { - g_simple_async_result_take_error (ctx->result, error); - sms_send_context_complete_and_free (ctx); - return; - } - - mm_dbg ("Couldn't send SMS from storage: '%s'; trying generic send...", - error->message); - g_error_free (error); - - ctx->from_storage = FALSE; - sms_send_next_part (ctx); - return; - } - - message_reference = read_message_reference_from_reply (response, &error); - if (error) { - g_simple_async_result_take_error (ctx->result, error); - sms_send_context_complete_and_free (ctx); - return; - } - - mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, - (guint)message_reference); - - ctx->current = g_list_next (ctx->current); - sms_send_next_part (ctx); -} - -static void -sms_send_next_part (SmsSendContext *ctx) -{ - GError *error = NULL; - gchar *cmd; - - if (!ctx->current) { - /* Done we are */ - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - sms_send_context_complete_and_free (ctx); - return; - } - - /* Send from storage */ - if (ctx->from_storage) { - cmd = g_strdup_printf ("+CMSS=%d", - mm_sms_part_get_index ((MMSmsPart *)ctx->current->data)); - mm_base_modem_at_command (ctx->modem, - cmd, - 30, - FALSE, - (GAsyncReadyCallback)send_from_storage_ready, - ctx); - g_free (cmd); - return; - } - - /* Generic send */ - - if (ctx->msg_data) { - g_free (ctx->msg_data); - ctx->msg_data = NULL; - } - - if (!sms_get_store_or_send_command ((MMSmsPart *)ctx->current->data, - ctx->use_pdu_mode, - TRUE, - &cmd, - &ctx->msg_data, - &error)) { - g_simple_async_result_take_error (ctx->result, error); - sms_send_context_complete_and_free (ctx); - return; - } - - g_assert (cmd != NULL); - g_assert (ctx->msg_data != NULL); - mm_base_modem_at_command (ctx->modem, - cmd, - 30, - FALSE, - (GAsyncReadyCallback)send_generic_ready, - ctx); - g_free (cmd); -} - -static void -send_lock_sms_storages_ready (MMBroadbandModem *modem, - GAsyncResult *res, - SmsSendContext *ctx) -{ - GError *error = NULL; - - if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { - g_simple_async_result_take_error (ctx->result, error); - sms_send_context_complete_and_free (ctx); - return; - } - - /* We are now locked. Whatever result we have here, we need to make sure - * we unlock the storages before finishing. */ - ctx->need_unlock = TRUE; - - /* Go on to send the parts */ - ctx->current = ctx->self->priv->parts; - sms_send_next_part (ctx); -} - -static void -sms_send (MMSms *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SmsSendContext *ctx; - - /* Setup the context */ - ctx = g_new0 (SmsSendContext, 1); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - sms_send); - ctx->self = g_object_ref (self); - ctx->modem = g_object_ref (self->priv->modem); - - /* If the SMS is STORED, try to send from storage */ - ctx->from_storage = (mm_sms_get_storage (self) != MM_SMS_STORAGE_UNKNOWN); - if (ctx->from_storage) { - /* When sending from storage, first lock storage to use */ - g_assert (MM_IS_BROADBAND_MODEM (self->priv->modem)); - mm_broadband_modem_lock_sms_storages ( - MM_BROADBAND_MODEM (self->priv->modem), - MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ - mm_sms_get_storage (self), - (GAsyncReadyCallback)send_lock_sms_storages_ready, - ctx); - return; - } - - /* Different ways to do it if on PDU or text mode */ - g_object_get (self->priv->modem, - MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, - NULL); - ctx->current = self->priv->parts; - sms_send_next_part (ctx); -} - -/*****************************************************************************/ - -typedef struct { - MMSms *self; - MMBaseModem *modem; - GSimpleAsyncResult *result; - gboolean need_unlock; - GList *current; - guint n_failed; -} SmsDeletePartsContext; - -static void -sms_delete_parts_context_complete_and_free (SmsDeletePartsContext *ctx) -{ - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - /* Unlock mem1 storage if we had the lock */ - if (ctx->need_unlock) - mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), TRUE, FALSE); - g_object_unref (ctx->modem); - g_object_unref (ctx->self); - g_free (ctx); -} - -static gboolean -sms_delete_finish (MMSms *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void delete_next_part (SmsDeletePartsContext *ctx); - -static void -delete_part_ready (MMBaseModem *modem, - GAsyncResult *res, - SmsDeletePartsContext *ctx) -{ - GError *error = NULL; - - mm_base_modem_at_command_finish (modem, res, &error); - if (error) { - ctx->n_failed++; - mm_dbg ("Couldn't delete SMS part with index %u: '%s'", - mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), - error->message); - g_error_free (error); - } - - /* We reset the index, as there is no longer that part */ - mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, SMS_PART_INVALID_INDEX); - - ctx->current = g_list_next (ctx->current); - delete_next_part (ctx); -} - -static void -delete_next_part (SmsDeletePartsContext *ctx) -{ - gchar *cmd; - - /* Skip non-stored parts */ - while (ctx->current && - mm_sms_part_get_index ((MMSmsPart *)ctx->current->data) == SMS_PART_INVALID_INDEX) - ctx->current = g_list_next (ctx->current); - - /* If all removed, we're done */ - if (!ctx->current) { - if (ctx->n_failed > 0) - g_simple_async_result_set_error (ctx->result, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't delete %u parts from this SMS", - ctx->n_failed); - else - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - - sms_delete_parts_context_complete_and_free (ctx); - return; - } - - cmd = g_strdup_printf ("+CMGD=%d", - mm_sms_part_get_index ((MMSmsPart *)ctx->current->data)); - mm_base_modem_at_command (ctx->modem, - cmd, - 10, - FALSE, - (GAsyncReadyCallback)delete_part_ready, - ctx); - g_free (cmd); -} - -static void -delete_lock_sms_storages_ready (MMBroadbandModem *modem, - GAsyncResult *res, - SmsDeletePartsContext *ctx) -{ - GError *error = NULL; - - if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { - g_simple_async_result_take_error (ctx->result, error); - sms_delete_parts_context_complete_and_free (ctx); - return; - } - - /* We are now locked. Whatever result we have here, we need to make sure - * we unlock the storages before finishing. */ - ctx->need_unlock = TRUE; - - /* Go on deleting parts */ - ctx->current = ctx->self->priv->parts; - delete_next_part (ctx); -} - -static void -sms_delete (MMSms *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SmsDeletePartsContext *ctx; - - ctx = g_new0 (SmsDeletePartsContext, 1); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - sms_delete); - ctx->self = g_object_ref (self); - ctx->modem = g_object_ref (self->priv->modem); - - if (mm_sms_get_storage (self) == MM_SMS_STORAGE_UNKNOWN) { - mm_dbg ("Not removing parts from non-stored SMS"); - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - sms_delete_parts_context_complete_and_free (ctx); - return; - } - - /* Select specific storage to delete from */ - mm_broadband_modem_lock_sms_storages ( - MM_BROADBAND_MODEM (self->priv->modem), - mm_sms_get_storage (self), - MM_SMS_STORAGE_UNKNOWN, /* none required for mem2 */ - (GAsyncReadyCallback)delete_lock_sms_storages_ready, - ctx); -} - -/*****************************************************************************/ - -gboolean -mm_sms_delete_finish (MMSms *self, - GAsyncResult *res, - GError **error) -{ - if (MM_SMS_GET_CLASS (self)->delete_finish) { - gboolean deleted; - - deleted = MM_SMS_GET_CLASS (self)->delete_finish (self, res, error); - if (deleted) - /* We do change the state of this SMS back to UNKNOWN, as it is no - * longer stored in the device */ - mm_gdbus_sms_set_state (MM_GDBUS_SMS (self), MM_SMS_STATE_UNKNOWN); - - return deleted; - } - - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -void -mm_sms_delete (MMSms *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - if (MM_SMS_GET_CLASS (self)->delete && - MM_SMS_GET_CLASS (self)->delete_finish) { - MM_SMS_GET_CLASS (self)->delete (self, callback, user_data); - return; - } - - g_simple_async_report_error_in_idle (G_OBJECT (self), - callback, - user_data, - MM_CORE_ERROR, - MM_CORE_ERROR_UNSUPPORTED, - "Deleting SMS is not supported by this modem"); -} - -/*****************************************************************************/ - -static gboolean -assemble_sms (MMSms *self, - GError **error) -{ - GList *l; - guint idx; - MMSmsPart **sorted_parts; - GString *fulltext; - GByteArray *fulldata; - guint validity_relative; - - sorted_parts = g_new0 (MMSmsPart *, self->priv->max_parts); - - /* Note that sequence in multipart messages start with '1', while singlepart - * messages have '0' as sequence. */ - - if (self->priv->max_parts == 1) { - if (g_list_length (self->priv->parts) != 1) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Single part message with multiple parts (%u) found", - g_list_length (self->priv->parts)); - g_free (sorted_parts); - return FALSE; - } - - sorted_parts[0] = (MMSmsPart *)self->priv->parts->data; - } else { - /* Check if we have duplicate parts */ - for (l = self->priv->parts; l; l = g_list_next (l)) { - idx = mm_sms_part_get_concat_sequence ((MMSmsPart *)l->data); - - if (idx < 1 || idx > self->priv->max_parts) { - mm_warn ("Invalid part index (%u) found, ignoring", idx); - continue; - } - - if (sorted_parts[idx - 1]) { - mm_warn ("Duplicate part index (%u) found, ignoring", idx); - continue; - } - - /* Add the part to the proper index */ - sorted_parts[idx - 1] = (MMSmsPart *)l->data; - } - } - - fulltext = g_string_new (""); - fulldata = g_byte_array_sized_new (160 * self->priv->max_parts); - - /* Assemble text and data from all parts. Now 'idx' is the index of the - * array, so for multipart messages the real index of the part is 'idx + 1' - */ - for (idx = 0; idx < self->priv->max_parts; idx++) { - const gchar *parttext; - const GByteArray *partdata; - - if (!sorted_parts[idx]) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Cannot assemble SMS, missing part at index (%u)", - self->priv->max_parts == 1 ? idx : idx + 1); - g_string_free (fulltext, TRUE); - g_byte_array_free (fulldata, TRUE); - g_free (sorted_parts); - return FALSE; - } - - /* When the user creates the SMS, it will have either 'text' or 'data', - * not both. Also status report PDUs may not have neither text nor data. */ - parttext = mm_sms_part_get_text (sorted_parts[idx]); - partdata = mm_sms_part_get_data (sorted_parts[idx]); - - if (!parttext && !partdata && - mm_sms_part_get_pdu_type (sorted_parts[idx]) != MM_SMS_PDU_TYPE_STATUS_REPORT) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Cannot assemble SMS, part at index (%u) has neither text nor data", - self->priv->max_parts == 1 ? idx : idx + 1); - g_string_free (fulltext, TRUE); - g_byte_array_free (fulldata, TRUE); - g_free (sorted_parts); - return FALSE; - } - - if (parttext) - g_string_append (fulltext, parttext); - if (partdata) - g_byte_array_append (fulldata, partdata->data, partdata->len); - } - - /* If we got all parts, we also have the first one always */ - g_assert (sorted_parts[0] != NULL); - - /* Prepare for validity tuple */ - validity_relative = mm_sms_part_get_validity_relative (sorted_parts[0]); - - /* If we got everything, assemble the text! */ - g_object_set (self, - "pdu-type", mm_sms_part_get_pdu_type (sorted_parts[0]), - "text", fulltext->str, - "data", g_variant_new_from_data (G_VARIANT_TYPE ("ay"), - fulldata->data, - fulldata->len * sizeof (guint8), - TRUE, - (GDestroyNotify) g_byte_array_unref, - g_byte_array_ref (fulldata)), - "smsc", mm_sms_part_get_smsc (sorted_parts[0]), - "class", mm_sms_part_get_class (sorted_parts[0]), - "teleservice-id", mm_sms_part_get_cdma_teleservice_id (sorted_parts[0]), - "service-category", mm_sms_part_get_cdma_service_category (sorted_parts[0]), - "number", mm_sms_part_get_number (sorted_parts[0]), - "validity", (validity_relative ? - g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (validity_relative)) : - g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))), - "timestamp", mm_sms_part_get_timestamp (sorted_parts[0]), - "discharge-timestamp", mm_sms_part_get_discharge_timestamp (sorted_parts[0]), - "delivery-state", mm_sms_part_get_delivery_state (sorted_parts[0]), - /* delivery report request and message reference taken always from the last part */ - "message-reference", mm_sms_part_get_message_reference (sorted_parts[self->priv->max_parts - 1]), - "delivery-report-request", mm_sms_part_get_delivery_report_request (sorted_parts[self->priv->max_parts - 1]), - NULL); - - g_string_free (fulltext, TRUE); - g_byte_array_unref (fulldata); - g_free (sorted_parts); - - self->priv->is_assembled = TRUE; - - return TRUE; -} - -/*****************************************************************************/ - -static guint -cmp_sms_part_sequence (MMSmsPart *a, - MMSmsPart *b) -{ - return (mm_sms_part_get_concat_sequence (a) - mm_sms_part_get_concat_sequence (b)); -} - -gboolean -mm_sms_multipart_take_part (MMSms *self, - MMSmsPart *part, - GError **error) -{ - if (!self->priv->is_multipart) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "This SMS is not a multipart message"); - return FALSE; - } - - if (g_list_length (self->priv->parts) >= self->priv->max_parts) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Already took %u parts, cannot take more", - g_list_length (self->priv->parts)); - return FALSE; - } - - if (g_list_find_custom (self->priv->parts, - part, - (GCompareFunc)cmp_sms_part_sequence)) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Cannot take part, sequence %u already taken", - mm_sms_part_get_concat_sequence (part)); - return FALSE; - } - - if (mm_sms_part_get_concat_sequence (part) > self->priv->max_parts) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Cannot take part with sequence %u, maximum is %u", - mm_sms_part_get_concat_sequence (part), - self->priv->max_parts); - return FALSE; - } - - /* Insert sorted by concat sequence */ - self->priv->parts = g_list_insert_sorted (self->priv->parts, - part, - (GCompareFunc)cmp_sms_part_sequence); - - /* We only populate contents when the multipart SMS is complete */ - if (mm_sms_multipart_is_complete (self)) { - GError *inner_error = NULL; - - if (!assemble_sms (self, &inner_error)) { - /* We DO NOT propagate the error. The part was properly taken - * so ownership passed to the MMSms object. */ - mm_warn ("Couldn't assemble SMS: '%s'", - inner_error->message); - g_error_free (inner_error); - } else { - /* Completed AND assembled - * Change state RECEIVING->RECEIVED, and signal completeness */ - if (mm_gdbus_sms_get_state (MM_GDBUS_SMS (self)) == MM_SMS_STATE_RECEIVING) - mm_gdbus_sms_set_state (MM_GDBUS_SMS (self), MM_SMS_STATE_RECEIVED); - } - } - - return TRUE; -} - -MMSms * -mm_sms_new (MMBaseModem *modem) -{ - return MM_SMS (g_object_new (MM_TYPE_SMS, - MM_SMS_MODEM, modem, - NULL)); -} - -MMSms * -mm_sms_singlepart_new (MMBaseModem *modem, - MMSmsState state, - MMSmsStorage storage, - MMSmsPart *part, - GError **error) -{ - MMSms *self; - - g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); - - /* 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", state, - "storage", storage, - NULL); - - /* Keep the single part in the list */ - self->priv->parts = g_list_prepend (self->priv->parts, part); - - if (!assemble_sms (self, error)) { - /* Note: we need to remove the part from the list, as we really didn't - * take it, and therefore the caller is responsible for freeing it. */ - self->priv->parts = g_list_remove (self->priv->parts, part); - g_clear_object (&self); - } else - /* Only export once properly created */ - mm_sms_export (self); - - return self; -} - -MMSms * -mm_sms_multipart_new (MMBaseModem *modem, - MMSmsState state, - MMSmsStorage storage, - guint reference, - guint max_parts, - MMSmsPart *first_part, - GError **error) -{ - MMSms *self; - - g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); - - /* If this is the first part of a RECEIVED SMS, we overwrite the state - * as RECEIVING, to indicate that it is not completed yet. */ - if (state == MM_SMS_STATE_RECEIVED) - state = MM_SMS_STATE_RECEIVING; - - /* 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, - MM_SMS_IS_MULTIPART, TRUE, - MM_SMS_MAX_PARTS, max_parts, - MM_SMS_MULTIPART_REFERENCE, reference, - "state", state, - "storage", storage, - "validity", g_variant_new ("(uv)", - MM_SMS_VALIDITY_TYPE_UNKNOWN, - g_variant_new_boolean (FALSE)), - NULL); - - if (!mm_sms_multipart_take_part (self, first_part, error)) - g_clear_object (&self); - - /* We do export uncomplete multipart messages, in order to be able to - * request removal of all parts of those multipart SMS that will never - * get completed. - * Only the STATE of the SMS object will be valid in the exported DBus - * interface.*/ - if (self) - mm_sms_export (self); - - return self; -} - -MMSms * -mm_sms_new_from_properties (MMBaseModem *modem, - MMSmsProperties *properties, - GError **error) -{ - MMSms *self; - const gchar *text; - GByteArray *data; - - g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); - - text = mm_sms_properties_get_text (properties); - data = mm_sms_properties_peek_data_bytearray (properties); - - /* Don't create SMS from properties if either (text|data) or number is missing */ - if (!mm_sms_properties_get_number (properties) || - (!text && !data)) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Cannot create SMS: mandatory parameter '%s' is missing", - (!mm_sms_properties_get_number (properties)? - "number" : "text' or 'data")); - return NULL; - } - - /* Don't create SMS from properties if both text and data are given */ - if (text && data) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Cannot create SMS: both 'text' and 'data' given"); - return NULL; - } - - /* 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_properties_get_teleservice_id (properties) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN ? - MM_SMS_PDU_TYPE_SUBMIT : - MM_SMS_PDU_TYPE_CDMA_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), - "teleservice-id", mm_sms_properties_get_teleservice_id (properties), - "service-category", mm_sms_properties_get_service_category (properties), - "delivery-report-request", mm_sms_properties_get_delivery_report_request (properties), - "delivery-state", MM_SMS_DELIVERY_STATE_UNKNOWN, - "validity", (mm_sms_properties_get_validity_type (properties) == MM_SMS_VALIDITY_TYPE_RELATIVE ? - g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (mm_sms_properties_get_validity_relative (properties))) : - g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))), - NULL); - - /* Only export once properly created */ - mm_sms_export (self); - - return self; -} - -/*****************************************************************************/ - -static void -set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MMSms *self = MM_SMS (object); - - switch (prop_id) { - case PROP_PATH: - g_free (self->priv->path); - self->priv->path = g_value_dup_string (value); - - /* Export when we get a DBus connection AND we have a path */ - if (!self->priv->path) - mm_sms_dbus_unexport (self); - else if (self->priv->connection) - mm_sms_dbus_export (self); - break; - case PROP_CONNECTION: - g_clear_object (&self->priv->connection); - self->priv->connection = g_value_dup_object (value); - - /* Export when we get a DBus connection AND we have a path */ - if (!self->priv->connection) - mm_sms_dbus_unexport (self); - else if (self->priv->path) - mm_sms_dbus_export (self); - break; - case PROP_MODEM: - g_clear_object (&self->priv->modem); - self->priv->modem = g_value_dup_object (value); - if (self->priv->modem) { - /* Bind the modem's connection (which is set when it is exported, - * and unset when unexported) to the SMS's connection */ - g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION, - self, MM_SMS_CONNECTION, - G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); - } - break; - case PROP_IS_MULTIPART: - self->priv->is_multipart = g_value_get_boolean (value); - break; - case PROP_MAX_PARTS: - self->priv->max_parts = g_value_get_uint (value); - break; - case PROP_MULTIPART_REFERENCE: - self->priv->multipart_reference = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MMSms *self = MM_SMS (object); - - switch (prop_id) { - case PROP_PATH: - g_value_set_string (value, self->priv->path); - break; - case PROP_CONNECTION: - g_value_set_object (value, self->priv->connection); - break; - case PROP_MODEM: - g_value_set_object (value, self->priv->modem); - break; - case PROP_IS_MULTIPART: - g_value_set_boolean (value, self->priv->is_multipart); - break; - case PROP_MAX_PARTS: - g_value_set_uint (value, self->priv->max_parts); - break; - case PROP_MULTIPART_REFERENCE: - g_value_set_uint (value, self->priv->multipart_reference); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -mm_sms_init (MMSms *self) -{ - /* Initialize private data */ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - MM_TYPE_SMS, - MMSmsPrivate); - /* Defaults */ - self->priv->max_parts = 1; -} - -static void -finalize (GObject *object) -{ - MMSms *self = MM_SMS (object); - - g_list_free_full (self->priv->parts, (GDestroyNotify)mm_sms_part_free); - g_free (self->priv->path); - - G_OBJECT_CLASS (mm_sms_parent_class)->finalize (object); -} - -static void -dispose (GObject *object) -{ - MMSms *self = MM_SMS (object); - - if (self->priv->connection) { - /* If we arrived here with a valid connection, make sure we unexport - * the object */ - mm_sms_dbus_unexport (self); - g_clear_object (&self->priv->connection); - } - - g_clear_object (&self->priv->modem); - - G_OBJECT_CLASS (mm_sms_parent_class)->dispose (object); -} - -static void -mm_sms_class_init (MMSmsClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (object_class, sizeof (MMSmsPrivate)); - - /* Virtual methods */ - object_class->get_property = get_property; - object_class->set_property = set_property; - object_class->finalize = finalize; - object_class->dispose = dispose; - - klass->store = sms_store; - klass->store_finish = sms_store_finish; - klass->send = sms_send; - klass->send_finish = sms_send_finish; - klass->delete = sms_delete; - klass->delete_finish = sms_delete_finish; - - properties[PROP_CONNECTION] = - g_param_spec_object (MM_SMS_CONNECTION, - "Connection", - "GDBus connection to the system bus.", - G_TYPE_DBUS_CONNECTION, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]); - - properties[PROP_PATH] = - g_param_spec_string (MM_SMS_PATH, - "Path", - "DBus path of the SMS", - NULL, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]); - - properties[PROP_MODEM] = - g_param_spec_object (MM_SMS_MODEM, - "Modem", - "The Modem which owns this SMS", - MM_TYPE_BASE_MODEM, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); - - properties[PROP_IS_MULTIPART] = - g_param_spec_boolean (MM_SMS_IS_MULTIPART, - "Is multipart", - "Flag specifying if the SMS is multipart", - FALSE, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_IS_MULTIPART, properties[PROP_IS_MULTIPART]); - - properties[PROP_MAX_PARTS] = - g_param_spec_uint (MM_SMS_MAX_PARTS, - "Max parts", - "Maximum number of parts composing this SMS", - 1,255, 1, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_MAX_PARTS, properties[PROP_MAX_PARTS]); - - properties[PROP_MULTIPART_REFERENCE] = - g_param_spec_uint (MM_SMS_MULTIPART_REFERENCE, - "Multipart reference", - "Common reference for all parts in the multipart SMS", - 0, G_MAXUINT, 0, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_MULTIPART_REFERENCE, properties[PROP_MULTIPART_REFERENCE]); -} diff --git a/src/mm-sms.h b/src/mm-sms.h deleted file mode 100644 index 0a3339f8..00000000 --- a/src/mm-sms.h +++ /dev/null @@ -1,129 +0,0 @@ -/* -*- 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: - * - * Author: Aleksander Morgado - * - * Copyright (C) 2012 Google, Inc. - */ - -#ifndef MM_SMS_H -#define MM_SMS_H - -#include -#include - -#define _LIBMM_INSIDE_MM -#include - -#include "mm-sms-part.h" -#include "mm-base-modem.h" - -#define MM_TYPE_SMS (mm_sms_get_type ()) -#define MM_SMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SMS, MMSms)) -#define MM_SMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SMS, MMSmsClass)) -#define MM_IS_SMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SMS)) -#define MM_IS_SMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SMS)) -#define MM_SMS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SMS, MMSmsClass)) - -typedef struct _MMSms MMSms; -typedef struct _MMSmsClass MMSmsClass; -typedef struct _MMSmsPrivate MMSmsPrivate; - -#define MM_SMS_PATH "sms-path" -#define MM_SMS_CONNECTION "sms-connection" -#define MM_SMS_MODEM "sms-modem" -#define MM_SMS_IS_MULTIPART "sms-is-multipart" -#define MM_SMS_MAX_PARTS "sms-max-parts" -#define MM_SMS_MULTIPART_REFERENCE "sms-multipart-reference" - -struct _MMSms { - MmGdbusSmsSkeleton parent; - MMSmsPrivate *priv; -}; - -struct _MMSmsClass { - MmGdbusSmsSkeletonClass parent; - - /* Store the SMS */ - void (* store) (MMSms *self, - MMSmsStorage storage, - GAsyncReadyCallback callback, - gpointer user_data); - gboolean (* store_finish) (MMSms *self, - GAsyncResult *res, - GError **error); - - /* Send the SMS */ - void (* send) (MMSms *self, - GAsyncReadyCallback callback, - gpointer user_data); - gboolean (* send_finish) (MMSms *self, - GAsyncResult *res, - GError **error); - - /* Delete the SMS */ - void (* delete) (MMSms *self, - GAsyncReadyCallback callback, - gpointer user_data); - gboolean (* delete_finish) (MMSms *self, - GAsyncResult *res, - GError **error); -}; - -GType mm_sms_get_type (void); - -/* This one can be overriden by plugins */ -MMSms *mm_sms_new (MMBaseModem *modem); - -MMSms *mm_sms_new_from_properties (MMBaseModem *modem, - MMSmsProperties *properties, - GError **error); - -MMSms *mm_sms_singlepart_new (MMBaseModem *modem, - MMSmsState state, - MMSmsStorage storage, - MMSmsPart *part, - GError **error); - -MMSms *mm_sms_multipart_new (MMBaseModem *modem, - MMSmsState state, - MMSmsStorage storage, - guint reference, - guint max_parts, - MMSmsPart *first_part, - GError **error); -gboolean mm_sms_multipart_take_part (MMSms *self, - MMSmsPart *part, - GError **error); - -void mm_sms_export (MMSms *self); -void mm_sms_unexport (MMSms *self); -const gchar *mm_sms_get_path (MMSms *self); -MMSmsStorage mm_sms_get_storage (MMSms *self); - -gboolean mm_sms_has_part_index (MMSms *self, - guint index); -GList *mm_sms_get_parts (MMSms *self); - -gboolean mm_sms_is_multipart (MMSms *self); -guint mm_sms_get_multipart_reference (MMSms *self); -gboolean mm_sms_multipart_is_complete (MMSms *self); -gboolean mm_sms_multipart_is_assembled (MMSms *self); - -void mm_sms_delete (MMSms *self, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean mm_sms_delete_finish (MMSms *self, - GAsyncResult *res, - GError **error); - -#endif /* MM_SMS_H */ -- cgit v1.2.3