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 "empathy-utils.h"
29 #include "empathy-irc-network-manager.h"
31 #define DEBUG_FLAG EMPATHY_DEBUG_IRC
32 #include "empathy-debug.h"
34 #define IRC_NETWORKS_DTD_FILENAME "empathy-irc-networks.dtd"
37 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIrcNetworkManager)
45 /* Do we have to save modifications to the user file ? */
46 gboolean have_to_save;
47 /* Are we loading networks from XML files ? */
49 /* source id of the autosave timer */
51 } EmpathyIrcNetworkManagerPriv;
61 G_DEFINE_TYPE (EmpathyIrcNetworkManager, empathy_irc_network_manager,
64 static void irc_network_manager_load_servers (
65 EmpathyIrcNetworkManager *manager);
66 static gboolean irc_network_manager_file_parse (
67 EmpathyIrcNetworkManager *manager, const gchar *filename,
68 gboolean user_defined);
69 static gboolean irc_network_manager_file_save (
70 EmpathyIrcNetworkManager *manager);
73 empathy_irc_network_manager_get_property (GObject *object,
78 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
79 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
83 case PROP_GLOBAL_FILE:
84 g_value_set_string (value, priv->global_file);
87 g_value_set_string (value, priv->user_file);
90 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
96 empathy_irc_network_manager_set_property (GObject *object,
101 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
102 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
106 case PROP_GLOBAL_FILE:
107 g_free (priv->global_file);
108 priv->global_file = g_value_dup_string (value);
111 g_free (priv->user_file);
112 priv->user_file = g_value_dup_string (value);
115 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
121 empathy_irc_network_manager_constructor (GType type,
123 GObjectConstructParam *props)
126 EmpathyIrcNetworkManager *self;
128 /* Parent constructor chain */
129 obj = G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->
130 constructor (type, n_props, props);
132 self = EMPATHY_IRC_NETWORK_MANAGER (obj);
133 irc_network_manager_load_servers (self);
139 empathy_irc_network_manager_finalize (GObject *object)
141 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
142 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
144 if (priv->save_timer_id > 0)
146 g_source_remove (priv->save_timer_id);
149 if (priv->have_to_save)
151 irc_network_manager_file_save (self);
154 g_free (priv->global_file);
155 g_free (priv->user_file);
157 g_hash_table_destroy (priv->networks);
159 G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->finalize (object);
163 empathy_irc_network_manager_init (EmpathyIrcNetworkManager *self)
165 EmpathyIrcNetworkManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
166 EMPATHY_TYPE_IRC_NETWORK_MANAGER, EmpathyIrcNetworkManagerPriv);
170 priv->networks = g_hash_table_new_full (g_str_hash, g_str_equal,
171 (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref);
175 priv->have_to_save = FALSE;
176 priv->loading = FALSE;
177 priv->save_timer_id = 0;
181 empathy_irc_network_manager_class_init (EmpathyIrcNetworkManagerClass *klass)
183 GObjectClass *object_class = G_OBJECT_CLASS (klass);
184 GParamSpec *param_spec;
186 object_class->constructor = empathy_irc_network_manager_constructor;
187 object_class->get_property = empathy_irc_network_manager_get_property;
188 object_class->set_property = empathy_irc_network_manager_set_property;
190 g_type_class_add_private (object_class, sizeof (EmpathyIrcNetworkManagerPriv));
192 object_class->finalize = empathy_irc_network_manager_finalize;
194 param_spec = g_param_spec_string (
196 "path of the global networks file",
197 "The path of the system-wide filename from which we have to load"
198 " the networks list",
200 G_PARAM_CONSTRUCT_ONLY |
202 G_PARAM_STATIC_NAME |
203 G_PARAM_STATIC_NICK |
204 G_PARAM_STATIC_BLURB);
205 g_object_class_install_property (object_class, PROP_GLOBAL_FILE, param_spec);
207 param_spec = g_param_spec_string (
209 "path of the user networks file",
210 "The path of user's filename from which we have to load"
211 " the networks list and to which we'll save his modifications",
213 G_PARAM_CONSTRUCT_ONLY |
215 G_PARAM_STATIC_NAME |
216 G_PARAM_STATIC_NICK |
217 G_PARAM_STATIC_BLURB);
218 g_object_class_install_property (object_class, PROP_USER_FILE, param_spec);
222 * empathy_irc_network_manager_new:
223 * @global_file: the path of the global networks file, or %NULL
224 * @user_file: the path of the user networks file, or %NULL
226 * Creates a new #EmpathyIrcNetworkManager
228 * Returns: a new #EmpathyIrcNetworkManager
230 EmpathyIrcNetworkManager *
231 empathy_irc_network_manager_new (const gchar *global_file,
232 const gchar *user_file)
234 EmpathyIrcNetworkManager *manager;
236 manager = g_object_new (EMPATHY_TYPE_IRC_NETWORK_MANAGER,
237 "global-file", global_file,
238 "user-file", user_file,
245 save_timeout (EmpathyIrcNetworkManager *self)
247 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
249 priv->save_timer_id = 0;
250 irc_network_manager_file_save (self);
256 reset_save_timeout (EmpathyIrcNetworkManager *self)
258 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
260 if (priv->save_timer_id > 0)
262 g_source_remove (priv->save_timer_id);
265 priv->save_timer_id = g_timeout_add_seconds (SAVE_TIMER,
266 (GSourceFunc) save_timeout, self);
270 network_modified (EmpathyIrcNetwork *network,
271 EmpathyIrcNetworkManager *self)
273 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
275 network->user_defined = TRUE;
279 priv->have_to_save = TRUE;
280 reset_save_timeout (self);
285 add_network (EmpathyIrcNetworkManager *self,
286 EmpathyIrcNetwork *network,
289 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
291 g_hash_table_insert (priv->networks, g_strdup (id), g_object_ref (network));
293 g_signal_connect (network, "modified", G_CALLBACK (network_modified), self);
297 * empathy_irc_network_manager_add:
298 * @manager: an #EmpathyIrcNetworkManager
299 * @network: the #EmpathyIrcNetwork to add
301 * Add an #EmpathyIrcNetwork to the given #EmpathyIrcNetworkManager.
305 empathy_irc_network_manager_add (EmpathyIrcNetworkManager *self,
306 EmpathyIrcNetwork *network)
308 EmpathyIrcNetworkManagerPriv *priv;
311 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
312 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
314 priv = GET_PRIV (self);
316 /* generate an id for this network */
320 id = g_strdup_printf ("id%u", ++priv->last_id);
321 } while (g_hash_table_lookup (priv->networks, id) != NULL &&
322 priv->last_id < G_MAXUINT);
324 if (priv->last_id == G_MAXUINT)
326 DEBUG ("Can't add network: too many networks using a similiar ID");
330 DEBUG ("add server with \"%s\" as ID", id);
332 network->user_defined = TRUE;
333 add_network (self, network, id);
335 priv->have_to_save = TRUE;
336 reset_save_timeout (self);
342 * empathy_irc_network_manager_remove:
343 * @manager: an #EmpathyIrcNetworkManager
344 * @network: the #EmpathyIrcNetwork to remove
346 * Remove an #EmpathyIrcNetwork from the given #EmpathyIrcNetworkManager.
350 empathy_irc_network_manager_remove (EmpathyIrcNetworkManager *self,
351 EmpathyIrcNetwork *network)
353 EmpathyIrcNetworkManagerPriv *priv;
355 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
356 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
358 priv = GET_PRIV (self);
360 network->user_defined = TRUE;
361 network->dropped = TRUE;
363 priv->have_to_save = TRUE;
364 reset_save_timeout (self);
368 append_network_to_list (const gchar *id,
369 EmpathyIrcNetwork *network,
372 if (network->dropped)
375 *list = g_slist_prepend (*list, g_object_ref (network));
379 * empathy_irc_network_manager_get_networks:
380 * @manager: an #EmpathyIrcNetworkManager
382 * Get the list of #EmpathyIrcNetwork associated with the given
385 * Returns: a new #GSList of refed #EmpathyIrcNetwork
388 empathy_irc_network_manager_get_networks (EmpathyIrcNetworkManager *self)
390 EmpathyIrcNetworkManagerPriv *priv;
391 GSList *irc_networks = NULL;
393 g_return_val_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self), NULL);
395 priv = GET_PRIV (self);
397 g_hash_table_foreach (priv->networks, (GHFunc) append_network_to_list,
404 * API to save/load and parse the irc_networks file.
408 load_global_file (EmpathyIrcNetworkManager *self)
410 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
412 if (priv->global_file == NULL)
415 if (!g_file_test (priv->global_file, G_FILE_TEST_EXISTS))
417 DEBUG ("Global networks file %s doesn't exist", priv->global_file);
421 irc_network_manager_file_parse (self, priv->global_file, FALSE);
425 load_user_file (EmpathyIrcNetworkManager *self)
427 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
429 if (priv->user_file == NULL)
432 if (!g_file_test (priv->user_file, G_FILE_TEST_EXISTS))
434 DEBUG ("User networks file %s doesn't exist", priv->global_file);
438 irc_network_manager_file_parse (self, priv->user_file, TRUE);
442 irc_network_manager_load_servers (EmpathyIrcNetworkManager *self)
444 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
446 priv->loading = TRUE;
448 load_global_file (self);
449 load_user_file (self);
451 priv->loading = FALSE;
452 priv->have_to_save = FALSE;
456 irc_network_manager_parse_irc_server (EmpathyIrcNetwork *network,
459 xmlNodePtr server_node;
461 for (server_node = node->children; server_node;
462 server_node = server_node->next)
464 gchar *address = NULL, *port = NULL, *ssl = NULL;
466 if (strcmp (server_node->name, "server") != 0)
469 address = xmlGetProp (server_node, "address");
470 port = xmlGetProp (server_node, "port");
471 ssl = xmlGetProp (server_node, "ssl");
476 gboolean have_ssl = FALSE;
477 EmpathyIrcServer *server;
480 port_nb = strtol (port, NULL, 10);
482 if (port_nb <= 0 || port_nb > G_MAXUINT16)
485 if (ssl == NULL || strcmp (ssl, "TRUE") == 0)
488 DEBUG ("parsed server %s port %d ssl %d", address, port_nb, have_ssl);
490 server = empathy_irc_server_new (address, port_nb, have_ssl);
491 empathy_irc_network_append_server (network, server);
504 irc_network_manager_parse_irc_network (EmpathyIrcNetworkManager *self,
506 gboolean user_defined)
508 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
509 EmpathyIrcNetwork *network;
514 id = xmlGetProp (node, "id");
515 if (xmlHasProp (node, "dropped"))
519 DEBUG ("the 'dropped' attribute shouldn't be used in the global file");
522 network = g_hash_table_lookup (priv->networks, id);
525 network->dropped = TRUE;
526 network->user_defined = TRUE;
532 if (!xmlHasProp (node, "name"))
535 name = xmlGetProp (node, "name");
536 network = empathy_irc_network_new (name);
538 if (xmlHasProp (node, "network_charset"))
541 charset = xmlGetProp (node, "network_charset");
542 g_object_set (network, "charset", charset, NULL);
546 add_network (self, network, id);
547 DEBUG ("add network %s (id %s)", name, id);
549 for (child = node->children; child; child = child->next)
553 tag = (gchar *) child->name;
554 str = (gchar *) xmlNodeGetContent (child);
559 if (strcmp (tag, "servers") == 0)
561 irc_network_manager_parse_irc_server (network, child);
567 network->user_defined = user_defined;
568 g_object_unref (network);
574 irc_network_manager_file_parse (EmpathyIrcNetworkManager *self,
575 const gchar *filename,
576 gboolean user_defined)
578 EmpathyIrcNetworkManagerPriv *priv;
579 xmlParserCtxtPtr ctxt;
584 priv = GET_PRIV (self);
586 DEBUG ("Attempting to parse file:'%s'...", filename);
588 ctxt = xmlNewParserCtxt ();
590 /* Parse and validate the file. */
591 doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
594 g_warning ("Failed to parse file:'%s'", filename);
595 xmlFreeParserCtxt (ctxt);
599 if (!empathy_xml_validate (doc, IRC_NETWORKS_DTD_FILENAME)) {
600 g_warning ("Failed to validate file:'%s'", filename);
602 xmlFreeParserCtxt (ctxt);
606 /* The root node, networks. */
607 networks = xmlDocGetRootElement (doc);
609 for (node = networks->children; node; node = node->next)
611 irc_network_manager_parse_irc_network (self, node, user_defined);
615 xmlFreeParserCtxt (ctxt);
621 write_network_to_xml (const gchar *id,
622 EmpathyIrcNetwork *network,
625 xmlNodePtr network_node, servers_node;
627 gchar *name, *charset;
629 if (!network->user_defined)
630 /* no need to write this network to the XML */
633 network_node = xmlNewChild (root, NULL, "network", NULL);
634 xmlNewProp (network_node, "id", id);
636 if (network->dropped)
638 xmlNewProp (network_node, "dropped", "1");
642 g_object_get (network,
646 xmlNewProp (network_node, "name", name);
647 xmlNewProp (network_node, "network_charset", charset);
651 servers = empathy_irc_network_get_servers (network);
653 servers_node = xmlNewChild (network_node, NULL, "servers", NULL);
654 for (l = servers; l != NULL; l = g_slist_next (l))
656 EmpathyIrcServer *server;
657 xmlNodePtr server_node;
658 gchar *address, *tmp;
664 server_node = xmlNewChild (servers_node, NULL, "server", NULL);
666 g_object_get (server,
672 xmlNewProp (server_node, "address", address);
674 tmp = g_strdup_printf ("%u", port);
675 xmlNewProp (server_node, "port", tmp);
678 xmlNewProp (server_node, "ssl", ssl ? "TRUE": "FALSE");
684 g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
685 g_slist_free (servers);
689 irc_network_manager_file_save (EmpathyIrcNetworkManager *self)
691 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
695 if (priv->user_file == NULL)
697 DEBUG ("can't save: no user file defined");
701 DEBUG ("Saving IRC networks");
703 doc = xmlNewDoc ("1.0");
704 root = xmlNewNode (NULL, "networks");
705 xmlDocSetRootElement (doc, root);
707 g_hash_table_foreach (priv->networks, (GHFunc) write_network_to_xml, root);
709 /* Make sure the XML is indented properly */
710 xmlIndentTreeOutput = 1;
712 xmlSaveFormatFileEnc (priv->user_file, doc, "utf-8", 1);
718 priv->have_to_save = FALSE;
724 find_network_by_address (const gchar *id,
725 EmpathyIrcNetwork *network,
726 const gchar *address)
729 gboolean found = FALSE;
731 if (network->dropped)
734 servers = empathy_irc_network_get_servers (network);
736 for (l = servers; l != NULL && !found; l = g_slist_next (l))
738 EmpathyIrcServer *server = l->data;
741 g_object_get (server, "address", &_address, NULL);
742 found = (_address != NULL && strcmp (address, _address) == 0);
747 g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
748 g_slist_free (servers);
754 * empathy_irc_network_manager_find_network_by_address:
755 * @manager: an #EmpathyIrcNetworkManager
756 * @address: the server address to look for
758 * Find the #EmpathyIrcNetwork which owns an #EmpathyIrcServer
759 * that has the given address.
761 * Returns: the found #EmpathyIrcNetwork, or %NULL if not found.
764 empathy_irc_network_manager_find_network_by_address (
765 EmpathyIrcNetworkManager *self,
766 const gchar *address)
768 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
769 EmpathyIrcNetwork *network;
771 g_return_val_if_fail (address != NULL, NULL);
773 network = g_hash_table_find (priv->networks,
774 (GHRFunc) find_network_by_address, (gchar *) address);