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