For Linux and FreeBSD enable background scanning of 60 seconds
[dhcpcd-ui] / src / dhcpcd-qt / dhcpcd-wi.cpp
1 /*
2  * dhcpcd-qt
3  * Copyright 2014 Roy Marples <roy@marples.name>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <QAction>
28 #include <QObject>
29 #include <QInputDialog>
30 #include <QLineEdit>
31 #include <QMenu>
32 #include <QMessageBox>
33 #include <QSocketNotifier>
34 #include <QTimer>
35 #include <QWidgetAction>
36
37 #include <cerrno>
38
39 #include "config.h"
40 #include "dhcpcd-wi.h"
41 #include "dhcpcd-qt.h"
42 #include "dhcpcd-ifmenu.h"
43 #include "dhcpcd-ssidmenu.h"
44
45 DhcpcdWi::DhcpcdWi(DhcpcdQt *parent, DHCPCD_WPA *wpa)
46 {
47
48         this->dhcpcdQt = parent;
49         this->wpa = wpa;
50         menu = NULL;
51         scans = NULL;
52
53         notifier = NULL;
54         pingTimer = NULL;
55 #ifdef BG_SCAN
56         scanTimer = NULL;
57 #endif
58 }
59
60 DhcpcdWi::~DhcpcdWi()
61 {
62
63         if (menu) {
64                 dhcpcdQt->menuDeleted(menu);
65                 menu->setVisible(false);
66                 menu->deleteLater();
67                 menu = NULL;
68         }
69
70         if (notifier) {
71                 notifier->setEnabled(false);
72                 notifier->deleteLater();
73                 notifier = NULL;
74         }
75
76         if (pingTimer) {
77                 pingTimer->stop();
78                 pingTimer->deleteLater();
79                 pingTimer = NULL;
80         }
81
82 #ifdef BG_SCAN
83         if (scanTimer) {
84                 scanTimer->stop();
85                 scanTimer->deleteLater();
86                 scanTimer = NULL;
87         }
88 #endif
89
90         dhcpcd_wi_scans_free(scans);
91 }
92
93 DHCPCD_WPA *DhcpcdWi::getWpa()
94 {
95
96         return wpa;
97 }
98
99 DHCPCD_WI_SCAN *DhcpcdWi::getScans()
100 {
101
102         return scans;
103 }
104
105 bool DhcpcdWi::setScans(DHCPCD_WI_SCAN *scans)
106 {
107         bool changed = false;
108
109         if (menu) {
110                 QList<DhcpcdSsidMenu*> lst;
111                 DHCPCD_WI_SCAN *scan;
112                 DHCPCD_IF *i;
113                 bool found, associated;
114                 QAction *before;
115
116                 i = dhcpcd_wpa_if(wpa);
117                 for (scan = scans; scan; scan = scan->next) {
118                         found = false;
119                         before = NULL;
120                         associated = dhcpcd_wi_associated(i, scan);
121
122                         lst = menu->findChildren<DhcpcdSsidMenu*>();
123                         foreach(DhcpcdSsidMenu *sm, lst) {
124                                 DHCPCD_WI_SCAN *s = sm->getScan();
125
126                                 if (strcmp(scan->ssid, s->ssid) == 0) {
127                                         /* If association changes, remove
128                                          * the entry and re-create it
129                                          * so assoicates entries appear at
130                                          * the top */
131                                         if (associated != sm->isAssociated()) {
132                                                 menu->removeAction(sm);
133                                                 break;
134                                         }
135                                         sm->setScan(scan);
136                                         found = true;
137                                         break;
138                                 }
139                                 if (!associated &&
140                                     dhcpcd_wi_scan_compare(scan, s) < 0)
141                                         before = sm;
142                         }
143
144                         if (!found) {
145                                 if (associated) {
146                                         lst = menu->findChildren<DhcpcdSsidMenu*>();
147                                         if (lst.empty())
148                                                 before = NULL;
149                                         else
150                                                 before = lst.at(0);
151                                 }
152                                 createMenuItem(menu, scan, before);
153                                 changed = true;
154                         }
155                 }
156
157                 lst = menu->findChildren<DhcpcdSsidMenu*>();
158                 foreach(DhcpcdSsidMenu *sm, lst) {
159                         DHCPCD_WI_SCAN *s = sm->getScan();
160                         for (scan = scans; scan; scan = scan->next) {
161                                 if (strcmp(scan->ssid, s->ssid) == 0)
162                                         break;
163                         }
164                         if (scan == NULL) {
165                                 menu->removeAction(sm);
166                                 changed = true;
167                         }
168                 }
169         }
170
171         dhcpcd_wi_scans_free(this->scans);
172         this->scans = scans;
173
174         return changed;
175 }
176
177 void DhcpcdWi::createMenuItem(QMenu *menu, DHCPCD_WI_SCAN *scan,
178     QAction *before)
179 {
180         DhcpcdSsidMenu *ssidMenu = new DhcpcdSsidMenu(menu, this, scan);
181         menu->insertAction(before, ssidMenu);
182         connect(ssidMenu, SIGNAL(triggered(DHCPCD_WI_SCAN *)),
183             this, SLOT(connectSsid(DHCPCD_WI_SCAN *)));
184 }
185
186 void DhcpcdWi::createMenu1(QMenu *menu)
187 {
188         DHCPCD_IF *i;
189         DHCPCD_WI_SCAN *scan;
190         QAction *before;
191
192 #ifdef BG_SCAN
193         connect(menu, SIGNAL(aboutToShow()), this, SLOT(menuShown()));
194         connect(menu, SIGNAL(aboutToHide()), this, SLOT(menuHidden()));
195 #endif
196
197         i = dhcpcd_wpa_if(wpa);
198         for (scan = scans; scan; scan = scan->next) {
199                 before = NULL;
200                 if (dhcpcd_wi_associated(i, scan)) {
201                         QList<DhcpcdSsidMenu*> lst;
202
203                         lst = menu->findChildren<DhcpcdSsidMenu*>();
204                         if (!lst.empty())
205                                 before = lst.at(0);
206                 }
207                 createMenuItem(menu, scan, before);
208         }
209 }
210
211 void DhcpcdWi::createMenu(QMenu *menu)
212 {
213
214         if (this->menu && this->menu != menu)
215                 this->menu->deleteLater();
216         this->menu = menu;
217         createMenu1(menu);
218 }
219
220 QMenu *DhcpcdWi::createIfMenu(QMenu *parent)
221 {
222         DHCPCD_IF *ifp;
223         QIcon icon;
224
225         ifp = dhcpcd_wpa_if(wpa);
226         if (this->menu)
227                 this->menu->deleteLater();
228         menu = new DhcpcdIfMenu(ifp, parent);
229         icon = DhcpcdQt::getIcon("devices", "network-wireless");
230         menu->setIcon(icon);
231         createMenu1(menu);
232         return menu;
233 }
234
235 bool DhcpcdWi::open()
236 {
237         int fd = dhcpcd_wpa_open(wpa);
238
239         if (fd == -1) {
240                 qCritical("%s: dhcpcd_wpa_open: %s",
241                     dhcpcd_wpa_if(wpa)->ifname,
242                     strerror(errno));
243                 dhcpcd_wpa_close(wpa);
244                 return false;
245         }
246
247         notifier = new QSocketNotifier(fd, QSocketNotifier::Read);
248         connect(notifier, SIGNAL(activated(int)), this, SLOT(dispatch()));
249         pingTimer = new QTimer(this);
250         connect(pingTimer, SIGNAL(timeout()), this, SLOT(ping()));
251         pingTimer->start(DHCPCD_WPA_PING);
252 #ifdef BG_SCAN
253         scanTimer = new QTimer(this);
254         connect(scanTimer, SIGNAL(timeout()), this, SLOT(scan()));
255         scanTimer->start(DHCPCD_WPA_SCAN_LONG);
256 #endif
257         return true;
258 }
259
260 void DhcpcdWi::dispatch()
261 {
262
263         dhcpcd_wpa_dispatch(wpa);
264 }
265
266 void DhcpcdWi::ping()
267 {
268
269         if (!dhcpcd_wpa_ping(wpa))
270                 dhcpcd_wpa_close(wpa);
271 }
272
273 #ifdef BG_SCAN
274 void DhcpcdWi::scan()
275 {
276
277         dhcpcd_wpa_scan(wpa);
278 }
279
280 void DhcpcdWi::menuHidden()
281 {
282
283         if (scanTimer) {
284                 scanTimer->stop();
285                 scanTimer->start(DHCPCD_WPA_SCAN_LONG);
286         }
287 }
288
289 void DhcpcdWi::menuShown()
290 {
291
292         if (scanTimer) {
293                 scanTimer->stop();
294                 scanTimer->start(DHCPCD_WPA_SCAN_SHORT);
295         }
296 }
297 #endif
298
299 void DhcpcdWi::connectSsid(DHCPCD_WI_SCAN *scan)
300 {
301         DHCPCD_WI_SCAN s;
302         int err;
303
304         /* Take a copy of scan incase it's destroyed by a scan update */
305         memcpy(&s, scan, sizeof(s));
306         s.next = NULL;
307
308         if (s.flags & WSF_PSK) {
309                 bool ok;
310                 QString pwd = QInputDialog::getText(dhcpcdQt, s.ssid,
311                     tr("Pre Shared key"), QLineEdit::Normal, NULL, &ok);
312                 if (!ok)
313                         return;
314                 if (pwd.isNull() || pwd.isEmpty())
315                         err = dhcpcd_wpa_select(wpa, &s);
316                 else
317                         err = dhcpcd_wpa_configure(wpa, &s, pwd.toAscii());
318         } else
319                 err = dhcpcd_wpa_configure(wpa, &s, NULL);
320
321         QString errt;
322
323         switch (err) {
324         case DHCPCD_WPA_SUCCESS:
325                 return;
326         case DHCPCD_WPA_ERR_DISCONN:
327                 errt = tr("Failed to disconnect.");
328                 break;
329         case DHCPCD_WPA_ERR_RECONF:
330                 errt = tr("Faile to reconfigure.");
331                 break;
332         case DHCPCD_WPA_ERR_SET:
333                 errt = tr("Failed to set key management.");
334                 break;
335         case DHCPCD_WPA_ERR_SET_PSK:
336                 errt = tr("Failed to set password, probably too short.");
337                 break;
338         case DHCPCD_WPA_ERR_ENABLE:
339                 errt = tr("Failed to enable the network.");
340                 break;
341         case DHCPCD_WPA_ERR_SELECT:
342                 errt = tr("Failed to select the network.");
343                 break;
344         case DHCPCD_WPA_ERR_ASSOC:
345                 errt = tr("Failed to start association.");
346                 break;
347         case DHCPCD_WPA_ERR_WRITE:
348                 errt = tr("Failed to save wpa_supplicant configuration.\n\nYou should add update_config=1 to /etc/wpa_supplicant.conf.");
349                 break;
350         default:
351                 errt = strerror(errno);
352                 break;
353         }
354
355         QMessageBox::critical(dhcpcdQt, tr("Error setting wireless properties"),
356             errt);
357 }