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>
22 #include "empathy-irc-network-manager.h"
26 #include "empathy-utils.h"
28 #define DEBUG_FLAG EMPATHY_DEBUG_IRC
29 #include "empathy-debug.h"
31 #define IRC_NETWORKS_DTD_RESOURCENAME "/org/gnome/Empathy/empathy-irc-networks.dtd"
32 #define IRC_NETWORKS_FILENAME "irc-networks.xml"
35 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIrcNetworkManager)
43 /* Do we have to save modifications to the user file ? */
44 gboolean have_to_save;
45 /* Are we loading networks from XML files ? */
47 /* source id of the autosave timer */
49 } EmpathyIrcNetworkManagerPriv;
59 G_DEFINE_TYPE (EmpathyIrcNetworkManager, empathy_irc_network_manager,
62 static void irc_network_manager_load_servers (
63 EmpathyIrcNetworkManager *manager);
64 static gboolean irc_network_manager_file_parse (
65 EmpathyIrcNetworkManager *manager, const gchar *filename,
66 gboolean user_defined);
67 static gboolean irc_network_manager_file_save (
68 EmpathyIrcNetworkManager *manager);
71 empathy_irc_network_manager_get_property (GObject *object,
76 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
77 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
81 case PROP_GLOBAL_FILE:
82 g_value_set_string (value, priv->global_file);
85 g_value_set_string (value, priv->user_file);
88 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
94 empathy_irc_network_manager_set_property (GObject *object,
99 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
100 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
104 case PROP_GLOBAL_FILE:
105 g_free (priv->global_file);
106 priv->global_file = g_value_dup_string (value);
109 g_free (priv->user_file);
110 priv->user_file = g_value_dup_string (value);
113 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
119 empathy_irc_network_manager_constructor (GType type,
121 GObjectConstructParam *props)
124 EmpathyIrcNetworkManager *self;
126 /* Parent constructor chain */
127 obj = G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->
128 constructor (type, n_props, props);
130 self = EMPATHY_IRC_NETWORK_MANAGER (obj);
131 irc_network_manager_load_servers (self);
137 empathy_irc_network_manager_finalize (GObject *object)
139 EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
140 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
142 if (priv->save_timer_id > 0)
144 g_source_remove (priv->save_timer_id);
147 if (priv->have_to_save)
149 irc_network_manager_file_save (self);
152 g_free (priv->global_file);
153 g_free (priv->user_file);
155 g_hash_table_unref (priv->networks);
157 G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->finalize (object);
161 empathy_irc_network_manager_init (EmpathyIrcNetworkManager *self)
163 EmpathyIrcNetworkManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
164 EMPATHY_TYPE_IRC_NETWORK_MANAGER, EmpathyIrcNetworkManagerPriv);
168 priv->networks = g_hash_table_new_full (g_str_hash, g_str_equal,
169 (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref);
173 priv->have_to_save = FALSE;
174 priv->loading = FALSE;
175 priv->save_timer_id = 0;
179 empathy_irc_network_manager_class_init (EmpathyIrcNetworkManagerClass *klass)
181 GObjectClass *object_class = G_OBJECT_CLASS (klass);
182 GParamSpec *param_spec;
184 object_class->constructor = empathy_irc_network_manager_constructor;
185 object_class->get_property = empathy_irc_network_manager_get_property;
186 object_class->set_property = empathy_irc_network_manager_set_property;
188 g_type_class_add_private (object_class, sizeof (EmpathyIrcNetworkManagerPriv));
190 object_class->finalize = empathy_irc_network_manager_finalize;
192 param_spec = g_param_spec_string (
194 "path of the global networks file",
195 "The path of the system-wide filename from which we have to load"
196 " the networks list",
198 G_PARAM_CONSTRUCT_ONLY |
200 G_PARAM_STATIC_NAME |
201 G_PARAM_STATIC_NICK |
202 G_PARAM_STATIC_BLURB);
203 g_object_class_install_property (object_class, PROP_GLOBAL_FILE, param_spec);
205 param_spec = g_param_spec_string (
207 "path of the user networks file",
208 "The path of user's filename from which we have to load"
209 " the networks list and to which we'll save his modifications",
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_USER_FILE, param_spec);
220 * empathy_irc_network_manager_new:
221 * @global_file: the path of the global networks file, or %NULL
222 * @user_file: the path of the user networks file, or %NULL
224 * Creates a new #EmpathyIrcNetworkManager
226 * Returns: a new #EmpathyIrcNetworkManager
228 EmpathyIrcNetworkManager *
229 empathy_irc_network_manager_new (const gchar *global_file,
230 const gchar *user_file)
232 EmpathyIrcNetworkManager *manager;
234 manager = g_object_new (EMPATHY_TYPE_IRC_NETWORK_MANAGER,
235 "global-file", global_file,
236 "user-file", user_file,
243 save_timeout (EmpathyIrcNetworkManager *self)
245 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
247 priv->save_timer_id = 0;
248 irc_network_manager_file_save (self);
254 reset_save_timeout (EmpathyIrcNetworkManager *self)
256 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
258 if (priv->save_timer_id > 0)
260 g_source_remove (priv->save_timer_id);
263 priv->save_timer_id = g_timeout_add_seconds (SAVE_TIMER,
264 (GSourceFunc) save_timeout, self);
268 network_modified (EmpathyIrcNetwork *network,
269 EmpathyIrcNetworkManager *self)
271 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
273 network->user_defined = TRUE;
277 priv->have_to_save = TRUE;
278 reset_save_timeout (self);
283 add_network (EmpathyIrcNetworkManager *self,
284 EmpathyIrcNetwork *network,
287 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
289 g_hash_table_insert (priv->networks, g_strdup (id), g_object_ref (network));
291 g_signal_connect (network, "modified", G_CALLBACK (network_modified), self);
295 * empathy_irc_network_manager_add:
296 * @manager: an #EmpathyIrcNetworkManager
297 * @network: the #EmpathyIrcNetwork to add
299 * Add an #EmpathyIrcNetwork to the given #EmpathyIrcNetworkManager.
303 empathy_irc_network_manager_add (EmpathyIrcNetworkManager *self,
304 EmpathyIrcNetwork *network)
306 EmpathyIrcNetworkManagerPriv *priv;
309 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
310 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
312 priv = GET_PRIV (self);
314 /* generate an id for this network */
318 id = g_strdup_printf ("id%u", ++priv->last_id);
319 } while (g_hash_table_lookup (priv->networks, id) != NULL &&
320 priv->last_id < G_MAXUINT);
322 if (priv->last_id == G_MAXUINT)
324 DEBUG ("Can't add network: too many networks using a similar ID");
328 DEBUG ("add server with \"%s\" as ID", id);
330 network->user_defined = TRUE;
331 add_network (self, network, id);
333 priv->have_to_save = TRUE;
334 reset_save_timeout (self);
340 * empathy_irc_network_manager_remove:
341 * @manager: an #EmpathyIrcNetworkManager
342 * @network: the #EmpathyIrcNetwork to remove
344 * Remove an #EmpathyIrcNetwork from the given #EmpathyIrcNetworkManager.
348 empathy_irc_network_manager_remove (EmpathyIrcNetworkManager *self,
349 EmpathyIrcNetwork *network)
351 EmpathyIrcNetworkManagerPriv *priv;
353 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
354 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
356 priv = GET_PRIV (self);
358 network->user_defined = TRUE;
359 network->dropped = TRUE;
361 priv->have_to_save = TRUE;
362 reset_save_timeout (self);
366 append_active_networks_to_list (const gchar *id,
367 EmpathyIrcNetwork *network,
370 if (network->dropped)
373 *list = g_slist_prepend (*list, g_object_ref (network));
377 append_dropped_networks_to_list (const gchar *id,
378 EmpathyIrcNetwork *network,
381 if (!network->dropped)
384 *list = g_slist_prepend (*list, g_object_ref (network));
388 get_network_list (EmpathyIrcNetworkManager *self,
391 EmpathyIrcNetworkManagerPriv *priv;
392 GSList *irc_networks = NULL;
394 g_return_val_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self), NULL);
396 priv = GET_PRIV (self);
400 g_hash_table_foreach (priv->networks,
401 (GHFunc) append_active_networks_to_list, &irc_networks);
405 g_hash_table_foreach (priv->networks,
406 (GHFunc) append_dropped_networks_to_list, &irc_networks);
413 * empathy_irc_network_manager_get_networks:
414 * @manager: an #EmpathyIrcNetworkManager
416 * Get the list of #EmpathyIrcNetwork associated with the given
419 * Returns: a new #GSList of refed #EmpathyIrcNetwork
422 empathy_irc_network_manager_get_networks (EmpathyIrcNetworkManager *self)
424 return get_network_list (self, TRUE);
428 * empathy_irc_network_manager_get_dropped_networks:
429 * @manager: an #EmpathyIrcNetworkManager
431 * Get the list of dropped #EmpathyIrcNetworks associated with the given
434 * Returns: a new #GSList of refed dropped #EmpathyIrcNetworks
437 empathy_irc_network_manager_get_dropped_networks (EmpathyIrcNetworkManager *self)
439 return get_network_list (self, FALSE);
443 * API to save/load and parse the irc_networks file.
447 load_global_file (EmpathyIrcNetworkManager *self)
449 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
451 if (priv->global_file == NULL)
454 if (!g_file_test (priv->global_file, G_FILE_TEST_EXISTS))
456 DEBUG ("Global networks file %s doesn't exist", priv->global_file);
460 irc_network_manager_file_parse (self, priv->global_file, FALSE);
464 load_user_file (EmpathyIrcNetworkManager *self)
466 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
468 if (priv->user_file == NULL)
471 if (!g_file_test (priv->user_file, G_FILE_TEST_EXISTS))
473 DEBUG ("User networks file %s doesn't exist", priv->global_file);
477 irc_network_manager_file_parse (self, priv->user_file, TRUE);
481 irc_network_manager_load_servers (EmpathyIrcNetworkManager *self)
483 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
485 priv->loading = TRUE;
487 load_global_file (self);
488 load_user_file (self);
490 priv->loading = FALSE;
491 priv->have_to_save = FALSE;
495 irc_network_manager_parse_irc_server (EmpathyIrcNetwork *network,
498 xmlNodePtr server_node;
500 for (server_node = node->children; server_node;
501 server_node = server_node->next)
503 gchar *address = NULL, *port = NULL, *ssl = NULL;
505 if (strcmp ((const gchar *) server_node->name, "server") != 0)
508 address = (gchar *) xmlGetProp (server_node, (const xmlChar *) "address");
509 port = (gchar *) xmlGetProp (server_node, (const xmlChar *) "port");
510 ssl = (gchar *) xmlGetProp (server_node, (const xmlChar *) "ssl");
515 gboolean have_ssl = FALSE;
516 EmpathyIrcServer *server;
519 port_nb = strtol (port, NULL, 10);
521 if (port_nb <= 0 || port_nb > G_MAXUINT16)
524 if (ssl == NULL || strcmp (ssl, "TRUE") == 0)
527 DEBUG ("parsed server %s port %d ssl %d", address, port_nb, have_ssl);
529 server = empathy_irc_server_new (address, port_nb, have_ssl);
530 empathy_irc_network_append_server (network, server);
543 irc_network_manager_parse_irc_network (EmpathyIrcNetworkManager *self,
545 gboolean user_defined)
547 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
548 EmpathyIrcNetwork *network;
553 id = (gchar *) xmlGetProp (node, (const xmlChar *) "id");
554 if (xmlHasProp (node, (const xmlChar *) "dropped"))
558 DEBUG ("the 'dropped' attribute shouldn't be used in the global file");
561 network = g_hash_table_lookup (priv->networks, id);
564 network->dropped = TRUE;
565 network->user_defined = TRUE;
571 if (!xmlHasProp (node, (const xmlChar *) "name"))
574 name = (gchar *) xmlGetProp (node, (const xmlChar *) "name");
575 network = empathy_irc_network_new (name);
577 if (xmlHasProp (node, (const xmlChar *) "network_charset"))
580 charset = (gchar *) xmlGetProp (node, (const xmlChar *) "network_charset");
581 g_object_set (network, "charset", charset, NULL);
585 add_network (self, network, id);
586 DEBUG ("add network %s (id %s)", name, id);
588 for (child = node->children; child; child = child->next)
592 tag = (gchar *) child->name;
593 str = (gchar *) xmlNodeGetContent (child);
598 if (strcmp (tag, "servers") == 0)
600 irc_network_manager_parse_irc_server (network, child);
606 network->user_defined = user_defined;
607 g_object_unref (network);
613 irc_network_manager_file_parse (EmpathyIrcNetworkManager *self,
614 const gchar *filename,
615 gboolean user_defined)
617 xmlParserCtxtPtr ctxt;
622 DEBUG ("Attempting to parse file:'%s'...", filename);
624 ctxt = xmlNewParserCtxt ();
626 /* Parse and validate the file. */
627 doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
630 g_warning ("Failed to parse file:'%s'", filename);
631 xmlFreeParserCtxt (ctxt);
635 if (!empathy_xml_validate_from_resource (doc, IRC_NETWORKS_DTD_RESOURCENAME)) {
636 g_warning ("Failed to validate file:'%s'", filename);
638 xmlFreeParserCtxt (ctxt);
642 /* The root node, networks. */
643 networks = xmlDocGetRootElement (doc);
645 for (node = networks->children; node; node = node->next)
647 irc_network_manager_parse_irc_network (self, node, user_defined);
651 xmlFreeParserCtxt (ctxt);
657 write_network_to_xml (const gchar *id,
658 EmpathyIrcNetwork *network,
661 xmlNodePtr network_node, servers_node;
663 gchar *name, *charset;
665 if (!network->user_defined)
666 /* no need to write this network to the XML */
669 network_node = xmlNewChild (root, NULL, (const xmlChar *) "network", NULL);
670 xmlNewProp (network_node, (const xmlChar *) "id", (const xmlChar *) id);
672 if (network->dropped)
674 xmlNewProp (network_node, (const xmlChar *) "dropped",
675 (const xmlChar *) "1");
679 g_object_get (network,
683 xmlNewProp (network_node, (const xmlChar *) "name", (const xmlChar *) name);
684 xmlNewProp (network_node, (const xmlChar *) "network_charset",
685 (const xmlChar *) charset);
689 servers = empathy_irc_network_get_servers (network);
691 servers_node = xmlNewChild (network_node, NULL, (const xmlChar *) "servers",
693 for (l = servers; l != NULL; l = g_slist_next (l))
695 EmpathyIrcServer *server;
696 xmlNodePtr server_node;
697 gchar *address, *tmp;
703 server_node = xmlNewChild (servers_node, NULL, (const xmlChar *) "server",
706 g_object_get (server,
712 xmlNewProp (server_node, (const xmlChar *) "address",
713 (const xmlChar *) address);
715 tmp = g_strdup_printf ("%u", port);
716 xmlNewProp (server_node, (const xmlChar *) "port",
717 (const xmlChar *) tmp);
720 xmlNewProp (server_node, (const xmlChar *) "ssl",
721 ssl ? (const xmlChar *) "TRUE": (const xmlChar *) "FALSE");
727 g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
728 g_slist_free (servers);
732 irc_network_manager_file_save (EmpathyIrcNetworkManager *self)
734 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
738 if (priv->user_file == NULL)
740 DEBUG ("can't save: no user file defined");
744 DEBUG ("Saving IRC networks");
746 doc = xmlNewDoc ((const xmlChar *) "1.0");
747 root = xmlNewNode (NULL, (const xmlChar *) "networks");
748 xmlDocSetRootElement (doc, root);
750 g_hash_table_foreach (priv->networks, (GHFunc) write_network_to_xml, root);
752 /* Make sure the XML is indented properly */
753 xmlIndentTreeOutput = 1;
755 xmlSaveFormatFileEnc (priv->user_file, doc, "utf-8", 1);
760 priv->have_to_save = FALSE;
766 find_network_by_address (const gchar *id,
767 EmpathyIrcNetwork *network,
768 const gchar *address)
771 gboolean found = FALSE;
773 if (network->dropped)
776 servers = empathy_irc_network_get_servers (network);
778 for (l = servers; l != NULL && !found; l = g_slist_next (l))
780 EmpathyIrcServer *server = l->data;
783 g_object_get (server, "address", &_address, NULL);
784 found = (_address != NULL && strcmp (address, _address) == 0);
789 g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
790 g_slist_free (servers);
796 * empathy_irc_network_manager_find_network_by_address:
797 * @manager: an #EmpathyIrcNetworkManager
798 * @address: the server address to look for
800 * Find the #EmpathyIrcNetwork which owns an #EmpathyIrcServer
801 * that has the given address.
803 * Returns: the found #EmpathyIrcNetwork, or %NULL if not found.
806 empathy_irc_network_manager_find_network_by_address (
807 EmpathyIrcNetworkManager *self,
808 const gchar *address)
810 EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
811 EmpathyIrcNetwork *network;
813 g_return_val_if_fail (address != NULL, NULL);
815 network = g_hash_table_find (priv->networks,
816 (GHRFunc) find_network_by_address, (gchar *) address);
821 EmpathyIrcNetworkManager *
822 empathy_irc_network_manager_dup_default (void)
824 static EmpathyIrcNetworkManager *default_mgr = NULL;
825 gchar *dir, *user_file_with_path, *global_file_with_path;
827 if (default_mgr != NULL)
828 return g_object_ref (default_mgr);
830 dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
831 g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
832 user_file_with_path = g_build_filename (dir, IRC_NETWORKS_FILENAME, NULL);
835 global_file_with_path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
836 "libempathy", IRC_NETWORKS_FILENAME, NULL);
837 if (!g_file_test (global_file_with_path, G_FILE_TEST_EXISTS))
839 g_free (global_file_with_path);
840 global_file_with_path = g_build_filename (DATADIR, "empathy",
841 IRC_NETWORKS_FILENAME, NULL);
844 default_mgr = empathy_irc_network_manager_new (
845 global_file_with_path, user_file_with_path);
847 g_object_add_weak_pointer (G_OBJECT (default_mgr), (gpointer *) &default_mgr);
849 g_free (global_file_with_path);
850 g_free (user_file_with_path);