]> git.0d.be Git - empathy.git/blob - src/empathy-sanity-cleaning.c
Use a flat namespace for internal includes
[empathy.git] / src / empathy-sanity-cleaning.c
1 /*
2  * empathy-sanity-cleaning.c
3  * Code automatically called when starting a specific version of Empathy for
4  * the first time doing misc cleaning.
5  *
6  * Copyright (C) 2012 Collabora Ltd.
7  * @author Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23
24 #include "config.h"
25
26 #include "empathy-sanity-cleaning.h"
27
28 #include "empathy-gsettings.h"
29
30 #include "empathy-theme-manager.h"
31
32 #ifdef HAVE_UOA
33 #include "empathy-pkg-kit.h"
34 #include "empathy-uoa-utils.h"
35
36 #include <libaccounts-glib/ag-account-service.h>
37 #include <libaccounts-glib/ag-manager.h>
38 #include <libaccounts-glib/ag-service.h>
39 #endif
40
41 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
42 #include "empathy-debug.h"
43
44 /*
45  * This number has to be increased each time a new task is added or modified.
46  *
47  * If the number stored in gsettings is lower than it, all the tasks will
48  * be executed.
49  */
50 #define SANITY_CLEANING_NUMBER 4
51
52 typedef struct
53 {
54   TpAccountManager *am;
55   GSimpleAsyncResult *result;
56
57   gint ref_count;
58 } SanityCtx;
59
60 static SanityCtx *
61 sanity_ctx_new (TpAccountManager *am,
62     GSimpleAsyncResult *result)
63 {
64   SanityCtx *ctx = g_slice_new0 (SanityCtx);
65
66   ctx->am = g_object_ref (am);
67   ctx->result = g_object_ref (result);
68
69   ctx->ref_count = 1;
70   return ctx;
71 }
72
73 #ifdef HAVE_UOA
74 static SanityCtx *
75 sanity_ctx_ref (SanityCtx *ctx)
76 {
77   ctx->ref_count++;
78
79   return ctx;
80 }
81 #endif
82
83 static void
84 sanity_ctx_unref (SanityCtx *ctx)
85 {
86   ctx->ref_count--;
87
88   if (ctx->ref_count != 0)
89     return;
90
91   g_simple_async_result_complete_in_idle (ctx->result);
92
93   g_object_unref (ctx->am);
94   g_object_unref (ctx->result);
95
96   g_slice_free (SanityCtx, ctx);
97 }
98
99 static void
100 account_update_parameters_cb (GObject *source,
101     GAsyncResult *result,
102     gpointer user_data)
103 {
104   GError *error = NULL;
105   TpAccount *account = TP_ACCOUNT (source);
106
107   if (!tp_account_update_parameters_finish (account, result, NULL, &error))
108     {
109       DEBUG ("Failed to update parameters of account '%s': %s",
110           tp_account_get_path_suffix (account), error->message);
111
112       g_error_free (error);
113       return;
114     }
115
116   tp_account_reconnect_async (account, NULL, NULL);
117 }
118
119 /* Make sure XMPP accounts don't have a negative priority (bgo #671452) */
120 static void
121 fix_xmpp_account_priority (TpAccountManager *am)
122 {
123   GList *accounts, *l;
124
125   accounts = tp_account_manager_dup_valid_accounts (am);
126   for (l = accounts; l != NULL; l = g_list_next (l))
127     {
128       TpAccount *account = l->data;
129       GHashTable *params;
130       gint priority;
131
132       if (tp_strdiff (tp_account_get_protocol_name (account), "jabber"))
133         continue;
134
135       params = (GHashTable *) tp_account_get_parameters (account);
136       if (params == NULL)
137         continue;
138
139       priority = tp_asv_get_int32 (params, "priority", NULL);
140       if (priority >= 0)
141         continue;
142
143       DEBUG ("Resetting XMPP priority of account '%s' to 0",
144           tp_account_get_path_suffix (account));
145
146       params = tp_asv_new (
147           "priority", G_TYPE_INT, 0,
148           NULL);
149
150       tp_account_update_parameters_async (account, params, NULL,
151           account_update_parameters_cb, NULL);
152
153       g_hash_table_unref (params);
154     }
155
156   g_list_free_full (accounts, g_object_unref);
157 }
158
159 static void
160 set_facebook_account_fallback_server (TpAccountManager *am)
161 {
162   GList *accounts, *l;
163
164   accounts = tp_account_manager_dup_valid_accounts (am);
165   for (l = accounts; l != NULL; l = g_list_next (l))
166     {
167       TpAccount *account = l->data;
168       GHashTable *params;
169       gchar *fallback_servers[] = {
170           "chat.facebook.com:443",
171           NULL };
172
173       if (tp_strdiff (tp_account_get_service (account), "facebook"))
174         continue;
175
176       params = (GHashTable *) tp_account_get_parameters (account);
177       if (params == NULL)
178         continue;
179
180       if (tp_asv_get_strv (params, "fallback-servers") != NULL)
181         continue;
182
183       DEBUG ("Setting chat.facebook.com:443 as a fallback on account '%s'",
184           tp_account_get_path_suffix (account));
185
186       params = tp_asv_new (
187           "fallback-servers", G_TYPE_STRV, fallback_servers,
188           NULL);
189
190       tp_account_update_parameters_async (account, params, NULL,
191           account_update_parameters_cb, NULL);
192
193       g_hash_table_unref (params);
194     }
195
196   g_list_free_full (accounts, g_object_unref);
197 }
198
199 static void
200 upgrade_chat_theme_settings (void)
201 {
202   GSettings *gsettings_chat;
203   gchar *theme, *new_theme = NULL;
204   const char *variant = "";
205
206   gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
207
208   theme = g_settings_get_string (gsettings_chat,
209       EMPATHY_PREFS_CHAT_THEME);
210
211   if (!tp_strdiff (theme, "adium")) {
212     gchar *path;
213
214     path = g_settings_get_string (gsettings_chat,
215         EMPATHY_PREFS_CHAT_ADIUM_PATH);
216
217     new_theme = empathy_theme_manager_dup_theme_name_from_path (path);
218     if (new_theme == NULL)
219       {
220         /* Use the Classic theme as fallback */
221         new_theme = g_strdup ("Classic");
222       }
223
224     g_free (path);
225   } else if (!tp_strdiff (theme, "gnome")) {
226     new_theme = g_strdup ("PlanetGNOME");
227   } else if (!tp_strdiff (theme, "simple")) {
228     new_theme = g_strdup ("Boxes");
229     variant = "Simple";
230   } else if (!tp_strdiff (theme, "clean")) {
231     new_theme = g_strdup ("Boxes");
232     variant = "Clean";
233   } else if (!tp_strdiff (theme, "blue")) {
234     new_theme = g_strdup ("Boxes");
235     variant = "Blue";
236   } else {
237     /* Assume that's an Adium theme name. The theme manager will fallback to
238      * 'Classic' if it can't find it. */
239     goto finally;
240   }
241
242   DEBUG ("Migrating to '%s' variant '%s'", new_theme, variant);
243
244   g_settings_set_string (gsettings_chat,
245     EMPATHY_PREFS_CHAT_THEME, new_theme);
246   g_settings_set_string (gsettings_chat,
247     EMPATHY_PREFS_CHAT_THEME_VARIANT, variant);
248
249 finally:
250   g_free (theme);
251   g_free (new_theme);
252   g_object_unref (gsettings_chat);
253 }
254
255 #ifdef HAVE_UOA
256 typedef struct
257 {
258   TpAccount *new_account;
259   TpAccount *old_account;
260   gboolean enabled;
261 } UoaMigrationData;
262
263 static UoaMigrationData *
264 uoa_migration_data_new (TpAccount *account)
265 {
266   UoaMigrationData *data;
267
268   data = g_slice_new0 (UoaMigrationData);
269   data->old_account = g_object_ref (account);
270   data->enabled = tp_account_is_enabled (account);
271
272   return data;
273 }
274
275 static void
276 uoa_migration_data_free (UoaMigrationData *data)
277 {
278   g_clear_object (&data->new_account);
279   g_clear_object (&data->old_account);
280   g_slice_free (UoaMigrationData, data);
281 }
282
283 #define DATA_SANITY_CTX "data-sanity-ctx"
284
285 static void
286 uoa_account_remove_cb (GObject *source,
287     GAsyncResult *result,
288     gpointer user_data)
289 {
290   TpAccount *account = TP_ACCOUNT (source);
291   GError *error = NULL;
292
293   if (!tp_account_remove_finish (account, result, &error))
294     {
295       DEBUG ("Failed to remove account '%s': %s",
296           tp_account_get_path_suffix (account), error->message);
297       g_error_free (error);
298     }
299
300   g_object_set_data (G_OBJECT (account), DATA_SANITY_CTX, NULL);
301 }
302
303 static void
304 uoa_migration_done (UoaMigrationData *data)
305 {
306   tp_account_remove_async (data->old_account, uoa_account_remove_cb, NULL);
307
308   if (data->new_account != NULL)
309     tp_account_set_enabled_async (data->new_account, data->enabled, NULL, NULL);
310
311   uoa_migration_data_free (data);
312 }
313
314 static void
315 uoa_set_account_password_cb (GObject *source,
316     GAsyncResult *result,
317     gpointer user_data)
318 {
319   UoaMigrationData *data = user_data;
320   GError *error = NULL;
321
322   if (!empathy_keyring_set_account_password_finish (data->new_account, result,
323           &error))
324     {
325       DEBUG ("Error setting old account's password on the new one: %s\n",
326           error->message);
327       g_clear_error (&error);
328     }
329
330   uoa_migration_done (data);
331 }
332
333 static void
334 uoa_get_account_password_cb (GObject *source,
335     GAsyncResult *result,
336     gpointer user_data)
337 {
338   UoaMigrationData *data = user_data;
339   const gchar *password;
340   GError *error = NULL;
341
342   password = empathy_keyring_get_account_password_finish (data->old_account,
343       result, &error);
344   if (password == NULL)
345     {
346       DEBUG ("Error getting old account's password: %s\n", error->message);
347       g_clear_error (&error);
348
349       uoa_migration_done (data);
350     }
351   else
352     {
353       empathy_keyring_set_account_password_async (data->new_account, password,
354           TRUE, uoa_set_account_password_cb, data);
355     }
356 }
357
358 static void
359 uoa_account_created_cb (GObject *source,
360     GAsyncResult *result,
361     gpointer user_data)
362 {
363   TpAccountRequest *ar = (TpAccountRequest *) source;
364   UoaMigrationData *data = user_data;
365   GError *error = NULL;
366
367   data->new_account = tp_account_request_create_account_finish (ar, result,
368       &error);
369   if (data->new_account == NULL)
370     {
371       DEBUG ("Failed to migrate account '%s' to UOA: %s",
372           tp_account_get_path_suffix (data->old_account), error->message);
373       g_clear_error (&error);
374
375       uoa_migration_done (data);
376     }
377   else
378     {
379       DEBUG ("New account %s created to superseed %s",
380           tp_account_get_path_suffix (data->new_account),
381           tp_account_get_path_suffix (data->old_account));
382
383       /* Migrate password as well */
384       empathy_keyring_get_account_password_async (data->old_account,
385           uoa_get_account_password_cb, data);
386     }
387 }
388
389 static void
390 migrate_account_to_uoa (TpAccountManager *am,
391     TpAccount *account)
392 {
393   TpAccountRequest *ar;
394   GVariant *params;
395   GVariant *param;
396   GVariantIter iter;
397   const gchar * const *supersedes;
398   guint i;
399   UoaMigrationData *data;
400
401   DEBUG ("Migrating account %s to UOA storage\n",
402       tp_account_get_path_suffix (account));
403
404   ar = tp_account_request_new (am,
405       tp_account_get_cm_name (account),
406       tp_account_get_protocol_name (account),
407       tp_account_get_display_name (account));
408   tp_account_request_set_storage_provider (ar, EMPATHY_UOA_PROVIDER);
409   tp_account_request_set_icon_name (ar,
410       tp_account_get_icon_name (account));
411   tp_account_request_set_nickname (ar,
412       tp_account_get_nickname (account));
413   tp_account_request_set_service (ar,
414       tp_account_get_service (account));
415
416   /* Do not enable the new account until we imported the password as well */
417   tp_account_request_set_enabled (ar, FALSE);
418
419   supersedes = tp_account_get_supersedes (account);
420
421   for (i = 0; supersedes[i] != NULL; i++)
422     tp_account_request_add_supersedes (ar, supersedes[i]);
423
424   tp_account_request_add_supersedes (ar,
425       tp_proxy_get_object_path (account));
426
427   params = tp_account_dup_parameters_vardict (account);
428   g_variant_iter_init (&iter, params);
429   while ((param = g_variant_iter_next_value (&iter)))
430     {
431       GVariant *k, *v;
432       const gchar *key;
433
434       k = g_variant_get_child_value (param, 0);
435       key = g_variant_get_string (k, NULL);
436       v = g_variant_get_child_value (param, 1);
437
438       tp_account_request_set_parameter (ar, key,
439           g_variant_get_variant (v));
440
441       g_variant_unref (k);
442       g_variant_unref (v);
443     }
444
445   data = uoa_migration_data_new (account);
446   tp_account_set_enabled_async (account, FALSE, NULL, NULL);
447   tp_account_request_create_account_async (ar, uoa_account_created_cb,
448       data);
449
450   g_variant_unref (params);
451   g_object_unref (ar);
452 }
453
454 static void
455 uoa_plugin_install_cb (GObject *source,
456     GAsyncResult *result,
457     gpointer user_data)
458 {
459   TpAccount *account = user_data;
460   GError *error = NULL;
461   TpAccountManager *am;
462
463   if (!empathy_pkg_kit_install_packages_finish (result, &error))
464     {
465       DEBUG ("Failed to install plugin for account '%s' (%s); remove it",
466           tp_account_get_path_suffix (account), error->message);
467
468       g_error_free (error);
469
470       tp_account_remove_async (account, uoa_account_remove_cb, NULL);
471       goto out;
472     }
473
474   DEBUG ("Plugin for account '%s' has been installed; migrate account",
475       tp_account_get_path_suffix (account));
476
477   am = tp_account_manager_dup ();
478   migrate_account_to_uoa (am, account);
479   g_object_unref (am);
480
481 out:
482   g_object_unref (account);
483 }
484
485 static gchar *
486 dup_plugin_name_for_protocol (const gchar *protocol)
487 {
488   if (!tp_strdiff (protocol, "local-xmpp"))
489     return g_strdup ("account-plugin-salut");
490
491   return g_strdup_printf ("account-plugin-%s", protocol);
492 }
493
494 static gboolean
495 uoa_plugin_installed (AgManager *manager,
496     TpAccount *account)
497 {
498   AgAccount *ag_account;
499   const gchar *protocol;
500   GList *l;
501
502   protocol = tp_account_get_protocol_name (account);
503   ag_account = ag_manager_create_account (manager, protocol);
504
505   l = ag_account_list_services_by_type (ag_account, EMPATHY_UOA_SERVICE_TYPE);
506   if (l == NULL)
507     {
508       const gchar *packages[2];
509       gchar *pkg;
510
511       pkg = dup_plugin_name_for_protocol (protocol);
512
513       DEBUG ("%s is not installed; try to install it", pkg);
514
515       packages[0] = pkg;
516       packages[1] = NULL;
517
518       empathy_pkg_kit_install_packages_async (0, packages, NULL,
519           NULL, uoa_plugin_install_cb, g_object_ref (account));
520
521       g_free (pkg);
522       g_object_unref (ag_account);
523       return FALSE;
524     }
525
526   ag_service_list_free (l);
527
528   g_object_unref (ag_account);
529   return TRUE;
530 }
531
532 static void
533 migrate_accounts_to_uoa (SanityCtx *ctx)
534 {
535   GList *accounts, *l;
536   AgManager *manager;
537
538   DEBUG ("Start migrating accounts to UOA");
539
540   manager = empathy_uoa_manager_dup ();
541
542   accounts = tp_account_manager_dup_valid_accounts (ctx->am);
543   for (l = accounts; l != NULL; l = g_list_next (l))
544     {
545       TpAccount *account = l->data;
546
547       /* If account is already in a specific storage (like UOA or GOA),
548        * don't migrate it.
549        * Note that we cannot migrate GOA accounts anyway, since we can't delete
550        * them it would create duplicated accounts. */
551       if (!tp_str_empty (tp_account_get_storage_provider (account)))
552         continue;
553
554       g_object_set_data_full (G_OBJECT (account), DATA_SANITY_CTX,
555           sanity_ctx_ref (ctx), (GDestroyNotify) sanity_ctx_unref);
556
557       /* Try to install the plugin if it's missing */
558       if (!uoa_plugin_installed (manager, account))
559         continue;
560
561       migrate_account_to_uoa (ctx->am, account);
562     }
563
564   g_list_free_full (accounts, g_object_unref);
565
566   g_object_unref (manager);
567 }
568 #endif
569
570 static void
571 run_sanity_cleaning_tasks (SanityCtx *ctx)
572 {
573   DEBUG ("Starting sanity cleaning tasks");
574
575   fix_xmpp_account_priority (ctx->am);
576   set_facebook_account_fallback_server (ctx->am);
577   upgrade_chat_theme_settings ();
578 #ifdef HAVE_UOA
579   migrate_accounts_to_uoa (ctx);
580 #endif
581 }
582
583 static void
584 am_prepare_cb (GObject *source,
585     GAsyncResult *result,
586     gpointer user_data)
587 {
588   GError *error = NULL;
589   TpAccountManager *am = TP_ACCOUNT_MANAGER (source);
590   SanityCtx *ctx = user_data;
591
592   if (!tp_proxy_prepare_finish (am, result, &error))
593     {
594       DEBUG ("Failed to prepare account manager: %s", error->message);
595       g_simple_async_result_take_error (ctx->result, error);
596       goto out;
597     }
598
599   run_sanity_cleaning_tasks (ctx);
600
601 out:
602   sanity_ctx_unref (ctx);
603 }
604
605 void
606 empathy_sanity_checking_run_async (GAsyncReadyCallback callback,
607     gpointer user_data)
608 {
609   GSettings *settings;
610   guint number;
611   TpAccountManager *am;
612   GSimpleAsyncResult *result;
613   SanityCtx *ctx;
614
615   result = g_simple_async_result_new (NULL, callback, user_data,
616       empathy_sanity_checking_run_async);
617
618   settings = g_settings_new (EMPATHY_PREFS_SCHEMA);
619   number = g_settings_get_uint (settings, EMPATHY_PREFS_SANITY_CLEANING_NUMBER);
620
621   if (number == SANITY_CLEANING_NUMBER)
622     {
623       g_simple_async_result_complete_in_idle (result);
624       goto out;
625     }
626
627   am = tp_account_manager_dup ();
628
629   ctx = sanity_ctx_new (am, result);
630   tp_proxy_prepare_async (am, NULL, am_prepare_cb, ctx);
631
632   g_settings_set_uint (settings, EMPATHY_PREFS_SANITY_CLEANING_NUMBER,
633       SANITY_CLEANING_NUMBER);
634
635   g_object_unref (am);
636
637 out:
638   g_object_unref (settings);
639   g_object_unref (result);
640 }
641
642 gboolean
643 empathy_sanity_checking_run_finish (GAsyncResult *result,
644     GError **error)
645 {
646   g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
647         empathy_sanity_checking_run_async), FALSE);
648
649   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
650         error))
651     return FALSE;
652
653   return TRUE;
654 }