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);