1 /*
2  * Copyright (C) 2009-2011 Collabora Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Authors: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk>
19  */
20 
21 #include "config.h"
22 #include "dbus-service-internal.h"
23 
24 #include <string.h>
25 #include <sys/stat.h>
26 
27 #include <glib.h>
28 #include <telepathy-glib/telepathy-glib.h>
29 #include <telepathy-glib/telepathy-glib-dbus.h>
30 
31 #include <telepathy-logger/event-internal.h>
32 #include <telepathy-logger/text-event.h>
33 #include <telepathy-logger/log-manager.h>
34 #include <telepathy-logger/log-manager-internal.h>
35 
36 #include <extensions/extensions.h>
37 
38 #define DEBUG_FLAG TPL_DEBUG_DBUS_SERVICE
39 #include <telepathy-logger/action-chain-internal.h>
40 #include <telepathy-logger/debug-internal.h>
41 #include <telepathy-logger/util-internal.h>
42 
43 #define FAVOURITE_CONTACTS_FILENAME "favourite-contacts.txt"
44 
45 static void tpl_logger_iface_init (gpointer iface, gpointer iface_data);
46 
47 struct _TplDBusServicePriv
48 {
49   TplLogManager *manager;
50   /* map of (string) account name -> (string set) contact ID */
51   /* (the set is implemented as a hash table) */
52   GHashTable *accounts_contacts_map;
53   TplActionChain *favourite_contacts_actions;
54 };
55 
56 G_DEFINE_TYPE_WITH_CODE (TplDBusService, _tpl_dbus_service, G_TYPE_OBJECT,
57     G_IMPLEMENT_INTERFACE (TPL_TYPE_SVC_LOGGER, tpl_logger_iface_init));
58 
59 typedef struct _FavouriteContactClosure FavouriteContactClosure;
60 typedef void (*FavouriteContactCallback) (gboolean success,
61     FavouriteContactClosure *closure);
62 
63 
64 struct _FavouriteContactClosure {
65   TplDBusService *service;
66   gchar *account;
67   gchar *contact_id;
68   gchar *file_contents;
69   DBusGMethodInvocation *context;
70   FavouriteContactCallback cb;
71 };
72 
73 
74 static void
favourite_contact_closure_free(FavouriteContactClosure * closure)75 favourite_contact_closure_free (FavouriteContactClosure *closure)
76 {
77   if (closure == NULL)
78     return;
79 
80   if (closure->service != NULL)
81     g_object_unref (closure->service);
82 
83   g_free (closure->account);
84   g_free (closure->contact_id);
85   g_free (closure->file_contents);
86   g_slice_free (FavouriteContactClosure, closure);
87 }
88 
89 
90 static FavouriteContactClosure *
favourite_contact_closure_new(TplDBusService * self,const gchar * account,const gchar * contact_id,DBusGMethodInvocation * context)91 favourite_contact_closure_new (TplDBusService *self,
92     const gchar *account,
93     const gchar *contact_id,
94     DBusGMethodInvocation *context)
95 {
96   FavouriteContactClosure *closure;
97 
98   closure = g_slice_new0 (FavouriteContactClosure);
99   closure->service = g_object_ref (G_OBJECT (self));
100   closure->account = g_strdup (account);
101   closure->contact_id = g_strdup (contact_id);
102   /* XXX: ideally we'd up the ref count or duplicate this */
103   closure->context = context;
104 
105   return closure;
106 }
107 
108 
109 static gboolean
favourite_contacts_add_event(TplDBusService * self,const gchar * account,const gchar * contact_id)110 favourite_contacts_add_event (TplDBusService *self,
111     const gchar *account,
112     const gchar *contact_id)
113 {
114   GHashTable *contacts;
115   gboolean new_event = FALSE;
116   TplDBusServicePriv *priv;
117 
118   g_return_val_if_fail (TPL_IS_DBUS_SERVICE (self), FALSE);
119   g_return_val_if_fail (account != NULL, FALSE);
120   g_return_val_if_fail (contact_id != NULL, FALSE);
121 
122   priv = self->priv;
123 
124   DEBUG ("adding favourite contact: account '%s', ID '%s'",
125       account, contact_id);
126 
127   contacts = g_hash_table_lookup (priv->accounts_contacts_map, account);
128   if (contacts == NULL)
129     {
130       contacts = g_hash_table_new_full (g_str_hash, g_str_equal,
131           (GDestroyNotify) g_free, NULL);
132       g_hash_table_insert (priv->accounts_contacts_map, g_strdup (account),
133           contacts);
134       new_event = TRUE;
135     }
136   else if (g_hash_table_lookup (contacts, contact_id) == NULL)
137     {
138       new_event = TRUE;
139     }
140 
141   if (new_event)
142     {
143       /* add dummy string for the value just for the convenience of looking up
144        * whether the key already exists */
145       g_hash_table_insert (contacts, g_strdup (contact_id),
146           GINT_TO_POINTER (TRUE));
147     }
148 
149   return new_event;
150 }
151 
152 
153 static const gchar *
favourite_contacts_get_filename(void)154 favourite_contacts_get_filename (void)
155 {
156   static gchar *filename = NULL;
157 
158   if (filename == NULL)
159     {
160       filename = g_build_filename (g_get_user_data_dir (), TPL_DATA_DIR,
161           FAVOURITE_CONTACTS_FILENAME, NULL);
162     }
163 
164   return filename;
165 }
166 
167 
168 static gboolean
favourite_contacts_parse_line(TplDBusService * self,const gchar * line)169 favourite_contacts_parse_line (TplDBusService *self,
170     const gchar *line)
171 {
172   gboolean success = TRUE;
173   gchar **elements;
174 
175   if (line == NULL || line[0] == '\0')
176     return TRUE;
177 
178   /* this works on the assumption that account names can't have spaces in them
179    */
180   elements = g_strsplit (line, " ", 2);
181   if (g_strv_length (elements) < 2)
182     {
183       DEBUG ("invalid number of elements on favourite contacts file line:\n"
184           "%s\n", line);
185       success = FALSE;
186     }
187   else
188     favourite_contacts_add_event (self, elements[0], elements[1]);
189 
190   g_strfreev (elements);
191 
192   return success;
193 }
194 
195 
196 static void
favourite_contacts_file_read_line_cb(GObject * object,GAsyncResult * result,gpointer user_data)197 favourite_contacts_file_read_line_cb (GObject *object,
198     GAsyncResult *result,
199     gpointer user_data)
200 {
201   GDataInputStream *data_stream = G_DATA_INPUT_STREAM (object);
202   TplActionChain *action_chain = (TplActionChain *) (user_data);
203   TplDBusService *self = _tpl_action_chain_get_object (action_chain);
204   gchar *line;
205   GError *error = NULL;
206 
207   line = g_data_input_stream_read_line_finish (data_stream, result, NULL, &error);
208 
209   if (error != NULL)
210     {
211       g_prefix_error (&error, "failed to open favourite contacts file: ");
212       _tpl_action_chain_terminate (action_chain, error);
213       g_clear_error (&error);
214     }
215   else if (line != NULL)
216     {
217       favourite_contacts_parse_line (self, line);
218 
219       g_data_input_stream_read_line_async (data_stream, G_PRIORITY_DEFAULT,
220           NULL, favourite_contacts_file_read_line_cb, action_chain);
221     }
222   else
223     _tpl_action_chain_continue (action_chain);
224 }
225 
226 
227 static void
favourite_contacts_file_open_cb(GObject * object,GAsyncResult * result,gpointer user_data)228 favourite_contacts_file_open_cb (GObject *object,
229     GAsyncResult *result,
230     gpointer user_data)
231 {
232   GFile *file = G_FILE (object);
233   TplActionChain *action_chain = (TplActionChain *) user_data;
234   GFileInputStream *stream;
235   GError *error = NULL;
236 
237   if ((stream = g_file_read_finish (file, result, &error)))
238     {
239       GDataInputStream *data_stream = g_data_input_stream_new (
240           G_INPUT_STREAM (stream));
241 
242       g_data_input_stream_read_line_async (data_stream, G_PRIORITY_DEFAULT,
243           NULL, favourite_contacts_file_read_line_cb, action_chain);
244 
245       g_object_unref (stream);
246     }
247   else if (error->code == G_IO_ERROR_NOT_FOUND)
248     {
249       DEBUG ("Favourite contacts file doesn't exist yet. Will create as "
250           "necessary.");
251 
252       g_clear_error (&error);
253       _tpl_action_chain_continue (action_chain);
254     }
255   else
256     {
257       g_prefix_error (&error, "Failed to open the favourite contacts file: ");
258       _tpl_action_chain_terminate (action_chain, error);
259       g_clear_error (&error);
260     }
261 }
262 
263 
264 static void
pendingproc_favourite_contacts_file_open(TplActionChain * action_chain,gpointer user_data)265 pendingproc_favourite_contacts_file_open (TplActionChain *action_chain,
266     gpointer user_data)
267 {
268   const gchar *filename;
269   GFile *file;
270 
271   filename = favourite_contacts_get_filename ();
272   file = g_file_new_for_path (filename);
273 
274   g_file_read_async (file, G_PRIORITY_DEFAULT, NULL,
275       favourite_contacts_file_open_cb, action_chain);
276 
277   g_object_unref (G_OBJECT (file));
278 }
279 
280 
281 static void
tpl_dbus_service_dispose(GObject * obj)282 tpl_dbus_service_dispose (GObject *obj)
283 {
284   TplDBusServicePriv *priv = TPL_DBUS_SERVICE (obj)->priv;
285 
286   if (priv->accounts_contacts_map != NULL)
287     {
288       g_hash_table_unref (priv->accounts_contacts_map);
289       priv->accounts_contacts_map = NULL;
290     }
291 
292   if (priv->favourite_contacts_actions != NULL)
293     priv->favourite_contacts_actions = NULL;
294 
295   G_OBJECT_CLASS (_tpl_dbus_service_parent_class)->dispose (obj);
296 }
297 
298 
299 static void
favourite_contacts_file_parsed_cb(GObject * object,GAsyncResult * result,gpointer user_data)300 favourite_contacts_file_parsed_cb (GObject *object,
301     GAsyncResult *result,
302     gpointer user_data)
303 {
304   TplDBusService *self = TPL_DBUS_SERVICE (object);
305   TplDBusServicePriv *priv = self->priv;
306   GError *error = NULL;
307 
308   if (!_tpl_action_chain_new_finish (object, result, &error))
309     {
310       DEBUG ("Failed to parse the favourite contacts file and/or execute "
311           "subsequent queued method calls: %s", error->message);
312       g_error_free (error);
313     }
314 
315   priv->favourite_contacts_actions = NULL;
316 }
317 
318 
319 static void
tpl_dbus_service_constructed(GObject * object)320 tpl_dbus_service_constructed (GObject *object)
321 {
322   TplDBusServicePriv *priv = TPL_DBUS_SERVICE (object)->priv;
323 
324   priv->favourite_contacts_actions = _tpl_action_chain_new_async (object,
325       favourite_contacts_file_parsed_cb, object);
326 
327   _tpl_action_chain_append (priv->favourite_contacts_actions,
328       pendingproc_favourite_contacts_file_open, NULL);
329   _tpl_action_chain_continue (priv->favourite_contacts_actions);
330 }
331 
332 
333 static void
_tpl_dbus_service_class_init(TplDBusServiceClass * klass)334 _tpl_dbus_service_class_init (TplDBusServiceClass *klass)
335 {
336   GObjectClass* object_class = G_OBJECT_CLASS (klass);
337 
338   object_class->constructed = tpl_dbus_service_constructed;
339   object_class->dispose = tpl_dbus_service_dispose;
340 
341   g_type_class_add_private (object_class, sizeof (TplDBusServicePriv));
342 }
343 
344 
345 static void
_tpl_dbus_service_init(TplDBusService * self)346 _tpl_dbus_service_init (TplDBusService *self)
347 {
348   TplDBusServicePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
349       TPL_TYPE_DBUS_SERVICE, TplDBusServicePriv);
350 
351   g_return_if_fail (TPL_IS_DBUS_SERVICE (self));
352 
353   self->priv = priv;
354   priv->manager = tpl_log_manager_dup_singleton ();
355   priv->accounts_contacts_map = g_hash_table_new_full (g_str_hash, g_str_equal,
356       (GDestroyNotify) g_free, (GDestroyNotify) g_hash_table_unref);
357   priv->favourite_contacts_actions = NULL;
358 }
359 
360 
361 TplDBusService *
_tpl_dbus_service_new(void)362 _tpl_dbus_service_new (void)
363 {
364   return g_object_new (TPL_TYPE_DBUS_SERVICE, NULL);
365 }
366 
367 
368 static void
append_favourite_contacts_account_and_contacts(const gchar * account,GHashTable * contacts,GPtrArray * packed)369 append_favourite_contacts_account_and_contacts (const gchar *account,
370     GHashTable *contacts,
371     GPtrArray *packed)
372 {
373   GList *l;
374   gchar **contact_ids;
375   gint i;
376 
377   /* this case shouldn't happen, but this is just some basic sanity checking */
378   if (g_hash_table_size (contacts) < 1)
379     return;
380 
381   /* includes room for the terminal NULL */
382   contact_ids = g_new0 (gchar *, g_hash_table_size (contacts)+1);
383 
384   for (i = 0, l = g_hash_table_get_keys (contacts);
385       l;
386       i++, l = g_list_delete_link (l, l))
387     {
388       contact_ids[i] = l->data;
389     }
390 
391   g_ptr_array_add (packed, tp_value_array_build (2,
392       DBUS_TYPE_G_OBJECT_PATH, account,
393       G_TYPE_STRV, contact_ids,
394       G_TYPE_INVALID));
395 
396   g_free (contact_ids);
397 }
398 
399 
400 static void
pendingproc_get_favourite_contacts(TplActionChain * action_chain,gpointer user_data)401 pendingproc_get_favourite_contacts (TplActionChain *action_chain,
402     gpointer user_data)
403 {
404   FavouriteContactClosure *closure = user_data;
405   TplDBusServicePriv *priv;
406   GPtrArray *packed;
407 
408   g_return_if_fail (closure);
409   g_return_if_fail (TPL_IS_DBUS_SERVICE (closure->service));
410   g_return_if_fail (closure->context != NULL);
411 
412   priv = closure->service->priv;
413 
414   packed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_value_array_free);
415 
416   g_hash_table_foreach (priv->accounts_contacts_map,
417       (GHFunc) append_favourite_contacts_account_and_contacts, packed);
418 
419   tpl_svc_logger_return_from_get_favourite_contacts (closure->context, packed);
420 
421   g_ptr_array_unref (packed);
422   favourite_contact_closure_free (closure);
423 
424   if (action_chain != NULL)
425     _tpl_action_chain_continue (action_chain);
426 }
427 
428 
429 static void
tpl_dbus_service_get_favourite_contacts(TplSvcLogger * logger,DBusGMethodInvocation * context)430 tpl_dbus_service_get_favourite_contacts (TplSvcLogger *logger,
431     DBusGMethodInvocation *context)
432 {
433   TplDBusService *self;
434   TplDBusServicePriv *priv;
435   FavouriteContactClosure *closure;
436 
437   g_return_if_fail (TPL_IS_DBUS_SERVICE (logger));
438   g_return_if_fail (context != NULL);
439 
440   self = TPL_DBUS_SERVICE (logger);
441   priv = self->priv;
442 
443   closure = favourite_contact_closure_new (self, NULL, NULL, context);
444 
445   /* If we're still waiting on the contacts to finish being parsed from disk,
446    * queue this action */
447   if (priv->favourite_contacts_actions != NULL)
448     {
449       _tpl_action_chain_append (priv->favourite_contacts_actions,
450           pendingproc_get_favourite_contacts, closure);
451     }
452   else
453     pendingproc_get_favourite_contacts (NULL, closure);
454 }
455 
456 
457 static void
append_favourite_contacts_file_entries(const gchar * account,GHashTable * contacts,GString * string)458 append_favourite_contacts_file_entries (const gchar *account,
459     GHashTable *contacts,
460     GString *string)
461 {
462   GList *l;
463 
464   for (l = g_hash_table_get_keys (contacts); l; l = g_list_delete_link (l, l))
465     g_string_append_printf (string, "%s %s\n", account, (const gchar*) l->data);
466 }
467 
468 
469 static gchar *
favourite_contacts_to_string(TplDBusService * self)470 favourite_contacts_to_string (TplDBusService *self)
471 {
472   TplDBusServicePriv *priv = self->priv;
473   GString *string;
474 
475   string = g_string_new ("");
476 
477   g_hash_table_foreach (priv->accounts_contacts_map,
478       (GHFunc) append_favourite_contacts_file_entries, string);
479 
480   return g_string_free (string, FALSE);
481 }
482 
483 
484 static void
favourite_contacts_file_replace_contents_cb(GObject * object,GAsyncResult * result,gpointer user_data)485 favourite_contacts_file_replace_contents_cb (GObject *object,
486     GAsyncResult *result,
487     gpointer user_data)
488 {
489   GFile *file = G_FILE (object);
490   GError *error = NULL;
491   FavouriteContactClosure *closure = user_data;
492   gboolean success;
493 
494   if (g_file_replace_contents_finish (file, result, NULL, &error))
495     {
496       success = TRUE;
497     }
498   else
499     {
500       DEBUG ("Failed to save favourite contacts file: %s", error->message);
501       success = FALSE;
502       g_clear_error (&error);
503     }
504 
505   ((FavouriteContactCallback) closure->cb) (success, closure);
506 }
507 
508 
509 static void
favourite_contacts_file_save_async(TplDBusService * self,FavouriteContactClosure * closure)510 favourite_contacts_file_save_async (TplDBusService *self,
511     FavouriteContactClosure *closure)
512 {
513   gchar *dir;
514   const gchar *filename;
515   GFile *file;
516   gchar *file_contents;
517 
518   g_return_if_fail (closure != NULL);
519 
520   filename = favourite_contacts_get_filename ();
521   dir = g_path_get_dirname (filename);
522   g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
523   g_free (dir);
524 
525   file = g_file_new_for_path (filename);
526 
527   file_contents = favourite_contacts_to_string (self);
528 
529   closure->file_contents = file_contents;
530 
531   g_file_replace_contents_async (file,
532       file_contents, strlen (file_contents), NULL, FALSE,
533       G_FILE_CREATE_REPLACE_DESTINATION, NULL,
534       favourite_contacts_file_replace_contents_cb, closure);
535 
536   g_object_unref (file);
537 }
538 
539 
540 static void
add_favourite_contact_file_save_cb(gboolean added_favourite,FavouriteContactClosure * closure)541 add_favourite_contact_file_save_cb (gboolean added_favourite,
542     FavouriteContactClosure *closure)
543 {
544   TplDBusServicePriv *priv = closure->service->priv;
545   TplActionChain *action_chain = priv->favourite_contacts_actions;
546 
547   if (added_favourite)
548     {
549       const gchar *added[] = { NULL, NULL };
550       const gchar *removed[] = { NULL };
551 
552       added[0] = closure->contact_id;
553 
554       tpl_svc_logger_emit_favourite_contacts_changed (closure->service,
555           closure->account, added, removed);
556     }
557 
558   tpl_svc_logger_return_from_add_favourite_contact (closure->context);
559 
560   favourite_contact_closure_free (closure);
561   if (action_chain != NULL)
562     _tpl_action_chain_continue (action_chain);
563 }
564 
565 
566 static void
pendingproc_add_favourite_contact(TplActionChain * action_chain,gpointer user_data)567 pendingproc_add_favourite_contact (TplActionChain *action_chain,
568     gpointer user_data)
569 {
570   FavouriteContactClosure *closure = user_data;
571   gboolean should_add = FALSE;
572   GError *error = NULL;
573 
574   g_return_if_fail (closure);
575   g_return_if_fail (TPL_IS_DBUS_SERVICE (closure->service));
576   g_return_if_fail (closure->context != NULL);
577 
578   if (!tp_dbus_check_valid_object_path (closure->account, &error))
579     {
580       dbus_g_method_return_error (closure->context, error);
581 
582       goto pendingproc_add_favourite_contact_ERROR;
583     }
584 
585   should_add = favourite_contacts_add_event (closure->service, closure->account,
586       closure->contact_id);
587 
588   closure->cb = add_favourite_contact_file_save_cb;
589 
590   if (should_add)
591     favourite_contacts_file_save_async (closure->service, closure);
592   else
593     add_favourite_contact_file_save_cb (FALSE, closure);
594 
595   return;
596 
597 pendingproc_add_favourite_contact_ERROR:
598   if (action_chain != NULL)
599     _tpl_action_chain_terminate (action_chain, error);
600 
601   g_clear_error (&error);
602 }
603 
604 
605 static void
tpl_dbus_service_add_favourite_contact(TplSvcLogger * logger,const gchar * account,const gchar * contact_id,DBusGMethodInvocation * context)606 tpl_dbus_service_add_favourite_contact (TplSvcLogger *logger,
607     const gchar *account,
608     const gchar *contact_id,
609     DBusGMethodInvocation *context)
610 {
611   TplDBusService *self = TPL_DBUS_SERVICE (logger);
612   TplDBusServicePriv *priv;
613   FavouriteContactClosure *closure;
614 
615   g_return_if_fail (TPL_IS_DBUS_SERVICE (self));
616   g_return_if_fail (context != NULL);
617 
618   priv = self->priv;
619 
620   closure = favourite_contact_closure_new (self, account, contact_id, context);
621 
622   /* If we're still waiting on the contacts to finish being parsed from disk,
623    * queue this action */
624   if (priv->favourite_contacts_actions != NULL)
625     {
626       _tpl_action_chain_append (priv->favourite_contacts_actions,
627           pendingproc_add_favourite_contact, closure);
628     }
629   else
630     pendingproc_add_favourite_contact (NULL, closure);
631 }
632 
633 static void
remove_favourite_contact_file_save_cb(gboolean removed_favourite,FavouriteContactClosure * closure)634 remove_favourite_contact_file_save_cb (gboolean removed_favourite,
635     FavouriteContactClosure *closure)
636 {
637   TplDBusServicePriv *priv = closure->service->priv;
638   TplActionChain *action_chain = priv->favourite_contacts_actions;
639 
640   if (removed_favourite)
641     {
642       const gchar *added[] = { NULL };
643       const gchar *removed[] = { NULL, NULL };
644 
645       removed[0] = closure->contact_id;
646 
647       tpl_svc_logger_emit_favourite_contacts_changed (closure->service,
648           closure->account, added, removed);
649     }
650 
651   tpl_svc_logger_return_from_remove_favourite_contact (closure->context);
652 
653   favourite_contact_closure_free (closure);
654   if (action_chain != NULL)
655     _tpl_action_chain_continue (action_chain);
656 }
657 
658 
659 static void
pendingproc_remove_favourite_contact(TplActionChain * action_chain,gpointer user_data)660 pendingproc_remove_favourite_contact (TplActionChain *action_chain,
661     gpointer user_data)
662 {
663   FavouriteContactClosure *closure = user_data;
664   GHashTable *contacts;
665   gboolean removed = FALSE;
666   GError *error = NULL;
667 
668   g_return_if_fail (closure != NULL);
669   g_return_if_fail (TPL_IS_DBUS_SERVICE (closure->service));
670   g_return_if_fail (closure->context != NULL);
671 
672   TplDBusServicePriv *priv = closure->service->priv;
673 
674   if (!tp_dbus_check_valid_object_path (closure->account, &error))
675     {
676       dbus_g_method_return_error (closure->context, error);
677 
678       goto pendingproc_remove_favourite_contact_ERROR;
679     }
680 
681   DEBUG ("removing favourite contact: account '%s', ID '%s'",
682       closure->account, closure->contact_id);
683 
684   contacts = g_hash_table_lookup (priv->accounts_contacts_map,
685       closure->account);
686   if (contacts != NULL && g_hash_table_remove (contacts, closure->contact_id))
687       removed = TRUE;
688 
689   closure->cb = remove_favourite_contact_file_save_cb;
690 
691   if (removed)
692     favourite_contacts_file_save_async (closure->service, closure);
693   else
694     remove_favourite_contact_file_save_cb (FALSE, closure);
695 
696   return;
697 
698 pendingproc_remove_favourite_contact_ERROR:
699   if (action_chain != NULL)
700     _tpl_action_chain_terminate (action_chain, error);
701 
702   g_clear_error (&error);
703 }
704 
705 static void
tpl_dbus_service_remove_favourite_contact(TplSvcLogger * logger,const gchar * account,const gchar * contact_id,DBusGMethodInvocation * context)706 tpl_dbus_service_remove_favourite_contact (TplSvcLogger *logger,
707     const gchar *account,
708     const gchar *contact_id,
709     DBusGMethodInvocation *context)
710 {
711   TplDBusService *self = TPL_DBUS_SERVICE (logger);
712   TplDBusServicePriv *priv;
713   FavouriteContactClosure *closure;
714 
715   g_return_if_fail (TPL_IS_DBUS_SERVICE (self));
716   g_return_if_fail (context != NULL);
717 
718   priv = self->priv;
719 
720   closure = favourite_contact_closure_new (self, account, contact_id, context);
721 
722   /* If we're still waiting on the contacts to finish being parsed from disk,
723    * queue this action */
724   if (priv->favourite_contacts_actions != NULL)
725     {
726       _tpl_action_chain_append (priv->favourite_contacts_actions,
727           pendingproc_remove_favourite_contact, closure);
728     }
729   else
730     pendingproc_remove_favourite_contact (NULL, closure);
731 }
732 
733 
734 static void
tpl_dbus_service_clear(TplSvcLogger * logger,DBusGMethodInvocation * context)735 tpl_dbus_service_clear (TplSvcLogger *logger,
736     DBusGMethodInvocation *context)
737 {
738   TplDBusService *self = TPL_DBUS_SERVICE (logger);
739 
740   g_return_if_fail (TPL_IS_DBUS_SERVICE (self));
741   g_return_if_fail (context != NULL);
742 
743   /* We want to clear synchronously to avoid concurent write */
744   _tpl_log_manager_clear (self->priv->manager);
745 
746   tpl_svc_logger_return_from_clear (context);
747 }
748 
749 
750 static void
tpl_dbus_service_clear_account(TplSvcLogger * logger,const gchar * account_path,DBusGMethodInvocation * context)751 tpl_dbus_service_clear_account (TplSvcLogger *logger,
752     const gchar *account_path,
753     DBusGMethodInvocation *context)
754 {
755   TplDBusService *self = TPL_DBUS_SERVICE (logger);
756   TpDBusDaemon *bus;
757   TpAccount *account;
758   GError *error = NULL;
759 
760   g_return_if_fail (TPL_IS_DBUS_SERVICE (self));
761   g_return_if_fail (context != NULL);
762 
763   bus = tp_dbus_daemon_dup (&error);
764   if (bus == NULL)
765     {
766       DEBUG ("Unable to acquire the bus daemon: %s", error->message);
767       dbus_g_method_return_error (context, error);
768       goto out;
769     }
770 
771   account = tp_account_new (bus, account_path, &error);
772   if (account == NULL)
773     {
774       DEBUG ("Unable to acquire the account for %s: %s", account_path,
775           error->message);
776       dbus_g_method_return_error (context, error);
777       goto out;
778     }
779 
780   /* We want to clear synchronously to avoid concurent write */
781   _tpl_log_manager_clear_account (self->priv->manager, account);
782   g_object_unref (account);
783 
784   tpl_svc_logger_return_from_clear_account (context);
785 
786 out:
787   if (bus != NULL)
788     g_object_unref (bus);
789 
790   g_clear_error (&error);
791 }
792 
793 
794 static void
tpl_dbus_service_clear_entity(TplSvcLogger * logger,const gchar * account_path,const gchar * identifier,gint type,DBusGMethodInvocation * context)795 tpl_dbus_service_clear_entity (TplSvcLogger *logger,
796     const gchar *account_path,
797     const gchar *identifier,
798     gint type,
799     DBusGMethodInvocation *context)
800 {
801   TplDBusService *self = TPL_DBUS_SERVICE (logger);
802   TpDBusDaemon *bus;
803   TpAccount *account;
804   TplEntity *entity;
805   GError *error = NULL;
806 
807   g_return_if_fail (TPL_IS_DBUS_SERVICE (self));
808   g_return_if_fail (context != NULL);
809   g_return_if_fail (!TPL_STR_EMPTY (identifier));
810 
811   bus = tp_dbus_daemon_dup (&error);
812   if (bus == NULL)
813     {
814       DEBUG ("Unable to acquire the bus daemon: %s", error->message);
815       dbus_g_method_return_error (context, error);
816       goto out;
817     }
818 
819   account = tp_account_new (bus, account_path, &error);
820   if (account == NULL)
821     {
822       DEBUG ("Unable to acquire the account for %s: %s", account_path,
823           error->message);
824       dbus_g_method_return_error (context, error);
825       goto out;
826     }
827 
828   entity = tpl_entity_new (identifier, type, NULL, NULL);
829 
830   /* We want to clear synchronously to avoid concurent write */
831   _tpl_log_manager_clear_entity (self->priv->manager, account, entity);
832 
833   g_object_unref (account);
834   g_object_unref (entity);
835 
836   tpl_svc_logger_return_from_clear_account (context);
837 
838 out:
839   if (bus != NULL)
840     g_object_unref (bus);
841 
842   g_clear_error (&error);
843 }
844 
845 static void
tpl_logger_iface_init(gpointer iface,gpointer iface_data)846 tpl_logger_iface_init (gpointer iface,
847     gpointer iface_data)
848 {
849   TplSvcLoggerClass *klass = (TplSvcLoggerClass *) iface;
850 
851 #define IMPLEMENT(x) tpl_svc_logger_implement_##x (klass, tpl_dbus_service_##x)
852   IMPLEMENT (get_favourite_contacts);
853   IMPLEMENT (add_favourite_contact);
854   IMPLEMENT (remove_favourite_contact);
855   IMPLEMENT (clear);
856   IMPLEMENT (clear_account);
857   IMPLEMENT (clear_entity);
858 #undef IMPLEMENT
859 }
860