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