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 © 2008–2010 Nokia Corporation.
7 * Copyright © 2009–2013 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 #include "mcd-account.h"
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include <dbus/dbus.h>
32 #include <glib/gstdio.h>
33 #include <telepathy-glib/telepathy-glib.h>
34 #include <telepathy-glib/telepathy-glib-dbus.h>
35
36 #include "mcd-account-priv.h"
37 #include "mcd-account-conditions.h"
38 #include "mcd-account-manager-priv.h"
39 #include "mcd-account-addressing.h"
40 #include "mcd-connection-priv.h"
41 #include "mcd-misc.h"
42 #include "mcd-manager.h"
43 #include "mcd-manager-priv.h"
44 #include "mcd-master.h"
45 #include "mcd-master-priv.h"
46 #include "mcd-dbusprop.h"
47
48 #include "_gen/interfaces.h"
49 #include "_gen/enums.h"
50 #include "_gen/gtypes.h"
51 #include "_gen/cli-Connection_Manager_Interface_Account_Storage-body.h"
52
53 #define MC_OLD_AVATAR_FILENAME "avatar.bin"
54
55 #define MCD_ACCOUNT_PRIV(account) (MCD_ACCOUNT (account)->priv)
56
57 static void account_iface_init (TpSvcAccountClass *iface,
58 gpointer iface_data);
59 static void properties_iface_init (TpSvcDBusPropertiesClass *iface,
60 gpointer iface_data);
61 static void account_avatar_iface_init (TpSvcAccountInterfaceAvatarClass *iface,
62 gpointer iface_data);
63 static void account_storage_iface_init (
64 TpSvcAccountInterfaceStorageClass *iface,
65 gpointer iface_data);
66 static void account_hidden_iface_init (
67 McSvcAccountInterfaceHiddenClass *iface,
68 gpointer iface_data);
69 static void account_external_password_storage_iface_init (
70 McSvcAccountInterfaceExternalPasswordStorageClass *iface,
71 gpointer iface_data);
72
73 static const McdDBusProp account_properties[];
74 static const McdDBusProp account_avatar_properties[];
75 static const McdDBusProp account_storage_properties[];
76 static const McdDBusProp account_hidden_properties[];
77 static const McdDBusProp account_external_password_storage_properties[];
78
79 static const McdInterfaceData account_interfaces[] = {
80 MCD_IMPLEMENT_IFACE (tp_svc_account_get_type, account, TP_IFACE_ACCOUNT),
81 MCD_IMPLEMENT_IFACE (tp_svc_account_interface_avatar_get_type,
82 account_avatar,
83 TP_IFACE_ACCOUNT_INTERFACE_AVATAR),
84 MCD_IMPLEMENT_IFACE (mc_svc_account_interface_conditions_get_type,
85 account_conditions,
86 MC_IFACE_ACCOUNT_INTERFACE_CONDITIONS),
87 MCD_IMPLEMENT_IFACE (tp_svc_account_interface_storage_get_type,
88 account_storage,
89 TP_IFACE_ACCOUNT_INTERFACE_STORAGE),
90 MCD_IMPLEMENT_IFACE (tp_svc_account_interface_addressing_get_type,
91 account_addressing,
92 TP_IFACE_ACCOUNT_INTERFACE_ADDRESSING),
93 MCD_IMPLEMENT_IFACE (mc_svc_account_interface_hidden_get_type,
94 account_hidden,
95 MC_IFACE_ACCOUNT_INTERFACE_HIDDEN),
96 MCD_IMPLEMENT_OPTIONAL_IFACE (
97 mc_svc_account_interface_external_password_storage_get_type,
98 account_external_password_storage,
99 MC_IFACE_ACCOUNT_INTERFACE_EXTERNAL_PASSWORD_STORAGE),
100
101 { NULL, }
102 };
103
104 G_DEFINE_TYPE_WITH_CODE (McdAccount, mcd_account, G_TYPE_OBJECT,
105 MCD_DBUS_INIT_INTERFACES (account_interfaces);
106 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
107 properties_iface_init);
108 )
109
110 typedef struct {
111 McdOnlineRequestCb callback;
112 gpointer user_data;
113 } McdOnlineRequestData;
114
115 struct _McdAccountPrivate
116 {
117 gchar *unique_name;
118 gchar *object_path;
119 gchar *manager_name;
120 gchar *protocol_name;
121
122 TpConnection *tp_connection;
123 TpContact *self_contact;
124 McdConnection *connection;
125 McdManager *manager;
126
127 McdStorage *storage;
128 TpDBusDaemon *dbus_daemon;
129 gboolean registered;
130 McdConnectivityMonitor *connectivity;
131
132 McdAccountConnectionContext *connection_context;
133 GKeyFile *keyfile; /* configuration file */
134 McpAccountStorage *storage_plugin;
135 GPtrArray *supersedes;
136
137 /* connection status */
138 TpConnectionStatus conn_status;
139 TpConnectionStatusReason conn_reason;
140 gchar *conn_dbus_error;
141 GHashTable *conn_error_details;
142
143 /* current presence fields */
144 TpConnectionPresenceType curr_presence_type;
145 gchar *curr_presence_status;
146 gchar *curr_presence_message;
147
148 /* requested presence fields */
149 TpConnectionPresenceType req_presence_type;
150 gchar *req_presence_status;
151 gchar *req_presence_message;
152
153 /* automatic presence fields */
154 TpConnectionPresenceType auto_presence_type;
155 gchar *auto_presence_status; /* TODO: consider loading these from the
156 configuration file as needed */
157 gchar *auto_presence_message;
158
159 GList *online_requests; /* list of McdOnlineRequestData structures
160 (callback with user data) to be called when the
161 account will be online */
162
163 /* %NULL if the account is valid; a valid error for reporting over the
164 * D-Bus if the account is invalid.
165 */
166 GError *invalid_reason;
167
168 gboolean connect_automatically;
169 gboolean enabled;
170 gboolean loaded;
171 gboolean has_been_online;
172 gboolean removed;
173 gboolean always_on;
174 gboolean changing_presence;
175 gboolean setting_avatar;
176 gboolean waiting_for_initial_avatar;
177 gboolean waiting_for_connectivity;
178
179 gboolean hidden;
180 /* In addition to affecting dispatching, this flag also makes this
181 * account bypass connectivity checks. */
182 gboolean always_dispatch;
183
184 /* These fields are used to cache the changed properties */
185 gboolean properties_frozen;
186 GHashTable *changed_properties;
187 guint properties_source;
188
189 gboolean password_saved;
190 };
191
192 enum
193 {
194 PROP_0,
195 PROP_DBUS_DAEMON,
196 PROP_CONNECTIVITY_MONITOR,
197 PROP_STORAGE,
198 PROP_NAME,
199 PROP_ALWAYS_ON,
200 PROP_HIDDEN,
201 };
202
203 enum
204 {
205 VALIDITY_CHANGED,
206 CONNECTION_PATH_CHANGED,
207 LAST_SIGNAL
208 };
209
210 static guint _mcd_account_signals[LAST_SIGNAL] = { 0 };
211 static GQuark account_ready_quark = 0;
212
213 GQuark
mcd_account_error_quark(void)214 mcd_account_error_quark (void)
215 {
216 static GQuark quark = 0;
217
218 if (quark == 0)
219 quark = g_quark_from_static_string ("mcd-account-error");
220
221 return quark;
222 }
223
224 /*
225 * _mcd_account_maybe_autoconnect:
226 * @account: the #McdAccount.
227 *
228 * Check whether automatic connection should happen (and attempt it if needed).
229 */
230 void
_mcd_account_maybe_autoconnect(McdAccount * account)231 _mcd_account_maybe_autoconnect (McdAccount *account)
232 {
233 McdAccountPrivate *priv;
234
235 g_return_if_fail (MCD_IS_ACCOUNT (account));
236 priv = account->priv;
237
238 if (!mcd_account_would_like_to_connect (account))
239 {
240 return;
241 }
242
243 if (_mcd_account_needs_dispatch (account))
244 {
245 DEBUG ("Always-dispatchable account %s needs no transport",
246 priv->unique_name);
247 }
248 else if (mcd_connectivity_monitor_is_online (priv->connectivity))
249 {
250 DEBUG ("Account %s has connectivity, connecting",
251 priv->unique_name);
252 }
253 else
254 {
255 DEBUG ("Account %s needs connectivity, not connecting",
256 priv->unique_name);
257 }
258
259 DEBUG ("connecting account %s", priv->unique_name);
260 _mcd_account_connect_with_auto_presence (account, FALSE);
261 }
262
263 static gboolean
value_is_same(const GValue * val1,const GValue * val2)264 value_is_same (const GValue *val1, const GValue *val2)
265 {
266 g_return_val_if_fail (val1 != NULL && val2 != NULL, FALSE);
267 switch (G_VALUE_TYPE (val1))
268 {
269 case G_TYPE_STRING:
270 return g_strcmp0 (g_value_get_string (val1),
271 g_value_get_string (val2)) == 0;
272 case G_TYPE_CHAR:
273 case G_TYPE_UCHAR:
274 case G_TYPE_INT:
275 case G_TYPE_UINT:
276 case G_TYPE_BOOLEAN:
277 return val1->data[0].v_uint == val2->data[0].v_uint;
278
279 case G_TYPE_INT64:
280 return g_value_get_int64 (val1) == g_value_get_int64 (val2);
281 case G_TYPE_UINT64:
282 return g_value_get_uint64 (val1) == g_value_get_uint64 (val2);
283
284 case G_TYPE_DOUBLE:
285 return g_value_get_double (val1) == g_value_get_double (val2);
286
287 default:
288 if (G_VALUE_TYPE (val1) == DBUS_TYPE_G_OBJECT_PATH)
289 {
290 return !tp_strdiff (g_value_get_boxed (val1),
291 g_value_get_boxed (val2));
292 }
293 else if (G_VALUE_TYPE (val1) == G_TYPE_STRV)
294 {
295 gchar **left = g_value_get_boxed (val1);
296 gchar **right = g_value_get_boxed (val2);
297
298 if (left == NULL || right == NULL ||
299 *left == NULL || *right == NULL)
300 {
301 return ((left == NULL || *left == NULL) &&
302 (right == NULL || *right == NULL));
303 }
304
305 while (*left != NULL || *right != NULL)
306 {
307 if (tp_strdiff (*left, *right))
308 {
309 return FALSE;
310 }
311
312 left++;
313 right++;
314 }
315
316 return TRUE;
317 }
318 else
319 {
320 g_warning ("%s: unexpected type %s",
321 G_STRFUNC, G_VALUE_TYPE_NAME (val1));
322 return FALSE;
323 }
324 }
325 }
326
327 static void
mcd_account_loaded(McdAccount * account)328 mcd_account_loaded (McdAccount *account)
329 {
330 g_return_if_fail (!account->priv->loaded);
331 account->priv->loaded = TRUE;
332
333 /* invoke all the callbacks */
334 g_object_ref (account);
335
336 _mcd_object_ready (account, account_ready_quark, NULL);
337
338 if (account->priv->online_requests != NULL)
339 {
340 /* if we have established that the account is not valid or is
341 * disabled, cancel all requests */
342 if (!mcd_account_is_valid (account) || !account->priv->enabled)
343 {
344 /* FIXME: pick better errors and put them in telepathy-spec? */
345 GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE,
346 "account isn't Valid (not enough information to put it "
347 "online)" };
348 GList *list;
349
350 if (mcd_account_is_valid (account))
351 {
352 e.message = "account isn't Enabled";
353 }
354
355 list = account->priv->online_requests;
356 account->priv->online_requests = NULL;
357
358 for (/* already initialized */ ;
359 list != NULL;
360 list = g_list_delete_link (list, list))
361 {
362 McdOnlineRequestData *data = list->data;
363
364 data->callback (account, data->user_data, &e);
365 g_slice_free (McdOnlineRequestData, data);
366 }
367 }
368
369 /* otherwise, we want to go online now */
370 if (account->priv->conn_status == TP_CONNECTION_STATUS_DISCONNECTED)
371 {
372 _mcd_account_connect_with_auto_presence (account, TRUE);
373 }
374 }
375
376 _mcd_account_maybe_autoconnect (account);
377
378 g_object_unref (account);
379 }
380
381 /*
382 * _mcd_account_set_parameter:
383 * @account: the #McdAccount.
384 * @name: the parameter name.
385 * @value: a #GValue with the value to set, or %NULL.
386 *
387 * Sets the parameter @name to the value in @value. If @value, is %NULL, the
388 * parameter is unset.
389 */
390 static void
_mcd_account_set_parameter(McdAccount * account,const gchar * name,const GValue * value)391 _mcd_account_set_parameter (McdAccount *account, const gchar *name,
392 const GValue *value)
393 {
394 McdAccountPrivate *priv = account->priv;
395 McdStorage *storage = priv->storage;
396 const gchar *account_name = mcd_account_get_unique_name (account);
397 gboolean secret = mcd_account_parameter_is_secret (account, name);
398
399 mcd_storage_set_parameter (storage, account_name, name, value, secret);
400 }
401
402 static GType mc_param_type (const TpConnectionManagerParam *param);
403
404 /**
405 * mcd_account_get_parameter:
406 * @account: the #McdAccount.
407 * @name: the parameter name.
408 * @parameter: location at which to store the parameter's current value, or
409 * %NULL if you don't actually care about the parameter's value.
410 * @error: location at which to store an error if the parameter cannot be
411 * retrieved.
412 *
413 * Get the @name parameter for @account.
414 *
415 * Returns: %TRUE if the parameter could be retrieved; %FALSE otherwise
416 */
417 gboolean
mcd_account_get_parameter(McdAccount * account,const gchar * name,GValue * parameter,GError ** error)418 mcd_account_get_parameter (McdAccount *account, const gchar *name,
419 GValue *parameter,
420 GError **error)
421 {
422 McdAccountPrivate *priv = account->priv;
423 const TpConnectionManagerParam *param;
424 GType type;
425
426 param = mcd_manager_get_protocol_param (priv->manager,
427 priv->protocol_name, name);
428 type = mc_param_type (param);
429
430 return mcd_account_get_parameter_of_known_type (account, name,
431 type, parameter, error);
432 }
433
434 gboolean
mcd_account_get_parameter_of_known_type(McdAccount * account,const gchar * name,GType type,GValue * parameter,GError ** error)435 mcd_account_get_parameter_of_known_type (McdAccount *account,
436 const gchar *name,
437 GType type,
438 GValue *parameter,
439 GError **error)
440 {
441 const gchar *account_name = mcd_account_get_unique_name (account);
442 McdStorage *storage = account->priv->storage;
443 GValue tmp = G_VALUE_INIT;
444
445 g_value_init (&tmp, type);
446
447 if (mcd_storage_get_parameter (storage, account_name, name, &tmp, error))
448 {
449 if (parameter != NULL)
450 {
451 memcpy (parameter, &tmp, sizeof (tmp));
452 }
453 else
454 {
455 g_value_unset (&tmp);
456 }
457
458 return TRUE;
459 }
460
461 g_value_unset (&tmp);
462 return FALSE;
463 }
464
465 typedef void (*CheckParametersCb) (
466 McdAccount *account,
467 const GError *invalid_reason,
468 gpointer user_data);
469 static void mcd_account_check_parameters (McdAccount *account,
470 CheckParametersCb callback, gpointer user_data);
471
472 static void
manager_ready_check_params_cb(McdAccount * account,const GError * invalid_reason,gpointer user_data)473 manager_ready_check_params_cb (McdAccount *account,
474 const GError *invalid_reason,
475 gpointer user_data)
476 {
477 McdAccountPrivate *priv = account->priv;
478
479 g_clear_error (&priv->invalid_reason);
480 if (invalid_reason != NULL)
481 {
482 priv->invalid_reason = g_error_copy (invalid_reason);
483 }
484
485 mcd_account_loaded (account);
486 }
487
488 static void
account_external_password_storage_get_accounts_cb(TpProxy * cm,const GValue * value,const GError * in_error,gpointer user_data,GObject * self)489 account_external_password_storage_get_accounts_cb (TpProxy *cm,
490 const GValue *value,
491 const GError *in_error,
492 gpointer user_data,
493 GObject *self)
494 {
495 McdAccount *account = MCD_ACCOUNT (self);
496 const char *account_id = user_data;
497 GHashTable *map, *props;
498
499 if (in_error != NULL)
500 {
501 DEBUG ("Failed to get Account property: %s", in_error->message);
502 return;
503 }
504
505 g_return_if_fail (G_VALUE_HOLDS (value, MC_HASH_TYPE_ACCOUNT_FLAGS_MAP));
506
507 map = g_value_get_boxed (value);
508
509 account->priv->password_saved =
510 GPOINTER_TO_UINT (g_hash_table_lookup (map, account_id)) &
511 MC_ACCOUNT_FLAG_CREDENTIALS_STORED;
512
513 DEBUG ("PasswordSaved = %u", account->priv->password_saved);
514
515 /* emit the changed signal */
516 props = tp_asv_new (
517 "PasswordSaved", G_TYPE_BOOLEAN, account->priv->password_saved,
518 NULL);
519
520 tp_svc_dbus_properties_emit_properties_changed (account,
521 MC_IFACE_ACCOUNT_INTERFACE_EXTERNAL_PASSWORD_STORAGE,
522 props,
523 NULL);
524
525 g_hash_table_unref (props);
526 }
527
528 static void
account_setup_identify_account_cb(TpProxy * protocol,const char * account_id,const GError * in_error,gpointer user_data,GObject * self)529 account_setup_identify_account_cb (TpProxy *protocol,
530 const char *account_id,
531 const GError *in_error,
532 gpointer user_data,
533 GObject *self)
534 {
535 McdAccount *account = MCD_ACCOUNT (self);
536 TpConnectionManager *cm = mcd_account_get_cm (account);
537
538 if (in_error != NULL)
539 {
540 DEBUG ("Error identifying account: %s", in_error->message);
541 return;
542 }
543
544 DEBUG ("Identified account as %s", account_id);
545
546 /* look up the current value of the CM.I.AS.Accounts property
547 * and monitor future changes */
548 tp_cli_dbus_properties_call_get (cm, -1,
549 MC_IFACE_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE,
550 "Accounts",
551 account_external_password_storage_get_accounts_cb,
552 g_strdup (account_id), g_free, G_OBJECT (account));
553 }
554
555 static void
account_external_password_storage_properties_changed_cb(TpProxy * cm,const char * iface,GHashTable * changed_properties,const char ** invalidated_properties,gpointer user_data,GObject * self)556 account_external_password_storage_properties_changed_cb (TpProxy *cm,
557 const char *iface,
558 GHashTable *changed_properties,
559 const char **invalidated_properties,
560 gpointer user_data,
561 GObject *self)
562 {
563 McdAccount *account = MCD_ACCOUNT (self);
564 TpProtocol *protocol = tp_connection_manager_get_protocol_object (
565 TP_CONNECTION_MANAGER (cm), account->priv->protocol_name);
566 GHashTable *params;
567
568 if (tp_strdiff (iface,
569 MC_IFACE_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE))
570 return;
571
572 /* look up account identity so we can look up our value in
573 * the Accounts map */
574 params = _mcd_account_dup_parameters (account);
575 tp_cli_protocol_call_identify_account (protocol, -1, params,
576 account_setup_identify_account_cb,
577 NULL, NULL, G_OBJECT (account));
578
579 g_hash_table_unref (params);
580 }
581
on_manager_ready(McdManager * manager,const GError * error,gpointer user_data)582 static void on_manager_ready (McdManager *manager, const GError *error,
583 gpointer user_data)
584 {
585 McdAccount *account = MCD_ACCOUNT (user_data);
586
587 if (error)
588 {
589 DEBUG ("got error: %s", error->message);
590 mcd_account_loaded (account);
591 }
592 else
593 {
594 TpConnectionManager *cm = mcd_manager_get_tp_proxy (manager);
595
596 mcd_account_check_parameters (account, manager_ready_check_params_cb,
597 NULL);
598
599 /* determine if we support Acct.I.ExternalPasswordStorage */
600 if (tp_proxy_has_interface_by_id (cm,
601 MC_IFACE_QUARK_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE))
602 {
603 TpProtocol *protocol = tp_connection_manager_get_protocol_object (
604 cm, account->priv->protocol_name);
605 GHashTable *params;
606
607 DEBUG ("CM %s has CM.I.AccountStorage iface",
608 mcd_manager_get_name (manager));
609
610 mcd_dbus_activate_optional_interface (
611 TP_SVC_DBUS_PROPERTIES (account),
612 MC_TYPE_SVC_ACCOUNT_INTERFACE_EXTERNAL_PASSWORD_STORAGE);
613
614 /* look up account identity so we can look up our value in
615 * the Accounts map */
616 params = _mcd_account_dup_parameters (account);
617 tp_cli_protocol_call_identify_account (protocol, -1, params,
618 account_setup_identify_account_cb,
619 NULL, NULL, G_OBJECT (account));
620
621 tp_cli_dbus_properties_connect_to_properties_changed (cm,
622 account_external_password_storage_properties_changed_cb,
623 NULL, NULL, G_OBJECT (account), NULL);
624
625 g_hash_table_unref (params);
626 }
627 }
628 }
629
630 static gboolean
load_manager(McdAccount * account)631 load_manager (McdAccount *account)
632 {
633 McdAccountPrivate *priv = account->priv;
634 McdMaster *master;
635
636 if (G_UNLIKELY (!priv->manager_name)) return FALSE;
637 master = mcd_master_get_default ();
638 priv->manager = _mcd_master_lookup_manager (master, priv->manager_name);
639 if (priv->manager)
640 {
641 g_object_ref (priv->manager);
642 mcd_manager_call_when_ready (priv->manager, on_manager_ready, account);
643 return TRUE;
644 }
645 else
646 return FALSE;
647 }
648
649 /* Returns the data dir for the given account name.
650 * Returned string must be freed by caller. */
651 static gchar *
get_old_account_data_path(McdAccountPrivate * priv)652 get_old_account_data_path (McdAccountPrivate *priv)
653 {
654 const gchar *base;
655
656 base = g_getenv ("MC_ACCOUNT_DIR");
657 if (!base)
658 base = ACCOUNTS_DIR;
659 if (!base)
660 return NULL;
661
662 if (base[0] == '~')
663 return g_build_filename (g_get_home_dir(), base + 1,
664 priv->unique_name, NULL);
665 else
666 return g_build_filename (base, priv->unique_name, NULL);
667 }
668
669 static void
account_delete_identify_account_cb(TpProxy * protocol,const char * account_id,const GError * in_error,gpointer user_data,GObject * self)670 account_delete_identify_account_cb (TpProxy *protocol,
671 const char *account_id,
672 const GError *in_error,
673 gpointer user_data,
674 GObject *self)
675 {
676 McdAccount *account = MCD_ACCOUNT (self);
677 TpConnectionManager *cm = mcd_account_get_cm (account);
678
679 if (in_error != NULL)
680 {
681 DEBUG ("Error identifying account: %s", in_error->message);
682 }
683 else
684 {
685 DEBUG ("Identified account as %s", account_id);
686
687 mc_cli_connection_manager_interface_account_storage_call_remove_account (
688 cm, -1, account_id,
689 NULL, NULL, NULL, NULL);
690 }
691
692 g_object_unref (account);
693 }
694
695 static void
unregister_dbus_service(McdAccount * self)696 unregister_dbus_service (McdAccount *self)
697 {
698 DBusGConnection *dbus_connection;
699
700 g_return_if_fail (MCD_IS_ACCOUNT (self));
701
702 if (!self->priv->registered)
703 return;
704
705 dbus_connection = tp_proxy_get_dbus_connection (self->priv->dbus_daemon);
706 dbus_g_connection_unregister_g_object (dbus_connection, (GObject *) self);
707
708 self->priv->registered = FALSE;
709 }
710
711 void
mcd_account_delete(McdAccount * account,McdAccountDeleteCb callback,gpointer user_data)712 mcd_account_delete (McdAccount *account,
713 McdAccountDeleteCb callback,
714 gpointer user_data)
715 {
716 McdAccountPrivate *priv = account->priv;
717 gchar *data_dir_str;
718 GError *error = NULL;
719 const gchar *name = mcd_account_get_unique_name (account);
720 TpConnectionManager *cm = mcd_account_get_cm (account);
721
722 /* if the CM implements CM.I.AccountStorage, we need to tell the CM
723 * to forget any account credentials it knows */
724 if (tp_proxy_has_interface_by_id (cm,
725 MC_IFACE_QUARK_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE))
726 {
727 TpProtocol *protocol;
728 GHashTable *params;
729
730 /* identify the account */
731 protocol = tp_connection_manager_get_protocol_object (cm,
732 account->priv->protocol_name);
733 params = _mcd_account_dup_parameters (account);
734
735 tp_cli_protocol_call_identify_account (protocol, -1, params,
736 account_delete_identify_account_cb,
737 NULL, NULL, g_object_ref (account));
738
739 g_hash_table_unref (params);
740 }
741
742 /* got to turn the account off before removing it, otherwise we can *
743 * end up with an orphaned CM holding the account online */
744 if (!_mcd_account_set_enabled (account, FALSE, FALSE,
745 MCD_DBUS_PROP_SET_FLAG_NONE, &error))
746 {
747 g_warning ("could not disable account %s (%s)", name, error->message);
748 callback (account, error, user_data);
749 g_error_free (error);
750 return;
751 }
752
753 mcd_storage_delete_account (priv->storage, name);
754
755 data_dir_str = get_old_account_data_path (priv);
756
757 if (data_dir_str != NULL)
758 {
759 GDir *data_dir = g_dir_open (data_dir_str, 0, NULL);
760
761 if (data_dir)
762 {
763 const gchar *filename;
764
765 while ((filename = g_dir_read_name (data_dir)) != NULL)
766 {
767 gchar *path = g_build_filename (data_dir_str, filename, NULL);
768
769 g_remove (path);
770 g_free (path);
771 }
772
773 g_dir_close (data_dir);
774 g_rmdir (data_dir_str);
775 }
776
777 g_free (data_dir_str);
778 }
779
780 mcd_storage_commit (priv->storage, name);
781
782 /* The callback may drop the latest ref on @account so make sure it stays
783 * alive while we still need it. */
784 g_object_ref (account);
785
786 if (callback != NULL)
787 callback (account, NULL, user_data);
788
789 /* If the account was not removed via the DBus Account interface code *
790 * path and something is holding a ref to it so it does not get disposed, *
791 * then this signal may not get fired, so we make sure it _does_ here */
792 if (!priv->removed)
793 {
794 DEBUG ("Forcing Account.Removed for %s", name);
795 priv->removed = TRUE;
796 tp_svc_account_emit_removed (account);
797 }
798
799 unregister_dbus_service (account);
800 g_object_unref (account);
801 }
802
803 void
_mcd_account_load(McdAccount * account,McdAccountLoadCb callback,gpointer user_data)804 _mcd_account_load (McdAccount *account, McdAccountLoadCb callback,
805 gpointer user_data)
806 {
807 if (account->priv->loaded)
808 callback (account, NULL, user_data);
809 else
810 _mcd_object_call_when_ready (account, account_ready_quark,
811 (McdReadyCb)callback, user_data);
812 }
813
814 static void
on_connection_abort(McdConnection * connection,McdAccount * account)815 on_connection_abort (McdConnection *connection, McdAccount *account)
816 {
817 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
818
819 DEBUG ("called (%p, account %s)", connection, priv->unique_name);
820 _mcd_account_set_connection (account, NULL);
821 }
822
823 static void mcd_account_changed_property (McdAccount *account,
824 const gchar *key, const GValue *value);
825
826 static void
mcd_account_request_presence_int(McdAccount * account,TpConnectionPresenceType type,const gchar * status,const gchar * message,gboolean user_initiated)827 mcd_account_request_presence_int (McdAccount *account,
828 TpConnectionPresenceType type,
829 const gchar *status,
830 const gchar *message,
831 gboolean user_initiated)
832 {
833 McdAccountPrivate *priv = account->priv;
834 gboolean changed = FALSE;
835
836 if (priv->req_presence_type != type)
837 {
838 priv->req_presence_type = type;
839 changed = TRUE;
840 }
841
842 if (tp_strdiff (priv->req_presence_status, status))
843 {
844 g_free (priv->req_presence_status);
845 priv->req_presence_status = g_strdup (status);
846 changed = TRUE;
847 }
848
849 if (tp_strdiff (priv->req_presence_message, message))
850 {
851 g_free (priv->req_presence_message);
852 priv->req_presence_message = g_strdup (message);
853 changed = TRUE;
854 }
855
856 if (changed)
857 {
858 GValue value = G_VALUE_INIT;
859
860 g_value_init (&value, TP_STRUCT_TYPE_SIMPLE_PRESENCE);
861 g_value_take_boxed (&value,
862 tp_value_array_build (3,
863 G_TYPE_UINT, type,
864 G_TYPE_STRING, status,
865 G_TYPE_STRING, message,
866 G_TYPE_INVALID));
867 mcd_account_changed_property (account, "RequestedPresence", &value);
868 g_value_unset (&value);
869 }
870
871 DEBUG ("Requested presence: %u %s %s",
872 priv->req_presence_type,
873 priv->req_presence_status,
874 priv->req_presence_message);
875
876 if (type >= TP_CONNECTION_PRESENCE_TYPE_AVAILABLE)
877 {
878 if (!priv->enabled)
879 {
880 DEBUG ("%s not Enabled", priv->unique_name);
881 return;
882 }
883
884 if (!mcd_account_is_valid (account))
885 {
886 DEBUG ("%s not Valid", priv->unique_name);
887 return;
888 }
889 }
890
891 if (priv->connection == NULL)
892 {
893 if (type >= TP_CONNECTION_PRESENCE_TYPE_AVAILABLE)
894 {
895 if (changed)
896 {
897 _mcd_account_set_changing_presence (account, TRUE);
898 }
899
900 _mcd_account_connection_begin (account, user_initiated);
901 }
902 }
903 else
904 {
905 if (changed)
906 {
907 _mcd_account_set_changing_presence (account, TRUE);
908 }
909
910 _mcd_connection_request_presence (priv->connection,
911 priv->req_presence_type,
912 priv->req_presence_status,
913 priv->req_presence_message);
914 }
915 }
916
917 /*
918 * mcd_account_rerequest_presence:
919 *
920 * Re-requests the account's current RequestedPresence, possibly triggering a
921 * new connection attempt.
922 */
923 static void
mcd_account_rerequest_presence(McdAccount * account,gboolean user_initiated)924 mcd_account_rerequest_presence (McdAccount *account,
925 gboolean user_initiated)
926 {
927 McdAccountPrivate *priv = account->priv;
928
929 mcd_account_request_presence_int (account,
930 priv->req_presence_type,
931 priv->req_presence_status,
932 priv->req_presence_message,
933 user_initiated);
934 }
935
936 void
_mcd_account_connect(McdAccount * account,GHashTable * params)937 _mcd_account_connect (McdAccount *account, GHashTable *params)
938 {
939 McdAccountPrivate *priv = account->priv;
940
941 g_assert (params != NULL);
942
943 if (!priv->connection)
944 {
945 McdConnection *connection;
946
947 if (!priv->manager && !load_manager (account))
948 {
949 g_warning ("%s: Could not find manager `%s'",
950 G_STRFUNC, priv->manager_name);
951 return;
952 }
953
954 connection = mcd_manager_create_connection (priv->manager, account);
955 _mcd_account_set_connection (account, connection);
956 }
957 _mcd_connection_connect (priv->connection, params);
958 }
959
960 static gboolean
emit_property_changed(gpointer userdata)961 emit_property_changed (gpointer userdata)
962 {
963 McdAccount *account = MCD_ACCOUNT (userdata);
964 McdAccountPrivate *priv = account->priv;
965
966 DEBUG ("called");
967
968 if (g_hash_table_size (priv->changed_properties) > 0)
969 {
970 tp_svc_account_emit_account_property_changed (account,
971 priv->changed_properties);
972 g_hash_table_remove_all (priv->changed_properties);
973 }
974
975 if (priv->properties_source != 0)
976 {
977 g_source_remove (priv->properties_source);
978 priv->properties_source = 0;
979 }
980 return FALSE;
981 }
982
983 static void
mcd_account_freeze_properties(McdAccount * self)984 mcd_account_freeze_properties (McdAccount *self)
985 {
986 g_return_if_fail (!self->priv->properties_frozen);
987 DEBUG ("%s", self->priv->unique_name);
988 self->priv->properties_frozen = TRUE;
989 }
990
991 static void
mcd_account_thaw_properties(McdAccount * self)992 mcd_account_thaw_properties (McdAccount *self)
993 {
994 g_return_if_fail (self->priv->properties_frozen);
995 DEBUG ("%s", self->priv->unique_name);
996 self->priv->properties_frozen = FALSE;
997
998 if (g_hash_table_size (self->priv->changed_properties) != 0)
999 {
1000 emit_property_changed (self);
1001 }
1002 }
1003
1004 /*
1005 * This function is responsible of emitting the AccountPropertyChanged signal.
1006 * One possible improvement would be to save the HashTable and have the signal
1007 * emitted in an idle function (or a timeout function with a very small delay)
1008 * to group together several property changes that occur at the same time.
1009 */
1010 static void
mcd_account_changed_property(McdAccount * account,const gchar * key,const GValue * value)1011 mcd_account_changed_property (McdAccount *account, const gchar *key,
1012 const GValue *value)
1013 {
1014 McdAccountPrivate *priv = account->priv;
1015
1016 DEBUG ("called: %s", key);
1017 if (priv->changed_properties &&
1018 g_hash_table_lookup (priv->changed_properties, key))
1019 {
1020 /* the changed property was also changed before; then let's force the
1021 * emission of the signal now, so that the property will appear in two
1022 * separate signals */
1023 DEBUG ("Forcibly emit PropertiesChanged now");
1024 emit_property_changed (account);
1025 }
1026
1027 if (priv->properties_source == 0)
1028 {
1029 DEBUG ("First changed property");
1030 priv->properties_source = g_timeout_add_full (G_PRIORITY_DEFAULT, 10,
1031 emit_property_changed,
1032 g_object_ref (account),
1033 g_object_unref);
1034 }
1035 g_hash_table_insert (priv->changed_properties, (gpointer) key,
1036 tp_g_value_slice_dup (value));
1037 }
1038
1039 typedef enum {
1040 SET_RESULT_ERROR,
1041 SET_RESULT_UNCHANGED,
1042 SET_RESULT_CHANGED
1043 } SetResult;
1044
1045 /*
1046 * mcd_account_set_string_val:
1047 * @account: an account
1048 * @key: a D-Bus property name that is a string
1049 * @value: the new value for that property
1050 * @error: set to an error if %SET_RESULT_ERROR is returned
1051 *
1052 * Returns: %SET_RESULT_CHANGED or %SET_RESULT_UNCHANGED on success,
1053 * %SET_RESULT_ERROR on error
1054 */
1055 static SetResult
mcd_account_set_string_val(McdAccount * account,const gchar * key,const GValue * value,McdDBusPropSetFlags flags,GError ** error)1056 mcd_account_set_string_val (McdAccount *account,
1057 const gchar *key,
1058 const GValue *value,
1059 McdDBusPropSetFlags flags,
1060 GError **error)
1061 {
1062 McdAccountPrivate *priv = account->priv;
1063 McdStorage *storage = priv->storage;
1064 const gchar *name = mcd_account_get_unique_name (account);
1065 const gchar *new_string;
1066
1067 if (!G_VALUE_HOLDS_STRING (value))
1068 {
1069 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1070 "Expected string for %s, but got %s", key,
1071 G_VALUE_TYPE_NAME (value));
1072 return SET_RESULT_ERROR;
1073 }
1074
1075 new_string = g_value_get_string (value);
1076
1077 if (tp_str_empty (new_string)) {
1078 new_string = NULL;
1079 }
1080
1081 if (flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE)
1082 {
1083 mcd_account_changed_property (account, key, value);
1084 return SET_RESULT_CHANGED;
1085 }
1086 else if (mcd_storage_set_string (storage, name, key, new_string))
1087 {
1088 mcd_storage_commit (storage, name);
1089 mcd_account_changed_property (account, key, value);
1090 return SET_RESULT_CHANGED;
1091 }
1092 else
1093 {
1094 return SET_RESULT_UNCHANGED;
1095 }
1096 }
1097
1098 static void
mcd_account_get_string_val(McdAccount * account,const gchar * key,GValue * value)1099 mcd_account_get_string_val (McdAccount *account, const gchar *key,
1100 GValue *value)
1101 {
1102 McdAccountPrivate *priv = account->priv;
1103 const gchar *name = mcd_account_get_unique_name (account);
1104
1105 g_value_init (value, G_TYPE_STRING);
1106
1107 if (!mcd_storage_get_attribute (priv->storage, name, key, value, NULL))
1108 {
1109 g_value_set_static_string (value, NULL);
1110 }
1111 }
1112
1113 static gboolean
set_display_name(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)1114 set_display_name (TpSvcDBusProperties *self,
1115 const gchar *name,
1116 const GValue *value,
1117 McdDBusPropSetFlags flags,
1118 GError **error)
1119 {
1120 McdAccount *account = MCD_ACCOUNT (self);
1121 McdAccountPrivate *priv = account->priv;
1122
1123 DEBUG ("called for %s", priv->unique_name);
1124 return (mcd_account_set_string_val (account,
1125 MC_ACCOUNTS_KEY_DISPLAY_NAME, value, flags,
1126 error) != SET_RESULT_ERROR);
1127 }
1128
1129 static void
get_display_name(TpSvcDBusProperties * self,const gchar * name,GValue * value)1130 get_display_name (TpSvcDBusProperties *self, const gchar *name, GValue *value)
1131 {
1132 McdAccount *account = MCD_ACCOUNT (self);
1133
1134 mcd_account_get_string_val (account, MC_ACCOUNTS_KEY_DISPLAY_NAME, value);
1135 }
1136
1137 static gboolean
set_icon(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)1138 set_icon (TpSvcDBusProperties *self,
1139 const gchar *name,
1140 const GValue *value,
1141 McdDBusPropSetFlags flags,
1142 GError **error)
1143 {
1144 McdAccount *account = MCD_ACCOUNT (self);
1145 McdAccountPrivate *priv = account->priv;
1146
1147 DEBUG ("called for %s", priv->unique_name);
1148 return (mcd_account_set_string_val (account,
1149 MC_ACCOUNTS_KEY_ICON, value, flags, error) != SET_RESULT_ERROR);
1150 }
1151
1152 static void
get_icon(TpSvcDBusProperties * self,const gchar * name,GValue * value)1153 get_icon (TpSvcDBusProperties *self, const gchar *name, GValue *value)
1154 {
1155 McdAccount *account = MCD_ACCOUNT (self);
1156
1157 mcd_account_get_string_val (account, MC_ACCOUNTS_KEY_ICON, value);
1158 }
1159
1160 static void
get_valid(TpSvcDBusProperties * self,const gchar * name,GValue * value)1161 get_valid (TpSvcDBusProperties *self, const gchar *name, GValue *value)
1162 {
1163 McdAccount *account = MCD_ACCOUNT (self);
1164
1165 g_value_init (value, G_TYPE_BOOLEAN);
1166 g_value_set_boolean (value, mcd_account_is_valid (account));
1167 }
1168
1169 static void
get_has_been_online(TpSvcDBusProperties * self,const gchar * name,GValue * value)1170 get_has_been_online (TpSvcDBusProperties *self, const gchar *name,
1171 GValue *value)
1172 {
1173 McdAccount *account = MCD_ACCOUNT (self);
1174 McdAccountPrivate *priv = account->priv;
1175
1176 g_value_init (value, G_TYPE_BOOLEAN);
1177 g_value_set_boolean (value, priv->has_been_online);
1178 }
1179
1180 /**
1181 * mcd_account_set_enabled:
1182 * @account: the #McdAccount
1183 * @enabled: %TRUE if the account is to be enabled
1184 * @write_out: %TRUE if this should be written to the keyfile
1185 * @error: return location for an error condition
1186 *
1187 * Returns: %TRUE on success
1188 */
1189 gboolean
_mcd_account_set_enabled(McdAccount * account,gboolean enabled,gboolean write_out,McdDBusPropSetFlags flags,GError ** error)1190 _mcd_account_set_enabled (McdAccount *account,
1191 gboolean enabled,
1192 gboolean write_out,
1193 McdDBusPropSetFlags flags,
1194 GError **error)
1195 {
1196 McdAccountPrivate *priv = account->priv;
1197
1198 if (priv->always_on && !enabled)
1199 {
1200 g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED,
1201 "Account %s cannot be disabled",
1202 priv->unique_name);
1203 return FALSE;
1204 }
1205
1206 if (priv->enabled != enabled)
1207 {
1208 GValue value = G_VALUE_INIT;
1209 const gchar *name = mcd_account_get_unique_name (account);
1210
1211 if (!enabled && priv->connection != NULL)
1212 _mcd_connection_request_presence (priv->connection,
1213 TP_CONNECTION_PRESENCE_TYPE_OFFLINE,
1214 "offline",
1215 NULL);
1216
1217 priv->enabled = enabled;
1218
1219 g_value_init (&value, G_TYPE_BOOLEAN);
1220 g_value_set_boolean (&value, enabled);
1221
1222 if (!(flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE))
1223 {
1224 mcd_storage_set_attribute (priv->storage, name,
1225 MC_ACCOUNTS_KEY_ENABLED, &value);
1226
1227 if (write_out)
1228 mcd_storage_commit (priv->storage, name);
1229 }
1230
1231 mcd_account_changed_property (account, "Enabled", &value);
1232
1233 g_value_unset (&value);
1234
1235 if (enabled)
1236 {
1237 mcd_account_rerequest_presence (account, TRUE);
1238 _mcd_account_maybe_autoconnect (account);
1239 }
1240 }
1241
1242 return TRUE;
1243 }
1244
1245 static gboolean
set_enabled(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)1246 set_enabled (TpSvcDBusProperties *self,
1247 const gchar *name,
1248 const GValue *value,
1249 McdDBusPropSetFlags flags,
1250 GError **error)
1251 {
1252 McdAccount *account = MCD_ACCOUNT (self);
1253 McdAccountPrivate *priv = account->priv;
1254 gboolean enabled;
1255
1256 DEBUG ("called for %s", priv->unique_name);
1257
1258 if (!G_VALUE_HOLDS_BOOLEAN (value))
1259 {
1260 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1261 "Expected boolean for Enabled, but got %s",
1262 G_VALUE_TYPE_NAME (value));
1263 return FALSE;
1264 }
1265
1266 enabled = g_value_get_boolean (value);
1267
1268 return _mcd_account_set_enabled (account, enabled, TRUE, flags, error);
1269 }
1270
1271 static void
get_enabled(TpSvcDBusProperties * self,const gchar * name,GValue * value)1272 get_enabled (TpSvcDBusProperties *self, const gchar *name, GValue *value)
1273 {
1274 McdAccount *account = MCD_ACCOUNT (self);
1275 McdAccountPrivate *priv = account->priv;
1276
1277 g_value_init (value, G_TYPE_BOOLEAN);
1278 g_value_set_boolean (value, priv->enabled);
1279 }
1280
1281 static gboolean
set_service(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)1282 set_service (TpSvcDBusProperties *self, const gchar *name,
1283 const GValue *value,
1284 McdDBusPropSetFlags flags,
1285 GError **error)
1286 {
1287 McdAccount *account = MCD_ACCOUNT (self);
1288 SetResult ret = SET_RESULT_ERROR;
1289 gboolean proceed = TRUE;
1290 static GRegex *rule = NULL;
1291 static gsize service_re_init = 0;
1292
1293 if (g_once_init_enter (&service_re_init))
1294 {
1295 GError *regex_error = NULL;
1296 rule = g_regex_new ("^(?:[a-z][a-z0-9_-]*)?$",
1297 G_REGEX_CASELESS|G_REGEX_DOLLAR_ENDONLY,
1298 0, ®ex_error);
1299 g_assert_no_error (regex_error);
1300 g_once_init_leave (&service_re_init, 1);
1301 }
1302
1303 if (G_VALUE_HOLDS_STRING (value))
1304 proceed = g_regex_match (rule, g_value_get_string (value), 0, NULL);
1305
1306 /* if value is not a string, mcd_account_set_string_val will set *
1307 * the appropriate error for us: don't duplicate that logic here */
1308 if (proceed)
1309 {
1310 ret = mcd_account_set_string_val (account, MC_ACCOUNTS_KEY_SERVICE,
1311 value, flags, error);
1312 }
1313 else
1314 {
1315 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1316 "Invalid service '%s': Must consist of ASCII alphanumeric "
1317 "characters, underscores (_) and hyphens (-) only, and "
1318 "start with a letter",
1319 g_value_get_string (value));
1320 }
1321
1322 return (ret != SET_RESULT_ERROR);
1323 }
1324
1325 static void
get_service(TpSvcDBusProperties * self,const gchar * name,GValue * value)1326 get_service (TpSvcDBusProperties *self, const gchar *name, GValue *value)
1327 {
1328 McdAccount *account = MCD_ACCOUNT (self);
1329
1330 mcd_account_get_string_val (account, MC_ACCOUNTS_KEY_SERVICE, value);
1331 }
1332
1333 static void
mcd_account_set_self_alias_cb(TpConnection * tp_connection,const GError * error,gpointer user_data,GObject * weak_object)1334 mcd_account_set_self_alias_cb (TpConnection *tp_connection,
1335 const GError *error,
1336 gpointer user_data,
1337 GObject *weak_object)
1338 {
1339 if (error)
1340 DEBUG ("%s", error->message);
1341 }
1342
1343 static void
mcd_account_send_nickname_to_connection(McdAccount * self,const gchar * nickname)1344 mcd_account_send_nickname_to_connection (McdAccount *self,
1345 const gchar *nickname)
1346 {
1347 if (self->priv->tp_connection == NULL)
1348 return;
1349
1350 if (self->priv->self_contact == NULL)
1351 return;
1352
1353 DEBUG ("%s: '%s'", self->priv->unique_name, nickname);
1354
1355 if (tp_proxy_has_interface_by_id (self->priv->tp_connection,
1356 TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING))
1357 {
1358 GHashTable *aliases = g_hash_table_new (NULL, NULL);
1359
1360 g_hash_table_insert (aliases,
1361 GUINT_TO_POINTER (tp_contact_get_handle (self->priv->self_contact)),
1362 (gchar *) nickname);
1363 tp_cli_connection_interface_aliasing_call_set_aliases (
1364 self->priv->tp_connection, -1, aliases,
1365 mcd_account_set_self_alias_cb, NULL, NULL, NULL);
1366 g_hash_table_unref (aliases);
1367 }
1368 }
1369
1370
1371 static gboolean
set_nickname(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)1372 set_nickname (TpSvcDBusProperties *self, const gchar *name,
1373 const GValue *value,
1374 McdDBusPropSetFlags flags,
1375 GError **error)
1376 {
1377 McdAccount *account = MCD_ACCOUNT (self);
1378 McdAccountPrivate *priv = account->priv;
1379 SetResult ret;
1380 GValue replacement = G_VALUE_INIT;
1381
1382 DEBUG ("called for %s", priv->unique_name);
1383
1384 /* If we're asked to set Nickname = "", set it to our identifier
1385 * (NormalizedName) instead, so that we always have some sort of nickname.
1386 * This matches what we do when connecting an account.
1387 *
1388 * Exception: if we're not fully connected yet (and hence have no
1389 * self-contact), rely on the corresponding special-case
1390 * when we do become connected.
1391 */
1392 if (G_VALUE_HOLDS_STRING (value) &&
1393 tp_str_empty (g_value_get_string (value)) &&
1394 priv->self_contact != NULL)
1395 {
1396 g_value_init (&replacement, G_TYPE_STRING);
1397 g_value_set_string (&replacement,
1398 tp_contact_get_identifier (priv->self_contact));
1399 value = &replacement;
1400 }
1401
1402 ret = mcd_account_set_string_val (account, MC_ACCOUNTS_KEY_NICKNAME,
1403 value, flags, error);
1404
1405 if (ret != SET_RESULT_ERROR)
1406 {
1407 mcd_account_send_nickname_to_connection (account,
1408 g_value_get_string (value));
1409 }
1410
1411 if (value == &replacement)
1412 g_value_unset (&replacement);
1413
1414 return (ret != SET_RESULT_ERROR);
1415 }
1416
1417 static void
get_nickname(TpSvcDBusProperties * self,const gchar * name,GValue * value)1418 get_nickname (TpSvcDBusProperties *self, const gchar *name, GValue *value)
1419 {
1420 McdAccount *account = MCD_ACCOUNT (self);
1421
1422 mcd_account_get_string_val (account, MC_ACCOUNTS_KEY_NICKNAME, value);
1423 }
1424
1425 static void
mcd_account_self_contact_notify_avatar_file_cb(McdAccount * self,GParamSpec * unused_param_spec G_GNUC_UNUSED,TpContact * self_contact)1426 mcd_account_self_contact_notify_avatar_file_cb (McdAccount *self,
1427 GParamSpec *unused_param_spec G_GNUC_UNUSED,
1428 TpContact *self_contact)
1429 {
1430 const gchar *token;
1431 gchar *prev_token;
1432 GFile *file;
1433 GError *error = NULL;
1434 gboolean changed;
1435
1436 if (self_contact != self->priv->self_contact)
1437 return;
1438
1439 file = tp_contact_get_avatar_file (self_contact);
1440 token = tp_contact_get_avatar_token (self_contact);
1441
1442 if (self->priv->setting_avatar)
1443 {
1444 DEBUG ("Ignoring avatar change notification: we are setting ours");
1445 return;
1446 }
1447
1448 if (self->priv->waiting_for_initial_avatar)
1449 {
1450 DEBUG ("Ignoring avatar change notification: we are waiting for the "
1451 "initial value");
1452 return;
1453 }
1454
1455 prev_token = _mcd_account_get_avatar_token (self);
1456 changed = tp_strdiff (prev_token, token);
1457 g_free (prev_token);
1458
1459 if (!changed)
1460 {
1461 DEBUG ("Avatar unchanged: '%s'", token);
1462 return;
1463 }
1464
1465 if (file == NULL)
1466 {
1467 if (!_mcd_account_set_avatar (self, NULL, "", "", &error))
1468 {
1469 DEBUG ("Attempt to clear avatar failed: %s", error->message);
1470 g_clear_error (&error);
1471 }
1472 }
1473 else
1474 {
1475 gchar *contents = NULL;
1476 gsize len = 0;
1477 GArray *arr;
1478
1479 if (!g_file_load_contents (file, NULL, &contents, &len, NULL, &error))
1480 {
1481 gchar *uri = g_file_get_uri (file);
1482
1483 WARNING ("Unable to read avatar file %s: %s", uri, error->message);
1484 g_clear_error (&error);
1485 g_free (uri);
1486 return;
1487 }
1488
1489 if (G_UNLIKELY (len > G_MAXUINT))
1490 {
1491 gchar *uri = g_file_get_uri (file);
1492
1493 WARNING ("Avatar file %s was ludicrously huge", uri);
1494 g_free (uri);
1495 g_free (contents);
1496 return;
1497 }
1498
1499 arr = g_array_sized_new (TRUE, FALSE, 1, (guint) len);
1500 g_array_append_vals (arr, contents, (guint) len);
1501 g_free (contents);
1502
1503 if (!_mcd_account_set_avatar (self, arr,
1504 tp_contact_get_avatar_mime_type (self_contact),
1505 tp_contact_get_avatar_token (self_contact), &error))
1506 {
1507 DEBUG ("Attempt to save avatar failed: %s", error->message);
1508 g_clear_error (&error);
1509 }
1510
1511 g_array_unref (arr);
1512 }
1513 }
1514
1515 static void
avatars_set_avatar_cb(TpConnection * tp_connection,const gchar * token,const GError * error,gpointer user_data,GObject * weak_object)1516 avatars_set_avatar_cb (TpConnection *tp_connection,
1517 const gchar *token,
1518 const GError *error,
1519 gpointer user_data,
1520 GObject *weak_object)
1521 {
1522 McdAccount *self = MCD_ACCOUNT (weak_object);
1523
1524 self->priv->setting_avatar = FALSE;
1525
1526 if (error != NULL)
1527 {
1528 DEBUG ("%s: %s", self->priv->unique_name, error->message);
1529 }
1530 else
1531 {
1532 DEBUG ("%s: new token %s", self->priv->unique_name, token);
1533 _mcd_account_set_avatar_token (self, token);
1534 }
1535 }
1536
1537 static void
avatars_clear_avatar_cb(TpConnection * tp_connection,const GError * error,gpointer user_data,GObject * weak_object)1538 avatars_clear_avatar_cb (TpConnection *tp_connection,
1539 const GError *error,
1540 gpointer user_data,
1541 GObject *weak_object)
1542 {
1543 McdAccount *self = MCD_ACCOUNT (weak_object);
1544
1545 self->priv->setting_avatar = FALSE;
1546
1547 if (error != NULL)
1548 {
1549 DEBUG ("%s: %s", self->priv->unique_name, error->message);
1550 }
1551 else
1552 {
1553 DEBUG ("%s: success", self->priv->unique_name);
1554 _mcd_account_set_avatar_token (self, "");
1555 }
1556 }
1557
1558 static void
mcd_account_send_avatar_to_connection(McdAccount * self,const GArray * avatar,const gchar * mime_type)1559 mcd_account_send_avatar_to_connection (McdAccount *self,
1560 const GArray *avatar,
1561 const gchar *mime_type)
1562 {
1563 if (self->priv->tp_connection == NULL)
1564 return;
1565
1566 if (self->priv->self_contact == NULL)
1567 return;
1568
1569 DEBUG ("%s: %u bytes", self->priv->unique_name, avatar->len);
1570
1571 if (tp_proxy_has_interface_by_id (self->priv->tp_connection,
1572 TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS))
1573 {
1574 self->priv->setting_avatar = TRUE;
1575
1576 if (avatar->len > 0 && avatar->len < G_MAXUINT)
1577 {
1578 tp_cli_connection_interface_avatars_call_set_avatar (
1579 self->priv->tp_connection, -1, avatar, mime_type,
1580 avatars_set_avatar_cb, NULL, NULL, (GObject *) self);
1581 }
1582 else
1583 {
1584 tp_cli_connection_interface_avatars_call_clear_avatar (
1585 self->priv->tp_connection, -1, avatars_clear_avatar_cb,
1586 NULL, NULL, (GObject *) self);
1587 }
1588 }
1589 else
1590 {
1591 DEBUG ("unsupported, ignoring");
1592 }
1593 }
1594
1595 static gboolean
set_avatar(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)1596 set_avatar (TpSvcDBusProperties *self, const gchar *name, const GValue *value,
1597 McdDBusPropSetFlags flags,
1598 GError **error)
1599 {
1600 McdAccount *account = MCD_ACCOUNT (self);
1601 McdAccountPrivate *priv = account->priv;
1602 const gchar *mime_type;
1603 const GArray *avatar;
1604 GValueArray *va;
1605
1606 DEBUG ("called for %s", priv->unique_name);
1607
1608 if (!G_VALUE_HOLDS (value, TP_STRUCT_TYPE_AVATAR))
1609 {
1610 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1611 "Unexpected type for Avatar: wanted (ay,s), got %s",
1612 G_VALUE_TYPE_NAME (value));
1613 return FALSE;
1614 }
1615
1616 va = g_value_get_boxed (value);
1617 avatar = g_value_get_boxed (va->values);
1618 mime_type = g_value_get_string (va->values + 1);
1619
1620 if (!_mcd_account_set_avatar (account, avatar, mime_type, NULL, error))
1621 {
1622 return FALSE;
1623 }
1624
1625 tp_svc_account_interface_avatar_emit_avatar_changed (account);
1626 return TRUE;
1627 }
1628
1629 static void
get_avatar(TpSvcDBusProperties * self,const gchar * name,GValue * value)1630 get_avatar (TpSvcDBusProperties *self, const gchar *name, GValue *value)
1631 {
1632 McdAccount *account = MCD_ACCOUNT (self);
1633 gchar *mime_type;
1634 GArray *avatar = NULL;
1635 GType type = TP_STRUCT_TYPE_AVATAR;
1636 GValueArray *va;
1637
1638 _mcd_account_get_avatar (account, &avatar, &mime_type);
1639 if (!avatar)
1640 avatar = g_array_new (FALSE, FALSE, 1);
1641
1642 g_value_init (value, type);
1643 g_value_take_boxed (value, dbus_g_type_specialized_construct (type));
1644 va = (GValueArray *) g_value_get_boxed (value);
1645 g_value_take_boxed (va->values, avatar);
1646 g_value_take_string (va->values + 1, mime_type);
1647 }
1648
1649 static void
get_parameters(TpSvcDBusProperties * self,const gchar * name,GValue * value)1650 get_parameters (TpSvcDBusProperties *self, const gchar *name,
1651 GValue *value)
1652 {
1653 McdAccount *account = MCD_ACCOUNT (self);
1654 GHashTable *params = _mcd_account_dup_parameters (account);
1655
1656 if (params == NULL)
1657 {
1658 if (mcd_account_is_valid (account))
1659 g_warning ("%s is supposedly valid, but _dup_parameters() failed!",
1660 mcd_account_get_unique_name (account));
1661
1662 params = tp_asv_new (NULL, NULL);
1663 }
1664
1665 g_value_init (value, TP_HASH_TYPE_STRING_VARIANT_MAP);
1666 g_value_take_boxed (value, params);
1667 }
1668
1669 gboolean
_mcd_account_presence_type_is_settable(TpConnectionPresenceType type)1670 _mcd_account_presence_type_is_settable (TpConnectionPresenceType type)
1671 {
1672 switch (type)
1673 {
1674 case TP_CONNECTION_PRESENCE_TYPE_UNSET:
1675 case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
1676 case TP_CONNECTION_PRESENCE_TYPE_ERROR:
1677 return FALSE;
1678
1679 default:
1680 return TRUE;
1681 }
1682 }
1683
1684 static gboolean
_presence_type_is_online(TpConnectionPresenceType type)1685 _presence_type_is_online (TpConnectionPresenceType type)
1686 {
1687 switch (type)
1688 {
1689 case TP_CONNECTION_PRESENCE_TYPE_UNSET:
1690 case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
1691 case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
1692 case TP_CONNECTION_PRESENCE_TYPE_ERROR:
1693 return FALSE;
1694
1695 default:
1696 return TRUE;
1697 }
1698 }
1699
1700 static gboolean
set_automatic_presence(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)1701 set_automatic_presence (TpSvcDBusProperties *self,
1702 const gchar *name,
1703 const GValue *value,
1704 McdDBusPropSetFlags flags,
1705 GError **error)
1706 {
1707 McdAccount *account = MCD_ACCOUNT (self);
1708 McdAccountPrivate *priv = account->priv;
1709 const gchar *status, *message;
1710 TpConnectionPresenceType type;
1711 gboolean changed = FALSE;
1712 GValueArray *va;
1713 const gchar *account_name = mcd_account_get_unique_name (account);
1714
1715 DEBUG ("called for %s", account_name);
1716
1717 if (!G_VALUE_HOLDS (value, TP_STRUCT_TYPE_SIMPLE_PRESENCE))
1718 {
1719 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1720 "Unexpected type for AutomaticPresence: wanted (u,s,s), "
1721 "got %s", G_VALUE_TYPE_NAME (value));
1722 return FALSE;
1723 }
1724
1725 va = g_value_get_boxed (value);
1726 type = g_value_get_uint (va->values);
1727 status = g_value_get_string (va->values + 1);
1728 message = g_value_get_string (va->values + 2);
1729
1730 if (!_presence_type_is_online (type))
1731 {
1732 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1733 "AutomaticPresence must be an online presence, not %d",
1734 type);
1735 return FALSE;
1736 }
1737
1738 DEBUG ("setting automatic presence: %d, %s, %s", type, status, message);
1739
1740 if (priv->auto_presence_type != type)
1741 {
1742 priv->auto_presence_type = type;
1743 changed = TRUE;
1744 }
1745
1746 if (tp_strdiff (priv->auto_presence_status, status))
1747 {
1748 g_free (priv->auto_presence_status);
1749 priv->auto_presence_status = g_strdup (status);
1750 changed = TRUE;
1751 }
1752
1753 if (tp_strdiff (priv->auto_presence_message, message))
1754 {
1755 g_free (priv->auto_presence_message);
1756 priv->auto_presence_message = g_strdup (message);
1757 changed = TRUE;
1758 }
1759
1760 if (changed)
1761 {
1762 mcd_storage_set_attribute (priv->storage, account_name,
1763 MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE,
1764 value);
1765 mcd_storage_commit (priv->storage, account_name);
1766 mcd_account_changed_property (account, name, value);
1767 }
1768
1769 return TRUE;
1770 }
1771
1772 static void
get_automatic_presence(TpSvcDBusProperties * self,const gchar * name,GValue * value)1773 get_automatic_presence (TpSvcDBusProperties *self,
1774 const gchar *name, GValue *value)
1775 {
1776 McdAccount *account = MCD_ACCOUNT (self);
1777 McdAccountPrivate *priv = account->priv;
1778 gchar *presence, *message;
1779 gint presence_type;
1780 GType type;
1781 GValueArray *va;
1782
1783 presence_type = priv->auto_presence_type;
1784 presence = priv->auto_presence_status;
1785 message = priv->auto_presence_message;
1786
1787 type = TP_STRUCT_TYPE_SIMPLE_PRESENCE;
1788 g_value_init (value, type);
1789 g_value_take_boxed (value, dbus_g_type_specialized_construct (type));
1790 va = (GValueArray *) g_value_get_boxed (value);
1791 g_value_set_uint (va->values, presence_type);
1792 g_value_set_static_string (va->values + 1, presence);
1793 g_value_set_static_string (va->values + 2, message);
1794 }
1795
1796 static gboolean
set_connect_automatically(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)1797 set_connect_automatically (TpSvcDBusProperties *self,
1798 const gchar *name,
1799 const GValue *value,
1800 McdDBusPropSetFlags flags,
1801 GError **error)
1802 {
1803 McdAccount *account = MCD_ACCOUNT (self);
1804 McdAccountPrivate *priv = account->priv;
1805 gboolean connect_automatically;
1806
1807 DEBUG ("called for %s", priv->unique_name);
1808
1809 if (!G_VALUE_HOLDS_BOOLEAN (value))
1810 {
1811 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1812 "Expected boolean for ConnectAutomatically, but got %s",
1813 G_VALUE_TYPE_NAME (value));
1814 return FALSE;
1815 }
1816
1817 connect_automatically = g_value_get_boolean (value);
1818
1819 if (priv->always_on && !connect_automatically)
1820 {
1821 g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED,
1822 "Account %s always connects automatically",
1823 priv->unique_name);
1824 return FALSE;
1825 }
1826
1827 if (priv->connect_automatically != connect_automatically)
1828 {
1829 const gchar *account_name = mcd_account_get_unique_name (account);
1830
1831 if (!(flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE))
1832 {
1833 mcd_storage_set_attribute (priv->storage, account_name,
1834 MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY,
1835 value);
1836 mcd_storage_commit (priv->storage, account_name);
1837 }
1838
1839 priv->connect_automatically = connect_automatically;
1840 mcd_account_changed_property (account, name, value);
1841
1842 if (connect_automatically)
1843 {
1844 _mcd_account_maybe_autoconnect (account);
1845 }
1846 }
1847
1848 return TRUE;
1849 }
1850
1851 static void
get_connect_automatically(TpSvcDBusProperties * self,const gchar * name,GValue * value)1852 get_connect_automatically (TpSvcDBusProperties *self,
1853 const gchar *name, GValue *value)
1854 {
1855 McdAccount *account = MCD_ACCOUNT (self);
1856 McdAccountPrivate *priv = account->priv;
1857
1858 DEBUG ("called for %s", priv->unique_name);
1859 g_value_init (value, G_TYPE_BOOLEAN);
1860 g_value_set_boolean (value, priv->connect_automatically);
1861 }
1862
1863 static void
get_connection(TpSvcDBusProperties * self,const gchar * name,GValue * value)1864 get_connection (TpSvcDBusProperties *self, const gchar *name, GValue *value)
1865 {
1866 McdAccount *account = MCD_ACCOUNT (self);
1867 McdAccountPrivate *priv = account->priv;
1868 const gchar *object_path;
1869
1870 g_value_init (value, DBUS_TYPE_G_OBJECT_PATH);
1871 if (priv->connection &&
1872 (object_path = mcd_connection_get_object_path (priv->connection)))
1873 g_value_set_boxed (value, object_path);
1874 else
1875 g_value_set_static_boxed (value, "/");
1876 }
1877
1878 static void
get_connection_status(TpSvcDBusProperties * self,const gchar * name,GValue * value)1879 get_connection_status (TpSvcDBusProperties *self,
1880 const gchar *name, GValue *value)
1881 {
1882 McdAccount *account = MCD_ACCOUNT (self);
1883
1884 g_value_init (value, G_TYPE_UINT);
1885 g_value_set_uint (value, account->priv->conn_status);
1886 }
1887
1888 static void
get_connection_status_reason(TpSvcDBusProperties * self,const gchar * name,GValue * value)1889 get_connection_status_reason (TpSvcDBusProperties *self,
1890 const gchar *name, GValue *value)
1891 {
1892 McdAccount *account = MCD_ACCOUNT (self);
1893
1894 g_value_init (value, G_TYPE_UINT);
1895 g_value_set_uint (value, account->priv->conn_reason);
1896 }
1897
1898 static void
get_connection_error(TpSvcDBusProperties * self,const gchar * name,GValue * value)1899 get_connection_error (TpSvcDBusProperties *self,
1900 const gchar *name,
1901 GValue *value)
1902 {
1903 McdAccount *account = MCD_ACCOUNT (self);
1904
1905 g_value_init (value, G_TYPE_STRING);
1906 g_value_set_string (value, account->priv->conn_dbus_error);
1907 }
1908
1909 static void
get_connection_error_details(TpSvcDBusProperties * self,const gchar * name,GValue * value)1910 get_connection_error_details (TpSvcDBusProperties *self,
1911 const gchar *name,
1912 GValue *value)
1913 {
1914 McdAccount *account = MCD_ACCOUNT (self);
1915
1916 g_value_init (value, TP_HASH_TYPE_STRING_VARIANT_MAP);
1917 g_value_set_boxed (value, account->priv->conn_error_details);
1918 }
1919
1920 static void
get_current_presence(TpSvcDBusProperties * self,const gchar * name,GValue * value)1921 get_current_presence (TpSvcDBusProperties *self, const gchar *name,
1922 GValue *value)
1923 {
1924 McdAccount *account = MCD_ACCOUNT (self);
1925 McdAccountPrivate *priv = account->priv;
1926 gchar *status, *message;
1927 gint presence_type;
1928 GType type;
1929 GValueArray *va;
1930
1931 presence_type = priv->curr_presence_type;
1932 status = priv->curr_presence_status;
1933 message = priv->curr_presence_message;
1934
1935 type = TP_STRUCT_TYPE_SIMPLE_PRESENCE;
1936 g_value_init (value, type);
1937 g_value_take_boxed (value, dbus_g_type_specialized_construct (type));
1938 va = (GValueArray *) g_value_get_boxed (value);
1939 g_value_set_uint (va->values, presence_type);
1940 g_value_set_static_string (va->values + 1, status);
1941 g_value_set_static_string (va->values + 2, message);
1942 }
1943
1944 static gboolean
set_requested_presence(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)1945 set_requested_presence (TpSvcDBusProperties *self,
1946 const gchar *name,
1947 const GValue *value,
1948 McdDBusPropSetFlags flags,
1949 GError **error)
1950 {
1951 McdAccount *account = MCD_ACCOUNT (self);
1952 McdAccountPrivate *priv = account->priv;
1953 const gchar *status, *message;
1954 gint type;
1955 GValueArray *va;
1956
1957 DEBUG ("called for %s", priv->unique_name);
1958
1959 if (!G_VALUE_HOLDS (value, TP_STRUCT_TYPE_SIMPLE_PRESENCE))
1960 {
1961 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1962 "Unexpected type for RequestedPresence: wanted (u,s,s), "
1963 "got %s", G_VALUE_TYPE_NAME (value));
1964 return FALSE;
1965 }
1966
1967 va = g_value_get_boxed (value);
1968 type = (gint)g_value_get_uint (va->values);
1969 status = g_value_get_string (va->values + 1);
1970 message = g_value_get_string (va->values + 2);
1971
1972 if (priv->always_on && !_presence_type_is_online (type))
1973 {
1974 g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED,
1975 "Account %s cannot be taken offline", priv->unique_name);
1976 return FALSE;
1977 }
1978
1979 if (!_mcd_account_presence_type_is_settable (type))
1980 {
1981 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1982 "RequestedPresence %d cannot be set on yourself", type);
1983 return FALSE;
1984 }
1985
1986 DEBUG ("setting requested presence: %d, %s, %s", type, status, message);
1987
1988 mcd_account_request_presence_int (account, type, status, message, TRUE);
1989 return TRUE;
1990 }
1991
1992 static void
get_requested_presence(TpSvcDBusProperties * self,const gchar * name,GValue * value)1993 get_requested_presence (TpSvcDBusProperties *self,
1994 const gchar *name, GValue *value)
1995 {
1996 McdAccount *account = MCD_ACCOUNT (self);
1997 McdAccountPrivate *priv = account->priv;
1998 gchar *presence, *message;
1999 gint presence_type;
2000 GType type;
2001 GValueArray *va;
2002
2003 presence_type = priv->req_presence_type;
2004 presence = priv->req_presence_status;
2005 message = priv->req_presence_message;
2006
2007 type = TP_STRUCT_TYPE_SIMPLE_PRESENCE;
2008 g_value_init (value, type);
2009 g_value_take_boxed (value, dbus_g_type_specialized_construct (type));
2010 va = (GValueArray *) g_value_get_boxed (value);
2011 g_value_set_uint (va->values, presence_type);
2012 g_value_set_static_string (va->values + 1, presence);
2013 g_value_set_static_string (va->values + 2, message);
2014 }
2015
2016 static void
get_changing_presence(TpSvcDBusProperties * self,const gchar * name,GValue * value)2017 get_changing_presence (TpSvcDBusProperties *self,
2018 const gchar *name, GValue *value)
2019 {
2020 McdAccount *account = MCD_ACCOUNT (self);
2021 McdAccountPrivate *priv = account->priv;
2022
2023 g_value_init (value, G_TYPE_BOOLEAN);
2024 g_value_set_boolean (value, priv->changing_presence);
2025 }
2026
2027 static void
get_normalized_name(TpSvcDBusProperties * self,const gchar * name,GValue * value)2028 get_normalized_name (TpSvcDBusProperties *self,
2029 const gchar *name, GValue *value)
2030 {
2031 McdAccount *account = MCD_ACCOUNT (self);
2032
2033 mcd_account_get_string_val (account, MC_ACCOUNTS_KEY_NORMALIZED_NAME,
2034 value);
2035 }
2036
2037 static gboolean
set_supersedes(TpSvcDBusProperties * svc,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)2038 set_supersedes (TpSvcDBusProperties *svc,
2039 const gchar *name,
2040 const GValue *value,
2041 McdDBusPropSetFlags flags,
2042 GError **error)
2043 {
2044 McdAccount *self = MCD_ACCOUNT (svc);
2045
2046 if (!G_VALUE_HOLDS (value, TP_ARRAY_TYPE_OBJECT_PATH_LIST))
2047 {
2048 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2049 "Unexpected type for Supersedes: wanted 'ao', got %s",
2050 G_VALUE_TYPE_NAME (value));
2051 return FALSE;
2052 }
2053
2054 if (self->priv->supersedes != NULL)
2055 g_ptr_array_unref (self->priv->supersedes);
2056
2057 self->priv->supersedes = g_value_dup_boxed (value);
2058 mcd_account_changed_property (self, name, value);
2059
2060 mcd_storage_set_attribute (self->priv->storage, self->priv->unique_name,
2061 MC_ACCOUNTS_KEY_SUPERSEDES, value);
2062 mcd_storage_commit (self->priv->storage, self->priv->unique_name);
2063
2064 return TRUE;
2065 }
2066
2067 static void
get_supersedes(TpSvcDBusProperties * svc,const gchar * name,GValue * value)2068 get_supersedes (TpSvcDBusProperties *svc,
2069 const gchar *name,
2070 GValue *value)
2071 {
2072 McdAccount *self = MCD_ACCOUNT (svc);
2073
2074 if (self->priv->supersedes == NULL)
2075 self->priv->supersedes = g_ptr_array_new ();
2076
2077 g_value_init (value, TP_ARRAY_TYPE_OBJECT_PATH_LIST);
2078 g_value_set_boxed (value, self->priv->supersedes);
2079 }
2080
2081 static McpAccountStorage *
get_storage_plugin(McdAccount * account)2082 get_storage_plugin (McdAccount *account)
2083 {
2084 McdAccountPrivate *priv = account->priv;
2085 const gchar *account_name = mcd_account_get_unique_name (account);
2086
2087 if (priv->storage_plugin != NULL)
2088 return priv->storage_plugin;
2089
2090 priv->storage_plugin = mcd_storage_get_plugin (priv->storage, account_name);
2091
2092 if (priv->storage_plugin != NULL)
2093 g_object_ref (priv->storage_plugin);
2094
2095 return priv->storage_plugin;
2096 }
2097
2098 static void
get_storage_provider(TpSvcDBusProperties * self,const gchar * name,GValue * value)2099 get_storage_provider (TpSvcDBusProperties *self,
2100 const gchar *name, GValue *value)
2101 {
2102 McdAccount *account = MCD_ACCOUNT (self);
2103 McpAccountStorage *storage_plugin = get_storage_plugin (account);
2104
2105 g_value_init (value, G_TYPE_STRING);
2106
2107 if (storage_plugin != NULL)
2108 g_value_set_string (value, mcp_account_storage_provider (storage_plugin));
2109 else
2110 g_value_set_static_string (value, "");
2111 }
2112
2113 static gboolean
set_storage_provider(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)2114 set_storage_provider (TpSvcDBusProperties *self,
2115 const gchar *name,
2116 const GValue *value,
2117 McdDBusPropSetFlags flags,
2118 GError **error)
2119 {
2120 McdAccount *account = MCD_ACCOUNT (self);
2121 McpAccountStorage *storage_plugin = get_storage_plugin (account);
2122 const gchar *current_provider = mcp_account_storage_provider (storage_plugin);
2123
2124 if (!G_VALUE_HOLDS_STRING (value) ||
2125 tp_strdiff (g_value_get_string (value), current_provider))
2126 {
2127 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2128 "Cannot change provider, it is defined at account creation only");
2129 return FALSE;
2130 }
2131
2132 return TRUE;
2133 }
2134
2135 static void
get_storage_identifier(TpSvcDBusProperties * self,const gchar * name,GValue * value)2136 get_storage_identifier (TpSvcDBusProperties *self,
2137 const gchar *name, GValue *value)
2138 {
2139
2140 McdAccount *account = MCD_ACCOUNT (self);
2141 McpAccountStorage *storage_plugin = get_storage_plugin (account);
2142 GValue identifier = G_VALUE_INIT;
2143
2144 g_value_init (value, G_TYPE_VALUE);
2145
2146 if (storage_plugin != NULL)
2147 {
2148 mcp_account_storage_get_identifier (
2149 storage_plugin, account->priv->unique_name, &identifier);
2150 }
2151 else
2152 {
2153 g_value_init (&identifier, G_TYPE_UINT);
2154
2155 g_value_set_uint (&identifier, 0);
2156 }
2157
2158 g_value_set_boxed (value, &identifier);
2159
2160 g_value_unset (&identifier);
2161 }
2162
2163 static void
get_storage_specific_info(TpSvcDBusProperties * self,const gchar * name,GValue * value)2164 get_storage_specific_info (TpSvcDBusProperties *self,
2165 const gchar *name, GValue *value)
2166 {
2167 GHashTable *storage_specific_info;
2168 McdAccount *account = MCD_ACCOUNT (self);
2169 McpAccountStorage *storage_plugin = get_storage_plugin (account);
2170
2171 g_value_init (value, TP_HASH_TYPE_STRING_VARIANT_MAP);
2172
2173 if (storage_plugin != NULL)
2174 storage_specific_info = mcp_account_storage_get_additional_info (
2175 storage_plugin, account->priv->unique_name);
2176 else
2177 storage_specific_info = g_hash_table_new (g_str_hash, g_str_equal);
2178
2179 g_value_take_boxed (value, storage_specific_info);
2180 }
2181
2182 static void
get_storage_restrictions(TpSvcDBusProperties * self,const gchar * name,GValue * value)2183 get_storage_restrictions (TpSvcDBusProperties *self,
2184 const gchar *name, GValue *value)
2185 {
2186 TpStorageRestrictionFlags flags;
2187 McdAccount *account = MCD_ACCOUNT (self);
2188 McpAccountStorage *storage_plugin = get_storage_plugin (account);
2189
2190 g_value_init (value, G_TYPE_UINT);
2191
2192 g_return_if_fail (storage_plugin != NULL);
2193
2194 flags = mcp_account_storage_get_restrictions (storage_plugin,
2195 account->priv->unique_name);
2196
2197 g_value_set_uint (value, flags);
2198 }
2199
2200 static const McdDBusProp account_properties[] = {
2201 { "Interfaces", NULL, mcd_dbus_get_interfaces },
2202 { "DisplayName", set_display_name, get_display_name },
2203 { "Icon", set_icon, get_icon },
2204 { "Valid", NULL, get_valid },
2205 { "Enabled", set_enabled, get_enabled },
2206 { "Nickname", set_nickname, get_nickname },
2207 { "Service", set_service, get_service },
2208 { "Parameters", NULL, get_parameters },
2209 { "AutomaticPresence", set_automatic_presence, get_automatic_presence },
2210 { "ConnectAutomatically", set_connect_automatically, get_connect_automatically },
2211 { "Connection", NULL, get_connection },
2212 { "ConnectionStatus", NULL, get_connection_status },
2213 { "ConnectionStatusReason", NULL, get_connection_status_reason },
2214 { "ConnectionError", NULL, get_connection_error },
2215 { "ConnectionErrorDetails", NULL, get_connection_error_details },
2216 { "CurrentPresence", NULL, get_current_presence },
2217 { "RequestedPresence", set_requested_presence, get_requested_presence },
2218 { "ChangingPresence", NULL, get_changing_presence },
2219 { "NormalizedName", NULL, get_normalized_name },
2220 { "HasBeenOnline", NULL, get_has_been_online },
2221 { "Supersedes", set_supersedes, get_supersedes },
2222 { 0 },
2223 };
2224
2225 static const McdDBusProp account_avatar_properties[] = {
2226 { "Avatar", set_avatar, get_avatar },
2227 { 0 },
2228 };
2229
2230 static const McdDBusProp account_storage_properties[] = {
2231 { "StorageProvider", set_storage_provider, get_storage_provider },
2232 { "StorageIdentifier", NULL, get_storage_identifier },
2233 { "StorageSpecificInformation", NULL, get_storage_specific_info },
2234 { "StorageRestrictions", NULL, get_storage_restrictions },
2235 { 0 },
2236 };
2237
2238 static void
account_avatar_iface_init(TpSvcAccountInterfaceAvatarClass * iface,gpointer iface_data)2239 account_avatar_iface_init (TpSvcAccountInterfaceAvatarClass *iface,
2240 gpointer iface_data)
2241 {
2242 }
2243
2244 static void
account_storage_iface_init(TpSvcAccountInterfaceStorageClass * iface,gpointer iface_data)2245 account_storage_iface_init (TpSvcAccountInterfaceStorageClass *iface,
2246 gpointer iface_data)
2247 {
2248 }
2249
2250 static void
get_hidden(TpSvcDBusProperties * self,const gchar * name,GValue * value)2251 get_hidden (TpSvcDBusProperties *self,
2252 const gchar *name, GValue *value)
2253 {
2254 g_value_init (value, G_TYPE_BOOLEAN);
2255 g_object_get_property (G_OBJECT (self), "hidden", value);
2256 }
2257
2258 static gboolean
set_hidden(TpSvcDBusProperties * self,const gchar * name,const GValue * value,McdDBusPropSetFlags flags,GError ** error)2259 set_hidden (TpSvcDBusProperties *self,
2260 const gchar *name,
2261 const GValue *value,
2262 McdDBusPropSetFlags flags,
2263 GError **error)
2264 {
2265 McdAccount *account = MCD_ACCOUNT (self);
2266 McdAccountPrivate *priv = account->priv;
2267 const gchar *account_name = mcd_account_get_unique_name (account);
2268
2269 if (!G_VALUE_HOLDS_BOOLEAN (value))
2270 {
2271 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2272 "Hidden must be set to a boolean, not a %s",
2273 G_VALUE_TYPE_NAME (value));
2274 return FALSE;
2275 }
2276
2277 /* Technically this property is immutable after the account's been created,
2278 * but currently it's not easy for this code to tell whether or not this is
2279 * a create-time property. It would probably be better if the create-time
2280 * properties were passed into us as a construct-time GObject property. But
2281 * that's a job for another month.
2282 *
2283 * So for now we check whether the value has changed, and violate the spec
2284 * by making this property mutable (at least with the keyfile backend).
2285 */
2286 if (mcd_storage_set_attribute (priv->storage, account_name,
2287 MC_ACCOUNTS_KEY_HIDDEN, value))
2288 {
2289 mcd_storage_commit (priv->storage, account_name);
2290 mcd_account_changed_property (account, MC_ACCOUNTS_KEY_HIDDEN, value);
2291 g_object_set_property (G_OBJECT (self), "hidden", value);
2292 }
2293
2294 return TRUE;
2295 }
2296
2297 static const McdDBusProp account_hidden_properties[] = {
2298 { "Hidden", set_hidden, get_hidden },
2299 { 0 },
2300 };
2301
2302 static void
account_hidden_iface_init(McSvcAccountInterfaceHiddenClass * iface,gpointer iface_data)2303 account_hidden_iface_init (
2304 McSvcAccountInterfaceHiddenClass *iface,
2305 gpointer iface_data)
2306 {
2307 /* wow, it's pretty crap that I need this. */
2308 }
2309
2310 static void
get_password_saved(TpSvcDBusProperties * self,const gchar * name,GValue * value)2311 get_password_saved (TpSvcDBusProperties *self,
2312 const gchar *name,
2313 GValue *value)
2314 {
2315 McdAccount *account = MCD_ACCOUNT (self);
2316
2317 g_assert_cmpstr (name, ==, "PasswordSaved");
2318
2319 g_value_init (value, G_TYPE_BOOLEAN);
2320 g_value_set_boolean (value, account->priv->password_saved);
2321 }
2322
2323 static const McdDBusProp account_external_password_storage_properties[] = {
2324 { "PasswordSaved", NULL, get_password_saved },
2325 { 0 },
2326 };
2327
2328 static void
account_external_password_storage_forget_credentials_cb(TpProxy * cm,const GError * in_error,gpointer user_data,GObject * self)2329 account_external_password_storage_forget_credentials_cb (TpProxy *cm,
2330 const GError *in_error,
2331 gpointer user_data,
2332 GObject *self)
2333 {
2334 DBusGMethodInvocation *context = user_data;
2335
2336 if (in_error != NULL)
2337 {
2338 dbus_g_method_return_error (context, in_error);
2339 return;
2340 }
2341
2342 mc_svc_account_interface_external_password_storage_return_from_forget_password (context);
2343 }
2344
2345 static void
account_external_password_storage_identify_account_cb(TpProxy * protocol,const char * account_id,const GError * in_error,gpointer user_data,GObject * self)2346 account_external_password_storage_identify_account_cb (TpProxy *protocol,
2347 const char *account_id,
2348 const GError *in_error,
2349 gpointer user_data,
2350 GObject *self)
2351 {
2352 McdAccount *account = MCD_ACCOUNT (self);
2353 DBusGMethodInvocation *context = user_data;
2354 TpConnectionManager *cm = mcd_account_get_cm (account);
2355
2356 if (in_error != NULL)
2357 {
2358 dbus_g_method_return_error (context, in_error);
2359 return;
2360 }
2361
2362 DEBUG ("Identified account as %s", account_id);
2363
2364 mc_cli_connection_manager_interface_account_storage_call_forget_credentials (
2365 cm, -1, account_id,
2366 account_external_password_storage_forget_credentials_cb,
2367 context, NULL, self);
2368 }
2369
2370 static void
account_external_password_storage_forget_password(McSvcAccountInterfaceExternalPasswordStorage * self,DBusGMethodInvocation * context)2371 account_external_password_storage_forget_password (
2372 McSvcAccountInterfaceExternalPasswordStorage *self,
2373 DBusGMethodInvocation *context)
2374 {
2375 McdAccount *account = MCD_ACCOUNT (self);
2376 TpConnectionManager *cm = mcd_account_get_cm (account);
2377 TpProtocol *protocol;
2378 GHashTable *params;
2379
2380 /* do we support the interface */
2381 if (!tp_proxy_has_interface_by_id (cm,
2382 MC_IFACE_QUARK_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE))
2383 {
2384 GError *error = g_error_new (TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
2385 "CM for this Account does not implement AccountStorage iface");
2386
2387 dbus_g_method_return_error (context, error);
2388 g_error_free (error);
2389
2390 return;
2391 }
2392
2393 /* identify the account */
2394 protocol = tp_connection_manager_get_protocol_object (cm,
2395 account->priv->protocol_name);
2396 params = _mcd_account_dup_parameters (account);
2397
2398 tp_cli_protocol_call_identify_account (protocol, -1, params,
2399 account_external_password_storage_identify_account_cb,
2400 context, NULL, G_OBJECT (self));
2401
2402 g_hash_table_unref (params);
2403 }
2404
2405 static void
account_external_password_storage_iface_init(McSvcAccountInterfaceExternalPasswordStorageClass * iface,gpointer iface_data)2406 account_external_password_storage_iface_init (
2407 McSvcAccountInterfaceExternalPasswordStorageClass *iface,
2408 gpointer iface_data)
2409 {
2410 #define IMPLEMENT(x) \
2411 mc_svc_account_interface_external_password_storage_implement_##x (\
2412 iface, account_external_password_storage_##x)
2413 IMPLEMENT (forget_password);
2414 #undef IMPLEMENT
2415 }
2416
2417 static void
properties_iface_init(TpSvcDBusPropertiesClass * iface,gpointer iface_data)2418 properties_iface_init (TpSvcDBusPropertiesClass *iface, gpointer iface_data)
2419 {
2420 #define IMPLEMENT(x) tp_svc_dbus_properties_implement_##x (\
2421 iface, dbusprop_##x)
2422 IMPLEMENT(set);
2423 IMPLEMENT(get);
2424 IMPLEMENT(get_all);
2425 #undef IMPLEMENT
2426 }
2427
2428 static GType
mc_param_type(const TpConnectionManagerParam * param)2429 mc_param_type (const TpConnectionManagerParam *param)
2430 {
2431 const gchar *dbus_signature;
2432
2433 if (G_UNLIKELY (param == NULL))
2434 return G_TYPE_INVALID;
2435
2436 dbus_signature = tp_connection_manager_param_get_dbus_signature (param);
2437
2438 if (G_UNLIKELY (!dbus_signature))
2439 return G_TYPE_INVALID;
2440
2441 switch (dbus_signature[0])
2442 {
2443 case DBUS_TYPE_STRING:
2444 return G_TYPE_STRING;
2445
2446 case DBUS_TYPE_BYTE:
2447 return G_TYPE_UCHAR;
2448
2449 case DBUS_TYPE_INT16:
2450 case DBUS_TYPE_INT32:
2451 return G_TYPE_INT;
2452
2453 case DBUS_TYPE_UINT16:
2454 case DBUS_TYPE_UINT32:
2455 return G_TYPE_UINT;
2456
2457 case DBUS_TYPE_BOOLEAN:
2458 return G_TYPE_BOOLEAN;
2459
2460 case DBUS_TYPE_DOUBLE:
2461 return G_TYPE_DOUBLE;
2462
2463 case DBUS_TYPE_OBJECT_PATH:
2464 return DBUS_TYPE_G_OBJECT_PATH;
2465
2466 case DBUS_TYPE_INT64:
2467 return G_TYPE_INT64;
2468
2469 case DBUS_TYPE_UINT64:
2470 return G_TYPE_UINT64;
2471
2472 case DBUS_TYPE_ARRAY:
2473 if (dbus_signature[1] == DBUS_TYPE_STRING)
2474 return G_TYPE_STRV;
2475 /* other array types are not supported:
2476 * fall through the default case */
2477 default:
2478 g_warning ("skipping parameter %s, unknown type %s",
2479 tp_connection_manager_param_get_name (param), dbus_signature);
2480 }
2481 return G_TYPE_INVALID;
2482 }
2483
2484 typedef struct
2485 {
2486 McdAccount *self;
2487 DBusGMethodInvocation *context;
2488 } RemoveMethodData;
2489
2490 static void
account_remove_delete_cb(McdAccount * account,const GError * error,gpointer user_data)2491 account_remove_delete_cb (McdAccount *account, const GError *error,
2492 gpointer user_data)
2493 {
2494 RemoveMethodData *data = (RemoveMethodData *) user_data;
2495
2496 if (error != NULL)
2497 {
2498 dbus_g_method_return_error (data->context, (GError *) error);
2499 return;
2500 }
2501
2502 if (!data->self->priv->removed)
2503 {
2504 data->self->priv->removed = TRUE;
2505 tp_svc_account_emit_removed (data->self);
2506 }
2507
2508 tp_svc_account_return_from_remove (data->context);
2509
2510 g_slice_free (RemoveMethodData, data);
2511 }
2512
2513 static void
account_remove(TpSvcAccount * svc,DBusGMethodInvocation * context)2514 account_remove (TpSvcAccount *svc, DBusGMethodInvocation *context)
2515 {
2516 McdAccount *self = MCD_ACCOUNT (svc);
2517 RemoveMethodData *data;
2518
2519 data = g_slice_new0 (RemoveMethodData);
2520 data->self = self;
2521 data->context = context;
2522
2523 DEBUG ("called");
2524 mcd_account_delete (self, account_remove_delete_cb, data);
2525 }
2526
2527 /*
2528 * @account: the account
2529 * @name: an attribute name, or "param-" + a parameter name
2530 *
2531 * Tell the account that one of its attributes or parameters has changed
2532 * behind its back (as opposed to an external change triggered by DBus,
2533 * for example). This occurs when a storage plugin wishes to notify us
2534 * that something has changed. This will trigger an update when the
2535 * callback receives the new value. */
2536 void
mcd_account_altered_by_plugin(McdAccount * account,const gchar * name)2537 mcd_account_altered_by_plugin (McdAccount *account,
2538 const gchar *name)
2539 {
2540 /* parameters are handled en bloc, reinvoke self with bloc key: */
2541 if (g_str_has_prefix (name, "param-"))
2542 {
2543 mcd_account_altered_by_plugin (account, "Parameters");
2544 }
2545 else
2546 {
2547 guint i = 0;
2548 const McdDBusProp *prop = NULL;
2549 GValue value = G_VALUE_INIT;
2550 GError *error = NULL;
2551
2552 DEBUG ("%s", name);
2553
2554 if (tp_strdiff (name, "Parameters") &&
2555 !mcd_storage_init_value_for_attribute (&value, name))
2556 {
2557 WARNING ("plugin wants to alter %s but I don't know what "
2558 "type that ought to be", name);
2559 return;
2560 }
2561
2562 if (!tp_strdiff (name, "Parameters"))
2563 {
2564 get_parameters (TP_SVC_DBUS_PROPERTIES (account), name, &value);
2565 }
2566 else if (!mcd_storage_get_attribute (account->priv->storage,
2567 account->priv->unique_name,
2568 name, &value, &error))
2569 {
2570 WARNING ("cannot get new value of %s: %s", name, error->message);
2571 g_error_free (error);
2572 return;
2573 }
2574
2575 /* find the property update handler */
2576 for (; prop == NULL && account_properties[i].name != NULL; i++)
2577 {
2578 if (g_str_equal (name, account_properties[i].name))
2579 prop = &account_properties[i];
2580 }
2581
2582 /* is a known property: invoke the getter method for it (if any): *
2583 * then issue the change notification (DBus signals etc) for it */
2584 if (prop != NULL)
2585 {
2586 /* poke the value back into itself with the setter: this *
2587 * extra round-trip may trigger extra actions like notifying *
2588 * the connection manager of the change, even though our own *
2589 * internal storage already has this value and needn't change */
2590 if (prop->setprop != NULL)
2591 {
2592 DEBUG ("Calling property setter for %s", name);
2593 if (!prop->setprop (TP_SVC_DBUS_PROPERTIES (account),
2594 prop->name, &value,
2595 MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE,
2596 &error))
2597 {
2598 WARNING ("Unable to set %s: %s", name, error->message);
2599 g_error_free (error);
2600 }
2601 }
2602 else
2603 {
2604 DEBUG ("Emitting signal directly for %s", name);
2605 mcd_account_changed_property (account, prop->name, &value);
2606 }
2607 }
2608 else
2609 {
2610 DEBUG ("%s does not appear to be an Account property", name);
2611 }
2612
2613 g_value_unset (&value);
2614 }
2615 }
2616
2617
2618 static void
mcd_account_check_parameters(McdAccount * account,CheckParametersCb callback,gpointer user_data)2619 mcd_account_check_parameters (McdAccount *account,
2620 CheckParametersCb callback,
2621 gpointer user_data)
2622 {
2623 McdAccountPrivate *priv = account->priv;
2624 TpProtocol *protocol;
2625 GList *params = NULL;
2626 GList *iter;
2627 GError *error = NULL;
2628
2629 g_return_if_fail (callback != NULL);
2630
2631 DEBUG ("called for %s", priv->unique_name);
2632 protocol = _mcd_manager_dup_protocol (priv->manager, priv->protocol_name);
2633
2634 if (protocol == NULL)
2635 {
2636 g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2637 "CM '%s' doesn't implement protocol '%s'", priv->manager_name,
2638 priv->protocol_name);
2639 goto out;
2640 }
2641
2642 params = tp_protocol_dup_params (protocol);
2643
2644 for (iter = params; iter != NULL; iter = iter->next)
2645 {
2646 TpConnectionManagerParam *param = iter->data;
2647 const gchar *param_name = tp_connection_manager_param_get_name (param);
2648
2649 if (!tp_connection_manager_param_is_required ((param)))
2650 continue;
2651
2652 if (!mcd_account_get_parameter (account, param_name, NULL, NULL))
2653 {
2654 g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2655 "missing required parameter '%s'", param_name);
2656 goto out;
2657 }
2658 }
2659
2660 out:
2661 if (error != NULL)
2662 {
2663 DEBUG ("%s", error->message);
2664 }
2665
2666 callback (account, error, user_data);
2667 g_clear_error (&error);
2668 g_list_free_full (params,
2669 (GDestroyNotify) tp_connection_manager_param_free);
2670 g_clear_object (&protocol);
2671 }
2672
2673 static void
set_parameters_maybe_autoconnect_cb(McdAccount * account,const GError * invalid_reason,gpointer user_data G_GNUC_UNUSED)2674 set_parameters_maybe_autoconnect_cb (McdAccount *account,
2675 const GError *invalid_reason,
2676 gpointer user_data G_GNUC_UNUSED)
2677 {
2678 /* Strictly speaking this doesn't need to be called unless invalid_reason
2679 * is NULL, but calling it in all cases gives us clearer debug output */
2680 _mcd_account_maybe_autoconnect (account);
2681 }
2682
2683 static void
apply_parameter_updates(McdAccount * account,GHashTable * params,const gchar ** unset,GHashTable * dbus_properties)2684 apply_parameter_updates (McdAccount *account,
2685 GHashTable *params,
2686 const gchar **unset,
2687 GHashTable *dbus_properties)
2688 {
2689 McdAccountPrivate *priv = account->priv;
2690 GHashTableIter iter;
2691 gpointer name, value;
2692 const gchar **unset_iter;
2693
2694 g_hash_table_iter_init (&iter, params);
2695 while (g_hash_table_iter_next (&iter, &name, &value))
2696 {
2697 _mcd_account_set_parameter (account, name, value);
2698 }
2699
2700 for (unset_iter = unset;
2701 unset_iter != NULL && *unset_iter != NULL;
2702 unset_iter++)
2703 {
2704 _mcd_account_set_parameter (account, *unset_iter, NULL);
2705 }
2706
2707 if (mcd_account_get_connection_status (account) ==
2708 TP_CONNECTION_STATUS_CONNECTED)
2709 {
2710 g_hash_table_iter_init (&iter, dbus_properties);
2711 while (g_hash_table_iter_next (&iter, &name, &value))
2712 {
2713 DEBUG ("updating parameter %s", (const gchar *) name);
2714 _mcd_connection_update_property (priv->connection, name, value);
2715 }
2716 }
2717
2718 mcd_account_check_validity (account,
2719 set_parameters_maybe_autoconnect_cb, NULL);
2720 }
2721
2722 static void
set_parameter_changed(GHashTable * dbus_properties,GPtrArray * not_yet,const TpConnectionManagerParam * param,const GValue * new_value)2723 set_parameter_changed (GHashTable *dbus_properties,
2724 GPtrArray *not_yet,
2725 const TpConnectionManagerParam *param,
2726 const GValue *new_value)
2727 {
2728 const gchar *name = tp_connection_manager_param_get_name (param);
2729
2730 DEBUG ("Parameter %s changed", name);
2731
2732 /* can the param be updated on the fly? If yes, prepare to do so; and if
2733 * not, prepare to reset the connection */
2734 if (tp_connection_manager_param_is_dbus_property (param))
2735 {
2736 g_hash_table_insert (dbus_properties, g_strdup (name),
2737 tp_g_value_slice_dup (new_value));
2738 }
2739 else
2740 {
2741 g_ptr_array_add (not_yet, g_strdup (name));
2742 }
2743 }
2744
2745 static gboolean
check_one_parameter_update(McdAccount * account,TpProtocol * protocol,GHashTable * dbus_properties,GPtrArray * not_yet,const gchar * name,const GValue * new_value,GError ** error)2746 check_one_parameter_update (McdAccount *account,
2747 TpProtocol *protocol,
2748 GHashTable *dbus_properties,
2749 GPtrArray *not_yet,
2750 const gchar *name,
2751 const GValue *new_value,
2752 GError **error)
2753 {
2754 const TpConnectionManagerParam *param =
2755 tp_protocol_get_param (protocol, name);
2756 GType type;
2757
2758 if (param == NULL)
2759 {
2760 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2761 "Protocol '%s' does not have parameter '%s'",
2762 tp_protocol_get_name (protocol), name);
2763 return FALSE;
2764 }
2765
2766 type = mc_param_type (param);
2767
2768 if (G_VALUE_TYPE (new_value) != type)
2769 {
2770 /* FIXME: use D-Bus type names, not GType names. */
2771 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2772 "parameter '%s' must be of type %s, not %s",
2773 tp_connection_manager_param_get_name (param),
2774 g_type_name (type), G_VALUE_TYPE_NAME (new_value));
2775 return FALSE;
2776 }
2777
2778 if (mcd_account_get_connection_status (account) ==
2779 TP_CONNECTION_STATUS_CONNECTED)
2780 {
2781 GValue current_value = G_VALUE_INIT;
2782
2783 /* Check if the parameter's current value (or its default, if it has
2784 * one and it's not set to anything) matches the new value.
2785 */
2786 if (mcd_account_get_parameter (account, tp_connection_manager_param_get_name (param),
2787 ¤t_value, NULL) ||
2788 tp_connection_manager_param_get_default (param, ¤t_value))
2789 {
2790 if (!value_is_same (¤t_value, new_value))
2791 set_parameter_changed (dbus_properties, not_yet, param,
2792 new_value);
2793
2794 g_value_unset (¤t_value);
2795 }
2796 else
2797 {
2798 /* The parameter wasn't previously set, and has no default value;
2799 * this update must be a change.
2800 */
2801 set_parameter_changed (dbus_properties, not_yet, param, new_value);
2802 }
2803 }
2804
2805 return TRUE;
2806 }
2807
2808 static gboolean
check_one_parameter_unset(McdAccount * account,TpProtocol * protocol,GHashTable * dbus_properties,GPtrArray * not_yet,const gchar * name,GError ** error)2809 check_one_parameter_unset (McdAccount *account,
2810 TpProtocol *protocol,
2811 GHashTable *dbus_properties,
2812 GPtrArray *not_yet,
2813 const gchar *name,
2814 GError **error)
2815 {
2816 const TpConnectionManagerParam *param =
2817 tp_protocol_get_param (protocol, name);
2818
2819 /* The spec decrees that “If the given parameters […] do not exist at all,
2820 * the account manager MUST accept this without error.”. Thus this function
2821 * is a no-op if @name doesn't actually exist.
2822 */
2823 if (param != NULL &&
2824 mcd_account_get_connection_status (account) ==
2825 TP_CONNECTION_STATUS_CONNECTED)
2826 {
2827 GValue current_value = G_VALUE_INIT;
2828
2829 if (mcd_account_get_parameter (account, tp_connection_manager_param_get_name (param),
2830 ¤t_value, NULL))
2831 {
2832 /* There's an existing value; let's see if it's the same as the
2833 * default, if any.
2834 */
2835 GValue default_value = G_VALUE_INIT;
2836
2837 if (tp_connection_manager_param_get_default (param, &default_value))
2838 {
2839 if (!value_is_same (¤t_value, &default_value))
2840 set_parameter_changed (dbus_properties, not_yet, param,
2841 &default_value);
2842
2843 g_value_unset (&default_value);
2844 }
2845 else
2846 {
2847 /* It has no default; we're gonna have to reconnect to make
2848 * this take effect.
2849 */
2850 g_ptr_array_add (not_yet, g_strdup (tp_connection_manager_param_get_name (param)));
2851 }
2852
2853 g_value_unset (¤t_value);
2854 }
2855 }
2856
2857 return TRUE;
2858 }
2859
2860 static gboolean
check_parameters(McdAccount * account,TpProtocol * protocol,GHashTable * params,const gchar ** unset,GHashTable * dbus_properties,GPtrArray * not_yet,GError ** error)2861 check_parameters (McdAccount *account,
2862 TpProtocol *protocol,
2863 GHashTable *params,
2864 const gchar **unset,
2865 GHashTable *dbus_properties,
2866 GPtrArray *not_yet,
2867 GError **error)
2868 {
2869 GHashTableIter iter;
2870 gpointer key, value;
2871 const gchar **unset_iter;
2872
2873 g_hash_table_iter_init (&iter, params);
2874 while (g_hash_table_iter_next (&iter, &key, &value))
2875 {
2876 if (!check_one_parameter_update (account, protocol, dbus_properties,
2877 not_yet, key, value, error))
2878 return FALSE;
2879 }
2880
2881 for (unset_iter = unset;
2882 unset_iter != NULL && *unset_iter != NULL;
2883 unset_iter++)
2884 {
2885 if (!check_one_parameter_unset (account, protocol, dbus_properties,
2886 not_yet, *unset_iter, error))
2887 return FALSE;
2888 }
2889
2890 return TRUE;
2891 }
2892
2893 /*
2894 * _mcd_account_set_parameters:
2895 * @account: the #McdAccount.
2896 * @name: the parameter name.
2897 * @params: names and values of parameters to set
2898 * @unset: names of parameters to unset
2899 * @callback: function to be called when finished
2900 * @user_data: data to be passed to @callback
2901 *
2902 * Alter the account parameters.
2903 *
2904 */
2905 void
_mcd_account_set_parameters(McdAccount * account,GHashTable * params,const gchar ** unset,McdAccountSetParametersCb callback,gpointer user_data)2906 _mcd_account_set_parameters (McdAccount *account, GHashTable *params,
2907 const gchar **unset,
2908 McdAccountSetParametersCb callback,
2909 gpointer user_data)
2910 {
2911 McdAccountPrivate *priv = account->priv;
2912 GHashTable *dbus_properties = NULL;
2913 GPtrArray *not_yet = NULL;
2914 GError *error = NULL;
2915 TpProtocol *protocol = NULL;
2916
2917 DEBUG ("called");
2918 if (G_UNLIKELY (!priv->manager && !load_manager (account)))
2919 {
2920 /* FIXME: this branch is never reached, even if the specified CM
2921 * doesn't actually exist: load_manager essentially always succeeds,
2922 * but of course the TpCM hasn't prepared (or failed, as it will if we
2923 * would like to hit this path) yet. So in practice we hit the next
2924 * block for nonexistant CMs too.
2925 */
2926 g_set_error (&error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
2927 "Manager '%s' not found", priv->manager_name);
2928 goto out;
2929 }
2930
2931 protocol = _mcd_manager_dup_protocol (priv->manager, priv->protocol_name);
2932
2933 if (G_UNLIKELY (protocol == NULL))
2934 {
2935 g_set_error (&error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
2936 "Protocol '%s' not found on CM '%s'", priv->protocol_name,
2937 priv->manager_name);
2938 goto out;
2939 }
2940
2941 /* An a{sv} of DBus_Property parameters we should set on the connection. */
2942 dbus_properties = g_hash_table_new_full (g_str_hash, g_str_equal,
2943 g_free, (GDestroyNotify) tp_g_value_slice_free);
2944 not_yet = g_ptr_array_new_with_free_func (g_free);
2945
2946 if (check_parameters (account, protocol, params, unset, dbus_properties,
2947 not_yet, &error))
2948 apply_parameter_updates (account, params, unset, dbus_properties);
2949
2950 out:
2951 if (callback != NULL)
2952 {
2953 if (error == NULL)
2954 callback (account, not_yet, NULL, user_data);
2955 else
2956 callback (account, NULL, error, user_data);
2957 }
2958
2959 g_clear_error (&error);
2960 tp_clear_pointer (&dbus_properties, g_hash_table_unref);
2961 tp_clear_pointer (¬_yet, g_ptr_array_unref);
2962 g_clear_object (&protocol);
2963 }
2964
2965 static void
account_update_parameters_cb(McdAccount * account,GPtrArray * not_yet,const GError * error,gpointer user_data)2966 account_update_parameters_cb (McdAccount *account, GPtrArray *not_yet,
2967 const GError *error, gpointer user_data)
2968 {
2969 McdAccountPrivate *priv = account->priv;
2970 DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
2971 const gchar *account_name = mcd_account_get_unique_name (account);
2972 GHashTable *params;
2973 GValue value = G_VALUE_INIT;
2974
2975 if (error != NULL)
2976 {
2977 dbus_g_method_return_error (context, (GError *) error);
2978 return;
2979 }
2980
2981 /* Emit the PropertiesChanged signal */
2982 params = _mcd_account_dup_parameters (account);
2983 g_return_if_fail (params != NULL);
2984
2985 g_value_init (&value, TP_HASH_TYPE_STRING_VARIANT_MAP);
2986 g_value_take_boxed (&value, params);
2987 mcd_account_changed_property (account, "Parameters", &value);
2988 g_value_unset (&value);
2989
2990 /* Commit the changes to disk */
2991 mcd_storage_commit (priv->storage, account_name);
2992
2993 /* And finally, return from UpdateParameters() */
2994 g_ptr_array_add (not_yet, NULL);
2995 tp_svc_account_return_from_update_parameters (context,
2996 (const gchar **) not_yet->pdata);
2997 }
2998
2999 static void
account_update_parameters(TpSvcAccount * self,GHashTable * set,const gchar ** unset,DBusGMethodInvocation * context)3000 account_update_parameters (TpSvcAccount *self, GHashTable *set,
3001 const gchar **unset, DBusGMethodInvocation *context)
3002 {
3003 McdAccount *account = MCD_ACCOUNT (self);
3004 McdAccountPrivate *priv = account->priv;
3005
3006 DEBUG ("called for %s", priv->unique_name);
3007
3008 _mcd_account_set_parameters (account, set, unset,
3009 account_update_parameters_cb, context);
3010 }
3011
3012 void
_mcd_account_reconnect(McdAccount * self,gboolean user_initiated)3013 _mcd_account_reconnect (McdAccount *self,
3014 gboolean user_initiated)
3015 {
3016 /* FIXME: this isn't quite right. If we've just called RequestConnection
3017 * (possibly with out of date parameters) but we haven't got a Connection
3018 * back from the CM yet, the old parameters will still be used, I think
3019 * (I can't quite make out what actually happens). */
3020 if (self->priv->connection)
3021 mcd_connection_close (self->priv->connection, NULL);
3022
3023 _mcd_account_connection_begin (self, user_initiated);
3024 }
3025
3026 static void
account_reconnect(TpSvcAccount * service,DBusGMethodInvocation * context)3027 account_reconnect (TpSvcAccount *service,
3028 DBusGMethodInvocation *context)
3029 {
3030 McdAccount *self = MCD_ACCOUNT (service);
3031 McdAccountPrivate *priv = self->priv;
3032
3033 DEBUG ("%s", mcd_account_get_unique_name (self));
3034
3035 /* if we can't, or don't want to, connect this method is a no-op */
3036 if (!priv->enabled ||
3037 !mcd_account_is_valid (self) ||
3038 priv->req_presence_type == TP_CONNECTION_PRESENCE_TYPE_OFFLINE)
3039 {
3040 DEBUG ("doing nothing (enabled=%c, valid=%c and "
3041 "combined presence=%i)",
3042 self->priv->enabled ? 'T' : 'F',
3043 mcd_account_is_valid (self) ? 'T' : 'F',
3044 self->priv->req_presence_type);
3045 tp_svc_account_return_from_reconnect (context);
3046 return;
3047 }
3048
3049 /* Reconnect() counts as user-initiated */
3050 _mcd_account_reconnect (self, TRUE);
3051
3052 /* FIXME: we shouldn't really return from this method until the
3053 * reconnection has actually happened, but that would require less tangled
3054 * integration between Account and Connection */
3055 tp_svc_account_return_from_reconnect (context);
3056 }
3057
3058 static void
account_iface_init(TpSvcAccountClass * iface,gpointer iface_data)3059 account_iface_init (TpSvcAccountClass *iface, gpointer iface_data)
3060 {
3061 #define IMPLEMENT(x) tp_svc_account_implement_##x (\
3062 iface, account_##x)
3063 IMPLEMENT(remove);
3064 IMPLEMENT(update_parameters);
3065 IMPLEMENT(reconnect);
3066 #undef IMPLEMENT
3067 }
3068
3069 static void
register_dbus_service(McdAccount * self,const GError * error,gpointer unused G_GNUC_UNUSED)3070 register_dbus_service (McdAccount *self,
3071 const GError *error,
3072 gpointer unused G_GNUC_UNUSED)
3073 {
3074 DBusGConnection *dbus_connection;
3075 TpDBusDaemon *dbus_daemon;
3076
3077 if (error != NULL)
3078 {
3079 /* due to some tangled error handling, the McdAccount might already
3080 * have been freed by the time we get here, so it's no longer safe to
3081 * dereference self here! */
3082 DEBUG ("%p failed to load: %s code %d: %s", self,
3083 g_quark_to_string (error->domain), error->code, error->message);
3084 return;
3085 }
3086
3087 g_assert (MCD_IS_ACCOUNT (self));
3088 /* these are invariants - the storage is set at construct-time
3089 * and the object path is set in mcd_account_setup, both of which are
3090 * run before this callback can possibly be invoked */
3091 g_assert (self->priv->storage != NULL);
3092 g_assert (self->priv->object_path != NULL);
3093
3094 dbus_daemon = self->priv->dbus_daemon;
3095 g_return_if_fail (dbus_daemon != NULL);
3096
3097 dbus_connection = tp_proxy_get_dbus_connection (TP_PROXY (dbus_daemon));
3098
3099 if (G_LIKELY (dbus_connection)) {
3100 dbus_g_connection_register_g_object (dbus_connection,
3101 self->priv->object_path,
3102 (GObject *) self);
3103
3104 self->priv->registered = TRUE;
3105 }
3106 }
3107
3108 /*
3109 * @account: (allow-none):
3110 * @dir_out: (out): e.g. ~/.local/share/telepathy/mission-control
3111 * @basename_out: (out): e.g. gabble-jabber-fred_40example_2ecom.avatar
3112 * @file_out: (out): @dir_out + "/" + @basename_out
3113 */
3114 static void
get_avatar_paths(McdAccount * account,gchar ** dir_out,gchar ** basename_out,gchar ** file_out)3115 get_avatar_paths (McdAccount *account,
3116 gchar **dir_out,
3117 gchar **basename_out,
3118 gchar **file_out)
3119 {
3120 gchar *dir = NULL;
3121
3122 dir = g_build_filename (g_get_user_data_dir (),
3123 "telepathy", "mission-control", NULL);
3124
3125 if (account == NULL)
3126 {
3127 if (file_out != NULL)
3128 *file_out = NULL;
3129
3130 if (basename_out != NULL)
3131 *basename_out = NULL;
3132 }
3133 else if (basename_out != NULL || file_out != NULL)
3134 {
3135 gchar *basename = NULL;
3136
3137 basename = g_strdup_printf ("%s.avatar", account->priv->unique_name);
3138 g_strdelimit (basename, "/", '-');
3139
3140 if (file_out != NULL)
3141 *file_out = g_build_filename (dir, basename, NULL);
3142
3143 if (basename_out != NULL)
3144 *basename_out = basename;
3145 else
3146 g_free (basename);
3147 }
3148
3149 if (dir_out != NULL)
3150 *dir_out = dir;
3151 else
3152 g_free (dir);
3153 }
3154
3155 static gboolean
save_avatar(McdAccount * self,gpointer data,gssize len,GError ** error)3156 save_avatar (McdAccount *self,
3157 gpointer data,
3158 gssize len,
3159 GError **error)
3160 {
3161 gchar *dir = NULL;
3162 gchar *file = NULL;
3163 gboolean ret = FALSE;
3164
3165 get_avatar_paths (self, &dir, NULL, &file);
3166
3167 if (mcd_ensure_directory (dir, error) &&
3168 g_file_set_contents (file, data, len, error))
3169 {
3170 DEBUG ("Saved avatar to %s", file);
3171 ret = TRUE;
3172 }
3173 else if (len == 0)
3174 {
3175 GArray *avatar = NULL;
3176
3177 /* It failed, but maybe that's OK, since we didn't really want
3178 * an avatar anyway. */
3179 _mcd_account_get_avatar (self, &avatar, NULL);
3180
3181 if (avatar == NULL)
3182 {
3183 /* Creating the empty file failed, but it's fine, since what's
3184 * on disk correctly indicates that we have no avatar. */
3185 DEBUG ("Ignoring failure to write empty avatar");
3186
3187 if (error != NULL)
3188 g_clear_error (error);
3189 }
3190 else
3191 {
3192 /* Continue to raise the error: we failed to write a 0-byte
3193 * file into the highest-priority avatar directory, and we do
3194 * need it, since there is a non-empty avatar in either that
3195 * directory or a lower-priority directory */
3196 g_array_free (avatar, TRUE);
3197 }
3198 }
3199
3200 g_free (dir);
3201 g_free (file);
3202 return ret;
3203 }
3204
3205 static gchar *_mcd_account_get_old_avatar_filename (McdAccount *account,
3206 gchar **old_dir);
3207
3208 static void
mcd_account_migrate_avatar(McdAccount * account)3209 mcd_account_migrate_avatar (McdAccount *account)
3210 {
3211 GError *error = NULL;
3212 gchar *old_file;
3213 gchar *old_dir = NULL;
3214 gchar *new_dir = NULL;
3215 gchar *basename = NULL;
3216 gchar *new_file = NULL;
3217 gchar *contents = NULL;
3218 guint i;
3219
3220 /* Try to migrate the avatar to a better location */
3221 old_file = _mcd_account_get_old_avatar_filename (account, &old_dir);
3222
3223 if (!g_file_test (old_file, G_FILE_TEST_EXISTS))
3224 {
3225 /* nothing to do */
3226 goto finally;
3227 }
3228
3229 DEBUG ("Migrating avatar from %s", old_file);
3230
3231 get_avatar_paths (account, &new_dir, &basename, &new_file);
3232
3233 if (g_file_test (new_file, G_FILE_TEST_IS_REGULAR))
3234 {
3235 DEBUG ("... already migrated to %s", new_file);
3236
3237 if (g_unlink (old_file) != 0)
3238 {
3239 DEBUG ("Failed to unlink %s: %s", old_file,
3240 g_strerror (errno));
3241 }
3242
3243 goto finally;
3244 }
3245
3246 if (!mcd_ensure_directory (new_dir, &error))
3247 {
3248 DEBUG ("%s", error->message);
3249 goto finally;
3250 }
3251
3252 if (g_rename (old_file, new_file) == 0)
3253 {
3254 DEBUG ("Renamed %s to %s", old_file, new_file);
3255 }
3256 else
3257 {
3258 gsize len;
3259
3260 DEBUG ("Unable to rename %s to %s, will try copy+delete: %s",
3261 old_file, new_file, g_strerror (errno));
3262
3263 if (!g_file_get_contents (old_file, &contents, &len, &error))
3264 {
3265 DEBUG ("Unable to load old avatar %s: %s", old_file,
3266 error->message);
3267 goto finally;
3268 }
3269
3270 if (g_file_set_contents (new_file, contents, len, &error))
3271 {
3272 DEBUG ("Copied old avatar from %s to %s", old_file, new_file);
3273 }
3274 else
3275 {
3276 DEBUG ("Unable to save new avatar %s: %s", new_file,
3277 error->message);
3278 goto finally;
3279 }
3280
3281 if (g_unlink (old_file) != 0)
3282 {
3283 DEBUG ("Failed to unlink %s: %s", old_file, g_strerror (errno));
3284 goto finally;
3285 }
3286 }
3287
3288 /* old_dir is typically ~/.mission-control/accounts/gabble/jabber/badger0.
3289 * We want to delete badger0, jabber, gabble, accounts if they are empty.
3290 * If they are not, we'll just get ENOTEMPTY and stop. */
3291 for (i = 0; i < 4; i++)
3292 {
3293 gchar *tmp;
3294
3295 if (g_rmdir (old_dir) != 0)
3296 {
3297 DEBUG ("Failed to rmdir %s: %s", old_dir, g_strerror (errno));
3298 goto finally;
3299 }
3300
3301 tmp = g_path_get_dirname (old_dir);
3302 g_free (old_dir);
3303 old_dir = tmp;
3304 }
3305
3306 finally:
3307 g_clear_error (&error);
3308 g_free (basename);
3309 g_free (new_file);
3310 g_free (new_dir);
3311 g_free (contents);
3312 g_free (old_file);
3313 g_free (old_dir);
3314 }
3315
3316 static gboolean
mcd_account_setup(McdAccount * account)3317 mcd_account_setup (McdAccount *account)
3318 {
3319 McdAccountPrivate *priv = account->priv;
3320 McdStorage *storage = priv->storage;
3321 const gchar *name = mcd_account_get_unique_name (account);
3322 GValue value = G_VALUE_INIT;
3323
3324 priv->manager_name =
3325 mcd_storage_dup_string (storage, name, MC_ACCOUNTS_KEY_MANAGER);
3326
3327 if (priv->manager_name == NULL)
3328 {
3329 g_warning ("Account '%s' has no manager", name);
3330 goto broken_account;
3331 }
3332
3333 priv->protocol_name =
3334 mcd_storage_dup_string (storage, name, MC_ACCOUNTS_KEY_PROTOCOL);
3335
3336 if (priv->protocol_name == NULL)
3337 {
3338 g_warning ("Account has no protocol");
3339 goto broken_account;
3340 }
3341
3342 priv->object_path = g_strconcat (TP_ACCOUNT_OBJECT_PATH_BASE, name, NULL);
3343
3344 if (!priv->always_on)
3345 {
3346 priv->enabled =
3347 mcd_storage_get_boolean (storage, name, MC_ACCOUNTS_KEY_ENABLED);
3348
3349 priv->connect_automatically =
3350 mcd_storage_get_boolean (storage, name,
3351 MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY);
3352 }
3353
3354 priv->has_been_online =
3355 mcd_storage_get_boolean (storage, name, MC_ACCOUNTS_KEY_HAS_BEEN_ONLINE);
3356 priv->hidden =
3357 mcd_storage_get_boolean (storage, name, MC_ACCOUNTS_KEY_HIDDEN);
3358
3359 /* special case flag (for ring accounts, so far) */
3360 priv->always_dispatch =
3361 mcd_storage_get_boolean (storage, name, MC_ACCOUNTS_KEY_ALWAYS_DISPATCH);
3362
3363 g_value_init (&value, TP_STRUCT_TYPE_SIMPLE_PRESENCE);
3364
3365 g_free (priv->auto_presence_status);
3366 g_free (priv->auto_presence_message);
3367
3368 if (mcd_storage_get_attribute (storage, name,
3369 MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE, &value,
3370 NULL))
3371 {
3372 GValueArray *va = g_value_get_boxed (&value);
3373
3374 priv->auto_presence_type = g_value_get_uint (va->values + 0);
3375 priv->auto_presence_status = g_value_dup_string (va->values + 1);
3376 priv->auto_presence_message = g_value_dup_string (va->values + 2);
3377
3378 if (priv->auto_presence_status == NULL)
3379 priv->auto_presence_status = g_strdup ("");
3380 if (priv->auto_presence_message == NULL)
3381 priv->auto_presence_message = g_strdup ("");
3382 }
3383 else
3384 {
3385 /* try the old versions */
3386 priv->auto_presence_type =
3387 mcd_storage_get_integer (storage, name,
3388 MC_ACCOUNTS_KEY_AUTO_PRESENCE_TYPE);
3389 priv->auto_presence_status =
3390 mcd_storage_dup_string (storage, name,
3391 MC_ACCOUNTS_KEY_AUTO_PRESENCE_STATUS);
3392 priv->auto_presence_message =
3393 mcd_storage_dup_string (storage, name,
3394 MC_ACCOUNTS_KEY_AUTO_PRESENCE_MESSAGE);
3395
3396 if (priv->auto_presence_status == NULL)
3397 priv->auto_presence_status = g_strdup ("");
3398 if (priv->auto_presence_message == NULL)
3399 priv->auto_presence_message = g_strdup ("");
3400
3401 /* migrate to a more sensible storage format */
3402 g_value_take_boxed (&value, tp_value_array_build (3,
3403 G_TYPE_UINT, (guint) priv->auto_presence_type,
3404 G_TYPE_STRING, priv->auto_presence_status,
3405 G_TYPE_STRING, priv->auto_presence_message,
3406 G_TYPE_INVALID));
3407
3408 if (mcd_storage_set_attribute (storage, name,
3409 MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE,
3410 &value))
3411 {
3412 mcd_storage_set_attribute (storage, name,
3413 MC_ACCOUNTS_KEY_AUTO_PRESENCE_TYPE,
3414 NULL);
3415 mcd_storage_set_attribute (storage, name,
3416 MC_ACCOUNTS_KEY_AUTO_PRESENCE_STATUS,
3417 NULL);
3418 mcd_storage_set_attribute (storage, name,
3419 MC_ACCOUNTS_KEY_AUTO_PRESENCE_MESSAGE,
3420 NULL);
3421 mcd_storage_commit (storage, name);
3422 }
3423 }
3424
3425 /* If invalid or something, force it to AVAILABLE - we want the auto
3426 * presence type to be an online status */
3427 if (!_presence_type_is_online (priv->auto_presence_type))
3428 {
3429 priv->auto_presence_type = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
3430 g_free (priv->auto_presence_status);
3431 priv->auto_presence_status = g_strdup ("available");
3432 }
3433
3434 g_value_unset (&value);
3435 g_value_init (&value, TP_ARRAY_TYPE_OBJECT_PATH_LIST);
3436
3437 if (priv->supersedes != NULL)
3438 g_ptr_array_unref (priv->supersedes);
3439
3440 if (mcd_storage_get_attribute (storage, name,
3441 MC_ACCOUNTS_KEY_SUPERSEDES, &value, NULL))
3442 {
3443 priv->supersedes = g_value_dup_boxed (&value);
3444 }
3445 else
3446 {
3447 priv->supersedes = g_ptr_array_new ();
3448 }
3449
3450 g_value_unset (&value);
3451
3452 /* check the manager */
3453 if (!priv->manager && !load_manager (account))
3454 {
3455 g_warning ("Could not find manager `%s'", priv->manager_name);
3456 mcd_account_loaded (account);
3457 }
3458
3459 /* even though the manager is absent or unusable, we still register *
3460 * the accounts dbus name as it is otherwise acceptably configured */
3461
3462 _mcd_account_load (account, register_dbus_service, NULL);
3463 return TRUE;
3464
3465 broken_account:
3466 /* normally, various callbacks would release locks when the manager *
3467 * became ready: however, this cannot happen for an incomplete account *
3468 * as it never gets a manager: We therefore invoke the account callbacks *
3469 * right now so the account manager doesn't hang around forever waiting *
3470 * for an event that cannot happen (at least until the account is fixed) */
3471 mcd_account_loaded (account);
3472 return FALSE;
3473 }
3474
3475 static void
set_property(GObject * obj,guint prop_id,const GValue * val,GParamSpec * pspec)3476 set_property (GObject *obj, guint prop_id,
3477 const GValue *val, GParamSpec *pspec)
3478 {
3479 McdAccount *account = MCD_ACCOUNT (obj);
3480 McdAccountPrivate *priv = account->priv;
3481
3482 switch (prop_id)
3483 {
3484 case PROP_STORAGE:
3485 g_assert (priv->storage == NULL);
3486 priv->storage = g_value_dup_object (val);
3487 break;
3488
3489 case PROP_DBUS_DAEMON:
3490 g_assert (priv->dbus_daemon == NULL);
3491 priv->dbus_daemon = g_value_dup_object (val);
3492 break;
3493
3494 case PROP_CONNECTIVITY_MONITOR:
3495 g_assert (priv->connectivity == NULL);
3496 priv->connectivity = g_value_dup_object (val);
3497 break;
3498
3499 case PROP_NAME:
3500 g_assert (priv->unique_name == NULL);
3501 priv->unique_name = g_value_dup_string (val);
3502 break;
3503
3504 case PROP_ALWAYS_ON:
3505 priv->always_on = g_value_get_boolean (val);
3506
3507 if (priv->always_on)
3508 {
3509 priv->enabled = TRUE;
3510 priv->connect_automatically = TRUE;
3511 priv->req_presence_type = priv->auto_presence_type;
3512 priv->req_presence_status = g_strdup (priv->auto_presence_status);
3513 priv->req_presence_message = g_strdup (priv->auto_presence_message);
3514 }
3515
3516 break;
3517 case PROP_HIDDEN:
3518 priv->hidden = g_value_get_boolean (val);
3519 break;
3520 default:
3521 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
3522 break;
3523 }
3524 }
3525
3526 static void
get_property(GObject * obj,guint prop_id,GValue * val,GParamSpec * pspec)3527 get_property (GObject *obj, guint prop_id,
3528 GValue *val, GParamSpec *pspec)
3529 {
3530 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (obj);
3531
3532 switch (prop_id)
3533 {
3534 case PROP_DBUS_DAEMON:
3535 g_value_set_object (val, priv->dbus_daemon);
3536 break;
3537
3538 case PROP_CONNECTIVITY_MONITOR:
3539 g_value_set_object (val, priv->connectivity);
3540 break;
3541
3542 case PROP_NAME:
3543 g_value_set_string (val, priv->unique_name);
3544 break;
3545 case PROP_HIDDEN:
3546 g_value_set_boolean (val, priv->hidden);
3547 break;
3548 default:
3549 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
3550 break;
3551 }
3552 }
3553
3554 static void
_mcd_account_finalize(GObject * object)3555 _mcd_account_finalize (GObject *object)
3556 {
3557 McdAccount *account = MCD_ACCOUNT (object);
3558 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
3559
3560 DEBUG ("%p (%s)", object, priv->unique_name);
3561
3562 if (priv->changed_properties)
3563 g_hash_table_unref (priv->changed_properties);
3564 if (priv->properties_source != 0)
3565 g_source_remove (priv->properties_source);
3566
3567 tp_clear_pointer (&priv->curr_presence_status, g_free);
3568 tp_clear_pointer (&priv->curr_presence_message, g_free);
3569
3570 tp_clear_pointer (&priv->req_presence_status, g_free);
3571 tp_clear_pointer (&priv->req_presence_message, g_free);
3572
3573 tp_clear_pointer (&priv->auto_presence_status, g_free);
3574 tp_clear_pointer (&priv->auto_presence_message, g_free);
3575
3576 tp_clear_pointer (&priv->manager_name, g_free);
3577 tp_clear_pointer (&priv->protocol_name, g_free);
3578 tp_clear_pointer (&priv->unique_name, g_free);
3579 tp_clear_pointer (&priv->object_path, g_free);
3580
3581 G_OBJECT_CLASS (mcd_account_parent_class)->finalize (object);
3582 }
3583
3584 static void
_mcd_account_dispose(GObject * object)3585 _mcd_account_dispose (GObject *object)
3586 {
3587 McdAccount *self = MCD_ACCOUNT (object);
3588 McdAccountPrivate *priv = self->priv;
3589
3590 DEBUG ("%p (%s)", object, priv->unique_name);
3591
3592 if (!self->priv->removed)
3593 {
3594 self->priv->removed = TRUE;
3595 tp_svc_account_emit_removed (self);
3596 }
3597
3598 if (priv->online_requests)
3599 {
3600 GError *error;
3601 GList *list = priv->online_requests;
3602
3603 error = g_error_new (TP_ERROR, TP_ERROR_DISCONNECTED,
3604 "Disposing account %s", priv->unique_name);
3605 while (list)
3606 {
3607 McdOnlineRequestData *data = list->data;
3608
3609 data->callback (MCD_ACCOUNT (object), data->user_data, error);
3610 g_slice_free (McdOnlineRequestData, data);
3611 list = g_list_delete_link (list, list);
3612 }
3613 g_error_free (error);
3614 priv->online_requests = NULL;
3615 }
3616
3617 tp_clear_object (&priv->manager);
3618 tp_clear_object (&priv->storage_plugin);
3619 tp_clear_object (&priv->storage);
3620 tp_clear_object (&priv->dbus_daemon);
3621 tp_clear_object (&priv->self_contact);
3622 tp_clear_object (&priv->connectivity);
3623
3624 _mcd_account_set_connection_context (self, NULL);
3625 _mcd_account_set_connection (self, NULL);
3626
3627 G_OBJECT_CLASS (mcd_account_parent_class)->dispose (object);
3628 }
3629
3630 static GObject *
_mcd_account_constructor(GType type,guint n_params,GObjectConstructParam * params)3631 _mcd_account_constructor (GType type, guint n_params,
3632 GObjectConstructParam *params)
3633 {
3634 GObjectClass *object_class = (GObjectClass *)mcd_account_parent_class;
3635 McdAccount *account;
3636 McdAccountPrivate *priv;
3637
3638 account = MCD_ACCOUNT (object_class->constructor (type, n_params, params));
3639 priv = account->priv;
3640
3641 g_return_val_if_fail (account != NULL, NULL);
3642
3643 if (G_UNLIKELY (!priv->storage || !priv->unique_name))
3644 {
3645 g_object_unref (account);
3646 return NULL;
3647 }
3648
3649 return (GObject *) account;
3650 }
3651
3652 static void
monitor_state_changed_cb(McdConnectivityMonitor * monitor,gboolean connected,McdInhibit * inhibit,gpointer user_data)3653 monitor_state_changed_cb (
3654 McdConnectivityMonitor *monitor,
3655 gboolean connected,
3656 McdInhibit *inhibit,
3657 gpointer user_data)
3658 {
3659 McdAccount *self = MCD_ACCOUNT (user_data);
3660
3661 if (connected)
3662 {
3663 if (mcd_account_would_like_to_connect (self))
3664 {
3665 DEBUG ("account %s would like to connect",
3666 self->priv->unique_name);
3667 _mcd_account_connect_with_auto_presence (self, FALSE);
3668 }
3669 }
3670 else
3671 {
3672 if (_mcd_account_needs_dispatch (self))
3673 {
3674 /* special treatment for cellular accounts */
3675 DEBUG ("account %s is always dispatched and does not need a "
3676 "transport", self->priv->unique_name);
3677 }
3678 else
3679 {
3680 McdConnection *connection;
3681
3682 DEBUG ("account %s must disconnect", self->priv->unique_name);
3683 connection = mcd_account_get_connection (self);
3684
3685 if (connection != NULL)
3686 mcd_connection_close (connection, inhibit);
3687 }
3688 }
3689
3690 if (!self->priv->waiting_for_connectivity)
3691 return;
3692
3693 /* If we've gone online, allow the account to actually try to connect;
3694 * if we've fallen offline, say as much. (I don't actually think this
3695 * code will be reached if !connected, but.)
3696 */
3697 DEBUG ("telling %s to %s", self->priv->unique_name,
3698 connected ? "proceed" : "give up");
3699 mcd_account_connection_proceed_with_reason (self, connected,
3700 connected ? TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED
3701 : TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
3702 self->priv->waiting_for_connectivity = FALSE;
3703 }
3704
3705 static void
_mcd_account_constructed(GObject * object)3706 _mcd_account_constructed (GObject *object)
3707 {
3708 GObjectClass *object_class = (GObjectClass *)mcd_account_parent_class;
3709 McdAccount *account = MCD_ACCOUNT (object);
3710
3711 if (object_class->constructed)
3712 object_class->constructed (object);
3713
3714 DEBUG ("%p (%s)", object, account->priv->unique_name);
3715
3716 mcd_account_migrate_avatar (account);
3717 mcd_account_setup (account);
3718
3719 tp_g_signal_connect_object (account->priv->connectivity, "state-change",
3720 (GCallback) monitor_state_changed_cb, account, 0);
3721 }
3722
3723 static void
mcd_account_add_signals(TpProxy * self,guint quark,DBusGProxy * proxy,gpointer data)3724 mcd_account_add_signals (TpProxy *self,
3725 guint quark,
3726 DBusGProxy *proxy,
3727 gpointer data)
3728 {
3729 mc_cli_Connection_Manager_Interface_Account_Storage_add_signals (self,
3730 quark, proxy, data);
3731 }
3732
3733 static void
mcd_account_class_init(McdAccountClass * klass)3734 mcd_account_class_init (McdAccountClass * klass)
3735 {
3736 GObjectClass *object_class = G_OBJECT_CLASS (klass);
3737 g_type_class_add_private (object_class, sizeof (McdAccountPrivate));
3738
3739 object_class->constructor = _mcd_account_constructor;
3740 object_class->constructed = _mcd_account_constructed;
3741 object_class->dispose = _mcd_account_dispose;
3742 object_class->finalize = _mcd_account_finalize;
3743 object_class->set_property = set_property;
3744 object_class->get_property = get_property;
3745
3746 klass->check_request = _mcd_account_check_request_real;
3747
3748 g_object_class_install_property
3749 (object_class, PROP_DBUS_DAEMON,
3750 g_param_spec_object ("dbus-daemon", "DBus daemon", "DBus daemon",
3751 TP_TYPE_DBUS_DAEMON,
3752 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
3753
3754 g_object_class_install_property
3755 (object_class, PROP_CONNECTIVITY_MONITOR,
3756 g_param_spec_object ("connectivity-monitor",
3757 "Connectivity monitor",
3758 "Connectivity monitor",
3759 MCD_TYPE_CONNECTIVITY_MONITOR,
3760 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
3761
3762 g_object_class_install_property
3763 (object_class, PROP_STORAGE,
3764 g_param_spec_object ("storage", "storage",
3765 "storage", MCD_TYPE_STORAGE,
3766 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
3767
3768 g_object_class_install_property
3769 (object_class, PROP_NAME,
3770 g_param_spec_string ("name", "Unique name", "Unique name",
3771 NULL,
3772 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
3773
3774 g_object_class_install_property
3775 (object_class, PROP_ALWAYS_ON,
3776 g_param_spec_boolean ("always-on", "Always on?", "Always on?",
3777 FALSE,
3778 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
3779 G_PARAM_STATIC_STRINGS));
3780
3781 g_object_class_install_property
3782 (object_class, PROP_HIDDEN,
3783 g_param_spec_boolean ("hidden", "Hidden?", "Is this account hidden?",
3784 FALSE,
3785 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3786 /* Signals */
3787 _mcd_account_signals[VALIDITY_CHANGED] =
3788 g_signal_new ("validity-changed",
3789 G_OBJECT_CLASS_TYPE (klass),
3790 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
3791 0,
3792 NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
3793 G_TYPE_NONE, 1,
3794 G_TYPE_BOOLEAN);
3795 _mcd_account_signals[CONNECTION_PATH_CHANGED] =
3796 g_signal_new ("connection-path-changed",
3797 G_OBJECT_CLASS_TYPE (klass),
3798 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
3799 0,
3800 NULL, NULL, g_cclosure_marshal_VOID__STRING,
3801 G_TYPE_NONE, 1, G_TYPE_STRING);
3802
3803 account_ready_quark = g_quark_from_static_string ("mcd_account_load");
3804
3805 tp_proxy_or_subclass_hook_on_interface_add (TP_TYPE_CONNECTION_MANAGER,
3806 mcd_account_add_signals);
3807 }
3808
3809 static void
mcd_account_init(McdAccount * account)3810 mcd_account_init (McdAccount *account)
3811 {
3812 McdAccountPrivate *priv;
3813
3814 priv = G_TYPE_INSTANCE_GET_PRIVATE ((account),
3815 MCD_TYPE_ACCOUNT,
3816 McdAccountPrivate);
3817 account->priv = priv;
3818
3819 priv->req_presence_type = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
3820 priv->req_presence_status = g_strdup ("offline");
3821 priv->req_presence_message = g_strdup ("");
3822
3823 priv->curr_presence_type = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
3824 priv->curr_presence_status = g_strdup ("offline");
3825 priv->curr_presence_message = g_strdup ("");
3826
3827 priv->always_on = FALSE;
3828 priv->always_dispatch = FALSE;
3829 priv->enabled = FALSE;
3830 priv->connect_automatically = FALSE;
3831
3832 priv->changing_presence = FALSE;
3833
3834 priv->auto_presence_type = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
3835 priv->auto_presence_status = g_strdup ("available");
3836 priv->auto_presence_message = g_strdup ("");
3837
3838 /* initializes the interfaces */
3839 mcd_dbus_init_interfaces_instances (account);
3840
3841 priv->conn_status = TP_CONNECTION_STATUS_DISCONNECTED;
3842 priv->conn_reason = TP_CONNECTION_STATUS_REASON_REQUESTED;
3843 priv->conn_dbus_error = g_strdup ("");
3844 priv->conn_error_details = g_hash_table_new_full (g_str_hash, g_str_equal,
3845 g_free, (GDestroyNotify) tp_g_value_slice_free);
3846
3847 priv->changed_properties = g_hash_table_new_full (g_str_hash, g_str_equal,
3848 NULL, (GDestroyNotify) tp_g_value_slice_free);
3849
3850 g_set_error (&priv->invalid_reason, TP_ERROR, TP_ERROR_NOT_YET,
3851 "This account is not yet fully loaded");
3852 }
3853
3854 McdAccount *
mcd_account_new(McdAccountManager * account_manager,const gchar * name,McdConnectivityMonitor * connectivity)3855 mcd_account_new (McdAccountManager *account_manager,
3856 const gchar *name,
3857 McdConnectivityMonitor *connectivity)
3858 {
3859 gpointer *obj;
3860 McdStorage *storage = mcd_account_manager_get_storage (account_manager);
3861 TpDBusDaemon *dbus = mcd_account_manager_get_dbus_daemon (account_manager);
3862
3863 obj = g_object_new (MCD_TYPE_ACCOUNT,
3864 "storage", storage,
3865 "dbus-daemon", dbus,
3866 "connectivity-monitor", connectivity,
3867 "name", name,
3868 NULL);
3869 return MCD_ACCOUNT (obj);
3870 }
3871
3872 McdStorage *
_mcd_account_get_storage(McdAccount * account)3873 _mcd_account_get_storage (McdAccount *account)
3874 {
3875 return account->priv->storage;
3876 }
3877
3878 /*
3879 * mcd_account_is_valid:
3880 * @account: the #McdAccount.
3881 *
3882 * Checks that the account is usable:
3883 * - Manager, protocol and TODO presets (if specified) must exist
3884 * - All required parameters for the protocol must be set
3885 *
3886 * Returns: %TRUE if the account is valid, false otherwise.
3887 */
3888 gboolean
mcd_account_is_valid(McdAccount * account)3889 mcd_account_is_valid (McdAccount *account)
3890 {
3891 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
3892 return priv->invalid_reason == NULL;
3893 }
3894
3895 /**
3896 * mcd_account_is_enabled:
3897 * @account: the #McdAccount.
3898 *
3899 * Checks if the account is enabled:
3900 *
3901 * Returns: %TRUE if the account is enabled, false otherwise.
3902 */
3903 gboolean
mcd_account_is_enabled(McdAccount * account)3904 mcd_account_is_enabled (McdAccount *account)
3905 {
3906 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
3907 return priv->enabled;
3908 }
3909
3910 gboolean
_mcd_account_is_hidden(McdAccount * account)3911 _mcd_account_is_hidden (McdAccount *account)
3912 {
3913 g_return_val_if_fail (MCD_IS_ACCOUNT (account), FALSE);
3914
3915 return account->priv->hidden;
3916 }
3917
3918 const gchar *
mcd_account_get_unique_name(McdAccount * account)3919 mcd_account_get_unique_name (McdAccount *account)
3920 {
3921 return account->priv->unique_name;
3922 }
3923
3924 const gchar *
mcd_account_get_object_path(McdAccount * account)3925 mcd_account_get_object_path (McdAccount *account)
3926 {
3927 return account->priv->object_path;
3928 }
3929
3930 /**
3931 * _mcd_account_dup_parameters:
3932 * @account: the #McdAccount.
3933 *
3934 * Get the parameters set for this account. The resulting #GHashTable will be
3935 * newly allocated and must be g_hash_table_unref()'d after use.
3936 *
3937 * Returns: @account's current parameters, or %NULL if they could not be
3938 * retrieved.
3939 */
3940 GHashTable *
_mcd_account_dup_parameters(McdAccount * account)3941 _mcd_account_dup_parameters (McdAccount *account)
3942 {
3943 McdAccountPrivate *priv;
3944 TpProtocol *protocol;
3945 GList *protocol_params;
3946 GList *iter;
3947 GHashTable *params;
3948
3949 g_return_val_if_fail (MCD_IS_ACCOUNT (account), NULL);
3950
3951 priv = account->priv;
3952
3953 DEBUG ("called");
3954
3955 /* FIXME: this is ridiculous. MC stores the parameters for the account, so
3956 * it should be able to expose them on D-Bus even if the CM is uninstalled.
3957 * It shouldn't need to iterate across the parameters supported by the CM.
3958 * But it does, because MC doesn't store the types of parameters. So it
3959 * needs the CM (or .manager file) to be around to tell it whether "true"
3960 * is a string or a boolean…
3961 */
3962 if (!priv->manager && !load_manager (account))
3963 {
3964 DEBUG ("unable to load manager for account %s", priv->unique_name);
3965 return NULL;
3966 }
3967
3968 protocol = _mcd_manager_dup_protocol (priv->manager,
3969 priv->protocol_name);
3970
3971 if (G_UNLIKELY (protocol == NULL))
3972 {
3973 DEBUG ("unable to get protocol for %s account %s", priv->protocol_name,
3974 priv->unique_name);
3975 return NULL;
3976 }
3977
3978 params = g_hash_table_new_full (g_str_hash, g_str_equal,
3979 g_free,
3980 (GDestroyNotify) tp_g_value_slice_free);
3981
3982 protocol_params = tp_protocol_dup_params (protocol);
3983
3984 for (iter = protocol_params; iter != NULL; iter = iter->next)
3985 {
3986 TpConnectionManagerParam *param = iter->data;
3987 const gchar *name = tp_connection_manager_param_get_name (param);
3988 GValue v = G_VALUE_INIT;
3989
3990 if (mcd_account_get_parameter (account, name, &v, NULL))
3991 {
3992 g_hash_table_insert (params, g_strdup (name),
3993 tp_g_value_slice_dup (&v));
3994 g_value_unset (&v);
3995 }
3996 }
3997
3998 g_list_free_full (protocol_params,
3999 (GDestroyNotify) tp_connection_manager_param_free);
4000 g_object_unref (protocol);
4001 return params;
4002 }
4003
4004 /**
4005 * mcd_account_request_presence:
4006 * @account: the #McdAccount.
4007 * @presence: a #TpConnectionPresenceType.
4008 * @status: presence status.
4009 * @message: presence status message.
4010 *
4011 * Request a presence status on the account, initiated by some other part of
4012 * MC (i.e. not by user request).
4013 */
4014 void
mcd_account_request_presence(McdAccount * account,TpConnectionPresenceType presence,const gchar * status,const gchar * message)4015 mcd_account_request_presence (McdAccount *account,
4016 TpConnectionPresenceType presence,
4017 const gchar *status, const gchar *message)
4018 {
4019 mcd_account_request_presence_int (account, presence, status, message,
4020 FALSE);
4021 }
4022
4023 static void
mcd_account_update_self_presence(McdAccount * account,guint presence,const gchar * status,const gchar * message,TpContact * self_contact)4024 mcd_account_update_self_presence (McdAccount *account,
4025 guint presence,
4026 const gchar *status,
4027 const gchar *message,
4028 TpContact *self_contact)
4029 {
4030 McdAccountPrivate *priv = account->priv;
4031 gboolean changed = FALSE;
4032 GValue value = G_VALUE_INIT;
4033
4034 if (self_contact != account->priv->self_contact)
4035 return;
4036
4037 if (priv->curr_presence_type != presence)
4038 {
4039 priv->curr_presence_type = presence;
4040 changed = TRUE;
4041 }
4042 if (tp_strdiff (priv->curr_presence_status, status))
4043 {
4044 g_free (priv->curr_presence_status);
4045 priv->curr_presence_status = g_strdup (status);
4046 changed = TRUE;
4047 }
4048 if (tp_strdiff (priv->curr_presence_message, message))
4049 {
4050 g_free (priv->curr_presence_message);
4051 priv->curr_presence_message = g_strdup (message);
4052 changed = TRUE;
4053 }
4054
4055 if (_mcd_connection_presence_info_is_ready (priv->connection))
4056 {
4057 _mcd_account_set_changing_presence (account, FALSE);
4058 }
4059
4060 if (!changed) return;
4061
4062 g_value_init (&value, TP_STRUCT_TYPE_SIMPLE_PRESENCE);
4063 g_value_take_boxed (&value,
4064 tp_value_array_build (3,
4065 G_TYPE_UINT, presence,
4066 G_TYPE_STRING, status,
4067 G_TYPE_STRING, message,
4068 G_TYPE_INVALID));
4069 mcd_account_changed_property (account, "CurrentPresence", &value);
4070 g_value_unset (&value);
4071 }
4072
4073 /* TODO: remove when the relative members will become public */
4074 void
mcd_account_get_requested_presence(McdAccount * account,TpConnectionPresenceType * presence,const gchar ** status,const gchar ** message)4075 mcd_account_get_requested_presence (McdAccount *account,
4076 TpConnectionPresenceType *presence,
4077 const gchar **status,
4078 const gchar **message)
4079 {
4080 McdAccountPrivate *priv = account->priv;
4081
4082 if (presence != NULL)
4083 *presence = priv->req_presence_type;
4084
4085 if (status != NULL)
4086 *status = priv->req_presence_status;
4087
4088 if (message != NULL)
4089 *message = priv->req_presence_message;
4090 }
4091
4092 /* TODO: remove when the relative members will become public */
4093 void
mcd_account_get_current_presence(McdAccount * account,TpConnectionPresenceType * presence,const gchar ** status,const gchar ** message)4094 mcd_account_get_current_presence (McdAccount *account,
4095 TpConnectionPresenceType *presence,
4096 const gchar **status,
4097 const gchar **message)
4098 {
4099 McdAccountPrivate *priv = account->priv;
4100
4101 if (presence != NULL)
4102 *presence = priv->curr_presence_type;
4103
4104 if (status != NULL)
4105 *status = priv->curr_presence_status;
4106
4107 if (message != NULL)
4108 *message = priv->curr_presence_message;
4109 }
4110
4111 /*
4112 * mcd_account_would_like_to_connect:
4113 * @account: an account
4114 *
4115 * Returns: %TRUE if @account is not currently in the process of trying to
4116 * connect, but would like to be, in a perfect world.
4117 */
4118 gboolean
mcd_account_would_like_to_connect(McdAccount * account)4119 mcd_account_would_like_to_connect (McdAccount *account)
4120 {
4121 McdAccountPrivate *priv;
4122
4123 g_return_val_if_fail (MCD_IS_ACCOUNT (account), FALSE);
4124 priv = account->priv;
4125
4126 if (!priv->enabled)
4127 {
4128 DEBUG ("%s not Enabled", priv->unique_name);
4129 return FALSE;
4130 }
4131
4132 if (!mcd_account_is_valid (account))
4133 {
4134 DEBUG ("%s not Valid", priv->unique_name);
4135 return FALSE;
4136 }
4137
4138 if (priv->conn_status != TP_CONNECTION_STATUS_DISCONNECTED)
4139 {
4140 DEBUG ("%s already connecting/connected", priv->unique_name);
4141 return FALSE;
4142 }
4143
4144 if (!priv->connect_automatically &&
4145 !_presence_type_is_online (priv->req_presence_type))
4146 {
4147 DEBUG ("%s does not ConnectAutomatically, and its RequestedPresence "
4148 "(%u, '%s', '%s') doesn't indicate the user wants to be online",
4149 priv->unique_name, priv->req_presence_type,
4150 priv->req_presence_status, priv->req_presence_message);
4151 return FALSE;
4152 }
4153
4154 return TRUE;
4155 }
4156
4157 /* TODO: remove when the relative members will become public */
4158 const gchar *
mcd_account_get_manager_name(McdAccount * account)4159 mcd_account_get_manager_name (McdAccount *account)
4160 {
4161 McdAccountPrivate *priv = account->priv;
4162
4163 return priv->manager_name;
4164 }
4165
4166 /* TODO: remove when the relative members will become public */
4167 const gchar *
mcd_account_get_protocol_name(McdAccount * account)4168 mcd_account_get_protocol_name (McdAccount *account)
4169 {
4170 McdAccountPrivate *priv = account->priv;
4171
4172 return priv->protocol_name;
4173 }
4174
4175 /**
4176 * mcd_account_get_cm:
4177 * @account: an account
4178 *
4179 * Fetches the connection manager through which @account connects. If @account
4180 * is not ready, or is invalid (perhaps because the connection manager is
4181 * missing), this may be %NULL.
4182 *
4183 * Returns: the connection manager through which @account connects, or %NULL.
4184 */
4185 TpConnectionManager *
mcd_account_get_cm(McdAccount * account)4186 mcd_account_get_cm (McdAccount *account)
4187 {
4188 g_return_val_if_fail (account != NULL, NULL);
4189 g_return_val_if_fail (MCD_IS_ACCOUNT (account), NULL);
4190
4191 return mcd_manager_get_tp_proxy (account->priv->manager);
4192 }
4193
4194 void
_mcd_account_set_normalized_name(McdAccount * account,const gchar * name)4195 _mcd_account_set_normalized_name (McdAccount *account, const gchar *name)
4196 {
4197 McdAccountPrivate *priv = account->priv;
4198 GValue value = G_VALUE_INIT;
4199 const gchar *account_name = mcd_account_get_unique_name (account);
4200
4201 DEBUG ("called (%s)", name);
4202
4203 g_value_init (&value, G_TYPE_STRING);
4204 g_value_set_static_string (&value, name);
4205
4206 mcd_storage_set_attribute (priv->storage, account_name,
4207 MC_ACCOUNTS_KEY_NORMALIZED_NAME, &value);
4208 mcd_storage_commit (priv->storage, account_name);
4209 mcd_account_changed_property (account, MC_ACCOUNTS_KEY_NORMALIZED_NAME,
4210 &value);
4211
4212 g_value_unset (&value);
4213 }
4214
4215 void
_mcd_account_set_avatar_token(McdAccount * account,const gchar * token)4216 _mcd_account_set_avatar_token (McdAccount *account, const gchar *token)
4217 {
4218 McdAccountPrivate *priv = account->priv;
4219 const gchar *account_name = mcd_account_get_unique_name (account);
4220
4221 DEBUG ("called (%s)", token);
4222 mcd_storage_set_string (priv->storage,
4223 account_name,
4224 MC_ACCOUNTS_KEY_AVATAR_TOKEN,
4225 token);
4226
4227 mcd_storage_commit (priv->storage, account_name);
4228 }
4229
4230 gchar *
_mcd_account_get_avatar_token(McdAccount * account)4231 _mcd_account_get_avatar_token (McdAccount *account)
4232 {
4233 McdAccountPrivate *priv = account->priv;
4234 const gchar *account_name = mcd_account_get_unique_name (account);
4235
4236 return mcd_storage_dup_string (priv->storage,
4237 account_name,
4238 MC_ACCOUNTS_KEY_AVATAR_TOKEN);
4239 }
4240
4241 gboolean
_mcd_account_set_avatar(McdAccount * account,const GArray * avatar,const gchar * mime_type,const gchar * token,GError ** error)4242 _mcd_account_set_avatar (McdAccount *account, const GArray *avatar,
4243 const gchar *mime_type, const gchar *token,
4244 GError **error)
4245 {
4246 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
4247 const gchar *account_name = mcd_account_get_unique_name (account);
4248
4249 DEBUG ("called");
4250
4251 if (G_LIKELY(avatar) && avatar->len > 0)
4252 {
4253 if (!save_avatar (account, avatar->data, avatar->len, error))
4254 {
4255 g_warning ("%s: writing avatar failed", G_STRLOC);
4256 return FALSE;
4257 }
4258 }
4259 else
4260 {
4261 /* We implement "deleting" an avatar by writing out a zero-length
4262 * file, so that it will override lower-priority directories. */
4263 if (!save_avatar (account, "", 0, error))
4264 {
4265 g_warning ("%s: writing empty avatar failed", G_STRLOC);
4266 return FALSE;
4267 }
4268 }
4269
4270 if (mime_type != NULL)
4271 mcd_storage_set_string (priv->storage,
4272 account_name,
4273 MC_ACCOUNTS_KEY_AVATAR_MIME,
4274 mime_type);
4275
4276 if (token)
4277 {
4278 gchar *prev_token;
4279
4280 prev_token = _mcd_account_get_avatar_token (account);
4281
4282 mcd_storage_set_string (priv->storage,
4283 account_name,
4284 MC_ACCOUNTS_KEY_AVATAR_TOKEN,
4285 token);
4286
4287 if (!prev_token || strcmp (prev_token, token) != 0)
4288 tp_svc_account_interface_avatar_emit_avatar_changed (account);
4289
4290 g_free (prev_token);
4291 }
4292 else
4293 {
4294 mcd_storage_set_attribute (priv->storage, account_name,
4295 MC_ACCOUNTS_KEY_AVATAR_TOKEN, NULL);
4296
4297 mcd_account_send_avatar_to_connection (account, avatar, mime_type);
4298 }
4299
4300 mcd_storage_commit (priv->storage, account_name);
4301
4302 return TRUE;
4303 }
4304
4305 static GArray *
load_avatar_or_warn(const gchar * filename)4306 load_avatar_or_warn (const gchar *filename)
4307 {
4308 GError *error = NULL;
4309 gchar *data = NULL;
4310 gsize length;
4311
4312 if (g_file_get_contents (filename, &data, &length, &error))
4313 {
4314 if (length > 0 && length < G_MAXUINT)
4315 {
4316 GArray *ret;
4317
4318 ret = g_array_new (FALSE, FALSE, 1);
4319 g_array_append_vals (ret, data, (guint) length);
4320 return ret;
4321 }
4322 else
4323 {
4324 DEBUG ("avatar %s was empty or ridiculously large (%"
4325 G_GSIZE_FORMAT " bytes)", filename, length);
4326 return NULL;
4327 }
4328 }
4329 else
4330 {
4331 DEBUG ("error reading %s: %s", filename, error->message);
4332 g_error_free (error);
4333 return NULL;
4334 }
4335 }
4336
4337 void
_mcd_account_get_avatar(McdAccount * account,GArray ** avatar,gchar ** mime_type)4338 _mcd_account_get_avatar (McdAccount *account, GArray **avatar,
4339 gchar **mime_type)
4340 {
4341 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
4342 const gchar *account_name = mcd_account_get_unique_name (account);
4343 gchar *basename;
4344 gchar *filename;
4345
4346 if (mime_type != NULL)
4347 *mime_type = mcd_storage_dup_string (priv->storage, account_name,
4348 MC_ACCOUNTS_KEY_AVATAR_MIME);
4349
4350 if (avatar == NULL)
4351 return;
4352
4353 *avatar = NULL;
4354
4355 get_avatar_paths (account, NULL, &basename, &filename);
4356
4357 if (g_file_test (filename, G_FILE_TEST_EXISTS))
4358 {
4359 *avatar = load_avatar_or_warn (filename);
4360 }
4361 else
4362 {
4363 const gchar * const *iter;
4364
4365 for (iter = g_get_system_data_dirs ();
4366 iter != NULL && *iter != NULL;
4367 iter++)
4368 {
4369 gchar *candidate = g_build_filename (*iter, "telepathy",
4370 "mission-control",
4371 basename, NULL);
4372
4373 if (g_file_test (candidate, G_FILE_TEST_EXISTS))
4374 {
4375 *avatar = load_avatar_or_warn (candidate);
4376 g_free (candidate);
4377 break;
4378 }
4379
4380 g_free (candidate);
4381 }
4382 }
4383
4384 g_free (filename);
4385 g_free (basename);
4386 }
4387
4388 GPtrArray *
_mcd_account_get_supersedes(McdAccount * self)4389 _mcd_account_get_supersedes (McdAccount *self)
4390 {
4391 return self->priv->supersedes;
4392 }
4393
4394 static void
mcd_account_self_contact_notify_alias_cb(McdAccount * self,GParamSpec * unused_param_spec G_GNUC_UNUSED,TpContact * self_contact)4395 mcd_account_self_contact_notify_alias_cb (McdAccount *self,
4396 GParamSpec *unused_param_spec G_GNUC_UNUSED,
4397 TpContact *self_contact)
4398 {
4399 GValue value = G_VALUE_INIT;
4400
4401 if (self_contact != self->priv->self_contact)
4402 return;
4403
4404 g_value_init (&value, G_TYPE_STRING);
4405 g_object_get_property (G_OBJECT (self_contact), "alias", &value);
4406 mcd_account_set_string_val (self, MC_ACCOUNTS_KEY_NICKNAME, &value,
4407 MCD_DBUS_PROP_SET_FLAG_NONE, NULL);
4408 g_value_unset (&value);
4409 }
4410
4411 static gchar *
mcd_account_get_alias(McdAccount * account)4412 mcd_account_get_alias (McdAccount *account)
4413 {
4414 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
4415 const gchar *account_name = mcd_account_get_unique_name (account);
4416
4417 return mcd_storage_dup_string (priv->storage, account_name,
4418 MC_ACCOUNTS_KEY_NICKNAME);
4419 }
4420
4421 static void
_mcd_account_online_request_completed(McdAccount * account,GError * error)4422 _mcd_account_online_request_completed (McdAccount *account, GError *error)
4423 {
4424 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
4425 GList *list;
4426
4427 list = priv->online_requests;
4428
4429 while (list)
4430 {
4431 McdOnlineRequestData *data = list->data;
4432
4433 data->callback (account, data->user_data, error);
4434 g_slice_free (McdOnlineRequestData, data);
4435 list = g_list_delete_link (list, list);
4436 }
4437 if (error)
4438 g_error_free (error);
4439 priv->online_requests = NULL;
4440 }
4441
4442 static inline void
process_online_requests(McdAccount * account,TpConnectionStatus status,TpConnectionStatusReason reason)4443 process_online_requests (McdAccount *account,
4444 TpConnectionStatus status,
4445 TpConnectionStatusReason reason)
4446 {
4447 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
4448 GError *error;
4449
4450 switch (status)
4451 {
4452 case TP_CONNECTION_STATUS_CONNECTED:
4453 error = NULL;
4454 break;
4455 case TP_CONNECTION_STATUS_DISCONNECTED:
4456 error = g_error_new (TP_ERROR, TP_ERROR_DISCONNECTED,
4457 "Account %s disconnected with reason %d",
4458 priv->unique_name, reason);
4459 break;
4460 default:
4461 return;
4462 }
4463 _mcd_account_online_request_completed (account, error);
4464 }
4465
4466 static void
on_conn_status_changed(McdConnection * connection,TpConnectionStatus status,TpConnectionStatusReason reason,TpConnection * tp_conn,const gchar * dbus_error,GHashTable * details,McdAccount * account)4467 on_conn_status_changed (McdConnection *connection,
4468 TpConnectionStatus status,
4469 TpConnectionStatusReason reason,
4470 TpConnection *tp_conn,
4471 const gchar *dbus_error,
4472 GHashTable *details,
4473 McdAccount *account)
4474 {
4475 _mcd_account_set_connection_status (account, status, reason, tp_conn,
4476 dbus_error, details);
4477 }
4478
4479 /* clear the "register" flag, if necessary */
4480 static void
clear_register(McdAccount * self)4481 clear_register (McdAccount *self)
4482 {
4483 GHashTable *params = _mcd_account_dup_parameters (self);
4484
4485 if (params == NULL)
4486 {
4487 DEBUG ("no params returned");
4488 return;
4489 }
4490
4491 if (tp_asv_get_boolean (params, "register", NULL))
4492 {
4493 GValue value = G_VALUE_INIT;
4494 const gchar *account_name = mcd_account_get_unique_name (self);
4495
4496 _mcd_account_set_parameter (self, "register", NULL);
4497
4498 g_hash_table_remove (params, "register");
4499
4500 g_value_init (&value, TP_HASH_TYPE_STRING_VARIANT_MAP);
4501 g_value_take_boxed (&value, params);
4502 mcd_account_changed_property (self, "Parameters", &value);
4503 g_value_unset (&value);
4504
4505 mcd_storage_commit (self->priv->storage, account_name);
4506 }
4507 else
4508 {
4509 g_hash_table_unref (params);
4510 }
4511 }
4512
4513 void
_mcd_account_set_connection_status(McdAccount * account,TpConnectionStatus status,TpConnectionStatusReason reason,TpConnection * tp_conn,const gchar * dbus_error,const GHashTable * details)4514 _mcd_account_set_connection_status (McdAccount *account,
4515 TpConnectionStatus status,
4516 TpConnectionStatusReason reason,
4517 TpConnection *tp_conn,
4518 const gchar *dbus_error,
4519 const GHashTable *details)
4520 {
4521 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
4522 gboolean changed = FALSE;
4523
4524 DEBUG ("%s: %u because %u", priv->unique_name, status, reason);
4525
4526 mcd_account_freeze_properties (account);
4527
4528 if (status == TP_CONNECTION_STATUS_CONNECTED)
4529 {
4530 _mcd_account_set_has_been_online (account);
4531 clear_register (account);
4532
4533 DEBUG ("clearing connection error details");
4534 g_free (priv->conn_dbus_error);
4535 priv->conn_dbus_error = g_strdup ("");
4536 g_hash_table_remove_all (priv->conn_error_details);
4537
4538 }
4539 else if (status == TP_CONNECTION_STATUS_DISCONNECTED)
4540 {
4541 /* we'll get this from the TpContact soon, but it makes sense
4542 * to bundle everything together into one signal */
4543 mcd_account_update_self_presence (account,
4544 TP_CONNECTION_PRESENCE_TYPE_OFFLINE,
4545 "offline",
4546 "",
4547 priv->self_contact);
4548
4549 if (dbus_error == NULL)
4550 dbus_error = "";
4551
4552 if (tp_strdiff (dbus_error, priv->conn_dbus_error))
4553 {
4554 DEBUG ("changing detailed D-Bus error from '%s' to '%s'",
4555 priv->conn_dbus_error, dbus_error);
4556 g_free (priv->conn_dbus_error);
4557 priv->conn_dbus_error = g_strdup (dbus_error);
4558 changed = TRUE;
4559 }
4560
4561 /* to avoid having to do deep comparisons, we assume that any change to
4562 * or from a non-empty hash table is interesting. */
4563 if ((details != NULL && tp_asv_size (details) > 0) ||
4564 tp_asv_size (priv->conn_error_details) > 0)
4565 {
4566 DEBUG ("changing error details");
4567 g_hash_table_remove_all (priv->conn_error_details);
4568
4569 if (details != NULL)
4570 tp_g_hash_table_update (priv->conn_error_details,
4571 (GHashTable *) details,
4572 (GBoxedCopyFunc) g_strdup,
4573 (GBoxedCopyFunc) tp_g_value_slice_dup);
4574
4575 changed = TRUE;
4576 }
4577 }
4578
4579 if (priv->tp_connection != tp_conn
4580 || (tp_conn != NULL && status == TP_CONNECTION_STATUS_DISCONNECTED))
4581 {
4582 tp_clear_object (&priv->tp_connection);
4583 tp_clear_object (&priv->self_contact);
4584
4585 if (tp_conn != NULL && status != TP_CONNECTION_STATUS_DISCONNECTED)
4586 priv->tp_connection = g_object_ref (tp_conn);
4587 else
4588 priv->tp_connection = NULL;
4589
4590 changed = TRUE;
4591 }
4592
4593 if (status != priv->conn_status)
4594 {
4595 DEBUG ("changing connection status from %u to %u", priv->conn_status,
4596 status);
4597 priv->conn_status = status;
4598 changed = TRUE;
4599 }
4600
4601 if (reason != priv->conn_reason)
4602 {
4603 DEBUG ("changing connection status reason from %u to %u",
4604 priv->conn_reason, reason);
4605 priv->conn_reason = reason;
4606 changed = TRUE;
4607 }
4608
4609 if (changed)
4610 {
4611 GValue value = G_VALUE_INIT;
4612
4613 _mcd_account_tp_connection_changed (account, priv->tp_connection);
4614
4615 g_value_init (&value, G_TYPE_UINT);
4616 g_value_set_uint (&value, priv->conn_status);
4617 mcd_account_changed_property (account, "ConnectionStatus", &value);
4618 g_value_set_uint (&value, priv->conn_reason);
4619 mcd_account_changed_property (account, "ConnectionStatusReason",
4620 &value);
4621 g_value_unset (&value);
4622
4623 g_value_init (&value, G_TYPE_STRING);
4624 g_value_set_string (&value, priv->conn_dbus_error);
4625 mcd_account_changed_property (account, "ConnectionError", &value);
4626 g_value_unset (&value);
4627
4628 g_value_init (&value, TP_HASH_TYPE_STRING_VARIANT_MAP);
4629 g_value_set_boxed (&value, priv->conn_error_details);
4630 mcd_account_changed_property (account, "ConnectionErrorDetails",
4631 &value);
4632 g_value_unset (&value);
4633 }
4634
4635 mcd_account_thaw_properties (account);
4636
4637 process_online_requests (account, status, reason);
4638 }
4639
4640 TpConnectionStatus
mcd_account_get_connection_status(McdAccount * account)4641 mcd_account_get_connection_status (McdAccount *account)
4642 {
4643 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
4644 return priv->conn_status;
4645 }
4646
4647 void
_mcd_account_tp_connection_changed(McdAccount * account,TpConnection * tp_conn)4648 _mcd_account_tp_connection_changed (McdAccount *account,
4649 TpConnection *tp_conn)
4650 {
4651 GValue value = G_VALUE_INIT;
4652
4653 g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
4654
4655 if (tp_conn == NULL)
4656 {
4657 g_value_set_static_boxed (&value, "/");
4658 }
4659 else
4660 {
4661 g_value_set_boxed (&value, tp_proxy_get_object_path (tp_conn));
4662 }
4663
4664 mcd_account_changed_property (account, "Connection", &value);
4665
4666 g_signal_emit (account, _mcd_account_signals[CONNECTION_PATH_CHANGED], 0,
4667 g_value_get_boxed (&value));
4668 g_value_unset (&value);
4669 }
4670
4671 McdConnection *
mcd_account_get_connection(McdAccount * account)4672 mcd_account_get_connection (McdAccount *account)
4673 {
4674 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
4675 return priv->connection;
4676 }
4677
4678 typedef struct
4679 {
4680 McdAccountCheckValidityCb callback;
4681 gpointer user_data;
4682 } CheckValidityData;
4683
4684 static void
check_validity_check_parameters_cb(McdAccount * account,const GError * invalid_reason,gpointer user_data)4685 check_validity_check_parameters_cb (McdAccount *account,
4686 const GError *invalid_reason,
4687 gpointer user_data)
4688 {
4689 CheckValidityData *data = (CheckValidityData *) user_data;
4690 McdAccountPrivate *priv = account->priv;
4691 gboolean now_valid = (invalid_reason == NULL);
4692 gboolean was_valid = (priv->invalid_reason == NULL);
4693
4694 g_clear_error (&priv->invalid_reason);
4695 if (invalid_reason != NULL)
4696 {
4697 priv->invalid_reason = g_error_copy (invalid_reason);
4698 }
4699
4700 if (was_valid != now_valid)
4701 {
4702 GValue value = G_VALUE_INIT;
4703 DEBUG ("Account validity changed (old: %d, new: %d)",
4704 was_valid, now_valid);
4705 g_signal_emit (account, _mcd_account_signals[VALIDITY_CHANGED], 0,
4706 now_valid);
4707 g_value_init (&value, G_TYPE_BOOLEAN);
4708 g_value_set_boolean (&value, now_valid);
4709 mcd_account_changed_property (account, "Valid", &value);
4710
4711 if (now_valid)
4712 {
4713 /* Newly valid - try setting requested presence again.
4714 * This counts as user-initiated, because the user caused the
4715 * account to become valid somehow. */
4716 mcd_account_rerequest_presence (account, TRUE);
4717 }
4718 }
4719
4720 if (data->callback != NULL)
4721 data->callback (account, invalid_reason, data->user_data);
4722
4723 g_slice_free (CheckValidityData, data);
4724 }
4725
4726 void
mcd_account_check_validity(McdAccount * account,McdAccountCheckValidityCb callback,gpointer user_data)4727 mcd_account_check_validity (McdAccount *account,
4728 McdAccountCheckValidityCb callback,
4729 gpointer user_data)
4730 {
4731 CheckValidityData *data;
4732
4733 g_return_if_fail (MCD_IS_ACCOUNT (account));
4734
4735 data = g_slice_new0 (CheckValidityData);
4736 data->callback = callback;
4737 data->user_data = user_data;
4738
4739 mcd_account_check_parameters (account, check_validity_check_parameters_cb,
4740 data);
4741 }
4742
4743 /*
4744 * _mcd_account_connect_with_auto_presence:
4745 * @account: the #McdAccount.
4746 * @user_initiated: %TRUE if the connection attempt is in response to a user
4747 * request (like a request for a channel)
4748 *
4749 * Request the account to go back online with the current RequestedPresence, if
4750 * it is not Offline, or with the configured AutomaticPresence otherwise.
4751 *
4752 * This is appropriate in these situations:
4753 * - going online automatically because we've gained connectivity
4754 * - going online automatically in order to request a channel
4755 */
4756 void
_mcd_account_connect_with_auto_presence(McdAccount * account,gboolean user_initiated)4757 _mcd_account_connect_with_auto_presence (McdAccount *account,
4758 gboolean user_initiated)
4759 {
4760 McdAccountPrivate *priv = account->priv;
4761
4762 if (_presence_type_is_online (priv->req_presence_type))
4763 mcd_account_rerequest_presence (account, user_initiated);
4764 else
4765 mcd_account_request_presence_int (account,
4766 priv->auto_presence_type,
4767 priv->auto_presence_status,
4768 priv->auto_presence_message,
4769 user_initiated);
4770 }
4771
4772 /*
4773 * _mcd_account_online_request:
4774 * @account: the #McdAccount.
4775 * @callback: a #McdOnlineRequestCb.
4776 * @userdata: user data to be passed to @callback.
4777 *
4778 * If the account is online, call @callback immediately; else, try to put the
4779 * account online (set its presence to the automatic presence) and eventually
4780 * invoke @callback.
4781 *
4782 * @callback is always invoked exactly once.
4783 */
4784 void
_mcd_account_online_request(McdAccount * account,McdOnlineRequestCb callback,gpointer userdata)4785 _mcd_account_online_request (McdAccount *account,
4786 McdOnlineRequestCb callback,
4787 gpointer userdata)
4788 {
4789 McdAccountPrivate *priv = account->priv;
4790 McdOnlineRequestData *data;
4791
4792 DEBUG ("connection status for %s is %d",
4793 priv->unique_name, priv->conn_status);
4794 if (priv->conn_status == TP_CONNECTION_STATUS_CONNECTED)
4795 {
4796 /* invoke the callback now */
4797 DEBUG ("%s is already connected", priv->unique_name);
4798 callback (account, userdata, NULL);
4799 return;
4800 }
4801
4802 if (priv->loaded && !mcd_account_is_valid (account))
4803 {
4804 /* FIXME: pick a better error and put it in telepathy-spec? */
4805 GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE,
4806 "account isn't Valid (not enough information to put it online)" };
4807
4808 DEBUG ("%s: %s", priv->unique_name, e.message);
4809 callback (account, userdata, &e);
4810 return;
4811 }
4812
4813 if (priv->loaded && !priv->enabled)
4814 {
4815 /* FIXME: pick a better error and put it in telepathy-spec? */
4816 GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE,
4817 "account isn't Enabled" };
4818
4819 DEBUG ("%s: %s", priv->unique_name, e.message);
4820 callback (account, userdata, &e);
4821 return;
4822 }
4823
4824 /* listen to the StatusChanged signal */
4825 if (priv->loaded && priv->conn_status == TP_CONNECTION_STATUS_DISCONNECTED)
4826 _mcd_account_connect_with_auto_presence (account, TRUE);
4827
4828 /* now the connection should be in connecting state; insert the
4829 * callback in the online_requests hash table, which will be processed
4830 * in the connection-status-changed callback */
4831 data = g_slice_new (McdOnlineRequestData);
4832 data->callback = callback;
4833 data->user_data = userdata;
4834 priv->online_requests = g_list_append (priv->online_requests, data);
4835 }
4836
4837 GKeyFile *
_mcd_account_get_keyfile(McdAccount * account)4838 _mcd_account_get_keyfile (McdAccount *account)
4839 {
4840 McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
4841 return priv->keyfile;
4842 }
4843
4844 static gchar *
_mcd_account_get_old_avatar_filename(McdAccount * account,gchar ** old_dir)4845 _mcd_account_get_old_avatar_filename (McdAccount *account,
4846 gchar **old_dir)
4847 {
4848 McdAccountPrivate *priv = account->priv;
4849 gchar *filename;
4850
4851 *old_dir = get_old_account_data_path (priv);
4852 filename = g_build_filename (*old_dir, MC_OLD_AVATAR_FILENAME, NULL);
4853 return filename;
4854 }
4855
4856 static void
mcd_account_process_initial_avatar_token(McdAccount * self,const gchar * token)4857 mcd_account_process_initial_avatar_token (McdAccount *self,
4858 const gchar *token)
4859 {
4860 GArray *avatar = NULL;
4861 gchar *mime_type = NULL;
4862 gchar *prev_token;
4863
4864 g_assert (self->priv->self_contact != NULL);
4865
4866 prev_token = _mcd_account_get_avatar_token (self);
4867
4868 DEBUG ("%s", self->priv->unique_name);
4869
4870 if (prev_token == NULL)
4871 DEBUG ("no previous local avatar token");
4872 else
4873 DEBUG ("previous local avatar token: '%s'", prev_token);
4874
4875 if (avatar == NULL)
4876 DEBUG ("no previous local avatar");
4877 else
4878 DEBUG ("previous local avatar: %u bytes, MIME type '%s'", avatar->len,
4879 (mime_type != NULL ? mime_type : "(null)"));
4880
4881 if (token == NULL)
4882 DEBUG ("no remote avatar token");
4883 else
4884 DEBUG ("remote avatar token: '%s'", token);
4885
4886 _mcd_account_get_avatar (self, &avatar, &mime_type);
4887
4888 /* If we have a stored avatar but no avatar token, we must have
4889 * changed it locally; set it.
4890 *
4891 * Meanwhile, if the self-contact's avatar token is missing, this is
4892 * a protocol like link-local XMPP where avatars don't persist.
4893 * We can distinguish between this case (token is missing, so token = NULL)
4894 * and the case where there is no avatar on an XMPP server (token is
4895 * present and empty), although it's ridiculously subtle.
4896 *
4897 * Either way, upload our avatar, if any. */
4898 if (tp_str_empty (prev_token) || token == NULL)
4899 {
4900
4901 if (avatar != NULL)
4902 {
4903 if (tp_str_empty (prev_token))
4904 DEBUG ("We have an avatar that has never been uploaded");
4905 if (tp_str_empty (token))
4906 DEBUG ("We have an avatar and the server doesn't");
4907
4908 mcd_account_send_avatar_to_connection (self, avatar, mime_type);
4909 goto out;
4910 }
4911 }
4912
4913 /* Otherwise, if the self-contact's avatar token
4914 * differs from ours, one of our "other selves" must have changed
4915 * it remotely. Behave the same as if it changes remotely
4916 * mid-session - i.e. download it and use it as our new avatar.
4917 *
4918 * In particular, this includes the case where we had
4919 * a non-empty avatar last time we signed in, but another client
4920 * has deleted it from the server since then (prev_token nonempty,
4921 * token = ""). */
4922 if (tp_strdiff (token, prev_token))
4923 {
4924 GFile *file = tp_contact_get_avatar_file (self->priv->self_contact);
4925
4926 DEBUG ("The server's avatar does not match ours");
4927
4928 if (file != NULL)
4929 {
4930 /* We have already downloaded it: copy it. */
4931 mcd_account_self_contact_notify_avatar_file_cb (self, NULL,
4932 self->priv->self_contact);
4933 }
4934 /* ... else we haven't downloaded it yet, but when we do,
4935 * notify::avatar-file will go off. */
4936 }
4937
4938 out:
4939 g_free (prev_token);
4940 tp_clear_pointer (&avatar, g_array_unref);
4941 g_free (mime_type);
4942 }
4943
4944 static void
account_conn_get_known_avatar_tokens_cb(TpConnection * conn,GHashTable * tokens,const GError * error,gpointer user_data,GObject * weak_object)4945 account_conn_get_known_avatar_tokens_cb (TpConnection *conn,
4946 GHashTable *tokens,
4947 const GError *error,
4948 gpointer user_data,
4949 GObject *weak_object)
4950 {
4951 McdAccount *self = g_object_ref (weak_object);
4952
4953 self->priv->waiting_for_initial_avatar = FALSE;
4954
4955 if (error != NULL)
4956 {
4957 DEBUG ("%s: GetKnownAvatarTokens raised %s #%d: %s",
4958 self->priv->unique_name, g_quark_to_string (error->domain),
4959 error->code, error->message);
4960 }
4961 else if (self->priv->self_contact == user_data)
4962 {
4963 TpHandle handle = tp_contact_get_handle (self->priv->self_contact);
4964
4965 mcd_account_process_initial_avatar_token (self,
4966 g_hash_table_lookup (tokens, GUINT_TO_POINTER (handle)));
4967 }
4968 else
4969 {
4970 DEBUG ("%s: GetKnownAvatarTokens for outdated self-contact '%s', "
4971 "ignoring",
4972 self->priv->unique_name, tp_contact_get_identifier (user_data));
4973 }
4974
4975 g_object_unref (self);
4976 }
4977
4978 static void
mcd_account_self_contact_upgraded_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)4979 mcd_account_self_contact_upgraded_cb (GObject *source_object,
4980 GAsyncResult *res,
4981 gpointer user_data)
4982 {
4983 TpConnection *conn = TP_CONNECTION (source_object);
4984 McdAccount *self = tp_weak_ref_dup_object (user_data);
4985 GPtrArray *contacts = NULL;
4986 GError *error = NULL;
4987
4988 if (self == NULL)
4989 return;
4990
4991 g_return_if_fail (MCD_IS_ACCOUNT (self));
4992
4993 if (tp_connection_upgrade_contacts_finish (conn, res, &contacts, &error))
4994 {
4995 TpContact *self_contact;
4996
4997 g_assert (contacts->len == 1);
4998 self_contact = g_ptr_array_index (contacts, 0);
4999
5000 if (self_contact == self->priv->self_contact)
5001 {
5002 DEBUG ("%s", tp_contact_get_identifier (self_contact));
5003
5004 tp_g_signal_connect_object (self_contact, "notify::alias",
5005 G_CALLBACK (mcd_account_self_contact_notify_alias_cb),
5006 self, G_CONNECT_SWAPPED);
5007 mcd_account_self_contact_notify_alias_cb (self, NULL, self_contact);
5008
5009 tp_g_signal_connect_object (self_contact, "notify::avatar-file",
5010 G_CALLBACK (mcd_account_self_contact_notify_avatar_file_cb),
5011 self, G_CONNECT_SWAPPED);
5012
5013 tp_g_signal_connect_object (self_contact, "presence-changed",
5014 G_CALLBACK (mcd_account_update_self_presence),
5015 self, G_CONNECT_SWAPPED);
5016
5017 /* If the connection doesn't support SimplePresence then the
5018 * presence will be (UNSET, '', '') which is what we want anyway. */
5019 mcd_account_update_self_presence (self,
5020 tp_contact_get_presence_type (self_contact),
5021 tp_contact_get_presence_status (self_contact),
5022 tp_contact_get_presence_message (self_contact),
5023 self_contact);
5024
5025 /* We have to use GetKnownAvatarTokens() because of its special
5026 * case for CMs that don't always download an up-to-date
5027 * avatar token before signalling CONNECTED. */
5028 if (tp_proxy_has_interface_by_id (conn,
5029 TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS))
5030 {
5031 guint self_handle = tp_contact_get_handle (self_contact);
5032 GArray *arr = g_array_new (FALSE, FALSE, sizeof (guint));
5033
5034 g_array_append_val (arr, self_handle);
5035 tp_cli_connection_interface_avatars_call_get_known_avatar_tokens (
5036 conn, -1, arr, account_conn_get_known_avatar_tokens_cb,
5037 g_object_ref (self_contact), g_object_unref,
5038 (GObject *) self);
5039 }
5040 }
5041 else if (self->priv->self_contact == NULL)
5042 {
5043 DEBUG ("self-contact '%s' has disappeared since we asked to "
5044 "upgrade it", tp_contact_get_identifier (self_contact));
5045 }
5046 else
5047 {
5048 DEBUG ("self-contact '%s' has changed to '%s' since we asked to "
5049 "upgrade it", tp_contact_get_identifier (self_contact),
5050 tp_contact_get_identifier (self->priv->self_contact));
5051 }
5052
5053 g_ptr_array_unref (contacts);
5054 }
5055 else
5056 {
5057 DEBUG ("failed to prepare self-contact: %s", error->message);
5058 g_clear_error (&error);
5059 }
5060
5061 g_object_unref (self);
5062 tp_weak_ref_destroy (user_data);
5063 }
5064
5065 static void
mcd_account_self_contact_changed_cb(McdAccount * self,GParamSpec * unused_param_spec G_GNUC_UNUSED,TpConnection * tp_connection)5066 mcd_account_self_contact_changed_cb (McdAccount *self,
5067 GParamSpec *unused_param_spec G_GNUC_UNUSED,
5068 TpConnection *tp_connection)
5069 {
5070 static const TpContactFeature contact_features[] = {
5071 TP_CONTACT_FEATURE_AVATAR_TOKEN,
5072 TP_CONTACT_FEATURE_AVATAR_DATA,
5073 TP_CONTACT_FEATURE_ALIAS,
5074 TP_CONTACT_FEATURE_PRESENCE
5075 };
5076 TpContact *self_contact;
5077
5078 if (tp_connection != self->priv->tp_connection)
5079 return;
5080
5081 self_contact = tp_connection_get_self_contact (tp_connection);
5082 g_assert (self_contact != NULL);
5083
5084 DEBUG ("%s", tp_contact_get_identifier (self_contact));
5085
5086 if (self_contact == self->priv->self_contact)
5087 return;
5088
5089 g_clear_object (&self->priv->self_contact);
5090 self->priv->self_contact = g_object_ref (self_contact);
5091
5092 _mcd_account_set_normalized_name (self,
5093 tp_contact_get_identifier (self_contact));
5094
5095 tp_connection_upgrade_contacts_async (tp_connection,
5096 1, &self_contact,
5097 G_N_ELEMENTS (contact_features), contact_features,
5098 mcd_account_self_contact_upgraded_cb,
5099 tp_weak_ref_new (self, NULL, NULL));
5100 }
5101
5102 static void
mcd_account_connection_ready_cb(McdAccount * account,McdConnection * connection)5103 mcd_account_connection_ready_cb (McdAccount *account,
5104 McdConnection *connection)
5105 {
5106 McdAccountPrivate *priv = account->priv;
5107 gchar *nickname;
5108 TpConnection *tp_connection;
5109 TpConnectionStatus status;
5110 TpConnectionStatusReason reason;
5111 const gchar *dbus_error = NULL;
5112 const GHashTable *details = NULL;
5113
5114 g_return_if_fail (MCD_IS_ACCOUNT (account));
5115 g_return_if_fail (connection == priv->connection);
5116
5117 tp_connection = mcd_connection_get_tp_connection (connection);
5118 g_return_if_fail (tp_connection != NULL);
5119 g_return_if_fail (priv->tp_connection == NULL ||
5120 tp_connection == priv->tp_connection);
5121 g_assert (tp_proxy_is_prepared (tp_connection,
5122 TP_CONNECTION_FEATURE_CONNECTED));
5123
5124 status = tp_connection_get_status (tp_connection, &reason);
5125 dbus_error = tp_connection_get_detailed_error (tp_connection, &details);
5126 _mcd_account_set_connection_status (account, status, reason,
5127 tp_connection, dbus_error, details);
5128
5129 tp_g_signal_connect_object (tp_connection, "notify::self-contact",
5130 G_CALLBACK (mcd_account_self_contact_changed_cb), account,
5131 G_CONNECT_SWAPPED);
5132 mcd_account_self_contact_changed_cb (account, NULL, tp_connection);
5133 g_assert (priv->self_contact != NULL);
5134
5135 /* FIXME: ideally, on protocols with server-stored nicknames, this should
5136 * only be done if the local Nickname has been changed since last time we
5137 * were online; Aliasing doesn't currently offer a way to tell whether
5138 * this is such a protocol, though.
5139 *
5140 * As a first step towards doing the right thing, we assume that if our
5141 * locally-stored nickname is just the protocol identifer, the
5142 * server-stored nickname (if any) takes precedence.
5143 */
5144 nickname = mcd_account_get_alias (account);
5145
5146 if (tp_str_empty (nickname))
5147 {
5148 DEBUG ("no nickname yet");
5149 }
5150 else if (!tp_strdiff (nickname,
5151 tp_contact_get_identifier (priv->self_contact)))
5152 {
5153 DEBUG ("not setting nickname to '%s' since it matches the "
5154 "NormalizedName", nickname);
5155 }
5156 else
5157 {
5158 mcd_account_send_nickname_to_connection (account, nickname);
5159 }
5160
5161 g_free (nickname);
5162 }
5163
5164 void
_mcd_account_set_connection(McdAccount * account,McdConnection * connection)5165 _mcd_account_set_connection (McdAccount *account, McdConnection *connection)
5166 {
5167 McdAccountPrivate *priv;
5168
5169 g_return_if_fail (MCD_IS_ACCOUNT (account));
5170 priv = account->priv;
5171 if (connection == priv->connection) return;
5172
5173 if (priv->connection)
5174 {
5175 g_signal_handlers_disconnect_by_func (priv->connection,
5176 on_connection_abort, account);
5177 g_signal_handlers_disconnect_by_func (priv->connection,
5178 on_conn_status_changed,
5179 account);
5180 g_signal_handlers_disconnect_by_func (priv->connection,
5181 mcd_account_connection_ready_cb,
5182 account);
5183 g_object_unref (priv->connection);
5184 }
5185
5186 tp_clear_object (&priv->tp_connection);
5187
5188 priv->connection = connection;
5189 priv->waiting_for_initial_avatar = TRUE;
5190
5191 if (connection)
5192 {
5193 g_return_if_fail (MCD_IS_CONNECTION (connection));
5194 g_object_ref (connection);
5195
5196 if (_mcd_connection_is_ready (connection))
5197 {
5198 mcd_account_connection_ready_cb (account, connection);
5199 }
5200 else
5201 {
5202 g_signal_connect_swapped (connection, "ready",
5203 G_CALLBACK (mcd_account_connection_ready_cb), account);
5204 }
5205
5206 g_signal_connect (connection, "connection-status-changed",
5207 G_CALLBACK (on_conn_status_changed), account);
5208 g_signal_connect (connection, "abort",
5209 G_CALLBACK (on_connection_abort), account);
5210 }
5211 else
5212 {
5213 priv->conn_status = TP_CONNECTION_STATUS_DISCONNECTED;
5214 }
5215 }
5216
5217 void
_mcd_account_set_has_been_online(McdAccount * account)5218 _mcd_account_set_has_been_online (McdAccount *account)
5219 {
5220 if (!account->priv->has_been_online)
5221 {
5222 GValue value = G_VALUE_INIT;
5223 const gchar *account_name = mcd_account_get_unique_name (account);
5224
5225 g_value_init (&value, G_TYPE_BOOLEAN);
5226 g_value_set_boolean (&value, TRUE);
5227
5228 mcd_storage_set_attribute (account->priv->storage, account_name,
5229 MC_ACCOUNTS_KEY_HAS_BEEN_ONLINE, &value);
5230 account->priv->has_been_online = TRUE;
5231 mcd_storage_commit (account->priv->storage, account_name);
5232 mcd_account_changed_property (account, MC_ACCOUNTS_KEY_HAS_BEEN_ONLINE,
5233 &value);
5234 g_value_unset (&value);
5235 }
5236 }
5237
5238 McdAccountConnectionContext *
_mcd_account_get_connection_context(McdAccount * self)5239 _mcd_account_get_connection_context (McdAccount *self)
5240 {
5241 g_return_val_if_fail (MCD_IS_ACCOUNT (self), NULL);
5242
5243 return self->priv->connection_context;
5244 }
5245
5246 void
_mcd_account_set_connection_context(McdAccount * self,McdAccountConnectionContext * c)5247 _mcd_account_set_connection_context (McdAccount *self,
5248 McdAccountConnectionContext *c)
5249 {
5250 g_return_if_fail (MCD_IS_ACCOUNT (self));
5251
5252 if (self->priv->connection_context != NULL)
5253 {
5254 _mcd_account_connection_context_free (self->priv->connection_context);
5255 }
5256
5257 self->priv->connection_context = c;
5258 }
5259
5260 gboolean
_mcd_account_get_always_on(McdAccount * self)5261 _mcd_account_get_always_on (McdAccount *self)
5262 {
5263 g_return_val_if_fail (MCD_IS_ACCOUNT (self), FALSE);
5264
5265 return self->priv->always_on;
5266 }
5267
5268 gboolean
_mcd_account_needs_dispatch(McdAccount * self)5269 _mcd_account_needs_dispatch (McdAccount *self)
5270 {
5271 g_return_val_if_fail (MCD_IS_ACCOUNT (self), FALSE);
5272
5273 return self->priv->always_dispatch;
5274 }
5275
5276 gboolean
mcd_account_parameter_is_secret(McdAccount * self,const gchar * name)5277 mcd_account_parameter_is_secret (McdAccount *self, const gchar *name)
5278 {
5279 McdAccountPrivate *priv = self->priv;
5280 const TpConnectionManagerParam *param;
5281
5282 param = mcd_manager_get_protocol_param (priv->manager,
5283 priv->protocol_name, name);
5284
5285 return (param != NULL &&
5286 tp_connection_manager_param_is_secret (param));
5287 }
5288
5289 void
_mcd_account_set_changing_presence(McdAccount * self,gboolean value)5290 _mcd_account_set_changing_presence (McdAccount *self, gboolean value)
5291 {
5292 McdAccountPrivate *priv = self->priv;
5293 GValue changing_presence = G_VALUE_INIT;
5294
5295 priv->changing_presence = value;
5296
5297 g_value_init (&changing_presence, G_TYPE_BOOLEAN);
5298 g_value_set_boolean (&changing_presence, value);
5299
5300 mcd_account_changed_property (self, "ChangingPresence",
5301 &changing_presence);
5302
5303 g_value_unset (&changing_presence);
5304 }
5305
5306 gchar *
mcd_account_dup_display_name(McdAccount * self)5307 mcd_account_dup_display_name (McdAccount *self)
5308 {
5309 const gchar *name = mcd_account_get_unique_name (self);
5310
5311 return mcd_storage_dup_string (self->priv->storage, name, "DisplayName");
5312 }
5313
5314 gchar *
mcd_account_dup_icon(McdAccount * self)5315 mcd_account_dup_icon (McdAccount *self)
5316 {
5317 const gchar *name = mcd_account_get_unique_name (self);
5318
5319 return mcd_storage_dup_string (self->priv->storage, name, "Icon");
5320 }
5321
5322 gchar *
mcd_account_dup_nickname(McdAccount * self)5323 mcd_account_dup_nickname (McdAccount *self)
5324 {
5325 const gchar *name = mcd_account_get_unique_name (self);
5326
5327 return mcd_storage_dup_string (self->priv->storage, name, "Nickname");
5328 }
5329
5330 McdConnectivityMonitor *
mcd_account_get_connectivity_monitor(McdAccount * self)5331 mcd_account_get_connectivity_monitor (McdAccount *self)
5332 {
5333 return self->priv->connectivity;
5334 }
5335
5336 gboolean
mcd_account_get_waiting_for_connectivity(McdAccount * self)5337 mcd_account_get_waiting_for_connectivity (McdAccount *self)
5338 {
5339 return self->priv->waiting_for_connectivity;
5340 }
5341
5342 void
mcd_account_set_waiting_for_connectivity(McdAccount * self,gboolean waiting)5343 mcd_account_set_waiting_for_connectivity (McdAccount *self,
5344 gboolean waiting)
5345 {
5346 self->priv->waiting_for_connectivity = waiting;
5347 }
5348