summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPekka Pessi <Pekka.Pessi@nokia.com>2011-01-27 16:40:38 +0200
committerPekka Pessi <Pekka.Pessi@nokia.com>2011-02-01 19:13:17 +0200
commit75a0a159b88125d2ce3f46da80b2acb3497c77be (patch)
tree53e5c074c2192c05fe979ae2bed2ea2f7ec5cc31
parent5f4bc6b9e3fa75a43552fe3fcd382db64118266f (diff)
Add ring-conference-manager
-rw-r--r--src/Makefile.am1
-rw-r--r--src/ring-conference-channel.c87
-rw-r--r--src/ring-conference-channel.h13
-rw-r--r--src/ring-conference-manager.c741
-rw-r--r--src/ring-conference-manager.h79
-rw-r--r--src/ring-connection.c46
6 files changed, 891 insertions, 76 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 68df9c1..7a65dae 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,6 +52,7 @@ libtpring_la_SOURCES = \
ring-media-channel.h ring-media-channel.c \
ring-call-channel.h ring-call-channel.c \
ring-member-channel.h ring-member-channel.c \
+ ring-conference-manager.h ring-conference-manager.c \
ring-conference-channel.h ring-conference-channel.c \
ring-param-spec.h ring-param-spec.c \
ring-emergency-service.h ring-emergency-service.c \
diff --git a/src/ring-conference-channel.c b/src/ring-conference-channel.c
index 5989653..ca22fba 100644
--- a/src/ring-conference-channel.c
+++ b/src/ring-conference-channel.c
@@ -1765,29 +1765,34 @@ static void reply_to_modem_call_request_conference(ModemCallService *_service,
ModemRequest *request, GError *error, gpointer _channel);
static gboolean
-ring_conference_channel_create_streams(RingConferenceChannel *_self,
- guint handle,
- gboolean audio,
- gboolean video,
- GError **error)
+ring_conference_channel_create_streams (RingConferenceChannel *_self,
+ guint handle,
+ gboolean audio,
+ gboolean video,
+ GError **error)
{
RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(_self);
RingConferenceChannelPrivate *priv = self->priv;
ModemCallService *service;
+ TpBaseConnection *baseconn;
(void)audio; (void)video;
- if (priv->streams_created) {
- DEBUG("Already associated with a conference");
- return TRUE;
- }
+ if (priv->streams_created)
+ {
+ DEBUG("Already associated with a conference");
+ return TRUE;
+ }
+
priv->streams_created = 1;
- if (!ring_connection_validate_initial_members(
- RING_CONNECTION(tp_base_channel_get_connection(TP_BASE_CHANNEL(self))),
- priv->initial_members,
- error))
- return FALSE;
+ baseconn = tp_base_channel_get_connection (TP_BASE_CHANNEL (self));
+
+ if (!ring_connection_validate_initial_members (RING_CONNECTION (baseconn),
+ priv->initial_members, error))
+ {
+ return FALSE;
+ }
service = ring_conference_channel_get_call_service (self);
@@ -1799,32 +1804,23 @@ ring_conference_channel_create_streams(RingConferenceChannel *_self,
}
void
-ring_conference_channel_initial_audio(RingConferenceChannel *self,
- RingMediaManager *manager,
- gpointer channelrequest)
+ring_conference_channel_initial_audio (RingConferenceChannel *self)
{
RingConferenceChannelPrivate *priv = self->priv;
ModemCallService *service;
ModemRequest *request;
- DEBUG("%s(%p, %p, %p) called", __func__, self, manager, channelrequest);
+ DEBUG ("(%p)", self);
priv->streams_created = 1;
service = ring_conference_channel_get_call_service (self),
request = modem_call_request_conference (service,
- reply_to_modem_call_request_conference,
- self);
+ reply_to_modem_call_request_conference,
+ self);
ring_conference_channel_queue_request (self, request);
-
- modem_request_add_qdatas(
- request,
- g_type_qname(RING_TYPE_MEDIA_MANAGER), g_object_ref(manager), g_object_unref,
- /* XXX - GDestroyNotify for channelrequest */
- g_quark_from_static_string("RingChannelRequest"), channelrequest, NULL,
- NULL);
}
static void
@@ -1835,41 +1831,24 @@ reply_to_modem_call_request_conference(ModemCallService *_service,
{
RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(_self);
RingConferenceChannelPrivate *priv = self->priv;
- gpointer channelrequest;
- GError error0[1] = {{
- TP_ERRORS, TP_ERROR_NOT_AVAILABLE, "Conference channel already exists"
- }};
ring_conference_channel_dequeue_request (self, request);
g_assert(priv->conf_created == FALSE);
+
if (error)
{
- DEBUG("Call.CreateMultiparty with channel %s (%p) failed: " GERROR_MSG_FMT,
- self->nick, self, GERROR_MSG_CODE(error));
- }
- else
- {
- priv->conf_created = TRUE;
- DEBUG("Call.CreateMultiparty with channel %s (%p) returned",
- self->nick, self);
- }
+ DEBUG("Call.CreateMultiparty with channel %s (%p) failed: "
+ GERROR_MSG_FMT, self->nick, self, GERROR_MSG_CODE(error));
+ ring_conference_channel_release(_self, 0, 0, error);
+ ring_conference_channel_close_impl(_self, FALSE, FALSE);
+ return;
+ }
- channelrequest = modem_request_steal_data(request, "RingChannelRequest");
- if (channelrequest) {
- RingMediaManager *manager = modem_request_get_data(request, "RingMediaManager");
-
- ring_media_manager_emit_new_channel(manager,
- channelrequest, self, error ? error0 : NULL);
- }
- else if (error) {
- ring_conference_channel_release(_self, 0, 0, error);
- ring_conference_channel_close_impl(_self, FALSE, FALSE);
- }
- else {
- ring_conference_channel_emit_initial(_self);
- }
+ priv->conf_created = TRUE;
+ DEBUG("VoiceCallManager.CreateMultiparty with channel %s (%p) returned",
+ self->nick, self);
}
/* ---------------------------------------------------------------------- */
diff --git a/src/ring-conference-channel.h b/src/ring-conference-channel.h
index 56897ee..dab4c17 100644
--- a/src/ring-conference-channel.h
+++ b/src/ring-conference-channel.h
@@ -88,16 +88,11 @@ void ring_conference_channel_emit_channel_removed(
void ring_conference_channel_emit_initial(RingConferenceChannel *channel);
-/* ---------------------------------------------------------------------- */
-/* "initial-members" */
+gboolean ring_conference_channel_check_initial_members (
+ RingConferenceChannel const *,
+ RingInitialMembers const *);
-gboolean ring_conference_channel_check_initial_members(
- RingConferenceChannel const *channel,
- RingInitialMembers const *initial);
-
-void ring_conference_channel_initial_audio(RingConferenceChannel *self,
- RingMediaManager *manager,
- gpointer request);
+void ring_conference_channel_initial_audio (RingConferenceChannel *self);
G_END_DECLS
diff --git a/src/ring-conference-manager.c b/src/ring-conference-manager.c
new file mode 100644
index 0000000..b4257e0
--- /dev/null
+++ b/src/ring-conference-manager.c
@@ -0,0 +1,741 @@
+/*
+ * ring-conference-manager.c - Manager for conference channels
+ *
+ * Copyright (C) 2007-2011 Nokia Corporation
+ * @author Pekka Pessi <first.surname@nokia.com>
+ * @author Lassi Syrjala <first.surname@nokia.com>
+ * @author Kai Vehmanen <first.surname@nokia.com>
+ *
+ * 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
+ */
+
+/*
+ * Based on telepathy-glib/examples/cm/echo/factory.c with notice:
+ *
+ * """
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ * """
+ */
+
+#include "config.h"
+
+#define DEBUG_FLAG RING_DEBUG_CONNECTION
+#include "ring-debug.h"
+
+#include "ring-conference-manager.h"
+#include "ring-conference-channel.h"
+#include "ring-connection.h"
+#include "ring-param-spec.h"
+#include "ring-util.h"
+
+#include "sms-glib/deliver.h"
+
+#include "ring-extensions/ring-extensions.h"
+
+#include "modem/call.h"
+#include "modem/tones.h"
+
+#include <telepathy-glib/base-connection.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/errors.h>
+#include <telepathy-glib/gtypes.h>
+#include <telepathy-glib/interfaces.h>
+
+#include <dbus/dbus-glib.h>
+
+#include <string.h>
+
+static void manager_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (RingConferenceManager, ring_conference_manager,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, manager_iface_init));
+
+struct _RingConferenceManagerPrivate
+{
+ RingConnection *connection;
+ ModemCallService *call_service;
+
+ GHashTable *channels;
+
+ unsigned dispose_has_run:1, :0;
+
+ struct {
+ gulong status_changed;
+ } signals;
+};
+
+enum
+{
+ PROP_NONE,
+ PROP_CONNECTION,
+ PROP_CALL_SERVICE,
+ N_PROPS
+};
+
+typedef enum
+ {
+ METHOD_COMPATIBLE,
+ METHOD_CREATE,
+ METHOD_ENSURE
+ } RequestotronMethod;
+
+/* ---------------------------------------------------------------------- */
+
+static void conference_manager_emit_new_channel (RingConferenceManager *,
+ gpointer request, gpointer channel, GError *error);
+
+static gboolean conference_requestotron (RingConferenceManager *,
+ gpointer,
+ GHashTable *);
+
+static void set_call_service (RingConferenceManager *, ModemCallService *);
+static void conference_removed (gpointer _channel);
+
+static void on_connection_status_changed (TpBaseConnection *conn,
+ guint status, guint reason,
+ RingConferenceManager *self);
+
+#define METHOD(i, x) (i ## _ ## x)
+
+/* ---------------------------------------------------------------------- */
+/* GObject interface */
+
+static void
+ring_conference_manager_init (RingConferenceManager *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ self, RING_TYPE_CONFERENCE_MANAGER, RingConferenceManagerPrivate);
+
+ self->priv->channels = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, conference_removed);
+}
+
+static void
+ring_conference_manager_constructed (GObject *object)
+{
+ RingConferenceManager *self = RING_CONFERENCE_MANAGER(object);
+ RingConferenceManagerPrivate *priv = self->priv;
+
+ priv->signals.status_changed = g_signal_connect (priv->connection,
+ "status-changed", (GCallback) on_connection_status_changed, self);
+
+ if (G_OBJECT_CLASS (ring_conference_manager_parent_class)->constructed)
+ G_OBJECT_CLASS (ring_conference_manager_parent_class)->constructed (object);
+}
+
+static void
+ring_conference_manager_dispose (GObject *object)
+{
+ RingConferenceManager *self = RING_CONFERENCE_MANAGER (object);
+ RingConferenceManagerPrivate *priv = self->priv;
+
+ if (priv->dispose_has_run)
+ return;
+ priv->dispose_has_run = TRUE;
+
+ ring_signal_disconnect (priv->connection, &priv->signals.status_changed);
+
+ set_call_service (self, NULL);
+
+ ((GObjectClass *) ring_conference_manager_parent_class)->dispose (object);
+}
+
+static void
+ring_conference_manager_finalize (GObject *object)
+{
+ RingConferenceManager *self = RING_CONFERENCE_MANAGER (object);
+ RingConferenceManagerPrivate *priv = self->priv;
+
+ g_hash_table_destroy (priv->channels);
+}
+
+static void
+ring_conference_manager_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ RingConferenceManager *self = RING_CONFERENCE_MANAGER (object);
+ RingConferenceManagerPrivate *priv = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->connection);
+ break;
+ case PROP_CALL_SERVICE:
+ g_value_set_pointer (value, priv->call_service);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ring_conference_manager_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RingConferenceManager *self = RING_CONFERENCE_MANAGER (object);
+ RingConferenceManagerPrivate *priv = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_CONNECTION:
+ /* We don't ref the connection, because it owns a reference to the
+ * factory, and it guarantees that the factory's lifetime is
+ * less than its lifetime */
+ priv->connection = g_value_get_object (value);
+ break;
+ case PROP_CALL_SERVICE:
+ set_call_service (self, g_value_get_pointer (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ring_conference_manager_class_init (RingConferenceManagerClass *klass)
+{
+ GObjectClass *object_class = (GObjectClass *) klass;
+
+ g_type_class_add_private (klass, sizeof (RingConferenceManagerPrivate));
+
+ object_class->constructed = ring_conference_manager_constructed;
+ object_class->dispose = ring_conference_manager_dispose;
+ object_class->finalize = ring_conference_manager_finalize;
+ object_class->get_property = ring_conference_manager_get_property;
+ object_class->set_property = ring_conference_manager_set_property;
+
+ g_object_class_install_property (object_class, PROP_CONNECTION,
+ ring_param_spec_connection ());
+
+ g_object_class_install_property (object_class, PROP_CALL_SERVICE,
+ g_param_spec_pointer ("call-service",
+ "Call Manager Object",
+ "oFono Call Manager",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+/* ---------------------------------------------------------------------- */
+/* RingConferenceManager interface */
+
+RingConferenceChannel *
+ring_conference_manager_lookup (RingConferenceManager *self,
+ char const *object_path)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (object_path != NULL, NULL);
+
+ return g_hash_table_lookup (self->priv->channels, object_path);
+}
+
+static void
+set_call_service (RingConferenceManager *self, ModemCallService *service)
+{
+ RingConferenceManagerPrivate *priv = self->priv;
+
+ if (priv->call_service)
+ {
+ if (priv->call_service)
+ g_object_unref (priv->call_service);
+ priv->call_service = NULL;
+ }
+
+ if (service)
+ {
+ priv->call_service = g_object_ref (MODEM_CALL_SERVICE (service));
+ }
+}
+
+static void
+conference_removed (gpointer _channel)
+{
+ if (!tp_base_channel_is_destroyed (_channel))
+ {
+ /* Ensure "Closed" has been emitted */
+ g_object_run_dispose (_channel);
+ }
+
+ g_object_unref (_channel);
+}
+
+static void
+on_connection_status_changed (TpBaseConnection *conn,
+ guint status,
+ guint reason,
+ RingConferenceManager *self)
+{
+ RingConferenceManagerPrivate *priv = self->priv;
+
+ if (status == TP_CONNECTION_STATUS_DISCONNECTED)
+ {
+ g_hash_table_remove_all (priv->channels);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+/* TpChannelManagerIface interface */
+
+static GHashTable *
+conference_channel_fixed_properties (void)
+{
+ static GHashTable *hash;
+
+ if (hash)
+ return hash;
+
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ char const *key;
+ GValue *value;
+
+ key = TP_IFACE_CHANNEL ".ChannelType";
+ value = tp_g_value_slice_new (G_TYPE_STRING);
+ g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+
+ g_hash_table_insert (hash, (gpointer)key, value);
+
+ return hash;
+}
+
+static char const * const conference_channel_allowed_properties[] =
+ {
+ TP_IFACE_CHANNEL ".InitialChannels",
+ TP_IFACE_CHANNEL ".TargetHandleType",
+ TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio",
+ TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo",
+ NULL
+ };
+
+RingInitialMembers *
+tp_asv_get_initial_members (GHashTable *properties)
+{
+ return tp_asv_get_boxed (properties,
+ TP_IFACE_CHANNEL ".InitialChannels",
+ TP_ARRAY_TYPE_OBJECT_PATH_LIST);
+}
+
+static void
+conference_manager_foreach_channel_class (TpChannelManager *_self,
+ TpChannelManagerChannelClassFunc func,
+ gpointer userdata)
+{
+ RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self);
+
+ /* If we're not connected, conferences aren't supported. */
+ if (self->priv->call_service == NULL)
+ return;
+
+ func (_self,
+ conference_channel_fixed_properties (),
+ conference_channel_allowed_properties,
+ userdata);
+}
+
+static void
+conference_manager_foreach_channel (TpChannelManager *_self,
+ TpExportableChannelFunc func,
+ gpointer user_data)
+{
+ RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self);
+ GHashTableIter i[1];
+ gpointer channel;
+
+ for (g_hash_table_iter_init (i, self->priv->channels);
+ g_hash_table_iter_next (i, NULL, &channel);)
+ func (channel, user_data);
+}
+
+static gboolean
+conference_manager_create_channel (TpChannelManager *_self,
+ gpointer request,
+ GHashTable *properties)
+{
+ RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self);
+
+ if (!ring_properties_satisfy (properties,
+ conference_channel_fixed_properties (),
+ conference_channel_allowed_properties) ||
+ tp_asv_get_initial_members (properties) == NULL)
+ return FALSE;
+
+ return conference_requestotron (self, request, properties);
+}
+
+static gboolean
+conference_manager_ensure_channel (TpChannelManager *_self,
+ gpointer request,
+ GHashTable *properties)
+{
+ RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self);
+
+ if (!ring_properties_satisfy (properties,
+ conference_channel_fixed_properties (),
+ conference_channel_allowed_properties) ||
+ tp_asv_get_initial_members (properties) == NULL)
+ return FALSE;
+
+ return conference_requestotron (self, request, properties);
+}
+
+static void
+manager_iface_init (gpointer ifacep, gpointer data)
+{
+ TpChannelManagerIface *iface = ifacep;
+
+#define IMPLEMENT(x) iface->x = conference_manager_##x
+
+ IMPLEMENT (foreach_channel);
+ IMPLEMENT (foreach_channel_class);
+ IMPLEMENT (create_channel);
+ IMPLEMENT (ensure_channel);
+
+#undef IMPLEMENT
+}
+
+/* ---------------------------------------------------------------------- */
+
+static RingConferenceChannel *
+conference_manager_find_existing (RingConferenceManager const *self,
+ RingInitialMembers *initial)
+{
+ RingConferenceManagerPrivate const *priv = self->priv;
+ GHashTableIter i[1];
+ gpointer existing = NULL;
+
+ g_hash_table_iter_init (i, priv->channels);
+
+ while (g_hash_table_iter_next (i, NULL, &existing))
+ {
+ if (initial->len == 0 ||
+ ring_conference_channel_check_initial_members (existing, initial))
+ return existing;
+ }
+
+ return NULL;
+}
+
+static char *
+conference_manager_new_object_path (RingConferenceManager const *self)
+{
+ RingConferenceManagerPrivate const *priv = self->priv;
+ char const *base_path = TP_BASE_CONNECTION (priv->connection)->object_path;
+
+ static unsigned object_index;
+ static unsigned object_index_init;
+
+ if (G_UNLIKELY (object_index_init == 0))
+ {
+ object_index = (guint)(time(NULL) - (41 * 365 * 24 * 60 * 60)) * 10;
+ object_index_init = 1;
+ }
+
+ /* Find an unique D-Bus object_path */
+ for (;;)
+ {
+ char *path = g_strdup_printf ("%s/conf%u", base_path, ++object_index);
+
+ if (!g_hash_table_lookup (priv->channels, path))
+ return path;
+
+ g_free (path);
+ }
+}
+
+static RingConferenceChannel *
+conference_manager_new_conference (RingConferenceManager *self,
+ RingInitialMembers *initial,
+ gboolean initial_audio)
+{
+ RingConferenceManagerPrivate *priv = self->priv;
+ char *object_path;
+ RingConferenceChannel *channel;
+
+ object_path = conference_manager_new_object_path (self);
+
+ channel = g_object_new (RING_TYPE_CONFERENCE_CHANNEL,
+ "connection", priv->connection,
+ /* KVXXX: "tones", priv->tones, */
+ "object-path", object_path,
+ "initial-channels", initial,
+ "initial-audio", initial_audio,
+ "initiator-handle", tp_base_connection_get_self_handle (
+ TP_BASE_CONNECTION (priv->connection)),
+ "requested", TRUE,
+ NULL);
+
+ g_free (object_path);
+
+ return channel;
+}
+
+static void
+on_conference_channel_closed (GObject *_channel, RingConferenceManager *self)
+{
+ gchar *object_path;
+
+ g_object_get (_channel, "object-path", &object_path, NULL);
+ g_hash_table_remove (self->priv->channels, object_path);
+ tp_channel_manager_emit_channel_closed (self, object_path);
+ g_free (object_path);
+}
+
+static void
+conference_manager_emit_new_channel (RingConferenceManager *self,
+ gpointer request,
+ gpointer _channel,
+ GError *error)
+{
+ DEBUG ("%s (%p, %p, %p, %p) called", __func__,
+ self, request, _channel, error);
+
+ RingConferenceManagerPrivate *priv = RING_CONFERENCE_MANAGER (self)->priv;
+ GSList *requests = request ? g_slist_prepend (NULL, request) : NULL;
+
+ if (error == NULL)
+ {
+ RingConferenceChannel *channel = RING_CONFERENCE_CHANNEL (_channel);
+ char *object_path = NULL;
+
+ g_signal_connect (channel, "closed",
+ G_CALLBACK (on_conference_channel_closed), self);
+
+ g_object_get (channel, "object-path", &object_path, NULL);
+
+ DEBUG ("got new channel %p nick %s type %s",
+ channel, channel->nick, G_OBJECT_TYPE_NAME (channel));
+
+ g_hash_table_insert (priv->channels, object_path, channel);
+
+ tp_channel_manager_emit_new_channel (self,
+ TP_EXPORTABLE_CHANNEL (channel), requests);
+
+ /* Emit Group and StreamedMedia signals */
+ ring_conference_channel_emit_initial (channel);
+ }
+ else
+ {
+ if (_channel)
+ {
+ RingConferenceChannel *channel = RING_CONFERENCE_CHANNEL (_channel);
+ DEBUG ("new channel %p nick %s failed with " GERROR_MSG_FMT,
+ channel, channel->nick, GERROR_MSG_CODE (error));
+ }
+
+ if (request)
+ {
+ tp_channel_manager_emit_request_failed (self,
+ request, error->domain, error->code, error->message);
+ }
+
+ if (_channel)
+ {
+ g_object_unref (_channel);
+ }
+ }
+
+ g_slist_free (requests);
+}
+
+static gboolean
+conference_requestotron (RingConferenceManager *self,
+ gpointer request,
+ GHashTable *properties)
+{
+ RingInitialMembers *initial_members;
+ gboolean initial_audio, initial_video;
+ RingConferenceChannel *channel;
+ GError *error = NULL;
+
+ if (self->priv->call_service == NULL)
+ {
+ tp_channel_manager_emit_request_failed (self, request,
+ TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "Modem does not support conferences");
+ return TRUE;
+ }
+
+ initial_members = tp_asv_get_initial_members (properties);
+ initial_audio = tp_asv_get_initial_audio (properties, TRUE);
+ initial_video = tp_asv_get_initial_video (properties, FALSE);
+
+ if (initial_video)
+ {
+ tp_channel_manager_emit_request_failed (self, request,
+ TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
+ "Video calls are not supported");
+ return TRUE;
+ }
+
+ if (tp_asv_get_uint32 (properties,
+ TP_IFACE_CHANNEL ".TargetHandleType", NULL) != 0)
+ {
+ tp_channel_manager_emit_request_failed (self, request,
+ TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "Invalid TargetHandleType");
+ return TRUE;
+ }
+
+ channel = conference_manager_find_existing (self, initial_members);
+ if (channel)
+ {
+ tp_channel_manager_emit_request_already_satisfied (self, request,
+ TP_EXPORTABLE_CHANNEL (channel));
+ return TRUE;
+ }
+
+ if (!ring_conference_manager_validate_initial_members (self,
+ initial_members, &error))
+ {
+ tp_channel_manager_emit_request_failed (self, request,
+ error->domain, error->code, error->message);
+ g_clear_error (&error);
+ return TRUE;
+ }
+
+ channel = conference_manager_new_conference (self,
+ initial_members, initial_audio);
+
+ if (initial_audio)
+ ring_conference_channel_initial_audio (channel);
+
+ conference_manager_emit_new_channel (self, request, channel, NULL);
+
+ return TRUE;
+}
+
+gboolean
+ring_conference_manager_validate_initial_members (RingConferenceManager *self,
+ RingInitialMembers *initial,
+ GError **error)
+{
+ RingConferenceManagerPrivate *priv = self->priv;
+ RingMemberChannel *ch1, *ch2;
+
+ if (initial == NULL) {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "No initial members");
+ return FALSE;
+ }
+
+ if (initial->len != 2) {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "Expecting exactly two initial members");
+ return FALSE;
+ }
+
+ ch1 = ring_connection_lookup_channel (priv->connection, initial->odata[0]);
+ ch2 = ring_connection_lookup_channel (priv->connection, initial->odata[1]);
+
+ if (ch1 == ch2) {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "Expecting distinct initial members");
+ return FALSE;
+ }
+
+ if (!RING_IS_MEMBER_CHANNEL (ch1) || !RING_IS_MEMBER_CHANNEL (ch2)) {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "Initial member is not a MediaStream channel");
+ return FALSE;
+ }
+
+ if (!ring_member_channel_can_become_member (ch1, error)) {
+ g_prefix_error (error, "First initial: ");
+ return FALSE;
+ }
+
+ if (!ring_member_channel_can_become_member (ch2, error)) {
+ g_prefix_error (error, "Second initial: ");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#ifdef nomore
+static void
+on_modem_call_conference_joined (ModemCallConference *mcc,
+ ModemCall *mc,
+ RingConferenceManager *self)
+{
+ RingConferenceManagerPrivate *priv = RING_CONFERENCE_MANAGER (self)->priv;
+ ModemCall **members;
+ GPtrArray *initial;
+ guint i;
+
+ if (modem_call_get_handler (MODEM_CALL (mcc)))
+ return;
+
+ members = modem_call_service_get_calls (priv->call_service);
+ initial = g_ptr_array_sized_new (MODEM_MAX_CALLS + 1);
+
+ for (i = 0; members[i]; i++)
+ {
+ if (modem_call_is_member (members[i]) &&
+ modem_call_get_handler (members[i]))
+ {
+ RingMemberChannel *member;
+ char *object_path = NULL;
+
+ member = RING_MEMBER_CHANNEL (modem_call_get_handler (members[i]));
+ g_object_get (member, "object-path", &object_path, NULL);
+
+ if (object_path)
+ g_ptr_array_add (initial, object_path);
+ }
+ }
+
+ if (initial->len >= 2)
+ {
+ RingConferenceChannel *channel;
+ char *object_path;
+
+ object_path = ring_conference_manager_new_object_path (self);
+
+ channel = (RingConferenceChannel *)
+ g_object_new (RING_TYPE_CONFERENCE_CHANNEL,
+ "connection", priv->connection,
+ "call-instance", mcc,
+ /* KVXXX: "tones", priv->tones, */
+ "object-path", object_path,
+ "initial-channels", initial,
+ "initial-audio", TRUE,
+ "initiator-handle", tp_base_connection_get_self_handle (
+ TP_BASE_CONNECTION (priv->connection)),
+ "requested", TRUE,
+ NULL);
+
+ g_free (object_path);
+
+ g_assert (channel == modem_call_get_handler (MODEM_CALL (mcc)));
+
+ conference_manager_emit_new_channel (self, NULL, channel, NULL);
+ }
+
+ g_ptr_array_add (initial, NULL);
+ g_strfreev ((char **)g_ptr_array_free (initial, FALSE));
+ g_free (members);
+}
+#endif
diff --git a/src/ring-conference-manager.h b/src/ring-conference-manager.h
new file mode 100644
index 0000000..7eca039
--- /dev/null
+++ b/src/ring-conference-manager.h
@@ -0,0 +1,79 @@
+/*
+ * ring-conference-manager.h - Manager for conference channels
+ *
+ * Copyright (C) 2007-2010 Nokia Corporation
+ * @author Pekka Pessi <first.surname@nokia.com>
+ *
+ * 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
+ */
+
+#ifndef RING_CONFERENCE_MANAGER_H
+#define RING_CONFERENCE_MANAGER_H
+
+#include <telepathy-glib/channel-manager.h>
+#include <ring-util.h>
+#include "modem/call.h"
+
+G_BEGIN_DECLS
+
+typedef struct _RingConferenceManager RingConferenceManager;
+typedef struct _RingConferenceManagerClass RingConferenceManagerClass;
+typedef struct _RingConferenceManagerPrivate RingConferenceManagerPrivate;
+
+G_END_DECLS
+
+#include <ring-conference-channel.h>
+
+G_BEGIN_DECLS
+
+struct _RingConferenceManagerClass {
+ GObjectClass parent_class;
+};
+
+struct _RingConferenceManager {
+ GObject parent;
+ RingConferenceManagerPrivate *priv;
+};
+
+GType ring_conference_manager_get_type (void);
+
+/* TYPE MACROS */
+#define RING_TYPE_CONFERENCE_MANAGER \
+ (ring_conference_manager_get_type())
+#define RING_CONFERENCE_MANAGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ RING_TYPE_CONFERENCE_MANAGER, RingConferenceManager))
+#define RING_CONFERENCE_MANAGER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST ((cls), \
+ RING_TYPE_CONFERENCE_MANAGER, RingConferenceManagerClass))
+#define RING_IS_CONFERENCE_MANAGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ RING_TYPE_CONFERENCE_MANAGER))
+#define RING_IS_CONFERENCE_MANAGER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE ((cls), \
+ RING_TYPE_CONFERENCE_MANAGER))
+#define RING_CONFERENCE_MANAGER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ RING_TYPE_CONFERENCE_MANAGER, RingConferenceManagerClass))
+
+RingConferenceChannel *ring_conference_manager_lookup (RingConferenceManager *,
+ char const *object_path);
+
+gboolean ring_conference_manager_validate_initial_members (
+ RingConferenceManager *, RingInitialMembers *, GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/src/ring-connection.c b/src/ring-connection.c
index 192070a..886dd9c 100644
--- a/src/ring-connection.c
+++ b/src/ring-connection.c
@@ -25,7 +25,9 @@
#include "ring-connection.h"
#include "ring-emergency-service.h"
+
#include "ring-media-manager.h"
+#include "ring-conference-manager.h"
#include "ring-text-manager.h"
#include "ring-param-spec.h"
@@ -75,6 +77,7 @@ struct _RingConnectionPrivate
RingMediaManager *media;
RingTextManager *text;
+ RingConferenceManager *conference;
gchar *modem_path;
Modem *modem;
@@ -371,12 +374,14 @@ ring_connection_get_property(GObject *obj,
break;
case PROP_STORED_MESSAGES:
#if nomore
- g_value_take_boxed(value, ring_text_manager_list_stored_messages(priv->text));
+ g_value_take_boxed(value,
+ ring_text_manager_list_stored_messages (priv->text));
#endif
break;
case PROP_KNOWN_SERVICE_POINTS:
- g_value_take_boxed(value, ring_media_manager_emergency_services(priv->media));
+ g_value_take_boxed(value,
+ ring_media_manager_emergency_services (priv->media));
break;
case PROP_ANON_MANDATORY:
@@ -870,11 +875,17 @@ ring_connection_create_channel_managers(TpBaseConnection *base)
priv->media =
g_object_new(RING_TYPE_MEDIA_MANAGER,
- "connection", self,
- "anon-modes", priv->anon_modes,
- NULL);
+ "connection", self,
+ "anon-modes", priv->anon_modes,
+ NULL);
g_ptr_array_add(channel_managers, priv->media);
+ priv->conference =
+ g_object_new (RING_TYPE_CONFERENCE_MANAGER,
+ "connection", self,
+ NULL);
+ g_ptr_array_add(channel_managers, priv->conference);
+
priv->text =
g_object_new(RING_TYPE_TEXT_MANAGER,
"connection", self,
@@ -932,6 +943,7 @@ ring_connection_modem_interface_added (Modem *modem,
else if (MODEM_IS_CALL_SERVICE (oface))
{
g_object_set (priv->media, "call-service", oface, NULL);
+ g_object_set (priv->conference, "call-service", oface, NULL);
}
else if (MODEM_IS_SMS_SERVICE (oface))
{
@@ -952,18 +964,19 @@ ring_connection_modem_interface_removed (Modem *modem,
if (MODEM_IS_SIM_SERVICE (oface))
{
- DEBUG ("active SIM_SERVICE");
+ DEBUG ("removed SIM_SERVICE");
g_object_set (self, "sim-service", NULL, NULL);
}
else if (MODEM_IS_CALL_SERVICE (oface))
{
- DEBUG ("active CALL_SERVICE");
+ DEBUG ("removed CALL_SERVICE");
g_assert (priv->media);
g_object_set (priv->media, "call-service", NULL, NULL);
+ g_object_set (priv->conference, "call-service", NULL, NULL);
}
else if (MODEM_IS_SMS_SERVICE (oface))
{
- DEBUG ("active SMS_SERVICE");
+ DEBUG ("removed SMS_SERVICE");
g_assert (priv->text);
g_object_set (priv->text, "sms-service", NULL, NULL);
}
@@ -1311,14 +1324,21 @@ ring_connection_inspect_contact(RingConnection const *self,
}
gpointer
-ring_connection_lookup_channel(RingConnection const *self,
- char const *object_path)
+ring_connection_lookup_channel (RingConnection const *self,
+ char const *object_path)
{
+ RingConnectionPrivate *priv = RING_CONNECTION(self)->priv;
gpointer channel;
- channel = ring_media_manager_lookup(self->priv->media, object_path);
- if (channel == NULL)
- channel = ring_text_manager_lookup(self->priv->text, object_path);
+ channel = ring_media_manager_lookup (priv->media, object_path);
+ if (channel)
+ return channel;
+
+ channel = ring_text_manager_lookup (priv->text, object_path);
+ if (channel)
+ return channel;
+
+ channel = ring_conference_manager_lookup (priv->conference, object_path);
return channel;
}