~ubuntu-desktop/dee/ubuntu

« back to all changes in this revision

Viewing changes to src/dee-client.c

  • Committer: Didier Roche
  • Date: 2012-01-12 16:14:54 UTC
  • mfrom: (130.1.206 MASTER)
  • mto: (250.1.1 ubuntu)
  • mto: This revision was merged to the branch mainline in revision 218.
  • Revision ID: didier.roche@canonical.com-20120112161454-31xl6hgcfngm6abi
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2011 Canonical, Ltd.
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU Lesser General Public License
 
6
 * version 3.0 as published by the Free Software Foundation.
 
7
 *
 
8
 * This library is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU Lesser General Public License version 3.0 for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU Lesser General Public
 
14
 * License along with this library. If not, see
 
15
 * <http://www.gnu.org/licenses/>.
 
16
 *
 
17
 * Authored by: Michal Hruby <michal.hruby@canonical.com>
 
18
 *
 
19
 */
 
20
/**
 
21
 * SECTION:dee-client
 
22
 * @short_description: Creates a client object you can use to connect
 
23
 *                     to a #DeeServer.
 
24
 * @include: dee.h
 
25
 *
 
26
 * #DeeClient is the endpoint for connecting to #DeeServer.
 
27
 */
 
28
 
 
29
#ifdef HAVE_CONFIG_H
 
30
#include <config.h>
 
31
#endif
 
32
 
 
33
#include <gio/gio.h>
 
34
 
 
35
#include "dee-client.h"
 
36
#include "dee-marshal.h"
 
37
#include "trace-log.h"
 
38
 
 
39
G_DEFINE_TYPE (DeeClient, dee_client, DEE_TYPE_PEER)
 
40
 
 
41
#define GET_PRIVATE(o) \
 
42
      (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEE_TYPE_CLIENT, DeeClientPrivate))
 
43
 
 
44
/**
 
45
 * DeeClientPrivate:
 
46
 *
 
47
 * Ignore this structure.
 
48
 **/
 
49
struct _DeeClientPrivate
 
50
{
 
51
  GDBusConnection *connection;
 
52
  GCancellable    *cancellable;
 
53
  gchar           *bus_address;
 
54
 
 
55
  guint            peer_found_timer_id;
 
56
  gulong           closed_signal_handler_id;
 
57
};
 
58
 
 
59
/* Globals */
 
60
enum
 
61
{
 
62
  PROP_0,
 
63
  PROP_BUS_ADDRESS
 
64
};
 
65
 
 
66
enum
 
67
{
 
68
  LAST_SIGNAL
 
69
};
 
70
 
 
71
//static guint32 _server_signals[LAST_SIGNAL] = { 0 };
 
72
 
 
73
/* Forwards */
 
74
static gboolean     dee_client_is_swarm_leader     (DeePeer *peer);
 
75
 
 
76
static const gchar* dee_client_get_swarm_leader    (DeePeer *peer);
 
77
 
 
78
static GSList*      dee_client_get_connections     (DeePeer *peer);
 
79
 
 
80
static gchar**      dee_client_list_peers          (DeePeer *peer);
 
81
 
 
82
static void         connecting_finished            (GObject *object,
 
83
                                                    GAsyncResult *res,
 
84
                                                    gpointer user_data);
 
85
 
 
86
static void         connection_closed              (GDBusConnection *connection,
 
87
                                                    gboolean         remote_peer_vanished,
 
88
                                                    GError          *error,
 
89
                                                    DeeClient       *client);
 
90
 
 
91
/* GObject methods */
 
92
static void
 
93
dee_client_get_property (GObject *object, guint property_id,
 
94
                         GValue *value, GParamSpec *pspec)
 
95
{
 
96
  DeeClientPrivate *priv;
 
97
 
 
98
  priv = DEE_CLIENT (object)->priv;
 
99
 
 
100
  switch (property_id)
 
101
    {
 
102
      case PROP_BUS_ADDRESS:
 
103
        g_value_set_string (value, priv->bus_address);
 
104
        break;
 
105
      default:
 
106
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
107
    }
 
108
}
 
109
 
 
110
static void
 
111
dee_client_set_property (GObject *object, guint property_id,
 
112
                              const GValue *value, GParamSpec *pspec)
 
113
{
 
114
  DeeClientPrivate *priv;
 
115
 
 
116
  priv = DEE_CLIENT (object)->priv;
 
117
 
 
118
  switch (property_id)
 
119
    {
 
120
      case PROP_BUS_ADDRESS:
 
121
        if (priv->bus_address) g_free (priv->bus_address);
 
122
        priv->bus_address = g_value_dup_string (value);
 
123
        break;
 
124
      default:
 
125
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
126
    }
 
127
}
 
128
 
 
129
static void
 
130
dee_client_constructed (GObject *self)
 
131
{
 
132
  DeeClientPrivate *priv;
 
133
  const gchar      *swarm_name;
 
134
  GDBusConnectionFlags flags;
 
135
 
 
136
  priv = DEE_CLIENT (self)->priv;
 
137
 
 
138
  /* we should chain up the constructed method here, but peer does things we
 
139
   * don't want to, so not chaining up... */
 
140
 
 
141
  swarm_name = dee_peer_get_swarm_name (DEE_PEER (self));
 
142
  if (swarm_name == NULL)
 
143
    {
 
144
      g_critical ("DeeClient created without a swarm name. You must specify "
 
145
                  "a non-NULL swarm name");
 
146
      return;
 
147
    }
 
148
 
 
149
  if (!priv->bus_address)
 
150
    {
 
151
      const gchar *username = g_get_user_name ();
 
152
      priv->bus_address = g_strdup_printf ("unix:abstract=%s-%s",
 
153
                                           username, swarm_name);
 
154
    }
 
155
 
 
156
  flags = G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT;
 
157
  priv->cancellable = g_cancellable_new ();
 
158
  g_dbus_connection_new_for_address (priv->bus_address,
 
159
                                     flags,
 
160
                                     NULL, // AuthObserver
 
161
                                     priv->cancellable,
 
162
                                     connecting_finished,
 
163
                                     self);
 
164
}
 
165
 
 
166
static void
 
167
dee_client_finalize (GObject *object)
 
168
{
 
169
  DeeClientPrivate *priv;
 
170
 
 
171
  priv = DEE_CLIENT (object)->priv;
 
172
 
 
173
  if (priv->cancellable)
 
174
    {
 
175
      g_cancellable_cancel (priv->cancellable);
 
176
      g_object_unref (priv->cancellable);
 
177
    }
 
178
 
 
179
  if (priv->closed_signal_handler_id)
 
180
    {
 
181
      g_signal_handler_disconnect (priv->connection,
 
182
                                   priv->closed_signal_handler_id);
 
183
      priv->closed_signal_handler_id = 0;
 
184
    }
 
185
 
 
186
  if (priv->connection)
 
187
    {
 
188
      // FIXME: close the connection?
 
189
      g_object_unref (priv->connection);
 
190
    }
 
191
 
 
192
  if (priv->peer_found_timer_id)
 
193
    {
 
194
      g_source_remove (priv->peer_found_timer_id);
 
195
      priv->peer_found_timer_id = 0;
 
196
    }
 
197
 
 
198
  if (priv->bus_address)
 
199
    {
 
200
      g_free (priv->bus_address);
 
201
    }
 
202
 
 
203
  G_OBJECT_CLASS (dee_client_parent_class)->finalize (object);
 
204
}
 
205
 
 
206
static void
 
207
dee_client_class_init (DeeClientClass *klass)
 
208
{
 
209
  GParamSpec   *pspec;
 
210
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
211
  DeePeerClass *peer_class = DEE_PEER_CLASS (klass);
 
212
 
 
213
  g_type_class_add_private (klass, sizeof (DeeClientPrivate));
 
214
 
 
215
  object_class->constructed    = dee_client_constructed;
 
216
  object_class->get_property   = dee_client_get_property;
 
217
  object_class->set_property   = dee_client_set_property;
 
218
  object_class->finalize       = dee_client_finalize;
 
219
 
 
220
  peer_class->is_swarm_leader  = dee_client_is_swarm_leader;
 
221
  peer_class->get_swarm_leader = dee_client_get_swarm_leader;
 
222
  peer_class->get_connections  = dee_client_get_connections;
 
223
  peer_class->list_peers       = dee_client_list_peers;
 
224
 
 
225
  /**
 
226
   * DeeClient::bus-address:
 
227
   *
 
228
   * D-Bus address the client will connect to. If you do not specify this 
 
229
   * property DeeClient will try to use abstract unix domain socket where
 
230
   * its name is a concatenation of user name running current process and
 
231
   * the swarm name.
 
232
   */
 
233
  pspec = g_param_spec_string ("bus-address", "Bus address",
 
234
                               "Bus address to use for the connection",
 
235
                               NULL,
 
236
                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
 
237
                               | G_PARAM_STATIC_STRINGS);
 
238
  g_object_class_install_property (object_class, PROP_BUS_ADDRESS, pspec);
 
239
}
 
240
 
 
241
static void
 
242
dee_client_init (DeeClient *self)
 
243
{
 
244
  self->priv = GET_PRIVATE (self);
 
245
}
 
246
 
 
247
/**
 
248
 * dee_client_new:
 
249
 *
 
250
 * @swarm_name: Name of swarm to join.
 
251
 *
 
252
 * Creates a new instance of #DeeClient and tries to connect to #DeeServer
 
253
 * created using dee_server_new(). The #DeePeer:swarm-leader property will
 
254
 * be set once the client connects.
 
255
 *
 
256
 * Return value: (transfer full): A newly constructed #DeeClient.
 
257
 */
 
258
DeeClient*
 
259
dee_client_new (const gchar* swarm_name)
 
260
{
 
261
  g_return_val_if_fail (swarm_name != NULL, NULL);
 
262
 
 
263
  return DEE_CLIENT (g_object_new (DEE_TYPE_CLIENT,
 
264
                                   "swarm-name", swarm_name, NULL));
 
265
}
 
266
 
 
267
/**
 
268
 * dee_client_new_for_address:
 
269
 *
 
270
 * @swarm_name: Name of swarm to join.
 
271
 * @bus_address: D-Bus address to use when connecting to the server.
 
272
 *
 
273
 * Creates a new instance of #DeeClient and tries to connect to @bus_address.
 
274
 * The #DeePeer:swarm-leader property will be set once the client connects.
 
275
 *
 
276
 * Return value: (transfer full): A newly constructed #DeeClient.
 
277
 */
 
278
DeeClient*
 
279
dee_client_new_for_address (const gchar* swarm_name,
 
280
                            const gchar* bus_address)
 
281
{
 
282
  g_return_val_if_fail (swarm_name != NULL, NULL);
 
283
 
 
284
  return DEE_CLIENT (g_object_new (DEE_TYPE_CLIENT, 
 
285
                                   "swarm-name", swarm_name,
 
286
                                   "bus-address", bus_address, NULL));
 
287
}
 
288
 
 
289
/* Private Methods */
 
290
 
 
291
static gboolean
 
292
dee_client_is_swarm_leader (DeePeer *peer)
 
293
{
 
294
  return FALSE;
 
295
}
 
296
 
 
297
static const gchar*
 
298
dee_client_get_swarm_leader (DeePeer *peer)
 
299
{
 
300
  DeeClientPrivate *priv;
 
301
 
 
302
  priv = DEE_CLIENT (peer)->priv;
 
303
  return priv->connection ? g_dbus_connection_get_guid (priv->connection) : NULL;
 
304
}
 
305
 
 
306
static GSList*
 
307
dee_client_get_connections (DeePeer *peer)
 
308
{
 
309
  DeeClientPrivate *priv;
 
310
  GSList *list = NULL;
 
311
 
 
312
  priv = DEE_CLIENT (peer)->priv;
 
313
 
 
314
  if (priv->connection)
 
315
    {
 
316
      list = g_slist_append (list, priv->connection);
 
317
    }
 
318
 
 
319
  return list;
 
320
}
 
321
 
 
322
static gchar**
 
323
dee_client_list_peers (DeePeer *peer)
 
324
{
 
325
  DeeClientPrivate *priv;
 
326
  gchar **result;
 
327
  int i = 0;
 
328
 
 
329
  priv = DEE_CLIENT (peer)->priv;
 
330
 
 
331
  result = g_new (gchar*, priv->connection ? 2 : 1);
 
332
 
 
333
  if (priv->connection)
 
334
    {
 
335
      result[i++] = g_strdup (g_dbus_connection_get_guid (priv->connection));
 
336
    }
 
337
  result[i] = NULL;
 
338
 
 
339
  return result;
 
340
}
 
341
 
 
342
static gboolean
 
343
emit_peer_found (gpointer user_data)
 
344
{
 
345
  g_return_val_if_fail (DEE_IS_CLIENT (user_data), FALSE);
 
346
 
 
347
  DeeClientPrivate *priv = DEE_CLIENT (user_data)->priv;
 
348
 
 
349
  g_signal_emit_by_name (user_data, "peer-found",
 
350
                         g_dbus_connection_get_guid (priv->connection));
 
351
 
 
352
  priv->peer_found_timer_id = 0;
 
353
 
 
354
  return FALSE;
 
355
}
 
356
 
 
357
static void
 
358
connecting_finished (GObject *object, GAsyncResult *res, gpointer user_data)
 
359
{
 
360
  GDBusConnection  *connection;
 
361
  DeeClient        *self;
 
362
  DeeClientPrivate *priv;
 
363
  GError           *error = NULL;
 
364
 
 
365
  connection = g_dbus_connection_new_for_address_finish (res, &error);
 
366
 
 
367
  if (error)
 
368
    {
 
369
      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 
370
        {
 
371
          g_warning ("Unable to connect to server: %s", error->message);
 
372
          // swarm-leader will be set to NULL for unsuccessful connections
 
373
          g_object_notify (G_OBJECT (user_data), "swarm-leader");
 
374
        }
 
375
      /* Don't touch the object in case we were cancelled, it's most likely
 
376
       * unreffed by now */
 
377
 
 
378
      g_error_free (error);
 
379
      return;
 
380
    }
 
381
 
 
382
  self = DEE_CLIENT (user_data);
 
383
  priv = self->priv;
 
384
  priv->connection = connection;
 
385
 
 
386
  g_object_unref (priv->cancellable);
 
387
  priv->cancellable = NULL;
 
388
 
 
389
  priv->closed_signal_handler_id = g_signal_connect (connection, "closed",
 
390
      G_CALLBACK (connection_closed), self);
 
391
 
 
392
  g_object_notify (G_OBJECT (user_data), "swarm-leader");
 
393
 
 
394
  g_signal_emit_by_name (user_data, "connection-acquired", connection);
 
395
 
 
396
  // FIXME: we might want to call some List method (same as DeePeer), so far
 
397
  // we'll just simulate an async method (tests expect this anyway)
 
398
  priv->peer_found_timer_id = g_idle_add_full (G_PRIORITY_DEFAULT,
 
399
                                               emit_peer_found, user_data,
 
400
                                               NULL);
 
401
}
 
402
 
 
403
static void
 
404
connection_closed (GDBusConnection *connection, gboolean remote_peer_vanished,
 
405
                   GError *error, DeeClient *client)
 
406
{
 
407
  DeeClientPrivate *priv;
 
408
 
 
409
  g_return_if_fail (DEE_IS_CLIENT (client));
 
410
 
 
411
  priv = client->priv;
 
412
  priv->connection = NULL;
 
413
 
 
414
  g_signal_handler_disconnect (connection, priv->closed_signal_handler_id);
 
415
  priv->closed_signal_handler_id = 0;
 
416
 
 
417
  /* Let's do reverse order of connecting_finished */
 
418
  g_signal_emit_by_name (client, "peer-lost",
 
419
                         g_dbus_connection_get_guid (priv->connection));
 
420
  g_signal_emit_by_name (client, "connection-closed", connection);
 
421
 
 
422
  g_object_notify (G_OBJECT (client), "swarm-leader");
 
423
 
 
424
  g_object_unref (connection);
 
425
}
 
426