1 /* vi: set et sw=4 ts=8 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
3 /*
4 * This file is part of mission-control
5 *
6 * Copyright © 2007-2011 Nokia Corporation.
7 * Copyright © 2009-2012 Collabora Ltd.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * version 2.1 as published by the Free Software Foundation.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24 #include "config.h"
25
26 #include "mcd-account-manager.h"
27
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <dbus/dbus-glib-lowlevel.h>
32 #include <dbus/dbus.h>
33
34 #include <telepathy-glib/telepathy-glib.h>
35 #include <telepathy-glib/telepathy-glib-dbus.h>
36
37 #include "mcd-account-manager-priv.h"
38 #include "mcd-storage.h"
39
40 #include "mcd-account.h"
41 #include "mcd-account-config.h"
42 #include "mcd-account-priv.h"
43 #include "mcd-connection-priv.h"
44 #include "mcd-dbusprop.h"
45 #include "mcd-master-priv.h"
46 #include "mcd-misc.h"
47 #include "mcd-storage.h"
48 #include "mission-control-plugins/mission-control-plugins.h"
49 #include "mission-control-plugins/implementation.h"
50 #include "plugin-loader.h"
51
52 #include "_gen/interfaces.h"
53
54 #define PARAM_PREFIX "param-"
55 #define WRITE_CONF_DELAY 500
56
57 #define MCD_ACCOUNT_MANAGER_PRIV(account_manager) \
58 (MCD_ACCOUNT_MANAGER (account_manager)->priv)
59
60 static void account_manager_iface_init (TpSvcAccountManagerClass *iface,
61 gpointer iface_data);
62 static void account_manager_hidden_iface_init (
63 McSvcAccountManagerInterfaceHiddenClass *iface,
64 gpointer iface_data);
65 static void properties_iface_init (TpSvcDBusPropertiesClass *iface,
66 gpointer iface_data);
67
68 static void _mcd_account_manager_constructed (GObject *obj);
69
70 static const McdDBusProp account_manager_properties[];
71 static const McdDBusProp account_manager_hidden_properties[];
72
73 static const McdInterfaceData account_manager_interfaces[] = {
74 MCD_IMPLEMENT_IFACE (tp_svc_account_manager_get_type,
75 account_manager,
76 TP_IFACE_ACCOUNT_MANAGER),
77 MCD_IMPLEMENT_IFACE (mc_svc_account_manager_interface_hidden_get_type,
78 account_manager_hidden,
79 MC_IFACE_ACCOUNT_MANAGER_INTERFACE_HIDDEN),
80 { NULL, }
81 };
82
83 G_DEFINE_TYPE_WITH_CODE (McdAccountManager, mcd_account_manager, G_TYPE_OBJECT,
84 MCD_DBUS_INIT_INTERFACES (account_manager_interfaces);
85 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
86 properties_iface_init);
87 )
88
89 struct _McdAccountManagerPrivate
90 {
91 TpDBusDaemon *dbus_daemon;
92 TpSimpleClientFactory *client_factory;
93 McdConnectivityMonitor *minotaur;
94
95 McdStorage *storage;
96 GHashTable *accounts;
97
98 gchar *account_connections_dir; /* directory for temporary file */
99 gchar *account_connections_file; /* in account_connections_dir */
100
101 gboolean dbus_registered;
102 };
103
104 typedef struct
105 {
106 McdAccountManager *account_manager;
107 McpAccountStorage *storage_plugin;
108 McdAccount *account;
109 gint account_lock;
110 } McdLoadAccountsData;
111
112 typedef struct
113 {
114 McdAccountManager *account_manager;
115 GHashTable *parameters;
116 GHashTable *properties;
117 McdGetAccountCb callback;
118 gpointer user_data;
119 GDestroyNotify destroy;
120
121 gboolean ok;
122 GError *error;
123 } McdCreateAccountData;
124
125 typedef struct
126 {
127 McdAccount *account;
128 gchar *key;
129 } McdAlterAccountData;
130
131 enum
132 {
133 PROP_0,
134 PROP_DBUS_DAEMON,
135 PROP_CLIENT_FACTORY
136 };
137
138 static guint write_conf_id = 0;
139
140 static void register_dbus_service (McdAccountManager *account_manager);
141
142 static void release_load_accounts_lock (McdLoadAccountsData *lad);
143 static void add_account (McdAccountManager *manager, McdAccount *account,
144 const gchar *source);
145 static void account_loaded (McdAccount *account,
146 const GError *error,
147 gpointer user_data);
148
149 /* calback chain for asynchronously updates from backends: */
150 static void
async_altered_validity_cb(McdAccount * account,const GError * invalid_reason,gpointer data)151 async_altered_validity_cb (McdAccount *account, const GError *invalid_reason, gpointer data)
152 {
153 DEBUG ("asynchronously altered account %s is %svalid",
154 mcd_account_get_unique_name (account), (invalid_reason == NULL) ? "" : "in");
155
156 g_object_unref (account);
157 }
158
159 static void
async_altered_manager_cb(McdManager * cm,const GError * error,gpointer data)160 async_altered_manager_cb (McdManager *cm, const GError *error, gpointer data)
161 {
162 McdAccount *account = data;
163 const gchar *name = NULL;
164
165 if (cm != NULL)
166 name = mcd_manager_get_name (cm);
167
168 if (error != NULL)
169 DEBUG ("manager %s not ready: %s", name, error->message);
170 else
171 DEBUG ("manager %s is ready", name);
172
173 /* this triggers the final parameter check which results in dbus signals *
174 * being fired and (potentially) the account going online automatically */
175 mcd_account_check_validity (account, async_altered_validity_cb, NULL);
176
177 g_object_unref (cm);
178 }
179
180 /* account has been updated by a third party, and the McpAccountStorage *
181 * plugin has just informed us of this fact */
182 static void
altered_cb(GObject * storage,const gchar * name,gpointer data)183 altered_cb (GObject *storage, const gchar *name, gpointer data)
184 {
185 McdAccountManager *am = MCD_ACCOUNT_MANAGER (data);
186 McdMaster *master = mcd_master_get_default ();
187 McdAccount *account = NULL;
188 McdManager *cm = NULL;
189 const gchar *cm_name = NULL;
190
191 account = mcd_account_manager_lookup_account (am, name);
192
193 if (G_UNLIKELY (!account))
194 {
195 g_warning ("%s: account %s does not exist", G_STRFUNC, name);
196 return;
197 }
198
199 /* in theory, the CM is already ready by this point, but make sure: */
200 cm_name = mcd_account_get_manager_name (account);
201
202 if (cm_name != NULL)
203 cm = _mcd_master_lookup_manager (master, cm_name);
204
205 if (cm != NULL)
206 {
207 g_object_ref (cm);
208 g_object_ref (account);
209 mcd_manager_call_when_ready (cm, async_altered_manager_cb, account);
210 }
211 }
212
213 static void
async_altered_one_manager_cb(McdManager * cm,const GError * error,gpointer data)214 async_altered_one_manager_cb (McdManager *cm,
215 const GError *error,
216 gpointer data)
217 {
218 McdAlterAccountData *altered = data;
219 const gchar *name = NULL;
220
221 if (cm != NULL)
222 name = mcd_manager_get_name (cm);
223
224 if (error != NULL)
225 DEBUG ("manager %s not ready: %s", name, error->message);
226 else
227 DEBUG ("manager %s is ready", name);
228
229 /* this triggers the final parameter check which results in dbus signals *
230 * being fired and (potentially) the account going online automatically */
231 mcd_account_altered_by_plugin (altered->account, altered->key);
232
233 g_object_unref (cm);
234 g_object_unref (altered->account);
235 g_free (altered->key);
236 g_slice_free (McdAlterAccountData, altered);
237 }
238
239
240 static void
altered_one_cb(GObject * storage,const gchar * account_name,const gchar * key,gpointer data)241 altered_one_cb (GObject *storage,
242 const gchar *account_name,
243 const gchar *key,
244 gpointer data)
245 {
246 McdAccountManager *am = MCD_ACCOUNT_MANAGER (data);
247 McdMaster *master = mcd_master_get_default ();
248 McdAccount *account = NULL;
249 McdManager *cm = NULL;
250 const gchar *cm_name = NULL;
251
252 account = mcd_account_manager_lookup_account (am, account_name);
253
254 if (G_UNLIKELY (!account))
255 {
256 g_warning ("%s: account %s does not exist", G_STRFUNC, account_name);
257 return;
258 }
259
260 /* in theory, the CM is already ready by this point, but make sure: */
261 cm_name = mcd_account_get_manager_name (account);
262
263 if (cm_name != NULL)
264 cm = _mcd_master_lookup_manager (master, cm_name);
265
266 if (cm != NULL)
267 {
268 McdAlterAccountData *altered = g_slice_new0 (McdAlterAccountData);
269
270 g_object_ref (cm);
271 altered->account = g_object_ref (account);
272 altered->key = g_strdup (key);
273
274 mcd_manager_call_when_ready (cm, async_altered_one_manager_cb, altered);
275 }
276 }
277
278 /* callbacks for the various stages in an backend-driven account creation */
279 static void
async_created_validity_cb(McdAccount * account,const GError * invalid_reason,gpointer data)280 async_created_validity_cb (McdAccount *account, const GError *invalid_reason, gpointer data)
281 {
282 DEBUG ("asynchronously created account %s is %svalid",
283 mcd_account_get_unique_name (account), (invalid_reason == NULL) ? "" : "in");
284
285 /* safely cached in the accounts hash by now */
286 g_object_unref (account);
287 }
288
289 static void
async_created_manager_cb(McdManager * cm,const GError * error,gpointer data)290 async_created_manager_cb (McdManager *cm, const GError *error, gpointer data)
291 {
292 McdLoadAccountsData *lad = data;
293 McdAccount *account = lad->account;
294 McdAccountManager *am = lad->account_manager;
295 McpAccountStorage *plugin = lad->storage_plugin;
296 const gchar *name = NULL;
297
298 if (cm != NULL)
299 name = mcd_manager_get_name (cm);
300
301 if (error != NULL)
302 DEBUG ("manager %s not ready: %s", name, error->message);
303 else
304 DEBUG ("manager %s is ready", name);
305
306 /* this takes a ref to the account and stores it in the accounts hash */
307 add_account (am, account, mcp_account_storage_name (plugin));
308
309 /* this will free the McdLoadAccountsData, don't use it after this */
310 _mcd_account_load (account, account_loaded, lad);
311
312 /* this triggers the final parameter check which results in dbus signals *
313 * being fired and (potentially) the account going online automatically */
314 mcd_account_check_validity (account, async_created_validity_cb, NULL);
315
316 g_object_unref (cm);
317 }
318
319 /* account created by an McpAccountStorage plugin after the initial setup *
320 * since the plugin does not have our cache, we need to poke the plugin *
321 * to fetch the named account explicitly at this point (ie it's a read, not *
322 * not a write, from the plugin's POV: */
323 static void
created_cb(GObject * storage_plugin_obj,const gchar * name,gpointer data)324 created_cb (GObject *storage_plugin_obj,
325 const gchar *name,
326 gpointer data)
327 {
328 McpAccountStorage *plugin = MCP_ACCOUNT_STORAGE (storage_plugin_obj);
329 McdAccountManager *am = MCD_ACCOUNT_MANAGER (data);
330 McdAccountManagerPrivate *priv = MCD_ACCOUNT_MANAGER_PRIV (am);
331 McdLoadAccountsData *lad = g_slice_new (McdLoadAccountsData);
332 McdAccount *account = NULL;
333 McdStorage *storage = priv->storage;
334 McdMaster *master = mcd_master_get_default ();
335 McdManager *cm = NULL;
336 const gchar *cm_name = NULL;
337
338 lad->account_manager = am;
339 lad->storage_plugin = plugin;
340 lad->account_lock = 1; /* will be released at the end of this function */
341
342 /* actually fetch the data into our cache from the plugin: */
343 if (mcd_storage_add_account_from_plugin (storage, plugin, name))
344 {
345 account = mcd_account_new (am, name, priv->minotaur);
346 lad->account = account;
347 }
348 else
349 {
350 /* that function already warned about it */
351 goto finish;
352 }
353
354 if (G_UNLIKELY (!account))
355 {
356 g_warning ("%s: account %s failed to instantiate", G_STRFUNC, name);
357 goto finish;
358 }
359
360 cm_name = mcd_account_get_manager_name (account);
361
362 if (cm_name != NULL)
363 cm = _mcd_master_lookup_manager (master, cm_name);
364
365 if (cm != NULL)
366 {
367 lad->account_lock++;
368 g_object_ref (cm);
369 mcd_manager_call_when_ready (cm, async_created_manager_cb, lad);
370 }
371 else
372 {
373 /* account is well and truly broken. forget it even existed: */
374 g_warning ("%s: account %s has no manager, ignoring it",
375 G_STRFUNC, name);
376 g_object_unref (account);
377 }
378
379 finish:
380 release_load_accounts_lock (lad);
381 }
382
383 static void
toggled_cb(GObject * plugin,const gchar * name,gboolean on,gpointer data)384 toggled_cb (GObject *plugin, const gchar *name, gboolean on, gpointer data)
385 {
386 McpAccountStorage *storage_plugin = MCP_ACCOUNT_STORAGE (plugin);
387 McdAccountManager *manager = MCD_ACCOUNT_MANAGER (data);
388 McdAccount *account = NULL;
389 GError *error = NULL;
390
391 account = mcd_account_manager_lookup_account (manager, name);
392
393 DEBUG ("%s plugin reports %s became %sabled",
394 mcp_account_storage_name (storage_plugin), name, on ? "en" : "dis");
395
396 if (account == NULL)
397 {
398 g_warning ("%s: Unknown account %s from %s plugin",
399 G_STRFUNC, name, mcp_account_storage_name (storage_plugin));
400 return;
401 }
402
403 _mcd_account_set_enabled (account, on, FALSE,
404 MCD_DBUS_PROP_SET_FLAG_NONE, &error);
405
406 if (error != NULL)
407 {
408 g_warning ("Error setting Enabled for %s: %s", name, error->message);
409 g_clear_error (&error);
410 }
411 }
412
413 static void
reconnect_cb(GObject * plugin,const gchar * name,gpointer data)414 reconnect_cb (GObject *plugin, const gchar *name, gpointer data)
415 {
416 McpAccountStorage *storage_plugin = MCP_ACCOUNT_STORAGE (plugin);
417 McdAccountManager *manager = MCD_ACCOUNT_MANAGER (data);
418 McdAccount *account = NULL;
419
420 account = mcd_account_manager_lookup_account (manager, name);
421
422 DEBUG ("%s plugin request %s reconnection",
423 mcp_account_storage_name (storage_plugin), name);
424
425 if (account == NULL)
426 {
427 g_warning ("%s: Unknown account %s from %s plugin",
428 G_STRFUNC, name, mcp_account_storage_name (storage_plugin));
429 return;
430 }
431
432 /* Storage ask to reconnect when important parameters changed, which is an
433 * user action. */
434 _mcd_account_reconnect (account, TRUE);
435 }
436
437 static void
_mcd_account_delete_cb(McdAccount * account,const GError * error,gpointer data)438 _mcd_account_delete_cb (McdAccount *account, const GError *error, gpointer data)
439 {
440 /* no need to do anything other than release the account ref, which *
441 * should be the last ref we hold by the time this rolls arouns: */
442 g_object_unref (account);
443 }
444
445 /* a backend plugin notified us that an account was vaporised: remove it */
446 static void
deleted_cb(GObject * plugin,const gchar * name,gpointer data)447 deleted_cb (GObject *plugin, const gchar *name, gpointer data)
448 {
449 McpAccountStorage *storage_plugin = MCP_ACCOUNT_STORAGE (plugin);
450 McdAccountManager *manager = MCD_ACCOUNT_MANAGER (data);
451 McdAccount *account = NULL;
452
453 account = g_hash_table_lookup (manager->priv->accounts, name);
454
455 DEBUG ("%s reported deletion of %s (%p)",
456 mcp_account_storage_name (storage_plugin), name, account);
457
458 if (account != NULL)
459 {
460 const gchar * object_path = mcd_account_get_object_path (account);
461
462 g_object_ref (account);
463 /* this unhooks the account's signal handlers */
464 g_hash_table_remove (manager->priv->accounts, name);
465 tp_svc_account_manager_emit_account_removed (manager, object_path);
466 mcd_account_delete (account, _mcd_account_delete_cb, NULL);
467 }
468 }
469
470 static gboolean
get_account_connection(const gchar * file_contents,const gchar * path,gchar ** p_bus_name,gchar ** p_account_name)471 get_account_connection (const gchar *file_contents, const gchar *path,
472 gchar **p_bus_name, gchar **p_account_name)
473 {
474 const gchar *line, *tab1, *tab2, *endline;
475 const gchar *connection_path, *bus_name, *account_name;
476 size_t len;
477
478 g_return_val_if_fail (path != NULL, FALSE);
479 if (!file_contents) return FALSE;
480
481 len = strlen (path);
482 line = file_contents;
483 while ((tab1 = strchr (line, '\t')) != NULL)
484 {
485 connection_path = line;
486
487 bus_name = tab1 + 1;
488 tab2 = strchr (bus_name, '\t');
489 if (!tab2) break;
490
491 account_name = tab2 + 1;
492 endline = strchr (account_name, '\n');
493 if (!endline) break;
494
495 if (line + len == tab1 &&
496 strncmp (path, connection_path, len) == 0)
497 {
498 *p_bus_name = g_strndup (bus_name, tab2 - bus_name);
499 *p_account_name = g_strndup (account_name, endline - account_name);
500 return TRUE;
501 }
502 line = endline + 1;
503 }
504 return FALSE;
505 }
506
507 static gboolean
recover_connection(McdAccountManager * account_manager,gchar * file_contents,const gchar * name)508 recover_connection (McdAccountManager *account_manager, gchar *file_contents,
509 const gchar *name)
510 {
511 McdAccount *account;
512 McdConnection *connection;
513 McdManager *manager;
514 McdMaster *master;
515 const gchar *manager_name;
516 gchar *object_path, *bus_name, *account_name;
517 GError *error = NULL;
518 gboolean ret = FALSE;
519
520 master = mcd_master_get_default ();
521 g_return_val_if_fail (MCD_IS_MASTER (master), FALSE);
522
523 object_path = g_strdelimit (g_strdup_printf ("/%s", name), ".", '/');
524 if (!get_account_connection (file_contents, object_path,
525 &bus_name, &account_name))
526 goto err_match;
527
528 account = g_hash_table_lookup (account_manager->priv->accounts,
529 account_name);
530 if (!account || !mcd_account_is_enabled (account))
531 goto err_account;
532
533 DEBUG ("account is %s", mcd_account_get_unique_name (account));
534 manager_name = mcd_account_get_manager_name (account);
535
536 manager = _mcd_master_lookup_manager (master, manager_name);
537 if (G_UNLIKELY (!manager))
538 {
539 DEBUG ("Manager %s not found", manager_name);
540 goto err_manager;
541 }
542
543 connection = mcd_manager_create_connection (manager, account);
544 if (G_UNLIKELY (!connection)) goto err_connection;
545
546 _mcd_connection_set_tp_connection (connection, bus_name, object_path,
547 &error);
548 if (G_UNLIKELY (error))
549 {
550 DEBUG ("got error: %s", error->message);
551 g_error_free (error);
552 goto err_connection;
553 }
554 ret = TRUE;
555
556 err_connection:
557 err_manager:
558 err_account:
559 g_free (account_name);
560 g_free (bus_name);
561 err_match:
562 g_free (object_path);
563 return ret;
564 }
565
566 static void
list_connection_names_cb(const gchar * const * names,gsize n,const gchar * const * cms,const gchar * const * protocols,const GError * error,gpointer user_data,GObject * weak_object)567 list_connection_names_cb (const gchar * const *names, gsize n,
568 const gchar * const *cms,
569 const gchar * const *protocols,
570 const GError *error, gpointer user_data,
571 GObject *weak_object)
572 {
573 McdAccountManager *account_manager = MCD_ACCOUNT_MANAGER (weak_object);
574 McdAccountManagerPrivate *priv = account_manager->priv;
575 gchar *contents = NULL;
576 guint i;
577
578 DEBUG ("%" G_GSIZE_FORMAT " connections", n);
579
580 /* if the file has no contents, we don't really care why */
581 if (!g_file_get_contents (priv->account_connections_file, &contents,
582 NULL, NULL))
583 {
584 contents = NULL;
585 }
586
587 for (i = 0; i < n; i++)
588 {
589 g_return_if_fail (names[i] != NULL);
590 DEBUG ("Connection %s", names[i]);
591 if (!recover_connection (account_manager, contents, names[i]))
592 {
593 /* Close the connection */
594 TpConnection *proxy;
595 gchar *path;
596
597 path = g_strdup_printf ("/%s", names[i]);
598 g_strdelimit (path, ".", '/');
599
600 DEBUG ("Killing connection");
601 proxy = tp_simple_client_factory_ensure_connection (
602 priv->client_factory, path, NULL, NULL);
603
604 if (proxy)
605 {
606 tp_cli_connection_call_disconnect (proxy, -1, NULL, NULL,
607 NULL, NULL);
608 g_object_unref (proxy);
609 }
610
611 g_free (path);
612 }
613 }
614 g_free (contents);
615 }
616
617 static void
on_account_validity_changed(McdAccount * account,gboolean valid,McdAccountManager * account_manager)618 on_account_validity_changed (McdAccount *account, gboolean valid,
619 McdAccountManager *account_manager)
620 {
621 const gchar *object_path;
622
623 object_path = mcd_account_get_object_path (account);
624
625 if (_mcd_account_is_hidden (account))
626 {
627 mc_svc_account_manager_interface_hidden_emit_hidden_account_validity_changed (
628 account_manager, object_path, valid);
629 }
630 else
631 {
632 tp_svc_account_manager_emit_account_validity_changed (account_manager,
633 object_path,
634 valid);
635 }
636 }
637
638 static void
on_account_removed(McdAccount * account,McdAccountManager * account_manager)639 on_account_removed (McdAccount *account, McdAccountManager *account_manager)
640 {
641 McdAccountManagerPrivate *priv = account_manager->priv;
642 McdStorage *storage = priv->storage;
643 const gchar *name, *object_path;
644
645 object_path = mcd_account_get_object_path (account);
646
647 if (_mcd_account_is_hidden (account))
648 {
649 mc_svc_account_manager_interface_hidden_emit_hidden_account_removed (
650 account_manager, object_path);
651 }
652 else
653 {
654 tp_svc_account_manager_emit_account_removed (account_manager,
655 object_path);
656 }
657
658 name = mcd_account_get_unique_name (account);
659 g_hash_table_remove (priv->accounts, name);
660
661 mcd_storage_delete_account (storage, name);
662 mcd_account_manager_write_conf_async (account_manager, account, NULL,
663 NULL);
664 }
665
666 static inline void
disconnect_signal(gpointer instance,gpointer func)667 disconnect_signal (gpointer instance, gpointer func)
668 {
669 g_signal_handlers_disconnect_matched (instance,
670 G_SIGNAL_MATCH_FUNC,
671 0, 0, NULL, func, NULL);
672 }
673
674 static void
unref_account(gpointer data)675 unref_account (gpointer data)
676 {
677 McdAccount *account = MCD_ACCOUNT (data);
678
679 DEBUG ("called for %s", mcd_account_get_unique_name (account));
680
681 disconnect_signal (account, on_account_validity_changed);
682 disconnect_signal (account, on_account_removed);
683
684 g_object_unref (account);
685 }
686
687 static void _mcd_account_manager_store_account_connections (
688 McdAccountManager *);
689
690 static void
add_account(McdAccountManager * account_manager,McdAccount * account,const gchar * source)691 add_account (McdAccountManager *account_manager, McdAccount *account,
692 const gchar *source)
693 {
694 McdAccountManagerPrivate *priv = account_manager->priv;
695 McdAccount *existing;
696 const gchar *name;
697
698 name = mcd_account_get_unique_name (account);
699 DEBUG ("adding account %s (%p) from %s", name, account, source);
700
701 existing = mcd_account_manager_lookup_account (account_manager, name);
702 if (existing != NULL)
703 {
704 g_warning ("...but we already have an account %p with that name!", existing);
705 }
706
707 g_hash_table_insert (priv->accounts, (gchar *)name,
708 g_object_ref (account));
709
710 /* if we have to connect to any signals from the account object, this is
711 * the place to do it */
712 g_signal_connect (account, "validity-changed",
713 G_CALLBACK (on_account_validity_changed),
714 account_manager);
715 g_signal_connect (account, "removed", G_CALLBACK (on_account_removed),
716 account_manager);
717 tp_g_signal_connect_object (account, "connection-path-changed",
718 G_CALLBACK (_mcd_account_manager_store_account_connections),
719 account_manager, G_CONNECT_SWAPPED);
720
721 /* some reports indicate this doesn't always fire for async backend *
722 * accounts: testing here hasn't shown this, but at least we will be *
723 * able to tell if this happens from MC debug logs now: */
724 DEBUG ("account %s validity: %d", name, mcd_account_is_valid (account));
725 /* if the account is already valid, synthesize a signal indicating that
726 * it's been added */
727 if (mcd_account_is_valid (account))
728 on_account_validity_changed (account, TRUE, account_manager);
729 }
730
731 static void
mcd_create_account_data_free(McdCreateAccountData * cad)732 mcd_create_account_data_free (McdCreateAccountData *cad)
733 {
734 g_hash_table_unref (cad->parameters);
735 tp_clear_pointer (&cad->properties, g_hash_table_unref);
736
737 if (G_UNLIKELY (cad->error))
738 g_error_free (cad->error);
739
740 g_slice_free (McdCreateAccountData, cad);
741 }
742
743 static gboolean
set_new_account_properties(McdAccount * account,GHashTable * properties,GError ** error)744 set_new_account_properties (McdAccount *account,
745 GHashTable *properties,
746 GError **error)
747 {
748 GHashTableIter iter;
749 gpointer key, value;
750 gboolean ok = TRUE;
751
752 g_hash_table_iter_init (&iter, properties);
753
754 while (g_hash_table_iter_next (&iter, &key, &value) && ok)
755 {
756 gchar *name = key;
757 gchar *dot, *iface, *pname;
758
759 if ((dot = strrchr (name, '.')) != NULL)
760 {
761 iface = g_strndup (name, dot - name);
762 pname = dot + 1;
763 ok = mcd_dbusprop_set_property (TP_SVC_DBUS_PROPERTIES (account),
764 iface, pname, value, error);
765 g_free (iface);
766 }
767 else
768 {
769 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
770 "Malformed property name: %s", name);
771 ok = FALSE;
772 }
773 }
774
775 return ok;
776 }
777
778 static void
complete_account_creation_finish(McdAccount * account,McdCreateAccountData * cad)779 complete_account_creation_finish (McdAccount *account,
780 McdCreateAccountData *cad)
781 {
782 McdAccountManager *account_manager = cad->account_manager;
783
784 if (!cad->ok)
785 {
786 mcd_account_delete (account, NULL, NULL);
787 tp_clear_object (&account);
788 }
789
790 mcd_account_manager_write_conf_async (account_manager, account, NULL,
791 NULL);
792
793 if (cad->callback != NULL)
794 cad->callback (account_manager, account, cad->error, cad->user_data);
795 mcd_create_account_data_free (cad);
796
797 tp_clear_object (&account);
798 }
799
800 static void
complete_account_creation_check_validity_cb(McdAccount * account,const GError * invalid_reason,gpointer user_data)801 complete_account_creation_check_validity_cb (McdAccount *account,
802 const GError *invalid_reason,
803 gpointer user_data)
804 {
805 McdCreateAccountData *cad = user_data;
806
807 if (invalid_reason != NULL)
808 {
809 cad->ok = FALSE;
810 g_set_error_literal (&cad->error, invalid_reason->domain,
811 invalid_reason->code, invalid_reason->message);
812 }
813
814 complete_account_creation_finish (account, cad);
815 }
816
817 static void
complete_account_creation_set_cb(McdAccount * account,GPtrArray * not_yet,const GError * set_error,gpointer user_data)818 complete_account_creation_set_cb (McdAccount *account, GPtrArray *not_yet,
819 const GError *set_error, gpointer user_data)
820 {
821 McdCreateAccountData *cad = user_data;
822 McdAccountManager *account_manager;
823 cad->ok = TRUE;
824
825 account_manager = cad->account_manager;
826
827 if (set_error != NULL)
828 {
829 cad->ok = FALSE;
830 g_set_error_literal (&cad->error, set_error->domain, set_error->code,
831 set_error->message);
832 }
833
834 if (cad->ok && cad->properties != NULL)
835 {
836 cad->ok = set_new_account_properties (account, cad->properties, &cad->error);
837 }
838
839 if (cad->ok)
840 {
841 add_account (account_manager, account, G_STRFUNC);
842 mcd_account_check_validity (account, complete_account_creation_check_validity_cb, cad);
843 }
844 else
845 {
846 complete_account_creation_finish (account, cad);
847 }
848 }
849
850 static void
complete_account_creation(McdAccount * account,const GError * cb_error,gpointer user_data)851 complete_account_creation (McdAccount *account,
852 const GError *cb_error,
853 gpointer user_data)
854 {
855 McdCreateAccountData *cad = user_data;
856 McdAccountManager *account_manager;
857
858 account_manager = cad->account_manager;
859 if (G_UNLIKELY (cb_error))
860 {
861 cad->callback (account_manager, account, cb_error, cad->user_data);
862 mcd_create_account_data_free (cad);
863 return;
864 }
865
866 _mcd_account_set_parameters (account, cad->parameters, NULL,
867 complete_account_creation_set_cb,
868 cad);
869 }
870
871 void
_mcd_account_manager_create_account(McdAccountManager * account_manager,const gchar * manager,const gchar * protocol,const gchar * display_name,GHashTable * params,GHashTable * properties,McdGetAccountCb callback,gpointer user_data,GDestroyNotify destroy)872 _mcd_account_manager_create_account (McdAccountManager *account_manager,
873 const gchar *manager,
874 const gchar *protocol,
875 const gchar *display_name,
876 GHashTable *params,
877 GHashTable *properties,
878 McdGetAccountCb callback,
879 gpointer user_data,
880 GDestroyNotify destroy)
881 {
882 McdAccountManagerPrivate *priv = account_manager->priv;
883 McdStorage *storage = priv->storage;
884 McdCreateAccountData *cad;
885 McdAccount *account;
886 gchar *unique_name = NULL;
887 const gchar *provider;
888 GError *e = NULL;
889
890 DEBUG ("called");
891 if (G_UNLIKELY (manager == NULL || manager[0] == 0 ||
892 protocol == NULL || protocol[0] == 0))
893 {
894 GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
895 "Invalid parameters"};
896 callback (account_manager, NULL, &error, user_data);
897 if (destroy)
898 destroy (user_data);
899 return;
900 }
901
902 provider = tp_asv_get_string (properties,
903 TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER);
904
905 unique_name = mcd_storage_create_account (storage, provider,
906 manager, protocol, params,
907 &e);
908
909 if (unique_name == NULL)
910 {
911 callback (account_manager, NULL, e, user_data);
912 g_clear_error (&e);
913 if (destroy)
914 destroy (user_data);
915 return;
916 }
917
918 /* create the basic account keys */
919 mcd_storage_set_string (storage, unique_name,
920 MC_ACCOUNTS_KEY_MANAGER, manager);
921 mcd_storage_set_string (storage, unique_name,
922 MC_ACCOUNTS_KEY_PROTOCOL, protocol);
923
924 if (display_name != NULL)
925 mcd_storage_set_string (storage, unique_name,
926 MC_ACCOUNTS_KEY_DISPLAY_NAME, display_name);
927
928 account = mcd_account_new (account_manager, unique_name, priv->minotaur);
929 g_free (unique_name);
930
931 if (G_LIKELY (account))
932 {
933 cad = g_slice_new (McdCreateAccountData);
934 cad->account_manager = account_manager;
935 cad->parameters = g_hash_table_ref (params);
936 cad->properties = (properties ? g_hash_table_ref (properties) : NULL);
937 cad->callback = callback;
938 cad->user_data = user_data;
939 cad->destroy = destroy;
940 cad->error = NULL;
941 _mcd_account_load (account, complete_account_creation, cad);
942 }
943 else
944 {
945 GError error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "" };
946 callback (account_manager, NULL, &error, user_data);
947 if (destroy)
948 destroy (user_data);
949 }
950 }
951
952 static void
create_account_cb(McdAccountManager * account_manager,McdAccount * account,const GError * error,gpointer user_data)953 create_account_cb (McdAccountManager *account_manager, McdAccount *account,
954 const GError *error, gpointer user_data)
955 {
956 DBusGMethodInvocation *context = user_data;
957 const gchar *object_path;
958
959 if (G_UNLIKELY (error))
960 {
961 dbus_g_method_return_error (context, (GError *)error);
962 return;
963 }
964
965 g_return_if_fail (MCD_IS_ACCOUNT (account));
966 object_path = mcd_account_get_object_path (account);
967 tp_svc_account_manager_return_from_create_account (context, object_path);
968 }
969
970 static void
account_manager_create_account(TpSvcAccountManager * self,const gchar * manager,const gchar * protocol,const gchar * display_name,GHashTable * parameters,GHashTable * properties,DBusGMethodInvocation * context)971 account_manager_create_account (TpSvcAccountManager *self,
972 const gchar *manager,
973 const gchar *protocol,
974 const gchar *display_name,
975 GHashTable *parameters,
976 GHashTable *properties,
977 DBusGMethodInvocation *context)
978 {
979 _mcd_account_manager_create_account (MCD_ACCOUNT_MANAGER (self),
980 manager, protocol, display_name,
981 parameters, properties,
982 create_account_cb, context, NULL);
983 }
984
985 static void
account_manager_iface_init(TpSvcAccountManagerClass * iface,gpointer iface_data)986 account_manager_iface_init (TpSvcAccountManagerClass *iface,
987 gpointer iface_data)
988 {
989 #define IMPLEMENT(x) tp_svc_account_manager_implement_##x (\
990 iface, account_manager_##x)
991 IMPLEMENT(create_account);
992 #undef IMPLEMENT
993 }
994
995 static void
account_manager_hidden_iface_init(McSvcAccountManagerInterfaceHiddenClass * iface,gpointer iface_data)996 account_manager_hidden_iface_init (
997 McSvcAccountManagerInterfaceHiddenClass *iface,
998 gpointer iface_data)
999 {
1000 }
1001
1002 static void
accounts_to_gvalue(GHashTable * accounts,gboolean valid,gboolean hidden,GValue * value)1003 accounts_to_gvalue (GHashTable *accounts, gboolean valid, gboolean hidden,
1004 GValue *value)
1005 {
1006 static GType ao_type = G_TYPE_INVALID;
1007 GPtrArray *account_array;
1008 GHashTableIter iter;
1009 McdAccount *account;
1010 gpointer k;
1011
1012 if (G_UNLIKELY (ao_type == G_TYPE_INVALID))
1013 ao_type = dbus_g_type_get_collection ("GPtrArray",
1014 DBUS_TYPE_G_OBJECT_PATH);
1015
1016 account_array = g_ptr_array_sized_new (g_hash_table_size (accounts));
1017
1018 g_hash_table_iter_init (&iter, accounts);
1019
1020 while (g_hash_table_iter_next (&iter, &k, (gpointer)&account))
1021 {
1022 if (mcd_account_is_valid (account) == valid &&
1023 _mcd_account_is_hidden (account) == hidden)
1024 {
1025 g_ptr_array_add (account_array,
1026 g_strdup (mcd_account_get_object_path (account)));
1027 }
1028 }
1029
1030 g_value_init (value, ao_type);
1031 g_value_take_boxed (value, account_array);
1032 }
1033
1034 static void
get_valid_accounts(TpSvcDBusProperties * self,const gchar * name,GValue * value)1035 get_valid_accounts (TpSvcDBusProperties *self, const gchar *name,
1036 GValue *value)
1037 {
1038 McdAccountManager *account_manager = MCD_ACCOUNT_MANAGER (self);
1039 McdAccountManagerPrivate *priv = account_manager->priv;
1040
1041 DEBUG ("called");
1042 accounts_to_gvalue (priv->accounts, TRUE, FALSE, value);
1043 }
1044
1045 static void
get_invalid_accounts(TpSvcDBusProperties * self,const gchar * name,GValue * value)1046 get_invalid_accounts (TpSvcDBusProperties *self, const gchar *name,
1047 GValue *value)
1048 {
1049 McdAccountManager *account_manager = MCD_ACCOUNT_MANAGER (self);
1050 McdAccountManagerPrivate *priv = account_manager->priv;
1051
1052 DEBUG ("called");
1053 accounts_to_gvalue (priv->accounts, FALSE, FALSE, value);
1054 }
1055
1056 static void
get_supported_account_properties(TpSvcDBusProperties * svc,const gchar * name,GValue * value)1057 get_supported_account_properties (TpSvcDBusProperties *svc,
1058 const gchar *name,
1059 GValue *value)
1060 {
1061 static const gchar * const supported[] = {
1062 TP_IFACE_ACCOUNT ".AutomaticPresence",
1063 TP_IFACE_ACCOUNT ".Enabled",
1064 TP_IFACE_ACCOUNT ".Icon",
1065 TP_IFACE_ACCOUNT ".Nickname",
1066 TP_IFACE_ACCOUNT ".ConnectAutomatically",
1067 TP_IFACE_ACCOUNT ".RequestedPresence",
1068 TP_IFACE_ACCOUNT ".Supersedes",
1069 TP_PROP_ACCOUNT_SERVICE,
1070 TP_IFACE_ACCOUNT_INTERFACE_AVATAR ".Avatar",
1071 MC_IFACE_ACCOUNT_INTERFACE_CONDITIONS ".Condition",
1072 TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER,
1073 MC_IFACE_ACCOUNT_INTERFACE_HIDDEN ".Hidden",
1074 NULL
1075 };
1076
1077 g_value_init (value, G_TYPE_STRV);
1078 g_value_set_static_boxed (value, supported);
1079 }
1080
1081 static const McdDBusProp account_manager_properties[] = {
1082 { "ValidAccounts", NULL, get_valid_accounts },
1083 { "InvalidAccounts", NULL, get_invalid_accounts },
1084 { "Interfaces", NULL, mcd_dbus_get_interfaces },
1085 { "SupportedAccountProperties", NULL, get_supported_account_properties },
1086 { 0 },
1087 };
1088
1089 static void
get_valid_hidden_accounts(TpSvcDBusProperties * self,const gchar * name,GValue * value)1090 get_valid_hidden_accounts (TpSvcDBusProperties *self, const gchar *name,
1091 GValue *value)
1092 {
1093 McdAccountManager *account_manager = MCD_ACCOUNT_MANAGER (self);
1094 McdAccountManagerPrivate *priv = account_manager->priv;
1095
1096 accounts_to_gvalue (priv->accounts, TRUE, TRUE, value);
1097 }
1098
1099 static void
get_invalid_hidden_accounts(TpSvcDBusProperties * self,const gchar * name,GValue * value)1100 get_invalid_hidden_accounts (TpSvcDBusProperties *self, const gchar *name,
1101 GValue *value)
1102 {
1103 McdAccountManager *account_manager = MCD_ACCOUNT_MANAGER (self);
1104 McdAccountManagerPrivate *priv = account_manager->priv;
1105
1106 accounts_to_gvalue (priv->accounts, FALSE, TRUE, value);
1107 }
1108
1109 static const McdDBusProp account_manager_hidden_properties[] = {
1110 { "ValidHiddenAccounts", NULL, get_valid_hidden_accounts },
1111 { "InvalidHiddenAccounts", NULL, get_invalid_hidden_accounts },
1112 { 0 },
1113 };
1114
1115 static void
properties_iface_init(TpSvcDBusPropertiesClass * iface,gpointer iface_data)1116 properties_iface_init (TpSvcDBusPropertiesClass *iface, gpointer iface_data)
1117 {
1118 #define IMPLEMENT(x) tp_svc_dbus_properties_implement_##x (\
1119 iface, dbusprop_##x)
1120 IMPLEMENT(set);
1121 IMPLEMENT(get);
1122 IMPLEMENT(get_all);
1123 #undef IMPLEMENT
1124 }
1125
1126 static gboolean
write_conf(gpointer userdata)1127 write_conf (gpointer userdata)
1128 {
1129 McdStorage *storage = MCD_STORAGE (userdata);
1130
1131 DEBUG ("called");
1132 g_source_remove (write_conf_id);
1133 write_conf_id = 0;
1134
1135 mcd_storage_commit (storage, NULL);
1136
1137 return TRUE;
1138 }
1139
1140 static void
release_load_accounts_lock(McdLoadAccountsData * lad)1141 release_load_accounts_lock (McdLoadAccountsData *lad)
1142 {
1143 g_return_if_fail (lad->account_lock > 0);
1144 lad->account_lock--;
1145 DEBUG ("called, count is now %d", lad->account_lock);
1146
1147 if (lad->account_lock == 0)
1148 {
1149 register_dbus_service (lad->account_manager);
1150 g_slice_free (McdLoadAccountsData, lad);
1151 }
1152 }
1153
1154 static void
account_loaded(McdAccount * account,const GError * error,gpointer user_data)1155 account_loaded (McdAccount *account, const GError *error, gpointer user_data)
1156 {
1157 McdLoadAccountsData *lad = user_data;
1158
1159 if (error)
1160 {
1161 g_warning ("%s: got error: %s", G_STRFUNC, error->message);
1162 g_hash_table_remove (lad->account_manager->priv->accounts,
1163 mcd_account_get_unique_name (account));
1164 }
1165
1166 release_load_accounts_lock (lad);
1167 }
1168
1169 static void
uncork_storage_plugins(McdAccountManager * account_manager)1170 uncork_storage_plugins (McdAccountManager *account_manager)
1171 {
1172 McdAccountManagerPrivate *priv = MCD_ACCOUNT_MANAGER_PRIV (account_manager);
1173
1174 mcd_account_manager_write_conf_async (account_manager, NULL, NULL, NULL);
1175 mcd_storage_ready (priv->storage);
1176 }
1177
1178 typedef struct
1179 {
1180 McdAccountManager *self;
1181 McdAccount *account;
1182 McdLoadAccountsData *lad;
1183 } MigrateCtx;
1184
1185 static MigrateCtx *
migrate_ctx_new(McdAccountManager * self,McdAccount * account,McdLoadAccountsData * lad)1186 migrate_ctx_new (McdAccountManager *self,
1187 McdAccount *account,
1188 McdLoadAccountsData *lad)
1189 {
1190 MigrateCtx *ctx = g_slice_new (MigrateCtx);
1191
1192 ctx->self = g_object_ref (self);
1193 ctx->account = g_object_ref (account);
1194 ctx->lad = lad;
1195
1196 /* Lock attempting to migrate the account */
1197 lad->account_lock++;
1198 return ctx;
1199 }
1200
1201 static void
migrate_ctx_free(MigrateCtx * ctx)1202 migrate_ctx_free (MigrateCtx *ctx)
1203 {
1204 g_object_unref (ctx->self);
1205 g_object_unref (ctx->account);
1206 release_load_accounts_lock (ctx->lad);
1207 g_slice_free (MigrateCtx, ctx);
1208 }
1209
1210
1211 static void
migrate_delete_account_cb(McdAccount * account,const GError * error,gpointer user_data)1212 migrate_delete_account_cb (McdAccount *account,
1213 const GError *error,
1214 gpointer user_data)
1215 {
1216 MigrateCtx *ctx = user_data;
1217
1218 migrate_ctx_free (ctx);
1219 }
1220
1221 static void
migrate_create_account_cb(McdAccountManager * account_manager,McdAccount * account,const GError * error,gpointer user_data)1222 migrate_create_account_cb (McdAccountManager *account_manager,
1223 McdAccount *account,
1224 const GError *error,
1225 gpointer user_data)
1226 {
1227 MigrateCtx *ctx = user_data;
1228
1229 if (error != NULL)
1230 {
1231 DEBUG ("Failed to create account: %s", error->message);
1232 _mcd_account_set_enabled (ctx->account, FALSE, TRUE,
1233 MCD_DBUS_PROP_SET_FLAG_NONE, NULL);
1234 migrate_ctx_free (ctx);
1235 return;
1236 }
1237
1238 DEBUG ("Account %s migrated, removing it",
1239 mcd_account_get_unique_name (ctx->account));
1240
1241 mcd_account_delete (ctx->account, migrate_delete_account_cb, ctx);
1242 }
1243
1244 static void
migrate_butterfly_haze_ready(McdManager * manager,const GError * error,gpointer user_data)1245 migrate_butterfly_haze_ready (McdManager *manager,
1246 const GError *error,
1247 gpointer user_data)
1248 {
1249 MigrateCtx *ctx = user_data;
1250 gchar *display_name;
1251 GValue v = G_VALUE_INIT;
1252 GValue password_v = G_VALUE_INIT;
1253 GHashTable *parameters, *properties;
1254 gchar *str;
1255 GPtrArray *supersedes;
1256 GPtrArray *old_supersedes;
1257
1258 if (error != NULL)
1259 {
1260 DEBUG ("Can't find Haze: %s", error->message);
1261 _mcd_account_set_enabled (ctx->account, FALSE, TRUE,
1262 MCD_DBUS_PROP_SET_FLAG_NONE, NULL);
1263 goto error;
1264 }
1265
1266 /* Parameters; the only mandatory one is 'account' */
1267 if (!mcd_account_get_parameter_of_known_type (ctx->account,
1268 "account", G_TYPE_STRING,
1269 &v, NULL))
1270 {
1271 _mcd_account_set_enabled (ctx->account, FALSE, TRUE,
1272 MCD_DBUS_PROP_SET_FLAG_NONE, NULL);
1273 goto error;
1274 }
1275
1276 parameters = g_hash_table_new (g_str_hash, g_str_equal);
1277 g_hash_table_insert (parameters, "account", &v);
1278
1279 /* If MC is storing the password, let's copy that too, so Empathy
1280 * can migrate it somewhere better. */
1281 if (mcd_account_get_parameter_of_known_type (ctx->account,
1282 "password", G_TYPE_STRING,
1283 &password_v, NULL))
1284 {
1285 g_hash_table_insert (parameters, "password", &password_v);
1286 }
1287
1288 display_name = mcd_account_dup_display_name (ctx->account);
1289
1290 /* Properties */
1291 properties = tp_asv_new (NULL, NULL);
1292
1293 str = mcd_account_dup_icon (ctx->account);
1294 if (str != NULL)
1295 tp_asv_take_string (properties, TP_PROP_ACCOUNT_ICON, str);
1296
1297 tp_asv_set_boolean (properties, TP_PROP_ACCOUNT_ENABLED,
1298 mcd_account_is_enabled (ctx->account));
1299
1300 str = mcd_account_dup_nickname (ctx->account);
1301 if (str != NULL)
1302 tp_asv_take_string (properties, TP_PROP_ACCOUNT_NICKNAME, str);
1303
1304 supersedes = g_ptr_array_new ();
1305 old_supersedes = _mcd_account_get_supersedes (ctx->account);
1306
1307 if (old_supersedes != NULL)
1308 {
1309 guint i;
1310
1311 for (i = 0; i < old_supersedes->len; i++)
1312 g_ptr_array_add (supersedes,
1313 g_strdup (g_ptr_array_index (old_supersedes, i)));
1314 }
1315
1316 g_ptr_array_add (supersedes,
1317 g_strdup (mcd_account_get_object_path (ctx->account)));
1318 tp_asv_take_boxed (properties, TP_PROP_ACCOUNT_SUPERSEDES,
1319 TP_ARRAY_TYPE_OBJECT_PATH_LIST, supersedes);
1320
1321 /* Set the service while we're on it */
1322 tp_asv_set_string (properties, TP_PROP_ACCOUNT_SERVICE, "windows-live");
1323
1324 _mcd_account_manager_create_account (ctx->self,
1325 "haze", "msn", display_name,
1326 parameters, properties,
1327 migrate_create_account_cb, ctx, NULL);
1328
1329 g_value_unset (&v);
1330 g_free (display_name);
1331 g_hash_table_unref (parameters);
1332 g_hash_table_unref (properties);
1333 return;
1334
1335 error:
1336 migrate_ctx_free (ctx);
1337 }
1338
1339 static void
butterfly_account_loaded(McdAccount * account,const GError * error,gpointer user_data)1340 butterfly_account_loaded (McdAccount *account,
1341 const GError *error,
1342 gpointer user_data)
1343 {
1344 MigrateCtx *ctx = user_data;
1345 McdMaster *master = mcd_master_get_default ();
1346 McdManager *manager;
1347
1348 if (error != NULL)
1349 goto error;
1350
1351 DEBUG ("Try migrating butterfly account %s",
1352 mcd_account_get_unique_name (account));
1353
1354 /* Check if Haze is installed */
1355 manager = _mcd_master_lookup_manager (master, "haze");
1356 if (manager == NULL)
1357 {
1358 DEBUG ("Can't find Haze");
1359 _mcd_account_set_enabled (account, FALSE, TRUE,
1360 MCD_DBUS_PROP_SET_FLAG_NONE, NULL);
1361 goto error;
1362 }
1363
1364 mcd_manager_call_when_ready (manager, migrate_butterfly_haze_ready, ctx);
1365 return;
1366
1367 error:
1368 migrate_ctx_free (ctx);
1369 }
1370
1371 static void
migrate_butterfly_account(McdAccountManager * self,McdAccount * account,McdLoadAccountsData * lad)1372 migrate_butterfly_account (McdAccountManager *self,
1373 McdAccount *account,
1374 McdLoadAccountsData *lad)
1375 {
1376 MigrateCtx *ctx;
1377
1378 ctx = migrate_ctx_new (self, account, lad);
1379
1380 _mcd_account_load (account, butterfly_account_loaded, ctx);
1381 }
1382
1383 /* Migrate some specific type of account. If something went wrong during the
1384 * migration we disable it. */
1385 static void
migrate_accounts(McdAccountManager * self,McdLoadAccountsData * lad)1386 migrate_accounts (McdAccountManager *self,
1387 McdLoadAccountsData *lad)
1388 {
1389 McdAccountManagerPrivate *priv = self->priv;
1390 GHashTableIter iter;
1391 gpointer v;
1392
1393 g_hash_table_iter_init (&iter, priv->accounts);
1394 while (g_hash_table_iter_next (&iter, NULL, &v))
1395 {
1396 McdAccount *account = v;
1397 TpConnectionManager *cm;
1398
1399 cm = mcd_account_get_cm (account);
1400
1401 if (cm == NULL)
1402 continue;
1403
1404 if (!tp_strdiff (tp_connection_manager_get_name (cm), "butterfly"))
1405 migrate_butterfly_account (self, account, lad);
1406 }
1407 }
1408
1409 /**
1410 * _mcd_account_manager_setup:
1411 * @account_manager: the #McdAccountManager.
1412 *
1413 * This function must be called by the McdMaster; it reads the accounts from
1414 * the config file, and it needs a McdMaster instance to be active.
1415 */
1416 void
_mcd_account_manager_setup(McdAccountManager * account_manager)1417 _mcd_account_manager_setup (McdAccountManager *account_manager)
1418 {
1419 McdAccountManagerPrivate *priv = account_manager->priv;
1420 McdStorage *storage = priv->storage;
1421 McdLoadAccountsData *lad;
1422 gchar **accounts, **name;
1423 GHashTableIter iter;
1424 gpointer v;
1425
1426 tp_list_connection_names (priv->dbus_daemon,
1427 list_connection_names_cb, NULL, NULL,
1428 (GObject *)account_manager);
1429
1430 lad = g_slice_new (McdLoadAccountsData);
1431 lad->account_manager = account_manager;
1432 lad->account_lock = 1; /* will be released at the end of this function */
1433
1434 accounts = mcd_storage_dup_accounts (storage, NULL);
1435
1436 for (name = accounts; *name != NULL; name++)
1437 {
1438 gboolean plausible = FALSE;
1439 const gchar *manager = NULL;
1440 const gchar *protocol = NULL;
1441 McdAccount *account = mcd_account_manager_lookup_account (
1442 account_manager, *name);
1443
1444 if (account != NULL)
1445 {
1446 /* FIXME: this shouldn't really happen */
1447 DEBUG ("already have account %p called '%s'; skipping", account, *name);
1448 continue;
1449 }
1450
1451 account = mcd_account_new (account_manager, *name, priv->minotaur);
1452
1453 if (G_UNLIKELY (!account))
1454 {
1455 g_warning ("%s: account %s failed to instantiate", G_STRFUNC,
1456 *name);
1457 continue;
1458 }
1459
1460 manager = mcd_account_get_manager_name (account);
1461 protocol = mcd_account_get_protocol_name (account);
1462
1463 plausible = !tp_str_empty (manager) && !tp_str_empty (protocol);
1464
1465 if (G_UNLIKELY (!plausible))
1466 {
1467 const gchar *dbg_manager = (manager == NULL) ? "(nil)" : manager;
1468 const gchar *dbg_protocol = (protocol == NULL) ? "(nil)" : protocol;
1469
1470 g_warning ("%s: account %s has implausible manager/protocol: %s/%s",
1471 G_STRFUNC, *name, dbg_manager, dbg_protocol);
1472 g_object_unref (account);
1473 continue;
1474 }
1475
1476 lad->account_lock++;
1477 add_account (lad->account_manager, account, "keyfile");
1478 _mcd_account_load (account, account_loaded, lad);
1479 g_object_unref (account);
1480 }
1481 g_strfreev (accounts);
1482
1483 uncork_storage_plugins (account_manager);
1484
1485 migrate_accounts (account_manager, lad);
1486
1487 release_load_accounts_lock (lad);
1488
1489 g_hash_table_iter_init (&iter, account_manager->priv->accounts);
1490
1491 while (g_hash_table_iter_next (&iter, NULL, &v))
1492 _mcd_account_maybe_autoconnect (v);
1493 }
1494
1495 static void
register_dbus_service(McdAccountManager * account_manager)1496 register_dbus_service (McdAccountManager *account_manager)
1497 {
1498 McdAccountManagerPrivate *priv = account_manager->priv;
1499 GError *error = NULL;
1500
1501 if (priv->dbus_registered)
1502 return;
1503
1504 if (!tp_dbus_daemon_request_name (priv->dbus_daemon,
1505 TP_ACCOUNT_MANAGER_BUS_NAME,
1506 TRUE /* idempotent */, &error))
1507 {
1508 /* FIXME: put in proper error handling when MC gains the ability to
1509 * be the AM or the CD but not both */
1510 g_warning("Failed registering '%s' service: %s",
1511 TP_ACCOUNT_MANAGER_BUS_NAME, error->message);
1512 g_error_free (error);
1513 exit (1);
1514 }
1515
1516 priv->dbus_registered = TRUE;
1517
1518 tp_dbus_daemon_register_object (priv->dbus_daemon,
1519 TP_ACCOUNT_MANAGER_OBJECT_PATH,
1520 account_manager);
1521 }
1522
1523 static void
set_property(GObject * obj,guint prop_id,const GValue * val,GParamSpec * pspec)1524 set_property (GObject *obj, guint prop_id,
1525 const GValue *val, GParamSpec *pspec)
1526 {
1527 McdAccountManager *account_manager = MCD_ACCOUNT_MANAGER (obj);
1528 McdAccountManagerPrivate *priv = account_manager->priv;
1529
1530 switch (prop_id)
1531 {
1532 case PROP_CLIENT_FACTORY:
1533 g_assert (priv->client_factory == NULL); /* construct-only */
1534 priv->client_factory =
1535 TP_SIMPLE_CLIENT_FACTORY (g_value_dup_object (val));
1536 priv->dbus_daemon =
1537 tp_simple_client_factory_get_dbus_daemon (priv->client_factory);
1538 g_object_ref (priv->dbus_daemon);
1539 break;
1540
1541 default:
1542 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
1543 break;
1544 }
1545 }
1546
1547 static void
get_property(GObject * obj,guint prop_id,GValue * val,GParamSpec * pspec)1548 get_property (GObject *obj, guint prop_id,
1549 GValue *val, GParamSpec *pspec)
1550 {
1551 McdAccountManagerPrivate *priv = MCD_ACCOUNT_MANAGER_PRIV (obj);
1552
1553 switch (prop_id)
1554 {
1555 case PROP_DBUS_DAEMON:
1556 g_value_set_object (val, priv->dbus_daemon);
1557 break;
1558 default:
1559 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
1560 break;
1561 }
1562 }
1563
1564 static void
_mcd_account_manager_finalize(GObject * object)1565 _mcd_account_manager_finalize (GObject *object)
1566 {
1567 McdAccountManagerPrivate *priv = MCD_ACCOUNT_MANAGER_PRIV (object);
1568
1569 if (write_conf_id)
1570 {
1571 write_conf (priv->storage);
1572 g_assert (write_conf_id == 0);
1573 }
1574
1575 tp_clear_object (&priv->storage);
1576 g_free (priv->account_connections_dir);
1577 remove (priv->account_connections_file);
1578 g_free (priv->account_connections_file);
1579
1580 g_hash_table_unref (priv->accounts);
1581
1582 G_OBJECT_CLASS (mcd_account_manager_parent_class)->finalize (object);
1583 }
1584
1585 static void
_mcd_account_manager_dispose(GObject * object)1586 _mcd_account_manager_dispose (GObject *object)
1587 {
1588 McdAccountManagerPrivate *priv = MCD_ACCOUNT_MANAGER_PRIV (object);
1589
1590 tp_clear_object (&priv->dbus_daemon);
1591 tp_clear_object (&priv->client_factory);
1592 tp_clear_object (&priv->minotaur);
1593
1594 G_OBJECT_CLASS (mcd_account_manager_parent_class)->dispose (object);
1595 }
1596
1597 static void
mcd_account_manager_class_init(McdAccountManagerClass * klass)1598 mcd_account_manager_class_init (McdAccountManagerClass *klass)
1599 {
1600 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1601 g_type_class_add_private (object_class, sizeof (McdAccountManagerPrivate));
1602
1603 object_class->dispose = _mcd_account_manager_dispose;
1604 object_class->finalize = _mcd_account_manager_finalize;
1605 object_class->set_property = set_property;
1606 object_class->get_property = get_property;
1607 object_class->constructed = _mcd_account_manager_constructed;
1608
1609 g_object_class_install_property
1610 (object_class, PROP_DBUS_DAEMON,
1611 g_param_spec_object ("dbus-daemon", "DBus daemon", "DBus daemon",
1612 TP_TYPE_DBUS_DAEMON,
1613 G_PARAM_READABLE));
1614
1615 g_object_class_install_property
1616 (object_class, PROP_CLIENT_FACTORY,
1617 g_param_spec_object ("client-factory",
1618 "Client factory",
1619 "Client factory",
1620 TP_TYPE_SIMPLE_CLIENT_FACTORY,
1621 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1622 }
1623
1624 static const gchar *
get_connections_cache_dir(void)1625 get_connections_cache_dir (void)
1626 {
1627 const gchar *from_env = g_getenv ("MC_ACCOUNT_DIR");
1628
1629 if (from_env != NULL)
1630 {
1631 return from_env;
1632 }
1633
1634 if ((ACCOUNTS_CACHE_DIR)[0] != '\0')
1635 {
1636 return ACCOUNTS_CACHE_DIR;
1637 }
1638
1639 return g_get_user_cache_dir ();
1640 }
1641
1642 static void
mcd_account_manager_init(McdAccountManager * account_manager)1643 mcd_account_manager_init (McdAccountManager *account_manager)
1644 {
1645 McdAccountManagerPrivate *priv;
1646
1647 priv = G_TYPE_INSTANCE_GET_PRIVATE ((account_manager),
1648 MCD_TYPE_ACCOUNT_MANAGER,
1649 McdAccountManagerPrivate);
1650 account_manager->priv = priv;
1651 }
1652
1653 static void
_mcd_account_manager_constructed(GObject * obj)1654 _mcd_account_manager_constructed (GObject *obj)
1655 {
1656 McdAccountManager *account_manager = MCD_ACCOUNT_MANAGER (obj);
1657 McdAccountManagerPrivate *priv = account_manager->priv;
1658 guint i = 0;
1659 static struct { const gchar *name; GCallback handler; } sig[] =
1660 { { "created", G_CALLBACK (created_cb) },
1661 { "altered", G_CALLBACK (altered_cb) },
1662 { "toggled", G_CALLBACK (toggled_cb) },
1663 { "deleted", G_CALLBACK (deleted_cb) },
1664 { "altered-one", G_CALLBACK (altered_one_cb) },
1665 { "reconnect", G_CALLBACK (reconnect_cb) },
1666 { NULL, NULL } };
1667
1668 DEBUG ("");
1669
1670 priv->minotaur = mcd_connectivity_monitor_new ();
1671
1672 priv->storage = mcd_storage_new (priv->dbus_daemon);
1673 priv->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
1674 NULL, unref_account);
1675
1676 priv->account_connections_dir = g_strdup (get_connections_cache_dir ());
1677 priv->account_connections_file =
1678 g_build_filename (priv->account_connections_dir, ".mc_connections",
1679 NULL);
1680
1681 DEBUG ("loading plugins");
1682 mcd_storage_load (priv->storage);
1683
1684 /* hook up all the storage plugin signals to their handlers: */
1685 for (i = 0; sig[i].name != NULL; i++)
1686 {
1687 mcd_storage_connect_signal (sig[i].name, sig[i].handler,
1688 account_manager);
1689 }
1690
1691 /* initializes the interfaces */
1692 mcd_dbus_init_interfaces_instances (account_manager);
1693 }
1694
1695 McdAccountManager *
mcd_account_manager_new(TpSimpleClientFactory * client_factory)1696 mcd_account_manager_new (TpSimpleClientFactory *client_factory)
1697 {
1698 gpointer *obj;
1699
1700 obj = g_object_new (MCD_TYPE_ACCOUNT_MANAGER,
1701 "client-factory", client_factory,
1702 NULL);
1703 return MCD_ACCOUNT_MANAGER (obj);
1704 }
1705
1706 /**
1707 * mcd_account_manager_get_dbus_daemon:
1708 * @account_manager: the #McdAccountManager.
1709 *
1710 * Returns: the #TpDBusDaemon.
1711 */
1712 TpDBusDaemon *
mcd_account_manager_get_dbus_daemon(McdAccountManager * account_manager)1713 mcd_account_manager_get_dbus_daemon (McdAccountManager *account_manager)
1714 {
1715 g_return_val_if_fail (MCD_IS_ACCOUNT_MANAGER (account_manager), NULL);
1716
1717 return account_manager->priv->dbus_daemon;
1718 }
1719
1720 McdConnectivityMonitor *
mcd_account_manager_get_connectivity_monitor(McdAccountManager * self)1721 mcd_account_manager_get_connectivity_monitor (McdAccountManager *self)
1722 {
1723 g_return_val_if_fail (MCD_IS_ACCOUNT_MANAGER (self), NULL);
1724 return self->priv->minotaur;
1725 }
1726
1727 /**
1728 * McdAccountManagerWriteConfCb:
1729 * @account_manager: the #McdAccountManager
1730 * @error: a set #GError on failure or %NULL if there was no error
1731 * @user_data: user data
1732 *
1733 * The callback from mcd_account_manager_write_conf_async(). If the config
1734 * writing was successful, @error will be %NULL, otherwise it will be set
1735 * with the appropriate error.
1736 */
1737
1738 /**
1739 * mcd_account_manager_write_conf_async:
1740 * @account_manager: the #McdAccountManager
1741 * @account: the account to be written, or %NULL to flush all accounts
1742 * @callback: a callback to be called on write success or failure
1743 * @user_data: data to be passed to @callback
1744 *
1745 * Write the account manager configuration to disk.
1746 */
1747 void
mcd_account_manager_write_conf_async(McdAccountManager * account_manager,McdAccount * account,McdAccountManagerWriteConfCb callback,gpointer user_data)1748 mcd_account_manager_write_conf_async (McdAccountManager *account_manager,
1749 McdAccount *account,
1750 McdAccountManagerWriteConfCb callback,
1751 gpointer user_data)
1752 {
1753 McdStorage *storage = NULL;
1754 const gchar *account_name = NULL;
1755
1756 g_return_if_fail (MCD_IS_ACCOUNT_MANAGER (account_manager));
1757
1758 storage = account_manager->priv->storage;
1759
1760 if (account != NULL)
1761 {
1762 account_name = mcd_account_get_unique_name (account);
1763
1764 DEBUG ("updating %s", account_name);
1765 mcd_storage_commit (storage, account_name);
1766 }
1767 else
1768 {
1769 GStrv groups;
1770 gsize n_accounts = 0;
1771
1772 groups = mcd_storage_dup_accounts (storage, &n_accounts);
1773 DEBUG ("updating all %" G_GSIZE_FORMAT " accounts", n_accounts);
1774
1775 mcd_storage_commit (storage, NULL);
1776
1777 g_strfreev (groups);
1778 }
1779
1780 if (callback != NULL)
1781 callback (account_manager, NULL, user_data);
1782 }
1783
1784 GHashTable *
_mcd_account_manager_get_accounts(McdAccountManager * account_manager)1785 _mcd_account_manager_get_accounts (McdAccountManager *account_manager)
1786 {
1787 return account_manager->priv->accounts;
1788 }
1789
1790 McdAccount *
mcd_account_manager_lookup_account(McdAccountManager * account_manager,const gchar * name)1791 mcd_account_manager_lookup_account (McdAccountManager *account_manager,
1792 const gchar *name)
1793 {
1794 McdAccountManagerPrivate *priv = account_manager->priv;
1795
1796 return g_hash_table_lookup (priv->accounts, name);
1797 }
1798
1799 McdAccount *
mcd_account_manager_lookup_account_by_path(McdAccountManager * account_manager,const gchar * object_path)1800 mcd_account_manager_lookup_account_by_path (McdAccountManager *account_manager,
1801 const gchar *object_path)
1802 {
1803 McdAccountManagerPrivate *priv = account_manager->priv;
1804
1805 if (!g_str_has_prefix (object_path, TP_ACCOUNT_OBJECT_PATH_BASE))
1806 {
1807 /* can't possibly be right */
1808 return NULL;
1809 }
1810
1811 return g_hash_table_lookup (priv->accounts,
1812 object_path + strlen (TP_ACCOUNT_OBJECT_PATH_BASE));
1813 }
1814
1815 /*
1816 * _mcd_account_manager_store_account_connections:
1817 * @account_manager: the #McdAccountManager.
1818 *
1819 * This function is used to remember what connection an account was bound to.
1820 * The data is stored in a temporary file, and can be read when MC restarts
1821 * after a crash.
1822 */
1823 static void
_mcd_account_manager_store_account_connections(McdAccountManager * manager)1824 _mcd_account_manager_store_account_connections (McdAccountManager *manager)
1825 {
1826 McdAccountManagerPrivate *priv;
1827 GHashTableIter iter;
1828 const gchar *account_name, *connection_path, *connection_name;
1829 McdAccount *account;
1830 FILE *file;
1831
1832 g_return_if_fail (MCD_IS_ACCOUNT_MANAGER (manager));
1833 priv = manager->priv;
1834
1835 /* make $XDG_CACHE_DIR (or whatever) if it doesn't exist */
1836 g_mkdir_with_parents (priv->account_connections_dir, 0700);
1837 _mcd_chmod_private (priv->account_connections_dir);
1838
1839 file = fopen (priv->account_connections_file, "w");
1840 if (G_UNLIKELY (!file)) return;
1841
1842 g_hash_table_iter_init (&iter, priv->accounts);
1843 while (g_hash_table_iter_next (&iter, (gpointer)&account_name,
1844 (gpointer)&account))
1845 {
1846 McdConnection *connection;
1847
1848 connection = mcd_account_get_connection (account);
1849 if (connection)
1850 {
1851 connection_path = mcd_connection_get_object_path (connection);
1852 connection_name = mcd_connection_get_name (connection);
1853 if (connection_path && connection_name)
1854 fprintf (file, "%s\t%s\t%s\n",
1855 connection_path, connection_name, account_name);
1856 }
1857 }
1858 fclose (file);
1859 }
1860
1861 McdStorage *
mcd_account_manager_get_storage(McdAccountManager * account_manager)1862 mcd_account_manager_get_storage (McdAccountManager *account_manager)
1863 {
1864 return account_manager->priv->storage;
1865 }
1866
1867