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