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"
35 #define IRC_NETWORKS_FILENAME "irc-networks.xml"
38 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIrcNetworkManager)
46 /* Do we have to save modifications to the user file ? */
47 gboolean have_to_save
;
48 /* Are we loading networks from XML files ? */
50 /* source id of the autosave timer */
52 } EmpathyIrcNetworkManagerPriv
;
62 G_DEFINE_TYPE (EmpathyIrcNetworkManager
, empathy_irc_network_manager
,
65 static void irc_network_manager_load_servers (
66 EmpathyIrcNetworkManager
*manager
);
67 static gboolean
irc_network_manager_file_parse (
68 EmpathyIrcNetworkManager
*manager
, const gchar
*filename
,
69 gboolean user_defined
);
70 static gboolean
irc_network_manager_file_save (
71 EmpathyIrcNetworkManager
*manager
);
74 empathy_irc_network_manager_get_property (GObject
*object
,
79 EmpathyIrcNetworkManager
*self
= EMPATHY_IRC_NETWORK_MANAGER (object
);
80 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
84 case PROP_GLOBAL_FILE
:
85 g_value_set_string (value
, priv
->global_file
);
88 g_value_set_string (value
, priv
->user_file
);
91 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
97 empathy_irc_network_manager_set_property (GObject
*object
,
102 EmpathyIrcNetworkManager
*self
= EMPATHY_IRC_NETWORK_MANAGER (object
);
103 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
107 case PROP_GLOBAL_FILE
:
108 g_free (priv
->global_file
);
109 priv
->global_file
= g_value_dup_string (value
);
112 g_free (priv
->user_file
);
113 priv
->user_file
= g_value_dup_string (value
);
116 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
122 empathy_irc_network_manager_constructor (GType type
,
124 GObjectConstructParam
*props
)
127 EmpathyIrcNetworkManager
*self
;
129 /* Parent constructor chain */
130 obj
= G_OBJECT_CLASS (empathy_irc_network_manager_parent_class
)->
131 constructor (type
, n_props
, props
);
133 self
= EMPATHY_IRC_NETWORK_MANAGER (obj
);
134 irc_network_manager_load_servers (self
);
140 empathy_irc_network_manager_finalize (GObject
*object
)
142 EmpathyIrcNetworkManager
*self
= EMPATHY_IRC_NETWORK_MANAGER (object
);
143 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
145 if (priv
->save_timer_id
> 0)
147 g_source_remove (priv
->save_timer_id
);
150 if (priv
->have_to_save
)
152 irc_network_manager_file_save (self
);
155 g_free (priv
->global_file
);
156 g_free (priv
->user_file
);
158 g_hash_table_unref (priv
->networks
);
160 G_OBJECT_CLASS (empathy_irc_network_manager_parent_class
)->finalize (object
);
164 empathy_irc_network_manager_init (EmpathyIrcNetworkManager
*self
)
166 EmpathyIrcNetworkManagerPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
167 EMPATHY_TYPE_IRC_NETWORK_MANAGER
, EmpathyIrcNetworkManagerPriv
);
171 priv
->networks
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
172 (GDestroyNotify
) g_free
, (GDestroyNotify
) g_object_unref
);
176 priv
->have_to_save
= FALSE
;
177 priv
->loading
= FALSE
;
178 priv
->save_timer_id
= 0;
182 empathy_irc_network_manager_class_init (EmpathyIrcNetworkManagerClass
*klass
)
184 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
185 GParamSpec
*param_spec
;
187 object_class
->constructor
= empathy_irc_network_manager_constructor
;
188 object_class
->get_property
= empathy_irc_network_manager_get_property
;
189 object_class
->set_property
= empathy_irc_network_manager_set_property
;
191 g_type_class_add_private (object_class
, sizeof (EmpathyIrcNetworkManagerPriv
));
193 object_class
->finalize
= empathy_irc_network_manager_finalize
;
195 param_spec
= g_param_spec_string (
197 "path of the global networks file",
198 "The path of the system-wide filename from which we have to load"
199 " the networks list",
201 G_PARAM_CONSTRUCT_ONLY
|
203 G_PARAM_STATIC_NAME
|
204 G_PARAM_STATIC_NICK
|
205 G_PARAM_STATIC_BLURB
);
206 g_object_class_install_property (object_class
, PROP_GLOBAL_FILE
, param_spec
);
208 param_spec
= g_param_spec_string (
210 "path of the user networks file",
211 "The path of user's filename from which we have to load"
212 " the networks list and to which we'll save his modifications",
214 G_PARAM_CONSTRUCT_ONLY
|
216 G_PARAM_STATIC_NAME
|
217 G_PARAM_STATIC_NICK
|
218 G_PARAM_STATIC_BLURB
);
219 g_object_class_install_property (object_class
, PROP_USER_FILE
, param_spec
);
223 * empathy_irc_network_manager_new:
224 * @global_file: the path of the global networks file, or %NULL
225 * @user_file: the path of the user networks file, or %NULL
227 * Creates a new #EmpathyIrcNetworkManager
229 * Returns: a new #EmpathyIrcNetworkManager
231 EmpathyIrcNetworkManager
*
232 empathy_irc_network_manager_new (const gchar
*global_file
,
233 const gchar
*user_file
)
235 EmpathyIrcNetworkManager
*manager
;
237 manager
= g_object_new (EMPATHY_TYPE_IRC_NETWORK_MANAGER
,
238 "global-file", global_file
,
239 "user-file", user_file
,
246 save_timeout (EmpathyIrcNetworkManager
*self
)
248 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
250 priv
->save_timer_id
= 0;
251 irc_network_manager_file_save (self
);
257 reset_save_timeout (EmpathyIrcNetworkManager
*self
)
259 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
261 if (priv
->save_timer_id
> 0)
263 g_source_remove (priv
->save_timer_id
);
266 priv
->save_timer_id
= g_timeout_add_seconds (SAVE_TIMER
,
267 (GSourceFunc
) save_timeout
, self
);
271 network_modified (EmpathyIrcNetwork
*network
,
272 EmpathyIrcNetworkManager
*self
)
274 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
276 network
->user_defined
= TRUE
;
280 priv
->have_to_save
= TRUE
;
281 reset_save_timeout (self
);
286 add_network (EmpathyIrcNetworkManager
*self
,
287 EmpathyIrcNetwork
*network
,
290 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
292 g_hash_table_insert (priv
->networks
, g_strdup (id
), g_object_ref (network
));
294 g_signal_connect (network
, "modified", G_CALLBACK (network_modified
), self
);
298 * empathy_irc_network_manager_add:
299 * @manager: an #EmpathyIrcNetworkManager
300 * @network: the #EmpathyIrcNetwork to add
302 * Add an #EmpathyIrcNetwork to the given #EmpathyIrcNetworkManager.
306 empathy_irc_network_manager_add (EmpathyIrcNetworkManager
*self
,
307 EmpathyIrcNetwork
*network
)
309 EmpathyIrcNetworkManagerPriv
*priv
;
312 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self
));
313 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network
));
315 priv
= GET_PRIV (self
);
317 /* generate an id for this network */
321 id
= g_strdup_printf ("id%u", ++priv
->last_id
);
322 } while (g_hash_table_lookup (priv
->networks
, id
) != NULL
&&
323 priv
->last_id
< G_MAXUINT
);
325 if (priv
->last_id
== G_MAXUINT
)
327 DEBUG ("Can't add network: too many networks using a similar ID");
331 DEBUG ("add server with \"%s\" as ID", id
);
333 network
->user_defined
= TRUE
;
334 add_network (self
, network
, id
);
336 priv
->have_to_save
= TRUE
;
337 reset_save_timeout (self
);
343 * empathy_irc_network_manager_remove:
344 * @manager: an #EmpathyIrcNetworkManager
345 * @network: the #EmpathyIrcNetwork to remove
347 * Remove an #EmpathyIrcNetwork from the given #EmpathyIrcNetworkManager.
351 empathy_irc_network_manager_remove (EmpathyIrcNetworkManager
*self
,
352 EmpathyIrcNetwork
*network
)
354 EmpathyIrcNetworkManagerPriv
*priv
;
356 g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self
));
357 g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network
));
359 priv
= GET_PRIV (self
);
361 network
->user_defined
= TRUE
;
362 network
->dropped
= TRUE
;
364 priv
->have_to_save
= TRUE
;
365 reset_save_timeout (self
);
369 append_active_networks_to_list (const gchar
*id
,
370 EmpathyIrcNetwork
*network
,
373 if (network
->dropped
)
376 *list
= g_slist_prepend (*list
, g_object_ref (network
));
380 append_dropped_networks_to_list (const gchar
*id
,
381 EmpathyIrcNetwork
*network
,
384 if (!network
->dropped
)
387 *list
= g_slist_prepend (*list
, g_object_ref (network
));
391 get_network_list (EmpathyIrcNetworkManager
*self
,
394 EmpathyIrcNetworkManagerPriv
*priv
;
395 GSList
*irc_networks
= NULL
;
397 g_return_val_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self
), NULL
);
399 priv
= GET_PRIV (self
);
403 g_hash_table_foreach (priv
->networks
,
404 (GHFunc
) append_active_networks_to_list
, &irc_networks
);
408 g_hash_table_foreach (priv
->networks
,
409 (GHFunc
) append_dropped_networks_to_list
, &irc_networks
);
416 * empathy_irc_network_manager_get_networks:
417 * @manager: an #EmpathyIrcNetworkManager
419 * Get the list of #EmpathyIrcNetwork associated with the given
422 * Returns: a new #GSList of refed #EmpathyIrcNetwork
425 empathy_irc_network_manager_get_networks (EmpathyIrcNetworkManager
*self
)
427 return get_network_list (self
, TRUE
);
431 * empathy_irc_network_manager_get_dropped_networks:
432 * @manager: an #EmpathyIrcNetworkManager
434 * Get the list of dropped #EmpathyIrcNetworks associated with the given
437 * Returns: a new #GSList of refed dropped #EmpathyIrcNetworks
440 empathy_irc_network_manager_get_dropped_networks (EmpathyIrcNetworkManager
*self
)
442 return get_network_list (self
, FALSE
);
446 * API to save/load and parse the irc_networks file.
450 load_global_file (EmpathyIrcNetworkManager
*self
)
452 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
454 if (priv
->global_file
== NULL
)
457 if (!g_file_test (priv
->global_file
, G_FILE_TEST_EXISTS
))
459 DEBUG ("Global networks file %s doesn't exist", priv
->global_file
);
463 irc_network_manager_file_parse (self
, priv
->global_file
, FALSE
);
467 load_user_file (EmpathyIrcNetworkManager
*self
)
469 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
471 if (priv
->user_file
== NULL
)
474 if (!g_file_test (priv
->user_file
, G_FILE_TEST_EXISTS
))
476 DEBUG ("User networks file %s doesn't exist", priv
->global_file
);
480 irc_network_manager_file_parse (self
, priv
->user_file
, TRUE
);
484 irc_network_manager_load_servers (EmpathyIrcNetworkManager
*self
)
486 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
488 priv
->loading
= TRUE
;
490 load_global_file (self
);
491 load_user_file (self
);
493 priv
->loading
= FALSE
;
494 priv
->have_to_save
= FALSE
;
498 irc_network_manager_parse_irc_server (EmpathyIrcNetwork
*network
,
501 xmlNodePtr server_node
;
503 for (server_node
= node
->children
; server_node
;
504 server_node
= server_node
->next
)
506 gchar
*address
= NULL
, *port
= NULL
, *ssl
= NULL
;
508 if (strcmp ((const gchar
*) server_node
->name
, "server") != 0)
511 address
= (gchar
*) xmlGetProp (server_node
, (const xmlChar
*) "address");
512 port
= (gchar
*) xmlGetProp (server_node
, (const xmlChar
*) "port");
513 ssl
= (gchar
*) xmlGetProp (server_node
, (const xmlChar
*) "ssl");
518 gboolean have_ssl
= FALSE
;
519 EmpathyIrcServer
*server
;
522 port_nb
= strtol (port
, NULL
, 10);
524 if (port_nb
<= 0 || port_nb
> G_MAXUINT16
)
527 if (ssl
== NULL
|| strcmp (ssl
, "TRUE") == 0)
530 DEBUG ("parsed server %s port %d ssl %d", address
, port_nb
, have_ssl
);
532 server
= empathy_irc_server_new (address
, port_nb
, have_ssl
);
533 empathy_irc_network_append_server (network
, server
);
546 irc_network_manager_parse_irc_network (EmpathyIrcNetworkManager
*self
,
548 gboolean user_defined
)
550 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
551 EmpathyIrcNetwork
*network
;
556 id
= (gchar
*) xmlGetProp (node
, (const xmlChar
*) "id");
557 if (xmlHasProp (node
, (const xmlChar
*) "dropped"))
561 DEBUG ("the 'dropped' attribute shouldn't be used in the global file");
564 network
= g_hash_table_lookup (priv
->networks
, id
);
567 network
->dropped
= TRUE
;
568 network
->user_defined
= TRUE
;
574 if (!xmlHasProp (node
, (const xmlChar
*) "name"))
577 name
= (gchar
*) xmlGetProp (node
, (const xmlChar
*) "name");
578 network
= empathy_irc_network_new (name
);
580 if (xmlHasProp (node
, (const xmlChar
*) "network_charset"))
583 charset
= (gchar
*) xmlGetProp (node
, (const xmlChar
*) "network_charset");
584 g_object_set (network
, "charset", charset
, NULL
);
588 add_network (self
, network
, id
);
589 DEBUG ("add network %s (id %s)", name
, id
);
591 for (child
= node
->children
; child
; child
= child
->next
)
595 tag
= (gchar
*) child
->name
;
596 str
= (gchar
*) xmlNodeGetContent (child
);
601 if (strcmp (tag
, "servers") == 0)
603 irc_network_manager_parse_irc_server (network
, child
);
609 network
->user_defined
= user_defined
;
610 g_object_unref (network
);
616 irc_network_manager_file_parse (EmpathyIrcNetworkManager
*self
,
617 const gchar
*filename
,
618 gboolean user_defined
)
620 xmlParserCtxtPtr ctxt
;
625 DEBUG ("Attempting to parse file:'%s'...", filename
);
627 ctxt
= xmlNewParserCtxt ();
629 /* Parse and validate the file. */
630 doc
= xmlCtxtReadFile (ctxt
, filename
, NULL
, 0);
633 g_warning ("Failed to parse file:'%s'", filename
);
634 xmlFreeParserCtxt (ctxt
);
638 if (!empathy_xml_validate (doc
, IRC_NETWORKS_DTD_FILENAME
)) {
639 g_warning ("Failed to validate file:'%s'", filename
);
641 xmlFreeParserCtxt (ctxt
);
645 /* The root node, networks. */
646 networks
= xmlDocGetRootElement (doc
);
648 for (node
= networks
->children
; node
; node
= node
->next
)
650 irc_network_manager_parse_irc_network (self
, node
, user_defined
);
654 xmlFreeParserCtxt (ctxt
);
660 write_network_to_xml (const gchar
*id
,
661 EmpathyIrcNetwork
*network
,
664 xmlNodePtr network_node
, servers_node
;
666 gchar
*name
, *charset
;
668 if (!network
->user_defined
)
669 /* no need to write this network to the XML */
672 network_node
= xmlNewChild (root
, NULL
, (const xmlChar
*) "network", NULL
);
673 xmlNewProp (network_node
, (const xmlChar
*) "id", (const xmlChar
*) id
);
675 if (network
->dropped
)
677 xmlNewProp (network_node
, (const xmlChar
*) "dropped",
678 (const xmlChar
*) "1");
682 g_object_get (network
,
686 xmlNewProp (network_node
, (const xmlChar
*) "name", (const xmlChar
*) name
);
687 xmlNewProp (network_node
, (const xmlChar
*) "network_charset",
688 (const xmlChar
*) charset
);
692 servers
= empathy_irc_network_get_servers (network
);
694 servers_node
= xmlNewChild (network_node
, NULL
, (const xmlChar
*) "servers",
696 for (l
= servers
; l
!= NULL
; l
= g_slist_next (l
))
698 EmpathyIrcServer
*server
;
699 xmlNodePtr server_node
;
700 gchar
*address
, *tmp
;
706 server_node
= xmlNewChild (servers_node
, NULL
, (const xmlChar
*) "server",
709 g_object_get (server
,
715 xmlNewProp (server_node
, (const xmlChar
*) "address",
716 (const xmlChar
*) address
);
718 tmp
= g_strdup_printf ("%u", port
);
719 xmlNewProp (server_node
, (const xmlChar
*) "port",
720 (const xmlChar
*) tmp
);
723 xmlNewProp (server_node
, (const xmlChar
*) "ssl",
724 ssl
? (const xmlChar
*) "TRUE": (const xmlChar
*) "FALSE");
730 g_slist_foreach (servers
, (GFunc
) g_object_unref
, NULL
);
731 g_slist_free (servers
);
735 irc_network_manager_file_save (EmpathyIrcNetworkManager
*self
)
737 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
741 if (priv
->user_file
== NULL
)
743 DEBUG ("can't save: no user file defined");
747 DEBUG ("Saving IRC networks");
749 doc
= xmlNewDoc ((const xmlChar
*) "1.0");
750 root
= xmlNewNode (NULL
, (const xmlChar
*) "networks");
751 xmlDocSetRootElement (doc
, root
);
753 g_hash_table_foreach (priv
->networks
, (GHFunc
) write_network_to_xml
, root
);
755 /* Make sure the XML is indented properly */
756 xmlIndentTreeOutput
= 1;
758 xmlSaveFormatFileEnc (priv
->user_file
, doc
, "utf-8", 1);
763 priv
->have_to_save
= FALSE
;
769 find_network_by_address (const gchar
*id
,
770 EmpathyIrcNetwork
*network
,
771 const gchar
*address
)
774 gboolean found
= FALSE
;
776 if (network
->dropped
)
779 servers
= empathy_irc_network_get_servers (network
);
781 for (l
= servers
; l
!= NULL
&& !found
; l
= g_slist_next (l
))
783 EmpathyIrcServer
*server
= l
->data
;
786 g_object_get (server
, "address", &_address
, NULL
);
787 found
= (_address
!= NULL
&& strcmp (address
, _address
) == 0);
792 g_slist_foreach (servers
, (GFunc
) g_object_unref
, NULL
);
793 g_slist_free (servers
);
799 * empathy_irc_network_manager_find_network_by_address:
800 * @manager: an #EmpathyIrcNetworkManager
801 * @address: the server address to look for
803 * Find the #EmpathyIrcNetwork which owns an #EmpathyIrcServer
804 * that has the given address.
806 * Returns: the found #EmpathyIrcNetwork, or %NULL if not found.
809 empathy_irc_network_manager_find_network_by_address (
810 EmpathyIrcNetworkManager
*self
,
811 const gchar
*address
)
813 EmpathyIrcNetworkManagerPriv
*priv
= GET_PRIV (self
);
814 EmpathyIrcNetwork
*network
;
816 g_return_val_if_fail (address
!= NULL
, NULL
);
818 network
= g_hash_table_find (priv
->networks
,
819 (GHRFunc
) find_network_by_address
, (gchar
*) address
);
824 EmpathyIrcNetworkManager
*
825 empathy_irc_network_manager_dup_default (void)
827 static EmpathyIrcNetworkManager
*default_mgr
= NULL
;
828 gchar
*dir
, *user_file_with_path
, *global_file_with_path
;
830 if (default_mgr
!= NULL
)
831 return g_object_ref (default_mgr
);
833 dir
= g_build_filename (g_get_user_config_dir (), PACKAGE_NAME
, NULL
);
834 g_mkdir_with_parents (dir
, S_IRUSR
| S_IWUSR
| S_IXUSR
);
835 user_file_with_path
= g_build_filename (dir
, IRC_NETWORKS_FILENAME
, NULL
);
838 global_file_with_path
= g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
839 "libempathy", IRC_NETWORKS_FILENAME
, NULL
);
840 if (!g_file_test (global_file_with_path
, G_FILE_TEST_EXISTS
))
842 g_free (global_file_with_path
);
843 global_file_with_path
= g_build_filename (DATADIR
, "empathy",
844 IRC_NETWORKS_FILENAME
, NULL
);
847 default_mgr
= empathy_irc_network_manager_new (
848 global_file_with_path
, user_file_with_path
);
850 g_object_add_weak_pointer (G_OBJECT (default_mgr
), (gpointer
*) &default_mgr
);
852 g_free (global_file_with_path
);
853 g_free (user_file_with_path
);