1 /*
2 * dbus-properties-mixin.c - D-Bus core Properties
3 * Copyright (C) 2008 Collabora Ltd.
4 * Copyright (C) 2008 Nokia Corporation
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 #include "config.h"
22
23 #include <telepathy-glib/dbus-properties-mixin.h>
24
25 #include <telepathy-glib/errors.h>
26 #include <telepathy-glib/svc-generic.h>
27 #include <telepathy-glib/util.h>
28
29 #define DEBUG_FLAG TP_DEBUG_PROPERTIES
30 #include "telepathy-glib/debug-internal.h"
31
32 /**
33 * SECTION:dbus-properties-mixin
34 * @title: TpDBusPropertiesMixin
35 * @short_description: a mixin implementation of the DBus.Properties interface
36 * @see_also: #TpSvcDBusProperties
37 *
38 * This mixin provides an implementation of the org.freedesktop.DBus.Properties
39 * interface. It relies on the auto-generated service-side GInterfaces from
40 * telepathy-glib >= 0.7.3, or something similar, to register the abstract
41 * properties and their GTypes; classes with the mixin can then register
42 * an implementation of the properties.
43 *
44 * To register D-Bus properties in a GInterface to be implementable with this
45 * mixin, either use the code-generation tools from telepathy-glib >= 0.7.3,
46 * or call tp_svc_interface_set_dbus_properties_info() from a section of the
47 * base_init function that only runs once.
48 *
49 * To use this mixin, include a #TpDBusPropertiesMixinClass somewhere
50 * in your class structure, populate it with pointers to statically allocated
51 * (or duplicated and never freed) data, and call
52 * tp_dbus_properties_mixin_class_init() from your class_init implementation.
53 *
54 * To use this mixin as the implementation of #TpSvcDBusProperties,
55 * call <literal>G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
56 * tp_dbus_properties_mixin_iface_init)</literal> in the fourth argument to
57 * <literal>G_DEFINE_TYPE_WITH_CODE</literal>.
58 *
59 * Since: 0.7.3
60 */
61
62 /**
63 * TpDBusPropertiesMixinFlags:
64 * @TP_DBUS_PROPERTIES_MIXIN_FLAG_READ: The property can be read using Get and
65 * GetAll
66 * @TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE: The property can be written using Set
67 * @TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED: The property's new value is
68 * included in emissions of PropertiesChanged
69 * @TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED: The property is announced
70 * as invalidated, without its value, in emissions of PropertiesChanged
71 *
72 * Bitfield representing allowed access to a property. At most one of
73 * %TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED and
74 * %TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED may be specified for a
75 * property.
76 *
77 * Since 0.11.5, there is a corresponding #GFlagsClass type,
78 * %TP_TYPE_DBUS_PROPERTIES_MIXIN_FLAGS.
79 *
80 * Since: 0.7.3
81 */
82
83 /**
84 * TP_TYPE_DBUS_PROPERTIES_MIXIN_FLAGS:
85 *
86 * The #GFlagsClass type of #TpDBusPropertiesMixinFlags.
87 *
88 * Since: 0.11.5
89 */
90
91 /**
92 * TpDBusPropertiesMixinPropInfo:
93 * @name: Quark representing the property's name
94 * @flags: Flags representing read/write access to the property
95 * @dbus_signature: The D-Bus signature of the property
96 * @type: The GType used in a GValue to implement the property
97 *
98 * Semi-abstract description of a property, as attached to a service
99 * GInterface. This structure must either be statically allocated, or
100 * duplicated and never freed, so it always remains valid.
101 *
102 * In addition to the documented members, there are two private pointers
103 * for future expansion, which must always be initialized to %NULL.
104 *
105 * Since: 0.7.3
106 */
107
108 /**
109 * TpDBusPropertiesMixinIfaceInfo:
110 * @dbus_interface: Quark representing the interface's name
111 * @props: Array of property descriptions, terminated by one with
112 * @name == %NULL
113 *
114 * Semi-abstract description of an interface. Each service GInterface that
115 * has properties must have one of these attached to it via
116 * tp_svc_interface_set_dbus_properties_info() in its base_init function;
117 * service GInterfaces that do not have properties may have one of these
118 * with no properties.
119 *
120 * This structure must either be statically allocated, or duplicated and never
121 * freed, so it always remains valid.
122 *
123 * In addition to the documented members, there are two private pointers
124 * for future expansion, which must always be initialized to %NULL.
125 *
126 * Since: 0.7.3
127 */
128
129 static GQuark
_iface_prop_info_quark(void)130 _iface_prop_info_quark (void)
131 {
132 static GQuark q = 0;
133
134 if (G_UNLIKELY (q == 0))
135 q = g_quark_from_static_string
136 ("tp_svc_interface_get_dbus_properties_info@TELEPATHY_GLIB_0.7.3");
137
138 return q;
139 }
140
141 /**
142 * tp_svc_interface_set_dbus_properties_info:
143 * @g_interface: The #GType of a service interface
144 * @info: an interface description
145 *
146 * Declare that @g_interface implements the given D-Bus interface, with the
147 * given properties. This may only be called once per GInterface, usually from
148 * a section of its base_init function that only runs once.
149 *
150 * This is typically only used within generated code; there is normally no
151 * reason to call it manually.
152 *
153 * Since: 0.7.3
154 */
155 void
tp_svc_interface_set_dbus_properties_info(GType g_interface,TpDBusPropertiesMixinIfaceInfo * info)156 tp_svc_interface_set_dbus_properties_info (GType g_interface,
157 TpDBusPropertiesMixinIfaceInfo *info)
158 {
159 GQuark q = _iface_prop_info_quark ();
160 TpDBusPropertiesMixinPropInfo *prop;
161
162 g_return_if_fail (G_TYPE_IS_INTERFACE (g_interface));
163 g_return_if_fail (g_type_get_qdata (g_interface, q) == NULL);
164 g_return_if_fail (info->dbus_interface != 0);
165 g_return_if_fail (info->props != NULL);
166
167 for (prop = info->props; prop->name != 0; prop++)
168 {
169 g_return_if_fail (prop->flags != 0);
170 g_return_if_fail (
171 (prop->flags & ~( TP_DBUS_PROPERTIES_MIXIN_FLAG_READ
172 | TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE
173 | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED
174 | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED
175 )) == 0);
176
177 /* Check that at most one change-related flag is set. */
178 if ((prop->flags & TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED) &&
179 (prop->flags & TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED))
180 {
181 CRITICAL ("at most one of EMITS_CHANGED and EMITS_INVALIDATED may be "
182 "specified for a property, but %s.%s has both",
183 g_quark_to_string (info->dbus_interface),
184 g_quark_to_string (prop->name));
185 g_return_if_reached ();
186 }
187
188 g_return_if_fail (prop->dbus_signature != NULL);
189 g_return_if_fail (prop->dbus_signature[0] != '\0');
190 g_return_if_fail (prop->type != 0);
191 }
192
193 g_type_set_qdata (g_interface, q, info);
194 }
195
196 /**
197 * tp_svc_interface_get_dbus_properties_info: (skip)
198 * @g_interface: The #GType of a service interface
199 *
200 * Retrieves the D-Bus property metadata for the given interface, if any.
201 * This function is typically not useful outside telepathy-glib itself, but may
202 * be useful for domain-specific variations on the theme of SetProperty. If in
203 * doubt, you probably don't need this function.
204 *
205 * Returns: D-Bus property metadata for @g_interface, or %NULL if it has
206 * none.
207 * Since: 0.15.8
208 */
209 TpDBusPropertiesMixinIfaceInfo *
tp_svc_interface_get_dbus_properties_info(GType g_interface)210 tp_svc_interface_get_dbus_properties_info (GType g_interface)
211 {
212 return g_type_get_qdata (g_interface, _iface_prop_info_quark ());
213 }
214
215 /**
216 * TpDBusPropertiesMixinGetter:
217 * @object: The exported object with the properties
218 * @iface: A quark representing the D-Bus interface name
219 * @name: A quark representing the D-Bus property name
220 * @value: A GValue pre-initialized to the right type, into which to put
221 * the value
222 * @getter_data: The getter_data from the #TpDBusPropertiesMixinPropImpl
223 *
224 * Signature of a callback used to get the value of a property.
225 *
226 * For simplicity, in this mixin we don't allow getting a property to fail;
227 * implementations must always be prepared to return *something*.
228 */
229
230 /**
231 * tp_dbus_properties_mixin_getter_gobject_properties:
232 * @object: The exported object with the properties
233 * @iface: A quark representing the D-Bus interface name
234 * @name: A quark representing the D-Bus property name
235 * @value: A GValue pre-initialized to the right type, into which to put
236 * the value
237 * @getter_data: The getter_data from the #TpDBusPropertiesMixinPropImpl,
238 * which must be a string containing the GObject property's name
239 *
240 * An implementation of #TpDBusPropertiesMixinGetter which assumes that
241 * the @getter_data is the name of a readable #GObject property of an
242 * appropriate type, and uses it for the value of the D-Bus property.
243 */
244 void
tp_dbus_properties_mixin_getter_gobject_properties(GObject * object,GQuark iface,GQuark name,GValue * value,gpointer getter_data)245 tp_dbus_properties_mixin_getter_gobject_properties (GObject *object,
246 GQuark iface,
247 GQuark name,
248 GValue *value,
249 gpointer getter_data)
250 {
251 g_object_get_property (object, getter_data, value);
252 }
253
254 /**
255 * TpDBusPropertiesMixinSetter:
256 * @object: The exported object with the properties
257 * @iface: A quark representing the D-Bus interface name
258 * @name: A quark representing the D-Bus property name
259 * @value: The new value for the property
260 * @setter_data: The setter_data from the #TpDBusPropertiesMixinPropImpl
261 * @error: Used to return an error on failure
262 *
263 * Signature of a callback used to get the value of a property.
264 *
265 * Returns: %TRUE on success, %FALSE (setting @error) on failure
266 */
267
268 /**
269 * tp_dbus_properties_mixin_setter_gobject_properties:
270 * @object: The exported object with the properties
271 * @iface: A quark representing the D-Bus interface name
272 * @name: A quark representing the D-Bus property name
273 * @value: The new value for the property
274 * @setter_data: The setter_data from the #TpDBusPropertiesMixinPropImpl,
275 * which must be a string containing the GObject property's name
276 * @error: Not used
277 *
278 * An implementation of #TpDBusPropertiesMixinSetter which assumes that the
279 * @setter_data is the name of a writable #GObject property of an appropriate
280 * type, and sets that property to the given value.
281 *
282 * Returns: %TRUE
283 */
284 gboolean
tp_dbus_properties_mixin_setter_gobject_properties(GObject * object,GQuark iface,GQuark name,const GValue * value,gpointer setter_data,GError ** error)285 tp_dbus_properties_mixin_setter_gobject_properties (GObject *object,
286 GQuark iface,
287 GQuark name,
288 const GValue *value,
289 gpointer setter_data,
290 GError **error)
291 {
292 g_object_set_property (object, setter_data, value);
293 return TRUE;
294 }
295
296 /**
297 * TpDBusPropertiesMixinPropImpl:
298 * @name: The name of the property as it appears on D-Bus
299 * @getter_data: Arbitrary user-supplied data for the getter function
300 * @setter_data: Arbitrary user-supplied data for the setter function
301 *
302 * Structure representing an implementation of a property.
303 *
304 * In addition to the documented fields, there are three pointers which must
305 * be initialized to %NULL.
306 *
307 * This structure must either be statically allocated, or duplicated and never
308 * freed, so it always remains valid.
309 *
310 * Since: 0.7.3
311 */
312
313 /**
314 * TpDBusPropertiesMixinIfaceImpl:
315 * @name: The name of the interface
316 * @getter: A callback to get the current value of the property, to which
317 * the @getter_data from each property implementation will be passed
318 * @setter: A callback to set a new value for the property, to which
319 * the @setter_data from each property implementation will be passed
320 * @props: An array of property implementations, terminated by one with
321 * @name equal to %NULL
322 *
323 * Structure representing an implementation of an interface's properties.
324 *
325 * In addition to the documented fields, there are four pointers which must
326 * be initialized to %NULL.
327 *
328 * This structure must either be statically allocated, or duplicated and never
329 * freed, so it always remains valid.
330 *
331 * Since: 0.7.3
332 */
333
334 /**
335 * TpDBusPropertiesMixinClass:
336 * @interfaces: An array of interface implementations, terminated by one with
337 * @name equal to %NULL
338 *
339 * Structure representing all of a class's property implementations. One of
340 * these structures may be placed in the layout of an object class structure.
341 *
342 * In addition to the documented fields, there are 7 pointers reserved for
343 * future use, which must be initialized to %NULL.
344 *
345 * Since: 0.7.3
346 */
347
348 static GQuark
_prop_mixin_offset_quark(void)349 _prop_mixin_offset_quark (void)
350 {
351 static GQuark q = 0;
352
353 if (G_UNLIKELY (q == 0))
354 q = g_quark_from_static_string
355 ("tp_dbus_properties_mixin_class_init@TELEPATHY_GLIB_0.7.3");
356
357 return q;
358 }
359
360 static GQuark
_extra_prop_impls_quark(void)361 _extra_prop_impls_quark (void)
362 {
363 static GQuark q = 0;
364
365 if (G_UNLIKELY (q == 0))
366 q = g_quark_from_static_string
367 ("tp_dbus_properties_mixin_implement_interface@TELEPATHY_GLIB_0.7.9");
368
369 return q;
370 }
371
372
373 static gboolean
link_interface(GType type,const GType * interfaces,GQuark iface_quark,TpDBusPropertiesMixinIfaceImpl * iface_impl)374 link_interface (GType type,
375 const GType *interfaces,
376 GQuark iface_quark,
377 TpDBusPropertiesMixinIfaceImpl *iface_impl)
378 {
379 TpDBusPropertiesMixinIfaceInfo *iface_info = NULL;
380 TpDBusPropertiesMixinPropImpl *prop_impl;
381
382 g_return_val_if_fail (iface_impl->props != NULL, FALSE);
383
384 /* no point bothering if there is no quark for the interface name */
385 if (iface_quark != 0)
386 {
387 const GType *iface;
388
389 for (iface = interfaces; *iface != 0; iface++)
390 {
391 iface_info = tp_svc_interface_get_dbus_properties_info (*iface);
392
393 if (iface_info != NULL &&
394 iface_info->dbus_interface == iface_quark)
395 break;
396 else
397 iface_info = NULL;
398 }
399 }
400
401 if (iface_info == NULL)
402 {
403 CRITICAL ("%s tried to implement undefined interface %s "
404 "(perhaps you forgot to call G_IMPLEMENT_INTERFACE?)",
405 g_type_name (type), iface_impl->name);
406 return FALSE;
407 }
408
409 iface_impl->mixin_priv = iface_info;
410
411 for (prop_impl = iface_impl->props; prop_impl->name != NULL; prop_impl++)
412 {
413 TpDBusPropertiesMixinPropInfo *prop_info;
414 GQuark name_quark = g_quark_try_string (prop_impl->name);
415
416 prop_impl->mixin_priv = NULL;
417
418 /* no point bothering if there is no quark for this name */
419 if (name_quark != 0)
420 {
421 for (prop_info = iface_info->props;
422 prop_info->name != 0;
423 prop_info++)
424 {
425 if (prop_info->name == name_quark)
426 {
427 prop_impl->mixin_priv = prop_info;
428 break;
429 }
430 }
431 }
432
433 if (prop_impl->mixin_priv == NULL)
434 {
435 CRITICAL ("%s tried to implement nonexistent property %s"
436 " on interface %s", g_type_name (type), prop_impl->name,
437 iface_impl->name);
438 return FALSE;
439 }
440 }
441
442 return TRUE;
443 }
444
445 /* if this assertion fails, TpDBusPropertiesMixinIfaceImpl.mixin_next (which
446 * used to be a GCallback but is now a gpointer) will be an ABI break on this
447 * architecture, so do some evil trick with unions or something */
448 G_STATIC_ASSERT (sizeof (GCallback) == sizeof (gpointer));
449
450 /* FIXME: GNOME#556489: getter and setter should be (scope infinite) if that
451 * existed */
452 /**
453 * tp_dbus_properties_mixin_implement_interface: (skip)
454 * @cls: a subclass of #GObjectClass
455 * @iface: a quark representing the the name of the interface to implement
456 * @getter: a callback to get properties on this interface, or %NULL if they
457 * are all write-only
458 * @setter: a callback to set properties on this interface, or %NULL if they
459 * are all read-only
460 * @props: an array of #TpDBusPropertiesMixinPropImpl representing individual
461 * properties, terminated by one with @name == %NULL
462 *
463 * Declare that, in addition to any interfaces set in
464 * tp_dbus_properties_mixin_class_init(), the given class (and its subclasses)
465 * will implement the properties of the interface @iface using the callbacks
466 * @getter and @setter and the properties given by @props.
467 *
468 * This function should be called from the class_init callback in such a way
469 * that it will only be called once, even if the class is subclassed.
470 *
471 * Typically, the static array @interfaces in the #TpDBusPropertiesMixinClass
472 * should be used for interfaces whose properties are implemented directly by
473 * the class @cls, and this function should be used for interfaces whose
474 * properties are implemented by mixins.
475 *
476 * It is an error for the same interface to appear in the array @interfaces
477 * in the #TpDBusPropertiesMixinClass, and also be set up by this function.
478 *
479 * If a class C and a subclass S both implement the properties of the same
480 * interface, only the implementations from the subclass S will be used,
481 * regardless of whether the implementations in C and/or S were set up by
482 * this function or via the array @interfaces in the
483 * #TpDBusPropertiesMixinClass.
484 */
485 void
tp_dbus_properties_mixin_implement_interface(GObjectClass * cls,GQuark iface,TpDBusPropertiesMixinGetter getter,TpDBusPropertiesMixinSetter setter,TpDBusPropertiesMixinPropImpl * props)486 tp_dbus_properties_mixin_implement_interface (GObjectClass *cls,
487 GQuark iface,
488 TpDBusPropertiesMixinGetter getter,
489 TpDBusPropertiesMixinSetter setter,
490 TpDBusPropertiesMixinPropImpl *props)
491 {
492 GQuark extras_quark = _extra_prop_impls_quark ();
493 GType type = G_OBJECT_CLASS_TYPE (cls);
494 GType *interfaces = g_type_interfaces (type, NULL);
495 TpDBusPropertiesMixinIfaceImpl *iface_impl;
496
497 g_return_if_fail (G_IS_OBJECT_CLASS (cls));
498
499 /* never freed - intentional per-class leak */
500 iface_impl = g_new0 (TpDBusPropertiesMixinIfaceImpl, 1);
501 iface_impl->name = g_quark_to_string (iface);
502 iface_impl->getter = getter;
503 iface_impl->setter = setter;
504 iface_impl->props = props;
505
506 /* align property implementations with abstract properties */
507 if (G_LIKELY (link_interface (type, interfaces, iface, iface_impl)))
508 {
509 TpDBusPropertiesMixinIfaceImpl *next = g_type_get_qdata (type,
510 extras_quark);
511 #ifdef ENABLE_DEBUG
512 GQuark offset_quark = _prop_mixin_offset_quark ();
513 gpointer offset_qdata = g_type_get_qdata (type, offset_quark);
514 TpDBusPropertiesMixinClass *mixin = NULL;
515 TpDBusPropertiesMixinIfaceImpl *iter;
516
517 if (offset_qdata != NULL)
518 mixin = &G_STRUCT_MEMBER (TpDBusPropertiesMixinClass, cls,
519 GPOINTER_TO_SIZE (offset_qdata));
520
521 /* assert that we're not trying to implement the same interface twice */
522 for (iter = next;
523 iter != NULL && iter->name != NULL;
524 iter = iter->mixin_next)
525 {
526 TpDBusPropertiesMixinIfaceInfo *other_info = iter->mixin_priv;
527
528 g_assert (other_info != NULL);
529
530 if (G_UNLIKELY (other_info->dbus_interface == iface))
531 {
532 CRITICAL ("type %s tried to implement interface %s with %s "
533 "twice", g_type_name (type), g_quark_to_string (iface),
534 G_STRFUNC);
535 goto out;
536 }
537 }
538
539 /* assert that we're not trying to implement the same interface via
540 * this function and the static data */
541 if (mixin != NULL && mixin->interfaces != NULL)
542 {
543 for (iter = mixin->interfaces;
544 iter->name != NULL;
545 iter++)
546 {
547 TpDBusPropertiesMixinIfaceInfo *other_info = iter->mixin_priv;
548
549 g_assert (other_info != NULL);
550
551 if (G_UNLIKELY (other_info->dbus_interface == iface))
552 {
553 CRITICAL ("type %s tried to implement interface %s with %s "
554 "and also in static data", g_type_name (type),
555 g_quark_to_string (iface), G_STRFUNC);
556 goto out;
557 }
558 }
559 }
560 #endif
561
562 /* form a linked list */
563 iface_impl->mixin_next = next;
564 g_type_set_qdata (type, extras_quark, iface_impl);
565 }
566
567 #ifdef ENABLE_DEBUG
568 out:
569 #endif
570 g_free (interfaces);
571 }
572
573
574 /**
575 * tp_dbus_properties_mixin_class_init:
576 * @cls: a subclass of #GObjectClass
577 * @offset: the offset within @cls of a TpDBusPropertiesMixinClass structure
578 *
579 * Initialize the class @cls to use the D-Bus Properties mixin.
580 * The given struct member, of size sizeof(TpDBusPropertiesMixinClass),
581 * will be used to store property implementation information.
582 *
583 * Each property and each interface must have been declared as a member of
584 * a GInterface implemented by @cls, using
585 * tp_svc_interface_set_dbus_properties_info().
586 *
587 * Before calling this function, the array @interfaces must have been
588 * placed in the #TpDBusPropertiesMixinClass structure; if it would be empty,
589 * it may instead be %NULL.
590 *
591 * This function should be called from the class_init callback in such a way
592 * that it will only be called once, even if the class is subclassed.
593 *
594 * Changed in 0.7.9: TpDBusPropertiesMixinClass::interfaces may now be %NULL,
595 * which means that only interfaces whose properties are set up using
596 * tp_dbus_properties_mixin_implement_interface() will be used.
597 *
598 * Changed in 0.7.15: @offset may now be 0, in which case the
599 * #TpDBusPropertiesMixinClass can be omitted from @cls. It is treated as if
600 * it were present, but with all fields (including
601 * TpDBusPropertiesMixinClass::interfaces) being %NULL, so only interfaces
602 * whose properties are set using
603 * tp_dbus_properties_mixin_implement_interface() will be used.
604 *
605 * Since: 0.7.3
606 */
607 void
tp_dbus_properties_mixin_class_init(GObjectClass * cls,gsize offset)608 tp_dbus_properties_mixin_class_init (GObjectClass *cls,
609 gsize offset)
610 {
611 GQuark q = _prop_mixin_offset_quark ();
612 GType type = G_OBJECT_CLASS_TYPE (cls);
613 TpDBusPropertiesMixinClass *mixin;
614 TpDBusPropertiesMixinIfaceImpl *iface_impl;
615 GType *interfaces;
616
617 g_return_if_fail (G_IS_OBJECT_CLASS (cls));
618 g_return_if_fail (g_type_get_qdata (type, q) == NULL);
619 g_type_set_qdata (type, q, GSIZE_TO_POINTER (offset));
620
621 if (offset == 0)
622 return;
623
624 mixin = &G_STRUCT_MEMBER (TpDBusPropertiesMixinClass, cls, offset);
625
626 if (mixin->interfaces == NULL)
627 return;
628
629 interfaces = g_type_interfaces (type, NULL);
630
631 for (iface_impl = mixin->interfaces;
632 iface_impl->name != NULL;
633 iface_impl++)
634 {
635 GQuark iface_quark = g_quark_try_string (iface_impl->name);
636 #ifdef ENABLE_DEBUG
637 TpDBusPropertiesMixinIfaceImpl *other_impl;
638 #endif
639
640 if (G_UNLIKELY (!link_interface (type, interfaces, iface_quark,
641 iface_impl)))
642 goto out;
643
644 #ifdef ENABLE_DEBUG
645 for (other_impl = mixin->interfaces;
646 other_impl != iface_impl;
647 other_impl++)
648 {
649 TpDBusPropertiesMixinIfaceInfo *other_info = other_impl->mixin_priv;
650
651 if (G_UNLIKELY (iface_quark == other_info->dbus_interface))
652 {
653 CRITICAL ("type %s tried to implement interface %s in static "
654 "data twice", g_type_name (type), iface_impl->name);
655 goto out;
656 }
657 }
658 #endif
659 }
660
661 out:
662
663 g_free (interfaces);
664 }
665
666 static TpDBusPropertiesMixinIfaceImpl *
_tp_dbus_properties_mixin_find_iface_impl(GObject * self,const gchar * name)667 _tp_dbus_properties_mixin_find_iface_impl (GObject *self,
668 const gchar *name)
669 {
670 GType type;
671 GQuark offset_quark = _prop_mixin_offset_quark ();
672 GQuark extras_quark = _extra_prop_impls_quark ();
673 GQuark iface_quark = g_quark_try_string (name);
674
675 if (iface_quark == 0)
676 return NULL;
677
678 for (type = G_OBJECT_TYPE (self);
679 type != 0;
680 type = g_type_parent (type))
681 {
682 gpointer offset = g_type_get_qdata (type, offset_quark);
683 TpDBusPropertiesMixinClass *mixin = NULL;
684 TpDBusPropertiesMixinIfaceImpl *iface_impl;
685 TpDBusPropertiesMixinIfaceInfo *iface_info;
686
687 if (offset != NULL)
688 mixin = &G_STRUCT_MEMBER (TpDBusPropertiesMixinClass,
689 G_OBJECT_GET_CLASS (self), GPOINTER_TO_SIZE (offset));
690
691 if (mixin != NULL && mixin->interfaces != NULL)
692 {
693 for (iface_impl = mixin->interfaces;
694 iface_impl->name != NULL;
695 iface_impl++)
696 {
697 iface_info = iface_impl->mixin_priv;
698
699 if (iface_info->dbus_interface == iface_quark)
700 return iface_impl;
701 }
702 }
703
704 for (iface_impl = g_type_get_qdata (type, extras_quark);
705 iface_impl != NULL;
706 iface_impl = iface_impl->mixin_next)
707 {
708 iface_info = iface_impl->mixin_priv;
709
710 if (iface_info->dbus_interface == iface_quark)
711 return iface_impl;
712 }
713 }
714
715 return NULL;
716 }
717
718 static TpDBusPropertiesMixinPropImpl *
_tp_dbus_properties_mixin_find_prop_impl(TpDBusPropertiesMixinIfaceImpl * iface_impl,const gchar * name)719 _tp_dbus_properties_mixin_find_prop_impl
720 (TpDBusPropertiesMixinIfaceImpl *iface_impl,
721 const gchar *name)
722 {
723 GQuark prop_quark = g_quark_try_string (name);
724 TpDBusPropertiesMixinPropImpl *prop_impl;
725
726 if (prop_quark == 0)
727 return NULL;
728
729 for (prop_impl = iface_impl->props;
730 prop_impl->name != NULL;
731 prop_impl++)
732 {
733 TpDBusPropertiesMixinPropInfo *prop_info = prop_impl->mixin_priv;
734
735 if (prop_info->name == prop_quark)
736 return prop_impl;
737 }
738
739 return NULL;
740 }
741
742 static TpDBusPropertiesMixinPropImpl *
_iface_impl_get_property_impl(GObject * self,TpDBusPropertiesMixinIfaceImpl * iface_impl,const gchar * interface_name,const gchar * property_name,GError ** error)743 _iface_impl_get_property_impl (
744 GObject *self,
745 TpDBusPropertiesMixinIfaceImpl *iface_impl,
746 const gchar *interface_name,
747 const gchar *property_name,
748 GError **error)
749 {
750 TpDBusPropertiesMixinPropImpl *prop_impl;
751 TpDBusPropertiesMixinPropInfo *prop_info;
752
753 prop_impl = _tp_dbus_properties_mixin_find_prop_impl (iface_impl,
754 property_name);
755
756 if (prop_impl == NULL)
757 {
758 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
759 "Unknown property %s on %s", property_name, interface_name);
760 return FALSE;
761 }
762
763 prop_info = prop_impl->mixin_priv;
764
765 if ((prop_info->flags & TP_DBUS_PROPERTIES_MIXIN_FLAG_READ) == 0)
766 {
767 g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED,
768 "Property %s on %s is write-only", property_name, interface_name);
769 return FALSE;
770 }
771
772 if (iface_impl->getter == NULL)
773 {
774 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
775 "Getting properties on %s is unimplemented", interface_name);
776 return FALSE;
777 }
778
779 return prop_impl;
780 }
781
782 /**
783 * tp_dbus_properties_mixin_get:
784 * @self: an object with this mixin
785 * @interface_name: a D-Bus interface name
786 * @property_name: a D-Bus property name
787 * @value: an unset GValue (initialized to all zeroes)
788 * @error: used to return an error on failure
789 *
790 * Initialize @value with the type of the property @property_name on
791 * @interface_name, and write the value of that property into it as if
792 * by calling the D-Bus method org.freedesktop.DBus.Properties.Get.
793 *
794 * If Get would return a D-Bus error, @value remains unset and @error
795 * is filled in instead.
796 *
797 * Returns: %TRUE (filling @value) on success, %FALSE (setting @error)
798 * on failure
799 * Since: 0.7.13
800 */
801 gboolean
tp_dbus_properties_mixin_get(GObject * self,const gchar * interface_name,const gchar * property_name,GValue * value,GError ** error)802 tp_dbus_properties_mixin_get (GObject *self,
803 const gchar *interface_name,
804 const gchar *property_name,
805 GValue *value,
806 GError **error)
807 {
808 TpDBusPropertiesMixinIfaceImpl *iface_impl;
809 TpDBusPropertiesMixinPropImpl *prop_impl;
810
811 g_return_val_if_fail (G_IS_OBJECT (self), FALSE);
812 g_return_val_if_fail (interface_name != NULL, FALSE);
813 g_return_val_if_fail (property_name != NULL, FALSE);
814 g_return_val_if_fail (value != NULL, FALSE);
815
816 iface_impl = _tp_dbus_properties_mixin_find_iface_impl (self,
817 interface_name);
818
819 if (iface_impl == NULL)
820 {
821 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
822 "No properties known for interface %s", interface_name);
823 return FALSE;
824 }
825
826 prop_impl = _iface_impl_get_property_impl (self, iface_impl, interface_name,
827 property_name, error);
828
829 if (prop_impl != NULL)
830 {
831 TpDBusPropertiesMixinIfaceInfo *iface_info = iface_impl->mixin_priv;
832 TpDBusPropertiesMixinPropInfo *prop_info = prop_impl->mixin_priv;
833
834 g_value_init (value, prop_info->type);
835 iface_impl->getter (self, iface_info->dbus_interface,
836 prop_info->name, value, prop_impl->getter_data);
837 return TRUE;
838 }
839 else
840 {
841 return FALSE;
842 }
843 }
844
845
846 static void
tp_dbus_properties_mixin_fill_properties_hash_va(GObject * object,GHashTable * table,const gchar * first_interface,const gchar * first_property,va_list ap)847 tp_dbus_properties_mixin_fill_properties_hash_va (
848 GObject *object,
849 GHashTable *table,
850 const gchar *first_interface,
851 const gchar *first_property,
852 va_list ap)
853 {
854 const gchar *iface, *property;
855 gboolean first = TRUE;
856
857 for (iface = first_interface;
858 iface != NULL;
859 iface = va_arg (ap, gchar *))
860 {
861 GValue *value = g_slice_new0 (GValue);
862 GError *error = NULL;
863
864 if (first)
865 {
866 property = first_property;
867 first = FALSE;
868 }
869 else
870 {
871 property = va_arg (ap, gchar *);
872 }
873
874 /* If property is NULL, the caller might have omitted a comma or
875 * something; in any case, it shouldn't be.
876 */
877 g_assert (property != NULL);
878
879 if (tp_dbus_properties_mixin_get (object, iface, property, value,
880 &error))
881 {
882 g_assert (G_IS_VALUE (value));
883 g_hash_table_insert (table,
884 g_strdup_printf ("%s.%s", iface, property), value);
885 }
886 else
887 {
888 /* This is bad and definitely indicates a programming error. */
889 CRITICAL ("Couldn't fetch '%s' on interface '%s': %s",
890 property, iface, error->message);
891 g_clear_error (&error);
892 }
893
894 }
895 }
896
897 /**
898 * tp_dbus_properties_mixin_fill_properties_hash: (skip)
899 * @object: an object which uses the D-Bus properties mixin
900 * @table: (element-type utf8 GObject.Value): a hash table where the keys are
901 * strings copied with g_strdup() and the values are slice-allocated
902 * #GValue<!-- -->s
903 * @first_interface: the interface of the first property to be retrieved
904 * @first_property: the name of the first property to be retrieved
905 * @...: more (interface name, property name) pairs, terminated by %NULL.
906 *
907 * Retrieves the values of several D-Bus properties from an object, and adds
908 * them to a hash mapping the fully-qualified name of the property to its
909 * value. This is equivalent to calling tp_dbus_properties_mixin_get() for
910 * each property and adding it to the table yourself, with the proviso that
911 * this function will g_assert() if retrieving a property fails (for instance,
912 * because it does not exist).
913 *
914 * Note that in particular, @table does not have the same memory-allocation
915 * model as the hash tables required by tp_asv_set_string() and similar
916 * functions.
917 *
918 * Since: 0.11.11
919 */
920 void
tp_dbus_properties_mixin_fill_properties_hash(GObject * object,GHashTable * table,const gchar * first_interface,const gchar * first_property,...)921 tp_dbus_properties_mixin_fill_properties_hash (
922 GObject *object,
923 GHashTable *table,
924 const gchar *first_interface,
925 const gchar *first_property,
926 ...)
927 {
928 va_list ap;
929
930 va_start (ap, first_property);
931 tp_dbus_properties_mixin_fill_properties_hash_va (object, table,
932 first_interface, first_property, ap);
933 va_end (ap);
934 }
935
936 /**
937 * tp_dbus_properties_mixin_make_properties_hash: (skip)
938 * @object: an object which uses the D-Bus properties mixin
939 * @first_interface: the interface of the first property to be retrieved
940 * @first_property: the name of the first property to be retrieved
941 * @...: more (interface name, property name) pairs, terminated by %NULL.
942 *
943 * Retrieves the values of several D-Bus properties from an object, and builds
944 * a hash mapping the fully-qualified name of the property to its value. This
945 * is equivalent to calling tp_dbus_properties_mixin_get() for each property
946 * and building the table yourself, with the proviso that this function will
947 * g_assert() if retrieving a property fails (for instance, because it does not
948 * exist).
949 *
950 * Additional keys and values can be inserted into the returned hash table;
951 * if this is done, the inserted keys and values will be freed when the
952 * hash table is destroyed. The keys must be allocated with g_strdup() or
953 * equivalent, and the values must be slice-allocated (for instance with
954 * tp_g_value_slice_new_string() or a similar function).
955 *
956 * Note that in particular, tp_asv_set_string() and similar functions should
957 * not be used with this hash table.
958 *
959 * Returns: a hash table mapping (gchar *) fully-qualified property names to
960 * GValues, which must be freed by the caller (at which point its
961 * contents will also be freed).
962 */
963 GHashTable *
tp_dbus_properties_mixin_make_properties_hash(GObject * object,const gchar * first_interface,const gchar * first_property,...)964 tp_dbus_properties_mixin_make_properties_hash (
965 GObject *object,
966 const gchar *first_interface,
967 const gchar *first_property,
968 ...)
969 {
970 GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
971 (GDestroyNotify) tp_g_value_slice_free);
972 va_list ap;
973
974 va_start (ap, first_property);
975 tp_dbus_properties_mixin_fill_properties_hash_va (object, table,
976 first_interface, first_property, ap);
977 va_end (ap);
978
979 return table;
980 }
981
982 /**
983 * tp_dbus_properties_mixin_emit_properties_changed:
984 * @object: an object which uses the D-Bus properties mixin
985 * @interface_name: the interface on which properties have changed
986 * @properties: (allow-none): a %NULL-terminated array of (unqualified)
987 * property names whose values have changed.
988 *
989 * Emits the PropertiesChanged signal for the provided properties. Depending on
990 * the EmitsChangedSignal annotations in the introspection XML, either the new
991 * value of the property will be included in the signal, or merely the fact
992 * that the property has changed.
993 *
994 * For example, the MPRIS specification defines a TrackList interface with two
995 * properties, one of which is annotated with EmitsChangedSignal=true and one
996 * annotated with EmitsChangedSignal=invalidates. The following call would
997 * include the new value of CanEditTracks and list Tracks as invalidated:
998 *
999 * |[
1000 * const gchar *properties[] = { "CanEditTracks", "Tracks", NULL };
1001 *
1002 * tp_dbus_properties_mixin_emit_properties_changed (G_OBJECT (self),
1003 * "org.mpris.MediaPlayer2.TrackList", properties);
1004 * ]|
1005 *
1006 * It is an error to pass a property to this
1007 * function if the property is annotated with EmitsChangedSignal=false, or is
1008 * unannotated.
1009 *
1010 * Since: 0.15.6
1011 */
1012 void
tp_dbus_properties_mixin_emit_properties_changed(GObject * object,const gchar * interface_name,const gchar * const * properties)1013 tp_dbus_properties_mixin_emit_properties_changed (
1014 GObject *object,
1015 const gchar *interface_name,
1016 const gchar * const *properties)
1017 {
1018 TpDBusPropertiesMixinIfaceImpl *iface_impl;
1019 TpDBusPropertiesMixinIfaceInfo *iface_info;
1020 GHashTable *changed_properties;
1021 GPtrArray *invalidated_properties;
1022 const gchar * const *prop_name;
1023
1024 g_return_if_fail (interface_name != NULL);
1025 iface_impl = _tp_dbus_properties_mixin_find_iface_impl (object,
1026 interface_name);
1027 g_return_if_fail (iface_impl != NULL);
1028
1029 iface_info = iface_impl->mixin_priv;
1030
1031 /* If someone passes no property names, well … that's fine, we have nothing
1032 * to do.
1033 */
1034 if (properties == NULL || properties[0] == NULL)
1035 return;
1036
1037 changed_properties = g_hash_table_new_full (g_str_hash, g_str_equal,
1038 NULL, (GDestroyNotify) tp_g_value_slice_free);
1039 invalidated_properties = g_ptr_array_new ();
1040
1041 for (prop_name = properties; *prop_name != NULL; prop_name++)
1042 {
1043 TpDBusPropertiesMixinPropImpl *prop_impl;
1044 TpDBusPropertiesMixinPropInfo *prop_info;
1045 GError *error = NULL;
1046
1047 prop_impl = _iface_impl_get_property_impl (object, iface_impl,
1048 interface_name, *prop_name, &error);
1049
1050 if (prop_impl == NULL)
1051 {
1052 WARNING ("Couldn't get value for '%s.%s': %s", interface_name,
1053 *prop_name, error->message);
1054 g_clear_error (&error);
1055 g_return_if_reached ();
1056 }
1057
1058 prop_info = prop_impl->mixin_priv;
1059
1060 if (prop_info->flags & TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED)
1061 {
1062 GValue v = { 0, };
1063
1064 g_value_init (&v, prop_info->type);
1065 iface_impl->getter (object, iface_info->dbus_interface,
1066 prop_info->name, &v, prop_impl->getter_data);
1067 g_hash_table_insert (changed_properties, (gchar *) *prop_name,
1068 tp_g_value_slice_dup (&v));
1069
1070 g_value_unset (&v);
1071 }
1072 else if (prop_info->flags &
1073 TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED)
1074 {
1075 g_ptr_array_add (invalidated_properties, (gchar *) *prop_name);
1076 }
1077 else
1078 {
1079 WARNING ("'%s.%s' is not annotated with EmitsChangedSignal'",
1080 interface_name, *prop_name);
1081 }
1082 }
1083
1084 g_ptr_array_add (invalidated_properties, NULL);
1085
1086 tp_svc_dbus_properties_emit_properties_changed (object, interface_name,
1087 changed_properties, (const gchar **) invalidated_properties->pdata);
1088 g_hash_table_unref (changed_properties);
1089 g_ptr_array_unref (invalidated_properties);
1090 }
1091
1092 /**
1093 * tp_dbus_properties_mixin_emit_properties_changed_varargs: (skip)
1094 * @object: an object which uses the D-Bus properties mixin
1095 * @interface_name: the interface on which properties have changed
1096 * @...: property names (unqualified) whose values have changed, terminated by
1097 * %NULL.
1098 *
1099 * A shortcut for calling tp_dbus_properties_mixin_emit_properties_changed().
1100 *
1101 * Since: 0.15.6
1102 */
1103 void
tp_dbus_properties_mixin_emit_properties_changed_varargs(GObject * object,const gchar * interface_name,...)1104 tp_dbus_properties_mixin_emit_properties_changed_varargs (
1105 GObject *object,
1106 const gchar *interface_name,
1107 ...)
1108 {
1109 GPtrArray *property_names = g_ptr_array_new ();
1110 char *property_name;
1111 va_list ap;
1112
1113 va_start (ap, interface_name);
1114 do
1115 {
1116 property_name = va_arg (ap, char *);
1117 g_ptr_array_add (property_names, property_name);
1118 }
1119 while (property_name != NULL);
1120 va_end (ap);
1121
1122 tp_dbus_properties_mixin_emit_properties_changed (object, interface_name,
1123 (const gchar * const *) property_names->pdata);
1124 g_ptr_array_unref (property_names);
1125 }
1126
1127 static void
_tp_dbus_properties_mixin_get(TpSvcDBusProperties * iface,const gchar * interface_name,const gchar * property_name,DBusGMethodInvocation * context)1128 _tp_dbus_properties_mixin_get (TpSvcDBusProperties *iface,
1129 const gchar *interface_name,
1130 const gchar *property_name,
1131 DBusGMethodInvocation *context)
1132 {
1133 GObject *self = G_OBJECT (iface);
1134 GValue value = { 0 };
1135 GError *error = NULL;
1136
1137 if (tp_dbus_properties_mixin_get (self, interface_name, property_name,
1138 &value, &error))
1139 {
1140 tp_svc_dbus_properties_return_from_get (context, &value);
1141 g_value_unset (&value);
1142 }
1143 else
1144 {
1145 dbus_g_method_return_error (context, error);
1146 g_error_free (error);
1147 }
1148 }
1149
1150 /**
1151 * tp_dbus_properties_mixin_dup_all:
1152 * @self: an object with this mixin
1153 * @interface_name: a D-Bus interface name
1154 *
1155 * Get all the properties of a particular interface. This implementation
1156 * never returns an error: it will return an empty map if the interface
1157 * is unknown.
1158 *
1159 * Returns: (transfer container) (element-type utf8 GObject.Value): a map
1160 * from property name (without the interface name) to value
1161 * Since: 0.21.2
1162 */
1163 GHashTable *
tp_dbus_properties_mixin_dup_all(GObject * self,const gchar * interface_name)1164 tp_dbus_properties_mixin_dup_all (GObject *self,
1165 const gchar *interface_name)
1166 {
1167 TpDBusPropertiesMixinIfaceImpl *iface_impl;
1168 TpDBusPropertiesMixinIfaceInfo *iface_info;
1169 TpDBusPropertiesMixinPropImpl *prop_impl;
1170 /* no key destructor needed - the keys are immortal */
1171 GHashTable *values = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
1172 (GDestroyNotify) tp_g_value_slice_free);
1173
1174 iface_impl = _tp_dbus_properties_mixin_find_iface_impl (self,
1175 interface_name);
1176
1177 if (iface_impl == NULL || iface_impl->getter == NULL)
1178 return values;
1179
1180 iface_info = iface_impl->mixin_priv;
1181
1182 for (prop_impl = iface_impl->props;
1183 prop_impl->name != NULL;
1184 prop_impl++)
1185 {
1186 TpDBusPropertiesMixinPropInfo *prop_info = prop_impl->mixin_priv;
1187 GValue *value;
1188
1189 if ((prop_info->flags & TP_DBUS_PROPERTIES_MIXIN_FLAG_READ) == 0)
1190 continue;
1191
1192 value = tp_g_value_slice_new (prop_info->type);
1193 iface_impl->getter (self, iface_info->dbus_interface,
1194 prop_info->name, value, prop_impl->getter_data);
1195 g_hash_table_insert (values, (gchar *) prop_impl->name, value);
1196 }
1197
1198 return values;
1199 }
1200
1201 static void
_tp_dbus_properties_mixin_get_all_dbus(TpSvcDBusProperties * iface,const gchar * interface_name,DBusGMethodInvocation * context)1202 _tp_dbus_properties_mixin_get_all_dbus (TpSvcDBusProperties *iface,
1203 const gchar *interface_name,
1204 DBusGMethodInvocation *context)
1205 {
1206 GHashTable *values = tp_dbus_properties_mixin_dup_all (G_OBJECT (iface),
1207 interface_name);
1208
1209 tp_svc_dbus_properties_return_from_get_all (context, values);
1210 g_hash_table_unref (values);
1211 }
1212
1213 /**
1214 * tp_dbus_properties_mixin_set:
1215 * @self: an object with this mixin
1216 * @interface_name: a D-Bus interface name
1217 * @property_name: a D-Bus property name
1218 * @value: a GValue containing the new value for this property.
1219 * @error: used to return an error on failure
1220 *
1221 * Sets a property to the value specified by @value, as if by
1222 * calling the D-Bus method org.freedesktop.DBus.Properties.Set.
1223 *
1224 * If Set would return a D-Bus error, sets @error and returns %FALSE
1225 *
1226 * Returns: %TRUE on success; %FALSE (setting @error) on failure
1227 * Since: 0.15.8
1228 */
1229 gboolean
tp_dbus_properties_mixin_set(GObject * self,const gchar * interface_name,const gchar * property_name,const GValue * value,GError ** error)1230 tp_dbus_properties_mixin_set (
1231 GObject *self,
1232 const gchar *interface_name,
1233 const gchar *property_name,
1234 const GValue *value,
1235 GError **error)
1236 {
1237 TpDBusPropertiesMixinIfaceImpl *iface_impl;
1238 TpDBusPropertiesMixinIfaceInfo *iface_info;
1239 TpDBusPropertiesMixinPropImpl *prop_impl;
1240 TpDBusPropertiesMixinPropInfo *prop_info;
1241 GValue copy = { 0 };
1242 gboolean ret;
1243
1244 g_return_val_if_fail (G_IS_OBJECT (self), FALSE);
1245 g_return_val_if_fail (interface_name != NULL, FALSE);
1246 g_return_val_if_fail (property_name != NULL, FALSE);
1247 g_return_val_if_fail (G_IS_VALUE (value), FALSE);
1248
1249 iface_impl = _tp_dbus_properties_mixin_find_iface_impl (self,
1250 interface_name);
1251
1252 if (iface_impl == NULL)
1253 {
1254 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
1255 "No properties known for interface '%s'", interface_name);
1256 return FALSE;
1257 }
1258
1259 iface_info = iface_impl->mixin_priv;
1260
1261 prop_impl = _tp_dbus_properties_mixin_find_prop_impl (iface_impl,
1262 property_name);
1263
1264 if (prop_impl == NULL)
1265 {
1266 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
1267 "Unknown property '%s' on interface '%s'", property_name,
1268 interface_name);
1269 return FALSE;
1270 }
1271
1272 prop_info = prop_impl->mixin_priv;
1273
1274 if ((prop_info->flags & TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE) == 0)
1275 {
1276 g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED,
1277 "'%s.%s' is read-only", interface_name, property_name);
1278 return FALSE;
1279 }
1280
1281 if (iface_impl->setter == NULL)
1282 {
1283 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
1284 "Setting properties on '%s' is unimplemented", interface_name);
1285 return FALSE;
1286 }
1287
1288 if (G_VALUE_TYPE (value) != prop_info->type)
1289 {
1290 g_value_init (©, prop_info->type);
1291
1292 if (!g_value_transform (value, ©))
1293 {
1294 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1295 "Cannot convert %s to %s for property %s",
1296 g_type_name (G_VALUE_TYPE (value)),
1297 g_type_name (prop_info->type),
1298 property_name);
1299 ret = FALSE;
1300 goto out;
1301 }
1302
1303 /* use copy instead of value from now on */
1304 value = ©
1305 }
1306
1307 ret = iface_impl->setter (self, iface_info->dbus_interface,
1308 prop_info->name, value, prop_impl->setter_data, error);
1309
1310 out:
1311 if (G_IS_VALUE (©))
1312 g_value_unset (©);
1313
1314 return ret;
1315 }
1316
1317 static void
_tp_dbus_properties_mixin_set(TpSvcDBusProperties * iface,const gchar * interface_name,const gchar * property_name,const GValue * value,DBusGMethodInvocation * context)1318 _tp_dbus_properties_mixin_set (TpSvcDBusProperties *iface,
1319 const gchar *interface_name,
1320 const gchar *property_name,
1321 const GValue *value,
1322 DBusGMethodInvocation *context)
1323 {
1324 GObject *self = G_OBJECT (iface);
1325 GError *error = NULL;
1326
1327 if (tp_dbus_properties_mixin_set (self, interface_name, property_name, value,
1328 &error))
1329 {
1330 tp_svc_dbus_properties_return_from_set (context);
1331 }
1332 else
1333 {
1334 dbus_g_method_return_error (context, error);
1335 g_error_free (error);
1336 }
1337 }
1338
1339 /**
1340 * tp_dbus_properties_mixin_iface_init:
1341 * @g_iface: a pointer to a #TpSvcDBusPropertiesClass structure
1342 * @iface_data: ignored
1343 *
1344 * Declare that the DBus.Properties interface represented by @g_iface
1345 * is implemented using this mixin.
1346 */
1347 void
tp_dbus_properties_mixin_iface_init(gpointer g_iface,gpointer iface_data)1348 tp_dbus_properties_mixin_iface_init (gpointer g_iface,
1349 gpointer iface_data)
1350 {
1351 TpSvcDBusPropertiesClass *cls = g_iface;
1352
1353 #define IMPLEMENT(x, suffix) \
1354 tp_svc_dbus_properties_implement_##x (cls, \
1355 _tp_dbus_properties_mixin_##x##suffix)
1356 IMPLEMENT (get,);
1357 IMPLEMENT (get_all,_dbus);
1358 IMPLEMENT (set,);
1359 #undef IMPLEMENT
1360 }
1361