diff options
author | Dan Williams <dan@ioncontrol.co> | 2025-05-08 20:36:44 -0500 |
---|---|---|
committer | Dan Williams <dan@ioncontrol.co> | 2025-05-08 20:36:44 -0500 |
commit | 4bb6026e37e74aad4faa50e89f3f4d98bec7368d (patch) | |
tree | 63d214f48abebcd33bf3f0f17ddcf8fa28cae3c5 | |
parent | efcc960b130356e6b05d05a915ff0f9646b00d5f (diff) | |
parent | 1d5cc0addb6576d007183454c0702d8ee3ab586f (diff) |
Merge request !1341 from 'mbim-multipart-fix'
sms: always use SMS_PART_INVALID_INDEX unstored parts
https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1341
Closes #979
-rw-r--r-- | src/meson.build | 34 | ||||
-rw-r--r-- | src/mm-base-bearer.c | 1 | ||||
-rw-r--r-- | src/mm-base-sms.c | 1060 | ||||
-rw-r--r-- | src/mm-base-sms.h | 49 | ||||
-rw-r--r-- | src/mm-broadband-modem-mbim.c | 81 | ||||
-rw-r--r-- | src/mm-broadband-modem-qmi.c | 49 | ||||
-rw-r--r-- | src/mm-broadband-modem.c | 190 | ||||
-rw-r--r-- | src/mm-broadband-modem.h | 19 | ||||
-rw-r--r-- | src/mm-iface-modem-messaging.c | 136 | ||||
-rw-r--r-- | src/mm-iface-modem-messaging.h | 46 | ||||
-rw-r--r-- | src/mm-iface-modem.c | 12 | ||||
-rw-r--r-- | src/mm-sms-at.c | 839 | ||||
-rw-r--r-- | src/mm-sms-at.h | 60 | ||||
-rw-r--r-- | src/mm-sms-list.c | 135 | ||||
-rw-r--r-- | src/mm-sms-list.h | 11 | ||||
-rw-r--r-- | src/mm-sms-mbim.c | 72 | ||||
-rw-r--r-- | src/mm-sms-mbim.h | 6 | ||||
-rw-r--r-- | src/mm-sms-qmi.c | 86 | ||||
-rw-r--r-- | src/mm-sms-qmi.h | 6 | ||||
-rw-r--r-- | src/tests/meson.build | 1 | ||||
-rw-r--r-- | src/tests/test-sms-list.c | 140 |
21 files changed, 1799 insertions, 1234 deletions
diff --git a/src/meson.build b/src/meson.build index c3242953..6e1aae89 100644 --- a/src/meson.build +++ b/src/meson.build @@ -210,6 +210,7 @@ libport_dep = declare_dependency( sources = files( 'mm-auth-provider.c', 'mm-context.c', + 'mm-bind.c', ) incs = [ @@ -239,6 +240,33 @@ libauth_dep = declare_dependency( link_with: libauth, ) +# SMS library +sources = files( + 'mm-sms-list.c', + 'mm-base-sms.c', +) + +incs = [ + top_inc, +] + +deps = [libmm_glib_dep, libhelpers_dep, libauth_dep] + +private_deps = [] + +libsms = static_library( + 'sms', + sources: sources, + include_directories: incs, + dependencies: deps, +) + +libsms_dep = declare_dependency( + include_directories: ['.'], + dependencies: deps, + link_with: libsms, +) + # Daemon enums, required by plugins headers = files( 'mm-base-bearer.h', @@ -297,14 +325,12 @@ sources = files( 'mm-base-modem-at.c', 'mm-base-modem.c', 'mm-base-sim.c', - 'mm-base-sms.c', - 'mm-bind.c', + 'mm-sms-at.c', 'mm-bearer-list.c', 'mm-broadband-bearer.c', 'mm-broadband-modem.c', 'mm-call-list.c', 'mm-cbm-list.c', - 'mm-context.c', 'mm-device.c', 'mm-dispatcher.c', 'mm-dispatcher-connection.c', @@ -333,7 +359,6 @@ sources = files( 'mm-port-probe.c', 'mm-port-probe-at.c', 'mm-private-boxed-types.c', - 'mm-sms-list.c', 'mm-sleep-context.c', ) @@ -343,6 +368,7 @@ deps = [ gmodule_dep, libport_dep, libqcdm_dep, + libsms_dep, libauth_dep, ] diff --git a/src/mm-base-bearer.c b/src/mm-base-bearer.c index 7dadfff0..c2ab84cf 100644 --- a/src/mm-base-bearer.c +++ b/src/mm-base-bearer.c @@ -34,7 +34,6 @@ #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-iface-modem-cdma.h" #include "mm-base-bearer.h" -#include "mm-base-modem-at.h" #include "mm-base-modem.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" diff --git a/src/mm-base-sms.c b/src/mm-base-sms.c index 0e7c7527..4f67d5ea 100644 --- a/src/mm-base-sms.c +++ b/src/mm-base-sms.c @@ -27,12 +27,8 @@ #include <libmm-glib.h> #include "mm-base-sms.h" -#include "mm-broadband-modem.h" -#include "mm-iface-modem.h" -#include "mm-iface-modem-messaging.h" +#include "mm-auth-provider.h" #include "mm-sms-part-3gpp.h" -#include "mm-base-modem-at.h" -#include "mm-base-modem.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" @@ -51,19 +47,36 @@ enum { PROP_PATH, PROP_CONNECTION, PROP_BIND_TO, - PROP_MODEM, PROP_IS_MULTIPART, PROP_MAX_PARTS, PROP_MULTIPART_REFERENCE, + PROP_IS_3GPP, + PROP_DEFAULT_STORAGE, + PROP_SUPPORTED_STORAGES, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; +enum { + SIGNAL_SET_LOCAL_MULTIPART_REFERENCE, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST] = { 0 }; + struct _MMBaseSmsPrivate { + gboolean initialized; + /* The connection to the system bus */ GDBusConnection *connection; guint dbus_id; + /* Object to which our connection property should be bound */ + GObject *connection_parent; + /* GObject property name of the parent's connection property to + * which this SMS"s connection should be bound. + */ + gchar *connection_parent_property_name; /* The authorization provider */ MMAuthProvider *authp; @@ -72,8 +85,6 @@ struct _MMBaseSmsPrivate { /* The object this SMS is bound to */ GObject *bind_to; - /* The modem which owns this SMS */ - MMBaseModem *modem; /* The path where the SMS object is exported */ gchar *path; @@ -88,6 +99,13 @@ struct _MMBaseSmsPrivate { /* Set to true when all needed parts were received, * parsed and assembled */ gboolean is_assembled; + + /* TRUE for 3GPP SMS; FALSE for CDMA */ + gboolean is_3gpp; + + /* SMS storage */ + MMSmsStorage default_storage; + GArray *supported_storages; }; /*****************************************************************************/ @@ -310,25 +328,7 @@ 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); + return self->priv->is_3gpp ? generate_3gpp_submit_pdus (self, error) : generate_cdma_submit_pdus (self, error); } /*****************************************************************************/ @@ -390,26 +390,22 @@ prepare_sms_to_be_stored (MMBaseSms *self, * when storing locally, as we could collide with the references used * in other existing messages. */ if (self->priv->is_multipart) { - GList *l; - guint8 reference; - - /* 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) { - g_prefix_error (error, "Cannot get local multipart reference: "); + const gchar *number; + + number = mm_gdbus_sms_get_number (MM_GDBUS_SMS (self)); + g_signal_emit (self, + signals[SIGNAL_SET_LOCAL_MULTIPART_REFERENCE], + 0, + number); + if (self->priv->multipart_reference == 0) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_TOO_MANY, + "Cannot create multipart SMS: No valid multipart reference " + "available for destination number '%s'", + number); return FALSE; } - - 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; @@ -421,6 +417,7 @@ handle_store_auth_ready (MMAuthProvider *authp, HandleStoreContext *ctx) { GError *error = NULL; + gboolean storage_supported = FALSE; if (!mm_auth_provider_authorize_finish (authp, res, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); @@ -452,18 +449,24 @@ handle_store_auth_ready (MMAuthProvider *authp, } /* Check if the requested storage is allowed for storing */ - if (!mm_iface_modem_messaging_is_storage_supported_for_storing (MM_IFACE_MODEM_MESSAGING (ctx->self->priv->modem), - ctx->storage, - &error)) { - mm_obj_warn (ctx->self, "failed storing SMS message: %s", error->message); - mm_dbus_method_invocation_take_error (ctx->invocation, error); - handle_store_context_free (ctx); - return; + if (ctx->self->priv->supported_storages) { + guint i; + + for (i = 0; i < ctx->self->priv->supported_storages->len; i++) { + if (ctx->storage == g_array_index (ctx->self->priv->supported_storages, MMSmsStorage, i)) { + storage_supported = TRUE; + break; + } + } } - /* Prepare the SMS to be stored, creating the PDU list if required */ - if (!prepare_sms_to_be_stored (ctx->self, &error)) { - mm_obj_warn (ctx->self, "failed preparing SMS message to be stored: %s", error->message); + if (!storage_supported) { + g_set_error (&error, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Storage '%s' is not supported for storing", + mm_sms_storage_get_string (ctx->storage)); + mm_obj_warn (ctx->self, "failed storing SMS message: %s", error->message); mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_store_context_free (ctx); return; @@ -479,6 +482,14 @@ handle_store_auth_ready (MMAuthProvider *authp, return; } + /* Prepare the SMS to be stored, creating the PDU list if required */ + if (!prepare_sms_to_be_stored (ctx->self, &error)) { + mm_obj_warn (ctx->self, "failed preparing SMS message to be stored: %s", error->message); + mm_dbus_method_invocation_take_error (ctx->invocation, error); + handle_store_context_free (ctx); + return; + } + MM_BASE_SMS_GET_CLASS (ctx->self)->store (ctx->self, ctx->storage, (GAsyncReadyCallback)handle_store_ready, @@ -499,9 +510,7 @@ handle_store (MMBaseSms *self, 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); + ctx->storage = self->priv->default_storage; g_assert (ctx->storage != MM_SMS_STORAGE_UNKNOWN); } @@ -581,7 +590,7 @@ prepare_sms_to_be_sent (MMBaseSms *self, * 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); + 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); @@ -756,6 +765,23 @@ mm_base_sms_get_multipart_reference (MMBaseSms *self) return self->priv->multipart_reference; } +void +mm_base_sms_set_multipart_reference (MMBaseSms *self, guint reference) +{ + GList *l; + + g_return_if_fail (self->priv->is_multipart); + g_return_if_fail (reference > 0); + g_return_if_fail (reference <= 255); + g_return_if_fail (self->priv->multipart_reference == 0); + + 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); + } +} + gboolean mm_base_sms_multipart_is_complete (MMBaseSms *self) { @@ -794,740 +820,6 @@ mm_base_sms_get_parts (MMBaseSms *self) /*****************************************************************************/ -static gboolean -sms_get_store_or_send_command (MMBaseSms *self, - 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 { - g_autofree gchar *hex = NULL; - g_autofree guint8 *pdu = NULL; - guint pdulen = 0; - guint msgstart = 0; - - /* AT+CMGW=<length>[, <stat>]<CR> PDU can be entered. <CTRL-Z>/<ESC> */ - - pdu = mm_sms_part_3gpp_get_submit_pdu (part, &pdulen, &msgstart, self, error); - if (!pdu) - /* 'error' should already be set */ - return FALSE; - - /* Convert PDU to hex */ - hex = mm_utils_bin2hexstr (pdu, pdulen); - 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); - } - - return TRUE; -} - -/*****************************************************************************/ -/* Store the SMS */ - -typedef struct { - MMBaseModem *modem; - MMIfacePortAt *port; - MMSmsStorage storage; - gboolean need_unlock; - gboolean use_pdu_mode; - GList *current; - gchar *msg_data; -} SmsStoreContext; - -static void -sms_store_context_free (SmsStoreContext *ctx) -{ - /* 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->port); - g_object_unref (ctx->modem); - g_free (ctx->msg_data); - g_slice_free (SmsStoreContext, ctx); -} - -static gboolean -sms_store_finish (MMBaseSms *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - -static void sms_store_next_part (GTask *task); - -static void -store_msg_data_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - SmsStoreContext *ctx; - const gchar *response; - GError *error = NULL; - gint rv; - gint idx; - - response = mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - /* Read the new part index from the reply */ - rv = sscanf (response, "+CMGW: %d", &idx); - if (rv != 1 || idx < 0) { - g_task_return_new_error (task, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't read index of already stored part: " - "%d fields parsed", - rv); - g_object_unref (task); - return; - } - - ctx = g_task_get_task_data (task); - - /* 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 (task); -} - -static void -store_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - SmsStoreContext *ctx; - GError *error = NULL; - - mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - ctx = g_task_get_task_data (task); - - /* Send the actual message data. - * We send the data as 'raw' data because we do NOT want it to - * be treated as an AT command (i.e. we don't want it prefixed - * with AT+ and suffixed with <CR><LF>), plus, we want it to be - * sent right away (not queued after other AT commands). */ - mm_base_modem_at_command_full (ctx->modem, - ctx->port, - ctx->msg_data, - 10, - FALSE, - TRUE, /* raw */ - NULL, - (GAsyncReadyCallback)store_msg_data_ready, - task); -} - -static void -sms_store_next_part (GTask *task) -{ - MMBaseSms *self; - SmsStoreContext *ctx; - GError *error = NULL; - g_autofree gchar *cmd = NULL; - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - if (!ctx->current) { - /* Done we are */ - g_task_return_boolean (task, TRUE); - g_object_unref (task); - return; - } - - g_clear_pointer (&ctx->msg_data, g_free); - - if (!sms_get_store_or_send_command (self, - (MMSmsPart *)ctx->current->data, - ctx->use_pdu_mode, - FALSE, - &cmd, - &ctx->msg_data, - &error)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - g_assert (cmd != NULL); - g_assert (ctx->msg_data != NULL); - - mm_base_modem_at_command_full (ctx->modem, - ctx->port, - cmd, - 10, - FALSE, - FALSE, /* raw */ - NULL, - (GAsyncReadyCallback)store_ready, - task); -} - -static void -store_lock_sms_storages_ready (MMBroadbandModem *modem, - GAsyncResult *res, - GTask *task) -{ - MMBaseSms *self; - SmsStoreContext *ctx; - GError *error = NULL; - - if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - /* 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 = self->priv->parts; - sms_store_next_part (task); -} - -static void -sms_store (MMBaseSms *self, - MMSmsStorage storage, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SmsStoreContext *ctx; - GTask *task; - MMIfacePortAt *port; - GError *error = NULL; - - task = g_task_new (self, NULL, callback, user_data); - - /* Select port for the operation */ - port = mm_base_modem_peek_best_at_port (self->priv->modem, &error); - if (!port) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - /* Setup the context */ - ctx = g_slice_new0 (SmsStoreContext); - ctx->modem = g_object_ref (self->priv->modem); - ctx->port = g_object_ref (port); - 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); - g_task_set_task_data (task, ctx, (GDestroyNotify)sms_store_context_free); - - /* 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, - task); -} - -/*****************************************************************************/ -/* Send the SMS */ - -typedef struct { - MMBaseModem *modem; - MMIfacePortAt *port; - gboolean need_unlock; - gboolean from_storage; - gboolean use_pdu_mode; - GList *current; - gchar *msg_data; -} SmsSendContext; - -static void -sms_send_context_free (SmsSendContext *ctx) -{ - /* 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->port); - g_object_unref (ctx->modem); - g_free (ctx->msg_data); - g_slice_free (SmsSendContext, ctx); -} - -static gboolean -sms_send_finish (MMBaseSms *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - -static void sms_send_next_part (GTask *task); - -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, - GTask *task) -{ - SmsSendContext *ctx; - GError *error = NULL; - const gchar *response; - gint message_reference; - - response = mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - message_reference = read_message_reference_from_reply (response, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - ctx = g_task_get_task_data (task); - - mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, - (guint)message_reference); - - ctx->current = g_list_next (ctx->current); - sms_send_next_part (task); -} - -static void -send_generic_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - SmsSendContext *ctx; - GError *error = NULL; - - mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - ctx = g_task_get_task_data (task); - - /* Send the actual message data. - * We send the data as 'raw' data because we do NOT want it to - * be treated as an AT command (i.e. we don't want it prefixed - * with AT+ and suffixed with <CR><LF>), plus, we want it to be - * sent right away (not queued after other AT commands). */ - mm_base_modem_at_command_full (ctx->modem, - ctx->port, - ctx->msg_data, - MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, - FALSE, - TRUE, /* raw */ - NULL, - (GAsyncReadyCallback)send_generic_msg_data_ready, - task); -} - -static void -send_from_storage_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - MMBaseSms *self; - SmsSendContext *ctx; - GError *error = NULL; - const gchar *response; - gint message_reference; - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - response = mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - mm_obj_dbg (self, "couldn't send SMS from storage: %s; trying generic send...", error->message); - g_error_free (error); - - ctx->from_storage = FALSE; - sms_send_next_part (task); - return; - } - - message_reference = read_message_reference_from_reply (response, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - 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 (task); -} - -static void -sms_send_next_part (GTask *task) -{ - MMBaseSms *self; - SmsSendContext *ctx; - GError *error = NULL; - g_autofree gchar *cmd = NULL; - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - if (!ctx->current) { - /* Done we are */ - g_task_return_boolean (task, TRUE); - g_object_unref (task); - 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_full (ctx->modem, - ctx->port, - cmd, - MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, - FALSE, - FALSE, - NULL, - (GAsyncReadyCallback)send_from_storage_ready, - task); - return; - } - - /* Generic send */ - - g_clear_pointer (&ctx->msg_data, g_free); - - if (!sms_get_store_or_send_command (self, - (MMSmsPart *)ctx->current->data, - ctx->use_pdu_mode, - TRUE, - &cmd, - &ctx->msg_data, - &error)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - g_assert (cmd != NULL); - g_assert (ctx->msg_data != NULL); - - /* no network involved in this initial AT command, so lower timeout */ - mm_base_modem_at_command_full (ctx->modem, - ctx->port, - cmd, - 10, - FALSE, - FALSE, /* raw */ - NULL, - (GAsyncReadyCallback)send_generic_ready, - task); -} - -static void -send_lock_sms_storages_ready (MMBroadbandModem *modem, - GAsyncResult *res, - GTask *task) -{ - MMBaseSms *self; - SmsSendContext *ctx; - GError *error = NULL; - - if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - /* 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 = self->priv->parts; - sms_send_next_part (task); -} - -static void -sms_send (MMBaseSms *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SmsSendContext *ctx; - GTask *task; - MMIfacePortAt *port; - GError *error = NULL; - - task = g_task_new (self, NULL, callback, user_data); - - /* Select port for the operation */ - port = mm_base_modem_peek_best_at_port (self->priv->modem, &error); - if (!port) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - /* Setup the context */ - ctx = g_slice_new0 (SmsSendContext); - ctx->modem = g_object_ref (self->priv->modem); - ctx->port = g_object_ref (port); - g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free); - - /* 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, - task); - 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 (task); -} - -/*****************************************************************************/ - -typedef struct { - MMBaseModem *modem; - gboolean need_unlock; - GList *current; - guint n_failed; -} SmsDeletePartsContext; - -static void -sms_delete_parts_context_free (SmsDeletePartsContext *ctx) -{ - /* 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_slice_free (SmsDeletePartsContext, ctx); -} - -static gboolean -sms_delete_finish (MMBaseSms *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - -static void delete_next_part (GTask *task); - -static void -delete_part_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - MMBaseSms *self; - SmsDeletePartsContext *ctx; - g_autoptr(GError) error = NULL; - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - mm_base_modem_at_command_finish (modem, res, &error); - if (error) { - ctx->n_failed++; - mm_obj_dbg (self, "couldn't delete SMS part with index %u: %s", - mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), - error->message); - } - - /* 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 (task); -} - -static void -delete_next_part (GTask *task) -{ - SmsDeletePartsContext *ctx; - g_autofree gchar *cmd = NULL; - - ctx = g_task_get_task_data (task); - - /* 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_task_return_new_error (task, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't delete %u parts from this SMS", - ctx->n_failed); - else - g_task_return_boolean (task, TRUE); - g_object_unref (task); - 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, - task); -} - -static void -delete_lock_sms_storages_ready (MMBroadbandModem *modem, - GAsyncResult *res, - GTask *task) -{ - MMBaseSms *self; - SmsDeletePartsContext *ctx; - GError *error = NULL; - - if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - /* 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 = self->priv->parts; - delete_next_part (task); -} - -static void -sms_delete (MMBaseSms *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SmsDeletePartsContext *ctx; - GTask *task; - - ctx = g_slice_new0 (SmsDeletePartsContext); - ctx->modem = g_object_ref (self->priv->modem); - - task = g_task_new (self, NULL, callback, user_data); - g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free); - - if (mm_base_sms_get_storage (self) == MM_SMS_STORAGE_UNKNOWN) { - mm_obj_dbg (self, "not removing parts from non-stored SMS"); - g_task_return_boolean (task, TRUE); - g_object_unref (task); - 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, - task); -} - -/*****************************************************************************/ - gboolean mm_base_sms_delete_finish (MMBaseSms *self, GAsyncResult *res, @@ -1800,28 +1092,15 @@ mm_base_sms_multipart_take_part (MMBaseSms *self, 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, - MM_BIND_TO, modem, - NULL)); -} - -MMBaseSms * -mm_base_sms_singlepart_new (MMBaseModem *modem, - MMSmsState state, - MMSmsStorage storage, - MMSmsPart *part, - GError **error) +gboolean +mm_base_sms_singlepart_init (MMBaseSms *self, + MMSmsState state, + MMSmsStorage storage, + MMSmsPart *part, + GError **error) { - MMBaseSms *self; - - g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); + g_assert (self->priv->initialized == FALSE); - /* 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, @@ -1837,34 +1116,31 @@ mm_base_sms_singlepart_new (MMBaseModem *modem, /* 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 FALSE; + } - return self; + /* Only export once properly created */ + self->priv->initialized = TRUE; + mm_base_sms_export (self); + return TRUE; } -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_init (MMBaseSms *self, + MMSmsState state, + MMSmsStorage storage, + guint reference, + guint max_parts, + MMSmsPart *first_part, + GError **error) { - MMBaseSms *self; - - g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); + g_assert (self->priv->initialized == FALSE); /* 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, @@ -1878,29 +1154,28 @@ mm_base_sms_multipart_new (MMBaseModem *modem, NULL); if (!mm_base_sms_multipart_take_part (self, first_part, error)) - g_clear_object (&self); + return FALSE; /* We do export incomplete 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); + self->priv->initialized = TRUE; + mm_base_sms_export (self); - return self; + return TRUE; } -MMBaseSms * -mm_base_sms_new_from_properties (MMBaseModem *modem, - MMSmsProperties *props, - GError **error) +gboolean +mm_base_sms_init_from_properties (MMBaseSms *self, + MMSmsProperties *props, + GError **error) { - MMBaseSms *self; const gchar *text; GByteArray *data; - g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem)); + g_assert (self->priv->initialized == FALSE); text = mm_sms_properties_get_text (props); data = mm_sms_properties_peek_data_bytearray (props); @@ -1914,7 +1189,7 @@ mm_base_sms_new_from_properties (MMBaseModem *modem, "Cannot create SMS: mandatory parameter '%s' is missing", (!mm_sms_properties_get_number (props)? "number" : "text' or 'data")); - return NULL; + return FALSE; } /* Don't create SMS from properties if both text and data are given */ @@ -1923,11 +1198,9 @@ mm_base_sms_new_from_properties (MMBaseModem *modem, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Cannot create SMS: both 'text' and 'data' given"); - return NULL; + return FALSE; } - /* 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, @@ -1955,10 +1228,11 @@ mm_base_sms_new_from_properties (MMBaseModem *modem, g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))), NULL); - /* Only export once properly created */ + /* Only export once properly initialized */ + self->priv->initialized = TRUE; mm_base_sms_export (self); - return self; + return TRUE; } /*****************************************************************************/ @@ -1974,6 +1248,19 @@ log_object_build_id (MMLogObject *_self) /*****************************************************************************/ +/* FIXME: use g_array_copy() when glib min version is >= 2.62 */ +static GArray * +copy_storage_array (GArray *orig) +{ + GArray *copy = NULL; + + if (orig) { + copy = g_array_sized_new (FALSE, FALSE, sizeof (MMSmsStorage), orig->len); + g_array_append_vals (copy, orig->data, orig->len); + } + return copy; +} + static void set_property (GObject *object, guint prop_id, @@ -2008,10 +1295,6 @@ set_property (GObject *object, self->priv->bind_to = g_value_dup_object (value); mm_bind_to (MM_BIND (self), MM_BASE_SMS_CONNECTION, self->priv->bind_to); break; - case PROP_MODEM: - g_clear_object (&self->priv->modem); - self->priv->modem = g_value_dup_object (value); - break; case PROP_IS_MULTIPART: self->priv->is_multipart = g_value_get_boolean (value); break; @@ -2021,6 +1304,17 @@ set_property (GObject *object, case PROP_MULTIPART_REFERENCE: self->priv->multipart_reference = g_value_get_uint (value); break; + case PROP_IS_3GPP: + self->priv->is_3gpp = g_value_get_boolean (value); + break; + case PROP_DEFAULT_STORAGE: + self->priv->default_storage = g_value_get_enum (value); + break; + case PROP_SUPPORTED_STORAGES: + /* Copy the array rather than just ref-ing it */ + g_clear_pointer (&self->priv->supported_storages, (GDestroyNotify)g_array_unref); + self->priv->supported_storages = copy_storage_array (g_value_get_boxed (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2045,9 +1339,6 @@ get_property (GObject *object, case PROP_BIND_TO: g_value_set_object (value, self->priv->bind_to); 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; @@ -2057,6 +1348,15 @@ get_property (GObject *object, case PROP_MULTIPART_REFERENCE: g_value_set_uint (value, self->priv->multipart_reference); break; + case PROP_IS_3GPP: + g_value_set_boolean (value, self->priv->is_3gpp); + break; + case PROP_DEFAULT_STORAGE: + g_value_set_enum (value, self->priv->default_storage); + break; + case PROP_SUPPORTED_STORAGES: + g_value_set_boxed (value, copy_storage_array (self->priv->supported_storages)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2104,10 +1404,10 @@ dispose (GObject *object) g_clear_object (&self->priv->connection); } - g_clear_object (&self->priv->modem); g_clear_object (&self->priv->bind_to); g_cancellable_cancel (self->priv->authp_cancellable); g_clear_object (&self->priv->authp_cancellable); + g_clear_pointer (&self->priv->supported_storages, (GDestroyNotify)g_array_unref); G_OBJECT_CLASS (mm_base_sms_parent_class)->dispose (object); } @@ -2136,13 +1436,6 @@ mm_base_sms_class_init (MMBaseSmsClass *klass) 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", @@ -2161,14 +1454,6 @@ mm_base_sms_class_init (MMBaseSmsClass *klass) g_object_class_override_property (object_class, PROP_BIND_TO, MM_BIND_TO); - 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", @@ -2192,4 +1477,39 @@ mm_base_sms_class_init (MMBaseSmsClass *klass) 0, G_MAXUINT, 0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MULTIPART_REFERENCE, properties[PROP_MULTIPART_REFERENCE]); + + properties[PROP_IS_3GPP] = + g_param_spec_boolean (MM_BASE_SMS_IS_3GPP, + "Is 3GPP", + "Whether the SMS is a 3GPP one or a CDMA one", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_IS_3GPP, properties[PROP_IS_3GPP]); + + properties[PROP_DEFAULT_STORAGE] = + g_param_spec_enum (MM_BASE_SMS_DEFAULT_STORAGE, + "Default storage", + "Default SMS storage", + MM_TYPE_SMS_STORAGE, + MM_SMS_STORAGE_UNKNOWN, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_DEFAULT_STORAGE, properties[PROP_DEFAULT_STORAGE]); + + properties[PROP_SUPPORTED_STORAGES] = + g_param_spec_boxed (MM_BASE_SMS_SUPPORTED_STORAGES, + "Supported storages", + "Array of MMSmsStorage indicating supported storages for storing the SMS", + G_TYPE_ARRAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_SUPPORTED_STORAGES, properties[PROP_SUPPORTED_STORAGES]); + + /* Signals */ + signals[SIGNAL_SET_LOCAL_MULTIPART_REFERENCE] = + g_signal_new (MM_BASE_SMS_SET_LOCAL_MULTIPART_REFERENCE, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMBaseSmsClass, set_local_multipart_reference), + NULL, NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, 0); } diff --git a/src/mm-base-sms.h b/src/mm-base-sms.h index 186cb8e6..ecd7ad2a 100644 --- a/src/mm-base-sms.h +++ b/src/mm-base-sms.h @@ -25,7 +25,6 @@ #include <libmm-glib.h> #include "mm-sms-part.h" -#include "mm-base-modem.h" /*****************************************************************************/ @@ -46,12 +45,19 @@ typedef struct _MMBaseSms MMBaseSms; typedef struct _MMBaseSmsClass MMBaseSmsClass; typedef struct _MMBaseSmsPrivate MMBaseSmsPrivate; +/* Properties */ #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" +#define MM_BASE_SMS_IS_3GPP "sms-is-3gpp" +#define MM_BASE_SMS_DEFAULT_STORAGE "sms-default-storage" +#define MM_BASE_SMS_SUPPORTED_STORAGES "sms-supported-storages" +#define MM_BASE_SMS_CONNECTION_PARENT_PROPERTY_NAME "sms-connection-parent-property-name" + +/* Signals */ +#define MM_BASE_SMS_SET_LOCAL_MULTIPART_REFERENCE "sms-set-local-multipart-reference" struct _MMBaseSms { MmGdbusSmsSkeleton parent; @@ -85,28 +91,31 @@ struct _MMBaseSmsClass { gboolean (* delete_finish) (MMBaseSms *self, GAsyncResult *res, GError **error); + + /* Signal */ + void (*set_local_multipart_reference) (MMBaseSms *self, + const gchar *number); }; GType mm_base_sms_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseSms, g_object_unref) -/* This one can be overridden 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_init_from_properties (MMBaseSms *self, + MMSmsProperties *properties, + GError **error); +gboolean mm_base_sms_singlepart_init (MMBaseSms *self, + MMSmsState state, + MMSmsStorage storage, + MMSmsPart *part, + GError **error); +gboolean mm_base_sms_multipart_init (MMBaseSms *self, + 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); @@ -123,6 +132,8 @@ GList *mm_base_sms_get_parts (MMBaseSms *self); gboolean mm_base_sms_is_multipart (MMBaseSms *self); guint mm_base_sms_get_max_parts (MMBaseSms *self); guint mm_base_sms_get_multipart_reference (MMBaseSms *self); +void mm_base_sms_set_multipart_reference (MMBaseSms *self, + guint reference); gboolean mm_base_sms_multipart_is_complete (MMBaseSms *self); gboolean mm_base_sms_multipart_is_assembled (MMBaseSms *self); diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index 8116d201..9957fe9e 100644 --- a/src/mm-broadband-modem-mbim.c +++ b/src/mm-broadband-modem-mbim.c @@ -5840,6 +5840,7 @@ static gboolean process_pdu_messages (MMBroadbandModemMbim *self, guint32 messages_count, MbimSmsPduReadRecordArray *pdu_messages, guint expected_index, + gboolean unstored, GError **error); static void @@ -5864,6 +5865,7 @@ sms_notification_read_flash_sms (MMBroadbandModemMbim *self, messages_count, pdu_messages, SMS_PART_INVALID_INDEX, + TRUE, &error)) mm_obj_dbg (self, "flash SMS message reading failed: %s", error->message); } @@ -5943,6 +5945,7 @@ alert_sms_read_query_ready (MbimDevice *device, messages_count, pdu_messages, ctx->expected_index, + FALSE, &error)) mm_obj_dbg (ctx->self, "SMS message reading failed: %s", error->message); @@ -9024,35 +9027,45 @@ load_initial_sms_parts_finish (MMIfaceModemMessaging *self, static void add_sms_part (MMBroadbandModemMbim *self, const MbimSmsPduReadRecord *pdu, + guint part_index, guint expected_index) { MMSmsPart *part; g_autoptr(GError) error = NULL; - part = mm_sms_part_3gpp_new_from_binary_pdu (pdu->message_index, + part = mm_sms_part_3gpp_new_from_binary_pdu (part_index, pdu->pdu_data, pdu->pdu_data_size, self, FALSE, &error); - if (part) { - mm_obj_dbg (self, "correctly parsed PDU (%d)", pdu->message_index); - if (expected_index != SMS_PART_INVALID_INDEX && (expected_index != mm_sms_part_get_index (part))) { - /* Some Fibocom L850 modems report an invalid part index (always 1) in the response - * message. Because we know which message part was requested, we can use that value - * instead to workaround this bug. */ - mm_obj_dbg (self, "Expected SMS part index '%u' but device reports index '%u': using the expected one", - expected_index, mm_sms_part_get_index (part)); - mm_sms_part_set_index (part, expected_index); - } - mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), - part, - mm_sms_state_from_mbim_message_status (pdu->message_status), - MM_SMS_STORAGE_MT); - } else { + if (!part) { /* Don't treat the error as critical */ mm_obj_dbg (self, "error parsing PDU (%d): %s", pdu->message_index, error->message); + return; + } + + mm_obj_dbg (self, "correctly parsed PDU (%d)", pdu->message_index); + if (expected_index != SMS_PART_INVALID_INDEX && (expected_index != mm_sms_part_get_index (part))) { + /* Some Fibocom L850 modems report an invalid part index (always 1) in the response + * message. Because we know which message part was requested, we can use that value + * instead to workaround this bug. */ + mm_obj_dbg (self, "Expected SMS part index '%u' but device reports index '%u': using the expected one", + expected_index, mm_sms_part_get_index (part)); + mm_sms_part_set_index (part, expected_index); + } + + if (!mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), + mm_broadband_modem_create_sms (MM_BROADBAND_MODEM (self)), + part, + mm_sms_state_from_mbim_message_status (pdu->message_status), + MM_SMS_STORAGE_MT, + &error)) { + /* Don't treat the error as critical */ + mm_obj_dbg (self, "error adding SMS (%d): %s", + pdu->message_index, error->message); + return; } } @@ -9062,6 +9075,7 @@ process_pdu_messages (MMBroadbandModemMbim *self, guint32 messages_count, MbimSmsPduReadRecordArray *pdu_messages, guint expected_index, + gboolean unstored, GError **error) { guint i; @@ -9087,11 +9101,26 @@ process_pdu_messages (MMBroadbandModemMbim *self, mm_obj_dbg (self, "%u SMS PDUs reported", messages_count); for (i = 0; i < messages_count; i++) { - if (pdu_messages[i]) { - mm_obj_dbg (self, "processing SMS PDU %u/%u...", i+1, messages_count); - add_sms_part (self, pdu_messages[i], expected_index); - } else + guint message_index; + + if (!pdu_messages[i]) { mm_obj_dbg (self, "ignoring invalid SMS PDU %u/%u...", i+1, messages_count); + continue; + } + + mm_obj_dbg (self, "processing SMS PDU %u/%u...", i+1, messages_count); + + message_index = pdu_messages[i]->message_index; + if (unstored && message_index != 0) { + mm_obj_warn (self, + "processed unstored PDU with non-zero message index (%u)", + message_index); + } + + add_sms_part (self, + pdu_messages[i], + unstored ? SMS_PART_INVALID_INDEX : message_index, + expected_index); } return TRUE; @@ -9126,6 +9155,7 @@ sms_read_query_ready (MbimDevice *device, messages_count, pdu_messages, SMS_PART_INVALID_INDEX, + FALSE, &error)) g_task_return_error (task, error); else @@ -9231,9 +9261,14 @@ enable_unsolicited_events_messaging (MMIfaceModemMessaging *_self, /* Create SMS (Messaging interface) */ static MMBaseSms * -messaging_create_sms (MMIfaceModemMessaging *self) +messaging_create_sms (MMBroadbandModem *self) { - return mm_sms_mbim_new (MM_BASE_MODEM (self)); + MMSmsStorage default_storage; + + default_storage = mm_iface_modem_messaging_get_default_storage (MM_IFACE_MODEM_MESSAGING (self)); + return mm_sms_mbim_new (MM_BASE_MODEM (self), + mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)), + default_storage); } /*****************************************************************************/ @@ -10716,7 +10751,6 @@ iface_modem_messaging_init (MMIfaceModemMessagingInterface *iface) iface->enable_unsolicited_events_finish = common_enable_disable_unsolicited_events_messaging_finish; iface->disable_unsolicited_events = disable_unsolicited_events_messaging; iface->disable_unsolicited_events_finish = common_enable_disable_unsolicited_events_messaging_finish; - iface->create_sms = messaging_create_sms; } static void @@ -10786,6 +10820,7 @@ mm_broadband_modem_mbim_class_init (MMBroadbandModemMbimClass *klass) /* Do not initialize the MBIM modem through AT commands */ broadband_modem_class->enabling_modem_init = NULL; broadband_modem_class->enabling_modem_init_finish = NULL; + broadband_modem_class->create_sms = messaging_create_sms; #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED g_object_class_install_property (object_class, PROP_QMI_UNSUPPORTED, diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c index a2609277..6d5b8b20 100644 --- a/src/mm-broadband-modem-qmi.c +++ b/src/mm-broadband-modem-qmi.c @@ -7938,8 +7938,8 @@ add_new_read_sms_part (MMIfaceModemMessaging *self, gboolean transfer_route, GArray *data) { - MMSmsPart *part = NULL; - GError *error = NULL; + MMSmsPart *part = NULL; + g_autoptr(GError) error = NULL; switch (format) { case QMI_WMS_MESSAGE_FORMAT_CDMA: @@ -7967,16 +7967,21 @@ add_new_read_sms_part (MMIfaceModemMessaging *self, break; } - if (part) { - mm_obj_dbg (self, "correctly parsed PDU (%d)", index); - mm_iface_modem_messaging_take_part (self, - part, - mm_sms_state_from_qmi_message_tag (tag), - mm_sms_storage_from_qmi_storage_type (storage)); - } else if (error) { + if (!part) { /* Don't treat the error as critical */ mm_obj_dbg (self, "error parsing PDU (%d): %s", index, error->message); - g_error_free (error); + return; + } + + mm_obj_dbg (self, "correctly parsed PDU (%d)", index); + if (!mm_iface_modem_messaging_take_part (self, + mm_broadband_modem_create_sms (MM_BROADBAND_MODEM (self)), + part, + mm_sms_state_from_qmi_message_tag (tag), + mm_sms_storage_from_qmi_storage_type (storage), + &error)) { + /* Don't treat the error as critical */ + mm_obj_dbg (self, "error adding SMS (%d): %s", index, error->message); } } @@ -8409,7 +8414,6 @@ messaging_event_report_indication_cb (QmiClientNas *client, QmiWmsAckIndicator ack_ind; guint32 transaction_id; QmiWmsMessageFormat msg_format; - QmiWmsMessageTagType tag; GArray *raw_data = NULL; /* Handle transfer-route MT messages */ @@ -8454,13 +8458,10 @@ messaging_event_report_indication_cb (QmiClientNas *client, } /* Defaults for transfer-route messages, which are not stored anywhere */ - storage = QMI_WMS_STORAGE_TYPE_NONE; - memory_index = 0; - tag = QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ; add_new_read_sms_part (MM_IFACE_MODEM_MESSAGING (self), - storage, - memory_index, - tag, + QMI_WMS_STORAGE_TYPE_NONE, + SMS_PART_INVALID_INDEX, + QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ, msg_format, TRUE, raw_data); @@ -8870,19 +8871,23 @@ messaging_enable_unsolicited_events (MMIfaceModemMessaging *_self, } /*****************************************************************************/ -/* Create SMS (Messaging interface) */ +/* Create SMS */ static MMBaseSms * -messaging_create_sms (MMIfaceModemMessaging *_self) +messaging_create_sms (MMBroadbandModem *_self) { MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); + MMSmsStorage default_storage; /* Handle AT URC only fallback */ if (self->priv->messaging_fallback_at_only) { - return iface_modem_messaging_parent->create_sms (_self); + return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_parent_class)->create_sms (_self); } - return mm_sms_qmi_new (MM_BASE_MODEM (self)); + default_storage = mm_iface_modem_messaging_get_default_storage (MM_IFACE_MODEM_MESSAGING (self)); + return mm_sms_qmi_new (MM_BASE_MODEM (self), + mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)), + default_storage); } @@ -15330,7 +15335,6 @@ iface_modem_messaging_init (MMIfaceModemMessagingInterface *iface) iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish; iface->disable_unsolicited_events = messaging_disable_unsolicited_events; iface->disable_unsolicited_events_finish = messaging_disable_unsolicited_events_finish; - iface->create_sms = messaging_create_sms; iface->init_current_storages = messaging_init_current_storages; iface->init_current_storages_finish = messaging_init_current_storages_finish; } @@ -15469,4 +15473,5 @@ mm_broadband_modem_qmi_class_init (MMBroadbandModemQmiClass *klass) /* Do not initialize the QMI modem through AT commands */ broadband_modem_class->enabling_modem_init = NULL; broadband_modem_class->enabling_modem_init_finish = NULL; + broadband_modem_class->create_sms = messaging_create_sms; } diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index 16d579c7..f164be01 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -52,6 +52,7 @@ #include "mm-cbm-part.h" #include "mm-sms-list.h" #include "mm-sms-part-3gpp.h" +#include "mm-sms-at.h" #include "mm-call-list.h" #include "mm-base-sim.h" #include "mm-log-object.h" @@ -7185,11 +7186,13 @@ modem_messaging_init_current_storages (MMIfaceModemMessaging *self, * the default one if needed. */ -void -mm_broadband_modem_unlock_sms_storages (MMBroadbandModem *self, - gboolean mem1, - gboolean mem2) +static void +modem_messaging_unlock_storages (MMIfaceModemMessaging *_self, + gboolean mem1, + gboolean mem2) { + MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); + if (mem1) { g_assert (self->priv->mem1_storage_locked); self->priv->mem1_storage_locked = FALSE; @@ -7201,10 +7204,10 @@ mm_broadband_modem_unlock_sms_storages (MMBroadbandModem *self, } } -gboolean -mm_broadband_modem_lock_sms_storages_finish (MMBroadbandModem *self, - GAsyncResult *res, - GError **error) +static gboolean +modem_messaging_lock_storages_finish (MMIfaceModemMessaging *self, + GAsyncResult *res, + GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } @@ -7246,13 +7249,14 @@ lock_storages_cpms_set_ready (MMBaseModem *_self, g_object_unref (task); } -void -mm_broadband_modem_lock_sms_storages (MMBroadbandModem *self, - MMSmsStorage mem1, /* reading/listing/deleting */ - MMSmsStorage mem2, /* storing/sending */ - GAsyncReadyCallback callback, - gpointer user_data) +static void +modem_messaging_lock_storages (MMIfaceModemMessaging *_self, + MMSmsStorage mem1, /* reading/listing/deleting */ + MMSmsStorage mem2, /* storing/sending */ + GAsyncReadyCallback callback, + gpointer user_data) { + MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); LockSmsStoragesContext *ctx; GTask *task; gchar *cmd = NULL; @@ -7267,7 +7271,7 @@ mm_broadband_modem_lock_sms_storages (MMBroadbandModem *self, self, callback, user_data, - mm_broadband_modem_lock_sms_storages, + modem_messaging_lock_storages, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "SMS storage currently locked, try again later"); @@ -7541,13 +7545,13 @@ sms_part_ready (MMBroadbandModem *self, GTask *task) { SmsPartContext *ctx; - MMSmsPart *part; - MM3gppPduInfo *info; - const gchar *response; - GError *error = NULL; + MMSmsPart *part; + MM3gppPduInfo *info; + const gchar *response; + GError *error = NULL; /* Always always always unlock mem1 storage. Warned you've been. */ - mm_broadband_modem_unlock_sms_storages (self, TRUE, FALSE); + mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (self), TRUE, FALSE); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { @@ -7570,16 +7574,22 @@ sms_part_ready (MMBroadbandModem *self, } part = mm_sms_part_3gpp_new_from_pdu (info->index, info->pdu, self, &error); - if (part) { - mm_obj_dbg (self, "correctly parsed PDU (%d)", ctx->idx); - mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), - part, - MM_SMS_STATE_RECEIVED, - self->priv->modem_messaging_sms_default_storage); - } else { + if (!part) { /* Don't treat the error as critical */ mm_obj_dbg (self, "error parsing PDU (%d): %s", ctx->idx, error->message); g_error_free (error); + } else { + mm_obj_dbg (self, "correctly parsed PDU (%d)", ctx->idx); + if (!mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), + mm_broadband_modem_create_sms (MM_BROADBAND_MODEM (self)), + part, + MM_SMS_STATE_RECEIVED, + self->priv->modem_messaging_sms_default_storage, + &error)) { + /* Don't treat the error as critical */ + mm_obj_dbg (self, "error adding SMS (%d): %s", ctx->idx, error->message); + g_error_free (error); + } } /* All done */ @@ -7589,15 +7599,15 @@ sms_part_ready (MMBroadbandModem *self, } static void -indication_lock_storages_ready (MMBroadbandModem *self, - GAsyncResult *res, - GTask *task) +indication_lock_storages_ready (MMIfaceModemMessaging *messaging, + GAsyncResult *res, + GTask *task) { SmsPartContext *ctx; gchar *command; GError *error = NULL; - if (!mm_broadband_modem_lock_sms_storages_finish (self, res, &error)) { + if (!mm_iface_modem_messaging_lock_storages_finish (messaging, res, &error)) { /* TODO: we should either make this lock() never fail, by automatically * retrying after some time, or otherwise retry here. */ g_task_return_error (task, error); @@ -7610,7 +7620,7 @@ indication_lock_storages_ready (MMBroadbandModem *self, /* Retrieve the message */ ctx = g_task_get_task_data (task); command = g_strdup_printf ("+CMGR=%d", ctx->idx); - mm_base_modem_at_command (MM_BASE_MODEM (self), + mm_base_modem_at_command (MM_BASE_MODEM (messaging), command, 10, FALSE, @@ -7658,11 +7668,11 @@ cmti_received (MMPortSerialAt *port, g_task_set_task_data (task, ctx, g_free); /* First, request to set the proper storage to read from */ - mm_broadband_modem_lock_sms_storages (self, - storage, - MM_SMS_STORAGE_UNKNOWN, - (GAsyncReadyCallback)indication_lock_storages_ready, - task); + mm_iface_modem_messaging_lock_storages (MM_IFACE_MODEM_MESSAGING (self), + storage, + MM_SMS_STORAGE_UNKNOWN, + (GAsyncReadyCallback)indication_lock_storages_ready, + task); } static void @@ -7670,10 +7680,10 @@ cds_received (MMPortSerialAt *port, GMatchInfo *info, MMBroadbandModem *self) { - GError *error = NULL; - MMSmsPart *part; - guint length; - gchar *pdu; + g_autoptr(GError) error = NULL; + MMSmsPart *part; + guint length; + gchar *pdu; mm_obj_dbg (self, "got new non-stored message indication"); @@ -7685,16 +7695,20 @@ cds_received (MMPortSerialAt *port, return; part = mm_sms_part_3gpp_new_from_pdu (SMS_PART_INVALID_INDEX, pdu, self, &error); - if (part) { - mm_obj_dbg (self, "correctly parsed non-stored PDU"); - mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), - part, - MM_SMS_STATE_RECEIVED, - MM_SMS_STORAGE_UNKNOWN); - } else { + if (!part) { /* Don't treat the error as critical */ mm_obj_dbg (self, "error parsing non-stored PDU: %s", error->message); - g_error_free (error); + } else { + mm_obj_dbg (self, "correctly parsed non-stored PDU"); + if (!mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), + mm_broadband_modem_create_sms (MM_BROADBAND_MODEM (self)), + part, + MM_SMS_STATE_RECEIVED, + MM_SMS_STORAGE_UNKNOWN, + &error)) { + /* Don't treat the error as critical */ + mm_obj_dbg (self, "error adding SMS: %s", error->message); + } } } @@ -8078,10 +8092,16 @@ sms_text_part_list_ready (MMBroadbandModem *self, mm_sms_part_set_class (part, -1); mm_obj_dbg (self, "correctly parsed SMS list entry (%d)", idx); - mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), - part, - sms_state_from_str (stat), - ctx->list_storage); + if (!mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), + mm_broadband_modem_create_sms (MM_BROADBAND_MODEM (self)), + part, + sms_state_from_str (stat), + ctx->list_storage, + &error)) { + mm_obj_dbg (self, "failed to add SMS: %s", inner_error->message); + goto next; + } + next: g_match_info_next (match_info, NULL); } @@ -8120,7 +8140,7 @@ sms_pdu_part_list_ready (MMBroadbandModem *self, GList *l; /* Always always always unlock mem1 storage. Warned you've been. */ - mm_broadband_modem_unlock_sms_storages (self, TRUE, FALSE); + mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (self), TRUE, FALSE); response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { @@ -8143,16 +8163,22 @@ sms_pdu_part_list_ready (MMBroadbandModem *self, MMSmsPart *part; part = mm_sms_part_3gpp_new_from_pdu (info->index, info->pdu, self, &error); - if (part) { - mm_obj_dbg (self, "correctly parsed PDU (%d)", info->index); - mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), - part, - sms_state_from_index (info->status), - ctx->list_storage); - } else { + if (!part) { /* Don't treat the error as critical */ mm_obj_dbg (self, "error parsing PDU (%d): %s", info->index, error->message); g_clear_error (&error); + } else { + mm_obj_dbg (self, "correctly parsed PDU (%d)", info->index); + if (!mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self), + mm_broadband_modem_create_sms (MM_BROADBAND_MODEM (self)), + part, + sms_state_from_index (info->status), + ctx->list_storage, + &error)) { + /* Don't treat the error as critical */ + mm_obj_dbg (self, "error adding SMS (%d): %s", info->index, error->message); + g_clear_error (&error); + } } } @@ -8164,13 +8190,13 @@ sms_pdu_part_list_ready (MMBroadbandModem *self, } static void -list_parts_lock_storages_ready (MMBroadbandModem *self, - GAsyncResult *res, - GTask *task) +list_parts_lock_storages_ready (MMIfaceModemMessaging *self, + GAsyncResult *res, + GTask *task) { GError *error = NULL; - if (!mm_broadband_modem_lock_sms_storages_finish (self, res, &error)) { + if (!mm_iface_modem_messaging_lock_storages_finish (self, res, &error)) { /* TODO: we should either make this lock() never fail, by automatically * retrying after some time, or otherwise retry here. */ g_task_return_error (task, error); @@ -8212,20 +8238,33 @@ modem_messaging_load_initial_sms_parts (MMIfaceModemMessaging *self, mm_obj_dbg (self, "listing SMS parts in storage '%s'", mm_sms_storage_get_string (storage)); /* First, request to set the proper storage to read from */ - mm_broadband_modem_lock_sms_storages (MM_BROADBAND_MODEM (self), - storage, - MM_SMS_STORAGE_UNKNOWN, - (GAsyncReadyCallback)list_parts_lock_storages_ready, - task); + mm_iface_modem_messaging_lock_storages (self, + storage, + MM_SMS_STORAGE_UNKNOWN, + (GAsyncReadyCallback)list_parts_lock_storages_ready, + task); } /*****************************************************************************/ -/* Create SMS (Messaging interface) */ +/* Create SMS */ + +MMBaseSms * +mm_broadband_modem_create_sms (MMBroadbandModem *self) +{ + g_assert (MM_BROADBAND_MODEM_GET_CLASS (self)->create_sms != NULL); + + return MM_BROADBAND_MODEM_GET_CLASS (self)->create_sms (self); +} static MMBaseSms * -modem_messaging_create_sms (MMIfaceModemMessaging *self) +modem_messaging_create_sms (MMBroadbandModem *self) { - return mm_base_sms_new (MM_BASE_MODEM (self)); + MMSmsStorage default_storage; + + default_storage = mm_iface_modem_messaging_get_default_storage (MM_IFACE_MODEM_MESSAGING (self)); + return mm_sms_at_new (MM_BASE_MODEM (self), + mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)), + default_storage); } /*****************************************************************************/ @@ -14537,9 +14576,11 @@ iface_modem_messaging_init (MMIfaceModemMessagingInterface *iface) iface->enable_unsolicited_events_finish = modem_messaging_enable_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_messaging_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_messaging_setup_cleanup_unsolicited_events_finish; - iface->create_sms = modem_messaging_create_sms; iface->init_current_storages = modem_messaging_init_current_storages; iface->init_current_storages_finish = modem_messaging_init_current_storages_finish; + iface->lock_storages = modem_messaging_lock_storages; + iface->lock_storages_finish = modem_messaging_lock_storages_finish; + iface->unlock_storages = modem_messaging_unlock_storages; } static void @@ -14656,6 +14697,7 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass) klass->disabling_stopped = disabling_stopped; klass->load_initial_eps_bearer_cid = load_initial_eps_bearer_cid; klass->load_initial_eps_bearer_cid_finish = load_initial_eps_bearer_cid_finish; + klass->create_sms = modem_messaging_create_sms; g_object_class_override_property (object_class, PROP_MODEM_DBUS_SKELETON, diff --git a/src/mm-broadband-modem.h b/src/mm-broadband-modem.h index f820e045..b96e64e3 100644 --- a/src/mm-broadband-modem.h +++ b/src/mm-broadband-modem.h @@ -27,6 +27,7 @@ #include "mm-modem-helpers.h" #include "mm-charsets.h" #include "mm-base-modem.h" +#include "mm-base-sms.h" #define MM_TYPE_BROADBAND_MODEM (mm_broadband_modem_get_type ()) #define MM_BROADBAND_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM, MMBroadbandModem)) @@ -105,6 +106,9 @@ struct _MMBroadbandModemClass { gint (* load_initial_eps_bearer_cid_finish) (MMBroadbandModem *self, GAsyncResult *res, GError **error); + + /* Create SMS objects */ + MMBaseSms * (* create_sms) (MMBroadbandModem *self); }; GType mm_broadband_modem_get_type (void); @@ -126,18 +130,6 @@ gchar *mm_broadband_modem_create_device_identifier (MMBroadbandModem *self, const gchar *ati1, GError **error); -/* Locking/unlocking SMS storages */ -void mm_broadband_modem_lock_sms_storages (MMBroadbandModem *self, - MMSmsStorage mem1, /* reading/listing/deleting */ - MMSmsStorage mem2, /* storing/sending */ - GAsyncReadyCallback callback, - gpointer user_data); -gboolean mm_broadband_modem_lock_sms_storages_finish (MMBroadbandModem *self, - GAsyncResult *res, - GError **error); -void mm_broadband_modem_unlock_sms_storages (MMBroadbandModem *self, - gboolean mem1, - gboolean mem2); /* Helper to update SIM hot swap */ gboolean mm_broadband_modem_sim_hot_swap_ports_context_init (MMBroadbandModem *self, GError **error); @@ -152,4 +144,7 @@ gboolean mm_broadband_modem_get_active_multiplexed_bearers (MMBroadbandModem *s /* Helper to manage initial EPS bearer */ gint mm_broadband_modem_get_initial_eps_bearer_cid (MMBroadbandModem *self); +/* Helper to create a new modem-specific SMS object */ +MMBaseSms *mm_broadband_modem_create_sms (MMBroadbandModem *self); + #endif /* MM_BROADBAND_MODEM_H */ diff --git a/src/mm-iface-modem-messaging.c b/src/mm-iface-modem-messaging.c index 581843c5..93cd0ae1 100644 --- a/src/mm-iface-modem-messaging.c +++ b/src/mm-iface-modem-messaging.c @@ -22,6 +22,7 @@ #include "mm-sms-list.h" #include "mm-error-helpers.h" #include "mm-log-object.h" +#include "mm-broadband-modem.h" #define SUPPORT_CHECKED_TAG "messaging-support-checked-tag" #define SUPPORTED_TAG "messaging-supported-tag" @@ -35,68 +36,53 @@ G_DEFINE_INTERFACE (MMIfaceModemMessaging, mm_iface_modem_messaging, MM_TYPE_IFA /*****************************************************************************/ -guint8 -mm_iface_modem_messaging_get_local_multipart_reference (MMIfaceModemMessaging *self, - const gchar *number, - GError **error) +void +mm_iface_modem_messaging_bind_simple_status (MMIfaceModemMessaging *self, + MMSimpleStatus *status) { - MMSmsList *list = NULL; - guint8 reference; - guint8 first; - - /* Start by looking for a random number */ - reference = g_random_int_range (1,255); - - /* Then, look for the given reference in user-created messages */ - g_object_get (self, - MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, - NULL); - if (!list) - return reference; - - first = reference; - do { - if (!mm_sms_list_has_local_multipart_reference (list, number, reference)) { - g_object_unref (list); - return reference; - } - - if (reference == 255) - reference = 1; - else - reference++; - } - while (reference != first); - - g_object_unref (list); - - /* We were not able to find a new valid multipart reference :/ - * return an error */ - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_TOO_MANY, - "Cannot create multipart SMS: No valid multipart reference " - "available for destination number '%s'", - number); - return 0; } /*****************************************************************************/ void -mm_iface_modem_messaging_bind_simple_status (MMIfaceModemMessaging *self, - MMSimpleStatus *status) +mm_iface_modem_messaging_lock_storages (MMIfaceModemMessaging *self, + MMSmsStorage mem1, + MMSmsStorage mem2, + GAsyncReadyCallback callback, + gpointer user_data) { + g_assert (MM_IFACE_MODEM_MESSAGING_GET_IFACE (self)->lock_storages != NULL); + + MM_IFACE_MODEM_MESSAGING_GET_IFACE (self)->lock_storages (self, + mem1, + mem2, + callback, + user_data); } -/*****************************************************************************/ +gboolean +mm_iface_modem_messaging_lock_storages_finish (MMIfaceModemMessaging *self, + GAsyncResult *res, + GError **error) -MMBaseSms * -mm_iface_modem_messaging_create_sms (MMIfaceModemMessaging *self) { - g_assert (MM_IFACE_MODEM_MESSAGING_GET_IFACE (self)->create_sms != NULL); + g_assert (MM_IFACE_MODEM_MESSAGING_GET_IFACE (self)->lock_storages_finish != NULL); - return MM_IFACE_MODEM_MESSAGING_GET_IFACE (self)->create_sms (self); + return MM_IFACE_MODEM_MESSAGING_GET_IFACE (self)->lock_storages_finish (self, + res, + error); +} + +void +mm_iface_modem_messaging_unlock_storages (MMIfaceModemMessaging *self, + gboolean mem1, + gboolean mem2) +{ + g_assert (MM_IFACE_MODEM_MESSAGING_GET_IFACE (self)->unlock_storages != NULL); + + MM_IFACE_MODEM_MESSAGING_GET_IFACE (self)->unlock_storages (self, + mem1, + mem2); } /*****************************************************************************/ @@ -296,8 +282,8 @@ handle_create_auth_ready (MMIfaceAuth *auth, return; } - sms = mm_base_sms_new_from_properties (MM_BASE_MODEM (self), properties, &error); - if (!sms) { + sms = mm_broadband_modem_create_sms (MM_BROADBAND_MODEM (self)); + if (!mm_base_sms_init_from_properties (sms, properties, &error)) { mm_dbus_method_invocation_take_error (ctx->invocation, error); handle_create_context_free (ctx); return; @@ -399,7 +385,8 @@ handle_set_default_storage_ready (MMIfaceModemMessaging *self, GAsyncResult *res, HandleSetDefaultStorageContext *ctx) { - GError *error = NULL; + GError *error = NULL; + g_autoptr(MMSmsList) list = NULL; if (!MM_IFACE_MODEM_MESSAGING_GET_IFACE (self)->set_default_storage_finish (self, res, &error)) { mm_obj_warn (self, "could not set default storage: %s", error->message); @@ -412,6 +399,13 @@ handle_set_default_storage_ready (MMIfaceModemMessaging *self, MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, ctx->storage, NULL); + /* Update default storage in all SMSs too */ + g_object_get (self, + MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, + NULL); + if (list) + mm_sms_list_set_default_storage (list, ctx->storage); + mm_obj_info (self, "set the default storage successfully"); mm_gdbus_modem_messaging_complete_set_default_storage (ctx->skeleton, ctx->invocation); handle_set_default_storage_context_free (ctx); @@ -482,13 +476,14 @@ handle_set_default_storage (MmGdbusModemMessaging *skeleton, /*****************************************************************************/ gboolean -mm_iface_modem_messaging_take_part (MMIfaceModemMessaging *self, - MMSmsPart *sms_part, - MMSmsState state, - MMSmsStorage storage) +mm_iface_modem_messaging_take_part (MMIfaceModemMessaging *self, + MMBaseSms *uninitialized_sms, + MMSmsPart *sms_part, + MMSmsState state, + MMSmsStorage storage, + GError **error) { g_autoptr(MMSmsList) list = NULL; - g_autoptr(GError) error = NULL; gboolean added = FALSE; g_object_get (self, @@ -496,15 +491,20 @@ mm_iface_modem_messaging_take_part (MMIfaceModemMessaging *self, NULL); if (list) { - added = mm_sms_list_take_part (list, sms_part, state, storage, &error); + added = mm_sms_list_take_part (list, uninitialized_sms, sms_part, state, storage, error); if (!added) - mm_obj_dbg (self, "couldn't take part in SMS list: %s", error->message); + g_prefix_error (error, "couldn't take part in SMS list: "); } - /* If part wasn't taken, we need to free the part ourselves */ + /* If part wasn't taken, we need to free the part and SMS ourselves */ if (!added) mm_sms_part_free (sms_part); + /* Always drop original ref to the uninialized SMS, as MMSmsList will have + * taken a reference to the SMS if it wants to keep it around. + */ + g_object_unref (uninitialized_sms); + return added; } @@ -558,6 +558,18 @@ mm_iface_modem_messaging_is_storage_supported_for_receiving (MMIfaceModemMessagi error); } +MMSmsStorage +mm_iface_modem_messaging_get_default_storage (MMIfaceModemMessaging *self) +{ + MMSmsStorage storage = MM_SMS_STORAGE_UNKNOWN; + + g_object_get (self, + MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, &storage, + NULL); + g_assert (storage != MM_SMS_STORAGE_UNKNOWN); + return storage; +} + /*****************************************************************************/ static void @@ -1008,7 +1020,7 @@ interface_enabling_step (GTask *task) case ENABLING_STEP_FIRST: { MMSmsList *list; - list = mm_sms_list_new (MM_BASE_MODEM (self), G_OBJECT (self)); + list = mm_sms_list_new (G_OBJECT (self)); g_object_set (self, MM_IFACE_MODEM_MESSAGING_SMS_LIST, list, NULL); diff --git a/src/mm-iface-modem-messaging.h b/src/mm-iface-modem-messaging.h index 44cb077b..6cf7a0f6 100644 --- a/src/mm-iface-modem-messaging.h +++ b/src/mm-iface-modem-messaging.h @@ -78,6 +78,21 @@ struct _MMIfaceModemMessagingInterface { GAsyncResult *res, GError **error); + /* Lock storages (async) */ + void (*lock_storages) (MMIfaceModemMessaging *self, + MMSmsStorage mem1, /* reading/listing/deleting */ + MMSmsStorage mem2, /* storing/sending */ + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*lock_storages_finish) (MMIfaceModemMessaging *self, + GAsyncResult *res, + GError **error); + + /* Unlock storages */ + void (*unlock_storages) (MMIfaceModemMessaging *self, + gboolean mem1, + gboolean mem2); + /* Setup SMS format (async) */ void (* setup_sms_format) (MMIfaceModemMessaging *self, GAsyncReadyCallback callback, @@ -127,9 +142,6 @@ struct _MMIfaceModemMessagingInterface { gboolean (*load_initial_sms_parts_finish) (MMIfaceModemMessaging *self, GAsyncResult *res, GError **error); - - /* Create SMS objects */ - MMBaseSms * (* create_sms) (MMIfaceModemMessaging *self); }; /* Initialize Messaging interface (async) */ @@ -166,10 +178,12 @@ void mm_iface_modem_messaging_bind_simple_status (MMIfaceModemMessaging *self, MMSimpleStatus *status); /* Report new SMS part */ -gboolean mm_iface_modem_messaging_take_part (MMIfaceModemMessaging *self, - MMSmsPart *sms_part, - MMSmsState state, - MMSmsStorage storage); +gboolean mm_iface_modem_messaging_take_part (MMIfaceModemMessaging *self, + MMBaseSms *uninitialized_sms, + MMSmsPart *sms_part, + MMSmsState state, + MMSmsStorage storage, + GError **error); /* Check storage support */ gboolean mm_iface_modem_messaging_is_storage_supported_for_storing (MMIfaceModemMessaging *self, @@ -179,12 +193,22 @@ gboolean mm_iface_modem_messaging_is_storage_supported_for_receiving (MMIfaceMod MMSmsStorage storage, GError **error); +MMSmsStorage mm_iface_modem_messaging_get_default_storage (MMIfaceModemMessaging *self); + /* SMS creation */ 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, - const gchar *number, - GError **error); +/* Locking/unlocking SMS storages */ +void mm_iface_modem_messaging_lock_storages (MMIfaceModemMessaging *self, + MMSmsStorage mem1, + MMSmsStorage mem2, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_iface_modem_messaging_lock_storages_finish (MMIfaceModemMessaging *self, + GAsyncResult *res, + GError **error); +void mm_iface_modem_messaging_unlock_storages (MMIfaceModemMessaging *self, + gboolean mem1, + gboolean mem2); #endif /* MM_IFACE_MODEM_MESSAGING_H */ diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c index 7bd0f0ff..1af2666b 100644 --- a/src/mm-iface-modem.c +++ b/src/mm-iface-modem.c @@ -6616,25 +6616,25 @@ mm_iface_modem_get_current_capabilities (MMIfaceModem *self) gboolean mm_iface_modem_is_3gpp (MMIfaceModem *self) { - return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_3GPP); + return !!(mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_3GPP); } gboolean mm_iface_modem_is_3gpp_lte (MMIfaceModem *self) { - return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_LTE); + return !!(mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_LTE); } gboolean mm_iface_modem_is_3gpp_5gnr (MMIfaceModem *self) { - return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_5GNR); + return !!(mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_5GNR); } gboolean mm_iface_modem_is_cdma (MMIfaceModem *self) { - return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_CDMA_EVDO); + return !!(mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_CDMA_EVDO); } gboolean @@ -6643,13 +6643,13 @@ mm_iface_modem_is_3gpp_only (MMIfaceModem *self) MMModemCapability capabilities; capabilities = mm_iface_modem_get_current_capabilities (self); - return (capabilities & MM_MODEM_CAPABILITY_3GPP) && !((MM_MODEM_CAPABILITY_3GPP ^ capabilities) & capabilities); + return !!((capabilities & MM_MODEM_CAPABILITY_3GPP) && !((MM_MODEM_CAPABILITY_3GPP ^ capabilities) & capabilities)); } gboolean mm_iface_modem_is_cdma_only (MMIfaceModem *self) { - return (mm_iface_modem_get_current_capabilities (self) == MM_MODEM_CAPABILITY_CDMA_EVDO); + return !!(mm_iface_modem_get_current_capabilities (self) == MM_MODEM_CAPABILITY_CDMA_EVDO); } /*****************************************************************************/ diff --git a/src/mm-sms-at.c b/src/mm-sms-at.c new file mode 100644 index 00000000..4f817e65 --- /dev/null +++ b/src/mm-sms-at.c @@ -0,0 +1,839 @@ +/* -*- 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. + * Copyright (C) 2025 Dan Williams <dan@ioncontrol.co> + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-sms-at.h" +#include "mm-iface-modem-messaging.h" +#include "mm-sms-part-3gpp.h" +#include "mm-base-modem-at.h" +#include "mm-log-object.h" +#include "mm-bind.h" + +G_DEFINE_TYPE (MMSmsAt, mm_sms_at, MM_TYPE_BASE_SMS) + +struct _MMSmsAtPrivate { + MMBaseModem *modem; +}; + +/*****************************************************************************/ + +static gboolean +sms_get_store_or_send_command (MMSmsAt *self, + 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 { + g_autofree gchar *hex = NULL; + g_autofree guint8 *pdu = NULL; + guint pdulen = 0; + guint msgstart = 0; + + /* AT+CMGW=<length>[, <stat>]<CR> PDU can be entered. <CTRL-Z>/<ESC> */ + + pdu = mm_sms_part_3gpp_get_submit_pdu (part, &pdulen, &msgstart, self, error); + if (!pdu) + /* 'error' should already be set */ + return FALSE; + + /* Convert PDU to hex */ + hex = mm_utils_bin2hexstr (pdu, pdulen); + 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); + } + + return TRUE; +} + +/*****************************************************************************/ +/* Store the SMS */ + +typedef struct { + MMBaseModem *modem; + MMIfacePortAt *port; + MMSmsStorage storage; + gboolean need_unlock; + gboolean use_pdu_mode; + GList *current; + gchar *msg_data; +} SmsStoreContext; + +static void +sms_store_context_free (SmsStoreContext *ctx) +{ + /* Unlock mem2 storage if we had the lock */ + if (ctx->need_unlock) { + mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (ctx->modem), + FALSE, + TRUE); + } + g_object_unref (ctx->port); + g_object_unref (ctx->modem); + g_free (ctx->msg_data); + g_slice_free (SmsStoreContext, ctx); +} + +static gboolean +sms_store_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void sms_store_next_part (GTask *task); + +static void +store_msg_data_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + SmsStoreContext *ctx; + const gchar *response; + GError *error = NULL; + gint rv; + gint idx; + + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Read the new part index from the reply */ + rv = sscanf (response, "+CMGW: %d", &idx); + if (rv != 1 || idx < 0) { + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't read index of already stored part: " + "%d fields parsed", + rv); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + + /* 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 (task); +} + +static void +store_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + SmsStoreContext *ctx; + GError *error = NULL; + + mm_base_modem_at_command_full_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + + /* Send the actual message data. + * We send the data as 'raw' data because we do NOT want it to + * be treated as an AT command (i.e. we don't want it prefixed + * with AT+ and suffixed with <CR><LF>), plus, we want it to be + * sent right away (not queued after other AT commands). */ + mm_base_modem_at_command_full (ctx->modem, + ctx->port, + ctx->msg_data, + 10, + FALSE, + TRUE, /* raw */ + NULL, + (GAsyncReadyCallback)store_msg_data_ready, + task); +} + +static void +sms_store_next_part (GTask *task) +{ + MMSmsAt *self; + SmsStoreContext *ctx; + GError *error = NULL; + g_autofree gchar *cmd = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + if (!ctx->current) { + /* Done we are */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + g_clear_pointer (&ctx->msg_data, g_free); + + if (!sms_get_store_or_send_command (self, + (MMSmsPart *)ctx->current->data, + ctx->use_pdu_mode, + FALSE, + &cmd, + &ctx->msg_data, + &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_assert (cmd != NULL); + g_assert (ctx->msg_data != NULL); + + mm_base_modem_at_command_full (ctx->modem, + ctx->port, + cmd, + 10, + FALSE, + FALSE, /* raw */ + NULL, + (GAsyncReadyCallback)store_ready, + task); +} + +static void +store_lock_sms_storages_ready (MMIfaceModemMessaging *messaging, + GAsyncResult *res, + GTask *task) +{ + MMSmsAt *self; + SmsStoreContext *ctx; + GError *error = NULL; + + if (!mm_iface_modem_messaging_lock_storages_finish (messaging, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + /* 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 = mm_base_sms_get_parts (MM_BASE_SMS (self)); + sms_store_next_part (task); +} + +static void +sms_store (MMBaseSms *sms, + MMSmsStorage storage, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMSmsAt *self = MM_SMS_AT (sms); + SmsStoreContext *ctx; + GTask *task; + MMIfacePortAt *port; + GError *error = NULL; + + task = g_task_new (self, NULL, callback, user_data); + + /* Select port for the operation */ + port = mm_base_modem_peek_best_at_port (self->priv->modem, &error); + if (!port) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Setup the context */ + ctx = g_slice_new0 (SmsStoreContext); + ctx->modem = g_object_ref (self->priv->modem); + ctx->port = g_object_ref (port); + ctx->storage = storage; + + /* Different ways to do it if on PDU or text mode */ + g_assert (MM_IS_IFACE_MODEM_MESSAGING (self->priv->modem)); + g_object_get (self->priv->modem, + MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, + NULL); + g_task_set_task_data (task, ctx, (GDestroyNotify)sms_store_context_free); + + /* First, lock storage to use */ + mm_iface_modem_messaging_lock_storages ( + MM_IFACE_MODEM_MESSAGING (self->priv->modem), + MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ + ctx->storage, + (GAsyncReadyCallback)store_lock_sms_storages_ready, + task); +} + +/*****************************************************************************/ +/* Send the SMS */ + +typedef struct { + MMBaseModem *modem; + MMIfacePortAt *port; + gboolean need_unlock; + gboolean from_storage; + gboolean use_pdu_mode; + GList *current; + gchar *msg_data; +} SmsSendContext; + +static void +sms_send_context_free (SmsSendContext *ctx) +{ + /* Unlock mem2 storage if we had the lock */ + if (ctx->need_unlock) { + mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (ctx->modem), + FALSE, + TRUE); + } + g_object_unref (ctx->port); + g_object_unref (ctx->modem); + g_free (ctx->msg_data); + g_slice_free (SmsSendContext, ctx); +} + +static gboolean +sms_send_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void sms_send_next_part (GTask *task); + +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, + GTask *task) +{ + SmsSendContext *ctx; + GError *error = NULL; + const gchar *response; + gint message_reference; + + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + message_reference = read_message_reference_from_reply (response, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + + mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, + (guint)message_reference); + + ctx->current = g_list_next (ctx->current); + sms_send_next_part (task); +} + +static void +send_generic_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + SmsSendContext *ctx; + GError *error = NULL; + + mm_base_modem_at_command_full_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + + /* Send the actual message data. + * We send the data as 'raw' data because we do NOT want it to + * be treated as an AT command (i.e. we don't want it prefixed + * with AT+ and suffixed with <CR><LF>), plus, we want it to be + * sent right away (not queued after other AT commands). */ + mm_base_modem_at_command_full (ctx->modem, + ctx->port, + ctx->msg_data, + MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, + FALSE, + TRUE, /* raw */ + NULL, + (GAsyncReadyCallback)send_generic_msg_data_ready, + task); +} + +static void +send_from_storage_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + MMSmsAt *self; + SmsSendContext *ctx; + GError *error = NULL; + const gchar *response; + gint message_reference; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (error) { + if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + mm_obj_dbg (self, "couldn't send SMS from storage: %s; trying generic send...", error->message); + g_error_free (error); + + ctx->from_storage = FALSE; + sms_send_next_part (task); + return; + } + + message_reference = read_message_reference_from_reply (response, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + 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 (task); +} + +static void +sms_send_next_part (GTask *task) +{ + MMSmsAt *self; + SmsSendContext *ctx; + GError *error = NULL; + g_autofree gchar *cmd = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + if (!ctx->current) { + /* Done we are */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + 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_full (ctx->modem, + ctx->port, + cmd, + MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, + FALSE, + FALSE, + NULL, + (GAsyncReadyCallback)send_from_storage_ready, + task); + return; + } + + /* Generic send */ + + g_clear_pointer (&ctx->msg_data, g_free); + + if (!sms_get_store_or_send_command (self, + (MMSmsPart *)ctx->current->data, + ctx->use_pdu_mode, + TRUE, + &cmd, + &ctx->msg_data, + &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_assert (cmd != NULL); + g_assert (ctx->msg_data != NULL); + + /* no network involved in this initial AT command, so lower timeout */ + mm_base_modem_at_command_full (ctx->modem, + ctx->port, + cmd, + 10, + FALSE, + FALSE, /* raw */ + NULL, + (GAsyncReadyCallback)send_generic_ready, + task); +} + +static void +send_lock_sms_storages_ready (MMIfaceModemMessaging *messaging, + GAsyncResult *res, + GTask *task) +{ + MMSmsAt *self; + SmsSendContext *ctx; + GError *error = NULL; + + if (!mm_iface_modem_messaging_lock_storages_finish (messaging, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + /* 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 = mm_base_sms_get_parts (MM_BASE_SMS (self)); + sms_send_next_part (task); +} + +static void +sms_send (MMBaseSms *sms, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMSmsAt *self = MM_SMS_AT (sms); + SmsSendContext *ctx; + GTask *task; + MMIfacePortAt *port; + GError *error = NULL; + + task = g_task_new (self, NULL, callback, user_data); + + /* Select port for the operation */ + port = mm_base_modem_peek_best_at_port (self->priv->modem, &error); + if (!port) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Setup the context */ + ctx = g_slice_new0 (SmsSendContext); + ctx->modem = g_object_ref (self->priv->modem); + ctx->port = g_object_ref (port); + g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free); + + /* If the SMS is STORED, try to send from storage */ + ctx->from_storage = (mm_base_sms_get_storage (MM_BASE_SMS (self)) != MM_SMS_STORAGE_UNKNOWN); + if (ctx->from_storage) { + /* When sending from storage, first lock storage to use */ + g_assert (MM_IS_IFACE_MODEM_MESSAGING (self->priv->modem)); + mm_iface_modem_messaging_lock_storages ( + MM_IFACE_MODEM_MESSAGING (self->priv->modem), + MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ + mm_base_sms_get_storage (MM_BASE_SMS (self)), + (GAsyncReadyCallback)send_lock_sms_storages_ready, + task); + 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 = mm_base_sms_get_parts (MM_BASE_SMS (self)); + sms_send_next_part (task); +} + +/*****************************************************************************/ + +typedef struct { + MMBaseModem *modem; + gboolean need_unlock; + GList *current; + guint n_failed; +} SmsDeletePartsContext; + +static void +sms_delete_parts_context_free (SmsDeletePartsContext *ctx) +{ + /* Unlock mem1 storage if we had the lock */ + if (ctx->need_unlock) { + mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (ctx->modem), + TRUE, + FALSE); + } + g_object_unref (ctx->modem); + g_slice_free (SmsDeletePartsContext, ctx); +} + +static gboolean +sms_delete_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void delete_next_part (GTask *task); + +static void +delete_part_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + MMSmsAt *self; + SmsDeletePartsContext *ctx; + g_autoptr(GError) error = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + mm_base_modem_at_command_finish (modem, res, &error); + if (error) { + ctx->n_failed++; + mm_obj_dbg (self, "couldn't delete SMS part with index %u: %s", + mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), + error->message); + } + + /* 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 (task); +} + +static void +delete_next_part (GTask *task) +{ + SmsDeletePartsContext *ctx; + g_autofree gchar *cmd = NULL; + + ctx = g_task_get_task_data (task); + + /* 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_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't delete %u parts from this SMS", + ctx->n_failed); + else + g_task_return_boolean (task, TRUE); + g_object_unref (task); + 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, + task); +} + +static void +delete_lock_sms_storages_ready (MMIfaceModemMessaging *messaging, + GAsyncResult *res, + GTask *task) +{ + MMSmsAt *self; + SmsDeletePartsContext *ctx; + GError *error = NULL; + + if (!mm_iface_modem_messaging_lock_storages_finish (messaging, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + /* 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 = mm_base_sms_get_parts (MM_BASE_SMS (self)); + delete_next_part (task); +} + +static void +sms_delete (MMBaseSms *sms, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMSmsAt *self = MM_SMS_AT (sms); + SmsDeletePartsContext *ctx; + GTask *task; + + ctx = g_slice_new0 (SmsDeletePartsContext); + ctx->modem = g_object_ref (self->priv->modem); + + task = g_task_new (self, NULL, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free); + + if (mm_base_sms_get_storage (MM_BASE_SMS (self)) == MM_SMS_STORAGE_UNKNOWN) { + mm_obj_dbg (self, "not removing parts from non-stored SMS"); + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + /* Select specific storage to delete from */ + mm_iface_modem_messaging_lock_storages ( + MM_IFACE_MODEM_MESSAGING (self->priv->modem), + mm_base_sms_get_storage (MM_BASE_SMS (self)), + MM_SMS_STORAGE_UNKNOWN, /* none required for mem2 */ + (GAsyncReadyCallback)delete_lock_sms_storages_ready, + task); +} + +/*****************************************************************************/ + +MMBaseSms * +mm_sms_at_new (MMBaseModem *modem, + gboolean is_3gpp, + MMSmsStorage default_storage) +{ + MMBaseSms *sms; + + sms = MM_BASE_SMS (g_object_new (MM_TYPE_SMS_AT, + MM_BIND_TO, G_OBJECT (modem), + MM_BASE_SMS_IS_3GPP, is_3gpp, + MM_BASE_SMS_DEFAULT_STORAGE, default_storage, + NULL)); + MM_SMS_AT (sms)->priv->modem = g_object_ref (modem); + return sms; +} + +static void +mm_sms_at_init (MMSmsAt *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SMS_AT, MMSmsAtPrivate); +} + +static void +dispose (GObject *object) +{ + MMSmsAt *self = MM_SMS_AT (object); + + g_clear_object (&self->priv->modem); + + G_OBJECT_CLASS (mm_sms_at_parent_class)->dispose (object); +} + +static void +mm_sms_at_class_init (MMSmsAtClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMBaseSmsClass *base_sms_class = MM_BASE_SMS_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMSmsAtPrivate)); + + object_class->dispose = dispose; + + 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-at.h b/src/mm-sms-at.h new file mode 100644 index 00000000..551d50eb --- /dev/null +++ b/src/mm-sms-at.h @@ -0,0 +1,60 @@ +/* -*- 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 <aleksander@lanedo.com> + * + * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2025 Dan Williams <dan@ioncontrol.co> + */ + +#ifndef MM_SMS_AT_H +#define MM_SMS_AT_H + +#include <glib.h> +#include <glib-object.h> + +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-base-sms.h" +#include "mm-base-modem.h" + +/*****************************************************************************/ + +#define MM_TYPE_SMS_AT (mm_sms_at_get_type ()) +#define MM_SMS_AT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SMS_AT, MMSmsAt)) +#define MM_SMS_AT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SMS_AT, MMSmsAtClass)) +#define MM_IS_SMS_AT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SMS_AT)) +#define MM_IS_SMS_AT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SMS_AT)) +#define MM_SMS_AT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SMS_AT, MMSmsAtClass)) + +typedef struct _MMSmsAt MMSmsAt; +typedef struct _MMSmsAtClass MMSmsAtClass; +typedef struct _MMSmsAtPrivate MMSmsAtPrivate; + +struct _MMSmsAt { + MMBaseSms parent; + MMSmsAtPrivate *priv; +}; + +struct _MMSmsAtClass { + MMBaseSmsClass parent; +}; + +GType mm_sms_at_get_type (void); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsAt, g_object_unref) + +MMBaseSms *mm_sms_at_new (MMBaseModem *modem, + gboolean is_3gpp, + MMSmsStorage default_storage); + +#endif /* MM_SMS_AT_H */ diff --git a/src/mm-sms-list.c b/src/mm-sms-list.c index f2401dae..30ce1cc4 100644 --- a/src/mm-sms-list.c +++ b/src/mm-sms-list.c @@ -24,7 +24,6 @@ #define _LIBMM_INSIDE_MM #include <libmm-glib.h> -#include "mm-iface-modem-messaging.h" #include "mm-sms-list.h" #include "mm-base-sms.h" #include "mm-log-object.h" @@ -40,10 +39,8 @@ G_DEFINE_TYPE_EXTENDED (MMSmsList, mm_sms_list, G_TYPE_OBJECT, 0, enum { PROP_0, PROP_BIND_TO, - PROP_MODEM, PROP_LAST }; -static GParamSpec *properties[PROP_LAST]; enum { SIGNAL_ADDED, @@ -55,12 +52,12 @@ static guint signals[SIGNAL_LAST]; struct _MMSmsListPrivate { /* The object this SMS list is bound to */ GObject *bind_to; - /* The owner modem */ - MMBaseModem *modem; /* List of sms objects */ GList *list; }; +static void _release_sms_internal (MMBaseSms *sms, MMSmsList *self); + /*****************************************************************************/ gboolean @@ -161,7 +158,7 @@ delete_ready (MMBaseSms *sms, path, (GCompareFunc)cmp_sms_by_path); if (l) { - g_object_unref (MM_BASE_SMS (l->data)); + _release_sms_internal (MM_BASE_SMS (l->data), self); self->priv->list = g_list_delete_link (self->priv->list, l); } @@ -213,14 +210,61 @@ mm_sms_list_delete_sms (MMSmsList *self, /*****************************************************************************/ -void -mm_sms_list_add_sms (MMSmsList *self, - MMBaseSms *sms) +static void +set_local_multipart_reference (MMBaseSms *sms, + const gchar *number, + MMSmsList *self) +{ + guint8 reference; + guint8 first; + + /* Start by looking for a random number */ + reference = g_random_int_range (1, 255); + + /* Then, look for the given reference in user-created messages */ + first = reference; + do { + if (!mm_sms_list_has_local_multipart_reference (self, number, reference)) { + mm_base_sms_set_multipart_reference (sms, reference); + return; + } + + if (reference == 255) + reference = 1; + else + reference++; + } while (reference != first); +} + +static void +_release_sms_internal (MMBaseSms *sms, MMSmsList *self) +{ + g_signal_handlers_disconnect_by_func (sms, set_local_multipart_reference, self); + g_object_unref (sms); +} + +static void +_add_sms_internal (MMSmsList *self, + MMBaseSms *sms, + gboolean received) { self->priv->list = g_list_prepend (self->priv->list, g_object_ref (sms)); + g_signal_connect (sms, + MM_BASE_SMS_SET_LOCAL_MULTIPART_REFERENCE, + (GCallback)set_local_multipart_reference, + self); + g_signal_emit (self, signals[SIGNAL_ADDED], 0, mm_base_sms_get_path (sms), - FALSE); + received); +} + + +void +mm_sms_list_add_sms (MMSmsList *self, + MMBaseSms *sms) +{ + _add_sms_internal (self, sms, FALSE); } /*****************************************************************************/ @@ -259,39 +303,33 @@ cmp_sms_by_part_index_and_storage (MMBaseSms *sms, static gboolean take_singlepart (MMSmsList *self, + MMBaseSms *sms, MMSmsPart *part, MMSmsState state, MMSmsStorage storage, GError **error) { - MMBaseSms *sms; - - sms = mm_base_sms_singlepart_new (self->priv->modem, + if (!mm_base_sms_singlepart_init (sms, state, storage, part, - error); - if (!sms) + error)) return FALSE; mm_obj_dbg (sms, "creating new singlepart SMS object"); - - self->priv->list = g_list_prepend (self->priv->list, sms); - g_signal_emit (self, signals[SIGNAL_ADDED], 0, - mm_base_sms_get_path (sms), - state == MM_SMS_STATE_RECEIVED); + _add_sms_internal (self, sms, state == MM_SMS_STATE_RECEIVED); return TRUE; } static gboolean take_multipart (MMSmsList *self, + MMBaseSms *sms, MMSmsPart *part, MMSmsState state, MMSmsStorage storage, GError **error) { GList *l; - MMBaseSms *sms; guint concat_reference; concat_reference = mm_sms_part_get_concat_reference (part); @@ -304,25 +342,19 @@ take_multipart (MMSmsList *self, } /* Create new Multipart */ - sms = mm_base_sms_multipart_new (self->priv->modem, + if (!mm_base_sms_multipart_init (sms, state, storage, concat_reference, mm_sms_part_get_concat_max (part), part, - error); - if (!sms) + error)) return FALSE; mm_obj_dbg (sms, "creating new multipart SMS object: need to receive %u parts with reference '%u'", mm_sms_part_get_concat_max (part), concat_reference); - self->priv->list = g_list_prepend (self->priv->list, sms); - g_signal_emit (self, signals[SIGNAL_ADDED], 0, - mm_base_sms_get_path (sms), - (state == MM_SMS_STATE_RECEIVED || - state == MM_SMS_STATE_RECEIVING)); - + _add_sms_internal (self, sms, (state == MM_SMS_STATE_RECEIVED || state == MM_SMS_STATE_RECEIVING)); return TRUE; } @@ -347,6 +379,7 @@ mm_sms_list_has_part (MMSmsList *self, gboolean mm_sms_list_take_part (MMSmsList *self, + MMBaseSms *uninitialized_sms, MMSmsPart *part, MMSmsState state, MMSmsStorage storage, @@ -379,7 +412,7 @@ mm_sms_list_take_part (MMSmsList *self, mm_sms_part_get_concat_sequence (part), mm_sms_part_get_concat_max (part)); - return take_multipart (self, part, state, storage, error); + return take_multipart (self, uninitialized_sms, part, state, storage, error); } /* Otherwise, we build a whole new single-part MMSms just from this part */ @@ -389,7 +422,22 @@ mm_sms_list_take_part (MMSmsList *self, mm_sms_part_get_index (part)); else mm_obj_dbg (self, "SMS part (not stored) is from a singlepart SMS"); - return take_singlepart (self, part, state, storage, error); + return take_singlepart (self, uninitialized_sms, part, state, storage, error); +} + +/*****************************************************************************/ + +void +mm_sms_list_set_default_storage (MMSmsList *self, + MMSmsStorage default_storage) +{ + GList *l; + + for (l = self->priv->list; l; l = g_list_next (l)) { + g_object_set (MM_BASE_SMS (l->data), + MM_BASE_SMS_DEFAULT_STORAGE, default_storage, + NULL); + } } /*****************************************************************************/ @@ -403,11 +451,10 @@ log_object_build_id (MMLogObject *_self) /*****************************************************************************/ MMSmsList * -mm_sms_list_new (MMBaseModem *modem, GObject *bind_to) +mm_sms_list_new (GObject *bind_to) { /* Create the object */ return g_object_new (MM_TYPE_SMS_LIST, - MM_SMS_LIST_MODEM, modem, MM_BIND_TO, bind_to, NULL); } @@ -426,10 +473,6 @@ set_property (GObject *object, self->priv->bind_to = g_value_dup_object (value); mm_bind_to (MM_BIND (self), NULL, self->priv->bind_to); break; - case PROP_MODEM: - g_clear_object (&self->priv->modem); - self->priv->modem = g_value_dup_object (value); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -448,9 +491,6 @@ get_property (GObject *object, case PROP_BIND_TO: g_value_set_object (value, self->priv->bind_to); break; - case PROP_MODEM: - g_value_set_object (value, self->priv->modem); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -471,10 +511,9 @@ dispose (GObject *object) { MMSmsList *self = MM_SMS_LIST (object); - g_clear_object (&self->priv->modem); g_clear_object (&self->priv->bind_to); - g_list_free_full (self->priv->list, g_object_unref); - self->priv->list = NULL; + g_list_foreach (self->priv->list, (GFunc)_release_sms_internal, self); + g_clear_pointer (&self->priv->list, (GDestroyNotify)g_list_free); G_OBJECT_CLASS (mm_sms_list_parent_class)->dispose (object); } @@ -503,14 +542,6 @@ mm_sms_list_class_init (MMSmsListClass *klass) object_class->dispose = dispose; /* Properties */ - properties[PROP_MODEM] = - g_param_spec_object (MM_SMS_LIST_MODEM, - "Modem", - "The Modem which owns this SMS list", - MM_TYPE_BASE_MODEM, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); - g_object_class_override_property (object_class, PROP_BIND_TO, MM_BIND_TO); /* Signals */ diff --git a/src/mm-sms-list.h b/src/mm-sms-list.h index 775611c8..51228672 100644 --- a/src/mm-sms-list.h +++ b/src/mm-sms-list.h @@ -19,7 +19,7 @@ #include <glib.h> #include <glib-object.h> -#include "mm-base-modem.h" +#include "mm-base-sms.h" #include "mm-sms-part.h" #define MM_TYPE_SMS_LIST (mm_sms_list_get_type ()) @@ -33,8 +33,6 @@ typedef struct _MMSmsList MMSmsList; typedef struct _MMSmsListClass MMSmsListClass; typedef struct _MMSmsListPrivate MMSmsListPrivate; -#define MM_SMS_LIST_MODEM "sms-list-modem" - #define MM_SMS_ADDED "sms-added" #define MM_SMS_DELETED "sms-deleted" @@ -57,8 +55,7 @@ struct _MMSmsListClass { GType mm_sms_list_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsList, g_object_unref) -MMSmsList *mm_sms_list_new (MMBaseModem *modem, - GObject *bind_to); +MMSmsList *mm_sms_list_new (GObject *bind_to); GStrv mm_sms_list_get_paths (MMSmsList *self); guint mm_sms_list_get_count (MMSmsList *self); @@ -68,6 +65,7 @@ gboolean mm_sms_list_has_part (MMSmsList *self, guint index); gboolean mm_sms_list_take_part (MMSmsList *self, + MMBaseSms *uninitialized_sms, MMSmsPart *part, MMSmsState state, MMSmsStorage storage, @@ -88,4 +86,7 @@ gboolean mm_sms_list_has_local_multipart_reference (MMSmsList *self, const gchar *number, guint8 reference); +void mm_sms_list_set_default_storage (MMSmsList *self, + MMSmsStorage default_storage); + #endif /* MM_SMS_LIST_H */ diff --git a/src/mm-sms-mbim.c b/src/mm-sms-mbim.c index 0a404aa5..ffb6b466 100644 --- a/src/mm-sms-mbim.c +++ b/src/mm-sms-mbim.c @@ -35,25 +35,22 @@ G_DEFINE_TYPE (MMSmsMbim, mm_sms_mbim, MM_TYPE_BASE_SMS) +struct _MMSmsMbimPrivate { + MMBaseModem *modem; +}; + /*****************************************************************************/ static gboolean -peek_device (gpointer self, - MbimDevice **o_device, - GAsyncReadyCallback callback, - gpointer user_data) +peek_device (MMSmsMbim *self, + MbimDevice **o_device, + GAsyncReadyCallback callback, + gpointer user_data) { - MMBaseModem *modem = NULL; - - g_object_get (G_OBJECT (self), - MM_BASE_SMS_MODEM, &modem, - NULL); - g_assert (MM_IS_BASE_MODEM (modem)); - if (o_device) { MMPortMbim *port; - port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (modem)); + port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self->priv->modem)); if (!port) { g_task_report_new_error (self, callback, @@ -62,14 +59,12 @@ peek_device (gpointer self, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port"); - g_object_unref (modem); return FALSE; } *o_device = mm_port_mbim_peek_device (port); } - g_object_unref (modem); return TRUE; } @@ -77,7 +72,6 @@ peek_device (gpointer self, /* Send the SMS */ typedef struct { - MMBaseModem *modem; MbimDevice *device; GList *current; } SmsSendContext; @@ -86,7 +80,6 @@ static void sms_send_context_free (SmsSendContext *ctx) { g_object_unref (ctx->device); - g_object_unref (ctx->modem); g_slice_free (SmsSendContext, ctx); } @@ -193,15 +186,12 @@ sms_send (MMBaseSms *self, MbimDevice *device; GTask *task; - if (!peek_device (self, &device, callback, user_data)) + if (!peek_device (MM_SMS_MBIM (self), &device, callback, user_data)) return; /* Setup the context */ ctx = g_slice_new0 (SmsSendContext); ctx->device = g_object_ref (device); - g_object_get (self, - MM_BASE_SMS_MODEM, &ctx->modem, - NULL); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free); @@ -213,7 +203,6 @@ sms_send (MMBaseSms *self, /*****************************************************************************/ typedef struct { - MMBaseModem *modem; MbimDevice *device; GList *current; guint n_failed; @@ -223,7 +212,6 @@ static void sms_delete_parts_context_free (SmsDeletePartsContext *ctx) { g_object_unref (ctx->device); - g_object_unref (ctx->modem); g_slice_free (SmsDeletePartsContext, ctx); } @@ -322,14 +310,11 @@ sms_delete (MMBaseSms *self, MbimDevice *device; GTask *task; - if (!peek_device (self, &device, callback, user_data)) + if (!peek_device (MM_SMS_MBIM (self), &device, callback, user_data)) return; ctx = g_slice_new0 (SmsDeletePartsContext); ctx->device = g_object_ref (device); - g_object_get (self, - MM_BASE_SMS_MODEM, &ctx->modem, - NULL); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free); @@ -342,24 +327,49 @@ sms_delete (MMBaseSms *self, /*****************************************************************************/ MMBaseSms * -mm_sms_mbim_new (MMBaseModem *modem) +mm_sms_mbim_new (MMBaseModem *modem, + gboolean is_3gpp, + MMSmsStorage default_storage) { - return MM_BASE_SMS (g_object_new (MM_TYPE_SMS_MBIM, - MM_BASE_SMS_MODEM, modem, - MM_BIND_TO, G_OBJECT (modem), - NULL)); + MMBaseSms *sms; + + g_return_val_if_fail (MM_IS_BROADBAND_MODEM_MBIM (modem), NULL); + + sms = MM_BASE_SMS (g_object_new (MM_TYPE_SMS_MBIM, + MM_BIND_TO, G_OBJECT (modem), + MM_BASE_SMS_IS_3GPP, is_3gpp, + MM_BASE_SMS_DEFAULT_STORAGE, default_storage, + NULL)); + MM_SMS_MBIM (sms)->priv->modem = g_object_ref (modem); + return sms; } static void mm_sms_mbim_init (MMSmsMbim *self) { + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SMS_MBIM, MMSmsMbimPrivate); +} + +static void +dispose (GObject *object) +{ + MMSmsMbim *self = MM_SMS_MBIM (object); + + g_clear_object (&self->priv->modem); + + G_OBJECT_CLASS (mm_sms_mbim_parent_class)->dispose (object); } static void mm_sms_mbim_class_init (MMSmsMbimClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseSmsClass *base_sms_class = MM_BASE_SMS_CLASS (klass); + g_type_class_add_private (object_class, sizeof (MMSmsMbimPrivate)); + + object_class->dispose = dispose; + base_sms_class->store = NULL; base_sms_class->store_finish = NULL; base_sms_class->send = sms_send; diff --git a/src/mm-sms-mbim.h b/src/mm-sms-mbim.h index 5743f262..b045b99c 100644 --- a/src/mm-sms-mbim.h +++ b/src/mm-sms-mbim.h @@ -33,9 +33,11 @@ typedef struct _MMSmsMbim MMSmsMbim; typedef struct _MMSmsMbimClass MMSmsMbimClass; +typedef struct _MMSmsMbimPrivate MMSmsMbimPrivate; struct _MMSmsMbim { MMBaseSms parent; + MMSmsMbimPrivate *priv; }; struct _MMSmsMbimClass { @@ -45,6 +47,8 @@ struct _MMSmsMbimClass { GType mm_sms_mbim_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsMbim, g_object_unref) -MMBaseSms *mm_sms_mbim_new (MMBaseModem *modem); +MMBaseSms *mm_sms_mbim_new (MMBaseModem *modem, + gboolean is_3gpp, + MMSmsStorage default_storage); #endif /* MM_SMS_MBIM_H */ diff --git a/src/mm-sms-qmi.c b/src/mm-sms-qmi.c index abc45649..a131ccc9 100644 --- a/src/mm-sms-qmi.c +++ b/src/mm-sms-qmi.c @@ -37,6 +37,10 @@ G_DEFINE_TYPE (MMSmsQmi, mm_sms_qmi, MM_TYPE_BASE_SMS) +struct _MMSmsQmiPrivate { + MMBaseModem *modem; +}; + /*****************************************************************************/ static gboolean @@ -46,18 +50,10 @@ ensure_qmi_client (MMSmsQmi *self, GAsyncReadyCallback callback, gpointer user_data) { - MMBaseModem *modem = NULL; QmiClient *client; MMPortQmi *port; - g_object_get (self, - MM_BASE_SMS_MODEM, &modem, - NULL); - g_assert (MM_IS_BASE_MODEM (modem)); - - port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (modem)); - g_object_unref (modem); - + port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self->priv->modem)); if (!port) { g_task_report_new_error (self, callback, @@ -119,7 +115,6 @@ check_sms_type_support (MMSmsQmi *self, /* Store the SMS */ typedef struct { - MMBaseModem *modem; QmiClientWms *client; MMSmsStorage storage; GList *current; @@ -129,7 +124,6 @@ static void sms_store_context_free (SmsStoreContext *ctx) { g_object_unref (ctx->client); - g_object_unref (ctx->modem); g_slice_free (SmsStoreContext, ctx); } @@ -260,11 +254,12 @@ sms_store_next_part (GTask *task) } static void -sms_store (MMBaseSms *self, +sms_store (MMBaseSms *_self, MMSmsStorage storage, GAsyncReadyCallback callback, gpointer user_data) { + MMSmsQmi *self = MM_SMS_QMI (_self); SmsStoreContext *ctx; QmiClient *client = NULL; GError *error = NULL; @@ -280,17 +275,13 @@ sms_store (MMBaseSms *self, ctx = g_slice_new0 (SmsStoreContext); ctx->client = QMI_CLIENT_WMS (g_object_ref (client)); ctx->storage = storage; - g_object_get (self, - MM_BASE_SMS_MODEM, &ctx->modem, - NULL); - - ctx->current = mm_base_sms_get_parts (self); + ctx->current = mm_base_sms_get_parts (_self); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_store_context_free); /* Check whether we support the given SMS type */ - if (!check_sms_type_support (MM_SMS_QMI (self), ctx->modem, (MMSmsPart *)ctx->current->data, &error)) { + if (!check_sms_type_support (MM_SMS_QMI (self), self->priv->modem, (MMSmsPart *)ctx->current->data, &error)) { g_task_return_error (task, error); g_object_unref (task); return; @@ -304,7 +295,6 @@ sms_store (MMBaseSms *self, /* Send the SMS */ typedef struct { - MMBaseModem *modem; QmiClientWms *client; gboolean from_storage; GList *current; @@ -314,7 +304,6 @@ static void sms_send_context_free (SmsSendContext *ctx) { g_object_unref (ctx->client); - g_object_unref (ctx->modem); g_slice_free (SmsSendContext, ctx); } @@ -595,10 +584,11 @@ sms_send_next_part (GTask *task) } static void -sms_send (MMBaseSms *self, - GAsyncReadyCallback callback, - gpointer user_data) +sms_send (MMBaseSms *_self, + GAsyncReadyCallback callback, + gpointer user_data) { + MMSmsQmi *self = MM_SMS_QMI (_self); SmsSendContext *ctx; QmiClient *client = NULL; GError *error = NULL; @@ -613,20 +603,16 @@ sms_send (MMBaseSms *self, /* Setup the context */ ctx = g_slice_new0 (SmsSendContext); ctx->client = QMI_CLIENT_WMS (g_object_ref (client)); - g_object_get (self, - MM_BASE_SMS_MODEM, &ctx->modem, - NULL); - /* If the SMS is STORED, try to send from storage */ - ctx->from_storage = (mm_base_sms_get_storage (self) != MM_SMS_STORAGE_UNKNOWN); + ctx->from_storage = (mm_base_sms_get_storage (_self) != MM_SMS_STORAGE_UNKNOWN); - ctx->current = mm_base_sms_get_parts (self); + ctx->current = mm_base_sms_get_parts (_self); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free); /* Check whether we support the given SMS type */ - if (!check_sms_type_support (MM_SMS_QMI (self), ctx->modem, (MMSmsPart *)ctx->current->data, &error)) { + if (!check_sms_type_support (MM_SMS_QMI (self), self->priv->modem, (MMSmsPart *)ctx->current->data, &error)) { g_task_return_error (task, error); g_object_unref (task); return; @@ -638,7 +624,6 @@ sms_send (MMBaseSms *self, /*****************************************************************************/ typedef struct { - MMBaseModem *modem; QmiClientWms *client; GList *current; guint n_failed; @@ -648,7 +633,6 @@ static void sms_delete_parts_context_free (SmsDeletePartsContext *ctx) { g_object_unref (ctx->client); - g_object_unref (ctx->modem); g_slice_free (SmsDeletePartsContext, ctx); } @@ -771,9 +755,6 @@ sms_delete (MMBaseSms *self, ctx = g_slice_new0 (SmsDeletePartsContext); ctx->client = QMI_CLIENT_WMS (g_object_ref (client)); - g_object_get (self, - MM_BASE_SMS_MODEM, &ctx->modem, - NULL); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free); @@ -786,24 +767,49 @@ sms_delete (MMBaseSms *self, /*****************************************************************************/ MMBaseSms * -mm_sms_qmi_new (MMBaseModem *modem) +mm_sms_qmi_new (MMBaseModem *modem, + gboolean is_3gpp, + MMSmsStorage default_storage) { - return MM_BASE_SMS (g_object_new (MM_TYPE_SMS_QMI, - MM_BASE_SMS_MODEM, modem, - MM_BIND_TO, G_OBJECT (modem), - NULL)); + MMBaseSms *sms; + + g_return_val_if_fail (MM_IS_BROADBAND_MODEM_QMI (modem), NULL); + + sms = MM_BASE_SMS (g_object_new (MM_TYPE_SMS_QMI, + MM_BIND_TO, G_OBJECT (modem), + MM_BASE_SMS_IS_3GPP, is_3gpp, + MM_BASE_SMS_DEFAULT_STORAGE, default_storage, + NULL)); + MM_SMS_QMI (sms)->priv->modem = g_object_ref (modem); + return sms; } static void mm_sms_qmi_init (MMSmsQmi *self) { + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SMS_QMI, MMSmsQmiPrivate); +} + +static void +dispose (GObject *object) +{ + MMSmsQmi *self = MM_SMS_QMI (object); + + g_clear_object (&self->priv->modem); + + G_OBJECT_CLASS (mm_sms_qmi_parent_class)->dispose (object); } static void mm_sms_qmi_class_init (MMSmsQmiClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseSmsClass *base_sms_class = MM_BASE_SMS_CLASS (klass); + g_type_class_add_private (object_class, sizeof (MMSmsQmiPrivate)); + + object_class->dispose = dispose; + base_sms_class->store = sms_store; base_sms_class->store_finish = sms_store_finish; base_sms_class->send = sms_send; diff --git a/src/mm-sms-qmi.h b/src/mm-sms-qmi.h index af60a7f5..e59ff4ec 100644 --- a/src/mm-sms-qmi.h +++ b/src/mm-sms-qmi.h @@ -35,9 +35,11 @@ typedef struct _MMSmsQmi MMSmsQmi; typedef struct _MMSmsQmiClass MMSmsQmiClass; +typedef struct _MMSmsQmiPrivate MMSmsQmiPrivate; struct _MMSmsQmi { MMBaseSms parent; + MMSmsQmiPrivate *priv; }; struct _MMSmsQmiClass { @@ -47,6 +49,8 @@ struct _MMSmsQmiClass { GType mm_sms_qmi_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsQmi, g_object_unref) -MMBaseSms *mm_sms_qmi_new (MMBaseModem *modem); +MMBaseSms *mm_sms_qmi_new (MMBaseModem *modem, + gboolean is_3gpp, + MMSmsStorage default_storage); #endif /* MM_SMS_QMI_H */ diff --git a/src/tests/meson.build b/src/tests/meson.build index a6a1ae72..ea9ea3a3 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -10,6 +10,7 @@ test_units = { 'modem-helpers': libhelpers_dep, 'sms-part-3gpp': libhelpers_dep, 'sms-part-cdma': libhelpers_dep, + 'sms-list': libsms_dep, 'udev-rules': libkerneldevice_dep, } diff --git a/src/tests/test-sms-list.c b/src/tests/test-sms-list.c new file mode 100644 index 00000000..335d6cec --- /dev/null +++ b/src/tests/test-sms-list.c @@ -0,0 +1,140 @@ +/* -*- 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) 2025 Dan Williams <dan@ioncontrol.co> + */ + +#include <glib.h> +#include <glib-object.h> +#include <string.h> +#include <stdio.h> +#include <locale.h> + +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-sms-part-3gpp.h" +#include "mm-sms-list.h" +#include "mm-log-test.h" +#include "mm-base-modem.h" + +#include "mm-iface-modem-messaging.h" + +/****************************************************************/ + +static void +test_mbim_multipart_unstored (void) +{ + static const gchar *part1_pdu = + "07912160130300f4440b915155685703f900005240713104738a3e050003c40202da6f37881e96" + "9fcbf4b4fb0ccabfeb20f4fb0ea287e5e7323ded3e83dae17519747fcbd96490b95c6683d27310" + "1d5d0601"; + static const gchar *part2_pdu = + "07912160130300f4440b915155685703f900005240713104738aa0050003c40201ac69373d7c2e" + "83e87538bc2cbf87e565d039dc2e83c220769a4e6797416132394d4fbfdda0fb5b4e4783c2ee3c" + "888e2e83e86fd0db0c1a86e769f71b647eb3d9ef7bda7d06a5e7a0b09b0c9ab3df74109c1dce83" + "e8e8301d44479741f9771d949e83e861f9b94c4fbbcf20f13b4c9f83e8e832485c068ddfedf6db" + "0da2a3cba0fcbb0e1abfdb"; + MMSmsPart *part1, *part2; + MMSmsList *list; + MMBaseSms *sms1, *sms2; + GError *error = NULL; + + /* Some MBIM devices (Dell 5821e) report SMSes via the MBIM_CID_SMS_READ + * unsolicited notification usually used for Class-0 (flash/alert) messages. + * These always have a message index of 0; thus when a multipart SMS + * arrives it comes as two individual notifications both with 0 indexes. + * The MBIM modem code used SMS_PART_INVALID_INDEX for unstored SMSes. + * Ensure the MMSmsList can handle combining the two parts with the same + * index. + */ + part1 = mm_sms_part_3gpp_new_from_pdu (SMS_PART_INVALID_INDEX, part1_pdu, NULL, &error); + g_assert_no_error (error); + part2 = mm_sms_part_3gpp_new_from_pdu (SMS_PART_INVALID_INDEX, part2_pdu, NULL, &error); + g_assert_no_error (error); + + list = mm_sms_list_new (NULL); + + sms1 = MM_BASE_SMS (g_object_new (MM_TYPE_BASE_SMS, + MM_BASE_SMS_IS_3GPP, TRUE, + MM_BASE_SMS_DEFAULT_STORAGE, MM_SMS_STORAGE_MT, + NULL)); + mm_sms_list_take_part (list, + sms1, + part1, + MM_SMS_STATE_RECEIVED, + MM_SMS_STORAGE_MT, + &error); + g_assert_no_error (error); + + sms2 = MM_BASE_SMS (g_object_new (MM_TYPE_BASE_SMS, + MM_BASE_SMS_IS_3GPP, TRUE, + MM_BASE_SMS_DEFAULT_STORAGE, MM_SMS_STORAGE_MT, + NULL)); + mm_sms_list_take_part (list, + sms2, + part2, + MM_SMS_STATE_RECEIVED, + MM_SMS_STORAGE_MT, + &error); + g_assert_no_error (error); + + g_assert_cmpint (mm_sms_list_get_count (list), ==, 1); +} + +/****************************************************************/ + +static void +test_mbim_multipart_zero_index (void) +{ + static const gchar *pdu = + "07912160130300f4440b915155685703f900005240713104738a3e050003c40202da6f37881e96" + "9fcbf4b4fb0ccabfeb20f4fb0ea287e5e7323ded3e83dae17519747fcbd96490b95c6683d27310" + "1d5d0601"; + MMSmsPart *part; + MMSmsList *list; + MMBaseSms *sms; + GError *error = NULL; + + part = mm_sms_part_3gpp_new_from_pdu (0, pdu, NULL, &error); + g_assert_no_error (error); + + list = mm_sms_list_new (NULL); + + sms = MM_BASE_SMS (g_object_new (MM_TYPE_BASE_SMS, + MM_BASE_SMS_IS_3GPP, TRUE, + MM_BASE_SMS_DEFAULT_STORAGE, MM_SMS_STORAGE_MT, + NULL)); + mm_sms_list_take_part (list, + sms, + part, + MM_SMS_STATE_RECEIVED, + MM_SMS_STORAGE_MT, + &error); + g_assert_no_error (error); + + g_assert_cmpint (mm_sms_list_get_count (list), ==, 1); +} + +/****************************************************************/ + +int main (int argc, char **argv) +{ + setlocale (LC_ALL, ""); + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/MM/SMS/3GPP/sms-list/zero-index", test_mbim_multipart_zero_index); + g_test_add_func ("/MM/SMS/3GPP/sms-list/mbim-multipart-unstored", test_mbim_multipart_unstored); + + return g_test_run (); +} |