1 /* Mission Control plugin API - Account Storage plugins.
2 *
3 * Copyright © 2010 Nokia Corporation
4 * Copyright © 2010 Collabora Ltd.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * SECTION:account-storage
23 * @title: McpAccountStorage
24 * @short_description: Account Storage object, implemented by plugins
25 * @see_also:
26 * @include: mission-control-plugins/mission-control-plugins.h
27 *
28 * Plugins may implement #McpAccountStorage in order to provide account
29 * parameter storage backends to the AccountManager object.
30 *
31 * To do so, the plugin must implement a #GObject subclass that implements
32 * #McpAccountStorage, then return an instance of that subclass from
33 * mcp_plugin_ref_nth_object().
34 *
35 * Many methods take "the unique name of an account" as an argument.
36 * In this plugin, that means the unique "tail" of the account's
37 * object path, for instance "gabble/jabber/chris_40example_2ecom".
38 * The account's full object path is obtained by prepending
39 * %TP_ACCOUNT_OBJECT_PATH_BASE.
40 *
41 * A complete implementation of this interface with all methods would
42 * look something like this:
43 *
44 * <example><programlisting>
45 * G_DEFINE_TYPE_WITH_CODE (FooPlugin, foo_plugin,
46 * G_TYPE_OBJECT,
47 * G_IMPLEMENT_INTERFACE (...);
48 * G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_STORAGE,
49 * account_storage_iface_init));
50 * /<!-- -->* ... *<!-- -->/
51 * static void
52 * account_storage_iface_init (McpAccountStorageIface *iface)
53 * {
54 * iface->priority = 0;
55 * iface->name = "foo";
56 * iface->desc = "The FOO storage backend";
57 * iface->provider = "org.freedesktop.Telepathy.MissionControl5.FooStorage";
58 *
59 * iface->get = foo_plugin_get;
60 * iface->set = foo_plugin_get;
61 * iface->delete = foo_plugin_delete;
62 * iface->commit = foo_plugin_commit;
63 * iface->commit_one = foo_plugin_commit_one;
64 * iface->list = foo_plugin_list;
65 * iface->ready = foo_plugin_ready;
66 * iface->get_identifier = foo_plugin_get_identifier;
67 * iface->get_additional_info = foo_plugin_get_additional_info;
68 * iface->get_restrictions = foo_plugin_get_restrictions;
69 * iface->create = foo_plugin_create;
70 * iface->owns = foo_plugin_owns;
71 * iface->set_attribute = foo_plugin_set_attribute;
72 * iface->set_parameter = foo_plugin_set_parameter;
73 * }
74 * </programlisting></example>
75 *
76 * A single object can implement more than one interface; It is currently
77 * unlikely that you would find it useful to implement anything other than
78 * an account storage plugin in an account storage object, though.
79 */
80
81 #include "config.h"
82
83 #include <mission-control-plugins/mission-control-plugins.h>
84 #include <mission-control-plugins/mcp-signals-marshal.h>
85 #include <mission-control-plugins/implementation.h>
86 #include <mission-control-plugins/debug-internal.h>
87 #include <glib.h>
88
89 #define MCP_DEBUG_TYPE MCP_DEBUG_ACCOUNT_STORAGE
90
91 #ifdef ENABLE_DEBUG
92
93 #define SDEBUG(_p, _format, ...) \
94 DEBUG("%s: " _format, \
95 (_p != NULL) ? mcp_account_storage_name (_p) : "NULL", ##__VA_ARGS__)
96
97 #else /* ENABLE_DEBUG */
98
99 #define SDEBUG(_p, _format, ...) do {} while (0);
100
101 #endif /* ENABLE_DEBUG */
102
103 enum
104 {
105 CREATED,
106 ALTERED,
107 TOGGLED,
108 DELETED,
109 ALTERED_ONE,
110 RECONNECT,
111 NO_SIGNAL
112 };
113
114 static guint signals[NO_SIGNAL] = { 0 };
115
116 static gboolean
default_set(const McpAccountStorage * storage,const McpAccountManager * am,const gchar * account,const gchar * key,const gchar * val)117 default_set (const McpAccountStorage *storage,
118 const McpAccountManager *am,
119 const gchar *account,
120 const gchar *key,
121 const gchar *val)
122 {
123 return FALSE;
124 }
125
126 static gboolean
default_set_attribute(McpAccountStorage * storage,McpAccountManager * am,const gchar * account,const gchar * attribute,GVariant * value,McpAttributeFlags flags)127 default_set_attribute (McpAccountStorage *storage,
128 McpAccountManager *am,
129 const gchar *account,
130 const gchar *attribute,
131 GVariant *value,
132 McpAttributeFlags flags)
133 {
134 return FALSE;
135 }
136
137 static gboolean
default_set_parameter(McpAccountStorage * storage,McpAccountManager * am,const gchar * account,const gchar * parameter,GVariant * value,McpParameterFlags flags)138 default_set_parameter (McpAccountStorage *storage,
139 McpAccountManager *am,
140 const gchar *account,
141 const gchar *parameter,
142 GVariant *value,
143 McpParameterFlags flags)
144 {
145 return FALSE;
146 }
147
148 static gboolean
default_owns(McpAccountStorage * storage,McpAccountManager * am,const gchar * account)149 default_owns (McpAccountStorage *storage,
150 McpAccountManager *am,
151 const gchar *account)
152 {
153 /* This has the side-effect of pushing the "manager" key back into @am,
154 * but that should be a no-op in practice: we always call this
155 * method in priority order and stop at the first one that says "yes",
156 * and @am's idea of what "manager" is should have come from that same
157 * plugin anyway. */
158 return mcp_account_storage_get (storage, am, account, "manager");
159 }
160
161 static void
class_init(gpointer klass,gpointer data)162 class_init (gpointer klass,
163 gpointer data)
164 {
165 GType type = G_TYPE_FROM_CLASS (klass);
166 McpAccountStorageIface *iface = klass;
167
168 iface->owns = default_owns;
169 iface->set = default_set;
170 iface->set_attribute = default_set_attribute;
171 iface->set_parameter = default_set_parameter;
172
173 if (signals[CREATED] != 0)
174 {
175 DEBUG ("already registered signals");
176 return;
177 }
178
179 /**
180 * McpAccountStorage::created
181 * @account: the unique name of the created account
182 *
183 * Emitted if an external entity creates an account
184 * in the backend the emitting plugin handles.
185 *
186 * Should not be fired until mcp_account_storage_ready() has been called
187 *
188 */
189 signals[CREATED] = g_signal_new ("created",
190 type, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
191 g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
192 1, G_TYPE_STRING);
193
194 /**
195 * McpAccountStorage::altered
196 * @account: the unique name of the altered account
197 *
198 * This signal does not appear to be fully implemented
199 * (see <ulink href="https://bugs.freedesktop.org/show_bug.cgi?id=28288"
200 * >freedesktop.org bug 28288</ulink>).
201 * Emit #McpAccountStorage::altered-one instead.
202 *
203 * Should not be fired until mcp_account_storage_ready() has been called
204 *
205 */
206 signals[ALTERED] = g_signal_new ("altered",
207 type, G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED, 0, NULL, NULL,
208 g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
209 1, G_TYPE_STRING);
210
211 /**
212 * McpAccountStorage::altered-one
213 * @account: the unique name of the altered account
214 * @name: the name of the altered property (its key)
215 *
216 * Emitted if an external entity alters an account
217 * in the backend that the emitting plugin handles.
218 *
219 * Before emitting this signal, the plugin must call
220 * either mcp_account_manager_set_attribute(),
221 * either mcp_account_manager_set_parameter() or
222 * mcp_account_manager_set_value() to push the new value
223 * into the account manager.
224 *
225 * Note that mcp_account_manager_set_parameter() does not use the
226 * "param-" prefix, but this signal and mcp_account_manager_set_value()
227 * both do.
228 *
229 * Should not be fired until mcp_account_storage_ready() has been called
230 */
231 signals[ALTERED_ONE] = g_signal_new ("altered-one",
232 type, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
233 _mcp_marshal_VOID__STRING_STRING, G_TYPE_NONE,
234 2, G_TYPE_STRING, G_TYPE_STRING);
235
236
237 /**
238 * McpAccountStorage::deleted
239 * @account: the unique name of the deleted account
240 *
241 * Emitted if an external entity deletes an account
242 * in the backend the emitting plugin handles.
243 *
244 * Should not be fired until mcp_account_storage_ready() has been called
245 *
246 */
247 signals[DELETED] = g_signal_new ("deleted",
248 type, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
249 g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
250 1, G_TYPE_STRING);
251
252 /**
253 * McpAccountStorage::toggled
254 * @account: the unique name of the toggled account
255 * @enabled: #gboolean indicating whether the account is enabled
256 *
257 * Emitted if an external entity enables/disables an account
258 * in the backend the emitting plugin handles. This is similar to
259 * emitting #McpAccountStorage::altered-one for the attribute
260 * "Enabled", except that the plugin is not required to call
261 * a function like mcp_account_manager_set_value() first.
262 *
263 * Should not be fired until mcp_account_storage_ready() has been called
264 *
265 */
266 signals[TOGGLED] = g_signal_new ("toggled",
267 type, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
268 _mcp_marshal_VOID__STRING_BOOLEAN, G_TYPE_NONE,
269 2, G_TYPE_STRING, G_TYPE_BOOLEAN);
270
271 /**
272 * McpAccountStorage::reconnect
273 * @account: the unique name of the account to reconnect
274 *
275 * emitted if an external entity modified important parameters of the
276 * account and a reconnection is required in order to apply them.
277 *
278 * Should not be fired until mcp_account_storage_ready() has been called
279 **/
280 signals[RECONNECT] = g_signal_new ("reconnect",
281 type, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
282 g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
283 1, G_TYPE_STRING);
284 }
285
286 GType
mcp_account_storage_get_type(void)287 mcp_account_storage_get_type (void)
288 {
289 static gsize once = 0;
290 static GType type = 0;
291
292 if (g_once_init_enter (&once))
293 {
294 static const GTypeInfo info =
295 {
296 sizeof (McpAccountStorageIface),
297 NULL, /* base_init */
298 NULL, /* base_finalize */
299 class_init, /* class_init */
300 NULL, /* class_finalize */
301 NULL, /* class_data */
302 0, /* instance_size */
303 0, /* n_preallocs */
304 NULL, /* instance_init */
305 NULL /* value_table */
306 };
307
308 type = g_type_register_static (G_TYPE_INTERFACE,
309 "McpAccountStorage", &info, 0);
310 g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
311
312 g_once_init_leave (&once, 1);
313 }
314
315 return type;
316 }
317
318 /**
319 * McpAccountStorage:
320 *
321 * An object implementing the account storage plugin interface.
322 */
323
324 /**
325 * McpAccountStorageIface:
326 * @parent: the standard fields for an interface
327 * @priority: returned by mcp_account_storage_priority()
328 * @name: returned by mcp_account_storage_name()
329 * @desc: returned by mcp_account_storage_description()
330 * @provider: returned by mcp_account_storage_provider()
331 * @set: implementation of mcp_account_storage_set()
332 * @get: implementation of mcp_account_storage_get()
333 * @delete: implementation of mcp_account_storage_delete()
334 * @commit: implementation of mcp_account_storage_commit()
335 * @list: implementation of mcp_account_storage_list()
336 * @ready: implementation of mcp_account_storage_ready()
337 * @commit_one: implementation of mcp_account_storage_commit_one()
338 * @get_identifier: implementation of mcp_account_storage_get_identifier()
339 * @get_additional_info: implementation of
340 * mcp_account_storage_get_additional_info()
341 * @get_restrictions: implementation of mcp_account_storage_get_restrictions()
342 * @create: implementation of mcp_account_storage_create()
343 *
344 * The interface vtable for an account storage plugin.
345 */
346
347 void
mcp_account_storage_iface_set_priority(McpAccountStorageIface * iface,guint prio)348 mcp_account_storage_iface_set_priority (McpAccountStorageIface *iface,
349 guint prio)
350 {
351 iface->priority = prio;
352 }
353
354 void
mcp_account_storage_iface_set_name(McpAccountStorageIface * iface,const gchar * name)355 mcp_account_storage_iface_set_name (McpAccountStorageIface *iface,
356 const gchar *name)
357 {
358 iface->name = name;
359 }
360
361 void
mcp_account_storage_iface_set_desc(McpAccountStorageIface * iface,const gchar * desc)362 mcp_account_storage_iface_set_desc (McpAccountStorageIface *iface,
363 const gchar *desc)
364 {
365 iface->desc = desc;
366 }
367
368 void
mcp_account_storage_iface_set_provider(McpAccountStorageIface * iface,const gchar * provider)369 mcp_account_storage_iface_set_provider (McpAccountStorageIface *iface,
370 const gchar *provider)
371 {
372 iface->provider = provider;
373 }
374
375 void
mcp_account_storage_iface_implement_get(McpAccountStorageIface * iface,McpAccountStorageGetFunc method)376 mcp_account_storage_iface_implement_get (McpAccountStorageIface *iface,
377 McpAccountStorageGetFunc method)
378 {
379 iface->get = method;
380 }
381
382 void
mcp_account_storage_iface_implement_set(McpAccountStorageIface * iface,McpAccountStorageSetFunc method)383 mcp_account_storage_iface_implement_set (McpAccountStorageIface *iface,
384 McpAccountStorageSetFunc method)
385 {
386 iface->set = method;
387 }
388
389 void
mcp_account_storage_iface_implement_delete(McpAccountStorageIface * iface,McpAccountStorageDeleteFunc method)390 mcp_account_storage_iface_implement_delete (McpAccountStorageIface *iface,
391 McpAccountStorageDeleteFunc method)
392 {
393 iface->delete = method;
394 }
395
396 void
mcp_account_storage_iface_implement_commit(McpAccountStorageIface * iface,McpAccountStorageCommitFunc method)397 mcp_account_storage_iface_implement_commit (McpAccountStorageIface *iface,
398 McpAccountStorageCommitFunc method)
399 {
400 iface->commit = method;
401 }
402
403 void
mcp_account_storage_iface_implement_commit_one(McpAccountStorageIface * iface,McpAccountStorageCommitOneFunc method)404 mcp_account_storage_iface_implement_commit_one (McpAccountStorageIface *iface,
405 McpAccountStorageCommitOneFunc method)
406 {
407 iface->commit_one = method;
408 }
409
410 void
mcp_account_storage_iface_implement_list(McpAccountStorageIface * iface,McpAccountStorageListFunc method)411 mcp_account_storage_iface_implement_list (McpAccountStorageIface *iface,
412 McpAccountStorageListFunc method)
413 {
414 iface->list = method;
415 }
416
417 void
mcp_account_storage_iface_implement_ready(McpAccountStorageIface * iface,McpAccountStorageReadyFunc method)418 mcp_account_storage_iface_implement_ready (McpAccountStorageIface *iface,
419 McpAccountStorageReadyFunc method)
420 {
421 iface->ready = method;
422 }
423
424 void
mcp_account_storage_iface_implement_get_identifier(McpAccountStorageIface * iface,McpAccountStorageGetIdentifierFunc method)425 mcp_account_storage_iface_implement_get_identifier (
426 McpAccountStorageIface *iface,
427 McpAccountStorageGetIdentifierFunc method)
428 {
429 iface->get_identifier = method;
430 }
431
432 void
mcp_account_storage_iface_implement_get_additional_info(McpAccountStorageIface * iface,McpAccountStorageGetAdditionalInfoFunc method)433 mcp_account_storage_iface_implement_get_additional_info (
434 McpAccountStorageIface *iface,
435 McpAccountStorageGetAdditionalInfoFunc method)
436 {
437 iface->get_additional_info = method;
438 }
439
440 void
mcp_account_storage_iface_implement_get_restrictions(McpAccountStorageIface * iface,McpAccountStorageGetRestrictionsFunc method)441 mcp_account_storage_iface_implement_get_restrictions (
442 McpAccountStorageIface *iface,
443 McpAccountStorageGetRestrictionsFunc method)
444 {
445 iface->get_restrictions = method;
446 }
447
448 void
mcp_account_storage_iface_implement_create(McpAccountStorageIface * iface,McpAccountStorageCreate method)449 mcp_account_storage_iface_implement_create (
450 McpAccountStorageIface *iface,
451 McpAccountStorageCreate method)
452 {
453 iface->create = method;
454 }
455
456 /**
457 * mcp_account_storage_priority:
458 * @storage: an #McpAccountStorage instance
459 *
460 * Gets the priority for this plugin.
461 *
462 * Priorities currently run from MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_DEFAULT
463 * (the default storage plugin priority) upwards. More-positive numbers
464 * are higher priority.
465 *
466 * Plugins at a higher priority then MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING
467 * used to have the opportunity to "steal" passwords from the gnome keyring.
468 * It is no longer significant.
469 *
470 * Plugins at a lower priority than the default plugin will never be asked to
471 * store any details, although they may still be asked to list them at startup
472 * time, and may asynchronously notify MC of accounts via the signals above.
473 *
474 * When loading accounts at startup, plugins are consulted in order from
475 * lowest to highest, so that higher priority plugins may overrule settings
476 * from lower priority plugins.
477 *
478 * Loading all the accounts is only done at startup, before the dbus name
479 * is claimed, and is therefore the only time plugins are allowed to indulge
480 * in blocking calls (indeed, they are expected to carry out this operation,
481 * and ONLY this operation, synchronously).
482 *
483 * When values are being set, the plugins are invoked from highest priority
484 * to lowest, with the first plugin that claims a setting being assigned
485 * ownership, and all lower priority plugins being asked to delete the
486 * setting in question.
487 *
488 * Returns: the priority of this plugin
489 **/
490 gint
mcp_account_storage_priority(const McpAccountStorage * storage)491 mcp_account_storage_priority (const McpAccountStorage *storage)
492 {
493 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
494
495 g_return_val_if_fail (iface != NULL, -1);
496
497 return iface->priority;
498 }
499
500 /**
501 * McpAccountStorageGetFunc:
502 * @storage: the account storage plugin
503 * @am: object used to call back into the account manager
504 * @account: the unique name of the account
505 * @key: the setting whose value we wish to fetch: either an attribute
506 * like "DisplayName", or "param-" plus a parameter like "account"
507 *
508 * An implementation of mcp_account_storage_get().
509 *
510 * Returns: %TRUE if @storage is responsible for @account
511 */
512
513 /**
514 * mcp_account_storage_get:
515 * @storage: an #McpAccountStorage instance
516 * @am: an #McpAccountManager instance
517 * @account: the unique name of the account
518 * @key: the setting whose value we wish to fetch: either an attribute
519 * like "DisplayName", or "param-" plus a parameter like "account"
520 *
521 * Get a value from the plugin's in-memory cache.
522 * Before emitting this signal, the plugin must call
523 * either mcp_account_manager_set_attribute(),
524 * mcp_account_manager_set_parameter(),
525 * or mcp_account_manager_set_value() and (if appropriate)
526 * mcp_account_manager_parameter_make_secret()
527 * before returning from this method call.
528 *
529 * Note that mcp_account_manager_set_parameter() does not use the
530 * "param-" prefix, even if called from this function.
531 *
532 * If @key is %NULL the plugin should iterate through all attributes and
533 * parameters, and push each of them into @am, as if this method had
534 * been called once for each attribute or parameter. It must then return
535 * %TRUE if any attributes or parameters were found, or %FALSE if it
536 * was not responsible for @account.
537 *
538 * Returns: %TRUE if @storage is responsible for @account
539 */
540 gboolean
mcp_account_storage_get(const McpAccountStorage * storage,McpAccountManager * am,const gchar * account,const gchar * key)541 mcp_account_storage_get (const McpAccountStorage *storage,
542 McpAccountManager *am,
543 const gchar *account,
544 const gchar *key)
545 {
546 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
547
548 SDEBUG (storage, "");
549 g_return_val_if_fail (iface != NULL, FALSE);
550 g_return_val_if_fail (iface->get != NULL, FALSE);
551
552 return iface->get (storage, am, account, key);
553 }
554
555 /**
556 * McpAccountStorageSetFunc:
557 * @storage: an #McpAccountStorage instance
558 * @am: an #McpAccountManager instance
559 * @account: the unique name of the account
560 * @key: the setting whose value we wish to store: either an attribute
561 * like "DisplayName", or "param-" plus a parameter like "account"
562 * @val: a non-%NULL value for @key
563 *
564 * An implementation of mcp_account_storage_set().
565 *
566 * Returns: %TRUE if @storage is responsible for @account
567 */
568
569 /**
570 * mcp_account_storage_set:
571 * @storage: an #McpAccountStorage instance
572 * @am: an #McpAccountManager instance
573 * @account: the unique name of the account
574 * @key: the non-%NULL setting whose value we wish to store: either an
575 * attribute like "DisplayName", or "param-" plus a parameter like "account"
576 * @value: a value to associate with @key, escaped as if for a #GKeyFile
577 *
578 * The plugin is expected to either quickly and synchronously
579 * update its internal cache of values with @value, or to
580 * decline to store the setting.
581 *
582 * The plugin is not expected to write to its long term storage
583 * at this point. It can expect Mission Control to call either
584 * mcp_account_storage_commit() or mcp_account_storage_commit_one()
585 * after a short delay.
586 *
587 * Plugins that implement mcp_storage_set_attribute() and
588 * mcp_account_storage_set_parameter() can just return %FALSE here.
589 * There is a default implementation, which just returns %FALSE.
590 *
591 * Returns: %TRUE if the attribute was claimed, %FALSE otherwise
592 */
593 gboolean
mcp_account_storage_set(const McpAccountStorage * storage,const McpAccountManager * am,const gchar * account,const gchar * key,const gchar * value)594 mcp_account_storage_set (const McpAccountStorage *storage,
595 const McpAccountManager *am,
596 const gchar *account,
597 const gchar *key,
598 const gchar *value)
599 {
600 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
601
602 SDEBUG (storage, "");
603 g_return_val_if_fail (iface != NULL, FALSE);
604
605 return iface->set (storage, am, account, key, value);
606 }
607
608 /**
609 * mcp_account_storage_set_attribute:
610 * @storage: an #McpAccountStorage instance
611 * @am: an #McpAccountManager instance
612 * @account: the unique name of the account
613 * @attribute: the name of an attribute, e.g. "DisplayName"
614 * @value: a value to associate with @attribute
615 * @flags: flags influencing how the attribute is to be stored
616 *
617 * Store an attribute.
618 *
619 * The plugin is expected to either quickly and synchronously
620 * update its internal cache of values with @value, or to
621 * decline to store the attribute.
622 *
623 * The plugin is not expected to write to its long term storage
624 * at this point.
625 *
626 * There is a default implementation, which just returns %FALSE.
627 * Mission Control will call mcp_account_storage_set() instead,
628 * using a keyfile-escaped version of @value.
629 *
630 * Returns: %TRUE if the attribute was claimed, %FALSE otherwise
631 *
632 * Since: 5.15.0
633 */
634 gboolean
mcp_account_storage_set_attribute(McpAccountStorage * storage,McpAccountManager * am,const gchar * account,const gchar * attribute,GVariant * value,McpAttributeFlags flags)635 mcp_account_storage_set_attribute (McpAccountStorage *storage,
636 McpAccountManager *am,
637 const gchar *account,
638 const gchar *attribute,
639 GVariant *value,
640 McpAttributeFlags flags)
641 {
642 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
643
644 SDEBUG (storage, "");
645 g_return_val_if_fail (iface != NULL, FALSE);
646 g_return_val_if_fail (iface->set_attribute != NULL, FALSE);
647
648 return iface->set_attribute (storage, am, account, attribute, value, flags);
649 }
650
651 /**
652 * mcp_account_storage_set_parameter:
653 * @storage: an #McpAccountStorage instance
654 * @am: an #McpAccountManager instance
655 * @account: the unique name of the account
656 * @parameter: the name of a parameter, e.g. "account" (note that there
657 * is no "param-" prefix here)
658 * @value: a value to associate with @parameter
659 * @flags: flags influencing how the parameter is to be stored
660 *
661 * Store a parameter.
662 *
663 * The plugin is expected to either quickly and synchronously
664 * update its internal cache of values with @value, or to
665 * decline to store the parameter.
666 *
667 * The plugin is not expected to write to its long term storage
668 * at this point.
669 *
670 * There is a default implementation, which just returns %FALSE.
671 * Mission Control will call mcp_account_storage_set() instead,
672 * using "param-" + @parameter as key and a keyfile-escaped version
673 * of @value as value.
674 *
675 * Returns: %TRUE if the parameter was claimed, %FALSE otherwise
676 *
677 * Since: 5.15.0
678 */
679 gboolean
mcp_account_storage_set_parameter(McpAccountStorage * storage,McpAccountManager * am,const gchar * account,const gchar * parameter,GVariant * value,McpParameterFlags flags)680 mcp_account_storage_set_parameter (McpAccountStorage *storage,
681 McpAccountManager *am,
682 const gchar *account,
683 const gchar *parameter,
684 GVariant *value,
685 McpParameterFlags flags)
686 {
687 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
688
689 SDEBUG (storage, "");
690 g_return_val_if_fail (iface != NULL, FALSE);
691 g_return_val_if_fail (iface->set_parameter != NULL, FALSE);
692
693 return iface->set_parameter (storage, am, account, parameter, value, flags);
694 }
695
696 /**
697 * McpAccountStorageCreate:
698 * @storage: an #McpAccountStorage instance
699 * @am: an object which can be used to call back into the account manager
700 * @manager: the name of the manager
701 * @protocol: the name of the protocol
702 * @params: A gchar * / GValue * hash table of account parameters
703 * @error: a GError to fill
704 *
705 * An implementation of mcp_account_storage_create()
706 *
707 * Returns: (transfer full): the account name or %NULL
708 */
709
710 /**
711 * mcp_account_storage_create:
712 * @storage: an #McpAccountStorage instance
713 * @am: an object which can be used to call back into the account manager
714 * @manager: the name of the manager
715 * @protocol: the name of the protocol
716 * @params: A gchar * / GValue * hash table of account parameters
717 * @error: a GError to fill
718 *
719 * Inform the plugin that a new account is being created. @manager, @protocol
720 * and @params are given to help determining the account's unique name, but does
721 * not need to be stored on the account yet, mcp_account_storage_set() and
722 * mcp_account_storage_commit() will be called later.
723 *
724 * It is recommended to use mcp_account_manager_get_unique_name() to create the
725 * unique name, but it's not mandatory. One could base the unique name on an
726 * internal storage identifier, prefixed with the provider's name
727 * (e.g. goa__1234).
728 *
729 * #McpAccountStorage::created signal should not be emitted for this account,
730 * not even when mcp_account_storage_commit() will be called.
731 *
732 * Returns: (transfer full): the newly allocated account name, which should
733 * be freed once the caller is done with it, or %NULL if that couldn't
734 * be done.
735 */
736 gchar *
mcp_account_storage_create(const McpAccountStorage * storage,const McpAccountManager * am,const gchar * manager,const gchar * protocol,GHashTable * params,GError ** error)737 mcp_account_storage_create (const McpAccountStorage *storage,
738 const McpAccountManager *am,
739 const gchar *manager,
740 const gchar *protocol,
741 GHashTable *params,
742 GError **error)
743 {
744 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
745
746 g_return_val_if_fail (iface != NULL, NULL);
747
748 if (iface->create == NULL)
749 {
750 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
751 "This storage does not implement create function");
752 return NULL;
753 }
754
755 return iface->create (storage, am, manager, protocol, params, error);
756 }
757
758 /**
759 * McpAccountStorageDeleteFunc:
760 * @storage: an #McpAccountStorage instance
761 * @am: an #McpAccountManager instance
762 * @account: the unique name of the account
763 * @key: (allow-none): the setting whose value we wish to store - either an
764 * attribute like "DisplayName", or "param-" plus a parameter like
765 * "account" - or %NULL to delete the entire account
766 *
767 * An implementation of mcp_account_storage_delete().
768 *
769 * Returns: %TRUE if the setting or settings are not
770 * the plugin's cache after this operation, %FALSE otherwise.
771 */
772
773 /**
774 * mcp_account_storage_delete:
775 * @storage: an #McpAccountStorage instance
776 * @am: an #McpAccountManager instance
777 * @account: the unique name of the account
778 * @key: (allow-none): the setting whose value we wish to store - either an
779 * attribute like "DisplayName", or "param-" plus a parameter like
780 * "account" - or %NULL to delete the entire account
781 *
782 * The plugin is expected to remove the setting for @key from its
783 * internal cache and to remember that its state has changed, so
784 * that it can delete said setting from its long term storage if
785 * its long term storage method makes this necessary.
786 *
787 * If @key is %NULL, the plugin should forget all its settings for
788 * @account,and remember to delete the entire account from its storage later.
789 *
790 * The plugin is not expected to update its long term storage at
791 * this point.
792 *
793 * Returns: %TRUE if the setting or settings are not
794 * the plugin's cache after this operation, %FALSE otherwise.
795 * This is very unlikely to ever be %FALSE, as a plugin is always
796 * expected to be able to manipulate its own cache.
797 */
798 gboolean
mcp_account_storage_delete(const McpAccountStorage * storage,const McpAccountManager * am,const gchar * account,const gchar * key)799 mcp_account_storage_delete (const McpAccountStorage *storage,
800 const McpAccountManager *am,
801 const gchar *account,
802 const gchar *key)
803 {
804 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
805
806 SDEBUG (storage, "");
807 g_return_val_if_fail (iface != NULL, FALSE);
808
809 return iface->delete (storage, am, account, key);
810 }
811
812 /**
813 * McpAccountStorageCommitFunc:
814 * @storage: an #McpAccountStorage instance
815 * @am: an #McpAccountManager instance
816 *
817 * An implementation of mcp_account_storage_commit().
818 *
819 * Returns: %TRUE if the commit process was started successfully
820 */
821
822 /**
823 * mcp_account_storage_commit:
824 * @storage: an #McpAccountStorage instance
825 * @am: an #McpAccountManager instance
826 *
827 * The plugin is expected to write its cache to long term storage,
828 * deleting, adding or updating entries in said storage as needed.
829 *
830 * This call is expected to return promptly, but the plugin is
831 * not required to have finished its commit operation when it returns,
832 * merely to have started the operation.
833 *
834 * If the @commit_one method is implemented, it will be called preferentially
835 * if only one account is to be committed. If the @commit_one method is
836 * implemented but @commit is not, @commit_one will be called with
837 * @account_name = %NULL to commit all accounts.
838 *
839 * Returns: %TRUE if the commit process was started (but not necessarily
840 * completed) successfully; %FALSE if there was a problem that was immediately
841 * obvious.
842 */
843 gboolean
mcp_account_storage_commit(const McpAccountStorage * storage,const McpAccountManager * am)844 mcp_account_storage_commit (const McpAccountStorage *storage,
845 const McpAccountManager *am)
846 {
847 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
848
849 SDEBUG (storage, "committing all accounts");
850 g_return_val_if_fail (iface != NULL, FALSE);
851
852 if (iface->commit != NULL)
853 {
854 return iface->commit (storage, am);
855 }
856 else if (iface->commit_one != NULL)
857 {
858 return iface->commit_one (storage, am, NULL);
859 }
860 else
861 {
862 SDEBUG (storage,
863 "neither commit nor commit_one is implemented; cannot save accounts");
864 return FALSE;
865 }
866 }
867
868 /**
869 * McpAccountStorageCommitOneFunc:
870 * @storage: an #McpAccountStorage instance
871 * @am: an #McpAccountManager instance
872 * @account: (allow-none): the unique suffix of an account's object path,
873 * or %NULL
874 *
875 * An implementation of mcp_account_storage_commit_one().
876 *
877 * Returns: %TRUE if the commit process was started successfully
878 */
879
880 /**
881 * mcp_account_storage_commit_one:
882 * @storage: an #McpAccountStorage instance
883 * @am: an #McpAccountManager instance
884 * @account: (allow-none): the unique suffix of an account's object path,
885 * or %NULL if all accounts are to be committed and
886 * mcp_account_storage_commit() is unimplemented
887 *
888 * The same as mcp_account_storage_commit(), but only commit the given
889 * account. This is optional to implement; the default implementation
890 * is to call @commit.
891 *
892 * If both mcp_account_storage_commit_one() and mcp_account_storage_commit()
893 * are implemented, Mission Control will never pass @account = %NULL to
894 * this method.
895 *
896 * Returns: %TRUE if the commit process was started (but not necessarily
897 * completed) successfully; %FALSE if there was a problem that was immediately
898 * obvious.
899 */
900 gboolean
mcp_account_storage_commit_one(const McpAccountStorage * storage,const McpAccountManager * am,const gchar * account)901 mcp_account_storage_commit_one (const McpAccountStorage *storage,
902 const McpAccountManager *am,
903 const gchar *account)
904 {
905 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
906
907 SDEBUG (storage, "called for %s", account ? account : "<all accounts>");
908 g_return_val_if_fail (iface != NULL, FALSE);
909
910 if (iface->commit_one != NULL)
911 return iface->commit_one (storage, am, account);
912 else
913 /* Fall back to plain ->commit() */
914 return mcp_account_storage_commit (storage, am);
915 }
916
917 /**
918 * McpAccountStorageListFunc:
919 * @storage: an #McpAccountStorage instance
920 * @am: an #McpAccountManager instance
921 *
922 * An implementation of mcp_account_storage_list().
923 *
924 * Returns: (transfer full): a list of account names
925 */
926
927 /**
928 * mcp_account_storage_list:
929 * @storage: an #McpAccountStorage instance
930 * @am: an #McpAccountManager instance
931 *
932 * Load details of every account stored by this plugin into an in-memory cache
933 * so that it can respond to requests promptly.
934 *
935 * This method is called only at initialisation time, before the dbus name
936 * has been claimed, and is the only one permitted to block.
937 *
938 * Returns: (element-type utf8) (transfer full): a list of account names that
939 * the plugin has settings for. The account names should be freed with
940 * g_free(), and the list with g_list_free(), when the caller is done with
941 * them.
942 */
943 GList *
mcp_account_storage_list(const McpAccountStorage * storage,const McpAccountManager * am)944 mcp_account_storage_list (const McpAccountStorage *storage,
945 const McpAccountManager *am)
946 {
947 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
948
949 SDEBUG (storage, "");
950 g_return_val_if_fail (iface != NULL, NULL);
951
952 return iface->list (storage, am);
953 }
954
955 /**
956 * McpAccountStorageReadyFunc:
957 * @storage: an #McpAccountStorage instance
958 * @am: an #McpAccountManager instance
959 *
960 * An implementation of mcp_account_storage_ready().
961 */
962
963 /**
964 * mcp_account_storage_ready:
965 * @storage: an #McpAccountStorage instance
966 * @am: an #McpAccountManager instance
967 *
968 * Informs the plugin that it is now permitted to create new accounts,
969 * ie it can now fire its "created", "altered", "toggled" and "deleted"
970 * signals.
971 */
972 void
mcp_account_storage_ready(const McpAccountStorage * storage,const McpAccountManager * am)973 mcp_account_storage_ready (const McpAccountStorage *storage,
974 const McpAccountManager *am)
975 {
976 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
977
978 g_return_if_fail (iface != NULL);
979
980 /* plugins that can't create accounts from external sources don't *
981 * need to implement this method, as they can never fire the async *
982 * account change signals: */
983 if (iface->ready != NULL)
984 iface->ready (storage, am);
985 }
986
987 /**
988 * McpAccountStorageGetIdentifierFunc:
989 * @storage: an #McpAccountStorage instance
990 * @account: the unique name of the account
991 * @identifier: (out caller-allocates): a zero-filled #GValue whose type
992 * can be sent over D-Bus by dbus-glib, to hold the identifier.
993 *
994 * An implementation of mcp_account_storage_get_identifier().
995 */
996
997 /**
998 * mcp_account_storage_get_identifier:
999 * @storage: an #McpAccountStorage instance
1000 * @account: the unique name of the account
1001 * @identifier: (out caller-allocates): a zero-filled #GValue whose type
1002 * can be sent over D-Bus by dbus-glib, to hold the identifier.
1003 *
1004 * Get the storage-specific identifier for this account. The type is variant,
1005 * hence the GValue.
1006 *
1007 * This method will only be called for the storage plugin that "owns"
1008 * the account.
1009 */
1010 void
mcp_account_storage_get_identifier(const McpAccountStorage * storage,const gchar * account,GValue * identifier)1011 mcp_account_storage_get_identifier (const McpAccountStorage *storage,
1012 const gchar *account,
1013 GValue *identifier)
1014 {
1015 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
1016
1017 SDEBUG (storage, "");
1018 g_return_if_fail (iface != NULL);
1019 g_return_if_fail (identifier != NULL);
1020 g_return_if_fail (!G_IS_VALUE (identifier));
1021
1022 if (iface->get_identifier == NULL)
1023 {
1024 g_value_init (identifier, G_TYPE_STRING);
1025 g_value_set_string (identifier, account);
1026 }
1027 else
1028 {
1029 iface->get_identifier (storage, account, identifier);
1030 }
1031 }
1032
1033 /**
1034 * McpAccountStorageGetAdditionalInfoFunc
1035 * @storage: an #McpAccountStorage instance
1036 * @account: the unique name of the account
1037 *
1038 * An implementation of mcp_account_storage_get_identifier().
1039 *
1040 * Returns: (transfer container) (element-type utf8 GObject.Value): additional
1041 * storage-specific information
1042 */
1043
1044 /**
1045 * mcp_account_storage_get_additional_info:
1046 * @storage: an #McpAccountStorage instance
1047 * @account: the unique name of the account
1048 *
1049 * Return additional storage-specific information about this account, which is
1050 * made available on D-Bus but not otherwise interpreted by Mission Control.
1051 *
1052 * This method will only be called for the storage plugin that "owns"
1053 * the account.
1054 *
1055 * Returns: (transfer container) (element-type utf8 GObject.Value): additional
1056 * storage-specific information
1057 */
1058 GHashTable *
mcp_account_storage_get_additional_info(const McpAccountStorage * storage,const gchar * account)1059 mcp_account_storage_get_additional_info (const McpAccountStorage *storage,
1060 const gchar *account)
1061 {
1062 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
1063 GHashTable *ret = NULL;
1064
1065 SDEBUG (storage, "");
1066 g_return_val_if_fail (iface != NULL, FALSE);
1067
1068 if (iface->get_additional_info != NULL)
1069 ret = iface->get_additional_info (storage, account);
1070
1071 if (ret == NULL)
1072 ret = g_hash_table_new (g_str_hash, g_str_equal);
1073
1074 return ret;
1075 }
1076
1077 /**
1078 * McpAccountStorageGetRestrictionsFunc
1079 * @storage: an #McpAccountStorage instance
1080 * @account: the unique name of the account
1081 *
1082 * An implementation of mcp_account_storage_get_restrictions().
1083 *
1084 * Returns: any restrictions that apply to this account.
1085 */
1086
1087 /**
1088 * mcp_account_storage_get_restrictions:
1089 * @storage: an #McpAccountStorage instance
1090 * @account: the unique name of the account
1091 *
1092 * This method will only be called for the storage plugin that "owns"
1093 * the account.
1094 *
1095 * Returns: a bitmask of %TpStorageRestrictionFlags with the restrictions to
1096 * account storage.
1097 */
1098 /* FIXME: when breaking API, make this return TpStorageRestrictionFlags */
1099 guint
mcp_account_storage_get_restrictions(const McpAccountStorage * storage,const gchar * account)1100 mcp_account_storage_get_restrictions (const McpAccountStorage *storage,
1101 const gchar *account)
1102 {
1103 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
1104
1105 g_return_val_if_fail (iface != NULL, 0);
1106
1107 if (iface->get_restrictions == NULL)
1108 return 0;
1109 else
1110 return iface->get_restrictions (storage, account);
1111 }
1112
1113 /**
1114 * mcp_account_storage_name:
1115 * @storage: an #McpAccountStorage instance
1116 *
1117 * <!-- nothing else to say -->
1118 *
1119 * Returns: the plugin's name (for logging etc)
1120 */
1121 const gchar *
mcp_account_storage_name(const McpAccountStorage * storage)1122 mcp_account_storage_name (const McpAccountStorage *storage)
1123 {
1124 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
1125
1126 g_return_val_if_fail (iface != NULL, NULL);
1127
1128 return iface->name;
1129 }
1130
1131 /**
1132 * mcp_account_storage_description:
1133 * @storage: an #McpAccountStorage instance
1134 *
1135 * <!-- nothing else to say -->
1136 *
1137 * Returns: the plugin's description (for logging etc)
1138 */
1139 const gchar *
mcp_account_storage_description(const McpAccountStorage * storage)1140 mcp_account_storage_description (const McpAccountStorage *storage)
1141 {
1142 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
1143
1144 g_return_val_if_fail (iface != NULL, NULL);
1145
1146 return iface->desc;
1147 }
1148
1149 /**
1150 * mcp_account_storage_provider:
1151 * @storage: an #McpAccountStorage instance
1152 *
1153 * <!-- nothing else to say -->
1154 *
1155 * Returns: a D-Bus namespaced name for this plugin, or "" if none
1156 * was provided in #McpAccountStorageIface.provider.
1157 */
1158 const gchar *
mcp_account_storage_provider(const McpAccountStorage * storage)1159 mcp_account_storage_provider (const McpAccountStorage *storage)
1160 {
1161 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
1162
1163 g_return_val_if_fail (iface != NULL, NULL);
1164
1165 return iface->provider != NULL ? iface->provider : "";
1166 }
1167
1168 /**
1169 * mcp_account_storage_emit_create:
1170 * @storage: an #McpAccountStorage instance
1171 * @account: the unique name of the created account
1172 *
1173 * Emits the #McpAccountStorage::created signal.
1174 */
1175 void
mcp_account_storage_emit_created(McpAccountStorage * storage,const gchar * account)1176 mcp_account_storage_emit_created (McpAccountStorage *storage,
1177 const gchar *account)
1178 {
1179 g_signal_emit (storage, signals[CREATED], 0, account);
1180 }
1181
1182 /**
1183 * mcp_account_storage_emit_altered:
1184 * @storage: an #McpAccountStorage instance
1185 * @account: the unique name of the altered account
1186 *
1187 * Emits the #McpAccountStorage::altered signal
1188 */
1189 void
mcp_account_storage_emit_altered(McpAccountStorage * storage,const gchar * account)1190 mcp_account_storage_emit_altered (McpAccountStorage *storage,
1191 const gchar *account)
1192 {
1193 g_signal_emit (storage, signals[ALTERED], 0, account);
1194 }
1195
1196 /**
1197 * mcp_account_storage_emit_altered_one:
1198 * @storage: an #McpAccountStorage instance
1199 * @account: the unique name of the altered account
1200 * @key: the key of the altered property: either an attribute name
1201 * like "DisplayName", or "param-" plus a parameter name like "account"
1202 *
1203 * Emits the #McpAccountStorage::altered-one signal
1204 */
1205 void
mcp_account_storage_emit_altered_one(McpAccountStorage * storage,const gchar * account,const gchar * key)1206 mcp_account_storage_emit_altered_one (McpAccountStorage *storage,
1207 const gchar *account,
1208 const gchar *key)
1209 {
1210 g_signal_emit (storage, signals[ALTERED_ONE], 0, account, key);
1211 }
1212
1213 /**
1214 * mcp_account_storage_emit_deleted:
1215 * @storage: an #McpAccountStorage instance
1216 * @account: the unique name of the deleted account
1217 *
1218 * Emits the #McpAccountStorage::deleted signal
1219 */
1220 void
mcp_account_storage_emit_deleted(McpAccountStorage * storage,const gchar * account)1221 mcp_account_storage_emit_deleted (McpAccountStorage *storage,
1222 const gchar *account)
1223 {
1224 g_signal_emit (storage, signals[DELETED], 0, account);
1225 }
1226
1227 /**
1228 * mcp_account_storage_emit_toggled:
1229 * @storage: an #McpAccountStorage instance
1230 * @account: the unique name of the account
1231 * @enabled: %TRUE if the account is now enabled
1232 *
1233 * Emits ::toggled signal
1234 */
1235 void
mcp_account_storage_emit_toggled(McpAccountStorage * storage,const gchar * account,gboolean enabled)1236 mcp_account_storage_emit_toggled (McpAccountStorage *storage,
1237 const gchar *account,
1238 gboolean enabled)
1239 {
1240 g_signal_emit (storage, signals[TOGGLED], 0, account, enabled);
1241 }
1242
1243 /**
1244 * mcp_account_storage_emit_reconnect:
1245 * @storage: an #McpAccountStorage instance
1246 * @account: the unique name of the account to reconnect
1247 *
1248 * Emits ::reconnect signal
1249 */
1250 void
mcp_account_storage_emit_reconnect(McpAccountStorage * storage,const gchar * account)1251 mcp_account_storage_emit_reconnect (McpAccountStorage *storage,
1252 const gchar *account)
1253 {
1254 g_signal_emit (storage, signals[RECONNECT], 0, account);
1255 }
1256
1257 /**
1258 * mcp_account_storage_owns:
1259 * @storage: an #McpAccountStorage instance
1260 * @am: an #McpAccountManager instance
1261 * @account: the unique name (object-path tail) of an account
1262 *
1263 * Check whether @account is stored in @storage. The highest-priority
1264 * plugin for which this function returns %TRUE is considered to be
1265 * responsible for @account.
1266 *
1267 * There is a default implementation, which calls mcp_account_storage_get()
1268 * for the well-known key "manager".
1269 *
1270 * Returns: %TRUE if @account is stored in @storage
1271 *
1272 * Since: 5.15.0
1273 */
1274 gboolean
mcp_account_storage_owns(McpAccountStorage * storage,McpAccountManager * am,const gchar * account)1275 mcp_account_storage_owns (McpAccountStorage *storage,
1276 McpAccountManager *am,
1277 const gchar *account)
1278 {
1279 McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage);
1280
1281 g_return_val_if_fail (iface != NULL, FALSE);
1282 g_return_val_if_fail (iface->owns != NULL, FALSE);
1283
1284 return iface->owns (storage, am, account);
1285 }
1286