Imported Upstream version 1.38
[platform/upstream/connman.git] / plugins / iwd.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2016  BMW Car IT GmbH.
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 <errno.h>
27 #include <string.h>
28 #include <stdbool.h>
29 #include <linux/if_ether.h>
30
31 #define CONNMAN_API_SUBJECT_TO_CHANGE
32 #include <connman/plugin.h>
33 #include <connman/dbus.h>
34 #include <connman/network.h>
35 #include <connman/technology.h>
36 #include <connman/inet.h>
37 #include <gdbus.h>
38
39 static DBusConnection *connection;
40 static GDBusClient *client;
41 static GDBusProxy *agent_proxy;
42 static GHashTable *adapters;
43 static GHashTable *devices;
44 static GHashTable *networks;
45 static GHashTable *known_networks;
46 static GHashTable *stations;
47 static GHashTable *access_points;
48 static bool agent_registered;
49
50 #define IWD_SERVICE                     "net.connman.iwd"
51 #define IWD_PATH                        "/"
52 #define IWD_AGENT_MANAGER_INTERFACE     "net.connman.iwd.AgentManager"
53 #define IWD_ADAPTER_INTERFACE           "net.connman.iwd.Adapter"
54 #define IWD_DEVICE_INTERFACE            "net.connman.iwd.Device"
55 #define IWD_NETWORK_INTERFACE           "net.connman.iwd.Network"
56 #define IWD_KNOWN_NETWORK_INTERFACE     "net.connman.iwd.KnownNetwork"
57 #define IWD_STATION_INTERFACE           "net.connman.iwd.Station"
58 #define IWD_AP_INTERFACE                "net.connman.iwd.AccessPoint"
59
60 #define IWD_AGENT_INTERFACE             "net.connman.iwd.Agent"
61 #define IWD_AGENT_ERROR_INTERFACE       "net.connman.iwd.Agent.Error"
62 #define AGENT_PATH                      "/net/connman/iwd_agent"
63
64 struct iwd_adapter {
65         GDBusProxy *proxy;
66         char *path;
67         char *vendor;
68         char *model;
69         bool powered;
70         bool ad_hoc;
71         bool station;
72         bool ap;
73 };
74
75 struct iwd_device {
76         GDBusProxy *proxy;
77         char *path;
78         char *adapter;
79         char *name;
80         char *address;
81         bool powered;
82         char *mode;
83
84         struct connman_device *device;
85 };
86
87 struct iwd_network {
88         GDBusProxy *proxy;
89         char *path;
90         char *device;
91         char *name;
92         char *type;
93         bool connected;
94         char *known_network;
95
96         struct iwd_device *iwdd;
97         struct connman_network *network;
98 };
99
100 struct iwd_known_network {
101         GDBusProxy *proxy;
102         char *path;
103         char *name;
104         char *type;
105         bool hidden;
106         char *last_connected_time;
107         bool auto_connect;
108         int auto_connect_id;
109 };
110
111 struct iwd_station {
112         GDBusProxy *proxy;
113         char *path;
114         char *state;
115         char *connected_network;
116         bool scanning;
117 };
118
119 struct iwd_ap {
120         GDBusProxy *proxy;
121         char *path;
122         bool started;
123
124         int index;
125         char *bridge;
126         struct connman_technology *tech;
127 };
128
129 static const char *proxy_get_string(GDBusProxy *proxy, const char *property)
130 {
131         DBusMessageIter iter;
132         const char *str;
133
134         if (!g_dbus_proxy_get_property(proxy, property, &iter))
135                 return NULL;
136
137         dbus_message_iter_get_basic(&iter, &str);
138
139         return str;
140 }
141
142 static GSList *proxy_get_strings(GDBusProxy *proxy, const char *property)
143 {
144         DBusMessageIter array, entry;
145         GSList *list = NULL;
146
147         if (!g_dbus_proxy_get_property(proxy, property, &array))
148                 return NULL;
149
150         dbus_message_iter_recurse(&array, &entry);
151
152         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING){
153                 const char *val;
154
155                 dbus_message_iter_get_basic(&entry, &val);
156                 list = g_slist_prepend(list, g_strdup(val));
157                 dbus_message_iter_next(&entry);
158         }
159
160         return list;
161 }
162
163 static bool proxy_get_bool(GDBusProxy *proxy, const char *property)
164 {
165         DBusMessageIter iter;
166         dbus_bool_t value;
167
168         if (!g_dbus_proxy_get_property(proxy, property, &iter))
169                 return false;
170
171         dbus_message_iter_get_basic(&iter, &value);
172
173         return value;
174 }
175
176 static void address2ident(const char *address, char *ident)
177 {
178         int i;
179
180         for (i = 0; i < ETH_ALEN; i++) {
181                 ident[i * 2] = address[i * 3];
182                 ident[i * 2 + 1] = address[i * 3 + 1];
183         }
184         ident[ETH_ALEN * 2] = '\0';
185 }
186
187 static int cm_network_probe(struct connman_network *network)
188 {
189         GHashTableIter iter;
190         gpointer key, value;
191
192         g_hash_table_iter_init(&iter, networks);
193
194         while (g_hash_table_iter_next(&iter, &key, &value)) {
195                 struct iwd_network *iwdn = value;
196
197                 if (network == iwdn->network)
198                         return 0;
199         }
200
201         return -EOPNOTSUPP;
202 }
203
204 static void update_network_connected(struct iwd_network *iwdn)
205 {
206         struct iwd_device *iwdd;
207         int index;
208
209         iwdd = g_hash_table_lookup(devices, iwdn->device);
210         if (!iwdd)
211                 return;
212
213         index = connman_inet_ifindex(iwdd->name);
214         if (index < 0)
215                 return;
216
217         DBG("interface name %s index %d", iwdd->name, index);
218         connman_network_set_index(iwdn->network, index);
219         connman_network_set_connected(iwdn->network, true);
220 }
221
222 static void update_network_disconnected(struct iwd_network *iwdn)
223 {
224         DBG("interface name %s", iwdn->name);
225         connman_network_set_connected(iwdn->network, false);
226 }
227
228 static void cm_network_connect_cb(DBusMessage *message, void *user_data)
229 {
230         const char *path = user_data;
231         struct iwd_network *iwdn;
232
233         iwdn = g_hash_table_lookup(networks, path);
234         if (!iwdn)
235                 return;
236
237         if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
238                 const char *dbus_error = dbus_message_get_error_name(message);
239
240                 if (!strcmp(dbus_error, "net.connman.iwd.InProgress"))
241                         return;
242
243                 DBG("%s connect failed: %s", path, dbus_error);
244                 if (!strcmp(dbus_error, "net.connman.iwd.Failed"))
245                         connman_network_set_error(iwdn->network,
246                                         CONNMAN_NETWORK_ERROR_INVALID_KEY);
247                 else
248                         connman_network_set_error(iwdn->network,
249                                         CONNMAN_NETWORK_ERROR_CONNECT_FAIL);
250                 return;
251         }
252
253         update_network_connected(iwdn);
254 }
255
256 static int cm_network_connect(struct connman_network *network)
257 {
258         struct iwd_network *iwdn = connman_network_get_data(network);
259
260         if (!iwdn)
261                 return -EINVAL;
262
263         if (!g_dbus_proxy_method_call(iwdn->proxy, "Connect",
264                         NULL, cm_network_connect_cb,
265                         g_strdup(iwdn->path), g_free))
266                 return -EIO;
267
268         connman_network_set_associating(iwdn->network, true);
269
270         return -EINPROGRESS;
271 }
272
273 static void cm_network_disconnect_cb(DBusMessage *message, void *user_data)
274 {
275         const char *path = user_data;
276         struct iwd_network *iwdn;
277
278         iwdn = g_hash_table_lookup(networks, path);
279         if (!iwdn)
280                 return;
281
282         if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
283                 const char *dbus_error = dbus_message_get_error_name(message);
284
285                 if (!strcmp(dbus_error, "net.connman.iwd.NotConnected")) {
286                         /* fall through */
287                 } else {
288                         DBG("%s disconnect failed: %s", path, dbus_error);
289                         return;
290                 }
291         }
292
293         /*
294          * We end up in a tight loop in the error case. That is
295          * when we can't connect, bail out in cm_network_connect_cb() with
296          * an error.
297          */
298         if (connman_network_get_connected(iwdn->network))
299                 update_network_disconnected(iwdn);
300 }
301
302 static int cm_network_disconnect(struct connman_network *network)
303 {
304         struct iwd_network *iwdn = connman_network_get_data(network);
305         struct iwd_station *iwds;
306
307         if (!iwdn && !iwdn->iwdd)
308                 return -EINVAL;
309
310         iwds = g_hash_table_lookup(stations, iwdn->iwdd->path);
311         if (!iwds)
312                 return -EIO;
313
314         if (!g_dbus_proxy_method_call(iwds->proxy, "Disconnect",
315                         NULL, cm_network_disconnect_cb, g_strdup(iwdn->path), g_free))
316                 return -EIO;
317
318         return -EINPROGRESS;
319 }
320
321 static struct connman_network_driver network_driver = {
322         .name           = "iwd",
323         .type           = CONNMAN_NETWORK_TYPE_WIFI,
324         .probe          = cm_network_probe,
325         .connect        = cm_network_connect,
326         .disconnect     = cm_network_disconnect,
327 };
328
329 static int cm_device_probe(struct connman_device *device)
330 {
331         GHashTableIter iter;
332         gpointer key, value;
333
334         g_hash_table_iter_init(&iter, devices);
335
336         while (g_hash_table_iter_next(&iter, &key, &value)) {
337                 struct iwd_device *iwdd = value;
338
339                 if (device == iwdd->device)
340                         return 0;
341         }
342
343         return -EOPNOTSUPP;
344 }
345
346 static void cm_device_remove(struct connman_device *device)
347 {
348 }
349
350 struct dev_cb_data {
351         char *path;
352         bool powered;
353 };
354
355 static void device_powered_cb(const DBusError *error, void *user_data)
356 {
357         struct dev_cb_data *cbd = user_data;
358         struct iwd_device *iwdd;
359
360         iwdd = g_hash_table_lookup(devices, cbd->path);
361         if (!iwdd)
362                 goto out;
363
364         if (dbus_error_is_set(error)) {
365                 connman_warn("WiFi device %s not enabled %s",
366                                 cbd->path, error->message);
367                 goto out;
368         }
369
370         connman_device_set_powered(iwdd->device, cbd->powered);
371 out:
372         g_free(cbd->path);
373         g_free(cbd);
374 }
375
376 static int set_device_powered(struct connman_device *device, bool powered)
377 {
378         struct iwd_device *iwdd = connman_device_get_data(device);
379         dbus_bool_t device_powered = powered;
380         struct dev_cb_data *cbd;
381
382         if (proxy_get_bool(iwdd->proxy, "Powered"))
383                 return -EALREADY;
384
385         cbd = g_new(struct dev_cb_data, 1);
386         cbd->path = g_strdup(iwdd->path);
387         cbd->powered = powered;
388
389         g_dbus_proxy_set_property_basic(iwdd->proxy, "Powered",
390                         DBUS_TYPE_BOOLEAN, &device_powered,
391                         device_powered_cb, cbd, NULL);
392
393         return -EINPROGRESS;
394 }
395
396 static int cm_device_enable(struct connman_device *device)
397 {
398         return set_device_powered(device, true);
399 }
400
401 static int cm_device_disable(struct connman_device *device)
402 {
403         return set_device_powered(device, false);
404 }
405
406 static void cm_device_scan_cb(DBusMessage *message, void *user_data)
407 {
408         const char *path = user_data;
409         struct iwd_station *iwds;
410
411         iwds = g_hash_table_lookup(networks, path);
412         if (!iwds)
413                 return;
414
415         if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
416                 const char *dbus_error = dbus_message_get_error_name(message);
417
418                 DBG("%s scan failed: %s", path, dbus_error);
419         }
420 }
421
422 static int cm_device_scan(struct connman_device *device,
423                                 struct connman_device_scan_params *params)
424 {
425         struct iwd_device *iwdd = connman_device_get_data(device);
426         struct iwd_station *iwds;
427
428         if (strcmp(iwdd->mode, "station"))
429                 return -EINVAL;
430
431         iwds = g_hash_table_lookup(stations, iwdd->path);
432         if (!iwds)
433                 return -EIO;
434
435         if (!g_dbus_proxy_method_call(iwds->proxy, "Scan",
436                         NULL, cm_device_scan_cb, g_strdup(iwds->path), g_free))
437                 return -EIO;
438
439         return -EINPROGRESS;
440 }
441
442 static struct connman_device_driver device_driver = {
443         .name           = "iwd",
444         .type           = CONNMAN_DEVICE_TYPE_WIFI,
445         .probe          = cm_device_probe,
446         .remove         = cm_device_remove,
447         .enable         = cm_device_enable,
448         .disable        = cm_device_disable,
449         .scan           = cm_device_scan,
450 };
451
452 static int cm_tech_probe(struct connman_technology *technology)
453 {
454         return 0;
455 }
456
457 static void cm_tech_remove(struct connman_technology *technology)
458 {
459 }
460
461 struct tech_cb_data {
462         struct iwd_device *iwdd;
463         char *path;
464         char *ssid;
465         char *passphrase;
466         char *bridge;
467         int index;
468         struct connman_technology *tech;
469 };
470
471 static void tech_cb_free(struct tech_cb_data *cbd)
472 {
473         g_free(cbd->path);
474         g_free(cbd->ssid);
475         g_free(cbd->passphrase);
476         g_free(cbd->bridge);
477         g_free(cbd);
478 }
479
480 static int cm_change_tethering(struct iwd_device *iwdd,
481                         struct connman_technology *technology,
482                         const char *identifier, const char *passphrase,
483                         const char *bridge, bool enabled);
484
485 static void tech_ap_start_cb(DBusMessage *message, void *user_data)
486 {
487
488         struct tech_cb_data *cbd = user_data;
489
490         if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
491                 const char *dbus_error = dbus_message_get_error_name(message);
492
493                 connman_warn("iwd device %s could not enable AccessPoint mode: %s",
494                         cbd->path, dbus_error);
495                 goto out;
496         }
497
498         /* wait for 'Started' signal */
499         return;
500 out:
501         cm_change_tethering(cbd->iwdd, cbd->tech,
502                         cbd->ssid, cbd->passphrase, cbd->bridge, false);
503         tech_cb_free(cbd);
504 }
505
506 static void tech_ap_stop_cb(DBusMessage *message, void *user_data)
507 {
508         struct tech_cb_data *cbd = user_data;
509
510         if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
511                 const char *dbus_error = dbus_message_get_error_name(message);
512
513                 connman_warn("iwd device %s could not disable AccessPoint mode: %s",
514                         cbd->path, dbus_error);
515                 goto out;
516         }
517
518         return;
519 out:
520         tech_cb_free(cbd);
521 }
522
523 static void ap_start_append(DBusMessageIter *iter, void *user_data)
524 {
525         struct tech_cb_data *cbd = user_data;
526
527         DBG("ssid %s", cbd->ssid);
528         DBG("passphrase %s", cbd->passphrase);
529
530         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cbd->ssid);
531         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cbd->passphrase);
532 }
533
534 static void tech_enable_tethering_cb(const DBusError *error, void *user_data)
535 {
536         struct tech_cb_data *cbd = user_data;
537         struct iwd_device *iwdd;
538         struct iwd_ap *iwdap;
539
540         DBG("");
541
542         iwdd = g_hash_table_lookup(devices, cbd->path);
543         if (!iwdd) {
544                 DBG("device already removed");
545                 goto out;
546         }
547
548         if (dbus_error_is_set(error)) {
549                 connman_warn("iwd device %s could not enable AcessPoint mode: %s",
550                         cbd->path, error->message);
551                 goto out;
552         }
553
554         iwdap = g_hash_table_lookup(access_points, iwdd->path);
555         if (!iwdap) {
556                 DBG("%s no ap object found", iwdd->path);
557                 goto out;
558         }
559
560         iwdap->index = cbd->index;
561         iwdap->bridge = g_strdup(cbd->bridge);
562         iwdap->tech = cbd->tech;
563
564         if (!g_dbus_proxy_method_call(iwdap->proxy, "Start",
565                                 ap_start_append, tech_ap_start_cb, cbd, NULL)) {
566                 connman_warn("iwd ap %s could not start AccessPoint mode: %s",
567                         cbd->path, error->message);
568                 goto out;
569         }
570
571         return;
572 out:
573         if (iwdap) {
574                 iwdap->index = -1;
575                 g_free(iwdap->bridge);
576                 iwdap->bridge = NULL;
577         }
578         tech_cb_free(cbd);
579 }
580
581 static void tech_disable_tethering_cb(const DBusError *error, void *user_data)
582 {
583         struct tech_cb_data *cbd = user_data;
584         struct iwd_device *iwdd;
585         struct iwd_ap *iwdap;
586
587         DBG("");
588
589         iwdd = g_hash_table_lookup(devices, cbd->path);
590         if (!iwdd) {
591                 DBG("device already removed");
592                 goto out;
593         }
594
595         if (dbus_error_is_set(error)) {
596                 connman_warn("iwd device %s could not enable Station mode: %s",
597                         cbd->path, error->message);
598                 goto out;
599         }
600
601         iwdap = g_hash_table_lookup(access_points, iwdd->path);
602         if (!iwdap) {
603                 DBG("%s no ap object found", iwdd->path);
604                 goto out;
605         }
606
607         g_free(iwdap->bridge);
608         iwdap->index = -1;
609         iwdap->bridge = NULL;
610         iwdap->tech = NULL;
611
612         if (!connman_inet_remove_from_bridge(cbd->index, cbd->bridge))
613                 goto out;
614
615         connman_technology_tethering_notify(cbd->tech, false);
616
617         if (!g_dbus_proxy_method_call(iwdap->proxy, "Stop",
618                                         NULL, tech_ap_stop_cb, cbd, NULL)) {
619                 connman_warn("iwd ap %s could not start AccessPoint mode: %s",
620                         cbd->path, error->message);
621                 goto out;
622         }
623
624         return;
625 out:
626         tech_cb_free(cbd);
627 }
628
629 static int cm_change_tethering(struct iwd_device *iwdd,
630                         struct connman_technology *technology,
631                         const char *identifier, const char *passphrase,
632                         const char *bridge, bool enabled)
633 {
634         struct tech_cb_data *cbd;
635         int index;
636         const char *mode;
637         GDBusResultFunction cb;
638
639         index = connman_inet_ifindex(iwdd->name);
640         if (index < 0)
641                 return -ENODEV;
642
643         cbd = g_new(struct tech_cb_data, 1);
644         cbd->iwdd = iwdd;
645         cbd->path = g_strdup(iwdd->path);
646         cbd->ssid = g_strdup(identifier);
647         cbd->passphrase = g_strdup(passphrase);
648         cbd->bridge = g_strdup(bridge);
649         cbd->tech = technology;
650         cbd->index = index;
651
652         if (enabled) {
653                 mode = "ap";
654                 cb = tech_enable_tethering_cb;
655         } else {
656                 mode = "station";
657                 cb = tech_disable_tethering_cb;
658         }
659
660         if (!g_dbus_proxy_set_property_basic(iwdd->proxy,
661                         "Mode", DBUS_TYPE_STRING, &mode,
662                         cb, cbd, NULL)) {
663                 tech_cb_free(cbd);
664                 return -EIO;
665         }
666
667         return 0;
668 }
669
670 static int cm_tech_tethering(struct connman_technology *technology,
671                         const char *identifier, const char *passphrase,
672                         const char *bridge, bool enabled)
673 {
674         GHashTableIter iter;
675         gpointer key, value;
676         int err = 0, res;
677
678         g_hash_table_iter_init(&iter, devices);
679
680         while (g_hash_table_iter_next(&iter, &key, &value)) {
681                 struct iwd_device *iwdd = value;
682                 struct iwd_adapter *iwda;
683
684                 iwda = g_hash_table_lookup(adapters, iwdd->adapter);
685                 if (!iwda)
686                         continue;
687
688                 if (!iwda->station || !iwda->ap )
689                         /* No support for Station and AccessPoint mode */
690                         continue;
691
692                 if (!enabled && !g_strcmp0("ap", iwdd->mode)) {
693                         res = cm_change_tethering(iwdd, technology, identifier,
694                                                 passphrase, bridge, enabled);
695                         if (res)
696                                 connman_warn("%s switching to Station mode failed",
697                                         iwdd->path);
698                         if (!err)
699                                 err = res;
700                         continue;
701                 }
702
703                 if (enabled && !g_strcmp0("station", iwdd->mode)) {
704                         err = cm_change_tethering(iwdd, technology, identifier,
705                                         passphrase, bridge, enabled);
706                         if (err)
707                                 connman_warn("%s switching to AccessPoint mode failed",
708                                         iwdd->path);
709                         break;
710                 }
711         }
712
713         return err;
714 }
715
716 static struct connman_technology_driver tech_driver = {
717         .name           = "iwd",
718         .type           = CONNMAN_SERVICE_TYPE_WIFI,
719         .probe          = cm_tech_probe,
720         .remove         = cm_tech_remove,
721         .set_tethering  = cm_tech_tethering,
722 };
723
724 static const char *security_remap(const char *security)
725 {
726         if (!g_strcmp0(security, "open"))
727                 return "none";
728         else if (!g_strcmp0(security, "psk"))
729                 return "psk";
730         else if (!g_strcmp0(security, "8021x"))
731                 return "ieee8021x";
732
733         return "unknown";
734 }
735
736 static char *create_identifier(const char *path, const char *security)
737 {
738         char *start, *end, *identifier;
739         char *_path = g_strdup(path);
740
741         /*
742          * _path is something like
743          *     /0/4/5363686970686f6c5f427573696e6573735f454150_8021x
744          */
745         start = strrchr(_path, '/');
746         start++;
747         end = strchr(start, '_');
748         *end = '\0';
749
750         /*
751          * Create an ident which is identical to the corresponding
752          * wpa_supplicant identifier.
753          */
754         identifier = g_strdup_printf("%s_managed_%s", start,
755                                 security_remap(security));
756         g_free(_path);
757
758         return identifier;
759 }
760
761 static void add_network(const char *path, struct iwd_network *iwdn)
762 {
763         struct iwd_device *iwdd;
764         char *identifier;
765
766         iwdd = g_hash_table_lookup(devices, iwdn->device);
767         if (!iwdd)
768                 return;
769
770         identifier = create_identifier(path, iwdn->type);
771         iwdn->network = connman_network_create(identifier,
772                                         CONNMAN_NETWORK_TYPE_WIFI);
773         connman_network_set_data(iwdn->network, iwdn);
774
775         connman_network_set_name(iwdn->network, iwdn->name);
776         connman_network_set_blob(iwdn->network, "WiFi.SSID", iwdn->name,
777                                         strlen(iwdn->name));
778         connman_network_set_string(iwdn->network, "WiFi.Security",
779                                         security_remap(iwdn->type));
780         connman_network_set_string(iwdn->network, "WiFi.Mode", "managed");
781
782         if (connman_device_add_network(iwdd->device, iwdn->network) < 0) {
783                 connman_network_unref(iwdn->network);
784                 iwdn->network = NULL;
785                 return;
786         }
787         iwdn->iwdd = iwdd;
788
789         connman_network_set_available(iwdn->network, true);
790         connman_network_set_group(iwdn->network, identifier);
791
792         g_free(identifier);
793 }
794
795 static void remove_network(struct iwd_network *iwdn)
796 {
797         if (!iwdn->network)
798                 return;
799
800         if (iwdn->iwdd)
801                 connman_device_remove_network(iwdn->iwdd->device,
802                                                 iwdn->network);
803
804         connman_network_unref(iwdn->network);
805         iwdn->network = NULL;
806 }
807
808 static void add_device(const char *path, struct iwd_device *iwdd)
809 {
810         char ident[ETH_ALEN * 2 + 1];
811
812         iwdd->device = connman_device_create("wifi", CONNMAN_DEVICE_TYPE_WIFI);
813         if (!iwdd->device)
814                 return;
815
816         connman_device_set_data(iwdd->device, iwdd);
817
818         address2ident(iwdd->address, ident);
819         connman_device_set_ident(iwdd->device, ident);
820
821         if (connman_device_register(iwdd->device) < 0) {
822                 g_hash_table_remove(devices, path);
823                 return;
824         }
825
826         connman_device_set_powered(iwdd->device, iwdd->powered);
827 }
828
829 static void remove_device_networks(struct iwd_device *iwdd)
830 {
831         GHashTableIter iter;
832         gpointer key, value;
833         struct iwd_network *iwdn;
834         GSList *list, *nets = NULL;
835
836         g_hash_table_iter_init(&iter, networks);
837
838         while (g_hash_table_iter_next(&iter, &key, &value)) {
839                 iwdn = value;
840
841                 if (!strcmp(iwdd->path, iwdn->device))
842                         nets = g_slist_prepend(nets, iwdn);
843         }
844
845         for (list = nets; list; list = list->next) {
846                 iwdn = list->data;
847                 g_hash_table_remove(networks, iwdn->path);
848         }
849
850         g_slist_free(nets);
851 }
852
853 static void remove_device(struct iwd_device *iwdd)
854 {
855         if (!iwdd->device)
856                 return;
857
858         remove_device_networks(iwdd);
859         connman_device_unregister(iwdd->device);
860         connman_device_unref(iwdd->device);
861         iwdd->device = NULL;
862 }
863
864 static void adapter_property_change(GDBusProxy *proxy, const char *name,
865                 DBusMessageIter *iter, void *user_data)
866 {
867         struct iwd_adapter *adapter;
868         const char *path;
869
870         path = g_dbus_proxy_get_path(proxy);
871         adapter = g_hash_table_lookup(adapters, path);
872         if (!adapter)
873                 return;
874
875         if (!strcmp(name, "Powered")) {
876                 dbus_bool_t powered;
877
878                 dbus_message_iter_get_basic(iter, &powered);
879                 adapter->powered = powered;
880
881                 DBG("%p powered %d", path, adapter->powered);
882         }
883 }
884
885 static void device_property_change(GDBusProxy *proxy, const char *name,
886                 DBusMessageIter *iter, void *user_data)
887 {
888         struct iwd_device *iwdd;
889         const char *path;
890
891         path = g_dbus_proxy_get_path(proxy);
892         iwdd = g_hash_table_lookup(devices, path);
893         if (!iwdd)
894                 return;
895
896         if (!strcmp(name, "Name")) {
897                 const char *name;
898
899                 dbus_message_iter_get_basic(iter, &name);
900                 g_free(iwdd->name);
901                 iwdd->name = g_strdup(name);
902
903                 DBG("%p name %s", path, iwdd->name);
904         } else if (!strcmp(name, "Powered")) {
905                 dbus_bool_t powered;
906
907                 dbus_message_iter_get_basic(iter, &powered);
908                 iwdd->powered = powered;
909
910                 DBG("%s powered %d", path, iwdd->powered);
911         } else if (!strcmp(name, "Mode")) {
912                 const char *mode;
913
914                 dbus_message_iter_get_basic(iter, &mode);
915                 g_free(iwdd->mode);
916                 iwdd->mode = g_strdup(mode);
917
918                 DBG("%s mode %s", path, iwdd->mode);
919         }
920 }
921
922 static void network_property_change(GDBusProxy *proxy, const char *name,
923                 DBusMessageIter *iter, void *user_data)
924 {
925         struct iwd_network *iwdn;
926         const char *path;
927
928         path = g_dbus_proxy_get_path(proxy);
929         iwdn = g_hash_table_lookup(networks, path);
930         if (!iwdn)
931                 return;
932
933         if (!strcmp(name, "Connected")) {
934                 dbus_bool_t connected;
935
936                 dbus_message_iter_get_basic(iter, &connected);
937                 iwdn->connected = connected;
938
939                 DBG("%s connected %d", path, iwdn->connected);
940
941                 if (iwdn->connected)
942                         update_network_connected(iwdn);
943                 else
944                         update_network_disconnected(iwdn);
945         }
946 }
947
948 static unsigned char calculate_strength(int strength)
949 {
950         unsigned char res;
951
952         /*
953          * Network's maximum signal strength expressed in 100 * dBm.
954          * The value is the range of 0 (strongest signal) to -10000
955          * (weakest signal)
956          *
957          * ConnMan expects it in the range from 100 (strongest) to 0
958          * (weakest).
959          */
960         res = (unsigned char)((strength + 10000) / 100);
961
962         return res;
963 }
964
965 static void _update_signal_strength(const char *path, int16_t signal_strength)
966 {
967         struct iwd_network *iwdn;
968
969         iwdn = g_hash_table_lookup(networks, path);
970         if (!iwdn)
971                 return;
972
973         connman_network_set_strength(iwdn->network,
974                                         calculate_strength(signal_strength));
975         connman_network_update(iwdn->network);
976 }
977
978 static void ordered_networks_cb(DBusMessage *message, void *user_data)
979 {
980         DBusMessageIter array, entry;
981
982         DBG("");
983
984         if (!dbus_message_iter_init(message, &array))
985                 return;
986
987         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
988                 return;
989
990         dbus_message_iter_recurse(&array, &entry);
991         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRUCT) {
992                 DBusMessageIter value;
993                 const char *path;
994                 int16_t signal_strength;
995
996
997                 dbus_message_iter_recurse(&entry, &value);
998
999                 dbus_message_iter_get_basic(&value, &path);
1000
1001                 dbus_message_iter_next(&value);
1002                 dbus_message_iter_get_basic(&value, &signal_strength);
1003
1004                 _update_signal_strength(path, signal_strength);
1005
1006                 dbus_message_iter_next(&entry);
1007         }
1008 }
1009
1010 static void update_signal_strength(struct iwd_station *iwds)
1011 {
1012         if (!g_dbus_proxy_method_call(iwds->proxy,
1013                                         "GetOrderedNetworks",
1014                                         NULL, ordered_networks_cb,
1015                                         NULL, NULL))
1016                 DBG("GetOrderedNetworks() failed");
1017 }
1018
1019 static void station_property_change(GDBusProxy *proxy, const char *name,
1020                 DBusMessageIter *iter, void *user_data)
1021 {
1022         struct iwd_station *iwds;
1023         const char *path;
1024
1025         path = g_dbus_proxy_get_path(proxy);
1026         iwds = g_hash_table_lookup(stations, path);
1027         if (!iwds)
1028                 return;
1029
1030         if (!strcmp(name, "State")) {
1031                 const char *state;
1032
1033                 dbus_message_iter_get_basic(iter, &state);
1034                 g_free(iwds->state);
1035                 iwds->state = g_strdup(state);
1036
1037                 DBG("%s state %s", path, iwds->state);
1038         } else if (!strcmp(name, "ConnectedNetwork")) {
1039                 const char *connected_network;
1040
1041                 g_free(iwds->connected_network);
1042                 if (iter) {
1043                         dbus_message_iter_get_basic(iter, &connected_network);
1044                         iwds->connected_network = g_strdup(connected_network);
1045                 } else {
1046                         iwds->connected_network = NULL;
1047                 }
1048
1049                 DBG("%s connected_network %s", path, iwds->connected_network);
1050         } else if (!strcmp(name, "Scanning")) {
1051                 dbus_bool_t scanning;
1052
1053                 dbus_message_iter_get_basic(iter, &scanning);
1054                 iwds->scanning = scanning;
1055
1056                 if (!iwds->scanning)
1057                         update_signal_strength(iwds);
1058
1059                 DBG("%s scanning %d", path, iwds->scanning);
1060         }
1061 }
1062
1063 static void ap_property_change(GDBusProxy *proxy, const char *name,
1064                 DBusMessageIter *iter, void *user_data)
1065 {
1066         struct iwd_ap *iwdap;
1067         const char *path;
1068         int err;
1069
1070         path = g_dbus_proxy_get_path(proxy);
1071         iwdap = g_hash_table_lookup(access_points, path);
1072         if (!iwdap)
1073                 return;
1074
1075         if (!strcmp(name, "Started")) {
1076                 dbus_bool_t started;
1077
1078                 dbus_message_iter_get_basic(iter, &started);
1079                 iwdap->started = started;
1080
1081                 DBG("%s started %d", path, iwdap->started);
1082
1083                 if (iwdap->started && iwdap->index != -1) {
1084                         DBG("index %d bridge %s", iwdap->index, iwdap->bridge);
1085                         err = connman_technology_tethering_notify(
1086                                                 iwdap->tech, true);
1087                         if (err)
1088                                 return;
1089                         err = connman_inet_add_to_bridge(
1090                                                 iwdap->index, iwdap->bridge);
1091                 }
1092         }
1093 }
1094
1095 static void adapter_free(gpointer data)
1096 {
1097         struct iwd_adapter *iwda = data;
1098
1099         if (iwda->proxy) {
1100                 g_dbus_proxy_unref(iwda->proxy);
1101                 iwda->proxy = NULL;
1102         }
1103
1104         g_free(iwda->path);
1105         g_free(iwda->vendor);
1106         g_free(iwda->model);
1107         g_free(iwda);
1108 }
1109
1110 static void device_free(gpointer data)
1111 {
1112         struct iwd_device *iwdd = data;
1113
1114         if (iwdd->proxy) {
1115                 g_dbus_proxy_unref(iwdd->proxy);
1116                 iwdd->proxy = NULL;
1117         }
1118
1119         remove_device(iwdd);
1120
1121         g_free(iwdd->path);
1122         g_free(iwdd->adapter);
1123         g_free(iwdd->name);
1124         g_free(iwdd->address);
1125         g_free(iwdd);
1126 }
1127
1128 static void network_free(gpointer data)
1129 {
1130         struct iwd_network *iwdn = data;
1131
1132         if (iwdn->proxy) {
1133                 g_dbus_proxy_unref(iwdn->proxy);
1134                 iwdn->proxy = NULL;
1135         }
1136
1137         remove_network(iwdn);
1138
1139         g_free(iwdn->path);
1140         g_free(iwdn->device);
1141         g_free(iwdn->name);
1142         g_free(iwdn->type);
1143         g_free(iwdn->known_network);
1144         g_free(iwdn);
1145 }
1146
1147 static void known_network_free(gpointer data)
1148 {
1149         struct iwd_known_network *iwdkn = data;
1150
1151         if (iwdkn->proxy) {
1152                 g_dbus_proxy_unref(iwdkn->proxy);
1153                 iwdkn->proxy = NULL;
1154         }
1155
1156         if (iwdkn->auto_connect_id)
1157                 g_source_remove(iwdkn->auto_connect_id);
1158
1159         g_free(iwdkn->path);
1160         g_free(iwdkn->name);
1161         g_free(iwdkn->type);
1162         g_free(iwdkn->last_connected_time);
1163         g_free(iwdkn);
1164 }
1165
1166 static void station_free(gpointer data)
1167 {
1168         struct iwd_station *iwds = data;
1169
1170         if (iwds->proxy) {
1171                 g_dbus_proxy_unref(iwds->proxy);
1172                 iwds->proxy = NULL;
1173         }
1174         g_free(iwds->path);
1175         g_free(iwds->connected_network);
1176         g_free(iwds);
1177 }
1178
1179 static void ap_free(gpointer data)
1180 {
1181         struct iwd_ap *iwdap = data;
1182
1183         if (iwdap->proxy) {
1184                 g_dbus_proxy_unref(iwdap->proxy);
1185                 iwdap->proxy = NULL;
1186         }
1187         g_free(iwdap->bridge);
1188         g_free(iwdap);
1189 }
1190
1191 static void create_adapter(GDBusProxy *proxy)
1192 {
1193         const char *path = g_dbus_proxy_get_path(proxy);
1194         struct iwd_adapter *iwda;
1195         GSList *modes, *list;
1196
1197         iwda = g_try_new0(struct iwd_adapter, 1);
1198
1199         if (!iwda) {
1200                 connman_error("Out of memory creating IWD adapter");
1201                 return;
1202         }
1203
1204         iwda->path = g_strdup(path);
1205         g_hash_table_replace(adapters, iwda->path, iwda);
1206
1207         iwda->proxy = g_dbus_proxy_ref(proxy);
1208
1209         if (!iwda->proxy) {
1210                 connman_error("Cannot create IWD adapter watcher %s", path);
1211                 g_hash_table_remove(adapters, path);
1212                 return;
1213         }
1214
1215         iwda->vendor = g_strdup(proxy_get_string(proxy, "Vendor"));
1216         iwda->model = g_strdup(proxy_get_string(proxy, "Model"));
1217         iwda->powered = proxy_get_bool(proxy, "Powered");
1218
1219         modes = proxy_get_strings(proxy, "SupportedModes");
1220         for (list = modes; list; list = list->next) {
1221                 char *m = list->data;
1222
1223                 if (!m)
1224                         continue;
1225
1226                 if (!strcmp(m, "ad-hoc"))
1227                         iwda->ad_hoc = true;
1228                 else if (!strcmp(m, "station"))
1229                         iwda->station = true;
1230                 else if (!strcmp(m, "ap"))
1231                         iwda->ap = true;
1232         }
1233         g_slist_free_full(modes, g_free);
1234
1235         DBG("%s vendor '%s' model '%s' powered %d ad-hoc %d station %d ap %d",
1236                 path, iwda->vendor, iwda->model, iwda->powered,
1237                 iwda->ad_hoc, iwda->station, iwda->ap);
1238
1239         g_dbus_proxy_set_property_watch(iwda->proxy,
1240                         adapter_property_change, NULL);
1241 }
1242
1243 static void create_device(GDBusProxy *proxy)
1244 {
1245         const char *path = g_dbus_proxy_get_path(proxy);
1246         struct iwd_device *iwdd;
1247
1248         iwdd = g_try_new0(struct iwd_device, 1);
1249
1250         if (!iwdd) {
1251                 connman_error("Out of memory creating IWD device");
1252                 return;
1253         }
1254
1255         iwdd->path = g_strdup(path);
1256         g_hash_table_replace(devices, iwdd->path, iwdd);
1257
1258         iwdd->proxy = g_dbus_proxy_ref(proxy);
1259
1260         if (!iwdd->proxy) {
1261                 connman_error("Cannot create IWD device watcher %s", path);
1262                 g_hash_table_remove(devices, path);
1263                 return;
1264         }
1265
1266         iwdd->adapter = g_strdup(proxy_get_string(proxy, "Adapter"));
1267         iwdd->name = g_strdup(proxy_get_string(proxy, "Name"));
1268         iwdd->address = g_strdup(proxy_get_string(proxy, "Address"));
1269         iwdd->powered = proxy_get_bool(proxy, "Powered");
1270         iwdd->mode = g_strdup(proxy_get_string(proxy, "Mode"));
1271
1272         DBG("adapter %s name %s address %s powered %d mode %s",
1273                 iwdd->adapter, iwdd->name, iwdd->address,
1274                 iwdd->powered, iwdd->mode);
1275
1276         g_dbus_proxy_set_property_watch(iwdd->proxy,
1277                         device_property_change, NULL);
1278
1279         add_device(path, iwdd);
1280 }
1281
1282 static void unregister_agent();
1283
1284 static DBusMessage *agent_release_method(DBusConnection *dbus_conn,
1285                                         DBusMessage *message, void *user_data)
1286 {
1287         unregister_agent();
1288         return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
1289 }
1290
1291 static DBusMessage *get_reply_on_error(DBusMessage *message, int error)
1292 {
1293         return g_dbus_create_error(message,
1294                 IWD_AGENT_ERROR_INTERFACE ".Failed", "Invalid parameters");
1295 }
1296
1297 static DBusMessage *agent_request_passphrase(DBusConnection *dbus_conn,
1298                                                 DBusMessage *message,
1299                                                 void *user_data)
1300 {
1301         struct iwd_network *iwdn;
1302         DBusMessageIter iter;
1303         const char *path, *passwd;
1304
1305         DBG("");
1306
1307         dbus_message_iter_init(message, &iter);
1308
1309         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
1310                 return get_reply_on_error(message, EINVAL);
1311
1312         dbus_message_iter_get_basic(&iter, &path);
1313
1314         iwdn = g_hash_table_lookup(networks, path);
1315         if (!iwdn)
1316                 return get_reply_on_error(message, EINVAL);
1317
1318         passwd = connman_network_get_string(iwdn->network, "WiFi.Passphrase");
1319
1320         return g_dbus_create_reply(message, DBUS_TYPE_STRING, &passwd,
1321                                         DBUS_TYPE_INVALID);
1322 }
1323
1324 static DBusMessage *agent_cancel(DBusConnection *dbus_conn,
1325                                         DBusMessage *message, void *user_data)
1326 {
1327         DBusMessageIter iter;
1328         const char *reason;
1329
1330         dbus_message_iter_init(message, &iter);
1331
1332         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
1333                 return get_reply_on_error(message, EINVAL);
1334
1335         dbus_message_iter_get_basic(&iter, &reason);
1336
1337         DBG("cancel: %s", reason);
1338
1339         /*
1340          * We don't have to do anything here, because we asked the
1341          * user upfront for the passphrase. So
1342          * agent_request_passphrase() will always send a passphrase
1343          * immediately.
1344          */
1345
1346         return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
1347 }
1348
1349 static const GDBusMethodTable agent_methods[] = {
1350         { GDBUS_METHOD("Release", NULL, NULL, agent_release_method) },
1351         { GDBUS_METHOD("RequestPassphrase",
1352                         GDBUS_ARGS({ "path", "o" }),
1353                         GDBUS_ARGS({ "passphrase", "s" }),
1354                         agent_request_passphrase)},
1355         { GDBUS_METHOD("Cancel",
1356                         GDBUS_ARGS({ "reason", "s" }),
1357                         NULL, agent_cancel) },
1358         { },
1359 };
1360
1361 static void agent_register_builder(DBusMessageIter *iter, void *user_data)
1362 {
1363         const char *path = AGENT_PATH;
1364
1365         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
1366                                 &path);
1367 }
1368
1369 static void register_agent(GDBusProxy *proxy)
1370 {
1371         if (!g_dbus_proxy_method_call(proxy,
1372                                         "RegisterAgent",
1373                                         agent_register_builder,
1374                                         NULL, NULL, NULL))
1375                 return;
1376
1377         agent_proxy = g_dbus_proxy_ref(proxy);
1378 }
1379
1380 static void unregister_agent()
1381 {
1382         if (!agent_proxy)
1383                 return;
1384
1385         g_dbus_proxy_method_call(agent_proxy,
1386                                         "UnregisterAgent",
1387                                         agent_register_builder,
1388                                         NULL, NULL, NULL);
1389
1390         g_dbus_proxy_unref(agent_proxy);
1391         agent_proxy = NULL;
1392 }
1393
1394 static void iwd_is_present(DBusConnection *conn, void *user_data)
1395 {
1396         if (agent_registered)
1397                 return;
1398
1399         if (!g_dbus_register_interface(connection, AGENT_PATH,
1400                                         IWD_AGENT_INTERFACE, agent_methods,
1401                                         NULL, NULL, NULL, NULL))
1402                 return;
1403
1404         agent_registered = true;
1405 }
1406
1407 static void iwd_is_out(DBusConnection *conn, void *user_data)
1408 {
1409         if (agent_registered) {
1410                 g_dbus_unregister_interface(connection,
1411                                         AGENT_PATH, IWD_AGENT_INTERFACE);
1412                 agent_registered = false;
1413         }
1414 }
1415
1416 static void create_network(GDBusProxy *proxy)
1417 {
1418         const char *path = g_dbus_proxy_get_path(proxy);
1419         struct iwd_network *iwdn;
1420
1421         iwdn = g_try_new0(struct iwd_network, 1);
1422
1423         if (!iwdn) {
1424                 connman_error("Out of memory creating IWD network");
1425                 return;
1426         }
1427
1428         iwdn->path = g_strdup(path);
1429         g_hash_table_replace(networks, iwdn->path, iwdn);
1430
1431         iwdn->proxy = g_dbus_proxy_ref(proxy);
1432
1433         if (!iwdn->proxy) {
1434                 connman_error("Cannot create IWD network watcher %s", path);
1435                 g_hash_table_remove(networks, path);
1436                 return;
1437         }
1438
1439         iwdn->device = g_strdup(proxy_get_string(proxy, "Device"));
1440         iwdn->name = g_strdup(proxy_get_string(proxy, "Name"));
1441         iwdn->type = g_strdup(proxy_get_string(proxy, "Type"));
1442         iwdn->connected = proxy_get_bool(proxy, "Connected");
1443         iwdn->known_network = g_strdup(proxy_get_string(proxy, "KnownNetwork"));
1444
1445         DBG("device %s name '%s' type %s connected %d known_network %s",
1446                 iwdn->device, iwdn->name, iwdn->type, iwdn->connected,
1447                 iwdn->known_network);
1448
1449         g_dbus_proxy_set_property_watch(iwdn->proxy,
1450                         network_property_change, NULL);
1451
1452         add_network(path, iwdn);
1453 }
1454
1455 struct auto_connect_cb_data {
1456         char *path;
1457         bool auto_connect;
1458 };
1459
1460 static void auto_connect_cb_free(struct auto_connect_cb_data *cbd)
1461 {
1462         g_free(cbd->path);
1463         g_free(cbd);
1464 }
1465
1466 static void auto_connect_cb(const DBusError *error, void *user_data)
1467 {
1468         struct auto_connect_cb_data *cbd = user_data;
1469         struct iwd_known_network *iwdkn;
1470
1471         iwdkn = g_hash_table_lookup(known_networks, cbd->path);
1472         if (!iwdkn)
1473                 goto out;
1474
1475         if (dbus_error_is_set(error))
1476                 connman_warn("WiFi known network %s property auto connect %s",
1477                         cbd->path, error->message);
1478
1479         /* property is updated via watch known_network_property_change() */
1480 out:
1481         auto_connect_cb_free(cbd);
1482 }
1483
1484 static int set_auto_connect(struct iwd_known_network *iwdkn, bool auto_connect)
1485 {
1486         dbus_bool_t dbus_auto_connect = auto_connect;
1487         struct auto_connect_cb_data *cbd;
1488
1489         if (proxy_get_bool(iwdkn->proxy, "AutoConnect") == auto_connect)
1490                 return -EALREADY;
1491
1492         cbd = g_new(struct auto_connect_cb_data, 1);
1493         cbd->path = g_strdup(iwdkn->path);
1494         cbd->auto_connect = auto_connect;
1495
1496         if (!g_dbus_proxy_set_property_basic(iwdkn->proxy, "AutoConnect",
1497                                                 DBUS_TYPE_BOOLEAN,
1498                                                 &dbus_auto_connect,
1499                                                 auto_connect_cb, cbd, NULL)) {
1500                 auto_connect_cb_free(cbd);
1501                 return -EIO;
1502         }
1503
1504         return -EINPROGRESS;
1505 }
1506
1507 static gboolean disable_auto_connect_cb(gpointer data)
1508 {
1509         char *path = data;
1510         struct iwd_known_network *iwdkn;
1511
1512         iwdkn = g_hash_table_lookup(known_networks, path);
1513         if (!iwdkn)
1514                 return FALSE;
1515
1516         if (set_auto_connect(iwdkn, false) != -EINPROGRESS)
1517                 connman_warn("Failed to disable auto connect");
1518
1519         iwdkn->auto_connect_id = 0;
1520         return FALSE;
1521 }
1522
1523 static void disable_auto_connect(struct iwd_known_network *iwdkn)
1524 {
1525         if (iwdkn->auto_connect_id)
1526                 return;
1527
1528         iwdkn->auto_connect_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
1529                                                 0,
1530                                                 disable_auto_connect_cb,
1531                                                 g_strdup(iwdkn->path),
1532                                                 g_free);
1533 }
1534
1535 static void known_network_property_change(GDBusProxy *proxy, const char *name,
1536                 DBusMessageIter *iter, void *user_data)
1537 {
1538         struct iwd_known_network *iwdkn;
1539         const char *path;
1540
1541         path = g_dbus_proxy_get_path(proxy);
1542         iwdkn = g_hash_table_lookup(known_networks, path);
1543         if (!iwdkn)
1544                 return;
1545
1546         if (!strcmp(name, "AutoConnect")) {
1547                 dbus_bool_t auto_connect;
1548
1549                 dbus_message_iter_get_basic(iter, &auto_connect);
1550                 iwdkn->auto_connect = auto_connect;
1551
1552                 DBG("%p auto_connect %d", path, iwdkn->auto_connect);
1553
1554                 if (iwdkn->auto_connect)
1555                         disable_auto_connect(iwdkn);
1556         }
1557 }
1558
1559 static void create_know_network(GDBusProxy *proxy)
1560 {
1561         const char *path = g_dbus_proxy_get_path(proxy);
1562         struct iwd_known_network *iwdkn;
1563
1564         iwdkn = g_try_new0(struct iwd_known_network, 1);
1565         if (!iwdkn) {
1566                 connman_error("Out of memory creating IWD known network");
1567                 return;
1568         }
1569
1570         iwdkn->path = g_strdup(path);
1571         g_hash_table_replace(known_networks, iwdkn->path, iwdkn);
1572
1573         iwdkn->proxy = g_dbus_proxy_ref(proxy);
1574
1575         if (!iwdkn->proxy) {
1576                 connman_error("Cannot create IWD known network watcher %s", path);
1577                 g_hash_table_remove(known_networks, path);
1578                 return;
1579         }
1580
1581         iwdkn->name = g_strdup(proxy_get_string(proxy, "Name"));
1582         iwdkn->type = g_strdup(proxy_get_string(proxy, "Type"));
1583         iwdkn->hidden = proxy_get_bool(proxy, "Hidden");
1584         iwdkn->last_connected_time =
1585                 g_strdup(proxy_get_string(proxy, "LastConnectedTime"));
1586         iwdkn->auto_connect = proxy_get_bool(proxy, "AutoConnect");
1587
1588         DBG("name '%s' type %s hidden %d, last_connection_time %s auto_connect %d",
1589                 iwdkn->name, iwdkn->type, iwdkn->hidden,
1590                 iwdkn->last_connected_time, iwdkn->auto_connect);
1591
1592         g_dbus_proxy_set_property_watch(iwdkn->proxy,
1593                         known_network_property_change, NULL);
1594
1595         if (iwdkn->auto_connect)
1596                 disable_auto_connect(iwdkn);
1597 }
1598
1599 static void create_station(GDBusProxy *proxy)
1600 {
1601         const char *path = g_dbus_proxy_get_path(proxy);
1602         struct iwd_station *iwds;
1603
1604         iwds = g_try_new0(struct iwd_station, 1);
1605         if (!iwds) {
1606                 connman_error("Out of memory creating IWD station");
1607                 return;
1608         }
1609
1610         iwds->path = g_strdup(path);
1611         g_hash_table_replace(stations, iwds->path, iwds);
1612
1613         iwds->proxy = g_dbus_proxy_ref(proxy);
1614
1615         if (!iwds->proxy) {
1616                 connman_error("Cannot create IWD station watcher %s", path);
1617                 g_hash_table_remove(stations, path);
1618                 return;
1619         }
1620
1621         iwds->state = g_strdup(proxy_get_string(proxy, "State"));
1622         iwds->connected_network = g_strdup(proxy_get_string(proxy, "ConnectedNetwork"));
1623         iwds->scanning = proxy_get_bool(proxy, "Scanning");
1624
1625         DBG("state '%s' connected_network %s scanning %d",
1626                 iwds->state, iwds->connected_network, iwds->scanning);
1627
1628         g_dbus_proxy_set_property_watch(iwds->proxy,
1629                         station_property_change, NULL);
1630 }
1631
1632 static void create_ap(GDBusProxy *proxy)
1633 {
1634         const char *path = g_dbus_proxy_get_path(proxy);
1635         struct iwd_ap *iwdap;
1636
1637         iwdap = g_try_new0(struct iwd_ap, 1);
1638         if (!iwdap) {
1639                 connman_error("Out of memory creating IWD access point");
1640                 return;
1641         }
1642         iwdap->index = -1;
1643
1644         iwdap->path = g_strdup(path);
1645         g_hash_table_replace(access_points, iwdap->path, iwdap);
1646
1647         iwdap->proxy = g_dbus_proxy_ref(proxy);
1648
1649         if (!iwdap->proxy) {
1650                 connman_error("Cannot create IWD access point watcher %s", path);
1651                 g_hash_table_remove(access_points, path);
1652                 return;
1653         }
1654
1655         iwdap->started = proxy_get_bool(proxy, "Started");
1656
1657         DBG("started %d", iwdap->started);
1658
1659         g_dbus_proxy_set_property_watch(iwdap->proxy,
1660                         ap_property_change, NULL);
1661 }
1662
1663 static void object_added(GDBusProxy *proxy, void *user_data)
1664 {
1665         const char *interface;
1666
1667         interface = g_dbus_proxy_get_interface(proxy);
1668         if (!interface) {
1669                 connman_warn("Interface or proxy missing when adding "
1670                                                         "iwd object");
1671                 return;
1672         }
1673
1674         DBG("%s %s", interface, g_dbus_proxy_get_path(proxy));
1675
1676         if (!strcmp(interface, IWD_AGENT_MANAGER_INTERFACE))
1677                 register_agent(proxy);
1678         else if (!strcmp(interface, IWD_ADAPTER_INTERFACE))
1679                 create_adapter(proxy);
1680         else if (!strcmp(interface, IWD_DEVICE_INTERFACE))
1681                 create_device(proxy);
1682         else if (!strcmp(interface, IWD_NETWORK_INTERFACE))
1683                 create_network(proxy);
1684         else if (!strcmp(interface, IWD_KNOWN_NETWORK_INTERFACE))
1685                 create_know_network(proxy);
1686         else if (!strcmp(interface, IWD_STATION_INTERFACE))
1687                 create_station(proxy);
1688         else if (!strcmp(interface, IWD_AP_INTERFACE))
1689                 create_ap(proxy);
1690 }
1691
1692 static void object_removed(GDBusProxy *proxy, void *user_data)
1693 {
1694         const char *interface, *path;
1695
1696         interface = g_dbus_proxy_get_interface(proxy);
1697         if (!interface) {
1698                 connman_warn("Interface or proxy missing when removing "
1699                                                         "iwd object");
1700                 return;
1701         }
1702
1703         path = g_dbus_proxy_get_path(proxy);
1704         DBG("%s %s", interface, path);
1705
1706         if (!strcmp(interface, IWD_AGENT_MANAGER_INTERFACE))
1707                 unregister_agent();
1708         if (!strcmp(interface, IWD_ADAPTER_INTERFACE))
1709                 g_hash_table_remove(adapters, path);
1710         else if (!strcmp(interface, IWD_DEVICE_INTERFACE))
1711                 g_hash_table_remove(devices, path);
1712         else if (!strcmp(interface, IWD_NETWORK_INTERFACE))
1713                 g_hash_table_remove(networks, path);
1714         else if (!strcmp(interface, IWD_KNOWN_NETWORK_INTERFACE))
1715                 g_hash_table_remove(known_networks, path);
1716         else if (!strcmp(interface, IWD_STATION_INTERFACE))
1717                 g_hash_table_remove(stations, path);
1718         else if (!strcmp(interface, IWD_AP_INTERFACE))
1719                 g_hash_table_remove(access_points, path);
1720 }
1721
1722 static int iwd_init(void)
1723 {
1724         connection = connman_dbus_get_connection();
1725         if (!connection)
1726                 goto out;
1727
1728         adapters = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1729                         adapter_free);
1730
1731         devices = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1732                         device_free);
1733
1734         networks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1735                         network_free);
1736
1737         known_networks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1738                         known_network_free);
1739
1740         stations = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1741                         station_free);
1742
1743         access_points = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1744                         ap_free);
1745
1746         if (connman_technology_driver_register(&tech_driver) < 0) {
1747                 connman_warn("Failed to initialize technology for IWD");
1748                 goto out;
1749         }
1750
1751         if (connman_device_driver_register(&device_driver) < 0) {
1752                 connman_warn("Failed to initialize device driver for "
1753                                 IWD_SERVICE);
1754                 connman_technology_driver_unregister(&tech_driver);
1755                 goto out;
1756         }
1757
1758         if (connman_network_driver_register(&network_driver) < 0) {
1759                 connman_technology_driver_unregister(&tech_driver);
1760                 connman_device_driver_unregister(&device_driver);
1761                 goto out;
1762         }
1763
1764         client = g_dbus_client_new(connection, IWD_SERVICE, IWD_PATH);
1765         if (!client) {
1766                 connman_warn("Failed to initialize D-Bus client for "
1767                                 IWD_SERVICE);
1768                 goto out;
1769         }
1770
1771         g_dbus_client_set_connect_watch(client, iwd_is_present, NULL);
1772         g_dbus_client_set_disconnect_watch(client, iwd_is_out, NULL);
1773         g_dbus_client_set_proxy_handlers(client, object_added, object_removed,
1774                         NULL, NULL);
1775
1776         return 0;
1777
1778 out:
1779         if (devices)
1780                 g_hash_table_destroy(devices);
1781
1782         if (networks)
1783                 g_hash_table_destroy(networks);
1784
1785         if (known_networks)
1786                 g_hash_table_destroy(known_networks);
1787
1788         if (stations)
1789                 g_hash_table_destroy(stations);
1790
1791         if (access_points)
1792                 g_hash_table_destroy(access_points);
1793
1794         if (adapters)
1795                 g_hash_table_destroy(adapters);
1796
1797         if (connection)
1798                 dbus_connection_unref(connection);
1799
1800         return -EIO;
1801 }
1802
1803 static void iwd_exit(void)
1804 {
1805         connman_network_driver_unregister(&network_driver);
1806         connman_device_driver_unregister(&device_driver);
1807         connman_technology_driver_unregister(&tech_driver);
1808
1809         g_dbus_client_unref(client);
1810
1811         g_hash_table_destroy(access_points);
1812         g_hash_table_destroy(stations);
1813         g_hash_table_destroy(known_networks);
1814         g_hash_table_destroy(networks);
1815         g_hash_table_destroy(devices);
1816         g_hash_table_destroy(adapters);
1817
1818         dbus_connection_unref(connection);
1819 }
1820
1821 CONNMAN_PLUGIN_DEFINE(iwd, "IWD plugin", VERSION,
1822                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, iwd_init, iwd_exit)