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