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 G_DEFINE_TYPE (EmpathyIrcNetworkManager, empathy_irc_network_manager,
50 typedef struct _EmpathyIrcNetworkManagerPrivate
51 EmpathyIrcNetworkManagerPrivate;
53 struct _EmpathyIrcNetworkManagerPrivate {
60 /* Do we have to save modifications to the user file ? */
61 gboolean have_to_save;
62 /* Are we loading networks from XML files ? */
64 /* source id of the autosave timer */
68 #define EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE(obj)\
69 ((EmpathyIrcNetworkManagerPrivate *) obj->priv)
71 static void irc_network_manager_load_servers (
72 EmpathyIrcNetworkManager *manager);
73 static gboolean irc_network_manager_file_parse (
74 EmpathyIrcNetworkManager *manager, const gchar *filename,
75 gboolean user_defined);
76 static gboolean irc_network_manager_file_save (
77 EmpathyIrcNetworkManager *manager);
80 empathy_irc_network_manager_get_property (GObject *object,
85 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
86 EmpathyIrcNetworkManagerPrivate *priv =
87 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
91 case PROP_GLOBAL_FILE:
92 g_value_set_string (value, priv->global_file);
95 g_value_set_string (value, priv->user_file);
98 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
104 empathy_irc_network_manager_set_property (GObject *object,
109 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
110 EmpathyIrcNetworkManagerPrivate *priv =
111 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
115 case PROP_GLOBAL_FILE:
116 g_free (priv->global_file);
117 priv->global_file = g_value_dup_string (value);
120 g_free (priv->user_file);
121 priv->user_file = g_value_dup_string (value);
124 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
130 empathy_irc_network_manager_constructor (GType type,
132 GObjectConstructParam *props)
135 EmpathyIrcNetworkManager *self;
137 /* Parent constructor chain */
138 obj = G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->
139 constructor (type, n_props, props);
141 self = EMPATHY_IRC_NETWORK_MANAGER (obj);
142 irc_network_manager_load_servers (self);
148 empathy_irc_network_manager_finalize (GObject *object)
150 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
151 EmpathyIrcNetworkManagerPrivate *priv =
152 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
154 if (priv->save_timer_id > 0)
156 g_source_remove (priv->save_timer_id);
159 if (priv->have_to_save)
161 irc_network_manager_file_save (self);
164 g_free (priv->global_file);
165 g_free (priv->user_file);
167 g_hash_table_destroy (priv->networks);
169 G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->finalize (object);
173 empathy_irc_network_manager_init (EmpathyIrcNetworkManager *self)
175 EmpathyIrcNetworkManagerPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
176 EMPATHY_TYPE_IRC_NETWORK_MANAGER, EmpathyIrcNetworkManagerPrivate);
180 priv->networks = g_hash_table_new_full (g_str_hash, g_str_equal,
181 (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref);
185 priv->have_to_save = FALSE;
186 priv->loading = FALSE;
187 priv->save_timer_id = 0;
191 empathy_irc_network_manager_class_init (EmpathyIrcNetworkManagerClass *klass)
193 GObjectClass *object_class = G_OBJECT_CLASS (klass);
194 GParamSpec *param_spec;
196 object_class->constructor = empathy_irc_network_manager_constructor;
197 object_class->get_property = empathy_irc_network_manager_get_property;
198 object_class->set_property = empathy_irc_network_manager_set_property;
200 g_type_class_add_private (object_class,
201 sizeof (EmpathyIrcNetworkManagerPrivate));
203 object_class->finalize = empathy_irc_network_manager_finalize;
205 param_spec = g_param_spec_string (
207 "path of the global networks file",
208 "The path of the system-wide filename from which we have to load"
209 " the networks list",
211 G_PARAM_CONSTRUCT_ONLY |
213 G_PARAM_STATIC_NAME |
214 G_PARAM_STATIC_NICK |
215 G_PARAM_STATIC_BLURB);
216 g_object_class_install_property (object_class, PROP_GLOBAL_FILE, param_spec);
218 param_spec = g_param_spec_string (
220 "path of the user networks file",
221 "The path of user's filename from which we have to load"
222 " the networks list and to which we'll save his modifications",
224 G_PARAM_CONSTRUCT_ONLY |
226 G_PARAM_STATIC_NAME |
227 G_PARAM_STATIC_NICK |
228 G_PARAM_STATIC_BLURB);
229 g_object_class_install_property (object_class, PROP_USER_FILE, param_spec);
233 * empathy_irc_network_manager_new:
234 * @global_file: the path of the global networks file, or %NULL
235 * @user_file: the path of the user networks file, or %NULL
237 * Creates a new #EmpathyIrcNetworkManager
239 * Returns: a new #EmpathyIrcNetworkManager
241 EmpathyIrcNetworkManager *
242 empathy_irc_network_manager_new (const gchar *global_file,
243 const gchar *user_file)
245 EmpathyIrcNetworkManager *manager;
247 manager = g_object_new (EMPATHY_TYPE_IRC_NETWORK_MANAGER,
248 "global-file", global_file,
249 "user-file", user_file,
256 save_timeout (EmpathyIrcNetworkManager *self)
258 EmpathyIrcNetworkManagerPrivate *priv =
259 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
261 priv->save_timer_id = 0;
262 irc_network_manager_file_save (self);
268 reset_save_timeout (EmpathyIrcNetworkManager *self)
270 EmpathyIrcNetworkManagerPrivate *priv =
271 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
273 if (priv->save_timer_id > 0)
275 g_source_remove (priv->save_timer_id);
278 priv->save_timer_id = g_timeout_add_seconds (SAVE_TIMER,
279 (GSourceFunc) save_timeout, self);
283 network_modified (EmpathyIrcNetwork *network,
284 EmpathyIrcNetworkManager *self)
286 EmpathyIrcNetworkManagerPrivate *priv =
287 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
289 network->user_defined = TRUE;
293 priv->have_to_save = TRUE;
294 reset_save_timeout (self);
299 add_network (EmpathyIrcNetworkManager *self,
300 EmpathyIrcNetwork *network,
303 EmpathyIrcNetworkManagerPrivate *priv =
304 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
306 g_hash_table_insert (priv->networks, g_strdup (id), g_object_ref (network));
308 g_signal_connect (network, "modified", G_CALLBACK (network_modified), self);
312 * empathy_irc_network_manager_add:
313 * @manager: an #EmpathyIrcNetworkManager
314 * @network: the #EmpathyIrcNetwork to add
316 * Add an #EmpathyIrcNetwork to the given #EmpathyIrcNetworkManager.
320 empathy_irc_network_manager_add (EmpathyIrcNetworkManager *self,
321 EmpathyIrcNetwork *network)
323 EmpathyIrcNetworkManagerPrivate *priv;
326 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
327 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
329 priv = EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
331 /* generate an id for this network */
335 id = g_strdup_printf ("id%u", ++priv->last_id);
336 } while (g_hash_table_lookup (priv->networks, id) != NULL &&
337 priv->last_id < G_MAXUINT);
339 if (priv->last_id == G_MAXUINT)
341 DEBUG ("Can't add network: too many networks using a similiar ID");
345 DEBUG ("add server with \"%s\" as ID", id);
347 network->user_defined = TRUE;
348 add_network (self, network, id);
350 priv->have_to_save = TRUE;
351 reset_save_timeout (self);
357 * empathy_irc_network_manager_remove:
358 * @manager: an #EmpathyIrcNetworkManager
359 * @network: the #EmpathyIrcNetwork to remove
361 * Remove an #EmpathyIrcNetwork from the given #EmpathyIrcNetworkManager.
365 empathy_irc_network_manager_remove (EmpathyIrcNetworkManager *self,
366 EmpathyIrcNetwork *network)
368 EmpathyIrcNetworkManagerPrivate *priv;
370 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
371 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
373 priv = EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
375 network->user_defined = TRUE;
376 network->dropped = TRUE;
378 priv->have_to_save = TRUE;
379 reset_save_timeout (self);
383 append_network_to_list (const gchar *id,
384 EmpathyIrcNetwork *network,
387 if (network->dropped)
390 *list = g_slist_prepend (*list, g_object_ref (network));
394 * empathy_irc_network_manager_get_networks:
395 * @manager: an #EmpathyIrcNetworkManager
397 * Get the list of #EmpathyIrcNetwork associated with the given
400 * Returns: a new #GSList of refed #EmpathyIrcNetwork
403 empathy_irc_network_manager_get_networks (EmpathyIrcNetworkManager *self)
405 EmpathyIrcNetworkManagerPrivate *priv;
406 GSList *irc_networks = NULL;
408 g_return_val_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self), NULL);
410 priv = EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
412 g_hash_table_foreach (priv->networks, (GHFunc) append_network_to_list,
419 * API to save/load and parse the irc_networks file.
423 load_global_file (EmpathyIrcNetworkManager *self)
425 EmpathyIrcNetworkManagerPrivate *priv =
426 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
428 if (priv->global_file == NULL)
431 if (!g_file_test (priv->global_file, G_FILE_TEST_EXISTS))
433 DEBUG ("Global networks file %s doesn't exist", priv->global_file);
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 DEBUG ("User networks file %s doesn't exist", priv->global_file);
455 irc_network_manager_file_parse (self, priv->user_file, TRUE);
459 irc_network_manager_load_servers (EmpathyIrcNetworkManager *self)
461 EmpathyIrcNetworkManagerPrivate *priv =
462 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
464 priv->loading = TRUE;
466 load_global_file (self);
467 load_user_file (self);
469 priv->loading = FALSE;
470 priv->have_to_save = FALSE;
474 irc_network_manager_parse_irc_server (EmpathyIrcNetwork *network,
477 xmlNodePtr server_node;
479 for (server_node = node->children; server_node;
480 server_node = server_node->next)
482 gchar *address = NULL, *port = NULL, *ssl = NULL;
484 if (strcmp (server_node->name, "server") != 0)
487 address = xmlGetProp (server_node, "address");
488 port = xmlGetProp (server_node, "port");
489 ssl = xmlGetProp (server_node, "ssl");
494 gboolean have_ssl = FALSE;
495 EmpathyIrcServer *server;
498 port_nb = strtol (port, NULL, 10);
500 if (port_nb <= 0 || port_nb > G_MAXUINT16)
503 if (ssl == NULL || strcmp (ssl, "TRUE") == 0)
506 DEBUG ("parsed server %s port %d ssl %d", address, port_nb, have_ssl);
508 server = empathy_irc_server_new (address, port_nb, have_ssl);
509 empathy_irc_network_append_server (network, server);
522 irc_network_manager_parse_irc_network (EmpathyIrcNetworkManager *self,
524 gboolean user_defined)
526 EmpathyIrcNetworkManagerPrivate *priv =
527 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
528 EmpathyIrcNetwork *network;
533 id = xmlGetProp (node, "id");
534 if (xmlHasProp (node, "dropped"))
538 DEBUG ("the 'dropped' attribute shouldn't be used in the global file");
541 network = g_hash_table_lookup (priv->networks, id);
544 network->dropped = TRUE;
545 network->user_defined = TRUE;
551 if (!xmlHasProp (node, "name"))
554 name = xmlGetProp (node, "name");
555 network = empathy_irc_network_new (name);
557 if (xmlHasProp (node, "network_charset"))
560 charset = xmlGetProp (node, "network_charset");
561 g_object_set (network, "charset", charset, NULL);
565 add_network (self, network, id);
566 DEBUG ("add network %s (id %s)", name, id);
568 for (child = node->children; child; child = child->next)
572 tag = (gchar *) child->name;
573 str = (gchar *) xmlNodeGetContent (child);
578 if (strcmp (tag, "servers") == 0)
580 irc_network_manager_parse_irc_server (network, child);
586 network->user_defined = user_defined;
587 g_object_unref (network);
593 irc_network_manager_file_parse (EmpathyIrcNetworkManager *self,
594 const gchar *filename,
595 gboolean user_defined)
597 EmpathyIrcNetworkManagerPrivate *priv;
598 xmlParserCtxtPtr ctxt;
603 priv = EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
605 DEBUG ("Attempting to parse file:'%s'...", filename);
607 ctxt = xmlNewParserCtxt ();
609 /* Parse and validate the file. */
610 doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
613 g_warning ("Failed to parse file:'%s'", filename);
614 xmlFreeParserCtxt (ctxt);
618 if (!empathy_xml_validate (doc, IRC_NETWORKS_DTD_FILENAME)) {
619 g_warning ("Failed to validate file:'%s'", filename);
621 xmlFreeParserCtxt (ctxt);
625 /* The root node, networks. */
626 networks = xmlDocGetRootElement (doc);
628 for (node = networks->children; node; node = node->next)
630 irc_network_manager_parse_irc_network (self, node, user_defined);
634 xmlFreeParserCtxt (ctxt);
640 write_network_to_xml (const gchar *id,
641 EmpathyIrcNetwork *network,
644 xmlNodePtr network_node, servers_node;
646 gchar *name, *charset;
648 if (!network->user_defined)
649 /* no need to write this network to the XML */
652 network_node = xmlNewChild (root, NULL, "network", NULL);
653 xmlNewProp (network_node, "id", id);
655 if (network->dropped)
657 xmlNewProp (network_node, "dropped", "1");
661 g_object_get (network,
665 xmlNewProp (network_node, "name", name);
666 xmlNewProp (network_node, "network_charset", charset);
670 servers = empathy_irc_network_get_servers (network);
672 servers_node = xmlNewChild (network_node, NULL, "servers", NULL);
673 for (l = servers; l != NULL; l = g_slist_next (l))
675 EmpathyIrcServer *server;
676 xmlNodePtr server_node;
677 gchar *address, *tmp;
683 server_node = xmlNewChild (servers_node, NULL, "server", NULL);
685 g_object_get (server,
691 xmlNewProp (server_node, "address", address);
693 tmp = g_strdup_printf ("%u", port);
694 xmlNewProp (server_node, "port", tmp);
697 xmlNewProp (server_node, "ssl", ssl ? "TRUE": "FALSE");
703 g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
704 g_slist_free (servers);
708 irc_network_manager_file_save (EmpathyIrcNetworkManager *self)
710 EmpathyIrcNetworkManagerPrivate *priv =
711 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
715 if (priv->user_file == NULL)
717 DEBUG ("can't save: no user file defined");
721 DEBUG ("Saving IRC networks");
723 doc = xmlNewDoc ("1.0");
724 root = xmlNewNode (NULL, "networks");
725 xmlDocSetRootElement (doc, root);
727 g_hash_table_foreach (priv->networks, (GHFunc) write_network_to_xml, root);
729 /* Make sure the XML is indented properly */
730 xmlIndentTreeOutput = 1;
732 xmlSaveFormatFileEnc (priv->user_file, doc, "utf-8", 1);
738 priv->have_to_save = FALSE;
744 find_network_by_address (const gchar *id,
745 EmpathyIrcNetwork *network,
746 const gchar *address)
749 gboolean found = FALSE;
751 if (network->dropped)
754 servers = empathy_irc_network_get_servers (network);
756 for (l = servers; l != NULL && !found; l = g_slist_next (l))
758 EmpathyIrcServer *server = l->data;
761 g_object_get (server, "address", &_address, NULL);
762 found = (_address != NULL && strcmp (address, _address) == 0);
767 g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
768 g_slist_free (servers);
774 * empathy_irc_network_manager_find_network_by_address:
775 * @manager: an #EmpathyIrcNetworkManager
776 * @address: the server address to look for
778 * Find the #EmpathyIrcNetwork which owns an #EmpathyIrcServer
779 * that has the given address.
781 * Returns: the found #EmpathyIrcNetwork, or %NULL if not found.
784 empathy_irc_network_manager_find_network_by_address (
785 EmpathyIrcNetworkManager *self,
786 const gchar *address)
788 EmpathyIrcNetworkManagerPrivate *priv =
789 EMPATHY_IRC_NETWORK_MANAGER_GET_PRIVATE (self);
790 EmpathyIrcNetwork *network;
792 g_return_val_if_fail (address != NULL, NULL);
794 network = g_hash_table_find (priv->networks,
795 (GHRFunc) find_network_by_address, (gchar *) address);