]> git.0d.be Git - empathy.git/blob - libempathy/empathy-irc-network-manager.c
local-xmpp-assistant-widget: increase row-spacing
[empathy.git] / libempathy / empathy-irc-network-manager.c
1 /*
2  * Copyright (C) 2007-2008 Guillaume Desmottes
3  *
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.
8  *
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.
13  *
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
17  *
18  * Authors: Guillaume Desmottes <gdesmott@gnome.org>
19  */
20
21 #include <config.h>
22 #include <string.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
27
28 #include "empathy-utils.h"
29 #include "empathy-irc-network-manager.h"
30
31 #define DEBUG_FLAG EMPATHY_DEBUG_IRC
32 #include "empathy-debug.h"
33
34 #define IRC_NETWORKS_DTD_FILENAME "empathy-irc-networks.dtd"
35 #define IRC_NETWORKS_FILENAME "irc-networks.xml"
36 #define SAVE_TIMER 4
37
38 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIrcNetworkManager)
39 typedef struct {
40   GHashTable *networks;
41
42   gchar *global_file;
43   gchar *user_file;
44   guint last_id;
45
46   /* Do we have to save modifications to the user file ? */
47   gboolean have_to_save;
48   /* Are we loading networks from XML files ? */
49   gboolean loading;
50   /* source id of the autosave timer */
51   gint save_timer_id;
52 } EmpathyIrcNetworkManagerPriv;
53
54 /* properties */
55 enum
56 {
57   PROP_GLOBAL_FILE = 1,
58   PROP_USER_FILE,
59   LAST_PROPERTY
60 };
61
62 G_DEFINE_TYPE (EmpathyIrcNetworkManager, empathy_irc_network_manager,
63     G_TYPE_OBJECT);
64
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);
72
73 static void
74 empathy_irc_network_manager_get_property (GObject *object,
75                                           guint property_id,
76                                           GValue *value,
77                                           GParamSpec *pspec)
78 {
79   EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
80   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
81
82   switch (property_id)
83     {
84       case PROP_GLOBAL_FILE:
85         g_value_set_string (value, priv->global_file);
86         break;
87       case PROP_USER_FILE:
88         g_value_set_string (value, priv->user_file);
89         break;
90       default:
91         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
92         break;
93     }
94 }
95
96 static void
97 empathy_irc_network_manager_set_property (GObject *object,
98                                           guint property_id,
99                                           const GValue *value,
100                                           GParamSpec *pspec)
101 {
102   EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
103   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
104
105   switch (property_id)
106     {
107       case PROP_GLOBAL_FILE:
108         g_free (priv->global_file);
109         priv->global_file = g_value_dup_string (value);
110         break;
111       case PROP_USER_FILE:
112         g_free (priv->user_file);
113         priv->user_file = g_value_dup_string (value);
114         break;
115       default:
116         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
117         break;
118     }
119 }
120
121 static GObject *
122 empathy_irc_network_manager_constructor (GType type,
123                                          guint n_props,
124                                          GObjectConstructParam *props)
125 {
126   GObject *obj;
127   EmpathyIrcNetworkManager *self;
128
129   /* Parent constructor chain */
130   obj = G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->
131         constructor (type, n_props, props);
132
133   self = EMPATHY_IRC_NETWORK_MANAGER (obj);
134   irc_network_manager_load_servers (self);
135
136   return obj;
137 }
138
139 static void
140 empathy_irc_network_manager_finalize (GObject *object)
141 {
142   EmpathyIrcNetworkManager *self = EMPATHY_IRC_NETWORK_MANAGER (object);
143   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
144
145   if (priv->save_timer_id > 0)
146     {
147       g_source_remove (priv->save_timer_id);
148     }
149
150   if (priv->have_to_save)
151     {
152       irc_network_manager_file_save (self);
153     }
154
155   g_free (priv->global_file);
156   g_free (priv->user_file);
157
158   g_hash_table_unref (priv->networks);
159
160   G_OBJECT_CLASS (empathy_irc_network_manager_parent_class)->finalize (object);
161 }
162
163 static void
164 empathy_irc_network_manager_init (EmpathyIrcNetworkManager *self)
165 {
166   EmpathyIrcNetworkManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
167       EMPATHY_TYPE_IRC_NETWORK_MANAGER, EmpathyIrcNetworkManagerPriv);
168
169   self->priv = priv;
170
171   priv->networks = g_hash_table_new_full (g_str_hash, g_str_equal,
172       (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref);
173
174   priv->last_id = 0;
175
176   priv->have_to_save = FALSE;
177   priv->loading = FALSE;
178   priv->save_timer_id = 0;
179 }
180
181 static void
182 empathy_irc_network_manager_class_init (EmpathyIrcNetworkManagerClass *klass)
183 {
184   GObjectClass *object_class = G_OBJECT_CLASS (klass);
185   GParamSpec *param_spec;
186
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;
190
191   g_type_class_add_private (object_class, sizeof (EmpathyIrcNetworkManagerPriv));
192
193   object_class->finalize = empathy_irc_network_manager_finalize;
194
195   param_spec = g_param_spec_string (
196       "global-file",
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",
200       NULL,
201       G_PARAM_CONSTRUCT_ONLY |
202       G_PARAM_READWRITE |
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);
207
208   param_spec = g_param_spec_string (
209       "user-file",
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",
213       NULL,
214       G_PARAM_CONSTRUCT_ONLY |
215       G_PARAM_READWRITE |
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);
220 }
221
222 /**
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
226  *
227  * Creates a new #EmpathyIrcNetworkManager
228  *
229  * Returns: a new #EmpathyIrcNetworkManager
230  */
231 EmpathyIrcNetworkManager *
232 empathy_irc_network_manager_new (const gchar *global_file,
233                                  const gchar *user_file)
234 {
235   EmpathyIrcNetworkManager *manager;
236
237   manager = g_object_new (EMPATHY_TYPE_IRC_NETWORK_MANAGER,
238       "global-file", global_file,
239       "user-file", user_file,
240       NULL);
241
242   return manager;
243 }
244
245 static gboolean
246 save_timeout (EmpathyIrcNetworkManager *self)
247 {
248   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
249
250   priv->save_timer_id = 0;
251   irc_network_manager_file_save (self);
252
253   return FALSE;
254 }
255
256 static void
257 reset_save_timeout (EmpathyIrcNetworkManager *self)
258 {
259   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
260
261   if (priv->save_timer_id > 0)
262     {
263       g_source_remove (priv->save_timer_id);
264     }
265
266   priv->save_timer_id = g_timeout_add_seconds (SAVE_TIMER,
267       (GSourceFunc) save_timeout, self);
268 }
269
270 static void
271 network_modified (EmpathyIrcNetwork *network,
272                   EmpathyIrcNetworkManager *self)
273 {
274   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
275
276   network->user_defined = TRUE;
277
278   if (!priv->loading)
279     {
280       priv->have_to_save = TRUE;
281       reset_save_timeout (self);
282     }
283 }
284
285 static void
286 add_network (EmpathyIrcNetworkManager *self,
287              EmpathyIrcNetwork *network,
288              const gchar *id)
289 {
290   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
291
292   g_hash_table_insert (priv->networks, g_strdup (id), g_object_ref (network));
293
294   g_signal_connect (network, "modified", G_CALLBACK (network_modified), self);
295 }
296
297 /**
298  * empathy_irc_network_manager_add:
299  * @manager: an #EmpathyIrcNetworkManager
300  * @network: the #EmpathyIrcNetwork to add
301  *
302  * Add an #EmpathyIrcNetwork to the given #EmpathyIrcNetworkManager.
303  *
304  */
305 void
306 empathy_irc_network_manager_add (EmpathyIrcNetworkManager *self,
307                                  EmpathyIrcNetwork *network)
308 {
309   EmpathyIrcNetworkManagerPriv *priv;
310   gchar *id = NULL;
311
312   g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
313   g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
314
315   priv = GET_PRIV (self);
316
317   /* generate an id for this network */
318   do
319     {
320       g_free (id);
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);
324
325   if (priv->last_id == G_MAXUINT)
326     {
327       DEBUG ("Can't add network: too many networks using a similar ID");
328       return;
329     }
330
331   DEBUG ("add server with \"%s\" as ID", id);
332
333   network->user_defined = TRUE;
334   add_network (self, network, id);
335
336   priv->have_to_save = TRUE;
337   reset_save_timeout (self);
338
339   g_free (id);
340 }
341
342 /**
343  * empathy_irc_network_manager_remove:
344  * @manager: an #EmpathyIrcNetworkManager
345  * @network: the #EmpathyIrcNetwork to remove
346  *
347  * Remove an #EmpathyIrcNetwork from the given #EmpathyIrcNetworkManager.
348  *
349  */
350 void
351 empathy_irc_network_manager_remove (EmpathyIrcNetworkManager *self,
352                                     EmpathyIrcNetwork *network)
353 {
354   EmpathyIrcNetworkManagerPriv *priv;
355
356   g_return_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self));
357   g_return_if_fail (EMPATHY_IS_IRC_NETWORK (network));
358
359   priv = GET_PRIV (self);
360
361   network->user_defined = TRUE;
362   network->dropped = TRUE;
363
364   priv->have_to_save = TRUE;
365   reset_save_timeout (self);
366 }
367
368 static void
369 append_active_networks_to_list (const gchar *id,
370                         EmpathyIrcNetwork *network,
371                         GSList **list)
372 {
373   if (network->dropped)
374     return;
375
376   *list = g_slist_prepend (*list, g_object_ref (network));
377 }
378
379 static void
380 append_dropped_networks_to_list (const gchar *id,
381                         EmpathyIrcNetwork *network,
382                         GSList **list)
383 {
384   if (!network->dropped)
385     return;
386
387   *list = g_slist_prepend (*list, g_object_ref (network));
388 }
389
390 static GSList *
391 get_network_list (EmpathyIrcNetworkManager *self,
392     gboolean get_active)
393 {
394   EmpathyIrcNetworkManagerPriv *priv;
395   GSList *irc_networks = NULL;
396
397   g_return_val_if_fail (EMPATHY_IS_IRC_NETWORK_MANAGER (self), NULL);
398
399   priv = GET_PRIV (self);
400
401   if (get_active)
402     {
403       g_hash_table_foreach (priv->networks,
404           (GHFunc) append_active_networks_to_list, &irc_networks);
405     }
406   else
407     {
408       g_hash_table_foreach (priv->networks,
409           (GHFunc) append_dropped_networks_to_list, &irc_networks);
410     }
411
412   return irc_networks;
413 }
414
415 /**
416  * empathy_irc_network_manager_get_networks:
417  * @manager: an #EmpathyIrcNetworkManager
418  *
419  * Get the list of #EmpathyIrcNetwork associated with the given
420  * manager.
421  *
422  * Returns: a new #GSList of refed #EmpathyIrcNetwork
423  */
424 GSList *
425 empathy_irc_network_manager_get_networks (EmpathyIrcNetworkManager *self)
426 {
427   return get_network_list (self, TRUE);
428 }
429
430 /**
431  * empathy_irc_network_manager_get_dropped_networks:
432  * @manager: an #EmpathyIrcNetworkManager
433  *
434  * Get the list of dropped #EmpathyIrcNetworks associated with the given
435  * manager.
436  *
437  * Returns: a new #GSList of refed dropped #EmpathyIrcNetworks
438  */
439 GSList *
440 empathy_irc_network_manager_get_dropped_networks (EmpathyIrcNetworkManager *self)
441 {
442   return get_network_list (self, FALSE);
443 }
444
445 /*
446  * API to save/load and parse the irc_networks file.
447  */
448
449 static void
450 load_global_file (EmpathyIrcNetworkManager *self)
451 {
452   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
453
454   if (priv->global_file == NULL)
455     return;
456
457   if (!g_file_test (priv->global_file, G_FILE_TEST_EXISTS))
458     {
459       DEBUG ("Global networks file %s doesn't exist", priv->global_file);
460       return;
461     }
462
463   irc_network_manager_file_parse (self, priv->global_file, FALSE);
464 }
465
466 static void
467 load_user_file (EmpathyIrcNetworkManager *self)
468 {
469   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
470
471   if (priv->user_file == NULL)
472     return;
473
474   if (!g_file_test (priv->user_file, G_FILE_TEST_EXISTS))
475     {
476       DEBUG ("User networks file %s doesn't exist", priv->global_file);
477       return;
478     }
479
480   irc_network_manager_file_parse (self, priv->user_file, TRUE);
481 }
482
483 static void
484 irc_network_manager_load_servers (EmpathyIrcNetworkManager *self)
485 {
486   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
487
488   priv->loading = TRUE;
489
490   load_global_file (self);
491   load_user_file (self);
492
493   priv->loading = FALSE;
494   priv->have_to_save = FALSE;
495 }
496
497 static void
498 irc_network_manager_parse_irc_server (EmpathyIrcNetwork *network,
499                                       xmlNodePtr node)
500 {
501   xmlNodePtr server_node;
502
503   for (server_node = node->children; server_node;
504       server_node = server_node->next)
505     {
506       gchar *address = NULL, *port = NULL, *ssl = NULL;
507
508       if (strcmp ((const gchar *) server_node->name, "server") != 0)
509         continue;
510
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");
514
515       if (address != NULL)
516         {
517           gint port_nb = 0;
518           gboolean have_ssl = FALSE;
519           EmpathyIrcServer *server;
520
521           if (port != NULL)
522             port_nb = strtol (port, NULL, 10);
523
524           if (port_nb <= 0 || port_nb > G_MAXUINT16)
525             port_nb = 6667;
526
527           if (ssl == NULL || strcmp (ssl, "TRUE") == 0)
528             have_ssl = TRUE;
529
530           DEBUG ("parsed server %s port %d ssl %d", address, port_nb, have_ssl);
531
532           server = empathy_irc_server_new (address, port_nb, have_ssl);
533           empathy_irc_network_append_server (network, server);
534         }
535
536       if (address)
537         xmlFree (address);
538       if (port)
539         xmlFree (port);
540       if (ssl)
541         xmlFree (ssl);
542     }
543 }
544
545 static void
546 irc_network_manager_parse_irc_network (EmpathyIrcNetworkManager *self,
547                                        xmlNodePtr node,
548                                        gboolean user_defined)
549 {
550   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
551   EmpathyIrcNetwork  *network;
552   xmlNodePtr child;
553   gchar *str;
554   gchar *id, *name;
555
556   id = (gchar *) xmlGetProp (node, (const xmlChar *) "id");
557   if (xmlHasProp (node, (const xmlChar *) "dropped"))
558     {
559       if (!user_defined)
560         {
561           DEBUG ("the 'dropped' attribute shouldn't be used in the global file");
562         }
563
564       network = g_hash_table_lookup (priv->networks, id);
565       if (network != NULL)
566         {
567           network->dropped = TRUE;
568           network->user_defined = TRUE;
569         }
570        xmlFree (id);
571       return;
572     }
573
574   if (!xmlHasProp (node, (const xmlChar *) "name"))
575     return;
576
577   name = (gchar *) xmlGetProp (node, (const xmlChar *) "name");
578   network = empathy_irc_network_new (name);
579
580   if (xmlHasProp (node, (const xmlChar *) "network_charset"))
581     {
582       gchar *charset;
583       charset = (gchar *) xmlGetProp (node, (const xmlChar *) "network_charset");
584       g_object_set (network, "charset", charset, NULL);
585       xmlFree (charset);
586     }
587
588   add_network (self, network, id);
589   DEBUG ("add network %s (id %s)", name, id);
590
591   for (child = node->children; child; child = child->next)
592     {
593       gchar *tag;
594
595       tag = (gchar *) child->name;
596       str = (gchar *) xmlNodeGetContent (child);
597
598       if (!str)
599         continue;
600
601       if (strcmp (tag, "servers") == 0)
602         {
603           irc_network_manager_parse_irc_server (network, child);
604         }
605
606       xmlFree (str);
607     }
608
609   network->user_defined = user_defined;
610   g_object_unref (network);
611   xmlFree (name);
612   xmlFree (id);
613 }
614
615 static gboolean
616 irc_network_manager_file_parse (EmpathyIrcNetworkManager *self,
617                                 const gchar *filename,
618                                 gboolean user_defined)
619 {
620   xmlParserCtxtPtr ctxt;
621   xmlDocPtr doc;
622   xmlNodePtr networks;
623   xmlNodePtr node;
624
625   DEBUG ("Attempting to parse file:'%s'...", filename);
626
627   ctxt = xmlNewParserCtxt ();
628
629   /* Parse and validate the file. */
630   doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
631   if (!doc)
632     {
633       g_warning ("Failed to parse file:'%s'", filename);
634       xmlFreeParserCtxt (ctxt);
635       return FALSE;
636     }
637
638   if (!empathy_xml_validate (doc, IRC_NETWORKS_DTD_FILENAME)) {
639     g_warning ("Failed to validate file:'%s'", filename);
640     xmlFreeDoc (doc);
641     xmlFreeParserCtxt (ctxt);
642     return FALSE;
643   }
644
645   /* The root node, networks. */
646   networks = xmlDocGetRootElement (doc);
647
648   for (node = networks->children; node; node = node->next)
649     {
650       irc_network_manager_parse_irc_network (self, node, user_defined);
651     }
652
653   xmlFreeDoc (doc);
654   xmlFreeParserCtxt (ctxt);
655
656   return TRUE;
657 }
658
659 static void
660 write_network_to_xml (const gchar *id,
661                       EmpathyIrcNetwork *network,
662                       xmlNodePtr root)
663 {
664   xmlNodePtr network_node, servers_node;
665   GSList *servers, *l;
666   gchar *name, *charset;
667
668   if (!network->user_defined)
669     /* no need to write this network to the XML */
670     return;
671
672   network_node = xmlNewChild (root, NULL, (const xmlChar *) "network", NULL);
673   xmlNewProp (network_node, (const xmlChar *) "id", (const xmlChar *) id);
674
675   if (network->dropped)
676     {
677       xmlNewProp (network_node, (const xmlChar *) "dropped",
678           (const xmlChar *)  "1");
679       return;
680     }
681
682   g_object_get (network,
683       "name", &name,
684       "charset", &charset,
685       NULL);
686   xmlNewProp (network_node, (const xmlChar *) "name", (const xmlChar *) name);
687   xmlNewProp (network_node, (const xmlChar *) "network_charset",
688       (const xmlChar *) charset);
689   g_free (name);
690   g_free (charset);
691
692   servers = empathy_irc_network_get_servers (network);
693
694   servers_node = xmlNewChild (network_node, NULL, (const xmlChar *) "servers",
695       NULL);
696   for (l = servers; l != NULL; l = g_slist_next (l))
697     {
698       EmpathyIrcServer *server;
699       xmlNodePtr server_node;
700       gchar *address, *tmp;
701       guint port;
702       gboolean ssl;
703
704       server = l->data;
705
706       server_node = xmlNewChild (servers_node, NULL, (const xmlChar *) "server",
707           NULL);
708
709       g_object_get (server,
710           "address", &address,
711           "port", &port,
712           "ssl", &ssl,
713           NULL);
714
715       xmlNewProp (server_node, (const xmlChar *) "address",
716           (const xmlChar *) address);
717
718       tmp = g_strdup_printf ("%u", port);
719       xmlNewProp (server_node, (const xmlChar *) "port",
720           (const xmlChar *) tmp);
721       g_free (tmp);
722
723       xmlNewProp (server_node, (const xmlChar *) "ssl",
724           ssl ? (const xmlChar *) "TRUE": (const xmlChar *) "FALSE");
725
726       g_free (address);
727     }
728
729   /* free the list */
730   g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
731   g_slist_free (servers);
732 }
733
734 static gboolean
735 irc_network_manager_file_save (EmpathyIrcNetworkManager *self)
736 {
737   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
738   xmlDocPtr doc;
739   xmlNodePtr root;
740
741   if (priv->user_file == NULL)
742     {
743       DEBUG ("can't save: no user file defined");
744       return FALSE;
745     }
746
747   DEBUG ("Saving IRC networks");
748
749   doc = xmlNewDoc ((const xmlChar *)  "1.0");
750   root = xmlNewNode (NULL, (const xmlChar *) "networks");
751   xmlDocSetRootElement (doc, root);
752
753   g_hash_table_foreach (priv->networks, (GHFunc) write_network_to_xml, root);
754
755   /* Make sure the XML is indented properly */
756   xmlIndentTreeOutput = 1;
757
758   xmlSaveFormatFileEnc (priv->user_file, doc, "utf-8", 1);
759   xmlFreeDoc (doc);
760
761   xmlMemoryDump ();
762
763   priv->have_to_save = FALSE;
764
765   return TRUE;
766 }
767
768 static gboolean
769 find_network_by_address (const gchar *id,
770                          EmpathyIrcNetwork *network,
771                          const gchar *address)
772 {
773   GSList *servers, *l;
774   gboolean found = FALSE;
775
776   if (network->dropped)
777     return FALSE;
778
779   servers = empathy_irc_network_get_servers (network);
780
781   for (l = servers; l != NULL && !found; l = g_slist_next (l))
782     {
783       EmpathyIrcServer *server = l->data;
784       gchar *_address;
785
786       g_object_get (server, "address", &_address, NULL);
787       found = (_address != NULL && strcmp (address, _address) == 0);
788
789       g_free (_address);
790     }
791
792   g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
793   g_slist_free (servers);
794
795   return found;
796 }
797
798 /**
799  * empathy_irc_network_manager_find_network_by_address:
800  * @manager: an #EmpathyIrcNetworkManager
801  * @address: the server address to look for
802  *
803  * Find the #EmpathyIrcNetwork which owns an #EmpathyIrcServer
804  * that has the given address.
805  *
806  * Returns: the found #EmpathyIrcNetwork, or %NULL if not found.
807  */
808 EmpathyIrcNetwork *
809 empathy_irc_network_manager_find_network_by_address (
810     EmpathyIrcNetworkManager *self,
811     const gchar *address)
812 {
813   EmpathyIrcNetworkManagerPriv *priv = GET_PRIV (self);
814   EmpathyIrcNetwork *network;
815
816   g_return_val_if_fail (address != NULL, NULL);
817
818   network = g_hash_table_find (priv->networks,
819       (GHRFunc) find_network_by_address, (gchar *) address);
820
821   return network;
822 }
823
824 EmpathyIrcNetworkManager *
825 empathy_irc_network_manager_dup_default (void)
826 {
827   static EmpathyIrcNetworkManager *default_mgr = NULL;
828   gchar *dir, *user_file_with_path, *global_file_with_path;
829
830   if (default_mgr != NULL)
831     return g_object_ref (default_mgr);
832
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);
836   g_free (dir);
837
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))
841     {
842       g_free (global_file_with_path);
843       global_file_with_path = g_build_filename (DATADIR, "empathy",
844           IRC_NETWORKS_FILENAME, NULL);
845     }
846
847   default_mgr = empathy_irc_network_manager_new (
848       global_file_with_path, user_file_with_path);
849
850   g_object_add_weak_pointer (G_OBJECT (default_mgr), (gpointer *) &default_mgr);
851
852   g_free (global_file_with_path);
853   g_free (user_file_with_path);
854   return default_mgr;
855 }