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_DOMAIN "IrcNetworkManager"
34 #define IRC_NETWORKS_DTD_FILENAME "empathy-irc-networks.dtd"
37 G_DEFINE_TYPE (EmpathyIrcNetworkManager, empathy_irc_network_manager,
48 typedef struct _EmpathyIrcNetworkManagerPrivate
49 EmpathyIrcNetworkManagerPrivate;
51 struct _EmpathyIrcNetworkManagerPrivate {
58 /* Do we have to save modifications to the user file ? */
59 gboolean have_to_save;
60 /* Are we loading networks from XML files ? */
62 /* source id of the autosave timer */
66 #define EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE(obj)\
67 ((EmpathyIrcNetworkManagerPrivate *) obj->priv)
69 static void irc_network_manager_load_servers (
70 EmpathyIrcNetworkManager *manager);
71 static gboolean irc_network_manager_file_parse (
72 EmpathyIrcNetworkManager *manager, const gchar *filename,
73 gboolean user_defined);
74 static gboolean irc_network_manager_file_save (
75 EmpathyIrcNetworkManager *manager);
78 empathy_irc_network_manager_get_property (GObject *object,
83 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
84 EmpathyIrcNetworkManagerPrivate *priv =
85 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
89 case PROP_GLOBAL_FILE:
90 g_value_set_string (value, priv->global_file);
93 g_value_set_string (value, priv->user_file);
96 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
102 empathy_irc_network_manager_set_property (GObject *object,
107 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
108 EmpathyIrcNetworkManagerPrivate *priv =
109 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
113 case PROP_GLOBAL_FILE:
114 g_free (priv->global_file);
115 priv->global_file = g_value_dup_string (value);
118 g_free (priv->user_file);
119 priv->user_file = g_value_dup_string (value);
122 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
128 empathy_irc_network_manager_constructor (GType type,
130 GObjectConstructParam *props)
133 EmpathyIrcNetworkManager *self;
135 /* Parent constructor chain */
136 obj = G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->
137 constructor (type, n_props, props);
139 self = EMPATHY_IRC_NETWORK_MANAGER (obj);
140 irc_network_manager_load_servers (self);
146 empathy_irc_network_manager_finalize (GObject *object)
148 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
149 EmpathyIrcNetworkManagerPrivate *priv =
150 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
152 if (priv->save_timer_id > 0)
154 g_source_remove (priv->save_timer_id);
157 if (priv->have_to_save)
159 irc_network_manager_file_save (self);
162 g_free (priv->global_file);
163 g_free (priv->user_file);
165 g_hash_table_destroy (priv->networks);
167 G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->finalize (object);
171 empathy_irc_network_manager_init (EmpathyIrcNetworkManager *self)
173 EmpathyIrcNetworkManagerPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
174 EMPATHY_TYPE_IRC_NETWORK_MANAGER, EmpathyIrcNetworkManagerPrivate);
178 priv->networks = g_hash_table_new_full (g_str_hash, g_str_equal,
179 (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref);
183 priv->have_to_save = FALSE;
184 priv->loading = FALSE;
185 priv->save_timer_id = 0;
189 empathy_irc_network_manager_class_init (EmpathyIrcNetworkManagerClass *klass)
191 GObjectClass *object_class = G_OBJECT_CLASS (klass);
192 GParamSpec *param_spec;
194 object_class->constructor = empathy_irc_network_manager_constructor;
195 object_class->get_property = empathy_irc_network_manager_get_property;
196 object_class->set_property = empathy_irc_network_manager_set_property;
198 g_type_class_add_private (object_class,
199 sizeof (EmpathyIrcNetworkManagerPrivate));
201 object_class->finalize = empathy_irc_network_manager_finalize;
203 param_spec = g_param_spec_string (
205 "path of the global networks file",
206 "The path of the system-wide filename from which we have to load"
207 " the networks list",
209 G_PARAM_CONSTRUCT_ONLY |
211 G_PARAM_STATIC_NAME |
212 G_PARAM_STATIC_NICK |
213 G_PARAM_STATIC_BLURB);
214 g_object_class_install_property (object_class, PROP_GLOBAL_FILE, param_spec);
216 param_spec = g_param_spec_string (
218 "path of the user networks file",
219 "The path of user's filename from which we have to load"
220 " the networks list and to which we'll save his modifications",
222 G_PARAM_CONSTRUCT_ONLY |
224 G_PARAM_STATIC_NAME |
225 G_PARAM_STATIC_NICK |
226 G_PARAM_STATIC_BLURB);
227 g_object_class_install_property (object_class, PROP_USER_FILE, param_spec);
231 * empathy_irc_network_manager_new:
232 * @global_file: the path of the global networks file, or %NULL
233 * @user_file: the path of the user networks file, or %NULL
235 * Creates a new #EmpathyIrcNetworkManager
237 * Returns: a new #EmpathyIrcNetworkManager
239 EmpathyIrcNetworkManager *
240 empathy_irc_network_manager_new (const gchar *global_file,
241 const gchar *user_file)
243 EmpathyIrcNetworkManager *manager;
245 manager = g_object_new (EMPATHY_TYPE_IRC_NETWORK_MANAGER,
246 "global-file", global_file,
247 "user-file", user_file,
254 save_timeout (EmpathyIrcNetworkManager *self)
256 EmpathyIrcNetworkManagerPrivate *priv =
257 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
259 priv->save_timer_id = 0;
260 irc_network_manager_file_save (self);
266 reset_save_timeout (EmpathyIrcNetworkManager *self)
268 EmpathyIrcNetworkManagerPrivate *priv =
269 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
271 if (priv->save_timer_id > 0)
273 g_source_remove (priv->save_timer_id);
276 priv->save_timer_id = g_timeout_add_seconds (SAVE_TIMER,
277 (GSourceFunc) save_timeout, self);
281 network_modified (EmpathyIrcNetwork *network,
282 EmpathyIrcNetworkManager *self)
284 EmpathyIrcNetworkManagerPrivate *priv =
285 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
287 network->user_defined = TRUE;
291 priv->have_to_save = TRUE;
292 reset_save_timeout (self);
297 add_network (EmpathyIrcNetworkManager *self,
298 EmpathyIrcNetwork *network,
301 EmpathyIrcNetworkManagerPrivate *priv =
302 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
304 g_hash_table_insert (priv->networks, g_strdup (id), g_object_ref (network));
306 g_signal_connect (network, "modified", G_CALLBACK (network_modified), self);
310 * empathy_irc_network_manager_add:
311 * @manager: an #EmpathyIrcNetworkManager
312 * @network: the #EmpathyIrcNetwork to add
314 * Add an #EmpathyIrcNetwork to the given #EmpathyIrcNetworkManager.
318 empathy_irc_network_manager_add (EmpathyIrcNetworkManager *self,
319 EmpathyIrcNetwork *network)
321 EmpathyIrcNetworkManagerPrivate *priv;
324 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
325 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
327 priv = EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
329 /* generate an id for this network */
333 id = g_strdup_printf ("id%u", ++priv->last_id);
334 } while (g_hash_table_lookup (priv->networks, id) != NULL &&
335 priv->last_id < G_MAXUINT);
337 if (priv->last_id == G_MAXUINT)
339 empathy_debug (DEBUG_DOMAIN,
340 "Can't add network: too many networks using a similiar ID");
344 empathy_debug (DEBUG_DOMAIN, "add server with \"%s\" as ID", id);
346 network->user_defined = TRUE;
347 add_network (self, network, id);
349 priv->have_to_save = TRUE;
350 reset_save_timeout (self);
356 * empathy_irc_network_manager_remove:
357 * @manager: an #EmpathyIrcNetworkManager
358 * @network: the #EmpathyIrcNetwork to remove
360 * Remove an #EmpathyIrcNetwork from the given #EmpathyIrcNetworkManager.
364 empathy_irc_network_manager_remove (EmpathyIrcNetworkManager *self,
365 EmpathyIrcNetwork *network)
367 EmpathyIrcNetworkManagerPrivate *priv;
369 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
370 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
372 priv = EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
374 network->user_defined = TRUE;
375 network->dropped = TRUE;
377 priv->have_to_save = TRUE;
378 reset_save_timeout (self);
382 append_network_to_list (const gchar *id,
383 EmpathyIrcNetwork *network,
386 if (network->dropped)
389 *list = g_slist_prepend (*list, g_object_ref (network));
393 * empathy_irc_network_manager_get_networks:
394 * @manager: an #EmpathyIrcNetworkManager
396 * Get the list of #EmpathyIrcNetwork associated with the given
399 * Returns: a new #GSList of refed #EmpathyIrcNetwork
402 empathy_irc_network_manager_get_networks (EmpathyIrcNetworkManager *self)
404 EmpathyIrcNetworkManagerPrivate *priv;
405 GSList *irc_networks = NULL;
407 g_return_val_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self), NULL);
409 priv = EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
411 g_hash_table_foreach (priv->networks, (GHFunc) append_network_to_list,
418 * API to save/load and parse the irc_networks file.
422 load_global_file (EmpathyIrcNetworkManager *self)
424 EmpathyIrcNetworkManagerPrivate *priv =
425 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
427 if (priv->global_file == NULL)
430 if (!g_file_test (priv->global_file, G_FILE_TEST_EXISTS))
432 empathy_debug (DEBUG_DOMAIN, "Global networks file %s doesn't exist",
437 irc_network_manager_file_parse (self, priv->global_file, FALSE);
441 load_user_file (EmpathyIrcNetworkManager *self)
443 EmpathyIrcNetworkManagerPrivate *priv =
444 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
446 if (priv->user_file == NULL)
449 if (!g_file_test (priv->user_file, G_FILE_TEST_EXISTS))
451 empathy_debug (DEBUG_DOMAIN, "User networks file %s doesn't exist",
456 irc_network_manager_file_parse (self, priv->user_file, TRUE);
460 irc_network_manager_load_servers (EmpathyIrcNetworkManager *self)
462 EmpathyIrcNetworkManagerPrivate *priv =
463 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
465 priv->loading = TRUE;
467 load_global_file (self);
468 load_user_file (self);
470 priv->loading = FALSE;
471 priv->have_to_save = FALSE;
475 irc_network_manager_parse_irc_server (EmpathyIrcNetwork *network,
478 xmlNodePtr server_node;
480 for (server_node = node->children; server_node;
481 server_node = server_node->next)
483 gchar *address = NULL, *port = NULL, *ssl = NULL;
485 if (strcmp (server_node->name, "server") != 0)
488 address = xmlGetProp (server_node, "address");
489 port = xmlGetProp (server_node, "port");
490 ssl = xmlGetProp (server_node, "ssl");
495 gboolean have_ssl = FALSE;
496 EmpathyIrcServer *server;
499 port_nb = strtol (port, NULL, 10);
501 if (port_nb <= 0 || port_nb > G_MAXUINT16)
504 if (ssl == NULL || strcmp (ssl, "TRUE") == 0)
507 empathy_debug (DEBUG_DOMAIN, "parsed server %s port %d ssl %d",
508 address, port_nb, have_ssl);
510 server = empathy_irc_server_new (address, port_nb, have_ssl);
511 empathy_irc_network_append_server (network, server);
524 irc_network_manager_parse_irc_network (EmpathyIrcNetworkManager *self,
526 gboolean user_defined)
528 EmpathyIrcNetworkManagerPrivate *priv =
529 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
530 EmpathyIrcNetwork *network;
535 id = xmlGetProp (node, "id");
536 if (xmlHasProp (node, "dropped"))
540 empathy_debug (DEBUG_DOMAIN, "the \"dropped\" attribute shouldn't be"
541 " used in the global file");
544 network = g_hash_table_lookup (priv->networks, id);
547 network->dropped = TRUE;
548 network->user_defined = TRUE;
554 if (!xmlHasProp (node, "name"))
557 name = xmlGetProp (node, "name");
558 network = empathy_irc_network_new (name);
560 if (xmlHasProp (node, "network_charset"))
563 charset = xmlGetProp (node, "network_charset");
564 g_object_set (network, "charset", charset, NULL);
568 add_network (self, network, id);
569 empathy_debug (DEBUG_DOMAIN, "add network %s (id %s)", name, id);
571 for (child = node->children; child; child = child->next)
575 tag = (gchar *) child->name;
576 str = (gchar *) xmlNodeGetContent (child);
581 if (strcmp (tag, "servers") == 0)
583 irc_network_manager_parse_irc_server (network, child);
589 network->user_defined = user_defined;
590 g_object_unref (network);
596 irc_network_manager_file_parse (EmpathyIrcNetworkManager *self,
597 const gchar *filename,
598 gboolean user_defined)
600 EmpathyIrcNetworkManagerPrivate *priv;
601 xmlParserCtxtPtr ctxt;
606 priv = EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
608 empathy_debug (DEBUG_DOMAIN, "Attempting to parse file:'%s'...", filename);
610 ctxt = xmlNewParserCtxt ();
612 /* Parse and validate the file. */
613 doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
616 g_warning ("Failed to parse file:'%s'", filename);
617 xmlFreeParserCtxt (ctxt);
621 if (!empathy_xml_validate (doc, IRC_NETWORKS_DTD_FILENAME)) {
622 g_warning ("Failed to validate file:'%s'", filename);
624 xmlFreeParserCtxt (ctxt);
628 /* The root node, networks. */
629 networks = xmlDocGetRootElement (doc);
631 for (node = networks->children; node; node = node->next)
633 irc_network_manager_parse_irc_network (self, node, user_defined);
637 xmlFreeParserCtxt (ctxt);
643 write_network_to_xml (const gchar *id,
644 EmpathyIrcNetwork *network,
647 xmlNodePtr network_node, servers_node;
649 gchar *name, *charset;
651 if (!network->user_defined)
652 /* no need to write this network to the XML */
655 network_node = xmlNewChild (root, NULL, "network", NULL);
656 xmlNewProp (network_node, "id", id);
658 if (network->dropped)
660 xmlNewProp (network_node, "dropped", "1");
664 g_object_get (network,
668 xmlNewProp (network_node, "name", name);
669 xmlNewProp (network_node, "network_charset", charset);
673 servers = empathy_irc_network_get_servers (network);
675 servers_node = xmlNewChild (network_node, NULL, "servers", NULL);
676 for (l = servers; l != NULL; l = g_slist_next (l))
678 EmpathyIrcServer *server;
679 xmlNodePtr server_node;
680 gchar *address, *tmp;
686 server_node = xmlNewChild (servers_node, NULL, "server", NULL);
688 g_object_get (server,
694 xmlNewProp (server_node, "address", address);
696 tmp = g_strdup_printf ("%u", port);
697 xmlNewProp (server_node, "port", tmp);
700 xmlNewProp (server_node, "ssl", ssl ? "TRUE": "FALSE");
706 g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
707 g_slist_free (servers);
711 irc_network_manager_file_save (EmpathyIrcNetworkManager *self)
713 EmpathyIrcNetworkManagerPrivate *priv =
714 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
718 if (priv->user_file == NULL)
720 empathy_debug (DEBUG_DOMAIN, "can't save: no user file defined");
724 empathy_debug (DEBUG_DOMAIN, "Saving IRC networks");
726 doc = xmlNewDoc ("1.0");
727 root = xmlNewNode (NULL, "networks");
728 xmlDocSetRootElement (doc, root);
730 g_hash_table_foreach (priv->networks, (GHFunc) write_network_to_xml, root);
732 /* Make sure the XML is indented properly */
733 xmlIndentTreeOutput = 1;
735 xmlSaveFormatFileEnc (priv->user_file, doc, "utf-8", 1);
741 priv->have_to_save = FALSE;
747 find_network_by_address (const gchar *id,
748 EmpathyIrcNetwork *network,
749 const gchar *address)
752 gboolean found = FALSE;
754 if (network->dropped)
757 servers = empathy_irc_network_get_servers (network);
759 for (l = servers; l != NULL && !found; l = g_slist_next (l))
761 EmpathyIrcServer *server = l->data;
764 g_object_get (server, "address", &_address, NULL);
765 found = (_address != NULL && strcmp (address, _address) == 0);
770 g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
771 g_slist_free (servers);
777 * empathy_irc_network_manager_find_network_by_address:
778 * @manager: an #EmpathyIrcNetworkManager
779 * @address: the server address to look for
781 * Find the #EmpathyIrcNetwork which owns an #EmpathyIrcServer
782 * that has the given address.
784 * Returns: the found #EmpathyIrcNetwork, or %NULL if not found.
787 empathy_irc_network_manager_find_network_by_address (
788 EmpathyIrcNetworkManager *self,
789 const gchar *address)
791 EmpathyIrcNetworkManagerPrivate *priv =
792 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
793 EmpathyIrcNetwork *network;
795 g_return_val_if_fail (address != NULL, NULL);
797 network = g_hash_table_find (priv->networks,
798 (GHRFunc) find_network_by_address, (gchar *) address);