| /* -*- 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) 2012 Google, Inc. |
| */ |
| |
| #include <ModemManager.h> |
| #include <libmm-common.h> |
| |
| #include "mm-iface-modem.h" |
| #include "mm-iface-modem-messaging.h" |
| #include "mm-sms-list.h" |
| #include "mm-log.h" |
| |
| #define SUPPORT_CHECKED_TAG "messaging-support-checked-tag" |
| #define SUPPORTED_TAG "messaging-supported-tag" |
| #define STORAGE_CONTEXT_TAG "messaging-storage-context-tag" |
| |
| static GQuark support_checked_quark; |
| static GQuark supported_quark; |
| static GQuark storage_context_quark; |
| |
| /*****************************************************************************/ |
| |
| void |
| mm_iface_modem_messaging_bind_simple_status (MMIfaceModemMessaging *self, |
| MMSimpleStatus *status) |
| { |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| GArray *supported_mem1; |
| GArray *supported_mem2; |
| GArray *supported_mem3; |
| } StorageContext; |
| |
| static void |
| storage_context_free (StorageContext *ctx) |
| { |
| if (ctx->supported_mem1) |
| g_array_unref (ctx->supported_mem1); |
| if (ctx->supported_mem2) |
| g_array_unref (ctx->supported_mem2); |
| if (ctx->supported_mem3) |
| g_array_unref (ctx->supported_mem3); |
| g_free (ctx); |
| } |
| |
| static StorageContext * |
| get_storage_context (MMIfaceModemMessaging *self) |
| { |
| StorageContext *ctx; |
| |
| if (G_UNLIKELY (!storage_context_quark)) |
| storage_context_quark = (g_quark_from_static_string ( |
| STORAGE_CONTEXT_TAG)); |
| |
| ctx = g_object_get_qdata (G_OBJECT (self), storage_context_quark); |
| if (!ctx) { |
| /* Create context and keep it as object data */ |
| ctx = g_new0 (StorageContext, 1); |
| |
| g_object_set_qdata_full ( |
| G_OBJECT (self), |
| storage_context_quark, |
| ctx, |
| (GDestroyNotify)storage_context_free); |
| } |
| |
| return ctx; |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MmGdbusModemMessaging *skeleton; |
| GDBusMethodInvocation *invocation; |
| MMIfaceModemMessaging *self; |
| gchar *path; |
| } HandleDeleteContext; |
| |
| static void |
| handle_delete_context_free (HandleDeleteContext *ctx) |
| { |
| g_object_unref (ctx->skeleton); |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->self); |
| g_free (ctx->path); |
| g_free (ctx); |
| } |
| |
| static void |
| handle_delete_ready (MMSmsList *list, |
| GAsyncResult *res, |
| HandleDeleteContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!mm_sms_list_delete_sms_finish (list, res, &error)) |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| else |
| mm_gdbus_modem_messaging_complete_delete (ctx->skeleton, ctx->invocation); |
| |
| handle_delete_context_free (ctx); |
| } |
| |
| static void |
| handle_delete_auth_ready (MMBaseModem *self, |
| GAsyncResult *res, |
| HandleDeleteContext *ctx) |
| { |
| MMModemState modem_state = MM_MODEM_STATE_UNKNOWN; |
| MMSmsList *list = NULL; |
| GError *error = NULL; |
| |
| if (!mm_base_modem_authorize_finish (self, res, &error)) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_delete_context_free (ctx); |
| return; |
| } |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_STATE, &modem_state, |
| NULL); |
| |
| if (modem_state < MM_MODEM_STATE_ENABLED) { |
| g_dbus_method_invocation_return_error (ctx->invocation, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_WRONG_STATE, |
| "Cannot delete SMS: device not yet enabled"); |
| handle_delete_context_free (ctx); |
| return; |
| } |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, |
| NULL); |
| g_assert (list != NULL); |
| |
| mm_sms_list_delete_sms (list, |
| ctx->path, |
| (GAsyncReadyCallback)handle_delete_ready, |
| ctx); |
| g_object_unref (list); |
| } |
| |
| static gboolean |
| handle_delete (MmGdbusModemMessaging *skeleton, |
| GDBusMethodInvocation *invocation, |
| const gchar *path, |
| MMIfaceModemMessaging *self) |
| { |
| HandleDeleteContext *ctx; |
| |
| ctx = g_new (HandleDeleteContext, 1); |
| ctx->skeleton = g_object_ref (skeleton); |
| ctx->invocation = g_object_ref (invocation); |
| ctx->self = g_object_ref (self); |
| ctx->path = g_strdup (path); |
| |
| mm_base_modem_authorize (MM_BASE_MODEM (self), |
| invocation, |
| MM_AUTHORIZATION_MESSAGING, |
| (GAsyncReadyCallback)handle_delete_auth_ready, |
| ctx); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MmGdbusModemMessaging *skeleton; |
| GDBusMethodInvocation *invocation; |
| MMIfaceModemMessaging *self; |
| GVariant *dictionary; |
| } HandleCreateContext; |
| |
| static void |
| handle_create_context_free (HandleCreateContext *ctx) |
| { |
| g_object_unref (ctx->skeleton); |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->self); |
| g_variant_unref (ctx->dictionary); |
| g_free (ctx); |
| } |
| |
| static void |
| handle_create_auth_ready (MMBaseModem *self, |
| GAsyncResult *res, |
| HandleCreateContext *ctx) |
| { |
| MMModemState modem_state = MM_MODEM_STATE_UNKNOWN; |
| MMSmsList *list = NULL; |
| GError *error = NULL; |
| MMSmsProperties *properties; |
| MMSms *sms; |
| |
| if (!mm_base_modem_authorize_finish (self, res, &error)) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_create_context_free (ctx); |
| return; |
| } |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_STATE, &modem_state, |
| NULL); |
| |
| if (modem_state < MM_MODEM_STATE_ENABLED) { |
| g_dbus_method_invocation_return_error (ctx->invocation, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_WRONG_STATE, |
| "Cannot create SMS: device not yet enabled"); |
| handle_create_context_free (ctx); |
| return; |
| } |
| |
| /* Parse input properties */ |
| properties = mm_sms_properties_new_from_dictionary (ctx->dictionary, &error); |
| if (!properties) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_create_context_free (ctx); |
| return; |
| } |
| |
| sms = mm_sms_new_from_properties (MM_BASE_MODEM (self), |
| properties, |
| &error); |
| if (!sms) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_create_context_free (ctx); |
| return; |
| } |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, |
| NULL); |
| g_assert (list != NULL); |
| |
| /* Add it to the list */ |
| mm_sms_list_add_sms (list, sms); |
| |
| /* Complete the DBus call */ |
| mm_gdbus_modem_messaging_complete_create (ctx->skeleton, |
| ctx->invocation, |
| mm_sms_get_path (sms)); |
| g_object_unref (sms); |
| |
| g_object_unref (properties); |
| g_object_unref (list); |
| |
| handle_create_context_free (ctx); |
| } |
| |
| static gboolean |
| handle_create (MmGdbusModemMessaging *skeleton, |
| GDBusMethodInvocation *invocation, |
| GVariant *dictionary, |
| MMIfaceModemMessaging *self) |
| { |
| HandleCreateContext *ctx; |
| |
| ctx = g_new (HandleCreateContext, 1); |
| ctx->skeleton = g_object_ref (skeleton); |
| ctx->invocation = g_object_ref (invocation); |
| ctx->self = g_object_ref (self); |
| ctx->dictionary = g_variant_ref (dictionary); |
| |
| mm_base_modem_authorize (MM_BASE_MODEM (self), |
| invocation, |
| MM_AUTHORIZATION_MESSAGING, |
| (GAsyncReadyCallback)handle_create_auth_ready, |
| ctx); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| static gboolean |
| handle_list (MmGdbusModemMessaging *skeleton, |
| GDBusMethodInvocation *invocation, |
| MMIfaceModemMessaging *self) |
| { |
| GStrv paths; |
| MMSmsList *list = NULL; |
| MMModemState modem_state; |
| |
| modem_state = MM_MODEM_STATE_UNKNOWN; |
| g_object_get (self, |
| MM_IFACE_MODEM_STATE, &modem_state, |
| NULL); |
| |
| if (modem_state < MM_MODEM_STATE_ENABLED) { |
| g_dbus_method_invocation_return_error (invocation, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_WRONG_STATE, |
| "Cannot list SMS messages: " |
| "device not yet enabled"); |
| return TRUE; |
| } |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, |
| NULL); |
| g_assert (list != NULL); |
| |
| paths = mm_sms_list_get_paths (list); |
| mm_gdbus_modem_messaging_complete_list (skeleton, |
| invocation, |
| (const gchar *const *)paths); |
| g_strfreev (paths); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| gboolean |
| mm_iface_modem_messaging_take_part (MMIfaceModemMessaging *self, |
| MMSmsPart *sms_part, |
| MMSmsState state, |
| MMSmsStorage storage) |
| { |
| MMSmsList *list = NULL; |
| GError *error = NULL; |
| gboolean added; |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list, |
| NULL); |
| g_assert (list != NULL); |
| added = mm_sms_list_take_part (list, sms_part, state, storage, &error); |
| if (!added) { |
| mm_dbg ("Couldn't take part in SMS list: '%s'", error->message); |
| g_error_free (error); |
| |
| /* If part wasn't taken, we need to free the part ourselves */ |
| mm_sms_part_free (sms_part); |
| } |
| g_object_unref (list); |
| |
| return added; |
| } |
| |
| /*****************************************************************************/ |
| |
| gboolean |
| mm_iface_modem_messaging_set_preferred_storages_finish (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_preferred_storages_finish) |
| return MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_preferred_storages_finish (self, res, error); |
| |
| return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); |
| } |
| |
| static gboolean |
| is_storage_supported (GArray *supported, |
| MMSmsStorage preferred, |
| const gchar *name, |
| GError **error) |
| { |
| guint i; |
| |
| /* We do allow setting UNKNOWN here, so that we set the *default* storage */ |
| if (preferred == MM_SMS_STORAGE_UNKNOWN) |
| return TRUE; |
| |
| for (i = 0; i < supported->len; i++) { |
| if (preferred == g_array_index (supported, MMSmsStorage, i)) |
| return TRUE; |
| } |
| |
| g_set_error (error, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_UNSUPPORTED, |
| "Storage '%s' is not supported in '%s'", |
| mm_sms_storage_get_string (preferred), |
| name); |
| return FALSE; |
| } |
| |
| void |
| mm_iface_modem_messaging_set_preferred_storages (MMIfaceModemMessaging *self, |
| MMSmsStorage mem1, |
| MMSmsStorage mem2, |
| MMSmsStorage mem3, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| GError *error = NULL; |
| StorageContext *ctx; |
| MMSmsStorage default_mem1; |
| MMSmsStorage default_mem2; |
| MMSmsStorage default_mem3; |
| |
| if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_preferred_storages || |
| !MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_preferred_storages_finish) { |
| g_simple_async_report_error_in_idle (G_OBJECT (self), |
| callback, |
| user_data, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_UNSUPPORTED, |
| "Setting preferred storage is not supported"); |
| return; |
| } |
| |
| /* Check if the requested storages are really supported */ |
| ctx = get_storage_context (self); |
| if (!is_storage_supported (ctx->supported_mem1, mem1, "mem1", &error) || |
| !is_storage_supported (ctx->supported_mem2, mem2, "mem2", &error) || |
| !is_storage_supported (ctx->supported_mem3, mem3, "mem3", &error)) { |
| g_simple_async_report_take_gerror_in_idle (G_OBJECT (self), |
| callback, |
| user_data, |
| error); |
| return; |
| } |
| |
| default_mem1 = MM_SMS_STORAGE_UNKNOWN; |
| default_mem2 = MM_SMS_STORAGE_UNKNOWN; |
| default_mem3 = MM_SMS_STORAGE_UNKNOWN; |
| g_object_get (self, |
| MM_IFACE_MODEM_MESSAGING_SMS_MEM1_STORAGE, &default_mem1, |
| MM_IFACE_MODEM_MESSAGING_SMS_MEM2_STORAGE, &default_mem2, |
| MM_IFACE_MODEM_MESSAGING_SMS_MEM3_STORAGE, &default_mem3, |
| NULL); |
| |
| /* If unknown given, set defaults */ |
| if (mem1 == MM_SMS_STORAGE_UNKNOWN) |
| mem1 = default_mem1; |
| if (mem2 == MM_SMS_STORAGE_UNKNOWN) |
| mem2 = default_mem2; |
| if (mem3 == MM_SMS_STORAGE_UNKNOWN) |
| mem3 = default_mem3; |
| |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_preferred_storages ( |
| self, mem1, mem2, mem3, callback, user_data); |
| } |
| |
| /*****************************************************************************/ |
| |
| static void |
| sms_added (MMSmsList *list, |
| const gchar *sms_path, |
| gboolean received, |
| MmGdbusModemMessaging *skeleton) |
| { |
| mm_dbg ("Added %s SMS at '%s'", |
| received ? "received" : "local", |
| sms_path); |
| mm_gdbus_modem_messaging_emit_added (skeleton, sms_path, received); |
| } |
| |
| static void |
| sms_deleted (MMSmsList *list, |
| const gchar *sms_path, |
| MmGdbusModemMessaging *skeleton) |
| { |
| mm_dbg ("Deleted SMS at '%s'", sms_path); |
| mm_gdbus_modem_messaging_emit_deleted (skeleton, sms_path); |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct _DisablingContext DisablingContext; |
| static void interface_disabling_step (DisablingContext *ctx); |
| |
| typedef enum { |
| DISABLING_STEP_FIRST, |
| DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, |
| DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, |
| DISABLING_STEP_LAST |
| } DisablingStep; |
| |
| struct _DisablingContext { |
| MMIfaceModemMessaging *self; |
| DisablingStep step; |
| GSimpleAsyncResult *result; |
| MmGdbusModemMessaging *skeleton; |
| }; |
| |
| static DisablingContext * |
| disabling_context_new (MMIfaceModemMessaging *self, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| DisablingContext *ctx; |
| |
| ctx = g_new0 (DisablingContext, 1); |
| ctx->self = g_object_ref (self); |
| ctx->result = g_simple_async_result_new (G_OBJECT (self), |
| callback, |
| user_data, |
| disabling_context_new); |
| ctx->step = DISABLING_STEP_FIRST; |
| g_object_get (ctx->self, |
| MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, &ctx->skeleton, |
| NULL); |
| g_assert (ctx->skeleton != NULL); |
| |
| return ctx; |
| } |
| |
| static void |
| disabling_context_complete_and_free (DisablingContext *ctx) |
| { |
| g_simple_async_result_complete_in_idle (ctx->result); |
| g_object_unref (ctx->self); |
| g_object_unref (ctx->result); |
| g_object_unref (ctx->skeleton); |
| g_free (ctx); |
| } |
| |
| gboolean |
| mm_iface_modem_messaging_disable_finish (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); |
| } |
| |
| static void |
| disable_unsolicited_events_ready (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| DisablingContext *ctx) |
| { |
| GError *error = NULL; |
| |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error); |
| if (error) { |
| g_simple_async_result_take_error (ctx->result, error); |
| disabling_context_complete_and_free (ctx); |
| return; |
| } |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_disabling_step (ctx); |
| } |
| |
| static void |
| cleanup_unsolicited_events_ready (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| DisablingContext *ctx) |
| { |
| GError *error = NULL; |
| |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error); |
| if (error) { |
| g_simple_async_result_take_error (ctx->result, error); |
| disabling_context_complete_and_free (ctx); |
| return; |
| } |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_disabling_step (ctx); |
| } |
| |
| static void |
| interface_disabling_step (DisablingContext *ctx) |
| { |
| switch (ctx->step) { |
| case DISABLING_STEP_FIRST: |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: |
| /* Allow cleaning up unsolicited events */ |
| if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->disable_unsolicited_events && |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->disable_unsolicited_events_finish) { |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->disable_unsolicited_events ( |
| ctx->self, |
| (GAsyncReadyCallback)disable_unsolicited_events_ready, |
| ctx); |
| return; |
| } |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: |
| /* Allow cleaning up unsolicited events */ |
| if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events && |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events_finish) { |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events ( |
| ctx->self, |
| (GAsyncReadyCallback)cleanup_unsolicited_events_ready, |
| ctx); |
| return; |
| } |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case DISABLING_STEP_LAST: |
| /* Clear SMS list */ |
| g_object_set (ctx->self, |
| MM_IFACE_MODEM_MESSAGING_SMS_LIST, NULL, |
| NULL); |
| |
| /* We are done without errors! */ |
| g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); |
| disabling_context_complete_and_free (ctx); |
| return; |
| } |
| |
| g_assert_not_reached (); |
| } |
| |
| void |
| mm_iface_modem_messaging_disable (MMIfaceModemMessaging *self, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| interface_disabling_step (disabling_context_new (self, |
| callback, |
| user_data)); |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct _EnablingContext EnablingContext; |
| static void interface_enabling_step (EnablingContext *ctx); |
| |
| typedef enum { |
| ENABLING_STEP_FIRST, |
| ENABLING_STEP_SETUP_SMS_FORMAT, |
| ENABLING_STEP_LOAD_INITIAL_SMS_PARTS, |
| ENABLING_STEP_STORAGE_DEFAULTS, |
| ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, |
| ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, |
| ENABLING_STEP_LAST |
| } EnablingStep; |
| |
| struct _EnablingContext { |
| MMIfaceModemMessaging *self; |
| EnablingStep step; |
| GSimpleAsyncResult *result; |
| GCancellable *cancellable; |
| MmGdbusModemMessaging *skeleton; |
| guint mem1_storage_index; |
| }; |
| |
| static EnablingContext * |
| enabling_context_new (MMIfaceModemMessaging *self, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| EnablingContext *ctx; |
| |
| ctx = g_new0 (EnablingContext, 1); |
| ctx->self = g_object_ref (self); |
| ctx->cancellable = g_object_ref (cancellable); |
| ctx->result = g_simple_async_result_new (G_OBJECT (self), |
| callback, |
| user_data, |
| enabling_context_new); |
| ctx->step = ENABLING_STEP_FIRST; |
| g_object_get (ctx->self, |
| MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, &ctx->skeleton, |
| NULL); |
| g_assert (ctx->skeleton != NULL); |
| |
| return ctx; |
| } |
| |
| static void |
| enabling_context_complete_and_free (EnablingContext *ctx) |
| { |
| g_simple_async_result_complete_in_idle (ctx->result); |
| g_object_unref (ctx->self); |
| g_object_unref (ctx->result); |
| g_object_unref (ctx->cancellable); |
| g_object_unref (ctx->skeleton); |
| g_free (ctx); |
| } |
| |
| static gboolean |
| enabling_context_complete_and_free_if_cancelled (EnablingContext *ctx) |
| { |
| if (!g_cancellable_is_cancelled (ctx->cancellable)) |
| return FALSE; |
| |
| g_simple_async_result_set_error (ctx->result, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_CANCELLED, |
| "Interface enabling cancelled"); |
| enabling_context_complete_and_free (ctx); |
| return TRUE; |
| } |
| |
| gboolean |
| mm_iface_modem_messaging_enable_finish (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); |
| } |
| |
| #undef VOID_REPLY_READY_FN |
| #define VOID_REPLY_READY_FN(NAME) \ |
| static void \ |
| NAME##_ready (MMIfaceModemMessaging *self, \ |
| GAsyncResult *res, \ |
| EnablingContext *ctx) \ |
| { \ |
| GError *error = NULL; \ |
| \ |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->NAME##_finish (self, res, &error); \ |
| if (error) { \ |
| g_simple_async_result_take_error (ctx->result, error); \ |
| enabling_context_complete_and_free (ctx); \ |
| return; \ |
| } \ |
| \ |
| /* Go on to next step */ \ |
| ctx->step++; \ |
| interface_enabling_step (ctx); \ |
| } |
| |
| VOID_REPLY_READY_FN (setup_sms_format) |
| |
| static void load_initial_sms_parts_from_storages (EnablingContext *ctx); |
| |
| static void |
| load_initial_sms_parts_ready (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| EnablingContext *ctx) |
| { |
| GError *error = NULL; |
| |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts_finish (self, res, &error); |
| if (error) { |
| g_simple_async_result_take_error (ctx->result, error); |
| enabling_context_complete_and_free (ctx); |
| return; |
| } |
| |
| /* Go on with the storage iteration */ |
| ctx->mem1_storage_index++; |
| load_initial_sms_parts_from_storages (ctx); |
| } |
| |
| static void |
| load_initial_sms_parts_from_storages (EnablingContext *ctx) |
| { |
| gboolean all_loaded = FALSE; |
| StorageContext *storage_ctx; |
| |
| storage_ctx = get_storage_context (ctx->self); |
| |
| if (ctx->mem1_storage_index >= storage_ctx->supported_mem1->len) |
| all_loaded = TRUE; |
| /* We'll skip the 'MT' storage, as that is a combination of 'SM' and 'ME' */ |
| else if (g_array_index (storage_ctx->supported_mem1, |
| MMSmsStorage, |
| ctx->mem1_storage_index) == MM_SMS_STORAGE_MT) { |
| ctx->mem1_storage_index++; |
| if (ctx->mem1_storage_index >= storage_ctx->supported_mem1->len) |
| all_loaded = TRUE; |
| } |
| |
| if (all_loaded) { |
| /* Go on with next step */ |
| ctx->step++; |
| interface_enabling_step (ctx); |
| return; |
| } |
| |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_initial_sms_parts ( |
| ctx->self, |
| g_array_index (storage_ctx->supported_mem1, |
| MMSmsStorage, |
| ctx->mem1_storage_index), |
| (GAsyncReadyCallback)load_initial_sms_parts_ready, |
| ctx); |
| return; |
| } |
| |
| static void |
| set_default_storages_ready (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| EnablingContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!mm_iface_modem_messaging_set_preferred_storages_finish (self, res, &error)) { |
| mm_dbg ("Couldn't set preferred storages: '%s'", error->message); |
| g_error_free (error); |
| } |
| |
| /* Go on with next step */ |
| ctx->step++; |
| interface_enabling_step (ctx); |
| } |
| |
| VOID_REPLY_READY_FN (setup_unsolicited_events) |
| |
| static void |
| enable_unsolicited_events_ready (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| EnablingContext *ctx) |
| { |
| GError *error = NULL; |
| |
| /* Not critical! */ |
| if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) { |
| mm_dbg ("Couldn't enable unsolicited events: '%s'", error->message); |
| g_error_free (error); |
| } |
| |
| /* Go on with next step */ |
| ctx->step++; |
| interface_enabling_step (ctx); |
| } |
| |
| static void |
| interface_enabling_step (EnablingContext *ctx) |
| { |
| /* Don't run new steps if we're cancelled */ |
| if (enabling_context_complete_and_free_if_cancelled (ctx)) |
| return; |
| |
| switch (ctx->step) { |
| case ENABLING_STEP_FIRST: { |
| MMSmsList *list; |
| |
| list = mm_sms_list_new (MM_BASE_MODEM (ctx->self)); |
| g_object_set (ctx->self, |
| MM_IFACE_MODEM_MESSAGING_SMS_LIST, list, |
| NULL); |
| |
| /* Connect to list's signals */ |
| g_signal_connect (list, |
| MM_SMS_ADDED, |
| G_CALLBACK (sms_added), |
| ctx->skeleton); |
| g_signal_connect (list, |
| MM_SMS_DELETED, |
| G_CALLBACK (sms_deleted), |
| ctx->skeleton); |
| |
| g_object_unref (list); |
| |
| /* Fall down to next step */ |
| ctx->step++; |
| } |
| |
| case ENABLING_STEP_SETUP_SMS_FORMAT: |
| /* Allow setting SMS format to use */ |
| if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_sms_format && |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_sms_format_finish) { |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_sms_format ( |
| ctx->self, |
| (GAsyncReadyCallback)setup_sms_format_ready, |
| ctx); |
| return; |
| } |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case ENABLING_STEP_LOAD_INITIAL_SMS_PARTS: |
| /* Allow loading the initial list of SMS parts */ |
| if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_initial_sms_parts && |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_initial_sms_parts_finish) { |
| load_initial_sms_parts_from_storages (ctx); |
| return; |
| } |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case ENABLING_STEP_STORAGE_DEFAULTS: |
| /* Set storage defaults */ |
| mm_dbg ("Setting default preferred storages..."); |
| mm_iface_modem_messaging_set_preferred_storages (ctx->self, |
| MM_SMS_STORAGE_UNKNOWN, |
| MM_SMS_STORAGE_UNKNOWN, |
| MM_SMS_STORAGE_UNKNOWN, |
| (GAsyncReadyCallback)set_default_storages_ready, |
| ctx); |
| return; |
| |
| case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: |
| /* Allow setting up unsolicited events */ |
| if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_unsolicited_events && |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_unsolicited_events_finish) { |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_unsolicited_events ( |
| ctx->self, |
| (GAsyncReadyCallback)setup_unsolicited_events_ready, |
| ctx); |
| return; |
| } |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: |
| /* Allow setting up unsolicited events */ |
| if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->enable_unsolicited_events && |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->enable_unsolicited_events_finish) { |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->enable_unsolicited_events ( |
| ctx->self, |
| (GAsyncReadyCallback)enable_unsolicited_events_ready, |
| ctx); |
| return; |
| } |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case ENABLING_STEP_LAST: |
| /* We are done without errors! */ |
| g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); |
| enabling_context_complete_and_free (ctx); |
| return; |
| } |
| |
| g_assert_not_reached (); |
| } |
| |
| void |
| mm_iface_modem_messaging_enable (MMIfaceModemMessaging *self, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| interface_enabling_step (enabling_context_new (self, |
| cancellable, |
| callback, |
| user_data)); |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct _InitializationContext InitializationContext; |
| static void interface_initialization_step (InitializationContext *ctx); |
| |
| typedef enum { |
| INITIALIZATION_STEP_FIRST, |
| INITIALIZATION_STEP_CHECK_SUPPORT, |
| INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED, |
| INITIALIZATION_STEP_LOAD_SUPPORTED_STORAGES, |
| INITIALIZATION_STEP_LAST |
| } InitializationStep; |
| |
| struct _InitializationContext { |
| MMIfaceModemMessaging *self; |
| MmGdbusModemMessaging *skeleton; |
| GCancellable *cancellable; |
| GSimpleAsyncResult *result; |
| InitializationStep step; |
| }; |
| |
| static InitializationContext * |
| initialization_context_new (MMIfaceModemMessaging *self, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| InitializationContext *ctx; |
| |
| ctx = g_new0 (InitializationContext, 1); |
| ctx->self = g_object_ref (self); |
| ctx->cancellable = g_object_ref (cancellable); |
| ctx->result = g_simple_async_result_new (G_OBJECT (self), |
| callback, |
| user_data, |
| initialization_context_new); |
| ctx->step = INITIALIZATION_STEP_FIRST; |
| g_object_get (ctx->self, |
| MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, &ctx->skeleton, |
| NULL); |
| g_assert (ctx->skeleton != NULL); |
| return ctx; |
| } |
| |
| static void |
| initialization_context_complete_and_free (InitializationContext *ctx) |
| { |
| g_simple_async_result_complete_in_idle (ctx->result); |
| g_object_unref (ctx->self); |
| g_object_unref (ctx->result); |
| g_object_unref (ctx->cancellable); |
| g_object_unref (ctx->skeleton); |
| g_free (ctx); |
| } |
| |
| static gboolean |
| initialization_context_complete_and_free_if_cancelled (InitializationContext *ctx) |
| { |
| if (!g_cancellable_is_cancelled (ctx->cancellable)) |
| return FALSE; |
| |
| g_simple_async_result_set_error (ctx->result, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_CANCELLED, |
| "Interface initialization cancelled"); |
| initialization_context_complete_and_free (ctx); |
| return TRUE; |
| } |
| |
| static void |
| load_supported_storages_ready (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| InitializationContext *ctx) |
| { |
| StorageContext *storage_ctx; |
| GError *error = NULL; |
| |
| storage_ctx = get_storage_context (self); |
| if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages_finish ( |
| self, |
| res, |
| &storage_ctx->supported_mem1, |
| &storage_ctx->supported_mem2, |
| &storage_ctx->supported_mem3, |
| &error)) { |
| mm_dbg ("Couldn't load supported storages: '%s'", error->message); |
| g_error_free (error); |
| } else { |
| gchar *mem1; |
| gchar *mem2; |
| gchar *mem3; |
| |
| mem1 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem1->data, |
| storage_ctx->supported_mem1->len); |
| mem2 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem2->data, |
| storage_ctx->supported_mem2->len); |
| mem3 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem3->data, |
| storage_ctx->supported_mem3->len); |
| |
| mm_dbg ("Supported storages loaded:"); |
| mm_dbg (" mem1 (list/read/delete) storages: '%s'", mem1); |
| mm_dbg (" mem2 (write/send) storages: '%s'", mem2); |
| mm_dbg (" mem3 (reception) storages: '%s'", mem3); |
| g_free (mem1); |
| g_free (mem2); |
| g_free (mem3); |
| } |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_initialization_step (ctx); |
| } |
| |
| static void |
| check_support_ready (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| InitializationContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support_finish (self, |
| res, |
| &error)) { |
| if (error) { |
| /* This error shouldn't be treated as critical */ |
| mm_dbg ("Messaging support check failed: '%s'", error->message); |
| g_error_free (error); |
| } |
| } else { |
| /* Messaging is supported! */ |
| g_object_set_qdata (G_OBJECT (self), |
| supported_quark, |
| GUINT_TO_POINTER (TRUE)); |
| } |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_initialization_step (ctx); |
| } |
| |
| static void |
| interface_initialization_step (InitializationContext *ctx) |
| { |
| /* Don't run new steps if we're cancelled */ |
| if (initialization_context_complete_and_free_if_cancelled (ctx)) |
| return; |
| |
| switch (ctx->step) { |
| case INITIALIZATION_STEP_FIRST: |
| /* Setup quarks if we didn't do it before */ |
| if (G_UNLIKELY (!support_checked_quark)) |
| support_checked_quark = (g_quark_from_static_string ( |
| SUPPORT_CHECKED_TAG)); |
| if (G_UNLIKELY (!supported_quark)) |
| supported_quark = (g_quark_from_static_string ( |
| SUPPORTED_TAG)); |
| |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case INITIALIZATION_STEP_CHECK_SUPPORT: |
| if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self), |
| support_checked_quark))) { |
| /* Set the checked flag so that we don't run it again */ |
| g_object_set_qdata (G_OBJECT (ctx->self), |
| support_checked_quark, |
| GUINT_TO_POINTER (TRUE)); |
| /* Initially, assume we don't support it */ |
| g_object_set_qdata (G_OBJECT (ctx->self), |
| supported_quark, |
| GUINT_TO_POINTER (FALSE)); |
| |
| if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->check_support && |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->check_support_finish) { |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->check_support ( |
| ctx->self, |
| (GAsyncReadyCallback)check_support_ready, |
| ctx); |
| return; |
| } |
| |
| /* If there is no implementation to check support, assume we DON'T |
| * support it. */ |
| } |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED: |
| if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self), |
| supported_quark))) { |
| g_simple_async_result_set_error (ctx->result, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_UNSUPPORTED, |
| "Messaging not supported"); |
| initialization_context_complete_and_free (ctx); |
| return; |
| } |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case INITIALIZATION_STEP_LOAD_SUPPORTED_STORAGES: |
| if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_supported_storages && |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_supported_storages_finish) { |
| MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_supported_storages ( |
| ctx->self, |
| (GAsyncReadyCallback)load_supported_storages_ready, |
| ctx); |
| return; |
| } |
| /* Fall down to next step */ |
| ctx->step++; |
| |
| case INITIALIZATION_STEP_LAST: |
| /* We are done without errors! */ |
| |
| /* Handle method invocations */ |
| g_signal_connect (ctx->skeleton, |
| "handle-create", |
| G_CALLBACK (handle_create), |
| ctx->self); |
| g_signal_connect (ctx->skeleton, |
| "handle-delete", |
| G_CALLBACK (handle_delete), |
| ctx->self); |
| g_signal_connect (ctx->skeleton, |
| "handle-list", |
| G_CALLBACK (handle_list), |
| ctx->self); |
| |
| /* Finally, export the new interface */ |
| mm_gdbus_object_skeleton_set_modem_messaging (MM_GDBUS_OBJECT_SKELETON (ctx->self), |
| MM_GDBUS_MODEM_MESSAGING (ctx->skeleton)); |
| |
| g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); |
| initialization_context_complete_and_free (ctx); |
| return; |
| } |
| |
| g_assert_not_reached (); |
| } |
| |
| gboolean |
| mm_iface_modem_messaging_initialize_finish (MMIfaceModemMessaging *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| g_return_val_if_fail (MM_IS_IFACE_MODEM_MESSAGING (self), FALSE); |
| g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); |
| |
| return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); |
| } |
| |
| void |
| mm_iface_modem_messaging_initialize (MMIfaceModemMessaging *self, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| MmGdbusModemMessaging *skeleton = NULL; |
| |
| g_return_if_fail (MM_IS_IFACE_MODEM_MESSAGING (self)); |
| |
| /* Did we already create it? */ |
| g_object_get (self, |
| MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, &skeleton, |
| NULL); |
| if (!skeleton) { |
| skeleton = mm_gdbus_modem_messaging_skeleton_new (); |
| |
| g_object_set (self, |
| MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, skeleton, |
| NULL); |
| } |
| |
| /* Perform async initialization here */ |
| interface_initialization_step (initialization_context_new (self, |
| cancellable, |
| callback, |
| user_data)); |
| g_object_unref (skeleton); |
| } |
| |
| void |
| mm_iface_modem_messaging_shutdown (MMIfaceModemMessaging *self) |
| { |
| g_return_if_fail (MM_IS_IFACE_MODEM_MESSAGING (self)); |
| |
| /* Unexport DBus interface and remove the skeleton */ |
| mm_gdbus_object_skeleton_set_modem_messaging (MM_GDBUS_OBJECT_SKELETON (self), NULL); |
| g_object_set (self, |
| MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, NULL, |
| NULL); |
| } |
| |
| /*****************************************************************************/ |
| |
| static void |
| iface_modem_messaging_init (gpointer g_iface) |
| { |
| static gboolean initialized = FALSE; |
| |
| if (initialized) |
| return; |
| |
| /* Properties */ |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_object (MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON, |
| "Messaging DBus skeleton", |
| "DBus skeleton for the Messaging interface", |
| MM_GDBUS_TYPE_MODEM_MESSAGING_SKELETON, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_object (MM_IFACE_MODEM_MESSAGING_SMS_LIST, |
| "SMS list", |
| "List of SMS objects managed in the interface", |
| MM_TYPE_SMS_LIST, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_boolean (MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, |
| "PDU mode", |
| "Whether PDU mode should be used", |
| FALSE, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_enum (MM_IFACE_MODEM_MESSAGING_SMS_MEM1_STORAGE, |
| "SMS mem1 storage", |
| "Default storage to be used when listing/reading/deleting SMS messages", |
| MM_TYPE_SMS_STORAGE, |
| MM_SMS_STORAGE_ME, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_enum (MM_IFACE_MODEM_MESSAGING_SMS_MEM2_STORAGE, |
| "SMS mem2 storage", |
| "Default storage to be used when writing/sending SMS messages", |
| MM_TYPE_SMS_STORAGE, |
| MM_SMS_STORAGE_ME, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_enum (MM_IFACE_MODEM_MESSAGING_SMS_MEM3_STORAGE, |
| "SMS mem3 storage", |
| "Default storage to be used when receiving SMS messages", |
| MM_TYPE_SMS_STORAGE, |
| MM_SMS_STORAGE_ME, |
| G_PARAM_READWRITE)); |
| |
| initialized = TRUE; |
| } |
| |
| GType |
| mm_iface_modem_messaging_get_type (void) |
| { |
| static GType iface_modem_messaging_type = 0; |
| |
| if (!G_UNLIKELY (iface_modem_messaging_type)) { |
| static const GTypeInfo info = { |
| sizeof (MMIfaceModemMessaging), /* class_size */ |
| iface_modem_messaging_init, /* base_init */ |
| NULL, /* base_finalize */ |
| }; |
| |
| iface_modem_messaging_type = g_type_register_static (G_TYPE_INTERFACE, |
| "MMIfaceModemMessaging", |
| &info, |
| 0); |
| |
| g_type_interface_add_prerequisite (iface_modem_messaging_type, MM_TYPE_IFACE_MODEM); |
| } |
| |
| return iface_modem_messaging_type; |
| } |