]> git.0d.be Git - empathy.git/blob - src/empathy-import-mc4-accounts.c
Fix use after free
[empathy.git] / src / empathy-import-mc4-accounts.c
1 /*
2  * Copyright (C) 2009 Collabora Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program 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  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA  02110-1301  USA
18  *
19  * Authors: Arnaud Maillet <arnaud.maillet@collabora.co.uk>
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <glib.h>
25 #include <glib/gi18n.h>
26 #include <gconf/gconf-client.h>
27 #include <telepathy-glib/util.h>
28 #include <telepathy-glib/defs.h>
29 #include <dbus/dbus-protocol.h>
30 #include <gnome-keyring.h>
31 #include <libempathy/empathy-account-manager.h>
32 #include <libempathy/empathy-account-settings.h>
33 #include <libempathy/empathy-connection-managers.h>
34
35 #include "empathy-import-mc4-accounts.h"
36
37 #define DEBUG_FLAG EMPATHY_DEBUG_IMPORT_MC4_ACCOUNTS
38 #include <libempathy/empathy-debug.h>
39
40 #define MC_ACCOUNTS_GCONF_BASE "/apps/telepathy/mc/accounts"
41 #define IMPORTED_MC4_ACCOUNTS "/apps/empathy/accounts/imported_mc4_accounts"
42
43 typedef struct
44 {
45   gchar *profile;
46   gchar *protocol;
47 } ProfileProtocolMapItem;
48
49 static ProfileProtocolMapItem profile_protocol_map[] =
50 {
51   { "ekiga", "sip" },
52   { "fwd", "sip" },
53   { "gtalk", "jabber" },
54   { "msn-haze", "msn" },
55   { "salut", "local-xmpp" },
56   { "sipphone", "sip" },
57   { "sofiasip", "sip" },
58 };
59
60 typedef struct {
61   gchar *account_name;
62   gboolean enable;
63 } Misc;
64
65 static gchar *
66 _account_name_from_key (const gchar *key)
67 {
68   guint base_len = strlen (MC_ACCOUNTS_GCONF_BASE);
69   const gchar *base, *slash;
70
71   g_assert (g_str_has_prefix (key, MC_ACCOUNTS_GCONF_BASE));
72   g_assert (strlen (key) > base_len + 1);
73
74   base = key + base_len + 1;
75   slash = strchr (base, '/');
76
77   if (slash == NULL)
78     return g_strdup (base);
79   else
80     return g_strndup (base, slash - base);
81 }
82
83 static gchar *
84 _param_name_from_key (const gchar *key)
85 {
86  const gchar *base, *slash;
87  gchar *account_name;
88  gchar *ret;
89
90  account_name = _account_name_from_key (key);
91  base = strstr (key, account_name);
92  slash = strchr (base, '/');
93
94  ret = g_strdup (slash+1);
95  g_free (account_name);
96
97  return ret;
98 }
99
100 static gchar *
101 _create_default_display_name (const gchar *protocol)
102 {
103   if (!tp_strdiff (protocol, "local-xmpp"))
104     return g_strdup (_("People Nearby"));
105
106   return g_strdup_printf ("%s account", protocol);
107 }
108
109 static const gchar *
110 _get_manager_for_protocol (EmpathyConnectionManagers *managers,
111     const gchar *protocol)
112 {
113   GList *cms = empathy_connection_managers_get_cms (managers);
114   GList *l;
115   TpConnectionManager *haze = NULL;
116   TpConnectionManager *cm = NULL;
117
118   for (l = cms; l; l = l->next)
119     {
120       TpConnectionManager *tp_cm = l->data;
121
122       /* Only use haze if no other cm provides this protocol */
123       if (!tp_strdiff (tp_connection_manager_get_name (tp_cm), "haze"))
124         {
125           haze = tp_cm;
126           continue;
127         }
128
129       if (tp_connection_manager_has_protocol (tp_cm, protocol))
130         {
131           cm = tp_cm;
132           goto out;
133         }
134     }
135
136   if (haze != NULL && tp_connection_manager_has_protocol (haze, protocol))
137     return tp_connection_manager_get_name (haze);
138
139 out:
140   return cm != NULL ? tp_connection_manager_get_name (cm) : NULL;
141 }
142
143 static void
144 _move_contents (const gchar *old, const gchar *new)
145 {
146   GDir *source;
147   const gchar *f;
148   int ret;
149
150   ret = g_mkdir_with_parents (new, 0777);
151   if (ret == -1)
152     return;
153
154   source = g_dir_open (old, 0, NULL);
155   if (source == NULL)
156     return;
157
158   while ((f = g_dir_read_name (source)) != NULL)
159     {
160       gchar *old_path;
161       gchar *new_path;
162
163       old_path = g_build_path (G_DIR_SEPARATOR_S, old, f, NULL);
164       new_path = g_build_path (G_DIR_SEPARATOR_S, new, f, NULL);
165
166       if (g_file_test (old_path, G_FILE_TEST_IS_DIR))
167         {
168           _move_contents (old_path, new_path);
169         }
170       else
171         {
172           GFile *f_old, *f_new;
173
174           f_old = g_file_new_for_path (old_path);
175           f_new = g_file_new_for_path (new_path);
176
177           g_file_move (f_old, f_new, G_FILE_COPY_NONE,
178             NULL, NULL, NULL, NULL);
179
180           g_object_unref (f_old);
181           g_object_unref (f_new);
182         }
183
184       g_free (old_path);
185       g_free (new_path);
186     }
187
188   g_dir_close (source);
189 }
190
191 static void
192 _move_logs (EmpathyAccount *account, const gchar *account_name)
193 {
194   gchar *old_path, *new_path, *escaped;
195   const gchar *name;
196
197   name = empathy_account_get_unique_name (account);
198   if (g_str_has_prefix (name, TP_ACCOUNT_OBJECT_PATH_BASE))
199     name += strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
200
201   escaped = g_strdelimit (g_strdup (name), "/", '_');
202   new_path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (),
203     PACKAGE_NAME, "logs", escaped, NULL);
204   g_free (escaped);
205
206   old_path = g_build_path (G_DIR_SEPARATOR_S,
207     g_get_home_dir (),
208     ".gnome2", PACKAGE_NAME, "logs", account_name, NULL);
209
210   _move_contents (old_path, new_path);
211 }
212
213 static void
214 _create_account_cb (GObject *source,
215   GAsyncResult *result,
216   gpointer user_data)
217 {
218   EmpathyAccount *account;
219   GError *error = NULL;
220   Misc *misc = (Misc *)user_data;
221
222   if (!empathy_account_settings_apply_finish (
223       EMPATHY_ACCOUNT_SETTINGS (source), result, &error))
224     {
225       DEBUG ("Failed to create account: %s",
226           error ? error->message : "No error given");
227       g_error_free (error);
228       goto out;
229     }
230
231   DEBUG ("account created\n");
232   account = empathy_account_settings_get_account (
233     EMPATHY_ACCOUNT_SETTINGS (source));
234
235   _move_logs (account, misc->account_name);
236
237   empathy_account_set_enabled_async (account,
238       misc->enable, NULL, NULL);
239
240   g_free (misc->account_name);
241   g_slice_free (Misc, misc);
242
243 out:
244   g_object_unref (source);
245 }
246
247 static gchar *
248 _get_protocol_from_profile (const gchar *profile)
249 {
250   gint i;
251
252   DEBUG ("profile: %s\n", profile);
253
254   for (i = 0; i < G_N_ELEMENTS (profile_protocol_map); i++)
255     if (!tp_strdiff (profile, profile_protocol_map[i].profile))
256       return g_strdup (profile_protocol_map[i].protocol);
257
258   return g_strdup (profile);
259 }
260
261 static void
262 _set_password_from_keyring (EmpathyAccountSettings *settings,
263     const gchar *account_name, const gchar *key)
264 {
265   GnomeKeyringResult res;
266   gchar *password;
267   GnomeKeyringPasswordSchema keyring_schema = {
268       GNOME_KEYRING_ITEM_GENERIC_SECRET,
269       {
270         { "account", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
271         { "param", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
272         { NULL, 0 }
273       }
274    };
275
276   res = gnome_keyring_find_password_sync (&keyring_schema,
277     &password,
278     "account", account_name,
279     "param", key,
280     NULL);
281
282   if (res == GNOME_KEYRING_RESULT_OK)
283     {
284        empathy_account_settings_set_string (settings, key, password);
285        gnome_keyring_free_password (password);
286     }
287 }
288
289 static void
290 _handle_entry (EmpathyAccountSettings *settings, const gchar *account_name,
291     const gchar *key,
292     GConfEntry *entry)
293 {
294   const gchar *signature;
295
296   signature = empathy_account_settings_get_dbus_signature (settings, key);
297   if (signature == NULL)
298     {
299       DEBUG ("Parameter %s is unknown", signature);
300       return;
301     }
302
303   switch ((int)*signature)
304     {
305       case DBUS_TYPE_INT16:
306       case DBUS_TYPE_INT32:
307         {
308           gint v = gconf_value_get_int (gconf_entry_get_value (entry));
309           empathy_account_settings_set_int32 (settings, key, v);
310           break;
311         }
312       case DBUS_TYPE_UINT16:
313       case DBUS_TYPE_UINT32:
314         {
315           gint v = gconf_value_get_int (gconf_entry_get_value (entry));
316           empathy_account_settings_set_uint32 (settings, key, v);
317           break;
318         }
319       case DBUS_TYPE_STRING:
320         {
321           const gchar *v = gconf_value_get_string (
322               gconf_entry_get_value (entry));
323
324           /* MC 4 would put password in the keyring and leave the password in
325            * gconf keyring */
326
327           if (!tp_strdiff (key, "password") && !tp_strdiff (v, "keyring"))
328             _set_password_from_keyring (settings, account_name, key);
329           else
330               empathy_account_settings_set_string (settings, key, v);
331           break;
332         }
333       case DBUS_TYPE_BOOLEAN:
334         {
335           gboolean v = gconf_value_get_bool (
336               gconf_entry_get_value (entry));
337
338           empathy_account_settings_set_boolean (settings, key, v);
339           break;
340         }
341      default:
342        DEBUG ("Unsupported type in signature: %s", signature);
343     }
344 }
345
346 static void
347 _recurse_account (GSList *entries, EmpathyAccountSettings *settings,
348   const gchar *account_name)
349 {
350   GSList *tmp;
351
352   for (tmp = entries; tmp != NULL; tmp = tmp->next)
353     {
354
355       GConfEntry *entry;
356       gchar *param;
357
358       entry = (GConfEntry*) tmp->data;
359       param = _param_name_from_key (gconf_entry_get_key (entry));
360
361       if (g_str_has_prefix (param, "param-"))
362         {
363           _handle_entry (settings, account_name, param + strlen ("param-"),
364             entry);
365         }
366
367       g_free (param);
368       gconf_entry_unref (entry);
369     }
370 }
371
372 static gboolean
373 import_one_account (const char *path,
374   EmpathyConnectionManagers *managers,
375   GConfClient *client)
376 {
377   gchar *account_name = _account_name_from_key (path);
378   EmpathyAccountSettings *settings;
379   GError *error = NULL;
380   GSList *entries = NULL;
381   gchar *profile = NULL;
382   gchar *protocol = NULL;
383   const gchar *manager;
384   gchar *display_name;
385   gchar *key;
386   gboolean enabled = FALSE;
387   gboolean ret = FALSE;
388   Misc *misc;
389
390   DEBUG ("Starting import of %s (%s)", path, account_name);
391
392   key = g_strdup_printf ("%s/profile", path);
393   profile = gconf_client_get_string (client, key, NULL);
394   g_free (key);
395
396   if (profile == NULL)
397     {
398       DEBUG ("Account is missing a profile entry\n");
399       goto failed;
400     }
401
402   protocol = _get_protocol_from_profile (profile);
403   manager = _get_manager_for_protocol (managers, protocol);
404   if (manager == NULL)
405     {
406       DEBUG ("No manager available for this protocol %s", protocol);
407       goto failed;
408     }
409
410   key = g_strdup_printf ("%s/display_name", path);
411   display_name = gconf_client_get_string (client, key, NULL);
412   g_free (key);
413
414   if (display_name == NULL)
415     display_name = _create_default_display_name (protocol);
416
417   settings = empathy_account_settings_new (manager, protocol, display_name);
418   g_free (display_name);
419
420   /* Bit of a hack, as we know EmpathyConnectionManagers is ready the
421    * EmpathyAccountSettings should be ready right away as well */
422   g_assert (empathy_account_settings_is_ready (settings));
423
424   entries = gconf_client_all_entries (client, path, &error);
425
426   if (entries == NULL)
427     {
428       DEBUG ("Failed to get all entries: %s\n", error->message);
429       g_error_free (error);
430       goto failed;
431     }
432
433   _recurse_account (entries, settings, account_name);
434
435   key = g_strdup_printf ("%s/enabled", path);
436   enabled = gconf_client_get_bool (client, key, NULL);
437   g_free (key);
438
439   misc = g_slice_new (Misc);
440   misc->account_name = account_name;
441   misc->enable = enabled;
442
443   empathy_account_settings_apply_async (settings,
444           _create_account_cb, misc);
445   ret = TRUE;
446
447 out:
448   g_free (protocol);
449   g_free (profile);
450   g_slist_free (entries);
451
452   return ret;
453
454 failed:
455   DEBUG ("Failed to import %s", path);
456   g_free (account_name);
457   if (settings != NULL)
458     g_object_unref (settings);
459   goto out;
460 }
461
462 gboolean
463 empathy_import_mc4_has_imported (void)
464 {
465   GConfClient *client;
466   gboolean ret;
467
468   client = gconf_client_get_default ();
469
470   ret = gconf_client_get_bool (client, IMPORTED_MC4_ACCOUNTS, NULL);
471   g_object_unref (client);
472
473   return ret;
474 }
475
476 gboolean
477 empathy_import_mc4_accounts (EmpathyConnectionManagers *managers)
478 {
479   GConfClient *client;
480   GError *error = NULL;
481   GSList *dir, *dirs = NULL;
482   gboolean imported_mc4_accounts;
483   gboolean imported = FALSE;
484
485   g_return_val_if_fail (empathy_connection_managers_is_ready (managers),
486     FALSE);
487
488   client = gconf_client_get_default ();
489
490   imported_mc4_accounts = gconf_client_get_bool (client,
491       IMPORTED_MC4_ACCOUNTS, &error);
492
493   if (error != NULL)
494     {
495       DEBUG ("Failed to get import_mc4_accounts key: %s\n", error->message);
496       g_error_free (error);
497       goto out;
498     }
499
500   if (imported_mc4_accounts)
501     {
502       DEBUG ("Mc4 accounts previously imported");
503       goto out;
504     }
505
506   DEBUG ("MC 4 accounts are going to be imported\n");
507
508   dirs = gconf_client_all_dirs (client, MC_ACCOUNTS_GCONF_BASE, &error);
509
510   if (error != NULL)
511     {
512       DEBUG ("Failed to get MC4 account dirs: %s\n",
513           error->message);
514       g_clear_error (&error);
515       g_object_unref (client);
516       goto out;
517     }
518
519   for (dir = dirs; NULL != dir; dir = dir->next)
520     {
521       if (import_one_account ((gchar *)dir->data, managers, client))
522         imported = TRUE;
523       g_free (dir->data);
524     }
525
526 out:
527   gconf_client_set_bool (client, IMPORTED_MC4_ACCOUNTS, TRUE, NULL);
528
529   g_slist_free (dirs);
530   g_object_unref (client);
531   return imported;
532 }