/*
* modem/sms-service.c - ModemSMSService class
*
* Copyright (C) 2008-2010 Nokia Corporation
* @author Pekka Pessi <first.surname@nokia.com>
* @author Lassi Syrjala <first.surname@nokia.com>
* Copyright (C) 2013-2014 Jolla Ltd
*
* This work is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This work 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#define MODEM_DEBUG_FLAG MODEM_LOG_SMS
#include "debug.h"
#include "modem/sms.h"
#include "modem/sms-message.h"
#include "modem/sms-history.h"
#include "modem/request-private.h"
#include "modem/errors.h"
#if nomore
#include "sms-glib/deliver.h"
#include "sms-glib/status-report.h"
#endif
#include "modem/ofono.h"
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "signals-marshal.h"
#include <uuid/uuid.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* ---------------------------------------------------------------------- */
G_DEFINE_TYPE (ModemSMSService, modem_sms_service, MODEM_TYPE_OFACE)
/* Signals we emit */
enum
{
SIGNAL_INCOMING_MESSAGE,
SIGNAL_IMMEDIATE_MESSAGE,
SIGNAL_OUTGOING_COMPLETE,
SIGNAL_OUTGOING_ERROR,
SIGNAL_STATUS_REPORT,
#if nomore
SIGNAL_DELIVER,
#endif
N_SIGNALS
};
static guint signals[N_SIGNALS];
/* Properties */
enum
{
PROP_NONE,
PROP_CONTENT_TYPES,
PROP_SMSC,
PROP_VALIDITY_PERIOD,
PROP_REDUCED_CHARSET,
PROP_USE_DELIVERY_REPORTS,
LAST_PROPERTY
};
/* private data */
struct _ModemSMSServicePrivate
{
time_t connected; /* Timestamp when got connected */
char *smsc;
guint validity_period;
#if nomore
char **content_types;
GHashTable *received;
#endif
/* A list of pending MO SMS waiting for a send status
and - if requested - a status report */
GHashTable *pending_outgoing;
unsigned reduced_charset:1;
unsigned signals:1, :0;
gboolean use_delivery_reports;
};
/* ------------------------------------------------------------------------ */
#if nomore
static void modem_sms_incoming_deliver (ModemSMSService *self,
SMSGDeliver *deliver);
#endif
static void on_incoming_message (DBusGProxy *, char const *, GHashTable *, gpointer);
static void on_immediate_message (DBusGProxy *, char const *, GHashTable *, gpointer);
static void on_manager_message_removed (DBusGProxy *, char const *, gpointer);
static void add_pending_message (char const *, GHashTable *, gpointer);
/* ------------------------------------------------------------------------ */
/* GObject interface */
static void
modem_sms_service_init (ModemSMSService *self)
{
DEBUG ("enter");
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MODEM_TYPE_SMS_SERVICE, ModemSMSServicePrivate);
#if nomore
self->priv->received = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, /* Message object stored in hash owns the key */
g_object_unref);
#endif
self->priv->pending_outgoing = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, /* Message object stored in hash owns the key */
g_object_unref);
}
static void
modem_sms_service_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
ModemSMSService *self = MODEM_SMS_SERVICE (object);
ModemSMSServicePrivate *priv = self->priv;
switch (property_id)
{
#if nomore
case PROP_CONTENT_TYPES:
g_value_set_boxed (value, priv->content_types);
break;
#endif
case PROP_SMSC:
g_value_set_string (value, priv->smsc);
break;
case PROP_VALIDITY_PERIOD:
g_value_set_uint (value, priv->validity_period);
break;
case PROP_REDUCED_CHARSET:
g_value_set_boolean (value, priv->reduced_charset);
break;
case PROP_USE_DELIVERY_REPORTS:
g_value_set_boolean (value, priv->use_delivery_reports);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
modem_sms_service_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
ModemSMSService *self = MODEM_SMS_SERVICE (object);
ModemSMSServicePrivate *priv = self->priv;
gpointer old;
switch (property_id)
{
#if nomore
case PROP_CONTENT_TYPES:
old = priv->content_types;
priv->content_types = g_value_dup_boxed (value);
if (old) g_boxed_free (G_TYPE_STRV, old);
break;
#endif
case PROP_SMSC:
old = priv->smsc;
priv->smsc = g_value_dup_string (value);
g_free (old);
break;
case PROP_VALIDITY_PERIOD:
priv->validity_period = g_value_get_uint (value);
break;
case PROP_REDUCED_CHARSET:
priv->reduced_charset = g_value_get_boolean (value);
break;
case PROP_USE_DELIVERY_REPORTS:
priv->use_delivery_reports = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
modem_sms_service_constructed (GObject *object)
{
if (G_OBJECT_CLASS (modem_sms_service_parent_class)->constructed)
G_OBJECT_CLASS (modem_sms_service_parent_class)->constructed (object);
if (modem_oface_dbus_proxy (MODEM_OFACE (object)) == NULL)
g_warning("object created without dbus-proxy set");
}
static void
modem_sms_service_dispose (GObject *object)
{
if (G_OBJECT_CLASS (modem_sms_service_parent_class)->dispose)
G_OBJECT_CLASS (modem_sms_service_parent_class)->dispose (object);
}
static void
modem_sms_service_finalize (GObject *object)
{
ModemSMSService *self = MODEM_SMS_SERVICE (object);
ModemSMSServicePrivate *priv = self->priv;
DEBUG ("enter");
/* Free any data held directly by the object here */
g_free (priv->smsc);
#if nomore
if (priv->content_types)
g_boxed_free (G_TYPE_STRV, priv->content_types);
if (priv->received)
g_hash_table_destroy (priv->received);
#endif
g_hash_table_destroy (priv->pending_outgoing);
G_OBJECT_CLASS (modem_sms_service_parent_class)->finalize (object);
}
/* ------------------------------------------------------------------------- */
/* oface */
static void reply_to_sms_manager_get_messages (ModemOface *_self,
ModemRequest *request, GPtrArray *array, GError const *error,
gpointer user_data);
static char const *
modem_sms_service_property_mapper (char const *name)
{
if (!strcmp (name, "UseDeliveryReports"))
return "use-delivery-reports";
if (!strcmp (name, "ServiceCenterAddress"))
return "service-centre";
if (!strcmp (name, "Bearer"))
return NULL;
return NULL;
}
static void
modem_sms_service_connect (ModemOface *_self)
{
DEBUG ("(%p): enter", _self);
ModemSMSService *self = MODEM_SMS_SERVICE (_self);
ModemSMSServicePrivate *priv = self->priv;
DBusGProxy *proxy = modem_oface_dbus_proxy (_self);
if (!priv->signals)
{
priv->signals = TRUE;
#define CONNECT(handler, name, signature...) \
dbus_g_proxy_add_signal (proxy, (name), ##signature); \
dbus_g_proxy_connect_signal (proxy, (name), G_CALLBACK (handler), self, NULL)
CONNECT (on_immediate_message, "ImmediateMessage",
G_TYPE_STRING, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID);
CONNECT (on_incoming_message, "IncomingMessage",
G_TYPE_STRING, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID);
CONNECT (on_manager_message_removed, "MessageRemoved",
DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
CONNECT (on_manager_message_status_report, "StatusReport",
G_TYPE_STRING, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID);
}
modem_oface_connect_properties (_self, TRUE);
/* read pending MO SMS from oFono's TX queue */
modem_oface_add_connect_request (_self,
modem_oface_request_managed (_self, "GetMessages",
reply_to_sms_manager_get_messages, NULL));
}
static void
reply_to_sms_manager_get_messages (ModemOface *_self,
ModemRequest *request,
GPtrArray *array,
GError const *error,
gpointer user_data)
{
ModemSMSService *self = MODEM_SMS_SERVICE (_self);
DEBUG ("(%p): enter", _self);
if (!error)
{
guint i;
for (i = 0; i < array->len; i++)
{
GValueArray *va = g_ptr_array_index (array, i);
char const *path = g_value_get_boxed (va->values + 0);
GHashTable *properties = g_value_get_boxed (va->values + 1);
add_pending_message (path, properties, self);
}
}
modem_oface_check_connected (_self, request, error);
}
static void
modem_sms_service_connected (ModemOface *_self)
{
DEBUG ("(%p): enter", _self);
ModemSMSService *self = MODEM_SMS_SERVICE (_self);
ModemSMSServicePrivate *priv = self->priv;
time_t now = time (NULL);
priv->connected = now;
#if nomore
GHashTableIter i[1];
gpointer key, value;
for (g_hash_table_iter_init (i, priv->received);
g_hash_table_iter_next (i, &key, &value);)
{
g_object_set (value, "time-delivered", (guint64)now, NULL);
}
#endif
}
static void
modem_sms_service_disconnect (ModemOface *_self)
{
DEBUG ("(%p): enter", _self);
ModemSMSService *self = MODEM_SMS_SERVICE (_self);
ModemSMSServicePrivate *priv = self->priv;
DBusGProxy *proxy = modem_oface_dbus_proxy (_self);
if (priv->signals)
{
priv->signals = FALSE;
dbus_g_proxy_disconnect_signal (proxy, "ImmediateMessage",
G_CALLBACK (on_immediate_message), self);
dbus_g_proxy_disconnect_signal (proxy, "IncomingMessage",
G_CALLBACK (on_incoming_message), self);
dbus_g_proxy_disconnect_signal (proxy, "MessageRemoved",
G_CALLBACK (on_manager_message_removed), self);
dbus_g_proxy_disconnect_signal (proxy, "StatusReport",
G_CALLBACK (on_manager_message_status_report), self);
}
#if nomore
g_hash_table_remove_all (priv->received);
#endif
modem_oface_disconnect_properties (_self);
}
/* ------------------------------------------------------------------------- */
static void
modem_sms_service_class_init (ModemSMSServiceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ModemOfaceClass *oface_class = MODEM_OFACE_CLASS (klass);
DEBUG ("enter");
object_class->get_property = modem_sms_service_get_property;
object_class->set_property = modem_sms_service_set_property;
object_class->constructed = modem_sms_service_constructed;
object_class->dispose = modem_sms_service_dispose;
object_class->finalize = modem_sms_service_finalize;
oface_class->ofono_interface = MODEM_OFACE_SMS;
oface_class->property_mapper = modem_sms_service_property_mapper;
oface_class->connect = modem_sms_service_connect;
oface_class->connected = modem_sms_service_connected;
oface_class->disconnect = modem_sms_service_disconnect;
#if nomore
g_object_class_install_property (object_class, PROP_CONTENT_TYPES,
g_param_spec_boxed ("content-types",
"Content types used",
"List of MIME content types used by application.",
G_TYPE_STRV,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
#endif
g_object_class_install_property (object_class, PROP_SMSC,
g_param_spec_string ("service-centre",
"SMS Service Centre",
"ISDN Address for SMS Service Centre",
"", /* default value */
G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_VALIDITY_PERIOD,
g_param_spec_uint ("validity-period",
"SMS Validity Period",
"Period while SMS service centre "
"keep trying to deliver SMS.",
/* anything above 0 gets rounded up to 5 minutes */
0, /* 0 means no validity period */
63 * 7 * 24 * 60 * 60, /* max - 63 weeks */
0, /* no validity period - it is up to service centre */
G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_REDUCED_CHARSET,
g_param_spec_boolean ("reduced-charset",
"SMS reduced character set support",
"Whether SMS should be encoded with "
"a reduced character set",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_USE_DELIVERY_REPORTS,
g_param_spec_boolean ("use-delivery-reports",
"Use Delivery Reports",
"Use Delivery Reports",
FALSE, /* default value */
G_PARAM_READWRITE | G_PARAM_CONSTRUCT ));
signals[SIGNAL_IMMEDIATE_MESSAGE] =
g_signal_new ("immediate-message",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
_modem__marshal_VOID__STRING_BOXED,
G_TYPE_NONE, 2,
G_TYPE_STRING, G_TYPE_HASH_TABLE);
signals[SIGNAL_INCOMING_MESSAGE] =
g_signal_new ("incoming-message",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
_modem__marshal_VOID__STRING_BOXED,
G_TYPE_NONE, 2,
G_TYPE_STRING, G_TYPE_HASH_TABLE);
signals[SIGNAL_OUTGOING_COMPLETE] =
g_signal_new ("outgoing-complete",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
_modem__marshal_VOID__STRING_STRING,
G_TYPE_NONE, 2,
G_TYPE_STRING, G_TYPE_STRING);
signals[SIGNAL_OUTGOING_ERROR] =
g_signal_new ("outgoing-error",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
_modem__marshal_VOID__STRING_STRING_POINTER,
G_TYPE_NONE, 3,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
signals[SIGNAL_STATUS_REPORT] =
g_signal_new ("state-report",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
NULL,
G_TYPE_NONE, 3,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
#if nomore
signals[SIGNAL_DELIVER] =
g_signal_new ("deliver",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
G_TYPE_OBJECT);
#endif
g_type_class_add_private (klass, sizeof (ModemSMSServicePrivate));
modem_oface_register_type(MODEM_TYPE_SMS_HISTORY);
}
/* ---------------------------------------------------------------------- */
/* Signal connection helpers */
#if nomore
gulong
modem_sms_connect_to_connected (ModemSMSService *self,
ModemSMSConnectedHandler *handler,
gpointer data)
{
return g_signal_connect (self, "connected", G_CALLBACK (handler), data);
}
gulong
modem_sms_connect_to_deliver (ModemSMSService *self,
ModemSMSDeliverHandler *handler,
gpointer data)
{
return g_signal_connect (self, "deliver", G_CALLBACK (handler), data);
}
#endif
gulong
modem_sms_connect_to_incoming_message (ModemSMSService *self,
ModemSMSMessageHandler *handler,
gpointer data)
{
return g_signal_connect (self, "incoming-message",
G_CALLBACK (handler), data);
}
gulong
modem_sms_connect_to_immediate_message (ModemSMSService *self,
ModemSMSMessageHandler *handler,
gpointer data)
{
return g_signal_connect (self, "immediate-message",
G_CALLBACK (handler), data);
}
gulong
modem_sms_connect_to_outgoing_complete (ModemSMSService *self,
ModemSMSMessageHandler *handler,
gpointer data)
{
return g_signal_connect (self, "outgoing-complete",
G_CALLBACK (handler), data);
}
gulong
modem_sms_connect_to_outgoing_error (ModemSMSService *self,
ModemSMSMessageHandler *handler,
gpointer data)
{
return g_signal_connect (self, "outgoing-error",
G_CALLBACK (handler), data);
}
gulong
modem_sms_connect_to_status_report (ModemSMSService *self,
ModemSMSMessageHandler *handler,
gpointer data)
{
return g_signal_connect (self, "state-report",
G_CALLBACK (handler), data);
}
/* ------------------------------------------------------------------------- */
/* modem_sms_service interface */
guint64
modem_sms_service_time_connected (ModemSMSService const *self)
{
if (MODEM_IS_SMS_SERVICE (self))
return (guint64)self->priv->connected;
else
return 0;
}
gint64
modem_sms_parse_time (gchar const *s)
{
GTimeVal val;
if (g_time_val_from_iso8601(s, &val))
return val.tv_sec;
else
return 0;
}
/* ------------------------------------------------------------------------- */
static void
on_manager_message_removed (DBusGProxy *proxy,
char const *token,
gpointer user_data)
{
ModemSMSService *self = MODEM_SMS_SERVICE (user_data);
DEBUG ("%s", token);
gpointer obj = g_hash_table_lookup (self->priv->pending_outgoing,
(gpointer)token);
if (!obj)
{
DEBUG("No pending message with token %s, ignore", token);
return;
}
ModemSMSMessage *message = MODEM_SMS_MESSAGE(obj);
GValue srr = G_VALUE_INIT;
g_value_init (&srr, G_TYPE_BOOLEAN);
g_object_get_property(obj, "status_report_requested", &srr);
if (!g_value_get_boolean(&srr))
{
/* No status report requested, remove message now from pending list */
g_hash_table_remove (self->priv->pending_outgoing, (gpointer)token);
g_object_unref (message);
}
}
void
on_manager_message_status_report (DBusGProxy *proxy,
char const *token,
GHashTable *dict,
gpointer user_data)
{
ModemSMSService *self = MODEM_SMS_SERVICE (user_data);
gpointer value;
int delivered = 0;
value = g_hash_table_lookup (dict, "Delivered");
if (value)
{
delivered = g_value_get_boolean(value);
}
DEBUG ("Received status report for message %s, delivery success: %d",
token, delivered);
gpointer obj = g_hash_table_lookup (self->priv->pending_outgoing,
(gpointer)token);
if (!obj)
{
DEBUG("No pending message with token %s, ignore", token);
return;
}
ModemSMSMessage *message = MODEM_SMS_MESSAGE(obj);
GValue srr = G_VALUE_INIT;
g_value_init (&srr, G_TYPE_BOOLEAN);
g_object_get_property(obj, "status_report_requested", &srr);
if (!g_value_get_boolean(&srr))
{
DEBUG("Status report not requested for message with token %s, ignore",
token);
return;
}
GValue destination = G_VALUE_INIT;
const char* dest_string;
g_value_init (&destination, G_TYPE_STRING);
g_object_get_property(obj, "destination", &destination);
dest_string = g_value_get_string(&destination);
g_signal_emit (self, signals[SIGNAL_STATUS_REPORT], 0, dest_string,
token, delivered);
g_hash_table_remove (self->priv->pending_outgoing, (gpointer)token);
g_object_unref (message);
}
static void
dump_message_dict (GHashTable *dict)
{
char *key;
GValue *value;
GHashTableIter iter[1];
g_hash_table_iter_init (iter, dict);
while (g_hash_table_iter_next (iter, (gpointer)&key, (gpointer)&value))
{
char *s = g_strdup_value_contents (value);
DEBUG ("%s = %s", key, s);
g_free (s);
}
}
static void
add_pending_message (char const *message_path,
GHashTable *properties,
gpointer user_data)
{
ModemSMSService *self = MODEM_SMS_SERVICE (user_data);
DEBUG ("%s", message_path);
/*
* Some fields are not known when reading from oFono's TX queue. Use a
* destination address that is valid but unlikely to match an existing
* one, and don't wait for status reports.
*/
gpointer message_object = g_object_new( MODEM_TYPE_SMS_MESSAGE,
"destination", "00000000000000000000",
"message_token", message_path,
"message_service", self,
"status_report_requested", FALSE,
NULL );
g_hash_table_insert (self->priv->pending_outgoing, (gpointer)message_path,
g_object_ref (message_object));
}
#if nomore
static void
on_incoming_message (DBusGProxy *proxy,
char const *message,
GHashTable *dict,
gpointer _self)
{
char const *smsc = "1234567";
char const *type = "text/plain";
char const *originator = "";
char *token;
GError *error = NULL;
GValue *value;
SMSGDeliver *d;
ModemSMSService *self = MODEM_SMS_SERVICE (_self);
DEBUG ("message = \"%s\"", message);
dump_message_dict (dict);
value = g_hash_table_lookup (dict, "Sender");
if (value)
originator = g_value_get_string (value);
token = modem_sms_generate_token ();
d = sms_g_deliver_incoming (message, token, originator,
smsc, type, NULL, &error);
if (!d)
{
modem_message (MODEM_LOG_SMS,
"deserializing SMS-DELIVER \"%s\" failed: "
GERROR_MSG_FMT, token, GERROR_MSG_CODE (error));
g_clear_error (&error);
}
else {
modem_sms_incoming_deliver (self, d);
g_object_unref (d);
}
g_free (token);
}
#endif
/* ------------------------------------------------------------------------- */
gboolean
modem_sms_service_is_error_serious (GError *error)
{
if (error == NULL)
return FALSE;
return TRUE;
}
/* ---------------------------------------------------------------------- */
/* Properties */
ModemRequest *
modem_sms_set_sc_address (ModemSMSService *self,
char const *address,
ModemSMSServiceReply *callback,
gpointer user_data)
{
GValue value[1];
g_value_init (memset (value, 0, sizeof value), G_TYPE_STRING);
g_value_set_string (value, address);
return modem_oface_set_property_req (MODEM_OFACE (self),
"ServiceCenterAddress", value, (void *)callback, user_data);
}
ModemRequest *
modem_sms_set_srr (ModemSMSService *self,
gboolean srr,
ModemSMSServiceReply *callback,
gpointer user_data)
{
GValue value[1];
g_value_init (memset (value, 0, sizeof value), G_TYPE_BOOLEAN);
g_value_set_boolean (value, srr);
return modem_oface_set_property_req (MODEM_OFACE (self),
"UseDeliveryReports", value, (void *)callback, user_data);
}
/* ---------------------------------------------------------------------- */
/* Message deliver */
#if nomore
static void
modem_sms_incoming_deliver (ModemSMSService *self, SMSGDeliver *deliver)
{
ModemSMSServicePrivate *priv = self->priv;
gchar const *content_type = sms_g_deliver_get_content_type (deliver);
gchar const *token = sms_g_deliver_get_message_token (deliver);
if (priv->content_types)
{
int i;
for (i = 0; priv->content_types[i]; i++)
{
if (g_ascii_strcasecmp (content_type, priv->content_types[i]) == 0)
break;
}
if (priv->content_types[i] == NULL)
{
DEBUG ("SMS-DELIVER containing %s is ignored", content_type);
return;
}
}
if (g_hash_table_lookup (priv->received, (gpointer)token))
{
DEBUG ("SMS-DELIVER %s already read, ignoring", token);
return;
}
g_hash_table_insert (priv->received, (gpointer)token, g_object_ref (deliver));
DEBUG ("SMS-DELIVER connected:%ld", priv->connected);
if (priv->connected)
g_signal_emit (self, signals[SIGNAL_DELIVER], 0, deliver);
}
static void
modem_sms_incoming_status_report (ModemSMSService *self,
SMSGStatusReport *sr)
{
ModemSMSServicePrivate *priv = self->priv;
gchar const *token = sms_g_status_report_get_message_token (sr);
if (g_hash_table_lookup (priv->received, (gpointer)token))
{
DEBUG ("SMS-STATUS REPORT %s already read, ignoring", token);
return;
}
g_hash_table_insert (priv->received, (gpointer)token, g_object_ref (sr));
if (priv->connected)
g_signal_emit (self, signals[SIGNAL_STATUS_REPORT], 0, sr);
}
#endif
static void
on_immediate_message (DBusGProxy *proxy,
char const *message,
GHashTable *dict,
gpointer _self)
{
ModemSMSService *self = MODEM_SMS_SERVICE (_self);
ModemSMSServicePrivate *priv = self->priv;
if (!priv->connected)
return;
DEBUG ("immediate = \"%50s\"%s", message, strlen (message) > 50 ? "..." : "");
dump_message_dict (dict);
g_signal_emit (self, signals[SIGNAL_IMMEDIATE_MESSAGE], 0,
message, dict);
}
static void
on_incoming_message (DBusGProxy *proxy,
char const *message,
GHashTable *dict,
gpointer _self)
{
ModemSMSService *self = MODEM_SMS_SERVICE (_self);
ModemSMSServicePrivate *priv = self->priv;
if (!priv->connected)
return;
DEBUG ("incoming = \"%50s\"%s", message, strlen (message) > 50 ? "..." : "");
dump_message_dict (dict);
g_signal_emit (self, signals[SIGNAL_INCOMING_MESSAGE], 0,
message, dict);
}
/* ---------------------------------------------------------------------- */
/* Sending */
static void
reply_to_send_message (DBusGProxy *proxy,
DBusGProxyCall *call,
void *_request)
{
ModemRequest *request = _request;
ModemSMSService *self = modem_request_object (request);
ModemSMSServiceSendReply *callback = modem_request_callback (request);
gpointer user_data = modem_request_user_data (request);
char const *message_path = NULL;
GError *error = NULL;
if (dbus_g_proxy_end_call (proxy, call, &error,
DBUS_TYPE_G_OBJECT_PATH, &message_path,
G_TYPE_INVALID))
{
char const *destination;
destination = modem_request_get_data (request, "destination");
GValue srr = G_VALUE_INIT;
g_value_init (&srr, G_TYPE_BOOLEAN);
g_object_get_property(self, "use-delivery-reports", &srr);
gboolean srr_bool = g_value_get_boolean(&srr);
DEBUG("Status report requested for this message: %d", srr_bool);
gpointer message_object = g_object_new( MODEM_TYPE_SMS_MESSAGE,
"destination", destination,
"message_token", message_path,
"message_service", self,
"status_report_requested", srr_bool,
NULL );
g_hash_table_insert (self->priv->pending_outgoing, (gpointer)message_path,
g_object_ref (message_object));
}
callback (self, request, message_path, error, user_data);
g_clear_error (&error);
}
ModemRequest *
modem_sms_request_send (ModemSMSService *self,
char const *to, char const *message,
ModemSMSServiceSendReply *reply, gpointer user_data)
{
ModemRequest *request;
DEBUG (MODEM_OFACE_SMS ".SendMessage (%s,%s)", to, message);
request = modem_request (self,
modem_oface_dbus_proxy (MODEM_OFACE (self)),
"SendMessage", reply_to_send_message,
G_CALLBACK (reply), user_data,
G_TYPE_STRING, to,
G_TYPE_STRING, message,
G_TYPE_INVALID);
if (request)
modem_request_add_data_full (request, "destination", g_strdup (to), g_free);
return request;
}
static gchar const *
_modem_sms_is_valid_address (gchar const *address)
{
size_t len;
if (address == NULL)
return "NULL";
if (address[0] == '+')
{
address++;
}
len = strspn (address, "0123456789");
if (address[len])
return "invalid character";
if (len == 0)
return "too short";
if (len > 20)
return "too long";
return NULL;
}
/** Return TRUE if @a address is a valid SMS address.
*
* A valid SMS address is a phone number with at most 20 digits either in
* national or in international format (starting with +).
*
* @param address - ISDN address of address
*
* @retval TRUE - address is a valid SMS address
* @retval FALSE - address is NULL, does not contain valid phone number, or it
* is too long.
*/
gboolean
modem_sms_is_valid_address (gchar const *address)
{
return !_modem_sms_is_valid_address (address);
}
/** Validate a SMS address @a address.
*
* A valid SMS address is a phone number with at most 20 digits either
* in national or in international format (starting with +).
*
* @param address - ISDN address of address
* @param error - return value for GError describing the validation error
*
* @retval TRUE - address is a valid SMS address
* @retval FALSE - address is NULL, does not contain valid phone number, or it
* is too long.
*/
gboolean
modem_sms_validate_address (gchar const *address, GError **error)
{
gchar const *reason = _modem_sms_is_valid_address (address);
if (reason)
g_set_error (error, MODEM_SMS_ERRORS, MODEM_SMS_ERROR_INVALID_PARAMETER,
"Invalid SMS address \"%s\": %s", address, reason);
return !reason;
}
void modem_sms_emit_outgoing(ModemSMSService *self, char *address, char *path){
g_signal_emit (self, signals[SIGNAL_OUTGOING_COMPLETE], 0, address, path);
}
void modem_sms_emit_error(ModemSMSService *self, char *address, char *path, GError error){
g_signal_emit (self, signals[SIGNAL_OUTGOING_ERROR], 0, address, path, &error);
}