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