]> git.0d.be Git - empathy.git/blob - src/empathy-import-dialog.c
Re-wrote the UI of the importer dialog. (Jonny Lamb)
[empathy.git] / src / empathy-import-dialog.c
1 /*
2  * Copyright (C) 2008 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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Authors: Jonny Lamb <jonny.lamb@collabora.co.uk>
20  * */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <glib.h>
27 #include <gtk/gtk.h>
28 #include <glade/glade.h>
29 #include <glib/gi18n.h>
30
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33
34 #include <libmissioncontrol/mc-account.h>
35 #include <telepathy-glib/util.h>
36
37 #include "empathy-import-dialog.h"
38
39 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
40 #include <libempathy/empathy-debug.h>
41 #include <libempathy/empathy-utils.h>
42
43 #include <libempathy-gtk/empathy-ui-utils.h>
44
45 /* Pidgin to MC map */
46 typedef struct
47 {
48   gchar *protocol;
49   gchar *pidgin_name;
50   gchar *mc_name;
51 } PidginMcMapItem;
52
53 static PidginMcMapItem pidgin_mc_map[] =
54 {
55   { "msn", "server", "server" },
56   { "msn", "port", "port" },
57
58   { "jabber", "connect_server", "server" },
59   { "jabber", "port", "port" },
60   { "jabber", "require_tls", "require-encryption" },
61   { "jabber", "old_ssl", "old-ssl" },
62
63   { "aim", "server", "server" },
64   { "aim", "port", "port" },
65
66   { "salut", "first", "first-name" },
67   { "salut", "last", "last-name" },
68   { "salut", "jid", "jid" },
69   { "salut", "email", "email" },
70
71   { "groupwise", "server", "server" },
72   { "groupwise", "port", "port" },
73
74   { "icq", "server", "server" },
75   { "icq", "port", "port" },
76
77   { "irc", "realname", "fullname" },
78   { "irc", "ssl", "use-ssl" },
79   { "irc", "port", "port" },
80
81   { "yahoo", "server", "server" },
82   { "yahoo", "port", "port" },
83   { "yahoo", "xfer_port", "xfer-port" },
84   { "yahoo", "ignore_invites", "ignore-invites" },
85   { "yahoo", "yahoojp", "yahoojp" },
86   { "yahoo", "xferjp_host", "xferjp-host" },
87   { "yahoo", "serverjp", "serverjp" },
88   { "yahoo", "xfer_host", "xfer-host" },
89 };
90
91 typedef struct
92 {
93   GHashTable *settings;
94   gchar *protocol;
95 } AccountData;
96
97 typedef struct
98 {
99   GtkWidget *window;
100   GtkWidget *treeview;
101   GtkWidget *button_ok;
102   GtkWidget *button_cancel;
103 } EmpathyImportDialog;
104
105 #define PIDGIN_ACCOUNT_TAG_NAME "name"
106 #define PIDGIN_ACCOUNT_TAG_ACCOUNT "account"
107 #define PIDGIN_ACCOUNT_TAG_PROTOCOL "protocol"
108 #define PIDGIN_ACCOUNT_TAG_PASSWORD "password"
109 #define PIDGIN_ACCOUNT_TAG_SETTINGS "settings"
110 #define PIDGIN_SETTING_PROP_TYPE "type"
111 #define PIDGIN_PROTOCOL_BONJOUR "bonjour"
112 #define PIDGIN_PROTOCOL_NOVELL "novell"
113
114 enum
115 {
116   COL_IMPORT = 0,
117   COL_PROTOCOL,
118   COL_NAME,
119   COL_SOURCE,
120   COL_ACCOUNT,
121   COL_COUNT
122 };
123
124
125 static void import_dialog_add_setting (GHashTable *settings,
126     gchar *key, gpointer value, EmpathyImportSettingType  type);
127 static gboolean import_dialog_add_account (gchar *protocol_name,
128     GHashTable *settings);
129 static void import_dialog_pidgin_parse_setting (gchar *protocol,
130     xmlNodePtr setting, GHashTable *settings);
131 static void import_dialog_pidgin_import_accounts ();
132 static void import_dialog_button_ok_clicked_cb (GtkButton *button,
133     EmpathyImportDialog *dialog);
134 static void import_dialog_button_cancel_clicked_cb (GtkButton *button,
135     EmpathyImportDialog *dialog);
136
137 static void
138 import_dialog_account_data_free (AccountData *data)
139 {
140         g_free (data->protocol);
141         g_hash_table_destroy (data->settings);
142 }
143
144 static gboolean
145 import_dialog_add_account (AccountData *data)
146 {
147   McProfile *profile;
148   McAccount *account;
149   GHashTableIter iter;
150   gpointer key, value;
151   gchar *display_name;
152   gchar *username;
153
154   DEBUG ("Looking up profile with protocol '%s'", data->protocol);
155   profile = mc_profile_lookup (data->protocol);
156
157   if (profile == NULL)
158     return FALSE;
159
160   account = mc_account_create (profile);
161
162   g_hash_table_iter_init (&iter, data->settings);
163   while (g_hash_table_iter_next (&iter, &key, &value))
164     {
165       const gchar *param = key;
166       GValue *gvalue = value;
167
168       switch (G_VALUE_TYPE (gvalue))
169         {
170           case G_TYPE_STRING:
171             DEBUG ("Set param '%s' to '%s' (string)",
172                 param, g_value_get_string (gvalue));
173             mc_account_set_param_string (account,
174                 param, g_value_get_string (gvalue));
175             break;
176
177           case G_TYPE_BOOLEAN:
178             DEBUG ("Set param '%s' to %s (boolean)",
179                 param, g_value_get_boolean (gvalue) ? "TRUE" : "FALSE");
180             mc_account_set_param_boolean (account,
181                 param, g_value_get_boolean (gvalue));
182             break;
183
184           case G_TYPE_INT:
185             DEBUG ("Set param '%s' to '%i' (integer)",
186                 param, g_value_get_int (gvalue));
187             mc_account_set_param_int (account,
188                 param, g_value_get_int (gvalue));
189             break;
190         }
191     }
192
193   /* Set the display name of the account */
194   mc_account_get_param_string (account, "account", &username);
195   display_name = g_strdup_printf ("%s (%s)",
196     mc_profile_get_display_name (profile), username);
197   mc_account_set_display_name (account, display_name);
198
199   g_free (username);
200   g_free (display_name);
201   g_object_unref (account);
202   g_object_unref (profile);
203
204   return TRUE;
205 }
206
207 static void
208 import_dialog_pidgin_parse_setting (AccountData *data,
209                                     xmlNodePtr setting)
210 {
211   PidginMcMapItem *item = NULL;
212   gchar *tag_name;
213   gchar *type = NULL;
214   gchar *content;
215   gint i;
216   GValue *value = NULL;
217
218   /* We can't do anything if we didn't discovered the protocol yet */
219   if (!data->protocol)
220     return;
221
222   /* We can't do anything if the setting don't have a name */
223   tag_name = (gchar *) xmlGetProp (setting, PIDGIN_ACCOUNT_TAG_NAME);
224   if (!tag_name)
225     return;
226
227   /* Search for the map corresponding to setting we are parsing */
228   for (i = 0; i < G_N_ELEMENTS (pidgin_mc_map); i++)
229     {
230       if (strcmp (data->protocol, pidgin_mc_map[i].protocol) == 0 &&
231           strcmp (tag_name, pidgin_mc_map[i].pidgin_name) == 0)
232         {
233           item = pidgin_mc_map + i;
234           break;
235         }
236     }
237   g_free (tag_name);
238
239   /* If we didn't find the item, there is nothing we can do */
240   if (!item)
241     return;
242
243   type = (gchar *) xmlGetProp (setting, PIDGIN_SETTING_PROP_TYPE);
244   content = (gchar *) xmlNodeGetContent (setting);
245
246   if (strcmp (type, "bool") == 0)
247     {
248       sscanf (content, "%i", &i);
249       value = tp_g_value_slice_new (G_TYPE_BOOLEAN);
250       g_value_set_boolean (value, i != 0);
251     }
252   else if (strcmp (type, "int") == 0)
253     {
254       sscanf (content, "%i", &i);
255       value = tp_g_value_slice_new (G_TYPE_INT);
256       g_value_set_int (value, i);
257     }
258   else if (strcmp (type, "string") == 0)
259     {
260       value = tp_g_value_slice_new (G_TYPE_STRING);
261       g_value_set_string (value, content);
262     }
263
264   if (value)
265     g_hash_table_insert (data->settings, item->mc_name, value);
266
267   g_free (type);
268   g_free (content);
269 }
270
271 static GList *
272 import_dialog_pidgin_load (void)
273 {
274   xmlNodePtr rootnode, node, child, setting;
275   xmlParserCtxtPtr ctxt;
276   xmlDocPtr doc;
277   gchar *filename;
278   GList *accounts = NULL;
279
280   /* Load pidgin accounts xml */
281   ctxt = xmlNewParserCtxt ();
282   filename = g_build_filename (g_get_home_dir (), ".purple", "accounts.xml",
283       NULL);
284   doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
285   g_free (filename);
286
287   rootnode = xmlDocGetRootElement (doc);
288   if (rootnode == NULL)
289     goto OUT;
290
291   for (node = rootnode->children; node; node = node->next)
292     {
293       AccountData *data;
294
295       /* If it is not an account node, skip */
296       if (strcmp ((gchar *) node->name, PIDGIN_ACCOUNT_TAG_ACCOUNT) != 0)
297         continue;
298
299       /* Create account data struct */
300       data = g_slice_new0 (AccountData);
301       data->settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
302         (GDestroyNotify) tp_g_value_slice_free);
303
304       /* Parse account's child nodes to fill the account data struct */
305       for (child = node->children; child; child = child->next)
306         {
307           GValue *value;
308
309           /* Protocol */
310           if (strcmp ((gchar *) child->name,
311               PIDGIN_ACCOUNT_TAG_PROTOCOL) == 0)
312             {
313               const gchar *protocol;
314               gchar *content;
315
316               protocol = content = (gchar *) xmlNodeGetContent (child);
317
318               if (g_str_has_prefix (protocol, "prpl-"))
319                 protocol += 5;
320
321               if (strcmp (protocol, PIDGIN_PROTOCOL_BONJOUR) == 0)
322                 protocol = "salut";
323               else if (strcmp (protocol, PIDGIN_PROTOCOL_NOVELL) == 0)
324                 protocol = "groupwise";
325
326               data->protocol = g_strdup (protocol);
327               g_free (content);
328             }
329           /* Username and IRC server */
330           else if (strcmp ((gchar *) child->name,
331               PIDGIN_ACCOUNT_TAG_NAME) == 0)
332             {
333               gchar *name;
334               GStrv name_resource = NULL;
335               GStrv nick_server = NULL;
336               const gchar *username;
337
338               name = (gchar *) xmlNodeGetContent (child);
339
340               /* Split "username/resource" */
341               if (g_strrstr (name, "/") != NULL)
342                 {
343                   name_resource = g_strsplit (name, "/", 2);
344                   username = name_resource[0];
345                 }
346               else
347                 username = name;
348
349              /* Split "username@server" if it is an IRC account */
350              if (data->protocol && strstr (name, "@") &&
351                  strcmp (data->protocol, "irc") == 0)
352               {
353                 nick_server = g_strsplit (name, "@", 2);
354                 username = nick_server[0];
355
356                 /* Add the server setting */
357                 value = tp_g_value_slice_new (G_TYPE_STRING);
358                 g_value_set_string (value, nick_server[1]);
359                 g_hash_table_insert (data->settings, "server", value);
360               }
361
362               /* Add the account setting */
363               value = tp_g_value_slice_new (G_TYPE_STRING);
364               g_value_set_string (value, username);
365               g_hash_table_insert (data->settings, "account", value);
366
367               g_strfreev (name_resource);
368               g_strfreev (nick_server);
369               g_free (name);
370             }
371           /* Password */
372           else if (strcmp ((gchar *) child->name,
373               PIDGIN_ACCOUNT_TAG_PASSWORD) == 0)
374             {
375               gchar *password;
376
377               password = (gchar *) xmlNodeGetContent (child);
378
379               /* Add the password setting */
380               value = tp_g_value_slice_new (G_TYPE_STRING);
381               g_value_set_string (value, password);
382               g_hash_table_insert (data->settings, "password", value);
383
384               g_free (password);
385             }
386           /* Other settings */
387           else if (strcmp ((gchar *) child->name,
388               PIDGIN_ACCOUNT_TAG_SETTINGS) == 0)
389               for (setting = child->children; setting; setting = setting->next)
390                 import_dialog_pidgin_parse_setting (data, setting);
391         }
392
393       /* If we have the needed settings, add the account data to the list,
394        * otherwise free the data */
395       if (data->protocol && g_hash_table_size (data->settings) > 0)
396         accounts = g_list_prepend (accounts, data);
397       else
398         import_dialog_account_data_free (data);
399     }
400
401 OUT:
402   xmlFreeDoc(doc);
403   xmlFreeParserCtxt (ctxt);
404
405   return accounts;
406 }
407
408 static void
409 import_dialog_button_ok_clicked_cb (GtkButton *button,
410                                     EmpathyImportDialog *dialog)
411 {
412   if (FALSE)
413     import_dialog_pidgin_import_accounts ();
414
415   DEBUG ("ok clicked");
416
417   gtk_widget_hide (GTK_WIDGET (dialog->window));
418 }
419
420 static void
421 import_dialog_button_cancel_clicked_cb (GtkButton *button,
422                                         EmpathyImportDialog *dialog)
423 {
424   gtk_widget_hide (GTK_WIDGET (dialog->window));
425 }
426
427 static void
428 import_dialog_add_accounts (EmpathyImportDialog *dialog)
429 {
430   GtkTreeModel *model;
431   GtkTreeIter iter;
432   GList *accounts, *account;
433
434   model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview));
435
436   accounts = g_list_alloc ();
437
438   for (account = accounts; account; account = account->next)
439     {
440       /* Add the accounts here. */
441     }
442
443   /* A sample item for testing. */
444   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
445
446   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
447       COL_IMPORT, TRUE,
448       COL_PROTOCOL, "Jabber",
449       COL_NAME, "foo@gmail.com",
450       COL_SOURCE, "Pidgin",
451       COL_ACCOUNT, NULL,
452       -1);
453
454   g_list_free (accounts);
455 }
456
457 static void
458 import_dialog_cell_toggled_cb (GtkCellRendererToggle *cell_renderer,
459                                const gchar *path_str,
460                                EmpathyImportDialog *dialog)
461 {
462   GtkTreeModel *model;
463   GtkTreeIter iter;
464   GtkTreePath *path;
465
466   path = gtk_tree_path_new_from_string (path_str);
467   model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview));
468
469   gtk_tree_model_get_iter (model, &iter, path);
470
471   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
472       COL_IMPORT, !gtk_cell_renderer_toggle_get_active (cell_renderer),
473       -1);
474
475   gtk_tree_path_free (path);
476 }
477
478 static void
479 import_dialog_set_up_account_list (EmpathyImportDialog *dialog)
480 {
481   GtkListStore *store;
482   GtkTreeView *view;
483   GtkTreeViewColumn *column;
484   GtkCellRenderer *cell;
485
486   store = gtk_list_store_new (COL_COUNT, G_TYPE_BOOLEAN, G_TYPE_STRING,
487       G_TYPE_STRING, G_TYPE_STRING, G_TYPE_HASH_TABLE);
488
489   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
490       GTK_TREE_MODEL (store));
491
492   g_object_unref (store);
493
494   view = GTK_TREE_VIEW (dialog->treeview);
495   gtk_tree_view_set_headers_visible (view, TRUE);
496
497   /* Import column */
498   cell = gtk_cell_renderer_toggle_new ();
499   gtk_tree_view_insert_column_with_attributes (view, -1,
500       _("Import"), cell,
501       "active", COL_IMPORT,
502       NULL);
503
504   g_signal_connect (cell, "toggled",
505       G_CALLBACK (import_dialog_cell_toggled_cb), dialog);
506
507   /* Protocol column */
508   column = gtk_tree_view_column_new ();
509   gtk_tree_view_column_set_title (column, _("Protocol"));
510   gtk_tree_view_column_set_expand (column, TRUE);
511   gtk_tree_view_append_column (view, column);
512
513   cell = gtk_cell_renderer_text_new ();
514   g_object_set (cell,
515       "editable", FALSE,
516       NULL);
517   gtk_tree_view_column_pack_start (column, cell, TRUE);
518   gtk_tree_view_column_add_attribute (column, cell, "text", COL_PROTOCOL);
519
520   /* Account column */
521   column = gtk_tree_view_column_new ();
522   gtk_tree_view_column_set_title (column, _("Account"));
523   gtk_tree_view_column_set_expand (column, TRUE);
524   gtk_tree_view_append_column (view, column);
525
526   cell = gtk_cell_renderer_text_new ();
527   g_object_set (cell,
528       "editable", FALSE,
529       NULL);
530   gtk_tree_view_column_pack_start (column, cell, TRUE);
531   gtk_tree_view_column_add_attribute (column, cell, "text", COL_NAME);
532
533   /* Source column */
534   column = gtk_tree_view_column_new ();
535   gtk_tree_view_column_set_title (column, _("Source"));
536   gtk_tree_view_column_set_expand (column, TRUE);
537   gtk_tree_view_append_column (view, column);
538
539   cell = gtk_cell_renderer_text_new ();
540   g_object_set (cell,
541       "editable", FALSE,
542       NULL);
543   gtk_tree_view_column_pack_start (column, cell, TRUE);
544   gtk_tree_view_column_add_attribute (column, cell, "text", COL_SOURCE);
545
546   import_dialog_add_accounts (dialog);
547 }
548
549 void
550 empathy_import_dialog_show (GtkWindow *parent)
551 {
552   static EmpathyImportDialog *dialog = NULL;
553   GladeXML *glade;
554   gchar *filename;
555
556   if (dialog)
557     {
558       gtk_window_present (GTK_WINDOW (dialog->window));
559       return;
560     }
561
562   dialog = g_slice_new0 (EmpathyImportDialog);
563
564   filename = empathy_file_lookup ("empathy-import-dialog.glade", "src");
565   glade = empathy_glade_get_file (filename,
566       "import_dialog",
567       NULL,
568       "import_dialog", &dialog->window,
569       "treeview", &dialog->treeview,
570       NULL);
571
572   empathy_glade_connect (glade,
573       dialog,
574       "button_ok", "clicked", import_dialog_button_ok_clicked_cb,
575       "button_cancel", "clicked", import_dialog_button_cancel_clicked_cb,
576       NULL);
577
578   import_dialog_set_up_account_list (dialog);
579
580   g_object_add_weak_pointer (G_OBJECT (dialog->window), (gpointer) &dialog);
581
582   g_free (filename);
583   g_object_unref (glade);
584
585   if (parent)
586     gtk_window_set_transient_for (GTK_WINDOW (dialog->window), parent);
587
588   gtk_widget_show (dialog->window);
589 }