290f5883040d18062879a048e3c1afb5f3c1030f
[connman] / src / network.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27
28 #include <gdbus.h>
29
30 #include "connman.h"
31
32 struct connman_network {
33         struct connman_element element;
34         enum connman_network_type type;
35         enum connman_network_protocol protocol;
36         connman_bool_t connected;
37         connman_bool_t remember;
38         connman_uint8_t strength;
39         char *identifier;
40         char *name;
41         char *node;
42
43         struct connman_network_driver *driver;
44         void *driver_data;
45
46         connman_bool_t registered;
47
48         struct connman_device *device;
49
50         struct {
51                 void *ssid;
52                 int ssid_len;
53                 char *mode;
54                 char *security;
55                 char *passphrase;
56         } wifi;
57 };
58
59 static DBusMessage *get_properties(DBusConnection *conn,
60                                         DBusMessage *msg, void *data)
61 {
62         struct connman_network *network = data;
63         DBusMessage *reply;
64         DBusMessageIter array, dict;
65
66         DBG("conn %p", conn);
67
68         reply = dbus_message_new_method_return(msg);
69         if (reply == NULL)
70                 return NULL;
71
72         dbus_message_iter_init_append(reply, &array);
73
74         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
75                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
76                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
77                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
78
79         if (network->device) {
80                 const char *path = connman_device_get_path(network->device);
81                 if (path != NULL)
82                         connman_dbus_dict_append_variant(&dict, "Device",
83                                                 DBUS_TYPE_OBJECT_PATH, &path);
84         }
85
86         if (network->name != NULL)
87                 connman_dbus_dict_append_variant(&dict, "Name",
88                                         DBUS_TYPE_STRING, &network->name);
89
90         connman_dbus_dict_append_variant(&dict, "Connected",
91                                 DBUS_TYPE_BOOLEAN, &network->connected);
92
93         connman_dbus_dict_append_variant(&dict, "Remember",
94                                 DBUS_TYPE_BOOLEAN, &network->remember);
95
96         if (network->strength > 0)
97                 connman_dbus_dict_append_variant(&dict, "Strength",
98                                         DBUS_TYPE_BYTE, &network->strength);
99
100         if (network->wifi.ssid != NULL && network->wifi.ssid_len > 0)
101                 connman_dbus_dict_append_array(&dict, "WiFi.SSID",
102                                 DBUS_TYPE_BYTE, &network->wifi.ssid,
103                                                 network->wifi.ssid_len);
104
105         if (network->wifi.mode != NULL)
106                 connman_dbus_dict_append_variant(&dict, "WiFi.Mode",
107                                 DBUS_TYPE_STRING, &network->wifi.mode);
108
109         if (network->wifi.security != NULL)
110                 connman_dbus_dict_append_variant(&dict, "WiFi.Security",
111                                 DBUS_TYPE_STRING, &network->wifi.security);
112
113         if (network->wifi.passphrase != NULL)
114                 connman_dbus_dict_append_variant(&dict, "WiFi.Passphrase",
115                                 DBUS_TYPE_STRING, &network->wifi.passphrase);
116
117         dbus_message_iter_close_container(&array, &dict);
118
119         return reply;
120 }
121
122 static DBusMessage *set_property(DBusConnection *conn,
123                                         DBusMessage *msg, void *data)
124 {
125         struct connman_network *network = data;
126         DBusMessageIter iter, value;
127         const char *name;
128
129         DBG("conn %p", conn);
130
131         if (dbus_message_iter_init(msg, &iter) == FALSE)
132                 return __connman_error_invalid_arguments(msg);
133
134         dbus_message_iter_get_basic(&iter, &name);
135         dbus_message_iter_next(&iter);
136         dbus_message_iter_recurse(&iter, &value);
137
138         if (__connman_security_check_privileges(msg) < 0)
139                 return __connman_error_permission_denied(msg);
140
141         if (g_str_equal(name, "Remember") == TRUE) {
142                 connman_bool_t remember;
143
144                 dbus_message_iter_get_basic(&value, &remember);
145
146                 if (network->remember == remember)
147                         return __connman_error_invalid_arguments(msg);
148         } else if (g_str_equal(name, "WiFi.Passphrase") == TRUE) {
149                 const char *passphrase;
150
151                 dbus_message_iter_get_basic(&value, &passphrase);
152
153                 g_free(network->wifi.passphrase);
154                 network->wifi.passphrase = g_strdup(passphrase);
155         }
156
157         __connman_storage_save_network(network);
158
159         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
160 }
161
162 static DBusMessage *do_connect(DBusConnection *conn,
163                                         DBusMessage *msg, void *data)
164 {
165         struct connman_network *network = data;
166         int err;
167
168         DBG("conn %p", conn);
169
170         if (network->driver && network->driver->connect) {
171                 err = network->driver->connect(network);
172                 if (err < 0 && err != -EINPROGRESS)
173                         return __connman_error_failed(msg);
174         }
175
176         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
177 }
178
179 static DBusMessage *do_disconnect(DBusConnection *conn,
180                                         DBusMessage *msg, void *data)
181 {
182         struct connman_network *network = data;
183         int err;
184
185         DBG("conn %p", conn);
186
187         if (network->driver && network->driver->disconnect) {
188                 err = network->driver->disconnect(network);
189                 if (err < 0 && err != -EINPROGRESS)
190                         return __connman_error_failed(msg);
191         }
192
193         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
194 }
195
196 static GDBusMethodTable network_methods[] = {
197         { "GetProperties", "",   "a{sv}", get_properties },
198         { "SetProperty",   "sv", "",      set_property   },
199         { "Connect",       "",   "",      do_connect     },
200         { "Disconnect",    "",   "",      do_disconnect  },
201         { },
202 };
203
204 static GDBusSignalTable network_signals[] = {
205         { "PropertyChanged", "sv" },
206         { },
207 };
208
209 static DBusConnection *connection;
210
211 static void emit_networks_signal(void)
212 {
213 }
214
215 static int register_interface(struct connman_element *element)
216 {
217         struct connman_network *network = element->network;
218
219         DBG("element %p name %s", element, element->name);
220
221         if (g_dbus_register_interface(connection, element->path,
222                                         CONNMAN_NETWORK_INTERFACE,
223                                         network_methods, network_signals,
224                                         NULL, network, NULL) == FALSE) {
225                 connman_error("Failed to register %s network", element->path);
226                 return -EIO;
227         }
228
229         network->registered = TRUE;
230
231         emit_networks_signal();
232
233         return 0;
234 }
235
236 static void unregister_interface(struct connman_element *element)
237 {
238         struct connman_network * network = element->network;
239
240         DBG("element %p name %s", element, element->name);
241
242         network->registered = FALSE;
243
244         emit_networks_signal();
245
246         g_dbus_unregister_interface(connection, element->path,
247                                                 CONNMAN_NETWORK_INTERFACE);
248 }
249
250 connman_bool_t __connman_network_has_driver(struct connman_network *network)
251 {
252         if (network == NULL || network->driver == NULL)
253                 return FALSE;
254
255         return network->registered;
256 }
257
258 static GSList *driver_list = NULL;
259
260 static gint compare_priority(gconstpointer a, gconstpointer b)
261 {
262         const struct connman_network_driver *driver1 = a;
263         const struct connman_network_driver *driver2 = b;
264
265         return driver2->priority - driver1->priority;
266 }
267
268 /**
269  * connman_network_driver_register:
270  * @driver: network driver definition
271  *
272  * Register a new network driver
273  *
274  * Returns: %0 on success
275  */
276 int connman_network_driver_register(struct connman_network_driver *driver)
277 {
278         DBG("driver %p name %s", driver, driver->name);
279
280         driver_list = g_slist_insert_sorted(driver_list, driver,
281                                                         compare_priority);
282
283         return 0;
284 }
285
286 /**
287  * connman_network_driver_unregister:
288  * @driver: network driver definition
289  *
290  * Remove a previously registered network driver
291  */
292 void connman_network_driver_unregister(struct connman_network_driver *driver)
293 {
294         DBG("driver %p name %s", driver, driver->name);
295
296         driver_list = g_slist_remove(driver_list, driver);
297 }
298
299 static void network_destruct(struct connman_element *element)
300 {
301         struct connman_network *network = element->network;
302
303         DBG("element %p name %s", element, element->name);
304
305         g_free(network->wifi.ssid);
306         g_free(network->wifi.mode);
307         g_free(network->wifi.security);
308         g_free(network->wifi.passphrase);
309
310         g_free(network->node);
311         g_free(network->name);
312         g_free(network->identifier);
313 }
314
315 /**
316  * connman_network_create:
317  * @identifier: network identifier (for example an unqiue name)
318  *
319  * Allocate a new network and assign the #identifier to it.
320  *
321  * Returns: a newly-allocated #connman_network structure
322  */
323 struct connman_network *connman_network_create(const char *identifier,
324                                                 enum connman_network_type type)
325 {
326         struct connman_network *network;
327
328         DBG("identifier %s type %d", identifier, type);
329
330         network = g_try_new0(struct connman_network, 1);
331         if (network == NULL)
332                 return NULL;
333
334         DBG("network %p", network);
335
336         network->element.refcount = 1;
337
338         network->element.name = g_strdup(identifier);
339         network->element.type = CONNMAN_ELEMENT_TYPE_NETWORK;
340         network->element.index = -1;
341
342         switch (type) {
343         case CONNMAN_NETWORK_TYPE_UNKNOWN:
344         case CONNMAN_NETWORK_TYPE_VENDOR:
345                 network->element.subtype = CONNMAN_ELEMENT_SUBTYPE_UNKNOWN;
346                 break;
347         case CONNMAN_NETWORK_TYPE_WIFI:
348                 network->element.subtype = CONNMAN_ELEMENT_SUBTYPE_WIFI;
349                 break;
350         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
351         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
352                 network->element.subtype = CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH;
353                 break;
354         case CONNMAN_NETWORK_TYPE_HSO:
355                 network->element.subtype = CONNMAN_ELEMENT_SUBTYPE_CELLULAR;
356                 break;
357         }
358
359         network->element.network = network;
360         network->element.destruct = network_destruct;
361
362         network->type = type;
363         network->identifier = g_strdup(identifier);
364
365         return network;
366 }
367
368 /**
369  * connman_network_ref:
370  * @network: network structure
371  *
372  * Increase reference counter of  network
373  */
374 struct connman_network *connman_network_ref(struct connman_network *network)
375 {
376         if (connman_element_ref(&network->element) == NULL)
377                 return NULL;
378
379         return network;
380 }
381
382 /**
383  * connman_network_unref:
384  * @network: network structure
385  *
386  * Decrease reference counter of network
387  */
388 void connman_network_unref(struct connman_network *network)
389 {
390         connman_element_unref(&network->element);
391 }
392
393 /**
394  * connman_network_get_identifier:
395  * @network: network structure
396  *
397  * Get identifier of network
398  */
399 const char *connman_network_get_identifier(struct connman_network *network)
400 {
401         return network->identifier;
402 }
403
404 /**
405  * connman_network_get_path:
406  * @network: network structure
407  *
408  * Get path name of network
409  */
410 const char *connman_network_get_path(struct connman_network *network)
411 {
412         return network->element.path;
413 }
414
415 /**
416  * connman_network_set_index:
417  * @network: network structure
418  * @index: index number
419  *
420  * Set index number of network
421  */
422 void connman_network_set_index(struct connman_network *network, int index)
423 {
424         network->element.index = index;
425 }
426
427 /**
428  * connman_network_get_index:
429  * @network: network structure
430  *
431  * Get index number of network
432  */
433 int connman_network_get_index(struct connman_network *network)
434 {
435         return network->element.index;
436 }
437
438 /**
439  * connman_network_set_protocol:
440  * @network: network structure
441  * @protocol: network protocol
442  *
443  * Change protocol of network
444  */
445 void connman_network_set_protocol(struct connman_network *network,
446                                         enum connman_network_protocol protocol)
447 {
448         network->protocol = protocol;
449 }
450
451 /**
452  * connman_network_set_connected:
453  * @network: network structure
454  * @connected: connected state
455  *
456  * Change connected state of network
457  */
458 int connman_network_set_connected(struct connman_network *network,
459                                                 connman_bool_t connected)
460 {
461         DBG("network %p connected %d", network, connected);
462
463         if (network->connected == connected)
464                 return -EALREADY;
465
466         network->connected = connected;
467
468         if (connected == TRUE) {
469                 struct connman_element *element;
470                 enum connman_element_type type = CONNMAN_ELEMENT_TYPE_UNKNOWN;
471
472                 switch (network->protocol) {
473                 case CONNMAN_NETWORK_PROTOCOL_UNKNOWN:
474                         return 0;
475                 case CONNMAN_NETWORK_PROTOCOL_IP:
476                         type = CONNMAN_ELEMENT_TYPE_DHCP;
477                         break;
478                 case CONNMAN_NETWORK_PROTOCOL_PPP:
479                         type = CONNMAN_ELEMENT_TYPE_PPP;
480                         break;
481                 }
482
483                 element = connman_element_create(NULL);
484                 if (element != NULL) {
485                         element->type    = type;
486                         element->subtype = network->element.subtype;
487                         element->index   = network->element.index;
488
489                         if (connman_element_register(element,
490                                                         &network->element) < 0)
491                                 connman_element_unref(element);
492                 }
493         } else
494                 connman_element_unregister_children(&network->element);
495
496         return 0;
497 }
498
499 /**
500  * connman_network_set_string:
501  * @network: network structure
502  * @key: unique identifier
503  * @value: string value
504  *
505  * Set string value for specific key
506  */
507 int connman_network_set_string(struct connman_network *network,
508                                         const char *key, const char *value)
509 {
510         DBG("network %p key %s value %s", network, key, value);
511
512         if (g_str_equal(key, "Name") == TRUE) {
513                 g_free(network->name);
514                 network->name = g_strdup(value);
515         } else if (g_str_equal(key, "Node") == TRUE) {
516                 g_free(network->node);
517                 network->node = g_strdup(value);
518         } else if (g_str_equal(key, "WiFi.Mode") == TRUE) {
519                 g_free(network->wifi.mode);
520                 network->wifi.mode = g_strdup(value);
521         } else if (g_str_equal(key, "WiFi.Security") == TRUE) {
522                 g_free(network->wifi.security);
523                 network->wifi.security = g_strdup(value);
524         }
525
526         return 0;
527 }
528
529 /**
530  * connman_network_get_string:
531  * @network: network structure
532  * @key: unique identifier
533  *
534  * Get string value for specific key
535  */
536 const char *connman_network_get_string(struct connman_network *network,
537                                                         const char *key)
538 {
539         DBG("network %p key %s", network);
540
541         if (g_str_equal(key, "Name") == TRUE)
542                 return network->name;
543         else if (g_str_equal(key, "Node") == TRUE)
544                 return network->node;
545         else if (g_str_equal(key, "WiFi.Mode") == TRUE)
546                 return network->wifi.mode;
547         else if (g_str_equal(key, "WiFi.Security") == TRUE)
548                 return network->wifi.security;
549
550         return NULL;
551 }
552
553 /**
554  * connman_network_set_uint8:
555  * @network: network structure
556  * @key: unique identifier
557  * @value: integer value
558  *
559  * Set integer value for specific key
560  */
561 int connman_network_set_uint8(struct connman_network *network,
562                                         const char *key, connman_uint8_t value)
563 {
564         DBG("network %p key %s value %d", network, key, value);
565
566         if (g_str_equal(key, "Strength") == TRUE)
567                 network->strength = value;
568
569         return 0;
570 }
571
572 /**
573  * connman_network_set_blob:
574  * @network: network structure
575  * @key: unique identifier
576  * @data: blob data
577  * @size: blob size
578  *
579  * Set binary blob value for specific key
580  */
581 int connman_network_set_blob(struct connman_network *network,
582                         const char *key, const void *data, unsigned int size)
583 {
584         DBG("network %p key %s size %d", network, key, size);
585
586         if (g_str_equal(key, "WiFi.SSID") == TRUE) {
587                 g_free(network->wifi.ssid);
588                 network->wifi.ssid = g_try_malloc(size);
589                 if (network->wifi.ssid != NULL) {
590                         memcpy(network->wifi.ssid, data, size);
591                         network->wifi.ssid_len = size;
592                 } else
593                         network->wifi.ssid_len = 0;
594         }
595
596         return 0;
597 }
598
599 /**
600  * connman_network_get_blob:
601  * @network: network structure
602  * @key: unique identifier
603  * @size: pointer to blob size
604  *
605  * Get binary blob value for specific key
606  */
607 const void *connman_network_get_blob(struct connman_network *network,
608                                         const char *key, unsigned int *size)
609 {
610         if (g_str_equal(key, "WiFi.SSID") == TRUE) {
611                 if (size != NULL)
612                         *size = network->wifi.ssid_len;
613                 return network->wifi.ssid;
614         }
615
616         return NULL;
617 }
618
619 void __connman_network_set_device(struct connman_network *network,
620                                         struct connman_device *device)
621 {
622         network->device = device;
623 }
624
625 /**
626  * connman_network_get_device:
627  * @network: network structure
628  *
629  * Get parent device of network
630  */
631 struct connman_device *connman_network_get_device(struct connman_network *network)
632 {
633         return network->device;
634 }
635
636 /**
637  * connman_network_get_data:
638  * @network: network structure
639  *
640  * Get private network data pointer
641  */
642 void *connman_network_get_data(struct connman_network *network)
643 {
644         return network->driver_data;
645 }
646
647 /**
648  * connman_network_set_data:
649  * @network: network structure
650  * @data: data pointer
651  *
652  * Set private network data pointer
653  */
654 void connman_network_set_data(struct connman_network *network, void *data)
655 {
656         network->driver_data = data;
657 }
658
659 static gboolean match_driver(struct connman_network *network,
660                                         struct connman_network_driver *driver)
661 {
662         if (network->type == driver->type ||
663                         driver->type == CONNMAN_NETWORK_TYPE_UNKNOWN)
664                 return TRUE;
665
666         return FALSE;
667 }
668
669 static int network_probe(struct connman_element *element)
670 {
671         struct connman_network *network = element->network;
672         GSList *list;
673         int err;
674
675         DBG("element %p name %s", element, element->name);
676
677         if (network == NULL)
678                 return -ENODEV;
679
680         for (list = driver_list; list; list = list->next) {
681                 struct connman_network_driver *driver = list->data;
682
683                 if (match_driver(network, driver) == FALSE)
684                         continue;
685
686                 DBG("driver %p name %s", driver, driver->name);
687
688                 if (driver->probe(network) == 0) {
689                         network->driver = driver;
690                         break;
691                 }
692         }
693
694         if (network->driver == NULL)
695                 return -ENODEV;
696
697         err = register_interface(element);
698         if (err < 0) {
699                 if (network->driver->remove)
700                         network->driver->remove(network);
701                 return err;
702         }
703
704         return 0;
705 }
706
707 static void network_remove(struct connman_element *element)
708 {
709         struct connman_network *network = element->network;
710
711         DBG("element %p name %s", element, element->name);
712
713         if (network == NULL)
714                 return;
715
716         if (network->driver == NULL)
717                 return;
718
719         unregister_interface(element);
720
721         if (network->driver->remove)
722                 network->driver->remove(network);
723 }
724
725 static struct connman_driver network_driver = {
726         .name           = "network",
727         .type           = CONNMAN_ELEMENT_TYPE_NETWORK,
728         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
729         .probe          = network_probe,
730         .remove         = network_remove,
731 };
732
733 static int network_load(struct connman_network *network)
734 {
735         GKeyFile *keyfile;
736         gchar *pathname, *data = NULL;
737         gsize length;
738         const char *name;
739
740         DBG("network %p", network);
741
742         name = connman_device_get_name(network->device);
743         if (name == NULL)
744                 return -EINVAL;
745
746         pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, name);
747         if (pathname == NULL)
748                 return -ENOMEM;
749
750         keyfile = g_key_file_new();
751
752         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) {
753                 g_free(pathname);
754                 return -ENOENT;
755         }
756
757         g_free(pathname);
758
759         if (g_key_file_load_from_data(keyfile, data, length,
760                                                         0, NULL) == FALSE) {
761                 g_free(data);
762                 return -EILSEQ;
763         }
764
765         g_free(data);
766
767         network->remember = g_key_file_get_boolean(keyfile,
768                                         network->identifier, "Remember", NULL);
769
770         g_free(network->wifi.security);
771         network->wifi.security = g_key_file_get_string(keyfile,
772                                 network->identifier, "WiFi.Security", NULL);
773
774         g_free(network->wifi.passphrase);
775         network->wifi.passphrase = g_key_file_get_string(keyfile,
776                                 network->identifier, "WiFi.Passphrase", NULL);
777
778         g_key_file_free(keyfile);
779
780         return 0;
781 }
782
783 static int network_save(struct connman_network *network)
784 {
785         GKeyFile *keyfile;
786         gchar *pathname, *data = NULL;
787         gsize length;
788         const char *name;
789
790         DBG("network %p", network);
791
792         name = connman_device_get_name(network->device);
793         if (name == NULL)
794                 return -EINVAL;
795
796         pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, name);
797         if (pathname == NULL)
798                 return -ENOMEM;
799
800         keyfile = g_key_file_new();
801
802         if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE)
803                 goto update;
804
805         if (length > 0) {
806                 if (g_key_file_load_from_data(keyfile, data, length,
807                                                         0, NULL) == FALSE)
808                         goto done;
809         }
810
811         g_free(data);
812
813 update:
814         g_key_file_set_boolean(keyfile, network->identifier,
815                                         "Remember", network->remember);
816
817         if (network->wifi.security != NULL)
818                 g_key_file_set_string(keyfile, network->identifier,
819                                 "WiFi.Security", network->wifi.security);
820
821         if (network->wifi.passphrase != NULL)
822                 g_key_file_set_string(keyfile, network->identifier,
823                                 "WiFi.Passphrase", network->wifi.passphrase);
824
825         data = g_key_file_to_data(keyfile, &length, NULL);
826
827         g_file_set_contents(pathname, data, length, NULL);
828
829 done:
830         g_free(data);
831
832         g_key_file_free(keyfile);
833
834         g_free(pathname);
835
836         return 0;
837 }
838
839 static struct connman_storage network_storage = {
840         .name           = "network",
841         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
842         .network_load   = network_load,
843         .network_save   = network_save,
844 };
845
846 int __connman_network_init(void)
847 {
848         DBG("");
849
850         connection = connman_dbus_get_connection();
851
852         if (connman_storage_register(&network_storage) < 0)
853                 connman_error("Failed to register network storage");
854
855         return connman_driver_register(&network_driver);
856 }
857
858 void __connman_network_cleanup(void)
859 {
860         DBG("");
861
862         connman_driver_unregister(&network_driver);
863
864         connman_storage_unregister(&network_storage);
865
866         dbus_connection_unref(connection);
867 }