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