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