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