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