1 /* vi: set et sw=4 ts=8 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
3 /*
4  * This file is part of mission-control
5  *
6  * Copyright (C) 2008-2009 Nokia Corporation.
7  * Copyright (C) 2009 Collabora Ltd.
8  *
9  * Contact: Alberto Mardegan  <alberto.mardegan@nokia.com>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * version 2.1 as published by the Free Software Foundation.
14  *
15  * This library is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  *
25  */
26 
27 #include "config.h"
28 
29 #include <string.h>
30 #include <telepathy-glib/telepathy-glib.h>
31 
32 #include "mcd-dbusprop.h"
33 #include "mcd-debug.h"
34 
35 #define MCD_INTERFACES_QUARK get_interfaces_quark()
36 
37 static GQuark
get_interfaces_quark(void)38 get_interfaces_quark (void)
39 {
40     static GQuark interfaces_quark = 0;
41 
42     if (G_UNLIKELY (!interfaces_quark))
43 	interfaces_quark = g_quark_from_static_string ("interfaces");
44     return interfaces_quark;
45 }
46 
47 #define MCD_ACTIVE_OPTIONAL_INTERFACES_QUARK \
48     get_active_optional_interfaces_quark()
49 
50 static GQuark
get_active_optional_interfaces_quark(void)51 get_active_optional_interfaces_quark (void)
52 {
53     static GQuark active_optional_interfaces_quark = 0;
54 
55     if (G_UNLIKELY (active_optional_interfaces_quark == 0))
56         active_optional_interfaces_quark =
57             g_quark_from_static_string ("active-optional-interfaces");
58 
59     return active_optional_interfaces_quark;
60 }
61 
62 /**
63  * get_active_optional_interfaces:
64  *
65  * DBus Interfaces marked as optional will only be included in the object's
66  * Interfaces property if they appear in this set.
67  *
68  * Returns: a #TpIntset of the active optional interfaces.
69  */
70 static TpIntset *
get_active_optional_interfaces(TpSvcDBusProperties * object)71 get_active_optional_interfaces (TpSvcDBusProperties *object)
72 {
73     TpIntset *aoi = g_object_get_qdata (G_OBJECT (object),
74                                         MCD_ACTIVE_OPTIONAL_INTERFACES_QUARK);
75 
76     if (G_UNLIKELY (aoi == NULL))
77     {
78         aoi = tp_intset_new ();
79 
80         g_object_set_qdata_full (G_OBJECT (object),
81                                  MCD_ACTIVE_OPTIONAL_INTERFACES_QUARK,
82                                  aoi,
83                                  (GDestroyNotify) tp_intset_destroy);
84     }
85 
86     return aoi;
87 }
88 
89 void
mcd_dbus_activate_optional_interface(TpSvcDBusProperties * object,GType interface)90 mcd_dbus_activate_optional_interface (TpSvcDBusProperties *object,
91                                       GType interface)
92 {
93     tp_intset_add (get_active_optional_interfaces (object), interface);
94 }
95 
96 gboolean
mcd_dbus_is_active_optional_interface(TpSvcDBusProperties * object,GType interface)97 mcd_dbus_is_active_optional_interface (TpSvcDBusProperties *object,
98                                        GType interface)
99 {
100     return tp_intset_is_member (get_active_optional_interfaces (object),
101                                 interface);
102 }
103 
104 static const McdDBusProp *
get_interface_properties(TpSvcDBusProperties * object,const gchar * interface)105 get_interface_properties (TpSvcDBusProperties *object, const gchar *interface)
106 {
107     McdInterfaceData *iface_data;
108     GType type;
109 
110     /* we must look up the ancestors, in case the object implementing the
111      * interface has been subclassed. */
112     for (type = G_OBJECT_TYPE (object); type != 0; type = g_type_parent (type))
113     {
114 	iface_data = g_type_get_qdata (type, MCD_INTERFACES_QUARK);
115 	if (!iface_data) continue;
116 
117 	while (iface_data->get_type)
118 	{
119 	    if (iface_data->interface &&
120 		strcmp (iface_data->interface, interface) == 0)
121 		return iface_data->properties;
122 	    iface_data++;
123 	}
124     }
125     return NULL;
126 }
127 
128 static const McdDBusProp *
get_mcddbusprop(TpSvcDBusProperties * self,const gchar * interface_name,const gchar * property_name,GError ** error)129 get_mcddbusprop (TpSvcDBusProperties *self,
130                  const gchar *interface_name,
131                  const gchar *property_name,
132                  GError **error)
133 {
134     const McdDBusProp *prop_array, *property;
135 
136     DEBUG ("%s, %s", interface_name, property_name);
137 
138     prop_array = get_interface_properties (self, interface_name);
139     if (!prop_array)
140     {
141         g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
142                      "invalid interface: %s", interface_name);
143         return NULL;
144     }
145 
146     /* look for our property */
147     for (property = prop_array; property->name != NULL; property++)
148         if (strcmp (property->name, property_name) == 0)
149             break;
150 
151     if (!property->name)
152     {
153         g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
154                      "invalid property: %s", property_name);
155         return NULL;
156     }
157 
158     return property;
159 }
160 
161 gboolean
mcd_dbusprop_set_property(TpSvcDBusProperties * self,const gchar * interface_name,const gchar * property_name,const GValue * value,GError ** error)162 mcd_dbusprop_set_property (TpSvcDBusProperties *self,
163                            const gchar *interface_name,
164                            const gchar *property_name,
165                            const GValue *value,
166                            GError **error)
167 {
168     const McdDBusProp *property;
169 
170     property = get_mcddbusprop (self, interface_name, property_name, error);
171 
172     if (property == NULL)
173       return FALSE;
174 
175     if (!property->setprop)
176     {
177         g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
178                      "property %s cannot be written", property_name);
179         return FALSE;
180     }
181 
182     /* we pass property->name, because we know it's a static value and there
183      * will be no need to care about its lifetime */
184     return property->setprop (self, property->name, value,
185                               MCD_DBUS_PROP_SET_FLAG_NONE, error);
186 }
187 
188 void
dbusprop_set(TpSvcDBusProperties * self,const gchar * interface_name,const gchar * property_name,const GValue * value,DBusGMethodInvocation * context)189 dbusprop_set (TpSvcDBusProperties *self,
190               const gchar *interface_name,
191               const gchar *property_name,
192               const GValue *value,
193               DBusGMethodInvocation *context)
194 {
195     GError *error = NULL;
196 
197     mcd_dbusprop_set_property (self, interface_name, property_name,
198 			       value, &error);
199     if (error)
200     {
201 	dbus_g_method_return_error (context, error);
202 	g_error_free (error);
203 	return;
204     }
205 
206     tp_svc_dbus_properties_return_from_set (context);
207 }
208 
209 gboolean
mcd_dbusprop_get_property(TpSvcDBusProperties * self,const gchar * interface_name,const gchar * property_name,GValue * value,GError ** error)210 mcd_dbusprop_get_property (TpSvcDBusProperties *self,
211                            const gchar *interface_name,
212                            const gchar *property_name,
213                            GValue *value,
214                            GError **error)
215 {
216     const McdDBusProp *property;
217 
218     property = get_mcddbusprop (self, interface_name, property_name, error);
219 
220     if (property == NULL)
221       return FALSE;
222 
223     if (!property->getprop)
224     {
225         g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
226                      "property %s cannot be read", property_name);
227         return FALSE;
228     }
229 
230     property->getprop (self, property_name, value);
231     return TRUE;
232 }
233 
234 void
dbusprop_get(TpSvcDBusProperties * self,const gchar * interface_name,const gchar * property_name,DBusGMethodInvocation * context)235 dbusprop_get (TpSvcDBusProperties *self,
236               const gchar *interface_name,
237               const gchar *property_name,
238               DBusGMethodInvocation *context)
239 {
240     GValue value = G_VALUE_INIT;
241     GError *error = NULL;
242 
243     DEBUG ("%s, %s", interface_name, property_name);
244 
245     mcd_dbusprop_get_property (self, interface_name, property_name,
246                                &value, &error);
247 
248     if (error)
249     {
250         dbus_g_method_return_error (context, error);
251         g_error_free (error);
252         return;
253     }
254 
255     tp_svc_dbus_properties_return_from_get (context, &value);
256     g_value_unset (&value);
257 }
258 
259 typedef struct
260 {
261     TpSvcDBusProperties *self;
262     DBusGMethodInvocation *context;
263     GHashTable *properties;
264     const McdDBusProp *property;
265 } GetAllData;
266 
267 static void
get_all_iter(GetAllData * data)268 get_all_iter (GetAllData *data)
269 {
270     if (data->property->name != NULL)
271     {
272         if (data->property->getprop)
273         {
274             GValue *out;
275 
276             out = g_malloc0 (sizeof (GValue));
277             data->property->getprop (data->self, data->property->name, out);
278             g_hash_table_insert (data->properties, (gchar *) data->property->name,
279                 tp_g_value_slice_dup (out));
280             g_value_unset (out);
281             g_free (out);
282 
283             data->property++;
284             get_all_iter (data);
285         }
286         else
287         {
288             data->property++;
289             get_all_iter (data);
290         }
291     }
292     else
293     {
294       tp_svc_dbus_properties_return_from_get_all (data->context,
295                                                   data->properties);
296       g_hash_table_unref (data->properties);
297       g_slice_free (GetAllData, data);
298 
299     }
300 }
301 
302 typedef struct
303 {
304     TpSvcDBusProperties *tp_svc_props;
305     gchar *interface;
306     gchar *property;
307 } DBusPropAsyncData;
308 
309 void
dbusprop_get_all(TpSvcDBusProperties * self,const gchar * interface_name,DBusGMethodInvocation * context)310 dbusprop_get_all (TpSvcDBusProperties *self,
311                   const gchar *interface_name,
312                   DBusGMethodInvocation *context)
313 {
314     const McdDBusProp *prop_array;
315     GError *error = NULL;
316     GetAllData *data;
317 
318     DEBUG ("%s", interface_name);
319 
320     prop_array = get_interface_properties (self, interface_name);
321     if (!prop_array)
322     {
323         g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
324                      "invalid interface: %s", interface_name);
325         dbus_g_method_return_error (context, error);
326         g_error_free (error);
327         return;
328     }
329 
330     data = g_slice_new0 (GetAllData);
331     data->self = self;
332     data->context = context;
333 
334     data->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
335                                               NULL, (GDestroyNotify) tp_g_value_slice_free);
336 
337     data->property = prop_array;
338 
339     get_all_iter (data);
340 }
341 
342 void
mcd_dbus_init_interfaces(GType g_define_type_id,const McdInterfaceData * iface_data)343 mcd_dbus_init_interfaces (GType g_define_type_id,
344 			  const McdInterfaceData *iface_data)
345 {
346     g_type_set_qdata (g_define_type_id, MCD_INTERFACES_QUARK,
347 		      (gpointer)iface_data);
348 
349     while (iface_data->get_type)
350     {
351 	GType type;
352 
353 	type = iface_data->get_type();
354 	G_IMPLEMENT_INTERFACE (type, iface_data->iface_init);
355 	iface_data++;
356     }
357 }
358 
359 void
mcd_dbus_init_interfaces_instances(gpointer self)360 mcd_dbus_init_interfaces_instances (gpointer self)
361 {
362     McdInterfaceData *iface_data;
363 
364     iface_data = g_type_get_qdata (G_OBJECT_TYPE (self), MCD_INTERFACES_QUARK);
365 
366     while (iface_data->get_type)
367     {
368 	if (iface_data->instance_init)
369 	    iface_data->instance_init (self);
370 	iface_data++;
371     }
372 }
373 
374 void
mcd_dbus_get_interfaces(TpSvcDBusProperties * self,const gchar * name,GValue * value)375 mcd_dbus_get_interfaces (TpSvcDBusProperties *self, const gchar *name,
376 			 GValue *value)
377 {
378     McdInterfaceData *iface_data, *id;
379     GPtrArray *a_ifaces;
380     GType type;
381 
382     DEBUG ("called");
383 
384     a_ifaces = g_ptr_array_new ();
385 
386     for (type = G_OBJECT_TYPE (self); type != 0; type = g_type_parent (type))
387     {
388 	iface_data = g_type_get_qdata (type, MCD_INTERFACES_QUARK);
389 	if (!iface_data) continue;
390 
391 	for (id = iface_data; id->get_type; id++)
392         {
393             if (id->optional &&
394                 !mcd_dbus_is_active_optional_interface (self,
395                                                         id->get_type ()))
396             {
397                 DEBUG ("skipping inactive optional iface %s", id->interface);
398                 continue;
399             }
400 
401 	    g_ptr_array_add (a_ifaces, g_strdup (id->interface));
402         }
403     }
404     g_ptr_array_add (a_ifaces, NULL);
405 
406     g_value_init (value, G_TYPE_STRV);
407     g_value_take_boxed (value, g_ptr_array_free (a_ifaces, FALSE));
408 }
409 
410