eldbus: Check if property_get_all() returned a error before read properties
[platform/upstream/eldbus.git] / src / lib / eldbus_proxy.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include "eldbus_private.h"
6 #include "eldbus_private_types.h"
7
8 /* TODO: mempool of Eldbus_Proxy, Eldbus_Proxy_Context_Event_Cb and
9  * Eldbus_Proxy_Context_Event
10  */
11
12 typedef struct _Eldbus_Proxy_Context_Event_Cb
13 {
14    EINA_INLIST;
15    Eldbus_Proxy_Event_Cb cb;
16    const void          *cb_data;
17    Eina_Bool            deleted : 1;
18 } Eldbus_Proxy_Context_Event_Cb;
19
20 typedef struct _Eldbus_Proxy_Context_Event
21 {
22    Eina_Inlist *list;
23    int          walking;
24    Eina_List   *to_delete;
25 } Eldbus_Proxy_Context_Event;
26
27 struct _Eldbus_Proxy
28 {
29    EINA_MAGIC;
30    int                       refcount;
31    Eldbus_Object             *obj;
32    const char               *interface;
33    Eina_Inlist              *pendings;
34    Eina_List                *handlers;
35    Eina_Inlist              *cbs_free;
36    Eina_Inlist              *data;
37    Eldbus_Proxy_Context_Event event_handlers[ELDBUS_PROXY_EVENT_LAST];
38    Eina_Hash *props;
39    Eldbus_Signal_Handler *properties_changed;
40    Eina_Bool monitor_enabled:1;
41 };
42
43 #define ELDBUS_PROXY_CHECK(proxy)                         \
44   do                                                     \
45     {                                                    \
46        EINA_SAFETY_ON_NULL_RETURN(proxy);                \
47        if (!EINA_MAGIC_CHECK(proxy, ELDBUS_PROXY_MAGIC))  \
48          {                                               \
49             EINA_MAGIC_FAIL(proxy, ELDBUS_PROXY_MAGIC);   \
50             return;                                      \
51          }                                               \
52        EINA_SAFETY_ON_TRUE_RETURN(proxy->refcount <= 0); \
53     }                                                    \
54   while (0)
55
56 #define ELDBUS_PROXY_CHECK_RETVAL(proxy, retval)                      \
57   do                                                                 \
58     {                                                                \
59        EINA_SAFETY_ON_NULL_RETURN_VAL(proxy, retval);                \
60        if (!EINA_MAGIC_CHECK(proxy, ELDBUS_PROXY_MAGIC))              \
61          {                                                           \
62             EINA_MAGIC_FAIL(proxy, ELDBUS_PROXY_MAGIC);               \
63             return retval;                                           \
64          }                                                           \
65        EINA_SAFETY_ON_TRUE_RETURN_VAL(proxy->refcount <= 0, retval); \
66     }                                                                \
67   while (0)
68
69 #define ELDBUS_PROXY_CHECK_GOTO(proxy, label)                  \
70   do                                                          \
71     {                                                         \
72        EINA_SAFETY_ON_NULL_GOTO(proxy, label);                \
73        if (!EINA_MAGIC_CHECK(proxy, ELDBUS_PROXY_MAGIC))       \
74          {                                                    \
75             EINA_MAGIC_FAIL(proxy, ELDBUS_PROXY_MAGIC);        \
76             goto label;                                       \
77          }                                                    \
78        EINA_SAFETY_ON_TRUE_GOTO(proxy->refcount <= 0, label); \
79     }                                                         \
80   while (0)
81
82 Eina_Bool
83 eldbus_proxy_init(void)
84 {
85    return EINA_TRUE;
86 }
87
88 void
89 eldbus_proxy_shutdown(void)
90 {
91 }
92
93 static void _eldbus_proxy_event_callback_call(Eldbus_Proxy *proxy, Eldbus_Proxy_Event_Type type, const void *event_info);
94 static void _eldbus_proxy_context_event_cb_del(Eldbus_Proxy_Context_Event *ce, Eldbus_Proxy_Context_Event_Cb *ctx);
95 static void _on_signal_handler_free(void *data, const void *dead_pointer);
96
97 static void
98 _eldbus_proxy_call_del(Eldbus_Proxy *proxy)
99 {
100    Eldbus_Proxy_Context_Event *ce;
101
102    _eldbus_proxy_event_callback_call(proxy, ELDBUS_PROXY_EVENT_DEL, NULL);
103
104    /* clear all del callbacks so we don't call them twice at
105     * _eldbus_proxy_clear()
106     */
107    ce = proxy->event_handlers + ELDBUS_PROXY_EVENT_DEL;
108    while (ce->list)
109      {
110         Eldbus_Proxy_Context_Event_Cb *ctx;
111
112         ctx = EINA_INLIST_CONTAINER_GET(ce->list,
113                                         Eldbus_Proxy_Context_Event_Cb);
114         _eldbus_proxy_context_event_cb_del(ce, ctx);
115      }
116 }
117
118 static void
119 _eldbus_proxy_clear(Eldbus_Proxy *proxy)
120 {
121    Eldbus_Signal_Handler *h;
122    Eldbus_Pending *p;
123    Eina_List *iter, *iter_next;
124    Eina_Inlist *in_l;
125    DBG("proxy=%p, refcount=%d, interface=%s, obj=%p",
126        proxy, proxy->refcount, proxy->interface, proxy->obj);
127    proxy->refcount = 1;
128    eldbus_object_proxy_del(proxy->obj, proxy, proxy->interface);
129    _eldbus_proxy_call_del(proxy);
130
131    EINA_LIST_FOREACH_SAFE(proxy->handlers, iter, iter_next, h)
132      {
133         DBG("proxy=%p delete owned signal handler %p %s",
134             proxy, h, eldbus_signal_handler_match_get(h));
135         eldbus_signal_handler_del(h);
136      }
137
138    EINA_INLIST_FOREACH_SAFE(proxy->pendings, in_l, p)
139      {
140         DBG("proxy=%p delete owned pending call=%p dest=%s path=%s %s.%s()",
141             proxy, p,
142             eldbus_pending_destination_get(p),
143             eldbus_pending_path_get(p),
144             eldbus_pending_interface_get(p),
145             eldbus_pending_method_get(p));
146         eldbus_pending_cancel(p);
147      }
148
149    eldbus_cbs_free_dispatch(&(proxy->cbs_free), proxy);
150    if (proxy->props)
151      eina_hash_free(proxy->props);
152    proxy->refcount = 0;
153 }
154
155 static void
156 _eldbus_proxy_free(Eldbus_Proxy *proxy)
157 {
158    unsigned int i;
159    Eldbus_Signal_Handler *h;
160
161    EINA_LIST_FREE(proxy->handlers, h)
162      {
163         if (h->dangling)
164           eldbus_signal_handler_free_cb_del(h, _on_signal_handler_free, proxy);
165         else
166            ERR("proxy=%p alive handler=%p %s", proxy, h,
167                eldbus_signal_handler_match_get(h));
168      }
169
170    if (proxy->pendings)
171      CRITICAL("Proxy %p released with live pending calls!", proxy);
172
173    for (i = 0; i < ELDBUS_PROXY_EVENT_LAST; i++)
174      {
175         Eldbus_Proxy_Context_Event *ce = proxy->event_handlers + i;
176         while (ce->list)
177           {
178              Eldbus_Proxy_Context_Event_Cb *ctx;
179              ctx = EINA_INLIST_CONTAINER_GET(ce->list,
180                                              Eldbus_Proxy_Context_Event_Cb);
181              _eldbus_proxy_context_event_cb_del(ce, ctx);
182           }
183         eina_list_free(ce->to_delete);
184      }
185
186    eina_stringshare_del(proxy->interface);
187    EINA_MAGIC_SET(proxy, EINA_MAGIC_NONE);
188    free(proxy);
189 }
190
191 static void
192 _on_object_free(void *data, const void *dead_pointer EINA_UNUSED)
193 {
194    Eldbus_Proxy *proxy = data;
195    ELDBUS_PROXY_CHECK(proxy);
196    DBG("proxy=%p, refcount=%d, interface=%s, obj=%p",
197        proxy, proxy->refcount, proxy->interface, proxy->obj);
198    eldbus_data_del_all(&(proxy->data));
199    _eldbus_proxy_clear(proxy);
200    _eldbus_proxy_free(proxy);
201 }
202
203 EAPI Eldbus_Proxy *
204 eldbus_proxy_get(Eldbus_Object *obj, const char *interface)
205 {
206    Eldbus_Proxy *proxy;
207
208    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
209    EINA_SAFETY_ON_NULL_RETURN_VAL(interface, NULL);
210
211    proxy = eldbus_object_proxy_get(obj, interface);
212    if (proxy)
213      return eldbus_proxy_ref(proxy);
214
215    proxy = calloc(1, sizeof(Eldbus_Proxy));
216    EINA_SAFETY_ON_NULL_RETURN_VAL(proxy, NULL);
217
218    proxy->refcount = 1;
219    proxy->obj = obj;
220    proxy->interface = eina_stringshare_add(interface);
221    EINA_MAGIC_SET(proxy, ELDBUS_PROXY_MAGIC);
222    if (!eldbus_object_proxy_add(obj, proxy))
223      goto cleanup;
224    eldbus_object_free_cb_add(obj, _on_object_free, proxy);
225
226    return proxy;
227
228 cleanup:
229    eina_stringshare_del(proxy->interface);
230    free(proxy);
231    return NULL;
232 }
233
234 static void _on_signal_handler_free(void *data, const void *dead_pointer);
235
236 static void
237 _eldbus_proxy_unref(Eldbus_Proxy *proxy)
238 {
239    proxy->refcount--;
240    if (proxy->refcount > 0) return;
241
242    eldbus_object_free_cb_del(proxy->obj, _on_object_free, proxy);
243    eldbus_data_del_all(&(proxy->data));
244    _eldbus_proxy_clear(proxy);
245    _eldbus_proxy_free(proxy);
246 }
247
248 EAPI Eldbus_Proxy *
249 eldbus_proxy_ref(Eldbus_Proxy *proxy)
250 {
251    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
252    DBG("proxy=%p, pre-refcount=%d, interface=%s, obj=%p",
253        proxy, proxy->refcount, proxy->interface, proxy->obj);
254    proxy->refcount++;
255    return proxy;
256 }
257
258 EAPI void
259 eldbus_proxy_unref(Eldbus_Proxy *proxy)
260 {
261    ELDBUS_PROXY_CHECK(proxy);
262    DBG("proxy=%p, pre-refcount=%d, interface=%s, obj=%p",
263        proxy, proxy->refcount, proxy->interface, proxy->obj);
264    _eldbus_proxy_unref(proxy);
265 }
266
267 EAPI void
268 eldbus_proxy_free_cb_add(Eldbus_Proxy *proxy, Eldbus_Free_Cb cb, const void *data)
269 {
270    ELDBUS_PROXY_CHECK(proxy);
271    EINA_SAFETY_ON_NULL_RETURN(cb);
272    proxy->cbs_free = eldbus_cbs_free_add(proxy->cbs_free, cb, data);
273 }
274
275 EAPI void
276 eldbus_proxy_free_cb_del(Eldbus_Proxy *proxy, Eldbus_Free_Cb cb, const void *data)
277 {
278    ELDBUS_PROXY_CHECK(proxy);
279    EINA_SAFETY_ON_NULL_RETURN(cb);
280    proxy->cbs_free = eldbus_cbs_free_del(proxy->cbs_free, cb, data);
281 }
282
283 EAPI void
284 eldbus_proxy_data_set(Eldbus_Proxy *proxy, const char *key, const void *data)
285 {
286    ELDBUS_PROXY_CHECK(proxy);
287    EINA_SAFETY_ON_NULL_RETURN(key);
288    EINA_SAFETY_ON_NULL_RETURN(data);
289    eldbus_data_set(&(proxy->data), key, data);
290 }
291
292 EAPI void *
293 eldbus_proxy_data_get(const Eldbus_Proxy *proxy, const char *key)
294 {
295    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
296    EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
297    return eldbus_data_get(&(((Eldbus_Proxy *)proxy)->data), key);
298 }
299
300 EAPI void *
301 eldbus_proxy_data_del(Eldbus_Proxy *proxy, const char *key)
302 {
303    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
304    EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
305    return eldbus_data_del(&(((Eldbus_Proxy *)proxy)->data), key);
306 }
307
308 static void
309 _property_changed_iter(void *data, const void *key, Eldbus_Message_Iter *var)
310 {
311    Eldbus_Proxy *proxy = data;
312    const char *skey = key;
313    Eina_Value *st_value, stack_value, *value;
314    Eldbus_Proxy_Event_Property_Changed event;
315
316    st_value = _message_iter_struct_to_eina_value(var);
317    eina_value_struct_value_get(st_value, "arg0", &stack_value);
318
319    value = eina_hash_find(proxy->props, skey);
320    if (value)
321      {
322         eina_value_flush(value);
323         eina_value_copy(&stack_value, value);
324      }
325    else
326      {
327         value = calloc(1, sizeof(Eina_Value));
328         eina_value_copy(&stack_value, value);
329         eina_hash_add(proxy->props, skey, value);
330      }
331
332    event.name = skey;
333    event.value = value;
334    event.proxy = proxy;
335    _eldbus_proxy_event_callback_call(proxy, ELDBUS_PROXY_EVENT_PROPERTY_CHANGED,
336                                     &event);
337    eina_value_free(st_value);
338    eina_value_flush(&stack_value);
339 }
340
341 static void
342 _properties_changed(void *data, const Eldbus_Message *msg)
343 {
344    Eldbus_Proxy *proxy = data;
345    Eldbus_Message_Iter *array, *invalidate;
346    const char *iface;
347    const char *invalidate_prop;
348
349    if (!eldbus_message_arguments_get(msg, "sa{sv}as", &iface, &array, &invalidate))
350      {
351         ERR("Error getting data from properties changed signal.");
352         return;
353      }
354    if (proxy->props)
355      eldbus_message_iter_dict_iterate(array, "sv", _property_changed_iter,
356                                      proxy);
357
358    while (eldbus_message_iter_get_and_next(invalidate, 's', &invalidate_prop))
359      {
360         Eldbus_Proxy_Event_Property_Removed event;
361         event.interface = proxy->interface;
362         event.name = invalidate_prop;
363         event.proxy = proxy;
364         if (proxy->props)
365           eina_hash_del(proxy->props, event.name, NULL);
366         _eldbus_proxy_event_callback_call(proxy, ELDBUS_PROXY_EVENT_PROPERTY_REMOVED,
367                                          &event);
368      }
369 }
370
371 static void
372 _props_cache_free(void *data)
373 {
374    Eina_Value *value = data;
375    eina_value_free(value);
376 }
377
378 EAPI void
379 eldbus_proxy_event_callback_add(Eldbus_Proxy *proxy, Eldbus_Proxy_Event_Type type, Eldbus_Proxy_Event_Cb cb, const void *cb_data)
380 {
381    Eldbus_Proxy_Context_Event *ce;
382    Eldbus_Proxy_Context_Event_Cb *ctx;
383
384    ELDBUS_PROXY_CHECK(proxy);
385    EINA_SAFETY_ON_NULL_RETURN(cb);
386    EINA_SAFETY_ON_TRUE_RETURN(type >= ELDBUS_PROXY_EVENT_LAST);
387
388    ce = proxy->event_handlers + type;
389
390    ctx = calloc(1, sizeof(Eldbus_Proxy_Context_Event_Cb));
391    EINA_SAFETY_ON_NULL_RETURN(ctx);
392    ctx->cb = cb;
393    ctx->cb_data = cb_data;
394
395    ce->list = eina_inlist_append(ce->list, EINA_INLIST_GET(ctx));
396
397    if (type == ELDBUS_PROXY_EVENT_PROPERTY_CHANGED)
398      {
399         if (proxy->properties_changed) return;
400         if (!proxy->props)
401           proxy->props = eina_hash_string_superfast_new(_props_cache_free);
402         proxy->properties_changed =
403                  eldbus_proxy_properties_changed_callback_add(proxy,
404                                                              _properties_changed,
405                                                              proxy);
406      }
407    else if (type == ELDBUS_PROXY_EVENT_PROPERTY_REMOVED)
408      {
409         if (proxy->properties_changed) return;
410         proxy->properties_changed =
411                  eldbus_proxy_properties_changed_callback_add(proxy,
412                                                              _properties_changed,
413                                                              proxy);
414      }
415 }
416
417 static void
418 _eldbus_proxy_context_event_cb_del(Eldbus_Proxy_Context_Event *ce, Eldbus_Proxy_Context_Event_Cb *ctx)
419 {
420    ce->list = eina_inlist_remove(ce->list, EINA_INLIST_GET(ctx));
421    free(ctx);
422 }
423
424 EAPI void
425 eldbus_proxy_event_callback_del(Eldbus_Proxy *proxy, Eldbus_Proxy_Event_Type type, Eldbus_Proxy_Event_Cb cb, const void *cb_data)
426 {
427    Eldbus_Proxy_Context_Event *ce;
428    Eldbus_Proxy_Context_Event_Cb *iter, *found = NULL;
429
430    ELDBUS_PROXY_CHECK(proxy);
431    EINA_SAFETY_ON_NULL_RETURN(cb);
432    EINA_SAFETY_ON_TRUE_RETURN(type >= ELDBUS_PROXY_EVENT_LAST);
433
434    ce = proxy->event_handlers + type;
435
436    EINA_INLIST_FOREACH(ce->list, iter)
437      {
438         if (cb != iter->cb) continue;
439         if ((cb_data) && (cb_data != iter->cb_data)) continue;
440
441         found = iter;
442         break;
443      }
444
445    EINA_SAFETY_ON_NULL_RETURN(found);
446    EINA_SAFETY_ON_TRUE_RETURN(found->deleted);
447
448    if (ce->walking)
449      {
450         found->deleted = EINA_TRUE;
451         ce->to_delete = eina_list_append(ce->to_delete, found);
452         return;
453      }
454
455    _eldbus_proxy_context_event_cb_del(ce, found);
456
457    if (type == ELDBUS_PROXY_EVENT_PROPERTY_CHANGED)
458      {
459         Eldbus_Proxy_Context_Event *ce_prop_remove;
460         ce_prop_remove = proxy->event_handlers +
461                  ELDBUS_PROXY_EVENT_PROPERTY_REMOVED;
462         if (!ce->list && !proxy->monitor_enabled)
463           {
464              eina_hash_free(proxy->props);
465              proxy->props = NULL;
466           }
467
468         if (!ce_prop_remove->list && !ce->list && !proxy->monitor_enabled)
469           {
470              eldbus_signal_handler_unref(proxy->properties_changed);
471              proxy->properties_changed = NULL;
472           }
473      }
474    else if (type == ELDBUS_PROXY_EVENT_PROPERTY_REMOVED)
475      {
476         Eldbus_Proxy_Context_Event *ce_prop_changed;
477         ce_prop_changed = proxy->event_handlers +
478                  ELDBUS_PROXY_EVENT_PROPERTY_CHANGED;
479
480         if (!ce_prop_changed->list && !ce->list && !proxy->monitor_enabled)
481           {
482              eldbus_signal_handler_unref(proxy->properties_changed);
483              proxy->properties_changed = NULL;
484           }
485      }
486 }
487
488 static void
489 _eldbus_proxy_event_callback_call(Eldbus_Proxy *proxy, Eldbus_Proxy_Event_Type type, const void *event_info)
490 {
491    Eldbus_Proxy_Context_Event *ce;
492    Eldbus_Proxy_Context_Event_Cb *iter;
493
494    ce = proxy->event_handlers + type;
495
496    ce->walking++;
497    EINA_INLIST_FOREACH(ce->list, iter)
498      {
499         if (iter->deleted) continue;
500         iter->cb((void *)iter->cb_data, proxy, (void *)event_info);
501      }
502    ce->walking--;
503    if (ce->walking > 0) return;
504
505    EINA_LIST_FREE(ce->to_delete, iter)
506      _eldbus_proxy_context_event_cb_del(ce, iter);
507 }
508
509 EAPI Eldbus_Object *
510 eldbus_proxy_object_get(const Eldbus_Proxy *proxy)
511 {
512    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
513    return proxy->obj;
514 }
515
516 EAPI const char *
517 eldbus_proxy_interface_get(const Eldbus_Proxy *proxy)
518 {
519    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
520    return proxy->interface;
521 }
522
523 static void
524 _on_pending_free(void *data, const void *dead_pointer)
525 {
526    Eldbus_Proxy *proxy = data;
527    Eldbus_Pending *pending = (Eldbus_Pending *)dead_pointer;
528    ELDBUS_PROXY_CHECK(proxy);
529    proxy->pendings = eina_inlist_remove(proxy->pendings,
530                                         EINA_INLIST_GET(pending));
531 }
532
533 static Eldbus_Pending *
534 _eldbus_proxy_send(Eldbus_Proxy *proxy, Eldbus_Message *msg, Eldbus_Message_Cb cb, const void *cb_data, double timeout)
535 {
536    Eldbus_Pending *pending;
537
538    pending = _eldbus_connection_send(proxy->obj->conn, msg, cb, cb_data, timeout);
539    if (!cb) return NULL;
540    EINA_SAFETY_ON_NULL_RETURN_VAL(pending, NULL);
541
542    eldbus_pending_free_cb_add(pending, _on_pending_free, proxy);
543    proxy->pendings = eina_inlist_append(proxy->pendings,
544                                         EINA_INLIST_GET(pending));
545
546    return pending;
547 }
548
549 EAPI Eldbus_Pending *
550 eldbus_proxy_send(Eldbus_Proxy *proxy, Eldbus_Message *msg, Eldbus_Message_Cb cb, const void *cb_data, double timeout)
551 {
552    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
553    EINA_SAFETY_ON_NULL_RETURN_VAL(msg, NULL);
554
555    return _eldbus_proxy_send(proxy, msg, cb, cb_data, timeout);
556 }
557
558 EAPI Eldbus_Message *
559 eldbus_proxy_method_call_new(Eldbus_Proxy *proxy, const char *member)
560 {
561    Eldbus_Message *msg;
562    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
563
564    msg = eldbus_message_method_call_new(
565                            eldbus_object_bus_name_get(proxy->obj),
566                            eldbus_object_path_get(proxy->obj),
567                            proxy->interface, member);
568    return msg;
569 }
570
571 static Eldbus_Pending *
572 _eldbus_proxy_vcall(Eldbus_Proxy *proxy, const char *member, Eldbus_Message_Cb cb, const void *cb_data, double timeout, const char *signature, va_list ap)
573 {
574    Eldbus_Message *msg = eldbus_proxy_method_call_new(proxy, member);
575    EINA_SAFETY_ON_NULL_RETURN_VAL(msg, NULL);
576
577    if (!eldbus_message_arguments_vappend(msg, signature, ap))
578      {
579         eldbus_message_unref(msg);
580         ERR("Error setting arguments");
581         return NULL;
582      }
583
584    return _eldbus_proxy_send(proxy, msg, cb, cb_data, timeout);
585 }
586
587 EAPI Eldbus_Pending *
588 eldbus_proxy_call(Eldbus_Proxy *proxy, const char *member, Eldbus_Message_Cb cb, const void *cb_data, double timeout, const char *signature, ...)
589 {
590    Eldbus_Pending *pending;
591    va_list ap;
592
593    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
594    EINA_SAFETY_ON_NULL_RETURN_VAL(member, NULL);
595    EINA_SAFETY_ON_NULL_RETURN_VAL(signature, NULL);
596
597    va_start(ap, signature);
598    pending = _eldbus_proxy_vcall(proxy, member, cb, cb_data, timeout,
599                                 signature, ap);
600    va_end(ap);
601
602    return pending;
603 }
604
605 EAPI Eldbus_Pending *
606 eldbus_proxy_vcall(Eldbus_Proxy *proxy, const char *member, Eldbus_Message_Cb cb, const void *cb_data, double timeout, const char *signature, va_list ap)
607 {
608    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
609    EINA_SAFETY_ON_NULL_RETURN_VAL(member, NULL);
610    EINA_SAFETY_ON_NULL_RETURN_VAL(signature, NULL);
611
612    return _eldbus_proxy_vcall(proxy, member, cb, cb_data, timeout,
613                              signature, ap);
614 }
615
616 static void
617 _on_signal_handler_free(void *data, const void *dead_pointer)
618 {
619    Eldbus_Proxy *proxy = data;
620    ELDBUS_PROXY_CHECK(proxy);
621    proxy->handlers = eina_list_remove(proxy->handlers, dead_pointer);
622 }
623
624 EAPI Eldbus_Signal_Handler *
625 eldbus_proxy_signal_handler_add(Eldbus_Proxy *proxy, const char *member, Eldbus_Signal_Cb cb, const void *cb_data)
626 {
627    Eldbus_Signal_Handler *handler;
628    const char *name, *path;
629
630    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
631    EINA_SAFETY_ON_NULL_RETURN_VAL(cb, NULL);
632
633    name = eldbus_object_bus_name_get(proxy->obj);
634    path = eldbus_object_path_get(proxy->obj);
635
636    handler = _eldbus_signal_handler_add(proxy->obj->conn, name, path,
637                                        proxy->interface, member, cb, cb_data);
638    EINA_SAFETY_ON_NULL_RETURN_VAL(handler, NULL);
639
640    eldbus_signal_handler_free_cb_add(handler, _on_signal_handler_free, proxy);
641    proxy->handlers = eina_list_append(proxy->handlers, handler);
642
643    return handler;
644 }
645
646 EAPI Eldbus_Pending *
647 eldbus_proxy_property_get(Eldbus_Proxy *proxy, const char *name, Eldbus_Message_Cb cb, const void *data)
648 {
649    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
650    EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
651    return eldbus_proxy_call(proxy->obj->properties, "Get", cb, data, -1,
652                            "ss", proxy->interface, name);
653 }
654
655 EAPI Eldbus_Pending *
656 eldbus_proxy_property_set(Eldbus_Proxy *proxy, const char *name, const char *sig, const void *value, Eldbus_Message_Cb cb, const void *data)
657 {
658    Eldbus_Message *msg;
659    Eldbus_Message_Iter *iter, *variant;
660
661    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
662    EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
663    EINA_SAFETY_ON_NULL_RETURN_VAL(sig, NULL);
664    EINA_SAFETY_ON_FALSE_RETURN_VAL(dbus_signature_validate_single(sig, NULL), NULL);
665    EINA_SAFETY_ON_NULL_RETURN_VAL(value, NULL);
666
667    msg = eldbus_proxy_method_call_new(proxy->obj->properties, "Set");
668    iter = eldbus_message_iter_get(msg);
669    eldbus_message_iter_basic_append(iter, 's', proxy->interface);
670    eldbus_message_iter_basic_append(iter, 's', name);
671    variant = eldbus_message_iter_container_new(iter, 'v', sig);
672    if (dbus_type_is_basic(sig[0]))
673      dbus_message_iter_append_basic(&variant->dbus_iterator, sig[0], &value);
674    else
675      {
676         if (!_message_iter_from_eina_value_struct(sig, variant, value))
677           {
678              eldbus_message_unref(msg);
679              return NULL;
680           }
681      }
682    eldbus_message_iter_container_close(iter, variant);
683
684    return eldbus_proxy_send(proxy->obj->properties, msg, cb, data, -1);
685 }
686
687 EAPI Eldbus_Pending *
688 eldbus_proxy_property_get_all(Eldbus_Proxy *proxy, Eldbus_Message_Cb cb, const void *data)
689 {
690    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
691    return eldbus_proxy_call(proxy->obj->properties, "GetAll", cb, data, -1,
692                            "s", proxy->interface);
693 }
694
695 EAPI Eldbus_Signal_Handler *
696 eldbus_proxy_properties_changed_callback_add(Eldbus_Proxy *proxy, Eldbus_Signal_Cb cb, const void *data)
697 {
698    Eldbus_Signal_Handler *sh;
699    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
700    sh = eldbus_proxy_signal_handler_add(proxy->obj->properties,
701                                        "PropertiesChanged", cb, data);
702    EINA_SAFETY_ON_NULL_RETURN_VAL(sh, NULL);
703    eldbus_signal_handler_match_extra_set(sh, "arg0", proxy->interface, NULL);
704    return sh;
705 }
706
707 static void
708 _property_iter(void *data, const void *key, Eldbus_Message_Iter *var)
709 {
710    Eldbus_Proxy *proxy = data;
711    const char *skey = key;
712    Eina_Value *st_value, stack_value, *value;
713
714    st_value = _message_iter_struct_to_eina_value(var);
715    eina_value_struct_value_get(st_value, "arg0", &stack_value);
716
717    value = eina_hash_find(proxy->props, skey);
718    if (!value)
719      {
720         value = eina_value_new(eina_value_type_get(&stack_value));
721         eina_hash_add(proxy->props, skey, value);
722      }
723    eina_value_flush(value);
724    eina_value_copy(&stack_value, value);
725
726    eina_value_free(st_value);
727    eina_value_flush(&stack_value);
728 }
729
730 static void
731 _props_get_all(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
732 {
733    Eldbus_Proxy *proxy = data;
734    Eldbus_Message_Iter *dict;
735    const char *name, *error_msg;
736
737    if (eldbus_message_error_get(msg, &name, &error_msg))
738      {
739         ERR("Error getting all properties of %s %s, error message: %s %s",
740             proxy->obj->name, proxy->obj->path, name, error_msg);
741         return;
742      }
743
744    if (!eldbus_message_arguments_get(msg, "a{sv}", &dict))
745      {
746         char *txt;
747
748         if (eldbus_message_arguments_get(msg, "s", &txt))
749           ERR("Error getting data from properties getAll: %s", txt);
750         return;
751      }
752    eldbus_message_iter_dict_iterate(dict, "sv", _property_iter, proxy);
753 }
754
755 EAPI void
756 eldbus_proxy_properties_monitor(Eldbus_Proxy *proxy, Eina_Bool enable)
757 {
758    ELDBUS_PROXY_CHECK(proxy);
759    if (proxy->monitor_enabled == enable)
760      return;
761
762    proxy->monitor_enabled = enable;
763    if (enable)
764      {
765         if (!proxy->props)
766           proxy->props = eina_hash_string_superfast_new(_props_cache_free);
767         eldbus_proxy_property_get_all(proxy, _props_get_all, proxy);
768
769         if (proxy->properties_changed)
770           return;
771         proxy->properties_changed =
772                  eldbus_proxy_properties_changed_callback_add(proxy,
773                                                              _properties_changed,
774                                                              proxy);
775      }
776    else
777      {
778         Eldbus_Proxy_Context_Event *ce_prop_changed, *ce_prop_removed;
779         ce_prop_changed = proxy->event_handlers + ELDBUS_PROXY_EVENT_PROPERTY_CHANGED;
780         ce_prop_removed = proxy->event_handlers + ELDBUS_PROXY_EVENT_PROPERTY_REMOVED;
781
782         if (!ce_prop_changed->list)
783           {
784              eina_hash_free(proxy->props);
785              proxy->props = NULL;
786           }
787         if (!ce_prop_changed->list && !ce_prop_removed->list)
788           {
789              eldbus_signal_handler_unref(proxy->properties_changed);
790              proxy->properties_changed = NULL;
791           }
792      }
793 }
794
795 EAPI Eina_Value *
796 eldbus_proxy_property_local_get(Eldbus_Proxy *proxy, const char *name)
797 {
798    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
799    EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
800    EINA_SAFETY_ON_NULL_RETURN_VAL(proxy->props, NULL);
801    return eina_hash_find(proxy->props, name);
802 }
803
804 EAPI const Eina_Hash *
805 eldbus_proxy_property_local_get_all(Eldbus_Proxy *proxy)
806 {
807    ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
808    EINA_SAFETY_ON_NULL_RETURN_VAL(proxy->props, NULL);
809    return proxy->props;
810 }