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