Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / wpa_supplicant / wpa_gui-qt4 / wpagui.cpp
1 /*
2  * wpa_gui - WpaGui class
3  * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8
9 #ifdef CONFIG_NATIVE_WINDOWS
10 #include <windows.h>
11 #endif /* CONFIG_NATIVE_WINDOWS */
12
13 #include <cstdio>
14 #include <unistd.h>
15 #include <QMessageBox>
16 #include <QCloseEvent>
17 #include <QImageReader>
18 #include <QSettings>
19
20 #include "wpagui.h"
21 #include "dirent.h"
22 #include "common/wpa_ctrl.h"
23 #include "userdatarequest.h"
24 #include "networkconfig.h"
25
26
27 #ifndef QT_NO_DEBUG
28 #define debug(M, ...) qDebug("DEBUG %d: " M, __LINE__, ##__VA_ARGS__)
29 #else
30 #define debug(M, ...) do {} while (0)
31 #endif
32
33
34 WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *,
35                Qt::WindowFlags)
36         : QMainWindow(parent), app(_app)
37 {
38         setupUi(this);
39         this->setWindowFlags(Qt::Dialog);
40
41 #ifdef CONFIG_NATIVE_WINDOWS
42         fileStopServiceAction = new QAction(this);
43         fileStopServiceAction->setObjectName("Stop Service");
44         fileStopServiceAction->setIconText(tr("Stop Service"));
45         fileMenu->insertAction(actionWPS, fileStopServiceAction);
46
47         fileStartServiceAction = new QAction(this);
48         fileStartServiceAction->setObjectName("Start Service");
49         fileStartServiceAction->setIconText(tr("Start Service"));
50         fileMenu->insertAction(fileStopServiceAction, fileStartServiceAction);
51
52         connect(fileStartServiceAction, SIGNAL(triggered()), this,
53                 SLOT(startService()));
54         connect(fileStopServiceAction, SIGNAL(triggered()), this,
55                 SLOT(stopService()));
56
57         addInterfaceAction = new QAction(this);
58         addInterfaceAction->setIconText(tr("Add Interface"));
59         fileMenu->insertAction(fileStartServiceAction, addInterfaceAction);
60
61         connect(addInterfaceAction, SIGNAL(triggered()), this,
62                 SLOT(addInterface()));
63 #endif /* CONFIG_NATIVE_WINDOWS */
64
65         (void) statusBar();
66
67         /*
68          * Disable WPS tab by default; it will be enabled if wpa_supplicant is
69          * built with WPS support.
70          */
71         wpsTab->setEnabled(false);
72         wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), false);
73
74         connect(fileEventHistoryAction, SIGNAL(triggered()), this,
75                 SLOT(eventHistory()));
76         connect(fileSaveConfigAction, SIGNAL(triggered()), this,
77                 SLOT(saveConfig()));
78         connect(actionWPS, SIGNAL(triggered()), this, SLOT(wpsDialog()));
79         connect(actionPeers, SIGNAL(triggered()), this, SLOT(peersDialog()));
80         connect(fileExitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
81         connect(networkAddAction, SIGNAL(triggered()), this,
82                 SLOT(addNetwork()));
83         connect(networkEditAction, SIGNAL(triggered()), this,
84                 SLOT(editSelectedNetwork()));
85         connect(networkRemoveAction, SIGNAL(triggered()), this,
86                 SLOT(removeSelectedNetwork()));
87         connect(networkEnableAllAction, SIGNAL(triggered()), this,
88                 SLOT(enableAllNetworks()));
89         connect(networkDisableAllAction, SIGNAL(triggered()), this,
90                 SLOT(disableAllNetworks()));
91         connect(networkRemoveAllAction, SIGNAL(triggered()), this,
92                 SLOT(removeAllNetworks()));
93         connect(helpIndexAction, SIGNAL(triggered()), this, SLOT(helpIndex()));
94         connect(helpContentsAction, SIGNAL(triggered()), this,
95                 SLOT(helpContents()));
96         connect(helpAboutAction, SIGNAL(triggered()), this, SLOT(helpAbout()));
97         connect(disconnectButton, SIGNAL(clicked()), this, SLOT(disconnect()));
98         connect(scanButton, SIGNAL(clicked()), this, SLOT(scan()));
99         connect(connectButton, SIGNAL(clicked()), this, SLOT(connectB()));
100         connect(adapterSelect, SIGNAL(activated(const QString&)), this,
101                 SLOT(selectAdapter(const QString&)));
102         connect(networkSelect, SIGNAL(activated(const QString&)), this,
103                 SLOT(selectNetwork(const QString&)));
104         connect(addNetworkButton, SIGNAL(clicked()), this, SLOT(addNetwork()));
105         connect(editNetworkButton, SIGNAL(clicked()), this,
106                 SLOT(editListedNetwork()));
107         connect(removeNetworkButton, SIGNAL(clicked()), this,
108                 SLOT(removeListedNetwork()));
109         connect(networkList, SIGNAL(itemSelectionChanged()), this,
110                 SLOT(updateNetworkDisabledStatus()));
111         connect(enableRadioButton, SIGNAL(toggled(bool)), this,
112                 SLOT(enableListedNetwork(bool)));
113         connect(disableRadioButton, SIGNAL(toggled(bool)), this,
114                 SLOT(disableListedNetwork(bool)));
115         connect(scanNetworkButton, SIGNAL(clicked()), this, SLOT(scan()));
116         connect(networkList, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
117                 this, SLOT(editListedNetwork()));
118         connect(wpaguiTab, SIGNAL(currentChanged(int)), this,
119                 SLOT(tabChanged(int)));
120         connect(wpsPbcButton, SIGNAL(clicked()), this, SLOT(wpsPbc()));
121         connect(wpsPinButton, SIGNAL(clicked()), this, SLOT(wpsGeneratePin()));
122         connect(wpsApPinEdit, SIGNAL(textChanged(const QString &)), this,
123                 SLOT(wpsApPinChanged(const QString &)));
124         connect(wpsApPinButton, SIGNAL(clicked()), this, SLOT(wpsApPin()));
125
126         eh = NULL;
127         scanres = NULL;
128         peers = NULL;
129         add_iface = NULL;
130         udr = NULL;
131         tray_icon = NULL;
132         startInTray = false;
133         quietMode = false;
134         ctrl_iface = NULL;
135         ctrl_conn = NULL;
136         monitor_conn = NULL;
137         msgNotifier = NULL;
138         ctrl_iface_dir = strdup("/var/run/wpa_supplicant");
139         signalMeterInterval = 0;
140
141         parse_argv();
142
143 #ifndef QT_NO_SESSIONMANAGER
144         if (app->isSessionRestored()) {
145                 QSettings settings("wpa_supplicant", "wpa_gui");
146                 settings.beginGroup("state");
147                 if (app->sessionId().compare(settings.value("session_id").
148                                              toString()) == 0)
149                         startInTray = settings.value("in_tray").toBool();
150                 settings.endGroup();
151         }
152 #endif
153
154         if (QSystemTrayIcon::isSystemTrayAvailable())
155                 createTrayIcon(startInTray);
156         else
157                 show();
158
159         connectedToService = false;
160         textStatus->setText(tr("connecting to wpa_supplicant"));
161         timer = new QTimer(this);
162         connect(timer, SIGNAL(timeout()), SLOT(ping()));
163         timer->setSingleShot(false);
164         timer->start(1000);
165
166         signalMeterTimer = new QTimer(this);
167         signalMeterTimer->setInterval(signalMeterInterval);
168         connect(signalMeterTimer, SIGNAL(timeout()), SLOT(signalMeterUpdate()));
169
170         if (openCtrlConnection(ctrl_iface) < 0) {
171                 debug("Failed to open control connection to "
172                       "wpa_supplicant.");
173         }
174
175         updateStatus();
176         networkMayHaveChanged = true;
177         updateNetworks();
178 }
179
180
181 WpaGui::~WpaGui()
182 {
183         delete msgNotifier;
184
185         if (monitor_conn) {
186                 wpa_ctrl_detach(monitor_conn);
187                 wpa_ctrl_close(monitor_conn);
188                 monitor_conn = NULL;
189         }
190         if (ctrl_conn) {
191                 wpa_ctrl_close(ctrl_conn);
192                 ctrl_conn = NULL;
193         }
194
195         if (eh) {
196                 eh->close();
197                 delete eh;
198                 eh = NULL;
199         }
200
201         if (scanres) {
202                 scanres->close();
203                 delete scanres;
204                 scanres = NULL;
205         }
206
207         if (peers) {
208                 peers->close();
209                 delete peers;
210                 peers = NULL;
211         }
212
213         if (add_iface) {
214                 add_iface->close();
215                 delete add_iface;
216                 add_iface = NULL;
217         }
218
219         if (udr) {
220                 udr->close();
221                 delete udr;
222                 udr = NULL;
223         }
224
225         free(ctrl_iface);
226         ctrl_iface = NULL;
227
228         free(ctrl_iface_dir);
229         ctrl_iface_dir = NULL;
230 }
231
232
233 void WpaGui::languageChange()
234 {
235         retranslateUi(this);
236 }
237
238
239 void WpaGui::parse_argv()
240 {
241         int c;
242         WpaGuiApp *app = qobject_cast<WpaGuiApp*>(qApp);
243         for (;;) {
244                 c = getopt(app->argc, app->argv, "i:m:p:tq");
245                 if (c < 0)
246                         break;
247                 switch (c) {
248                 case 'i':
249                         free(ctrl_iface);
250                         ctrl_iface = strdup(optarg);
251                         break;
252                 case 'm':
253                         signalMeterInterval = atoi(optarg) * 1000;
254                         break;
255                 case 'p':
256                         free(ctrl_iface_dir);
257                         ctrl_iface_dir = strdup(optarg);
258                         break;
259                 case 't':
260                         startInTray = true;
261                         break;
262                 case 'q':
263                         quietMode = true;
264                         break;
265                 }
266         }
267 }
268
269
270 int WpaGui::openCtrlConnection(const char *ifname)
271 {
272         char *cfile;
273         int flen;
274         char buf[2048], *pos, *pos2;
275         size_t len;
276
277         if (ifname) {
278                 if (ifname != ctrl_iface) {
279                         free(ctrl_iface);
280                         ctrl_iface = strdup(ifname);
281                 }
282         } else {
283 #ifdef CONFIG_CTRL_IFACE_UDP
284                 free(ctrl_iface);
285                 ctrl_iface = strdup("udp");
286 #endif /* CONFIG_CTRL_IFACE_UDP */
287 #ifdef CONFIG_CTRL_IFACE_UNIX
288                 struct dirent *dent;
289                 DIR *dir = opendir(ctrl_iface_dir);
290                 free(ctrl_iface);
291                 ctrl_iface = NULL;
292                 if (dir) {
293                         while ((dent = readdir(dir))) {
294 #ifdef _DIRENT_HAVE_D_TYPE
295                                 /* Skip the file if it is not a socket.
296                                  * Also accept DT_UNKNOWN (0) in case
297                                  * the C library or underlying file
298                                  * system does not support d_type. */
299                                 if (dent->d_type != DT_SOCK &&
300                                     dent->d_type != DT_UNKNOWN)
301                                         continue;
302 #endif /* _DIRENT_HAVE_D_TYPE */
303
304                                 if (strcmp(dent->d_name, ".") == 0 ||
305                                     strcmp(dent->d_name, "..") == 0)
306                                         continue;
307                                 debug("Selected interface '%s'",
308                                       dent->d_name);
309                                 ctrl_iface = strdup(dent->d_name);
310                                 break;
311                         }
312                         closedir(dir);
313                 }
314 #endif /* CONFIG_CTRL_IFACE_UNIX */
315 #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
316                 struct wpa_ctrl *ctrl;
317                 int ret;
318
319                 free(ctrl_iface);
320                 ctrl_iface = NULL;
321
322                 ctrl = wpa_ctrl_open(NULL);
323                 if (ctrl) {
324                         len = sizeof(buf) - 1;
325                         ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf,
326                                                &len, NULL);
327                         if (ret >= 0) {
328                                 connectedToService = true;
329                                 buf[len] = '\0';
330                                 pos = strchr(buf, '\n');
331                                 if (pos)
332                                         *pos = '\0';
333                                 ctrl_iface = strdup(buf);
334                         }
335                         wpa_ctrl_close(ctrl);
336                 }
337 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
338         }
339
340         if (ctrl_iface == NULL) {
341 #ifdef CONFIG_NATIVE_WINDOWS
342                 static bool first = true;
343                 if (first && !serviceRunning()) {
344                         first = false;
345                         if (QMessageBox::warning(
346                                     this, qAppName(),
347                                     tr("wpa_supplicant service is not "
348                                        "running.\n"
349                                        "Do you want to start it?"),
350                                     QMessageBox::Yes | QMessageBox::No) ==
351                             QMessageBox::Yes)
352                                 startService();
353                 }
354 #endif /* CONFIG_NATIVE_WINDOWS */
355                 return -1;
356         }
357
358 #ifdef CONFIG_CTRL_IFACE_UNIX
359         flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2;
360         cfile = (char *) malloc(flen);
361         if (cfile == NULL)
362                 return -1;
363         snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface);
364 #else /* CONFIG_CTRL_IFACE_UNIX */
365         flen = strlen(ctrl_iface) + 1;
366         cfile = (char *) malloc(flen);
367         if (cfile == NULL)
368                 return -1;
369         snprintf(cfile, flen, "%s", ctrl_iface);
370 #endif /* CONFIG_CTRL_IFACE_UNIX */
371
372         if (ctrl_conn) {
373                 wpa_ctrl_close(ctrl_conn);
374                 ctrl_conn = NULL;
375         }
376
377         if (monitor_conn) {
378                 delete msgNotifier;
379                 msgNotifier = NULL;
380                 wpa_ctrl_detach(monitor_conn);
381                 wpa_ctrl_close(monitor_conn);
382                 monitor_conn = NULL;
383         }
384
385         debug("Trying to connect to '%s'", cfile);
386         ctrl_conn = wpa_ctrl_open(cfile);
387         if (ctrl_conn == NULL) {
388                 free(cfile);
389                 return -1;
390         }
391         monitor_conn = wpa_ctrl_open(cfile);
392         free(cfile);
393         if (monitor_conn == NULL) {
394                 wpa_ctrl_close(ctrl_conn);
395                 return -1;
396         }
397         if (wpa_ctrl_attach(monitor_conn)) {
398                 debug("Failed to attach to wpa_supplicant");
399                 wpa_ctrl_close(monitor_conn);
400                 monitor_conn = NULL;
401                 wpa_ctrl_close(ctrl_conn);
402                 ctrl_conn = NULL;
403                 return -1;
404         }
405
406 #if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
407         msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn),
408                                           QSocketNotifier::Read, this);
409         connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs()));
410 #endif
411
412         adapterSelect->clear();
413         adapterSelect->addItem(ctrl_iface);
414         adapterSelect->setCurrentIndex(0);
415
416         len = sizeof(buf) - 1;
417         if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >=
418             0) {
419                 buf[len] = '\0';
420                 pos = buf;
421                 while (*pos) {
422                         pos2 = strchr(pos, '\n');
423                         if (pos2)
424                                 *pos2 = '\0';
425                         if (strcmp(pos, ctrl_iface) != 0)
426                                 adapterSelect->addItem(pos);
427                         if (pos2)
428                                 pos = pos2 + 1;
429                         else
430                                 break;
431                 }
432         }
433
434         len = sizeof(buf) - 1;
435         if (wpa_ctrl_request(ctrl_conn, "GET_CAPABILITY eap", 18, buf, &len,
436                              NULL) >= 0) {
437                 buf[len] = '\0';
438
439                 QString res(buf);
440                 QStringList types = res.split(QChar(' '));
441                 bool wps = types.contains("WSC");
442                 actionWPS->setEnabled(wps);
443                 wpsTab->setEnabled(wps);
444                 wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), wps);
445         }
446
447         return 0;
448 }
449
450
451 int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen)
452 {
453         int ret;
454
455         if (ctrl_conn == NULL)
456                 return -3;
457         ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen, NULL);
458         if (ret == -2)
459                 debug("'%s' command timed out.", cmd);
460         else if (ret < 0)
461                 debug("'%s' command failed.", cmd);
462
463         return ret;
464 }
465
466
467 QString WpaGui::wpaStateTranslate(char *state)
468 {
469         if (!strcmp(state, "DISCONNECTED"))
470                 return tr("Disconnected");
471         else if (!strcmp(state, "INACTIVE"))
472                 return tr("Inactive");
473         else if (!strcmp(state, "SCANNING"))
474                 return tr("Scanning");
475         else if (!strcmp(state, "AUTHENTICATING"))
476                 return tr("Authenticating");
477         else if (!strcmp(state, "ASSOCIATING"))
478                 return tr("Associating");
479         else if (!strcmp(state, "ASSOCIATED"))
480                 return tr("Associated");
481         else if (!strcmp(state, "4WAY_HANDSHAKE"))
482                 return tr("4-Way Handshake");
483         else if (!strcmp(state, "GROUP_HANDSHAKE"))
484                 return tr("Group Handshake");
485         else if (!strcmp(state, "COMPLETED"))
486                 return tr("Completed");
487         else
488                 return tr("Unknown");
489 }
490
491
492 void WpaGui::updateStatus()
493 {
494         char buf[2048], *start, *end, *pos;
495         size_t len;
496
497         pingsToStatusUpdate = 10;
498
499         len = sizeof(buf) - 1;
500         if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) {
501                 textStatus->setText(tr("Could not get status from "
502                                        "wpa_supplicant"));
503                 textAuthentication->clear();
504                 textEncryption->clear();
505                 textSsid->clear();
506                 textBssid->clear();
507                 textIpAddress->clear();
508                 updateTrayToolTip(tr("no status information"));
509                 updateTrayIcon(TrayIconOffline);
510                 signalMeterTimer->stop();
511
512 #ifdef CONFIG_NATIVE_WINDOWS
513                 static bool first = true;
514                 if (first && connectedToService &&
515                     (ctrl_iface == NULL || *ctrl_iface == '\0')) {
516                         first = false;
517                         if (QMessageBox::information(
518                                     this, qAppName(),
519                                     tr("No network interfaces in use.\n"
520                                        "Would you like to add one?"),
521                                     QMessageBox::Yes | QMessageBox::No) ==
522                             QMessageBox::Yes)
523                                 addInterface();
524                 }
525 #endif /* CONFIG_NATIVE_WINDOWS */
526                 return;
527         }
528
529         buf[len] = '\0';
530
531         bool auth_updated = false, ssid_updated = false;
532         bool bssid_updated = false, ipaddr_updated = false;
533         bool status_updated = false;
534         char *pairwise_cipher = NULL, *group_cipher = NULL;
535         char *mode = NULL;
536
537         start = buf;
538         while (*start) {
539                 bool last = false;
540                 end = strchr(start, '\n');
541                 if (end == NULL) {
542                         last = true;
543                         end = start;
544                         while (end[0] && end[1])
545                                 end++;
546                 }
547                 *end = '\0';
548
549                 pos = strchr(start, '=');
550                 if (pos) {
551                         *pos++ = '\0';
552                         if (strcmp(start, "bssid") == 0) {
553                                 bssid_updated = true;
554                                 textBssid->setText(pos);
555                         } else if (strcmp(start, "ssid") == 0) {
556                                 ssid_updated = true;
557                                 textSsid->setText(pos);
558                                 updateTrayToolTip(pos + tr(" (associated)"));
559                                 if (!signalMeterInterval) {
560                                         /* if signal meter is not enabled show
561                                          * full signal strength */
562                                         updateTrayIcon(TrayIconSignalExcellent);
563                                 }
564                         } else if (strcmp(start, "ip_address") == 0) {
565                                 ipaddr_updated = true;
566                                 textIpAddress->setText(pos);
567                         } else if (strcmp(start, "wpa_state") == 0) {
568                                 status_updated = true;
569                                 textStatus->setText(wpaStateTranslate(pos));
570                         } else if (strcmp(start, "key_mgmt") == 0) {
571                                 auth_updated = true;
572                                 textAuthentication->setText(pos);
573                                 /* TODO: could add EAP status to this */
574                         } else if (strcmp(start, "pairwise_cipher") == 0) {
575                                 pairwise_cipher = pos;
576                         } else if (strcmp(start, "group_cipher") == 0) {
577                                 group_cipher = pos;
578                         } else if (strcmp(start, "mode") == 0) {
579                                 mode = pos;
580                         }
581                 }
582
583                 if (last)
584                         break;
585                 start = end + 1;
586         }
587         if (status_updated && mode)
588                 textStatus->setText(textStatus->text() + " (" + mode + ")");
589
590         if (pairwise_cipher || group_cipher) {
591                 QString encr;
592                 if (pairwise_cipher && group_cipher &&
593                     strcmp(pairwise_cipher, group_cipher) != 0) {
594                         encr.append(pairwise_cipher);
595                         encr.append(" + ");
596                         encr.append(group_cipher);
597                 } else if (pairwise_cipher) {
598                         encr.append(pairwise_cipher);
599                 } else {
600                         encr.append(group_cipher);
601                         encr.append(" [group key only]");
602                 }
603                 textEncryption->setText(encr);
604         } else
605                 textEncryption->clear();
606
607         if (signalMeterInterval) {
608                 /*
609                  * Handle signal meter service. When network is not associated,
610                  * deactivate timer, otherwise keep it going. Tray icon has to
611                  * be initialized here, because of the initial delay of the
612                  * timer.
613                  */
614                 if (ssid_updated) {
615                         if (!signalMeterTimer->isActive()) {
616                                 updateTrayIcon(TrayIconConnected);
617                                 signalMeterTimer->start();
618                         }
619                 } else {
620                         signalMeterTimer->stop();
621                 }
622         }
623
624         if (!status_updated)
625                 textStatus->clear();
626         if (!auth_updated)
627                 textAuthentication->clear();
628         if (!ssid_updated) {
629                 textSsid->clear();
630                 updateTrayToolTip(tr("(not-associated)"));
631                 updateTrayIcon(TrayIconOffline);
632         }
633         if (!bssid_updated)
634                 textBssid->clear();
635         if (!ipaddr_updated)
636                 textIpAddress->clear();
637 }
638
639
640 void WpaGui::updateNetworks()
641 {
642         char buf[4096], *start, *end, *id, *ssid, *bssid, *flags;
643         size_t len;
644         int first_active = -1;
645         int was_selected = -1;
646         bool current = false;
647
648         if (!networkMayHaveChanged)
649                 return;
650
651         if (networkList->currentRow() >= 0)
652                 was_selected = networkList->currentRow();
653
654         networkSelect->clear();
655         networkList->clear();
656
657         if (ctrl_conn == NULL)
658                 return;
659
660         len = sizeof(buf) - 1;
661         if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
662                 return;
663
664         buf[len] = '\0';
665         start = strchr(buf, '\n');
666         if (start == NULL)
667                 return;
668         start++;
669
670         while (*start) {
671                 bool last = false;
672                 end = strchr(start, '\n');
673                 if (end == NULL) {
674                         last = true;
675                         end = start;
676                         while (end[0] && end[1])
677                                 end++;
678                 }
679                 *end = '\0';
680
681                 id = start;
682                 ssid = strchr(id, '\t');
683                 if (ssid == NULL)
684                         break;
685                 *ssid++ = '\0';
686                 bssid = strchr(ssid, '\t');
687                 if (bssid == NULL)
688                         break;
689                 *bssid++ = '\0';
690                 flags = strchr(bssid, '\t');
691                 if (flags == NULL)
692                         break;
693                 *flags++ = '\0';
694
695                 if (strstr(flags, "[DISABLED][P2P-PERSISTENT]")) {
696                         if (last)
697                                 break;
698                         start = end + 1;
699                         continue;
700                 }
701
702                 QString network(id);
703                 network.append(": ");
704                 network.append(ssid);
705                 networkSelect->addItem(network);
706                 networkList->addItem(network);
707
708                 if (strstr(flags, "[CURRENT]")) {
709                         networkSelect->setCurrentIndex(networkSelect->count() -
710                                                       1);
711                         current = true;
712                 } else if (first_active < 0 &&
713                            strstr(flags, "[DISABLED]") == NULL)
714                         first_active = networkSelect->count() - 1;
715
716                 if (last)
717                         break;
718                 start = end + 1;
719         }
720
721         if (networkSelect->count() > 1)
722                 networkSelect->addItem(tr("Select any network"));
723
724         if (!current && first_active >= 0)
725                 networkSelect->setCurrentIndex(first_active);
726
727         if (was_selected >= 0 && networkList->count() > 0) {
728                 if (was_selected < networkList->count())
729                         networkList->setCurrentRow(was_selected);
730                 else
731                         networkList->setCurrentRow(networkList->count() - 1);
732         }
733         else
734                 networkList->setCurrentRow(networkSelect->currentIndex());
735
736         networkMayHaveChanged = false;
737 }
738
739
740 void WpaGui::helpIndex()
741 {
742         debug("helpIndex");
743 }
744
745
746 void WpaGui::helpContents()
747 {
748         debug("helpContents");
749 }
750
751
752 void WpaGui::helpAbout()
753 {
754         QMessageBox::about(this, "wpa_gui for wpa_supplicant",
755                            "Copyright (c) 2003-2015,\n"
756                            "Jouni Malinen <j@w1.fi>\n"
757                            "and contributors.\n"
758                            "\n"
759                            "This software may be distributed under\n"
760                            "the terms of the BSD license.\n"
761                            "See README for more details.\n"
762                            "\n"
763                            "This product includes software developed\n"
764                            "by the OpenSSL Project for use in the\n"
765                            "OpenSSL Toolkit (http://www.openssl.org/)\n");
766 }
767
768
769 void WpaGui::disconnect()
770 {
771         char reply[10];
772         size_t reply_len = sizeof(reply);
773         ctrlRequest("DISCONNECT", reply, &reply_len);
774         stopWpsRun(false);
775 }
776
777
778 void WpaGui::scan()
779 {
780         if (scanres) {
781                 scanres->close();
782                 delete scanres;
783         }
784
785         scanres = new ScanResults();
786         if (scanres == NULL)
787                 return;
788         scanres->setWpaGui(this);
789         scanres->show();
790         scanres->exec();
791 }
792
793
794 void WpaGui::eventHistory()
795 {
796         if (eh) {
797                 eh->close();
798                 delete eh;
799         }
800
801         eh = new EventHistory();
802         if (eh == NULL)
803                 return;
804         eh->addEvents(msgs);
805         eh->show();
806         eh->exec();
807 }
808
809
810 void WpaGui::ping()
811 {
812         char buf[10];
813         size_t len;
814
815 #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
816         /*
817          * QSocketNotifier cannot be used with Windows named pipes, so use a
818          * timer to check for received messages for now. This could be
819          * optimized be doing something specific to named pipes or Windows
820          * events, but it is not clear what would be the best way of doing that
821          * in Qt.
822          */
823         receiveMsgs();
824 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
825
826         if (scanres && !scanres->isVisible()) {
827                 delete scanres;
828                 scanres = NULL;
829         }
830
831         if (eh && !eh->isVisible()) {
832                 delete eh;
833                 eh = NULL;
834         }
835
836         if (udr && !udr->isVisible()) {
837                 delete udr;
838                 udr = NULL;
839         }
840
841         len = sizeof(buf) - 1;
842         if (ctrlRequest("PING", buf, &len) < 0) {
843                 debug("PING failed - trying to reconnect");
844                 if (openCtrlConnection(ctrl_iface) >= 0) {
845                         debug("Reconnected successfully");
846                         pingsToStatusUpdate = 0;
847                 }
848         }
849
850         pingsToStatusUpdate--;
851         if (pingsToStatusUpdate <= 0) {
852                 updateStatus();
853                 updateNetworks();
854         }
855
856 #ifndef CONFIG_CTRL_IFACE_NAMED_PIPE
857         /* Use less frequent pings and status updates when the main window is
858          * hidden (running in taskbar). */
859         int interval = isHidden() ? 5000 : 1000;
860         if (timer->interval() != interval)
861                 timer->setInterval(interval);
862 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
863 }
864
865
866 void WpaGui::signalMeterUpdate()
867 {
868         char reply[128];
869         size_t reply_len = sizeof(reply);
870         char *rssi;
871         int rssi_value;
872
873         ctrlRequest("SIGNAL_POLL", reply, &reply_len);
874
875         /* In order to eliminate signal strength fluctuations, try
876          * to obtain averaged RSSI value in the first place. */
877         if ((rssi = strstr(reply, "AVG_RSSI=")) != NULL)
878                 rssi_value = atoi(&rssi[sizeof("AVG_RSSI")]);
879         else if ((rssi = strstr(reply, "RSSI=")) != NULL)
880                 rssi_value = atoi(&rssi[sizeof("RSSI")]);
881         else {
882                 debug("Failed to get RSSI value");
883                 updateTrayIcon(TrayIconSignalNone);
884                 return;
885         }
886
887         debug("RSSI value: %d", rssi_value);
888
889         /*
890          * NOTE: The code below assumes, that the unit of the value returned
891          * by the SIGNAL POLL request is dBm. It might not be true for all
892          * wpa_supplicant drivers.
893          */
894
895         /*
896          * Calibration is based on "various Internet sources". Nonetheless,
897          * it seems to be compatible with the Windows 8.1 strength meter -
898          * tested on Intel Centrino Advanced-N 6235.
899          */
900         if (rssi_value >= -60)
901                 updateTrayIcon(TrayIconSignalExcellent);
902         else if (rssi_value >= -68)
903                 updateTrayIcon(TrayIconSignalGood);
904         else if (rssi_value >= -76)
905                 updateTrayIcon(TrayIconSignalOk);
906         else if (rssi_value >= -84)
907                 updateTrayIcon(TrayIconSignalWeak);
908         else
909                 updateTrayIcon(TrayIconSignalNone);
910 }
911
912
913 static int str_match(const char *a, const char *b)
914 {
915         return strncmp(a, b, strlen(b)) == 0;
916 }
917
918
919 void WpaGui::processMsg(char *msg)
920 {
921         char *pos = msg, *pos2;
922         int priority = 2;
923
924         if (*pos == '<') {
925                 /* skip priority */
926                 pos++;
927                 priority = atoi(pos);
928                 pos = strchr(pos, '>');
929                 if (pos)
930                         pos++;
931                 else
932                         pos = msg;
933         }
934
935         WpaMsg wm(pos, priority);
936         if (eh)
937                 eh->addEvent(wm);
938         if (peers)
939                 peers->event_notify(wm);
940         msgs.append(wm);
941         while (msgs.count() > 100)
942                 msgs.pop_front();
943
944         /* Update last message with truncated version of the event */
945         if (strncmp(pos, "CTRL-", 5) == 0) {
946                 pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' ');
947                 if (pos2)
948                         pos2++;
949                 else
950                         pos2 = pos;
951         } else
952                 pos2 = pos;
953         QString lastmsg = pos2;
954         lastmsg.truncate(40);
955         textLastMessage->setText(lastmsg);
956
957         pingsToStatusUpdate = 0;
958         networkMayHaveChanged = true;
959
960         if (str_match(pos, WPA_CTRL_REQ))
961                 processCtrlReq(pos + strlen(WPA_CTRL_REQ));
962         else if (str_match(pos, WPA_EVENT_SCAN_RESULTS) && scanres)
963                 scanres->updateResults();
964         else if (str_match(pos, WPA_EVENT_DISCONNECTED))
965                 showTrayMessage(QSystemTrayIcon::Information, 3,
966                                 tr("Disconnected from network."));
967         else if (str_match(pos, WPA_EVENT_CONNECTED)) {
968                 showTrayMessage(QSystemTrayIcon::Information, 3,
969                                 tr("Connection to network established."));
970                 QTimer::singleShot(5 * 1000, this, SLOT(showTrayStatus()));
971                 stopWpsRun(true);
972         } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PBC)) {
973                 wpsStatusText->setText(tr("WPS AP in active PBC mode found"));
974                 if (textStatus->text() == "INACTIVE" ||
975                     textStatus->text() == "DISCONNECTED")
976                         wpaguiTab->setCurrentWidget(wpsTab);
977                 wpsInstructions->setText(tr("Press the PBC button on the "
978                                             "screen to start registration"));
979         } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PIN)) {
980                 wpsStatusText->setText(tr("WPS AP with recently selected "
981                                           "registrar"));
982                 if (textStatus->text() == "INACTIVE" ||
983                     textStatus->text() == "DISCONNECTED")
984                         wpaguiTab->setCurrentWidget(wpsTab);
985         } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_AUTH)) {
986                 showTrayMessage(QSystemTrayIcon::Information, 3,
987                                 "Wi-Fi Protected Setup (WPS) AP\n"
988                                 "indicating this client is authorized.");
989                 wpsStatusText->setText("WPS AP indicating this client is "
990                                        "authorized");
991                 if (textStatus->text() == "INACTIVE" ||
992                     textStatus->text() == "DISCONNECTED")
993                         wpaguiTab->setCurrentWidget(wpsTab);
994         } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) {
995                 wpsStatusText->setText(tr("WPS AP detected"));
996         } else if (str_match(pos, WPS_EVENT_OVERLAP)) {
997                 wpsStatusText->setText(tr("PBC mode overlap detected"));
998                 wpsInstructions->setText(tr("More than one AP is currently in "
999                                             "active WPS PBC mode. Wait couple "
1000                                             "of minutes and try again"));
1001                 wpaguiTab->setCurrentWidget(wpsTab);
1002         } else if (str_match(pos, WPS_EVENT_CRED_RECEIVED)) {
1003                 wpsStatusText->setText(tr("Network configuration received"));
1004                 wpaguiTab->setCurrentWidget(wpsTab);
1005         } else if (str_match(pos, WPA_EVENT_EAP_METHOD)) {
1006                 if (strstr(pos, "(WSC)"))
1007                         wpsStatusText->setText(tr("Registration started"));
1008         } else if (str_match(pos, WPS_EVENT_M2D)) {
1009                 wpsStatusText->setText(tr("Registrar does not yet know PIN"));
1010         } else if (str_match(pos, WPS_EVENT_FAIL)) {
1011                 wpsStatusText->setText(tr("Registration failed"));
1012         } else if (str_match(pos, WPS_EVENT_SUCCESS)) {
1013                 wpsStatusText->setText(tr("Registration succeeded"));
1014         }
1015 }
1016
1017
1018 void WpaGui::processCtrlReq(const char *req)
1019 {
1020         if (udr) {
1021                 udr->close();
1022                 delete udr;
1023         }
1024         udr = new UserDataRequest();
1025         if (udr == NULL)
1026                 return;
1027         if (udr->setParams(this, req) < 0) {
1028                 delete udr;
1029                 udr = NULL;
1030                 return;
1031         }
1032         udr->show();
1033         udr->exec();
1034 }
1035
1036
1037 void WpaGui::receiveMsgs()
1038 {
1039         char buf[256];
1040         size_t len;
1041
1042         while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) {
1043                 len = sizeof(buf) - 1;
1044                 if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) {
1045                         buf[len] = '\0';
1046                         processMsg(buf);
1047                 }
1048         }
1049 }
1050
1051
1052 void WpaGui::connectB()
1053 {
1054         char reply[10];
1055         size_t reply_len = sizeof(reply);
1056         ctrlRequest("REASSOCIATE", reply, &reply_len);
1057 }
1058
1059
1060 void WpaGui::selectNetwork( const QString &sel )
1061 {
1062         QString cmd(sel);
1063         char reply[10];
1064         size_t reply_len = sizeof(reply);
1065
1066         if (cmd.contains(QRegExp("^\\d+:")))
1067                 cmd.truncate(cmd.indexOf(':'));
1068         else
1069                 cmd = "any";
1070         cmd.prepend("SELECT_NETWORK ");
1071         ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
1072         triggerUpdate();
1073         stopWpsRun(false);
1074 }
1075
1076
1077 void WpaGui::enableNetwork(const QString &sel)
1078 {
1079         QString cmd(sel);
1080         char reply[10];
1081         size_t reply_len = sizeof(reply);
1082
1083         if (cmd.contains(QRegExp("^\\d+:")))
1084                 cmd.truncate(cmd.indexOf(':'));
1085         else if (!cmd.startsWith("all")) {
1086                 debug("Invalid editNetwork '%s'",
1087                       cmd.toLocal8Bit().constData());
1088                 return;
1089         }
1090         cmd.prepend("ENABLE_NETWORK ");
1091         ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
1092         triggerUpdate();
1093 }
1094
1095
1096 void WpaGui::disableNetwork(const QString &sel)
1097 {
1098         QString cmd(sel);
1099         char reply[10];
1100         size_t reply_len = sizeof(reply);
1101
1102         if (cmd.contains(QRegExp("^\\d+:")))
1103                 cmd.truncate(cmd.indexOf(':'));
1104         else if (!cmd.startsWith("all")) {
1105                 debug("Invalid editNetwork '%s'",
1106                       cmd.toLocal8Bit().constData());
1107                 return;
1108         }
1109         cmd.prepend("DISABLE_NETWORK ");
1110         ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
1111         triggerUpdate();
1112 }
1113
1114
1115 void WpaGui::editNetwork(const QString &sel)
1116 {
1117         QString cmd(sel);
1118         int id = -1;
1119
1120         if (cmd.contains(QRegExp("^\\d+:"))) {
1121                 cmd.truncate(cmd.indexOf(':'));
1122                 id = cmd.toInt();
1123         }
1124
1125         NetworkConfig *nc = new NetworkConfig();
1126         if (nc == NULL)
1127                 return;
1128         nc->setWpaGui(this);
1129
1130         if (id >= 0)
1131                 nc->paramsFromConfig(id);
1132         else
1133                 nc->newNetwork();
1134
1135         nc->show();
1136         nc->exec();
1137 }
1138
1139
1140 void WpaGui::editSelectedNetwork()
1141 {
1142         if (networkSelect->count() < 1) {
1143                 QMessageBox::information(
1144                         this, tr("No Networks"),
1145                         tr("There are no networks to edit.\n"));
1146                 return;
1147         }
1148         QString sel(networkSelect->currentText());
1149         editNetwork(sel);
1150 }
1151
1152
1153 void WpaGui::editListedNetwork()
1154 {
1155         if (networkList->currentRow() < 0) {
1156                 QMessageBox::information(this, tr("Select A Network"),
1157                                          tr("Select a network from the list to"
1158                                             " edit it.\n"));
1159                 return;
1160         }
1161         QString sel(networkList->currentItem()->text());
1162         editNetwork(sel);
1163 }
1164
1165
1166 void WpaGui::triggerUpdate()
1167 {
1168         updateStatus();
1169         networkMayHaveChanged = true;
1170         updateNetworks();
1171 }
1172
1173
1174 void WpaGui::addNetwork()
1175 {
1176         NetworkConfig *nc = new NetworkConfig();
1177         if (nc == NULL)
1178                 return;
1179         nc->setWpaGui(this);
1180         nc->newNetwork();
1181         nc->show();
1182         nc->exec();
1183 }
1184
1185
1186 void WpaGui::removeNetwork(const QString &sel)
1187 {
1188         QString cmd(sel);
1189         char reply[10];
1190         size_t reply_len = sizeof(reply);
1191
1192         if (cmd.contains(QRegExp("^\\d+:")))
1193                 cmd.truncate(cmd.indexOf(':'));
1194         else if (!cmd.startsWith("all")) {
1195                 debug("Invalid editNetwork '%s'",
1196                       cmd.toLocal8Bit().constData());
1197                 return;
1198         }
1199         cmd.prepend("REMOVE_NETWORK ");
1200         ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
1201         triggerUpdate();
1202 }
1203
1204
1205 void WpaGui::removeSelectedNetwork()
1206 {
1207         if (networkSelect->count() < 1) {
1208                 QMessageBox::information(this, tr("No Networks"),
1209                                          tr("There are no networks to remove."
1210                                             "\n"));
1211                 return;
1212         }
1213         QString sel(networkSelect->currentText());
1214         removeNetwork(sel);
1215 }
1216
1217
1218 void WpaGui::removeListedNetwork()
1219 {
1220         if (networkList->currentRow() < 0) {
1221                 QMessageBox::information(this, tr("Select A Network"),
1222                                          tr("Select a network from the list "
1223                                             "to remove it.\n"));
1224                 return;
1225         }
1226         QString sel(networkList->currentItem()->text());
1227         removeNetwork(sel);
1228 }
1229
1230
1231 void WpaGui::enableAllNetworks()
1232 {
1233         QString sel("all");
1234         enableNetwork(sel);
1235 }
1236
1237
1238 void WpaGui::disableAllNetworks()
1239 {
1240         QString sel("all");
1241         disableNetwork(sel);
1242 }
1243
1244
1245 void WpaGui::removeAllNetworks()
1246 {
1247         QString sel("all");
1248         removeNetwork(sel);
1249 }
1250
1251
1252 int WpaGui::getNetworkDisabled(const QString &sel)
1253 {
1254         QString cmd(sel);
1255         char reply[10];
1256         size_t reply_len = sizeof(reply) - 1;
1257         int pos = cmd.indexOf(':');
1258         if (pos < 0) {
1259                 debug("Invalid getNetworkDisabled '%s'",
1260                       cmd.toLocal8Bit().constData());
1261                 return -1;
1262         }
1263         cmd.truncate(pos);
1264         cmd.prepend("GET_NETWORK ");
1265         cmd.append(" disabled");
1266
1267         if (ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len) >= 0
1268             && reply_len >= 1) {
1269                 reply[reply_len] = '\0';
1270                 if (!str_match(reply, "FAIL"))
1271                         return atoi(reply);
1272         }
1273
1274         return -1;
1275 }
1276
1277
1278 void WpaGui::updateNetworkDisabledStatus()
1279 {
1280         if (networkList->currentRow() < 0)
1281                 return;
1282
1283         QString sel(networkList->currentItem()->text());
1284
1285         switch (getNetworkDisabled(sel)) {
1286         case 0:
1287                 if (!enableRadioButton->isChecked())
1288                         enableRadioButton->setChecked(true);
1289                 return;
1290         case 1:
1291                 if (!disableRadioButton->isChecked())
1292                         disableRadioButton->setChecked(true);
1293                 return;
1294         }
1295 }
1296
1297
1298 void WpaGui::enableListedNetwork(bool enabled)
1299 {
1300         if (networkList->currentRow() < 0 || !enabled)
1301                 return;
1302
1303         QString sel(networkList->currentItem()->text());
1304
1305         if (getNetworkDisabled(sel) == 1)
1306                 enableNetwork(sel);
1307 }
1308
1309
1310 void WpaGui::disableListedNetwork(bool disabled)
1311 {
1312         if (networkList->currentRow() < 0 || !disabled)
1313                 return;
1314
1315         QString sel(networkList->currentItem()->text());
1316
1317         if (getNetworkDisabled(sel) == 0)
1318                 disableNetwork(sel);
1319 }
1320
1321
1322 void WpaGui::saveConfig()
1323 {
1324         char buf[10];
1325         size_t len;
1326
1327         len = sizeof(buf) - 1;
1328         ctrlRequest("SAVE_CONFIG", buf, &len);
1329
1330         buf[len] = '\0';
1331
1332         if (str_match(buf, "FAIL"))
1333                 QMessageBox::warning(
1334                         this, tr("Failed to save configuration"),
1335                         tr("The configuration could not be saved.\n"
1336                            "\n"
1337                            "The update_config=1 configuration option\n"
1338                            "must be used for configuration saving to\n"
1339                            "be permitted.\n"));
1340         else
1341                 QMessageBox::information(
1342                         this, tr("Saved configuration"),
1343                         tr("The current configuration was saved."
1344                            "\n"));
1345 }
1346
1347
1348 void WpaGui::selectAdapter( const QString & sel )
1349 {
1350         if (openCtrlConnection(sel.toLocal8Bit().constData()) < 0)
1351                 debug("Failed to open control connection to "
1352                       "wpa_supplicant.");
1353         updateStatus();
1354         updateNetworks();
1355 }
1356
1357
1358 void WpaGui::createTrayIcon(bool trayOnly)
1359 {
1360         QApplication::setQuitOnLastWindowClosed(false);
1361
1362         tray_icon = new QSystemTrayIcon(this);
1363         updateTrayIcon(TrayIconOffline);
1364
1365         connect(tray_icon,
1366                 SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
1367                 this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
1368
1369         ackTrayIcon = false;
1370
1371         tray_menu = new QMenu(this);
1372
1373         disconnectAction = new QAction(tr("&Disconnect"), this);
1374         reconnectAction = new QAction(tr("Re&connect"), this);
1375         connect(disconnectAction, SIGNAL(triggered()), this,
1376                 SLOT(disconnect()));
1377         connect(reconnectAction, SIGNAL(triggered()), this,
1378                 SLOT(connectB()));
1379         tray_menu->addAction(disconnectAction);
1380         tray_menu->addAction(reconnectAction);
1381         tray_menu->addSeparator();
1382
1383         eventAction = new QAction(tr("&Event History"), this);
1384         scanAction = new QAction(tr("Scan &Results"), this);
1385         statAction = new QAction(tr("S&tatus"), this);
1386         connect(eventAction, SIGNAL(triggered()), this, SLOT(eventHistory()));
1387         connect(scanAction, SIGNAL(triggered()), this, SLOT(scan()));
1388         connect(statAction, SIGNAL(triggered()), this, SLOT(showTrayStatus()));
1389         tray_menu->addAction(eventAction);
1390         tray_menu->addAction(scanAction);
1391         tray_menu->addAction(statAction);
1392         tray_menu->addSeparator();
1393
1394         showAction = new QAction(tr("&Show Window"), this);
1395         hideAction = new QAction(tr("&Hide Window"), this);
1396         quitAction = new QAction(tr("&Quit"), this);
1397         connect(showAction, SIGNAL(triggered()), this, SLOT(show()));
1398         connect(hideAction, SIGNAL(triggered()), this, SLOT(hide()));
1399         connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
1400         tray_menu->addAction(showAction);
1401         tray_menu->addAction(hideAction);
1402         tray_menu->addSeparator();
1403         tray_menu->addAction(quitAction);
1404
1405         tray_icon->setContextMenu(tray_menu);
1406
1407         tray_icon->show();
1408
1409         if (!trayOnly)
1410                 show();
1411         inTray = trayOnly;
1412 }
1413
1414
1415 void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec,
1416                              const QString & msg)
1417 {
1418         if (!QSystemTrayIcon::supportsMessages())
1419                 return;
1420
1421         if (isVisible() || !tray_icon || !tray_icon->isVisible() || quietMode)
1422                 return;
1423
1424         tray_icon->showMessage(qAppName(), msg, type, sec * 1000);
1425 }
1426
1427
1428 void WpaGui::trayActivated(QSystemTrayIcon::ActivationReason how)
1429  {
1430         switch (how) {
1431         /* use close() here instead of hide() and allow the
1432          * custom closeEvent handler take care of children */
1433         case QSystemTrayIcon::Trigger:
1434                 ackTrayIcon = true;
1435                 if (isVisible()) {
1436                         close();
1437                         inTray = true;
1438                 } else {
1439                         show();
1440                         inTray = false;
1441                 }
1442                 break;
1443         case QSystemTrayIcon::MiddleClick:
1444                 showTrayStatus();
1445                 break;
1446         default:
1447                 break;
1448         }
1449 }
1450
1451
1452 void WpaGui::showTrayStatus()
1453 {
1454         char buf[2048];
1455         size_t len;
1456
1457         len = sizeof(buf) - 1;
1458         if (ctrlRequest("STATUS", buf, &len) < 0)
1459                 return;
1460         buf[len] = '\0';
1461
1462         QString msg, status(buf);
1463
1464         QStringList lines = status.split(QRegExp("\\n"));
1465         for (QStringList::Iterator it = lines.begin();
1466              it != lines.end(); it++) {
1467                 int pos = (*it).indexOf('=') + 1;
1468                 if (pos < 1)
1469                         continue;
1470
1471                 if ((*it).startsWith("bssid="))
1472                         msg.append("BSSID:\t" + (*it).mid(pos) + "\n");
1473                 else if ((*it).startsWith("ssid="))
1474                         msg.append("SSID: \t" + (*it).mid(pos) + "\n");
1475                 else if ((*it).startsWith("pairwise_cipher="))
1476                         msg.append("PAIR: \t" + (*it).mid(pos) + "\n");
1477                 else if ((*it).startsWith("group_cipher="))
1478                         msg.append("GROUP:\t" + (*it).mid(pos) + "\n");
1479                 else if ((*it).startsWith("key_mgmt="))
1480                         msg.append("AUTH: \t" + (*it).mid(pos) + "\n");
1481                 else if ((*it).startsWith("wpa_state="))
1482                         msg.append("STATE:\t" + (*it).mid(pos) + "\n");
1483                 else if ((*it).startsWith("ip_address="))
1484                         msg.append("IP:   \t" + (*it).mid(pos) + "\n");
1485                 else if ((*it).startsWith("Supplicant PAE state="))
1486                         msg.append("PAE:  \t" + (*it).mid(pos) + "\n");
1487                 else if ((*it).startsWith("EAP state="))
1488                         msg.append("EAP:  \t" + (*it).mid(pos) + "\n");
1489         }
1490
1491         if (!msg.isEmpty())
1492                 showTrayMessage(QSystemTrayIcon::Information, 10, msg);
1493 }
1494
1495
1496 void WpaGui::updateTrayToolTip(const QString &msg)
1497 {
1498         if (tray_icon)
1499                 tray_icon->setToolTip(msg);
1500 }
1501
1502
1503 void WpaGui::updateTrayIcon(TrayIconType type)
1504 {
1505         if (!tray_icon || currentIconType == type)
1506                 return;
1507
1508         QIcon fallback_icon;
1509         QStringList names;
1510
1511         if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
1512                 fallback_icon = QIcon(":/icons/wpa_gui.svg");
1513         else
1514                 fallback_icon = QIcon(":/icons/wpa_gui.png");
1515
1516         switch (type) {
1517         case TrayIconOffline:
1518                 names << "network-wireless-offline-symbolic"
1519                       << "network-wireless-offline"
1520                       << "network-wireless-signal-none-symbolic"
1521                       << "network-wireless-signal-none";
1522                 break;
1523         case TrayIconAcquiring:
1524                 names << "network-wireless-acquiring-symbolic"
1525                       << "network-wireless-acquiring";
1526                 break;
1527         case TrayIconConnected:
1528                 names << "network-wireless-connected-symbolic"
1529                       << "network-wireless-connected";
1530                 break;
1531         case TrayIconSignalNone:
1532                 names << "network-wireless-signal-none-symbolic"
1533                       << "network-wireless-signal-none";
1534                 break;
1535         case TrayIconSignalWeak:
1536                 names << "network-wireless-signal-weak-symbolic"
1537                       << "network-wireless-signal-weak";
1538                 break;
1539         case TrayIconSignalOk:
1540                 names << "network-wireless-signal-ok-symbolic"
1541                       << "network-wireless-signal-ok";
1542                 break;
1543         case TrayIconSignalGood:
1544                 names << "network-wireless-signal-good-symbolic"
1545                       << "network-wireless-signal-good";
1546                 break;
1547         case TrayIconSignalExcellent:
1548                 names << "network-wireless-signal-excellent-symbolic"
1549                       << "network-wireless-signal-excellent";
1550                 break;
1551         }
1552
1553         currentIconType = type;
1554         tray_icon->setIcon(loadThemedIcon(names, fallback_icon));
1555 }
1556
1557
1558 QIcon WpaGui::loadThemedIcon(const QStringList &names,
1559                              const QIcon &fallback)
1560 {
1561         QIcon icon;
1562
1563         for (QStringList::ConstIterator it = names.begin();
1564              it != names.end(); it++) {
1565                 icon = QIcon::fromTheme(*it);
1566                 if (!icon.isNull())
1567                         return icon;
1568         }
1569
1570         return fallback;
1571 }
1572
1573
1574 void WpaGui::closeEvent(QCloseEvent *event)
1575 {
1576         if (eh) {
1577                 eh->close();
1578                 delete eh;
1579                 eh = NULL;
1580         }
1581
1582         if (scanres) {
1583                 scanres->close();
1584                 delete scanres;
1585                 scanres = NULL;
1586         }
1587
1588         if (peers) {
1589                 peers->close();
1590                 delete peers;
1591                 peers = NULL;
1592         }
1593
1594         if (udr) {
1595                 udr->close();
1596                 delete udr;
1597                 udr = NULL;
1598         }
1599
1600         if (tray_icon && !ackTrayIcon) {
1601                 /* give user a visual hint that the tray icon exists */
1602                 if (QSystemTrayIcon::supportsMessages()) {
1603                         hide();
1604                         showTrayMessage(QSystemTrayIcon::Information, 3,
1605                                         qAppName() +
1606                                         tr(" will keep running in "
1607                                            "the system tray."));
1608                 } else {
1609                         QMessageBox::information(this, qAppName() +
1610                                                  tr(" systray"),
1611                                                  tr("The program will keep "
1612                                                     "running in the system "
1613                                                     "tray."));
1614                 }
1615                 ackTrayIcon = true;
1616         }
1617
1618         event->accept();
1619 }
1620
1621
1622 void WpaGui::wpsDialog()
1623 {
1624         wpaguiTab->setCurrentWidget(wpsTab);
1625 }
1626
1627
1628 void WpaGui::peersDialog()
1629 {
1630         if (peers) {
1631                 peers->close();
1632                 delete peers;
1633         }
1634
1635         peers = new Peers();
1636         if (peers == NULL)
1637                 return;
1638         peers->setWpaGui(this);
1639         peers->show();
1640         peers->exec();
1641 }
1642
1643
1644 void WpaGui::tabChanged(int index)
1645 {
1646         if (index != 2)
1647                 return;
1648
1649         if (wpsRunning)
1650                 return;
1651
1652         wpsApPinEdit->setEnabled(!bssFromScan.isEmpty());
1653         if (bssFromScan.isEmpty())
1654                 wpsApPinButton->setEnabled(false);
1655 }
1656
1657
1658 void WpaGui::wpsPbc()
1659 {
1660         char reply[20];
1661         size_t reply_len = sizeof(reply);
1662
1663         if (ctrlRequest("WPS_PBC", reply, &reply_len) < 0)
1664                 return;
1665
1666         wpsPinEdit->setEnabled(false);
1667         if (wpsStatusText->text().compare(tr("WPS AP in active PBC mode found"))) {
1668                 wpsInstructions->setText(tr("Press the push button on the AP to "
1669                                          "start the PBC mode."));
1670         } else {
1671                 wpsInstructions->setText(tr("If you have not yet done so, press "
1672                                          "the push button on the AP to start "
1673                                          "the PBC mode."));
1674         }
1675         wpsStatusText->setText(tr("Waiting for Registrar"));
1676         wpsRunning = true;
1677 }
1678
1679
1680 void WpaGui::wpsGeneratePin()
1681 {
1682         char reply[20];
1683         size_t reply_len = sizeof(reply) - 1;
1684
1685         if (ctrlRequest("WPS_PIN any", reply, &reply_len) < 0)
1686                 return;
1687
1688         reply[reply_len] = '\0';
1689
1690         wpsPinEdit->setText(reply);
1691         wpsPinEdit->setEnabled(true);
1692         wpsInstructions->setText(tr("Enter the generated PIN into the Registrar "
1693                                  "(either the internal one in the AP or an "
1694                                  "external one)."));
1695         wpsStatusText->setText(tr("Waiting for Registrar"));
1696         wpsRunning = true;
1697 }
1698
1699
1700 void WpaGui::setBssFromScan(const QString &bssid)
1701 {
1702         bssFromScan = bssid;
1703         wpsApPinEdit->setEnabled(!bssFromScan.isEmpty());
1704         wpsApPinButton->setEnabled(wpsApPinEdit->text().length() == 8);
1705         wpsStatusText->setText(tr("WPS AP selected from scan results"));
1706         wpsInstructions->setText(tr("If you want to use an AP device PIN, e.g., "
1707                                  "from a label in the device, enter the eight "
1708                                  "digit AP PIN and click Use AP PIN button."));
1709 }
1710
1711
1712 void WpaGui::wpsApPinChanged(const QString &text)
1713 {
1714         wpsApPinButton->setEnabled(text.length() == 8);
1715 }
1716
1717
1718 void WpaGui::wpsApPin()
1719 {
1720         char reply[20];
1721         size_t reply_len = sizeof(reply);
1722
1723         QString cmd("WPS_REG " + bssFromScan + " " + wpsApPinEdit->text());
1724         if (ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len) < 0)
1725                 return;
1726
1727         wpsStatusText->setText(tr("Waiting for AP/Enrollee"));
1728         wpsRunning = true;
1729 }
1730
1731
1732 void WpaGui::stopWpsRun(bool success)
1733 {
1734         if (wpsRunning)
1735                 wpsStatusText->setText(success ? tr("Connected to the network") :
1736                                        tr("Stopped"));
1737         else
1738                 wpsStatusText->setText("");
1739         wpsPinEdit->setEnabled(false);
1740         wpsInstructions->setText("");
1741         wpsRunning = false;
1742         bssFromScan = "";
1743         wpsApPinEdit->setEnabled(false);
1744         wpsApPinButton->setEnabled(false);
1745 }
1746
1747
1748 #ifdef CONFIG_NATIVE_WINDOWS
1749
1750 #ifndef WPASVC_NAME
1751 #define WPASVC_NAME TEXT("wpasvc")
1752 #endif
1753
1754 class ErrorMsg : public QMessageBox {
1755 public:
1756         ErrorMsg(QWidget *parent, DWORD last_err = GetLastError());
1757         void showMsg(QString msg);
1758 private:
1759         DWORD err;
1760 };
1761
1762 ErrorMsg::ErrorMsg(QWidget *parent, DWORD last_err) :
1763         QMessageBox(parent), err(last_err)
1764 {
1765         setWindowTitle(tr("wpa_gui error"));
1766         setIcon(QMessageBox::Warning);
1767 }
1768
1769 void ErrorMsg::showMsg(QString msg)
1770 {
1771         LPTSTR buf;
1772
1773         setText(msg);
1774         if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
1775                           FORMAT_MESSAGE_FROM_SYSTEM,
1776                           NULL, err, 0, (LPTSTR) (void *) &buf,
1777                           0, NULL) > 0) {
1778                 QString msg = QString::fromWCharArray(buf);
1779                 setInformativeText(QString("[%1] %2").arg(err).arg(msg));
1780                 LocalFree(buf);
1781         } else {
1782                 setInformativeText(QString("[%1]").arg(err));
1783         }
1784
1785         exec();
1786 }
1787
1788
1789 void WpaGui::startService()
1790 {
1791         SC_HANDLE svc, scm;
1792
1793         scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
1794         if (!scm) {
1795                 ErrorMsg(this).showMsg(tr("OpenSCManager failed"));
1796                 return;
1797         }
1798
1799         svc = OpenService(scm, WPASVC_NAME, SERVICE_START);
1800         if (!svc) {
1801                 ErrorMsg(this).showMsg(tr("OpenService failed"));
1802                 CloseServiceHandle(scm);
1803                 return;
1804         }
1805
1806         if (!StartService(svc, 0, NULL)) {
1807                 ErrorMsg(this).showMsg(tr("Failed to start wpa_supplicant "
1808                                        "service"));
1809         }
1810
1811         CloseServiceHandle(svc);
1812         CloseServiceHandle(scm);
1813 }
1814
1815
1816 void WpaGui::stopService()
1817 {
1818         SC_HANDLE svc, scm;
1819         SERVICE_STATUS status;
1820
1821         scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
1822         if (!scm) {
1823                 ErrorMsg(this).showMsg(tr("OpenSCManager failed"));
1824                 return;
1825         }
1826
1827         svc = OpenService(scm, WPASVC_NAME, SERVICE_STOP);
1828         if (!svc) {
1829                 ErrorMsg(this).showMsg(tr("OpenService failed"));
1830                 CloseServiceHandle(scm);
1831                 return;
1832         }
1833
1834         if (!ControlService(svc, SERVICE_CONTROL_STOP, &status)) {
1835                 ErrorMsg(this).showMsg(tr("Failed to stop wpa_supplicant "
1836                                        "service"));
1837         }
1838
1839         CloseServiceHandle(svc);
1840         CloseServiceHandle(scm);
1841 }
1842
1843
1844 bool WpaGui::serviceRunning()
1845 {
1846         SC_HANDLE svc, scm;
1847         SERVICE_STATUS status;
1848         bool running = false;
1849
1850         scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
1851         if (!scm) {
1852                 debug("OpenSCManager failed: %d", (int) GetLastError());
1853                 return false;
1854         }
1855
1856         svc = OpenService(scm, WPASVC_NAME, SERVICE_QUERY_STATUS);
1857         if (!svc) {
1858                 debug("OpenService failed: %d", (int) GetLastError());
1859                 CloseServiceHandle(scm);
1860                 return false;
1861         }
1862
1863         if (QueryServiceStatus(svc, &status)) {
1864                 if (status.dwCurrentState != SERVICE_STOPPED)
1865                         running = true;
1866         }
1867
1868         CloseServiceHandle(svc);
1869         CloseServiceHandle(scm);
1870
1871         return running;
1872 }
1873
1874 #endif /* CONFIG_NATIVE_WINDOWS */
1875
1876
1877 void WpaGui::addInterface()
1878 {
1879         if (add_iface) {
1880                 add_iface->close();
1881                 delete add_iface;
1882         }
1883         add_iface = new AddInterface(this, this);
1884         add_iface->show();
1885         add_iface->exec();
1886 }
1887
1888
1889 #ifndef QT_NO_SESSIONMANAGER
1890 void WpaGui::saveState()
1891 {
1892         QSettings settings("wpa_supplicant", "wpa_gui");
1893         settings.beginGroup("state");
1894         settings.setValue("session_id", app->sessionId());
1895         settings.setValue("in_tray", inTray);
1896         settings.endGroup();
1897 }
1898 #endif