]> git.0d.be Git - empathy.git/blob - src/empathy-import-dialog.c
Renamed import_dialog_add_accounts to import_dialog_add_accounts_to_model to avoid...
[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_DATA,
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
330           /* Username and IRC server. */
331           else if (strcmp ((gchar *) child->name,
332               PIDGIN_ACCOUNT_TAG_NAME) == 0)
333             {
334               gchar *name;
335               GStrv name_resource = NULL;
336               GStrv nick_server = NULL;
337               const gchar *username;
338
339               name = (gchar *) xmlNodeGetContent (child);
340
341               /* Split "username/resource" */
342               if (g_strrstr (name, "/") != NULL)
343                 {
344                   name_resource = g_strsplit (name, "/", 2);
345                   username = name_resource[0];
346                 }
347               else
348                 username = name;
349
350              /* Split "username@server" if it is an IRC account */
351              if (data->protocol && strstr (name, "@") &&
352                  strcmp (data->protocol, "irc") == 0)
353               {
354                 nick_server = g_strsplit (name, "@", 2);
355                 username = nick_server[0];
356
357                 /* Add the server setting */
358                 value = tp_g_value_slice_new (G_TYPE_STRING);
359                 g_value_set_string (value, nick_server[1]);
360                 g_hash_table_insert (data->settings, "server", value);
361               }
362
363               /* Add the account setting */
364               value = tp_g_value_slice_new (G_TYPE_STRING);
365               g_value_set_string (value, username);
366               g_hash_table_insert (data->settings, "account", value);
367
368               g_strfreev (name_resource);
369               g_strfreev (nick_server);
370               g_free (name);
371             }
372
373           /* Password */
374           else if (strcmp ((gchar *) child->name,
375               PIDGIN_ACCOUNT_TAG_PASSWORD) == 0)
376             {
377               gchar *password;
378
379               password = (gchar *) xmlNodeGetContent (child);
380
381               /* Add the password setting */
382               value = tp_g_value_slice_new (G_TYPE_STRING);
383               g_value_set_string (value, password);
384               g_hash_table_insert (data->settings, "password", value);
385
386               g_free (password);
387             }
388
389           /* Other settings */
390           else if (strcmp ((gchar *) child->name,
391               PIDGIN_ACCOUNT_TAG_SETTINGS) == 0)
392               for (setting = child->children; setting; setting = setting->next)
393                 import_dialog_pidgin_parse_setting (data, setting);
394         }
395
396       /* If we have the needed settings, add the account data to the list,
397        * otherwise free the data */
398       if (data->protocol && g_hash_table_size (data->settings) > 0)
399         accounts = g_list_prepend (accounts, data);
400       else
401         import_dialog_account_data_free (data);
402     }
403
404 OUT:
405   xmlFreeDoc(doc);
406   xmlFreeParserCtxt (ctxt);
407
408   return accounts;
409 }
410
411 static void
412 import_dialog_button_ok_clicked_cb (GtkButton *button,
413                                     EmpathyImportDialog *dialog)
414 {
415   if (FALSE)
416     import_dialog_pidgin_import_accounts ();
417
418   DEBUG ("ok clicked");
419
420   gtk_widget_hide (GTK_WIDGET (dialog->window));
421 }
422
423 static void
424 import_dialog_button_cancel_clicked_cb (GtkButton *button,
425                                         EmpathyImportDialog *dialog)
426 {
427   gtk_widget_hide (GTK_WIDGET (dialog->window));
428 }
429
430 static void
431 import_dialog_add_accounts_to_model (EmpathyImportDialog *dialog)
432 {
433   GtkTreeModel *model;
434   GtkTreeIter iter;
435   GList *accounts, *account;
436
437   model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview));
438
439   accounts = import_dialog_pidgin_load ();
440
441   for (account = accounts; account; account = account->next)
442     {
443       GValue *value, *account_data;
444       AccountData *data = (AccountData *) account->data;
445
446       account_data = tp_g_value_slice_new (G_TYPE_POINTER);
447       g_value_set_pointer (account_data, data);
448
449       value = g_hash_table_lookup (data->settings, "account");
450
451       gtk_list_store_append (GTK_LIST_STORE (model), &iter);
452
453       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
454           COL_IMPORT, TRUE,
455           COL_PROTOCOL, data->protocol,
456           COL_NAME, g_value_get_string (value),
457           COL_SOURCE, "Pidgin",
458           COL_ACCOUNT_DATA, account_data,
459           -1);
460     }
461
462   g_list_free (accounts);
463 }
464
465 static void
466 import_dialog_cell_toggled_cb (GtkCellRendererToggle *cell_renderer,
467                                const gchar *path_str,
468                                EmpathyImportDialog *dialog)
469 {
470   GtkTreeModel *model;
471   GtkTreeIter iter;
472   GtkTreePath *path;
473
474   path = gtk_tree_path_new_from_string (path_str);
475   model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview));
476
477   gtk_tree_model_get_iter (model, &iter, path);
478
479   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
480       COL_IMPORT, !gtk_cell_renderer_toggle_get_active (cell_renderer),
481       -1);
482
483   gtk_tree_path_free (path);
484 }
485
486 static void
487 import_dialog_set_up_account_list (EmpathyImportDialog *dialog)
488 {
489   GtkListStore *store;
490   GtkTreeView *view;
491   GtkTreeViewColumn *column;
492   GtkCellRenderer *cell;
493
494   store = gtk_list_store_new (COL_COUNT, G_TYPE_BOOLEAN, G_TYPE_STRING,
495       G_TYPE_STRING, G_TYPE_STRING, G_TYPE_VALUE);
496
497   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
498       GTK_TREE_MODEL (store));
499
500   g_object_unref (store);
501
502   view = GTK_TREE_VIEW (dialog->treeview);
503   gtk_tree_view_set_headers_visible (view, TRUE);
504
505   /* Import column */
506   cell = gtk_cell_renderer_toggle_new ();
507   gtk_tree_view_insert_column_with_attributes (view, -1,
508       _("Import"), cell,
509       "active", COL_IMPORT,
510       NULL);
511
512   g_signal_connect (cell, "toggled",
513       G_CALLBACK (import_dialog_cell_toggled_cb), dialog);
514
515   /* Protocol column */
516   column = gtk_tree_view_column_new ();
517   gtk_tree_view_column_set_title (column, _("Protocol"));
518   gtk_tree_view_column_set_expand (column, TRUE);
519   gtk_tree_view_append_column (view, column);
520
521   cell = gtk_cell_renderer_text_new ();
522   g_object_set (cell,
523       "editable", FALSE,
524       NULL);
525   gtk_tree_view_column_pack_start (column, cell, TRUE);
526   gtk_tree_view_column_add_attribute (column, cell, "text", COL_PROTOCOL);
527
528   /* Account column */
529   column = gtk_tree_view_column_new ();
530   gtk_tree_view_column_set_title (column, _("Account"));
531   gtk_tree_view_column_set_expand (column, TRUE);
532   gtk_tree_view_append_column (view, column);
533
534   cell = gtk_cell_renderer_text_new ();
535   g_object_set (cell,
536       "editable", FALSE,
537       NULL);
538   gtk_tree_view_column_pack_start (column, cell, TRUE);
539   gtk_tree_view_column_add_attribute (column, cell, "text", COL_NAME);
540
541   /* Source column */
542   column = gtk_tree_view_column_new ();
543   gtk_tree_view_column_set_title (column, _("Source"));
544   gtk_tree_view_column_set_expand (column, TRUE);
545   gtk_tree_view_append_column (view, column);
546
547   cell = gtk_cell_renderer_text_new ();
548   g_object_set (cell,
549       "editable", FALSE,
550       NULL);
551   gtk_tree_view_column_pack_start (column, cell, TRUE);
552   gtk_tree_view_column_add_attribute (column, cell, "text", COL_SOURCE);
553
554   import_dialog_add_accounts_to_model (dialog);
555 }
556
557 void
558 empathy_import_dialog_show (GtkWindow *parent)
559 {
560   static EmpathyImportDialog *dialog = NULL;
561   GladeXML *glade;
562   gchar *filename;
563
564   if (dialog)
565     {
566       gtk_window_present (GTK_WINDOW (dialog->window));
567       return;
568     }
569
570   dialog = g_slice_new0 (EmpathyImportDialog);
571
572   filename = empathy_file_lookup ("empathy-import-dialog.glade", "src");
573   glade = empathy_glade_get_file (filename,
574       "import_dialog",
575       NULL,
576       "import_dialog", &dialog->window,
577       "treeview", &dialog->treeview,
578       NULL);
579
580   empathy_glade_connect (glade,
581       dialog,
582       "button_ok", "clicked", import_dialog_button_ok_clicked_cb,
583       "button_cancel", "clicked", import_dialog_button_cancel_clicked_cb,
584       NULL);
585
586   import_dialog_set_up_account_list (dialog);
587
588   g_object_add_weak_pointer (G_OBJECT (dialog->window), (gpointer) &dialog);
589
590   g_free (filename);
591   g_object_unref (glade);
592
593   if (parent)
594     gtk_window_set_transient_for (GTK_WINDOW (dialog->window), parent);
595
596   gtk_widget_show (dialog->window);
597 }