]> git.0d.be Git - empathy.git/blob - src/empathy-import-pidgin.c
include telepathy-glib.h
[empathy.git] / src / empathy-import-pidgin.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., 51 Franklin St, Fifth Floor,
17  * Boston, MA  02110-1301  USA
18  *
19  * Authors: Jonny Lamb <jonny.lamb@collabora.co.uk>
20  * */
21
22 #include <config.h>
23
24 #include <string.h>
25 #include <unistd.h>
26
27 #include <glib.h>
28 #include <glib/gstdio.h>
29 #include <dbus/dbus-protocol.h>
30 #include <libxml/parser.h>
31 #include <libxml/tree.h>
32
33 #include <telepathy-glib/telepathy-glib.h>
34
35 #include "empathy-import-utils.h"
36 #include "empathy-import-pidgin.h"
37
38 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
39 #include <libempathy/empathy-debug.h>
40 #include <libempathy/empathy-utils.h>
41
42 #include <libempathy-gtk/empathy-ui-utils.h>
43
44 /* Pidgin to CM map */
45 typedef struct
46 {
47   const gchar *protocol;
48   const gchar *pidgin_name;
49   const gchar *cm_name;
50 } PidginCmMapItem;
51
52 static PidginCmMapItem pidgin_cm_map[] =
53 {
54   { "msn", "server", "server" },
55   { "msn", "port", "port" },
56
57   { "jabber", "connect_server", "server" },
58   { "jabber", "port", "port" },
59   { "jabber", "require_tls", "require-encryption" },
60   { "jabber", "old_ssl", "old-ssl" },
61
62   { "aim", "server", "server" },
63   { "aim", "port", "port" },
64
65   { "salut", "first", "first-name" },
66   { "salut", "last", "last-name" },
67   { "salut", "jid", "jid" },
68   { "salut", "email", "email" },
69
70   { "groupwise", "server", "server" },
71   { "groupwise", "port", "port" },
72
73   { "icq", "server", "server" },
74   { "icq", "port", "port" },
75
76   { "irc", "realname", "fullname" },
77   { "irc", "ssl", "use-ssl" },
78   { "irc", "port", "port" },
79
80   { "yahoo", "server", "server" },
81   { "yahoo", "port", "port" },
82   { "yahoo", "xfer_port", "xfer-port" },
83   { "yahoo", "ignore_invites", "ignore-invites" },
84   { "yahoo", "yahoojp", "yahoojp" },
85   { "yahoo", "xferjp_host", "xferjp-host" },
86   { "yahoo", "serverjp", "serverjp" },
87   { "yahoo", "xfer_host", "xfer-host" },
88 };
89
90 #define PIDGIN_ACCOUNT_TAG_NAME "name"
91 #define PIDGIN_ACCOUNT_TAG_ACCOUNT "account"
92 #define PIDGIN_ACCOUNT_TAG_PROTOCOL "protocol"
93 #define PIDGIN_ACCOUNT_TAG_PASSWORD "password"
94 #define PIDGIN_ACCOUNT_TAG_SETTINGS "settings"
95 #define PIDGIN_SETTING_PROP_UI "ui"
96 #define PIDGIN_SETTING_PROP_NAME "name"
97 #define PIDGIN_SETTING_PROP_TYPE "type"
98 #define PIDGIN_PROTOCOL_BONJOUR "bonjour"
99 #define PIDGIN_PROTOCOL_NOVELL "novell"
100
101 static void
102 import_dialog_pidgin_parse_setting (EmpathyImportAccountData *data,
103                                     xmlNodePtr setting)
104 {
105   PidginCmMapItem *item = NULL;
106   gchar *tag_name;
107   gchar *type = NULL;
108   gchar *content;
109   guint i;
110   GValue *value = NULL;
111
112   /* We can't do anything if the setting don't have a name */
113   tag_name = (gchar *) xmlGetProp (setting,
114       (xmlChar *) PIDGIN_SETTING_PROP_NAME);
115   if (!tag_name)
116     return;
117
118   /* Search for the map corresponding to setting we are parsing */
119   for (i = 0; i < G_N_ELEMENTS (pidgin_cm_map); i++)
120     {
121       if (!tp_strdiff (data->protocol, pidgin_cm_map[i].protocol) &&
122           !tp_strdiff (tag_name, pidgin_cm_map[i].pidgin_name))
123         {
124           item = pidgin_cm_map + i;
125           break;
126         }
127     }
128   g_free (tag_name);
129
130   /* If we didn't find the item, there is nothing we can do */
131   if (!item)
132     return;
133
134   type = (gchar *) xmlGetProp (setting, (xmlChar *) PIDGIN_SETTING_PROP_TYPE);
135   content = (gchar *) xmlNodeGetContent (setting);
136
137   if (!tp_strdiff (type, "bool"))
138     {
139       i = (gint) g_ascii_strtod (content, NULL);
140       value = tp_g_value_slice_new (G_TYPE_BOOLEAN);
141       g_value_set_boolean (value, i != 0);
142     }
143   else if (!tp_strdiff (type, "int"))
144     {
145       TpConnectionManager *cm = NULL;
146       TpProtocol *proto;
147       const TpConnectionManagerParam *param;
148       const gchar *signature;
149       int signature_i;
150
151       if (!empathy_import_protocol_is_supported (data->protocol, &cm))
152         return;
153
154       proto = tp_connection_manager_get_protocol_object (cm, data->protocol);
155       param = tp_protocol_get_param (proto, item->cm_name);
156       signature = tp_connection_manager_param_get_dbus_signature (param);
157       signature_i = (int) (*signature);
158
159       i = (gint) g_ascii_strtod (content, NULL);
160
161       if (signature_i == DBUS_TYPE_INT16 ||
162           signature_i == DBUS_TYPE_INT32)
163         {
164           value = tp_g_value_slice_new (G_TYPE_INT);
165           g_value_set_int (value, i);
166         }
167       else if (signature_i == DBUS_TYPE_UINT16 ||
168           signature_i == DBUS_TYPE_UINT32)
169         {
170           value = tp_g_value_slice_new (G_TYPE_UINT);
171           g_value_set_uint (value, (guint) i);
172         }
173     }
174   else if (!tp_strdiff (type, "string"))
175     {
176       value = tp_g_value_slice_new (G_TYPE_STRING);
177       g_value_set_string (value, content);
178     }
179
180   if (value)
181     g_hash_table_insert (data->settings, (gpointer) item->cm_name, value);
182
183   g_free (type);
184   g_free (content);
185 }
186
187 static void
188 import_dialog_pidgin_handle_settings (EmpathyImportAccountData *data,
189                                       xmlNodePtr settings)
190 {
191   xmlNodePtr setting;
192   gchar *tag_ui, *name, *type, *content;
193
194   tag_ui = (gchar *) xmlGetProp (settings, (xmlChar *) PIDGIN_SETTING_PROP_UI);
195
196   /* UI settings - fetch the Enabled parameter.
197    * The expected value of the ui property is 'gtk-gaim', which looks obsolete,
198    * but still valid for 2.7.3.
199    */
200   if (tag_ui && !tp_strdiff (tag_ui, "gtk-gaim"))
201     {
202       for (setting = settings->children; setting; setting = setting->next)
203         {
204           name = (gchar *) xmlGetProp (setting,
205               (xmlChar *) PIDGIN_SETTING_PROP_NAME);
206           type = (gchar *) xmlGetProp (setting,
207               (xmlChar *) PIDGIN_SETTING_PROP_TYPE);
208           /* The Enabled parameter is supposed to be boolean.
209            * Pidgin name of the setting is 'auto-login'.
210            */
211           if (!tp_strdiff (name, "auto-login") && !tp_strdiff (type, "bool"))
212             {
213               content = (gchar *) xmlNodeGetContent (setting);
214               data->enabled = (0 != (gint) g_ascii_strtod (content, NULL));
215               g_free (content);
216             }
217           g_free (type);
218           g_free (name);
219         }
220     }
221   /* General settings. */
222   else
223     {
224       for (setting = settings->children; setting; setting = setting->next)
225         import_dialog_pidgin_parse_setting (data, setting);
226     }
227
228   g_free (tag_ui);
229 }
230
231 GList *
232 empathy_import_pidgin_load (void)
233 {
234   xmlNodePtr rootnode, node, child;
235   xmlParserCtxtPtr ctxt;
236   xmlDocPtr doc;
237   gchar *filename;
238   GList *accounts = NULL;
239
240   /* Load pidgin accounts xml */
241   ctxt = xmlNewParserCtxt ();
242   filename = g_build_filename (g_get_home_dir (), ".purple", "accounts.xml",
243       NULL);
244
245   if (g_access (filename, R_OK) != 0)
246     goto FILENAME;
247
248   doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
249
250   rootnode = xmlDocGetRootElement (doc);
251   if (rootnode == NULL)
252     goto OUT;
253
254   for (node = rootnode->children; node; node = node->next)
255     {
256       EmpathyImportAccountData *data;
257
258       /* If it is not an account node, skip. */
259       if (tp_strdiff ((gchar *) node->name, PIDGIN_ACCOUNT_TAG_ACCOUNT))
260         continue;
261
262       /* Create account data struct */
263       data = empathy_import_account_data_new ("Pidgin");
264
265       /* Parse account's child nodes to fill the account data struct */
266       for (child = node->children; child; child = child->next)
267         {
268           GValue *value;
269
270           /* Protocol */
271           if (!tp_strdiff ((gchar *) child->name,
272               PIDGIN_ACCOUNT_TAG_PROTOCOL))
273             {
274               xmlChar *content;
275               const gchar *protocol;
276
277               content = xmlNodeGetContent (child);
278
279               protocol = (const gchar *) content;
280
281               if (g_str_has_prefix (protocol, "prpl-"))
282                 protocol += 5;
283
284               if (!tp_strdiff (protocol, PIDGIN_PROTOCOL_BONJOUR))
285                 data->protocol = g_strdup ("salut");
286               else if (!tp_strdiff (protocol, PIDGIN_PROTOCOL_NOVELL))
287                 data->protocol = g_strdup ("groupwise");
288               else
289                 data->protocol = g_strdup (protocol);
290
291               xmlFree (content);
292
293               if (data->protocol == NULL)
294                 break;
295             }
296
297           /* Username and IRC server. */
298           else if (!tp_strdiff ((gchar *) child->name,
299               PIDGIN_ACCOUNT_TAG_NAME))
300             {
301               gchar *name;
302               GStrv name_resource = NULL;
303               GStrv nick_server = NULL;
304               const gchar *username;
305
306               name = (gchar *) xmlNodeGetContent (child);
307
308               /* Split "username/resource" */
309               if (g_strrstr (name, "/") != NULL)
310                 {
311                   name_resource = g_strsplit (name, "/", 2);
312                   username = name_resource[0];
313                 }
314               else
315                 username = name;
316
317              /* Split "username@server" if it is an IRC account */
318              if (strstr (name, "@") && !tp_strdiff (data->protocol, "irc"))
319               {
320                 nick_server = g_strsplit (name, "@", 2);
321                 username = nick_server[0];
322
323                 /* Add the server setting */
324                 value = tp_g_value_slice_new (G_TYPE_STRING);
325                 g_value_set_string (value, nick_server[1]);
326                 g_hash_table_insert (data->settings, (gpointer) "server", value);
327               }
328
329               /* Add the account setting */
330               value = tp_g_value_slice_new (G_TYPE_STRING);
331               g_value_set_string (value, username);
332               g_hash_table_insert (data->settings, (gpointer) "account", value);
333
334               g_strfreev (name_resource);
335               g_strfreev (nick_server);
336               g_free (name);
337             }
338
339           /* Password */
340           else if (!tp_strdiff ((gchar *) child->name,
341               PIDGIN_ACCOUNT_TAG_PASSWORD))
342             {
343               gchar *password;
344
345               password = (gchar *) xmlNodeGetContent (child);
346
347               /* Add the password setting */
348               value = tp_g_value_slice_new (G_TYPE_STRING);
349               g_value_set_string (value, password);
350               g_hash_table_insert (data->settings, (gpointer) "password", value);
351
352               g_free (password);
353             }
354
355           /* Other settings */
356           else if (!tp_strdiff ((gchar *) child->name,
357               PIDGIN_ACCOUNT_TAG_SETTINGS))
358             import_dialog_pidgin_handle_settings (data, child);
359         }
360
361       /* If we have the needed settings, add the account data to the list,
362        * otherwise free the data */
363       if (data->protocol != NULL && g_hash_table_size (data->settings) > 0)
364         {
365           /* Special-case XMPP:
366            * http://bugzilla.gnome.org/show_bug.cgi?id=579992 */
367           if (!tp_strdiff (data->protocol, "jabber"))
368             {
369               if (EMP_STR_EMPTY (tp_asv_get_string (data->settings, "server")))
370                 {
371                   g_hash_table_remove (data->settings, "port");
372                   g_hash_table_remove (data->settings, "server");
373                 }
374             }
375
376           /* If there is no password then MC treats the account as not
377            * ready and doesn't display it. */
378           if (!g_hash_table_lookup (data->settings, "password"))
379             {
380               GValue *value;
381               value = tp_g_value_slice_new (G_TYPE_STRING);
382               g_value_set_string (value, "");
383               g_hash_table_insert (data->settings, (gpointer) "password", value);
384             }
385
386           accounts = g_list_prepend (accounts, data);
387         }
388       else
389         empathy_import_account_data_free (data);
390     }
391
392 OUT:
393   xmlFreeDoc (doc);
394   xmlFreeParserCtxt (ctxt);
395
396 FILENAME:
397   g_free (filename);
398
399   return accounts;
400 }
401
402 gboolean
403 empathy_import_pidgin_accounts_to_import (void)
404 {
405   gchar *filename;
406   gboolean out;
407   GFile *file;
408
409   filename = g_build_filename (g_get_home_dir (), ".purple", "accounts.xml",
410       NULL);
411   file = g_file_new_for_path (filename);
412   out = g_file_query_exists (file, NULL);
413
414   g_free (filename);
415   g_object_unref (file);
416
417   return out;
418 }