Move provider plugins into a dedicated directory

Since we will introduce another type of plugin for the policy engine
we want to have each plugin type in separate directories.

We also have to adjust:

- plugin search directories
- po file location
- update paths for calls-doc target
This commit is contained in:
Evangelos Ribeiro Tzaras
2022-07-16 22:16:24 +02:00
committed by Guido Günther
parent 8c6ece6a87
commit 86a8f3ae22
60 changed files with 19 additions and 13 deletions

View File

@@ -0,0 +1,412 @@
/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Calls.
*
* Calls 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 3 of the License, or
* (at your option) any later version.
*
* Calls 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.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#define G_LOG_DOMAIN "CallsMMCall"
#include "calls-mm-call.h"
#include "calls-call.h"
#include "calls-message-source.h"
#include "util.h"
#include <libmm-glib/libmm-glib.h>
#include <glib/gi18n.h>
struct _CallsMMCall {
GObject parent_instance;
MMCall *mm_call;
gchar *disconnect_reason;
};
static void calls_mm_call_message_source_interface_init (CallsMessageSourceInterface *iface);
G_DEFINE_TYPE_WITH_CODE (CallsMMCall, calls_mm_call, CALLS_TYPE_CALL,
G_IMPLEMENT_INTERFACE (CALLS_TYPE_MESSAGE_SOURCE,
calls_mm_call_message_source_interface_init))
enum {
PROP_0,
PROP_MM_CALL,
PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];
static void
notify_id_cb (CallsMMCall *self,
const gchar *id)
{
calls_call_set_id (CALLS_CALL (self), id);
}
struct CallsMMCallStateReasonMap {
MMCallStateReason value;
const gchar *desc;
};
static const struct CallsMMCallStateReasonMap STATE_REASON_MAP[] = {
#define row(ENUMVALUE,DESCRIPTION) \
{ MM_CALL_STATE_REASON_##ENUMVALUE, DESCRIPTION }
row (UNKNOWN, N_("Unknown reason")),
row (OUTGOING_STARTED, N_("Outgoing call started")),
row (INCOMING_NEW, N_("New incoming call")),
row (ACCEPTED, N_("Call accepted")),
row (TERMINATED, N_("Call ended")),
row (REFUSED_OR_BUSY, N_("Call disconnected (busy or call refused)")),
row (ERROR, N_("Call disconnected (wrong id or network problem)")),
row (AUDIO_SETUP_FAILED, N_("Call disconnected (error setting up audio channel)")),
row (TRANSFERRED, N_("Call transferred")),
row (DEFLECTED, N_("Call deflected")),
#undef row
{ -1, NULL }
};
static void
set_disconnect_reason (CallsMMCall *self,
MMCallStateReason reason)
{
const struct CallsMMCallStateReasonMap *map_row;
if (self->disconnect_reason)
g_free (self->disconnect_reason);
for (map_row = STATE_REASON_MAP; map_row->desc; ++map_row) {
if (map_row->value == reason) {
self->disconnect_reason =
g_strdup (gettext (map_row->desc));
return;
}
}
self->disconnect_reason =
g_strdup_printf (_("Call disconnected (unknown reason code %i)"),
(int) reason);
g_warning ("%s", self->disconnect_reason);
}
struct CallsMMCallStateMap {
MMCallState mm;
CallsCallState calls;
const gchar *desc;
};
static const struct CallsMMCallStateMap STATE_MAP[] = {
#define row(MMENUM,CALLSENUM) \
{ MM_CALL_STATE_##MMENUM, CALLS_CALL_STATE_##CALLSENUM, #MMENUM } \
row (UNKNOWN, DIALING),
row (DIALING, DIALING),
row (RINGING_OUT, ALERTING),
row (RINGING_IN, INCOMING),
row (ACTIVE, ACTIVE),
row (HELD, HELD),
row (WAITING, INCOMING),
row (TERMINATED, DISCONNECTED),
#undef row
{ -1, -1 }
};
static void
state_changed_cb (CallsMMCall *self,
MMCallState old_state,
MMCallState new_state,
MMCallStateReason reason)
{
const struct CallsMMCallStateMap *state_map_row;
const struct CallsMMCallStateReasonMap *reason_map_row;
const char *state_str = "state unmatched";
const char *reason_str = "reason unmatched";
if (new_state == MM_CALL_STATE_TERMINATED)
set_disconnect_reason (self, reason);
for (state_map_row = STATE_MAP; state_map_row->mm != -1; state_map_row++) {
if (state_map_row->mm == new_state) {
state_str = state_map_row->desc;
break;
}
}
g_assert_cmpint (state_map_row->mm, !=, -1);
for (reason_map_row = STATE_REASON_MAP; reason_map_row->value != -1; reason_map_row++) {
if (reason_map_row->value == reason) {
reason_str = gettext (reason_map_row->desc);
break;
}
}
g_assert_cmpint (reason_map_row->value, !=, -1);
g_debug ("MM call '%s' changed state to `%s': %s",
mm_call_get_path (self->mm_call),
state_str,
reason_str);
calls_call_set_state (CALLS_CALL (self), state_map_row->calls);
}
static const char *
calls_mm_call_get_protocol (CallsCall *self)
{
return "tel";
}
struct CallsMMOperationData {
const gchar *desc;
CallsMMCall *self;
gboolean (*finish_func) (MMCall *, GAsyncResult *, GError **);
};
static void
operation_cb (MMCall *mm_call,
GAsyncResult *res,
struct CallsMMOperationData *data)
{
gboolean ok;
g_autoptr (GError) error = NULL;
ok = data->finish_func (mm_call, res, &error);
if (!ok) {
g_warning ("Error %s MM call '%s': %s (domain: %s [%d])",
data->desc,
mm_call_get_path (mm_call),
error->message,
g_quark_to_string (error->domain),
error->code);
CALLS_ERROR (data->self, error);
}
g_free (data);
}
#define DEFINE_OPERATION(op,name,desc_str) \
static void \
name (CallsCall *call) \
{ \
CallsMMCall *self = CALLS_MM_CALL (call); \
struct CallsMMOperationData *data; \
\
data = g_new0 (struct CallsMMOperationData, 1); \
data->desc = desc_str; \
data->self = self; \
data->finish_func = mm_call_##op##_finish; \
\
mm_call_##op \
(self->mm_call, \
NULL, \
(GAsyncReadyCallback) operation_cb, \
data); \
}
DEFINE_OPERATION (accept, calls_mm_call_answer, "accepting");
DEFINE_OPERATION (hangup, calls_mm_call_hang_up, "hanging up");
DEFINE_OPERATION (start, calls_mm_call_start_call, "starting outgoing call");
static void
calls_mm_call_send_dtmf_tone (CallsCall *call, gchar key)
{
CallsMMCall *self = CALLS_MM_CALL (call);
struct CallsMMOperationData *data;
char key_str[2] = { key, '\0' };
data = g_new0 (struct CallsMMOperationData, 1);
data->desc = "sending DTMF";
data->self = self;
data->finish_func = mm_call_send_dtmf_finish;
mm_call_send_dtmf
(self->mm_call,
key_str,
NULL,
(GAsyncReadyCallback) operation_cb,
data);
}
static void
set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
CallsMMCall *self = CALLS_MM_CALL (object);
switch (property_id) {
case PROP_MM_CALL:
g_set_object (&self->mm_call,
MM_CALL (g_value_get_object (value)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
constructed (GObject *object)
{
CallsMMCall *self = CALLS_MM_CALL (object);
MmGdbusCall *gdbus_call = MM_GDBUS_CALL (self->mm_call);
MMCallState state;
MMCallDirection direction;
const char *number;
const char *path;
gboolean outgoing;
g_signal_connect_swapped (gdbus_call, "notify::number",
G_CALLBACK (notify_id_cb), self);
g_signal_connect_swapped (gdbus_call, "state-changed",
G_CALLBACK (state_changed_cb), self);
number = mm_call_get_number (self->mm_call);
notify_id_cb (self, number);
state = mm_call_get_state (self->mm_call);
state_changed_cb (self,
MM_CALL_STATE_UNKNOWN,
state,
mm_call_get_state_reason (self->mm_call));
direction = mm_call_get_direction (self->mm_call);
outgoing = direction == MM_CALL_DIRECTION_OUTGOING;
/* Start outgoing call */
if (state == MM_CALL_STATE_UNKNOWN &&
outgoing)
calls_mm_call_start_call (CALLS_CALL (self));
path = mm_call_get_path (self->mm_call);
if (direction == MM_CALL_DIRECTION_UNKNOWN)
g_debug ("New call (%s) with '%s'",
path, number);
else
g_debug ("New %s call (%s) %s %s",
outgoing ? "outgoing" : "incoming",
path,
outgoing ? "to" : "from",
number);
G_OBJECT_CLASS (calls_mm_call_parent_class)->constructed (object);
}
static void
dispose (GObject *object)
{
CallsMMCall *self = CALLS_MM_CALL (object);
g_clear_object (&self->mm_call);
G_OBJECT_CLASS (calls_mm_call_parent_class)->dispose (object);
}
static void
finalize (GObject *object)
{
CallsMMCall *self = CALLS_MM_CALL (object);
g_free (self->disconnect_reason);
G_OBJECT_CLASS (calls_mm_call_parent_class)->finalize (object);
}
static void
calls_mm_call_class_init (CallsMMCallClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
CallsCallClass *call_class = CALLS_CALL_CLASS (klass);
object_class->set_property = set_property;
object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->finalize = finalize;
call_class->get_protocol = calls_mm_call_get_protocol;
call_class->answer = calls_mm_call_answer;
call_class->hang_up = calls_mm_call_hang_up;
call_class->send_dtmf_tone = calls_mm_call_send_dtmf_tone;
props[PROP_MM_CALL] = g_param_spec_object ("mm-call",
"MM call",
"A libmm-glib proxy object for the underlying call object",
MM_TYPE_CALL,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_MM_CALL, props[PROP_MM_CALL]);
}
static void
calls_mm_call_message_source_interface_init (CallsMessageSourceInterface *iface)
{
}
static void
calls_mm_call_init (CallsMMCall *self)
{
}
CallsMMCall *
calls_mm_call_new (MMCall *mm_call)
{
gboolean inbound;
g_return_val_if_fail (MM_IS_CALL (mm_call), NULL);
inbound = mm_call_get_direction (mm_call) == MM_CALL_DIRECTION_INCOMING;
return g_object_new (CALLS_TYPE_MM_CALL,
"mm-call", mm_call,
"inbound", inbound,
"call-type", CALLS_CALL_TYPE_CELLULAR,
NULL);
}
const gchar *
calls_mm_call_get_object_path (CallsMMCall *call)
{
return mm_call_get_path (call->mm_call);
}
const gchar *
calls_mm_call_get_disconnect_reason (CallsMMCall *call)
{
return call->disconnect_reason;
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Calls.
*
* Calls 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 3 of the License, or
* (at your option) any later version.
*
* Calls 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.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifndef CALLS_MM_CALL_H__
#define CALLS_MM_CALL_H__
#include <libmm-glib.h>
#include <glib-object.h>
#include "calls-call.h"
G_BEGIN_DECLS
#define CALLS_TYPE_MM_CALL (calls_mm_call_get_type ())
G_DECLARE_FINAL_TYPE (CallsMMCall, calls_mm_call, CALLS, MM_CALL, CallsCall)
CallsMMCall *calls_mm_call_new (MMCall *mm_call);
const gchar *calls_mm_call_get_object_path (CallsMMCall *call);
const gchar *calls_mm_call_get_disconnect_reason (CallsMMCall *call);
G_END_DECLS
#endif /* CALLS_MM_CALL_H__ */

View File

@@ -0,0 +1,947 @@
/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Calls.
*
* Calls 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 3 of the License, or
* (at your option) any later version.
*
* Calls 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.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#define G_LOG_DOMAIN "CallsMMOrigin"
#include "calls-mm-origin.h"
#include "calls-origin.h"
#include "calls-ussd.h"
#include "calls-mm-call.h"
#include "calls-message-source.h"
#include "itu-e212-iso.h"
#include <glib/gi18n.h>
struct _CallsMMOrigin {
GObject parent_instance;
MMObject *mm_obj;
MMModemVoice *voice;
MMModem3gppUssd *ussd;
MMSim *sim;
/* XXX: These should be used only for pointer comparison,
* The content should never be used as it might be
* pointing to a freed location */
char *last_ussd_request;
char *last_ussd_response;
gulong ussd_handle_id;
char *id;
char *name;
GHashTable *calls;
char *country_code;
};
static void calls_mm_origin_message_source_interface_init (CallsOriginInterface *iface);
static void calls_mm_origin_origin_interface_init (CallsOriginInterface *iface);
static void calls_mm_origin_ussd_interface_init (CallsUssdInterface *iface);
G_DEFINE_TYPE_WITH_CODE (CallsMMOrigin, calls_mm_origin, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CALLS_TYPE_MESSAGE_SOURCE,
calls_mm_origin_message_source_interface_init)
G_IMPLEMENT_INTERFACE (CALLS_TYPE_USSD,
calls_mm_origin_ussd_interface_init)
G_IMPLEMENT_INTERFACE (CALLS_TYPE_ORIGIN,
calls_mm_origin_origin_interface_init))
enum {
PROP_0,
PROP_ID,
PROP_NAME,
PROP_CALLS,
PROP_MODEM,
PROP_COUNTRY_CODE,
PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];
static void
ussd_initiate_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
MMModem3gppUssd *ussd = (MMModem3gppUssd *) object;
g_autoptr (GTask) task = user_data;
CallsMMOrigin *self = user_data;
char *response = NULL;
GError *error = NULL;
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
g_assert (MM_IS_MODEM_3GPP_USSD (ussd));
g_assert (CALLS_IS_MM_ORIGIN (self));
response = mm_modem_3gpp_ussd_initiate_finish (ussd, result, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_pointer (task, response, g_free);
}
static void
ussd_reinitiate_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
CallsUssd *ussd = (CallsUssd *) object;
g_autoptr (GTask) task = user_data;
CallsMMOrigin *self = user_data;
GCancellable *cancellable;
GError *error = NULL;
const char *command;
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
g_assert (CALLS_IS_USSD (ussd));
g_assert (CALLS_IS_MM_ORIGIN (self));
calls_ussd_cancel_finish (ussd, result, &error);
cancellable = g_task_get_cancellable (task);
command = g_task_get_task_data (task);
if (error)
g_task_return_error (task, error);
else
mm_modem_3gpp_ussd_initiate (self->ussd, command, cancellable,
ussd_initiate_cb, g_steal_pointer (&task));
}
static void
ussd_respond_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
MMModem3gppUssd *ussd = (MMModem3gppUssd *) object;
CallsMMOrigin *self;
g_autoptr (GTask) task = user_data;
char *response = NULL;
GError *error = NULL;
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
g_assert (CALLS_IS_MM_ORIGIN (self));
g_assert (MM_IS_MODEM_3GPP_USSD (ussd));
response = mm_modem_3gpp_ussd_respond_finish (ussd, result, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_pointer (task, response, g_free);
}
static void
ussd_cancel_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
MMModem3gppUssd *ussd = (MMModem3gppUssd *) object;
CallsMMOrigin *self;
g_autoptr (GTask) task = user_data;
GError *error = NULL;
gboolean response;
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
g_assert (CALLS_IS_MM_ORIGIN (self));
g_assert (MM_IS_MODEM_3GPP_USSD (ussd));
response = mm_modem_3gpp_ussd_cancel_finish (ussd, result, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, response);
}
static CallsUssdState
calls_mm_ussd_get_state (CallsUssd *ussd)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (ussd);
if (!self->ussd)
return CALLS_USSD_STATE_UNKNOWN;
return mm_modem_3gpp_ussd_get_state (self->ussd);
}
static void
calls_mm_ussd_initiate_async (CallsUssd *ussd,
const char *command,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (ussd);
g_autoptr (GTask) task = NULL;
CallsUssdState state;
g_return_if_fail (CALLS_IS_USSD (ussd));
task = g_task_new (self, cancellable, callback, user_data);
if (!self->ussd) {
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"No USSD interface found");
return;
}
if (!command || !*command) {
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
"USSD command empty");
return;
}
state = calls_ussd_get_state (CALLS_USSD (self));
g_task_set_task_data (task, g_strdup (command), g_free);
if (state == CALLS_USSD_STATE_ACTIVE ||
state == CALLS_USSD_STATE_USER_RESPONSE)
calls_ussd_cancel_async (CALLS_USSD (self), cancellable,
ussd_reinitiate_cb, g_steal_pointer (&task));
else
mm_modem_3gpp_ussd_initiate (self->ussd, command, cancellable,
ussd_initiate_cb, g_steal_pointer (&task));
}
static char *
calls_mm_ussd_initiate_finish (CallsUssd *ussd,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (CALLS_IS_USSD (ussd), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
static void
calls_mm_ussd_respond_async (CallsUssd *ussd,
const char *response,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (ussd);
GTask *task;
g_return_if_fail (CALLS_IS_USSD (ussd));
task = g_task_new (self, cancellable, callback, user_data);
mm_modem_3gpp_ussd_respond (self->ussd, response, cancellable,
ussd_respond_cb, task);
}
static char *
calls_mm_ussd_respond_finish (CallsUssd *ussd,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (CALLS_IS_USSD (ussd), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
static void
calls_mm_ussd_cancel_async (CallsUssd *ussd,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (ussd);
GTask *task;
g_return_if_fail (CALLS_IS_USSD (ussd));
task = g_task_new (self, cancellable, callback, user_data);
mm_modem_3gpp_ussd_cancel (self->ussd, cancellable,
ussd_cancel_cb, task);
}
static gboolean
calls_mm_ussd_cancel_finish (CallsUssd *ussd,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (CALLS_IS_USSD (ussd), FALSE);
g_return_val_if_fail (G_IS_TASK (result), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
static void
dial_cb (MMModemVoice *voice,
GAsyncResult *res,
CallsMMOrigin *self)
{
MMCall *call;
g_autoptr (GError) error = NULL;
call = mm_modem_voice_create_call_finish (voice, res, &error);
if (!call) {
g_warning ("Error dialing number on ModemManager modem `%s': %s",
self->name, error->message);
CALLS_ERROR (self, error);
}
}
static void
dial (CallsOrigin *origin, const gchar *number)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (origin);
MMCallProperties *call_props;
g_assert (self->voice != NULL);
call_props = mm_call_properties_new ();
mm_call_properties_set_number (call_props, number);
mm_modem_voice_create_call
(self->voice,
call_props,
NULL,
(GAsyncReadyCallback) dial_cb,
self);
g_object_unref (call_props);
}
static gboolean
supports_protocol (CallsOrigin *origin,
const char *protocol)
{
g_assert (protocol);
g_assert (CALLS_IS_MM_ORIGIN (origin));
return g_strcmp0 (protocol, "tel") == 0;
}
static void
remove_calls (CallsMMOrigin *self, const gchar *reason)
{
GList *paths, *node;
gpointer call;
paths = g_hash_table_get_keys (self->calls);
for (node = paths; node != NULL; node = node->next) {
g_hash_table_steal_extended (self->calls, node->data, NULL, &call);
g_signal_emit_by_name (self, "call-removed",
CALLS_CALL (call), reason);
g_object_unref (call);
}
g_list_free_full (paths, g_free);
}
struct CallsMMOriginDeleteCallData {
CallsMMOrigin *self;
gchar *path;
};
static void
delete_call_cb (MMModemVoice *voice,
GAsyncResult *res,
struct CallsMMOriginDeleteCallData *data)
{
gboolean ok;
g_autoptr (GError) error = NULL;
ok = mm_modem_voice_delete_call_finish (voice, res, &error);
if (!ok) {
g_warning ("Error deleting call `%s' on MMModemVoice `%s': %s",
data->path, data->self->name, error->message);
CALLS_ERROR (data->self, error);
}
g_free (data->path);
g_free (data);
}
static void
delete_call (CallsMMOrigin *self,
CallsMMCall *call)
{
const gchar *path;
struct CallsMMOriginDeleteCallData *data;
path = calls_mm_call_get_object_path (call);
data = g_new0 (struct CallsMMOriginDeleteCallData, 1);
data->self = self;
data->path = g_strdup (path);
mm_modem_voice_delete_call
(self->voice,
path,
NULL,
(GAsyncReadyCallback) delete_call_cb,
data);
}
static void
call_state_changed_cb (CallsCall *call,
GParamSpec *pspec,
CallsMMOrigin *self)
{
g_assert (CALLS_IS_MM_ORIGIN (self));
g_assert (CALLS_IS_MM_CALL (call));
if (calls_call_get_state (call) != CALLS_CALL_STATE_DISCONNECTED)
return;
delete_call (self, CALLS_MM_CALL (call));
}
static void
add_call (CallsMMOrigin *self,
MMCall *mm_call)
{
CallsMMCall *call;
gchar *path;
call = calls_mm_call_new (mm_call);
g_signal_connect (call, "notify::state",
G_CALLBACK (call_state_changed_cb),
self);
path = mm_call_dup_path (mm_call);
g_hash_table_insert (self->calls, path, call);
g_signal_emit_by_name (CALLS_ORIGIN (self), "call-added",
CALLS_CALL (call));
if (mm_call_get_state (mm_call) == MM_CALL_STATE_TERMINATED) {
/* Delete any remnant disconnected call */
delete_call (self, call);
}
g_debug ("Call `%s' added", path);
/* FIXME: Hang up the call, since accepting a secondary call does not currently work.
* CallsMMCall[28822]: WARNING: Error accepting ModemManager call to `+4916XXXXXXXX': GDBus.Error:org.freedesktop.ModemManager1.Error.Core.Failed: This call was not ringing, cannot accept
*/
if (g_hash_table_size (self->calls) > 1)
calls_call_hang_up (CALLS_CALL (call));
}
struct CallsMMOriginCallAddedData {
CallsMMOrigin *self;
gchar *path;
};
static void
call_added_list_calls_cb (MMModemVoice *voice,
GAsyncResult *res,
struct CallsMMOriginCallAddedData *data)
{
g_autoptr (GError) error = NULL;
GList *calls;
calls = mm_modem_voice_list_calls_finish (voice, res, &error);
if (!calls) {
if (error) {
g_warning ("Error listing calls on MMModemVoice `%s'"
" after call-added signal: %s",
data->self->name, error->message);
CALLS_ERROR (data->self, error);
} else {
g_warning ("No calls on MMModemVoice `%s'"
" after call-added signal",
data->self->name);
}
} else {
GList *node;
MMCall *call;
gboolean found = FALSE;
for (node = calls; node; node = node->next) {
call = MM_CALL (node->data);
if (g_strcmp0 (mm_call_get_path (call), data->path) == 0) {
add_call (data->self, call);
found = TRUE;
}
}
if (!found) {
g_warning ("Could not find new call `%s' in call list"
" on MMModemVoice `%s' after call-added signal",
data->path, data->self->name);
}
g_list_free_full (calls, g_object_unref);
}
g_free (data->path);
g_free (data);
}
static void
call_added_cb (MMModemVoice *voice,
gchar *path,
CallsMMOrigin *self)
{
struct CallsMMOriginCallAddedData *data;
if (g_hash_table_contains (self->calls, path)) {
g_warning ("Received call-added signal for"
" existing call object path `%s'", path);
return;
}
data = g_new0 (struct CallsMMOriginCallAddedData, 1);
data->self = self;
data->path = g_strdup (path);
mm_modem_voice_list_calls
(voice,
NULL,
(GAsyncReadyCallback) call_added_list_calls_cb,
data);
}
static void
call_deleted_cb (MMModemVoice *voice,
const gchar *path,
CallsMMOrigin *self)
{
gpointer call;
gpointer key;
GString *reason;
const gchar *mm_reason;
g_debug ("Removing call `%s'", path);
g_hash_table_steal_extended (self->calls, path, &key, &call);
g_free (key);
if (!call) {
g_warning ("Could not find removed call `%s'", path);
return;
}
reason = g_string_new ("Call removed");
mm_reason = calls_mm_call_get_disconnect_reason (CALLS_MM_CALL (call));
if (mm_reason) {
g_string_assign (reason, mm_reason);
}
g_signal_emit_by_name (self, "call-removed", call, reason);
g_object_unref (call);
g_string_free (reason, TRUE);
g_debug ("Removed call `%s'", path);
}
static void
list_calls_cb (MMModemVoice *voice,
GAsyncResult *res,
CallsMMOrigin *self)
{
GList *calls, *node;
g_autoptr (GError) error = NULL;
calls = mm_modem_voice_list_calls_finish (voice, res, &error);
if (!calls) {
if (error) {
g_warning ("Error listing calls on MMModemVoice `%s': %s",
self->name, error->message);
CALLS_ERROR (self, error);
}
return;
}
for (node = calls; node; node = node->next) {
add_call (self, MM_CALL (node->data));
}
g_list_free_full (calls, g_object_unref);
}
static void
set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (object);
switch (property_id) {
case PROP_ID:
self->id = g_value_dup_string (value);
break;
case PROP_MODEM:
g_set_object (&self->mm_obj, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (object);
switch (property_id) {
case PROP_ID:
g_value_set_string (value, self->id);
break;
case PROP_NAME:
g_value_set_string (value, self->name);
break;
case PROP_CALLS:
g_value_set_pointer (value, g_hash_table_get_values (self->calls));
break;
case PROP_COUNTRY_CODE:
g_value_set_string (value, self->country_code);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gchar *
modem_get_name (MMModem *modem)
{
char *name = NULL;
const char * const *numbers = NULL;
numbers = mm_modem_get_own_numbers (modem);
if (numbers && g_strv_length ((char **) numbers) > 0) {
name = g_strdup (numbers[0]);
return name;
}
#define try(prop) \
name = mm_modem_dup_##prop (modem); \
if (name) { \
return name; \
}
try (model);
try (manufacturer);
try (device);
try (primary_port);
try (device_identifier);
try (plugin);
#undef try
return NULL;
}
static void
ussd_properties_changed_cb (CallsMMOrigin *self,
GVariant *properties)
{
const char *response;
GVariant *value;
CallsUssdState state;
g_assert (CALLS_IS_MM_ORIGIN (self));
state = calls_ussd_get_state (CALLS_USSD (self));
value = g_variant_lookup_value (properties, "State", NULL);
if (value)
g_signal_emit_by_name (self, "ussd-state-changed");
g_clear_pointer (&value, g_variant_unref);
/* XXX: We check for user state only because the NetworkRequest
* dbus property change isn't regularly emitted */
if (state == CALLS_USSD_STATE_USER_RESPONSE ||
(value = g_variant_lookup_value (properties, "NetworkRequest", NULL))) {
response = mm_modem_3gpp_ussd_get_network_request (self->ussd);
if (response && *response && response != self->last_ussd_request)
g_signal_emit_by_name (self, "ussd-added", response);
if (response && *response)
self->last_ussd_request = (char *) response;
g_clear_pointer (&value, g_variant_unref);
}
if (state != CALLS_USSD_STATE_USER_RESPONSE &&
(value = g_variant_lookup_value (properties, "NetworkNotification", NULL))) {
response = mm_modem_3gpp_ussd_get_network_notification (self->ussd);
if (response && *response && response != self->last_ussd_response)
g_signal_emit_by_name (self, "ussd-added", response);
if (response && *response)
self->last_ussd_response = (char *) response;
g_clear_pointer (&value, g_variant_unref);
}
}
static void
call_mm_ussd_changed_cb (CallsMMOrigin *self)
{
g_assert (CALLS_IS_MM_ORIGIN (self));
if (self->ussd_handle_id)
g_signal_handler_disconnect (self, self->ussd_handle_id);
self->ussd_handle_id = 0;
g_clear_object (&self->ussd);
self->ussd = mm_object_get_modem_3gpp_ussd (self->mm_obj);
/* XXX: We hook to dbus properties changed because the regular signal emission is inconsistent */
if (self->ussd)
self->ussd_handle_id = g_signal_connect_object (self->ussd, "g-properties-changed",
G_CALLBACK (ussd_properties_changed_cb), self,
G_CONNECT_SWAPPED);
}
static void
get_sim_ready_cb (MMModem *modem,
GAsyncResult *res,
gpointer user_data)
{
const char *code;
CallsMMOrigin *self = CALLS_MM_ORIGIN (user_data);
self->sim = mm_modem_get_sim_finish (modem, res, NULL);
code = get_country_iso_for_mcc (mm_sim_get_imsi (self->sim));
if (code) {
if (g_strcmp0 (self->country_code, code) == 0)
return;
g_debug ("Setting the country code to `%s'", code);
self->country_code = g_strdup (code);
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_COUNTRY_CODE]);
}
}
static void
constructed (GObject *object)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (object);
MmGdbusModemVoice *gdbus_voice;
self->name = modem_get_name (mm_object_get_modem (self->mm_obj));
mm_modem_get_sim (mm_object_get_modem (self->mm_obj),
NULL,
(GAsyncReadyCallback) get_sim_ready_cb,
self);
g_signal_connect_object (self->mm_obj, "notify::modem3gpp-ussd",
G_CALLBACK (call_mm_ussd_changed_cb), self,
G_CONNECT_SWAPPED);
call_mm_ussd_changed_cb (self);
self->voice = mm_object_get_modem_voice (self->mm_obj);
g_assert (self->voice != NULL);
gdbus_voice = MM_GDBUS_MODEM_VOICE (self->voice);
g_signal_connect (gdbus_voice, "call-added",
G_CALLBACK (call_added_cb), self);
g_signal_connect (gdbus_voice, "call-deleted",
G_CALLBACK (call_deleted_cb), self);
mm_modem_voice_list_calls
(self->voice,
NULL,
(GAsyncReadyCallback) list_calls_cb,
self);
G_OBJECT_CLASS (calls_mm_origin_parent_class)->constructed (object);
}
static void
dispose (GObject *object)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (object);
remove_calls (self, NULL);
g_clear_object (&self->mm_obj);
g_clear_object (&self->ussd);
g_clear_object (&self->sim);
g_clear_pointer (&self->country_code, g_free);
G_OBJECT_CLASS (calls_mm_origin_parent_class)->dispose (object);
}
static void
finalize (GObject *object)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (object);
g_hash_table_unref (self->calls);
g_free (self->name);
G_OBJECT_CLASS (calls_mm_origin_parent_class)->finalize (object);
}
static void
calls_mm_origin_class_init (CallsMMOriginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->finalize = finalize;
props[PROP_MODEM] =
g_param_spec_object ("mm-object",
"Modem Object",
"A libmm-glib proxy object for the modem",
MM_TYPE_OBJECT,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_MODEM, props[PROP_MODEM]);
#define IMPLEMENTS(ID, NAME) \
g_object_class_override_property (object_class, ID, NAME); \
props[ID] = g_object_class_find_property(object_class, NAME);
IMPLEMENTS (PROP_ID, "id");
IMPLEMENTS (PROP_NAME, "name");
IMPLEMENTS (PROP_CALLS, "calls");
IMPLEMENTS (PROP_COUNTRY_CODE, "country-code");
#undef IMPLEMENTS
}
static void
calls_mm_origin_message_source_interface_init (CallsOriginInterface *iface)
{
}
static void
calls_mm_origin_ussd_interface_init (CallsUssdInterface *iface)
{
iface->get_state = calls_mm_ussd_get_state;
iface->initiate_async = calls_mm_ussd_initiate_async;
iface->initiate_finish = calls_mm_ussd_initiate_finish;
iface->respond_async = calls_mm_ussd_respond_async;
iface->respond_finish = calls_mm_ussd_respond_finish;
iface->cancel_async = calls_mm_ussd_cancel_async;
iface->cancel_finish = calls_mm_ussd_cancel_finish;
}
static void
calls_mm_origin_origin_interface_init (CallsOriginInterface *iface)
{
iface->dial = dial;
iface->supports_protocol = supports_protocol;
}
static void
calls_mm_origin_init (CallsMMOrigin *self)
{
self->calls = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_object_unref);
}
CallsMMOrigin *
calls_mm_origin_new (MMObject *mm_obj,
const char *id)
{
return g_object_new (CALLS_TYPE_MM_ORIGIN,
"mm-object", mm_obj,
"id", id,
NULL);
}
gboolean
calls_mm_origin_matches (CallsMMOrigin *self,
MMObject *mm_obj)
{
g_return_val_if_fail (CALLS_IS_MM_ORIGIN (self), FALSE);
g_return_val_if_fail (MM_IS_OBJECT (mm_obj), FALSE);
if (self->mm_obj)
return g_strcmp0 (mm_object_get_path (mm_obj),
mm_object_get_path (self->mm_obj)) == 0;
return FALSE;
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Calls.
*
* Calls 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 3 of the License, or
* (at your option) any later version.
*
* Calls 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.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifndef CALLS_MM_ORIGIN_H__
#define CALLS_MM_ORIGIN_H__
#include <glib-object.h>
#include <libmm-glib.h>
G_BEGIN_DECLS
#define CALLS_TYPE_MM_ORIGIN (calls_mm_origin_get_type ())
G_DECLARE_FINAL_TYPE (CallsMMOrigin, calls_mm_origin, CALLS, MM_ORIGIN, GObject);
CallsMMOrigin *calls_mm_origin_new (MMObject *modem,
const char *id);
gboolean calls_mm_origin_matches (CallsMMOrigin *self,
MMObject *modem);
G_END_DECLS
#endif /* CALLS_MM_ORIGIN_H__ */

View File

@@ -0,0 +1,458 @@
/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Calls.
*
* Calls 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 3 of the License, or
* (at your option) any later version.
*
* Calls 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.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#define G_LOG_DOMAIN "CallsMMProvider"
#include "calls-mm-provider.h"
#include "calls-provider.h"
#include "calls-mm-origin.h"
#include "calls-message-source.h"
#include "calls-origin.h"
#include <libmm-glib.h>
#include <libpeas/peas.h>
#include <glib/gi18n.h>
static const char * const supported_protocols[] = {
"tel",
NULL
};
struct _CallsMMProvider {
CallsProvider parent_instance;
/* The status property */
gchar *status;
/** ID for the D-Bus watch */
guint watch_id;
/** ModemManager object proxy */
MMManager *mm;
/* A list of CallsOrigins */
GListStore *origins;
};
static void calls_mm_provider_message_source_interface_init (CallsMessageSourceInterface *iface);
G_DEFINE_DYNAMIC_TYPE_EXTENDED
(CallsMMProvider, calls_mm_provider, CALLS_TYPE_PROVIDER, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC (CALLS_TYPE_MESSAGE_SOURCE,
calls_mm_provider_message_source_interface_init))
static void
set_status (CallsMMProvider *self,
const gchar *new_status)
{
if (strcmp (self->status, new_status) == 0)
return;
g_free (self->status);
self->status = g_strdup (new_status);
g_object_notify (G_OBJECT (self), "status");
}
static void
update_status (CallsMMProvider *self)
{
const gchar *s;
if (!self->mm) {
s = _("ModemManager unavailable");
} else if (g_list_model_get_n_items (G_LIST_MODEL (self->origins)) == 0) {
s = _("No voice-capable modem available");
} else {
s = _("Normal");
}
set_status (self, s);
}
static gboolean
mm_provider_contains (CallsMMProvider *self,
MMObject *mm_obj)
{
GListModel *model;
guint n_items;
g_assert (CALLS_IS_MM_PROVIDER (self));
g_assert (MM_OBJECT (mm_obj));
model = G_LIST_MODEL (self->origins);
n_items = g_list_model_get_n_items (model);
for (guint i = 0; i < n_items; i++) {
g_autoptr (CallsMMOrigin) origin = NULL;
origin = g_list_model_get_item (model, i);
if (calls_mm_origin_matches (origin, mm_obj))
return TRUE;
}
return FALSE;
}
static void
add_origin (CallsMMProvider *self,
GDBusObject *object)
{
MMObject *mm_obj;
g_autoptr (CallsMMOrigin) origin = NULL;
g_autoptr (MMModem3gpp) modem_3gpp = NULL;
const gchar *path;
g_autofree char *imei = NULL;
mm_obj = MM_OBJECT (object);
path = g_dbus_object_get_object_path (object);
if (mm_provider_contains (self, mm_obj)) {
g_warning ("New voice interface on existing"
" origin with path `%s'", path);
return;
}
g_debug ("Adding new voice-capable modem `%s'",
path);
g_assert (MM_IS_OBJECT (object));
modem_3gpp = mm_object_get_modem_3gpp (mm_obj);
origin = calls_mm_origin_new (mm_obj,
mm_modem_3gpp_get_imei (modem_3gpp));
g_list_store_append (self->origins, origin);
update_status (self);
}
static void
interface_added_cb (CallsMMProvider *self,
GDBusObject *object,
GDBusInterface *interface)
{
GDBusInterfaceInfo *info;
info = g_dbus_interface_get_info (interface);
g_debug ("ModemManager interface `%s' found on object `%s'",
info->name,
g_dbus_object_get_object_path (object));
if (g_strcmp0 (info->name,
"org.freedesktop.ModemManager1.Modem.Voice") == 0) {
add_origin (self, object);
}
}
static void
remove_modem_object (CallsMMProvider *self,
const gchar *path,
GDBusObject *object)
{
GListModel *model;
guint n_items;
model = G_LIST_MODEL (self->origins);
n_items = g_list_model_get_n_items (model);
for (guint i = 0; i < n_items; i++) {
g_autoptr (CallsMMOrigin) origin = NULL;
origin = g_list_model_get_item (model, i);
if (calls_mm_origin_matches (origin, MM_OBJECT (object))) {
g_list_store_remove (self->origins, i);
update_status (self);
break;
}
}
}
static void
interface_removed_cb (CallsMMProvider *self,
GDBusObject *object,
GDBusInterface *interface)
{
const gchar *path;
GDBusInterfaceInfo *info;
path = g_dbus_object_get_object_path (object);
info = g_dbus_interface_get_info (interface);
g_debug ("ModemManager interface `%s' removed on object `%s'",
info->name, path);
if (g_strcmp0 (info->name,
"org.freedesktop.ModemManager1.Modem.Voice") == 0) {
remove_modem_object (self, path, object);
}
}
static void
add_mm_object (CallsMMProvider *self, GDBusObject *object)
{
GList *ifaces, *node;
ifaces = g_dbus_object_get_interfaces (object);
for (node = ifaces; node; node = node->next) {
interface_added_cb (self, object,
G_DBUS_INTERFACE (node->data));
}
g_list_free_full (ifaces, g_object_unref);
}
static void
add_mm_objects (CallsMMProvider *self)
{
GList *objects, *node;
objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->mm));
for (node = objects; node; node = node->next) {
add_mm_object (self, G_DBUS_OBJECT (node->data));
}
g_list_free_full (objects, g_object_unref);
}
static void
object_added_cb (CallsMMProvider *self,
GDBusObject *object)
{
g_debug ("ModemManager object `%s' added",
g_dbus_object_get_object_path (object));
add_mm_object (self, object);
}
static void
object_removed_cb (CallsMMProvider *self,
GDBusObject *object)
{
const gchar *path;
path = g_dbus_object_get_object_path (object);
g_debug ("ModemManager object `%s' removed", path);
remove_modem_object (self, path, object);
}
static void
mm_manager_new_cb (GDBusConnection *connection,
GAsyncResult *res,
CallsMMProvider *self)
{
GError *error = NULL;
self->mm = mm_manager_new_finish (res, &error);
if (!self->mm) {
g_error ("Error creating ModemManager Manager: %s",
error->message);
g_assert_not_reached ();
}
g_signal_connect_swapped (G_DBUS_OBJECT_MANAGER (self->mm),
"interface-added",
G_CALLBACK (interface_added_cb), self);
g_signal_connect_swapped (G_DBUS_OBJECT_MANAGER (self->mm),
"interface-removed",
G_CALLBACK (interface_removed_cb), self);
g_signal_connect_swapped (G_DBUS_OBJECT_MANAGER (self->mm),
"object-added",
G_CALLBACK (object_added_cb), self);
g_signal_connect_swapped (G_DBUS_OBJECT_MANAGER (self->mm),
"object-removed",
G_CALLBACK (object_removed_cb), self);
update_status (self);
add_mm_objects (self);
}
static void
mm_appeared_cb (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
CallsMMProvider *self)
{
g_debug ("ModemManager appeared on D-Bus");
mm_manager_new (connection,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
NULL,
(GAsyncReadyCallback) mm_manager_new_cb,
self);
}
static void
mm_vanished_cb (GDBusConnection *connection,
const gchar *name,
CallsMMProvider *self)
{
g_debug ("ModemManager vanished from D-Bus");
g_list_store_remove_all (self->origins);
update_status (self);
}
static const char *
calls_mm_provider_get_name (CallsProvider *provider)
{
return "ModemManager";
}
static const char *
calls_mm_provider_get_status (CallsProvider *provider)
{
CallsMMProvider *self = CALLS_MM_PROVIDER (provider);
return self->status;
}
static GListModel *
calls_mm_provider_get_origins (CallsProvider *provider)
{
CallsMMProvider *self = CALLS_MM_PROVIDER (provider);
return G_LIST_MODEL (self->origins);
}
static const char *const *
calls_mm_provider_get_protocols (CallsProvider *provider)
{
return supported_protocols;
}
static gboolean
calls_mm_provider_is_modem (CallsProvider *provider)
{
return TRUE;
}
static void
constructed (GObject *object)
{
CallsMMProvider *self = CALLS_MM_PROVIDER (object);
self->watch_id =
g_bus_watch_name (G_BUS_TYPE_SYSTEM,
MM_DBUS_SERVICE,
G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
(GBusNameAppearedCallback) mm_appeared_cb,
(GBusNameVanishedCallback) mm_vanished_cb,
self, NULL);
g_debug ("Watching for ModemManager");
G_OBJECT_CLASS (calls_mm_provider_parent_class)->constructed (object);
}
static void
dispose (GObject *object)
{
CallsMMProvider *self = CALLS_MM_PROVIDER (object);
if (self->watch_id) {
g_bus_unwatch_name (self->watch_id);
self->watch_id = 0;
}
g_list_store_remove_all (self->origins);
G_OBJECT_CLASS (calls_mm_provider_parent_class)->dispose (object);
}
static void
finalize (GObject *object)
{
CallsMMProvider *self = CALLS_MM_PROVIDER (object);
g_object_unref (self->origins);
g_free (self->status);
G_OBJECT_CLASS (calls_mm_provider_parent_class)->finalize (object);
}
static void
calls_mm_provider_class_init (CallsMMProviderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
CallsProviderClass *provider_class = CALLS_PROVIDER_CLASS (klass);
object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->finalize = finalize;
provider_class->get_name = calls_mm_provider_get_name;
provider_class->get_status = calls_mm_provider_get_status;
provider_class->get_origins = calls_mm_provider_get_origins;
provider_class->get_protocols = calls_mm_provider_get_protocols;
provider_class->is_modem = calls_mm_provider_is_modem;
}
static void
calls_mm_provider_class_finalize (CallsMMProviderClass *klass)
{
}
static void
calls_mm_provider_message_source_interface_init (CallsMessageSourceInterface *iface)
{
}
static void
calls_mm_provider_init (CallsMMProvider *self)
{
self->status = g_strdup (_("Initialized"));
self->origins = g_list_store_new (CALLS_TYPE_ORIGIN);
}
G_MODULE_EXPORT void
peas_register_types (PeasObjectModule *module)
{
calls_mm_provider_register_type (G_TYPE_MODULE (module));
peas_object_module_register_extension_type (module,
CALLS_TYPE_PROVIDER,
CALLS_TYPE_MM_PROVIDER);
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Calls.
*
* Calls 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 3 of the License, or
* (at your option) any later version.
*
* Calls 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.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifndef CALLS_MM_PROVIDER_H__
#define CALLS_MM_PROVIDER_H__
#include "calls-provider.h"
#include <glib-object.h>
#include <gio/gio.h>
#include <libpeas/peas.h>
G_BEGIN_DECLS
#define CALLS_TYPE_MM_PROVIDER (calls_mm_provider_get_type ())
G_DECLARE_FINAL_TYPE (CallsMMProvider, calls_mm_provider, CALLS, MM_PROVIDER, CallsProvider)
void peas_register_types (PeasObjectModule *module);
G_END_DECLS
#endif /* CALLS_MM_PROVIDER_H__ */

View File

@@ -0,0 +1,294 @@
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* itu-e212-iso.h
*
* Copyright 2020 Purism SPC
*
* Mapping from E.212 MCC to Countries in ISO 3166-1 alpha-2 format
*
* Author(s):
* Mohammed Sadiq <sadiq@sadiqpk.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <glib.h>
struct mcc_list {
guint mcc;
char code[3];
};
/*
* Extracted from:
* https://www.itu.int/dms_pub/itu-t/opb/sp/T-SP-E.212B-2018-PDF-E.pdf
*/
struct mcc_list mcc_list[] = {
{202, "GR"},
{204, "NL"},
{206, "BE"},
{208, "FR"},
{212, "MC"},
{213, "AD"},
{214, "ES"},
{216, "HU"},
{218, "BA"},
{219, "HR"},
{220, "RS"},
{221, "XK"},
{222, "IT"},
{225, "VA"},
{226, "RO"},
{228, "CH"},
{230, "CZ"},
{231, "SK"},
{232, "AT"},
{234, "GB"},
{235, "GB"},
{238, "DK"},
{240, "SE"},
{242, "NO"},
{244, "FI"},
{246, "LT"},
{247, "LV"},
{248, "EE"},
{250, "RU"},
{255, "UA"},
{257, "BY"},
{259, "MD"},
{260, "PL"},
{262, "DE"},
{266, "GI"},
{268, "PT"},
{270, "LU"},
{272, "IE"},
{274, "IS"},
{276, "AL"},
{278, "MT"},
{280, "CY"},
{282, "GE"},
{283, "AM"},
{284, "BG"},
{286, "TR"},
{288, "FO"},
{290, "GL"},
{292, "SM"},
{293, "SI"},
{294, "MK"},
{295, "LI"},
{297, "ME"},
{302, "CA"},
{308, "PM"},
{310, "US"},
{311, "US"},
{312, "US"},
{313, "US"},
{314, "US"},
{315, "US"},
{316, "US"},
{330, "PR"},
{332, "VI"},
{334, "MX"},
{338, "JM"},
/* Guadeloupe and Martinique are part of France */
{340, "GP"},
{340, "MQ"},
{342, "BB"},
{344, "AG"},
{346, "KY"},
{348, "VG"},
{350, "BM"},
{352, "GD"},
{354, "MS"},
{356, "KN"},
{358, "LC"},
{360, "VC"},
{362, "CW"},
{363, "AW"},
{364, "BS"},
{365, "AI"},
{366, "DM"},
{368, "CU"},
{370, "DO"},
{372, "HT"},
{374, "TT"},
{376, "TC"},
{400, "AZ"},
{401, "KZ"},
{402, "BT"},
{404, "IN"},
{405, "IN"},
{406, "IN"},
{410, "PK"},
{412, "AF"},
{413, "LK"},
{414, "MM"},
{415, "LB"},
{416, "JO"},
{417, "SY"},
{418, "IQ"},
{419, "KW"},
{420, "SA"},
{421, "YE"},
{422, "OM"},
{424, "AE"},
{425, "IL"},
{426, "BH"},
{427, "QA"},
{428, "MN"},
{429, "NP"},
{430, "AE"},
{431, "AE"},
{432, "IR"},
{434, "UZ"},
{436, "TJ"},
{437, "KG"},
{438, "TM"},
{440, "JP"},
{441, "JP"},
{450, "KP"},
{452, "VN"},
{454, "HK"},
{455, "MO"},
{456, "KH"},
{457, "LA"},
{460, "CN"},
{461, "CN"},
{466, "TW"},
{467, "KR"},
{470, "BD"},
{472, "MV"},
{502, "MY"},
{505, "AU"},
{510, "ID"},
{514, "TL"},
{515, "PH"},
{520, "TH"},
{525, "SG"},
{528, "BN"},
{530, "NZ"},
{536, "NR"},
{537, "PG"},
{539, "TO"},
{540, "SB"},
{541, "VU"},
{542, "FJ"},
{543, "WF"},
{544, "AS"},
{545, "KI"},
{546, "NC"},
{547, "PF"},
{548, "CK"},
{549, "AS"},
{550, "FM"},
{551, "MH"},
{552, "PW"},
{553, "TV"},
{554, "TK"},
{555, "NU"},
{602, "EG"},
{603, "DZ"},
{604, "MA"},
{605, "TN"},
{606, "LY"},
{607, "GM"},
{608, "SN"},
{609, "MR"},
{610, "ML"},
{611, "GN"},
{612, "CI"},
{613, "BF"},
{614, "NE"},
{615, "TG"},
{616, "BJ"},
{617, "MU"},
{618, "LR"},
{619, "SL"},
{620, "GH"},
{621, "NG"},
{622, "TD"},
{623, "CF"},
{624, "CM"},
{625, "CV"},
{626, "ST"},
{627, "GQ"},
{628, "GA"},
{629, "CG"},
{630, "CD"},
{631, "AO"},
{632, "GW"},
{633, "SC"},
{634, "SD"},
{635, "RW"},
{636, "ET"},
{637, "SO"},
{638, "DJ"},
{639, "KE"},
{640, "TZ"},
{641, "UG"},
{642, "BI"},
{643, "MZ"},
{645, "ZM"},
{646, "MG"},
{647, "RE"},
{648, "ZW"},
{649, "NA"},
{650, "MW"},
{651, "LS"},
{652, "BW"},
{653, "SZ"},
{654, "KM"},
{655, "ZA"},
{657, "ER"},
{658, "SH"},
{659, "SS"},
{702, "BZ"},
{704, "GT"},
{706, "SV"},
{708, "HN"},
{710, "NI"},
{712, "CR"},
{714, "PA"},
{716, "PE"},
{722, "AR"},
{724, "BR"},
{730, "CL"},
{732, "CO"},
{734, "VE"},
{736, "BO"},
{738, "GY"},
{740, "EC"},
{742, "GF"},
{744, "PY"},
{746, "SR"},
{748, "UY"},
{750, "FK"},
};
/*
* @mcc_str should have MCC as prefix,
* It doesn't matter if any thing else is followed.
* So it's okay to pass an IMSI.
*/
static inline const char *
get_country_iso_for_mcc (const char *mcc_str)
{
g_autofree char *str = NULL;
guint64 mcc;
if (!mcc_str || strlen (mcc_str) < 3)
return NULL;
str = g_strndup (mcc_str, 3);
mcc = g_ascii_strtoull (str, NULL, 10);
for (guint i = 0; i < G_N_ELEMENTS (mcc_list); i++)
if (mcc_list[i].mcc == mcc)
return mcc_list[i].code;
g_warning ("invalid MCC code: %" G_GUINT64_FORMAT, mcc);
return NULL;
}

View File

@@ -0,0 +1,57 @@
#
# Copyright (C) 2018 Purism SPC
#
# This file is part of Calls.
#
# Calls 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 3 of the License, or
# (at your option) any later version.
#
# Calls 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.
#
# You should have received a copy of the GNU General Public License
# along with Calls. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Bob Ham <bob.ham@puri.sm>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
mm_install_dir = join_paths(full_calls_plugin_libdir, 'mm')
mm_plugin = configure_file(
input: 'mm.plugin.in',
output: 'mm.plugin',
configuration: config_data,
install_dir: mm_install_dir
)
mm_deps = [
dependency('gobject-2.0'),
dependency('gtk+-3.0'),
dependency('ModemManager'),
dependency('mm-glib', version: '>= 1.12.0'),
dependency('libpeas-1.0'),
]
mm_sources = files(
[
'calls-mm-call.c', 'calls-mm-call.h',
'calls-mm-origin.c', 'calls-mm-origin.h',
'calls-mm-provider.c', 'calls-mm-provider.h'
]
)
calls_mm = shared_module(
'mm',
mm_sources,
dependencies: mm_deps,
include_directories: src_include,
link_with: libcalls,
install: true,
install_dir: mm_install_dir
)

View File

@@ -0,0 +1,7 @@
[Plugin]
Module=mm
Name=ModemManager
Description=ModemManager calls provider
Authors=Bob Ham <rah@settrans.net>
Copyright=Copyright (C) 2018 Purism SPC
Website=@PACKAGE_URL_RAW@