2 * Copyright (C) 2007-2008 Guillaume Desmottes
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library 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 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 * Authors: Guillaume Desmottes <gdesmott@gnome.org>
23 #include <sys/types.h>
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
28 #include <libempathy/empathy-debug.h>
30 #include "empathy-utils.h"
31 #include "empathy-irc-network-manager.h"
33 #define DEBUG_FLAG EMPATHY_DEBUG_IRC
34 #include "empathy-debug.h"
36 #define IRC_NETWORKS_DTD_FILENAME "empathy-irc-networks.dtd"
39 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIrcNetworkManager)
47 /* Do we have to save modifications to the user file ? */
48 gboolean have_to_save;
49 /* Are we loading networks from XML files ? */
51 /* source id of the autosave timer */
53 } EmpathyIrcNetworkManagerPriv;
63 G_DEFINE_TYPE (EmpathyIrcNetworkManager, empathy_irc_network_manager,
66 static void irc_network_manager_load_servers (
67 EmpathyIrcNetworkManager *manager);
68 static gboolean irc_network_manager_file_parse (
69 EmpathyIrcNetworkManager *manager, const gchar *filename,
70 gboolean user_defined);
71 static gboolean irc_network_manager_file_save (
72 EmpathyIrcNetworkManager *manager);
75 empathy_irc_network_manager_get_property (GObject *object,
80 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
81 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
85 case PROP_GLOBAL_FILE:
86 g_value_set_string (value, priv->global_file);
89 g_value_set_string (value, priv->user_file);
92 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
98 empathy_irc_network_manager_set_property (GObject *object,
103 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
104 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
108 case PROP_GLOBAL_FILE:
109 g_free (priv->global_file);
110 priv->global_file = g_value_dup_string (value);
113 g_free (priv->user_file);
114 priv->user_file = g_value_dup_string (value);
117 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
123 empathy_irc_network_manager_constructor (GType type,
125 GObjectConstructParam *props)
128 EmpathyIrcNetworkManager *self;
130 /* Parent constructor chain */
131 obj = G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->
132 constructor (type, n_props, props);
134 self = EMPATHY_IRC_NETWORK_MANAGER (obj);
135 irc_network_manager_load_servers (self);
141 empathy_irc_network_manager_finalize (GObject *object)
143 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
144 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
146 if (priv->save_timer_id > 0)
148 g_source_remove (priv->save_timer_id);
151 if (priv->have_to_save)
153 irc_network_manager_file_save (self);
156 g_free (priv->global_file);
157 g_free (priv->user_file);
159 g_hash_table_destroy (priv->networks);
161 G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->finalize (object);
165 empathy_irc_network_manager_init (EmpathyIrcNetworkManager *self)
167 EmpathyIrcNetworkManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
168 EMPATHY_TYPE_IRC_NETWORK_MANAGER, EmpathyIrcNetworkManagerPriv);
172 priv->networks = g_hash_table_new_full (g_str_hash, g_str_equal,
173 (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref);
177 priv->have_to_save = FALSE;
178 priv->loading = FALSE;
179 priv->save_timer_id = 0;
183 empathy_irc_network_manager_class_init (EmpathyIrcNetworkManagerClass *klass)
185 GObjectClass *object_class = G_OBJECT_CLASS (klass);
186 GParamSpec *param_spec;
188 object_class->constructor = empathy_irc_network_manager_constructor;
189 object_class->get_property = empathy_irc_network_manager_get_property;
190 object_class->set_property = empathy_irc_network_manager_set_property;
192 g_type_class_add_private (object_class, sizeof (EmpathyIrcNetworkManagerPriv));
194 object_class->finalize = empathy_irc_network_manager_finalize;
196 param_spec = g_param_spec_string (
198 "path of the global networks file",
199 "The path of the system-wide filename from which we have to load"
200 " the networks list",
202 G_PARAM_CONSTRUCT_ONLY |
204 G_PARAM_STATIC_NAME |
205 G_PARAM_STATIC_NICK |
206 G_PARAM_STATIC_BLURB);
207 g_object_class_install_property (object_class, PROP_GLOBAL_FILE, param_spec);
209 param_spec = g_param_spec_string (
211 "path of the user networks file",
212 "The path of user's filename from which we have to load"
213 " the networks list and to which we'll save his modifications",
215 G_PARAM_CONSTRUCT_ONLY |
217 G_PARAM_STATIC_NAME |
218 G_PARAM_STATIC_NICK |
219 G_PARAM_STATIC_BLURB);
220 g_object_class_install_property (object_class, PROP_USER_FILE, param_spec);
224 * empathy_irc_network_manager_new:
225 * @global_file: the path of the global networks file, or %NULL
226 * @user_file: the path of the user networks file, or %NULL
228 * Creates a new #EmpathyIrcNetworkManager
230 * Returns: a new #EmpathyIrcNetworkManager
232 EmpathyIrcNetworkManager *
233 empathy_irc_network_manager_new (const gchar *global_file,
234 const gchar *user_file)
236 EmpathyIrcNetworkManager *manager;
238 manager = g_object_new (EMPATHY_TYPE_IRC_NETWORK_MANAGER,
239 "global-file", global_file,
240 "user-file", user_file,
247 save_timeout (EmpathyIrcNetworkManager *self)
249 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
251 priv->save_timer_id = 0;
252 irc_network_manager_file_save (self);
258 reset_save_timeout (EmpathyIrcNetworkManager *self)
260 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
262 if (priv->save_timer_id > 0)
264 g_source_remove (priv->save_timer_id);
267 priv->save_timer_id = g_timeout_add_seconds (SAVE_TIMER,
268 (GSourceFunc) save_timeout, self);
272 network_modified (EmpathyIrcNetwork *network,
273 EmpathyIrcNetworkManager *self)
275 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
277 network->user_defined = TRUE;
281 priv->have_to_save = TRUE;
282 reset_save_timeout (self);
287 add_network (EmpathyIrcNetworkManager *self,
288 EmpathyIrcNetwork *network,
291 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
293 g_hash_table_insert (priv->networks, g_strdup (id), g_object_ref (network));
295 g_signal_connect (network, "modified", G_CALLBACK (network_modified), self);
299 * empathy_irc_network_manager_add:
300 * @manager: an #EmpathyIrcNetworkManager
301 * @network: the #EmpathyIrcNetwork to add
303 * Add an #EmpathyIrcNetwork to the given #EmpathyIrcNetworkManager.
307 empathy_irc_network_manager_add (EmpathyIrcNetworkManager *self,
308 EmpathyIrcNetwork *network)
310 EmpathyIrcNetworkManagerPriv *priv;
313 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
314 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
316 priv = GET_PRIV (self);
318 /* generate an id for this network */
322 id = g_strdup_printf ("id%u", ++priv->last_id);
323 } while (g_hash_table_lookup (priv->networks, id) != NULL &&
324 priv->last_id < G_MAXUINT);
326 if (priv->last_id == G_MAXUINT)
328 DEBUG ("Can't add network: too many networks using a similiar ID");
332 DEBUG ("add server with \"%s\" as ID", id);
334 network->user_defined = TRUE;
335 add_network (self, network, id);
337 priv->have_to_save = TRUE;
338 reset_save_timeout (self);
344 * empathy_irc_network_manager_remove:
345 * @manager: an #EmpathyIrcNetworkManager
346 * @network: the #EmpathyIrcNetwork to remove
348 * Remove an #EmpathyIrcNetwork from the given #EmpathyIrcNetworkManager.
352 empathy_irc_network_manager_remove (EmpathyIrcNetworkManager *self,
353 EmpathyIrcNetwork *network)
355 EmpathyIrcNetworkManagerPriv *priv;
357 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
358 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
360 priv = GET_PRIV (self);
362 network->user_defined = TRUE;
363 network->dropped = TRUE;
365 priv->have_to_save = TRUE;
366 reset_save_timeout (self);
370 append_network_to_list (const gchar *id,
371 EmpathyIrcNetwork *network,
374 if (network->dropped)
377 *list = g_slist_prepend (*list, g_object_ref (network));
381 * empathy_irc_network_manager_get_networks:
382 * @manager: an #EmpathyIrcNetworkManager
384 * Get the list of #EmpathyIrcNetwork associated with the given
387 * Returns: a new #GSList of refed #EmpathyIrcNetwork
390 empathy_irc_network_manager_get_networks (EmpathyIrcNetworkManager *self)
392 EmpathyIrcNetworkManagerPriv *priv;
393 GSList *irc_networks = NULL;
395 g_return_val_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self), NULL);
397 priv = GET_PRIV (self);
399 g_hash_table_foreach (priv->networks, (GHFunc) append_network_to_list,
406 * API to save/load and parse the irc_networks file.
410 load_global_file (EmpathyIrcNetworkManager *self)
412 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
414 if (priv->global_file == NULL)
417 if (!g_file_test (priv->global_file, G_FILE_TEST_EXISTS))
419 DEBUG ("Global networks file %s doesn't exist", priv->global_file);
423 irc_network_manager_file_parse (self, priv->global_file, FALSE);
427 load_user_file (EmpathyIrcNetworkManager *self)
429 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
431 if (priv->user_file == NULL)
434 if (!g_file_test (priv->user_file, G_FILE_TEST_EXISTS))
436 DEBUG ("User networks file %s doesn't exist", priv->global_file);
440 irc_network_manager_file_parse (self, priv->user_file, TRUE);
444 irc_network_manager_load_servers (EmpathyIrcNetworkManager *self)
446 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
448 priv->loading = TRUE;
450 load_global_file (self);
451 load_user_file (self);
453 priv->loading = FALSE;
454 priv->have_to_save = FALSE;
458 irc_network_manager_parse_irc_server (EmpathyIrcNetwork *network,
461 xmlNodePtr server_node;
463 for (server_node = node->children; server_node;
464 server_node = server_node->next)
466 gchar *address = NULL, *port = NULL, *ssl = NULL;
468 if (strcmp (server_node->name, "server") != 0)
471 address = xmlGetProp (server_node, "address");
472 port = xmlGetProp (server_node, "port");
473 ssl = xmlGetProp (server_node, "ssl");
478 gboolean have_ssl = FALSE;
479 EmpathyIrcServer *server;
482 port_nb = strtol (port, NULL, 10);
484 if (port_nb <= 0 || port_nb > G_MAXUINT16)
487 if (ssl == NULL || strcmp (ssl, "TRUE") == 0)
490 DEBUG ("parsed server %s port %d ssl %d", address, port_nb, have_ssl);
492 server = empathy_irc_server_new (address, port_nb, have_ssl);
493 empathy_irc_network_append_server (network, server);
506 irc_network_manager_parse_irc_network (EmpathyIrcNetworkManager *self,
508 gboolean user_defined)
510 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
511 EmpathyIrcNetwork *network;
516 id = xmlGetProp (node, "id");
517 if (xmlHasProp (node, "dropped"))
521 DEBUG ("the 'dropped' attribute shouldn't be used in the global file");
524 network = g_hash_table_lookup (priv->networks, id);
527 network->dropped = TRUE;
528 network->user_defined = TRUE;
534 if (!xmlHasProp (node, "name"))
537 name = xmlGetProp (node, "name");
538 network = empathy_irc_network_new (name);
540 if (xmlHasProp (node, "network_charset"))
543 charset = xmlGetProp (node, "network_charset");
544 g_object_set (network, "charset", charset, NULL);
548 add_network (self, network, id);
549 DEBUG ("add network %s (id %s)", name, id);
551 for (child = node->children; child; child = child->next)
555 tag = (gchar *) child->name;
556 str = (gchar *) xmlNodeGetContent (child);
561 if (strcmp (tag, "servers") == 0)
563 irc_network_manager_parse_irc_server (network, child);
569 network->user_defined = user_defined;
570 g_object_unref (network);
576 irc_network_manager_file_parse (EmpathyIrcNetworkManager *self,
577 const gchar *filename,
578 gboolean user_defined)
580 EmpathyIrcNetworkManagerPriv *priv;
581 xmlParserCtxtPtr ctxt;
586 priv = GET_PRIV (self);
588 DEBUG ("Attempting to parse file:'%s'...", filename);
590 ctxt = xmlNewParserCtxt ();
592 /* Parse and validate the file. */
593 doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
596 g_warning ("Failed to parse file:'%s'", filename);
597 xmlFreeParserCtxt (ctxt);
601 if (!empathy_xml_validate (doc, IRC_NETWORKS_DTD_FILENAME)) {
602 g_warning ("Failed to validate file:'%s'", filename);
604 xmlFreeParserCtxt (ctxt);
608 /* The root node, networks. */
609 networks = xmlDocGetRootElement (doc);
611 for (node = networks->children; node; node = node->next)
613 irc_network_manager_parse_irc_network (self, node, user_defined);
617 xmlFreeParserCtxt (ctxt);
623 write_network_to_xml (const gchar *id,
624 EmpathyIrcNetwork *network,
627 xmlNodePtr network_node, servers_node;
629 gchar *name, *charset;
631 if (!network->user_defined)
632 /* no need to write this network to the XML */
635 network_node = xmlNewChild (root, NULL, "network", NULL);
636 xmlNewProp (network_node, "id", id);
638 if (network->dropped)
640 xmlNewProp (network_node, "dropped", "1");
644 g_object_get (network,
648 xmlNewProp (network_node, "name", name);
649 xmlNewProp (network_node, "network_charset", charset);
653 servers = empathy_irc_network_get_servers (network);
655 servers_node = xmlNewChild (network_node, NULL, "servers", NULL);
656 for (l = servers; l != NULL; l = g_slist_next (l))
658 EmpathyIrcServer *server;
659 xmlNodePtr server_node;
660 gchar *address, *tmp;
666 server_node = xmlNewChild (servers_node, NULL, "server", NULL);
668 g_object_get (server,
674 xmlNewProp (server_node, "address", address);
676 tmp = g_strdup_printf ("%u", port);
677 xmlNewProp (server_node, "port", tmp);
680 xmlNewProp (server_node, "ssl", ssl ? "TRUE": "FALSE");
686 g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
687 g_slist_free (servers);
691 irc_network_manager_file_save (EmpathyIrcNetworkManager *self)
693 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
697 if (priv->user_file == NULL)
699 DEBUG ("can't save: no user file defined");
703 DEBUG ("Saving IRC networks");
705 doc = xmlNewDoc ("1.0");
706 root = xmlNewNode (NULL, "networks");
707 xmlDocSetRootElement (doc, root);
709 g_hash_table_foreach (priv->networks, (GHFunc) write_network_to_xml, root);
711 /* Make sure the XML is indented properly */
712 xmlIndentTreeOutput = 1;
714 xmlSaveFormatFileEnc (priv->user_file, doc, "utf-8", 1);
720 priv->have_to_save = FALSE;
726 find_network_by_address (const gchar *id,
727 EmpathyIrcNetwork *network,
728 const gchar *address)
731 gboolean found = FALSE;
733 if (network->dropped)
736 servers = empathy_irc_network_get_servers (network);
738 for (l = servers; l != NULL && !found; l = g_slist_next (l))
740 EmpathyIrcServer *server = l->data;
743 g_object_get (server, "address", &_address, NULL);
744 found = (_address != NULL && strcmp (address, _address) == 0);
749 g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
750 g_slist_free (servers);
756 * empathy_irc_network_manager_find_network_by_address:
757 * @manager: an #EmpathyIrcNetworkManager
758 * @address: the server address to look for
760 * Find the #EmpathyIrcNetwork which owns an #EmpathyIrcServer
761 * that has the given address.
763 * Returns: the found #EmpathyIrcNetwork, or %NULL if not found.
766 empathy_irc_network_manager_find_network_by_address (
767 EmpathyIrcNetworkManager *self,
768 const gchar *address)
770 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
771 EmpathyIrcNetwork *network;
773 g_return_val_if_fail (address != NULL, NULL);
775 network = g_hash_table_find (priv->networks,
776 (GHRFunc) find_network_by_address, (gchar *) address);