aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dan@ioncontrol.co>2025-05-08 20:36:44 -0500
committerDan Williams <dan@ioncontrol.co>2025-05-08 20:36:44 -0500
commit4bb6026e37e74aad4faa50e89f3f4d98bec7368d (patch)
tree63d214f48abebcd33bf3f0f17ddcf8fa28cae3c5
parentefcc960b130356e6b05d05a915ff0f9646b00d5f (diff)
parent1d5cc0addb6576d007183454c0702d8ee3ab586f (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.build34
-rw-r--r--src/mm-base-bearer.c1
-rw-r--r--src/mm-base-sms.c1060
-rw-r--r--src/mm-base-sms.h49
-rw-r--r--src/mm-broadband-modem-mbim.c81
-rw-r--r--src/mm-broadband-modem-qmi.c49
-rw-r--r--src/mm-broadband-modem.c190
-rw-r--r--src/mm-broadband-modem.h19
-rw-r--r--src/mm-iface-modem-messaging.c136
-rw-r--r--src/mm-iface-modem-messaging.h46
-rw-r--r--src/mm-iface-modem.c12
-rw-r--r--src/mm-sms-at.c839
-rw-r--r--src/mm-sms-at.h60
-rw-r--r--src/mm-sms-list.c135
-rw-r--r--src/mm-sms-list.h11
-rw-r--r--src/mm-sms-mbim.c72
-rw-r--r--src/mm-sms-mbim.h6
-rw-r--r--src/mm-sms-qmi.c86
-rw-r--r--src/mm-sms-qmi.h6
-rw-r--r--src/tests/meson.build1
-rw-r--r--src/tests/test-sms-list.c140
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 ();
+}