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
34WpaGui::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
181WpaGui::~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
233void WpaGui::languageChange()
234{
235	retranslateUi(this);
236}
237
238
239void 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
270int 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
451int 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
467QString 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
492void 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
640void 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
740void WpaGui::helpIndex()
741{
742	debug("helpIndex");
743}
744
745
746void WpaGui::helpContents()
747{
748	debug("helpContents");
749}
750
751
752void 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
769void 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
778void 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
794void 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
810void 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
866void 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
913static int str_match(const char *a, const char *b)
914{
915	return strncmp(a, b, strlen(b)) == 0;
916}
917
918
919void 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
1018void 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
1037void 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
1052void WpaGui::connectB()
1053{
1054	char reply[10];
1055	size_t reply_len = sizeof(reply);
1056	ctrlRequest("REASSOCIATE", reply, &reply_len);
1057}
1058
1059
1060void 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
1077void 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
1096void 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
1115void 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
1140void 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
1153void 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
1166void WpaGui::triggerUpdate()
1167{
1168	updateStatus();
1169	networkMayHaveChanged = true;
1170	updateNetworks();
1171}
1172
1173
1174void 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
1186void 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
1205void 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
1218void 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
1231void WpaGui::enableAllNetworks()
1232{
1233	QString sel("all");
1234	enableNetwork(sel);
1235}
1236
1237
1238void WpaGui::disableAllNetworks()
1239{
1240	QString sel("all");
1241	disableNetwork(sel);
1242}
1243
1244
1245void WpaGui::removeAllNetworks()
1246{
1247	QString sel("all");
1248	removeNetwork(sel);
1249}
1250
1251
1252int 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
1278void 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
1298void 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
1310void 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
1322void 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
1348void 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
1358void 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
1415void 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
1428void 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
1452void 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
1496void WpaGui::updateTrayToolTip(const QString &msg)
1497{
1498	if (tray_icon)
1499		tray_icon->setToolTip(msg);
1500}
1501
1502
1503void 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
1558QIcon 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
1574void 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
1622void WpaGui::wpsDialog()
1623{
1624	wpaguiTab->setCurrentWidget(wpsTab);
1625}
1626
1627
1628void 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
1644void 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
1658void 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
1680void 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
1700void 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
1712void WpaGui::wpsApPinChanged(const QString &text)
1713{
1714	wpsApPinButton->setEnabled(text.length() == 8);
1715}
1716
1717
1718void 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
1732void 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
1754class ErrorMsg : public QMessageBox {
1755public:
1756	ErrorMsg(QWidget *parent, DWORD last_err = GetLastError());
1757	void showMsg(QString msg);
1758private:
1759	DWORD err;
1760};
1761
1762ErrorMsg::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
1769void 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
1789void 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
1816void 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
1844bool 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
1877void 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
1890void 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