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