1/*
2 * wpa_gui - Peers class
3 * Copyright (c) 2009, Atheros Communications
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15#include <cstdio>
16#include <QImageReader>
17#include <QMessageBox>
18
19#include "common/wpa_ctrl.h"
20#include "wpagui.h"
21#include "stringquery.h"
22#include "peers.h"
23
24
25enum {
26	peer_role_address = Qt::UserRole + 1,
27	peer_role_type,
28	peer_role_uuid,
29	peer_role_details,
30	peer_role_pri_dev_type,
31	peer_role_ssid,
32	peer_role_config_methods,
33	peer_role_dev_passwd_id,
34	peer_role_bss_id
35};
36
37/*
38 * TODO:
39 * - add current AP info (e.g., from WPS) in station mode
40 */
41
42enum peer_type {
43	PEER_TYPE_ASSOCIATED_STATION,
44	PEER_TYPE_AP,
45	PEER_TYPE_AP_WPS,
46	PEER_TYPE_WPS_PIN_NEEDED,
47	PEER_TYPE_WPS_ER_AP,
48	PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
49	PEER_TYPE_WPS_ER_ENROLLEE,
50	PEER_TYPE_WPS_ENROLLEE
51};
52
53
54Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
55	: QDialog(parent)
56{
57	setupUi(this);
58
59	if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
60	{
61		default_icon = new QIcon(":/icons/wpa_gui.svg");
62		ap_icon = new QIcon(":/icons/ap.svg");
63		laptop_icon = new QIcon(":/icons/laptop.svg");
64	} else {
65		default_icon = new QIcon(":/icons/wpa_gui.png");
66		ap_icon = new QIcon(":/icons/ap.png");
67		laptop_icon = new QIcon(":/icons/laptop.png");
68	}
69
70	peers->setModel(&model);
71	peers->setResizeMode(QListView::Adjust);
72
73	peers->setContextMenuPolicy(Qt::CustomContextMenu);
74	connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
75		this, SLOT(context_menu(const QPoint &)));
76
77	wpagui = NULL;
78}
79
80
81void Peers::setWpaGui(WpaGui *_wpagui)
82{
83	wpagui = _wpagui;
84	update_peers();
85}
86
87
88Peers::~Peers()
89{
90	delete default_icon;
91	delete ap_icon;
92	delete laptop_icon;
93}
94
95
96void Peers::languageChange()
97{
98	retranslateUi(this);
99}
100
101
102QString Peers::ItemType(int type)
103{
104	QString title;
105	switch (type) {
106	case PEER_TYPE_ASSOCIATED_STATION:
107		title = tr("Associated station");
108		break;
109	case PEER_TYPE_AP:
110		title = tr("AP");
111		break;
112	case PEER_TYPE_AP_WPS:
113		title = tr("WPS AP");
114		break;
115	case PEER_TYPE_WPS_PIN_NEEDED:
116		title = tr("WPS PIN needed");
117		break;
118	case PEER_TYPE_WPS_ER_AP:
119		title = tr("ER: WPS AP");
120		break;
121	case PEER_TYPE_WPS_ER_AP_UNCONFIGURED:
122		title = tr("ER: WPS AP (Unconfigured)");
123		break;
124	case PEER_TYPE_WPS_ER_ENROLLEE:
125		title = tr("ER: WPS Enrollee");
126		break;
127	case PEER_TYPE_WPS_ENROLLEE:
128		title = tr("WPS Enrollee");
129		break;
130	}
131	return title;
132}
133
134
135void Peers::context_menu(const QPoint &pos)
136{
137	QMenu *menu = new QMenu;
138	if (menu == NULL)
139		return;
140
141	QModelIndex idx = peers->indexAt(pos);
142	if (idx.isValid()) {
143		ctx_item = model.itemFromIndex(idx);
144		int type = ctx_item->data(peer_role_type).toInt();
145		menu->addAction(Peers::ItemType(type))->setEnabled(false);
146		menu->addSeparator();
147
148		int config_methods = -1;
149		QVariant var = ctx_item->data(peer_role_config_methods);
150		if (var.isValid())
151			config_methods = var.toInt();
152
153		if ((type == PEER_TYPE_ASSOCIATED_STATION ||
154		     type == PEER_TYPE_AP_WPS ||
155		     type == PEER_TYPE_WPS_PIN_NEEDED ||
156		     type == PEER_TYPE_WPS_ER_ENROLLEE ||
157		     type == PEER_TYPE_WPS_ENROLLEE) &&
158		    (config_methods == -1 || (config_methods & 0x010c))) {
159			menu->addAction(tr("Enter WPS PIN"), this,
160					SLOT(enter_pin()));
161		}
162
163		if (type == PEER_TYPE_AP_WPS) {
164			menu->addAction(tr("Connect (PBC)"), this,
165					SLOT(connect_pbc()));
166		}
167
168		if ((type == PEER_TYPE_ASSOCIATED_STATION ||
169		     type == PEER_TYPE_WPS_ER_ENROLLEE ||
170		     type == PEER_TYPE_WPS_ENROLLEE) &&
171		    config_methods >= 0 && (config_methods & 0x0080)) {
172			menu->addAction(tr("Enroll (PBC)"), this,
173					SLOT(connect_pbc()));
174		}
175
176		if (type == PEER_TYPE_WPS_ER_AP) {
177			menu->addAction(tr("Learn Configuration"), this,
178					SLOT(learn_ap_config()));
179		}
180
181		menu->addAction(tr("Properties"), this, SLOT(properties()));
182	} else {
183		ctx_item = NULL;
184		menu->addAction(QString(tr("Refresh")), this,
185				SLOT(ctx_refresh()));
186	}
187
188	menu->exec(peers->mapToGlobal(pos));
189}
190
191
192void Peers::enter_pin()
193{
194	if (ctx_item == NULL)
195		return;
196
197	int peer_type = ctx_item->data(peer_role_type).toInt();
198	QString uuid;
199	QString addr;
200	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
201		uuid = ctx_item->data(peer_role_uuid).toString();
202	else
203		addr = ctx_item->data(peer_role_address).toString();
204
205	StringQuery input(tr("PIN:"));
206	input.setWindowTitle(tr("PIN for ") + ctx_item->text());
207	if (input.exec() != QDialog::Accepted)
208		return;
209
210	char cmd[100];
211	char reply[100];
212	size_t reply_len;
213
214	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
215		snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
216			 uuid.toAscii().constData(),
217			 input.get_string().toAscii().constData());
218	} else {
219		snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
220			 addr.toAscii().constData(),
221			 input.get_string().toAscii().constData());
222	}
223	reply_len = sizeof(reply) - 1;
224	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
225		QMessageBox msg;
226		msg.setIcon(QMessageBox::Warning);
227		msg.setText(tr("Failed to set the WPS PIN."));
228		msg.exec();
229	}
230}
231
232
233void Peers::ctx_refresh()
234{
235	update_peers();
236}
237
238
239void Peers::add_station(QString info)
240{
241	QStringList lines = info.split(QRegExp("\\n"));
242	QString name;
243
244	for (QStringList::Iterator it = lines.begin();
245	     it != lines.end(); it++) {
246		int pos = (*it).indexOf('=') + 1;
247		if (pos < 1)
248			continue;
249
250		if ((*it).startsWith("wpsDeviceName="))
251			name = (*it).mid(pos);
252	}
253
254	if (name.isEmpty())
255		name = lines[0];
256
257	QStandardItem *item = new QStandardItem(*laptop_icon, name);
258	if (item) {
259		item->setData(lines[0], peer_role_address);
260		item->setData(PEER_TYPE_ASSOCIATED_STATION,
261			      peer_role_type);
262		item->setData(info, peer_role_details);
263		item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
264		model.appendRow(item);
265	}
266}
267
268
269void Peers::add_stations()
270{
271	char reply[2048];
272	size_t reply_len;
273	char cmd[30];
274	int res;
275
276	reply_len = sizeof(reply) - 1;
277	if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
278		return;
279
280	do {
281		reply[reply_len] = '\0';
282		QString info(reply);
283		char *txt = reply;
284		while (*txt != '\0' && *txt != '\n')
285			txt++;
286		*txt++ = '\0';
287		if (strncmp(reply, "FAIL", 4) == 0 ||
288		    strncmp(reply, "UNKNOWN", 7) == 0)
289			break;
290
291		add_station(info);
292
293		reply_len = sizeof(reply) - 1;
294		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
295		res = wpagui->ctrlRequest(cmd, reply, &reply_len);
296	} while (res >= 0);
297}
298
299
300void Peers::add_single_station(const char *addr)
301{
302	char reply[2048];
303	size_t reply_len;
304	char cmd[30];
305
306	reply_len = sizeof(reply) - 1;
307	snprintf(cmd, sizeof(cmd), "STA %s", addr);
308	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
309		return;
310
311	reply[reply_len] = '\0';
312	QString info(reply);
313	char *txt = reply;
314	while (*txt != '\0' && *txt != '\n')
315		txt++;
316	*txt++ = '\0';
317	if (strncmp(reply, "FAIL", 4) == 0 ||
318	    strncmp(reply, "UNKNOWN", 7) == 0)
319		return;
320
321	add_station(info);
322}
323
324
325void Peers::remove_bss(int id)
326{
327	if (model.rowCount() == 0)
328		return;
329
330	QModelIndexList lst = model.match(model.index(0, 0), peer_role_bss_id,
331					  id);
332	if (lst.size() == 0)
333		return;
334	model.removeRow(lst[0].row());
335}
336
337
338bool Peers::add_bss(const char *cmd)
339{
340	char reply[2048];
341	size_t reply_len;
342
343	reply_len = sizeof(reply) - 1;
344	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
345		return false;
346	reply[reply_len] = '\0';
347
348	QString bss(reply);
349	if (bss.isEmpty() || bss.startsWith("FAIL"))
350		return false;
351
352	QString ssid, bssid, flags, wps_name, pri_dev_type;
353	int id = -1;
354
355	QStringList lines = bss.split(QRegExp("\\n"));
356	for (QStringList::Iterator it = lines.begin();
357	     it != lines.end(); it++) {
358		int pos = (*it).indexOf('=') + 1;
359		if (pos < 1)
360			continue;
361
362		if ((*it).startsWith("bssid="))
363			bssid = (*it).mid(pos);
364		else if ((*it).startsWith("id="))
365			id = (*it).mid(pos).toInt();
366		else if ((*it).startsWith("flags="))
367			flags = (*it).mid(pos);
368		else if ((*it).startsWith("ssid="))
369			ssid = (*it).mid(pos);
370		else if ((*it).startsWith("wps_device_name="))
371			wps_name = (*it).mid(pos);
372		else if ((*it).startsWith("wps_primary_device_type="))
373			pri_dev_type = (*it).mid(pos);
374	}
375
376	QString name = wps_name;
377	if (name.isEmpty())
378		name = ssid + "\n" + bssid;
379
380	QStandardItem *item = new QStandardItem(*ap_icon, name);
381	if (item) {
382		item->setData(bssid, peer_role_address);
383		if (id >= 0)
384			item->setData(id, peer_role_bss_id);
385		int type;
386		if (flags.contains("[WPS"))
387			type = PEER_TYPE_AP_WPS;
388		else
389			type = PEER_TYPE_AP;
390		item->setData(type, peer_role_type);
391
392		for (int i = 0; i < lines.size(); i++) {
393			if (lines[i].length() > 60) {
394				lines[i].remove(60, lines[i].length());
395				lines[i] += "..";
396			}
397		}
398		item->setToolTip(ItemType(type));
399		item->setData(lines.join("\n"), peer_role_details);
400		if (!pri_dev_type.isEmpty())
401			item->setData(pri_dev_type,
402				      peer_role_pri_dev_type);
403		if (!ssid.isEmpty())
404			item->setData(ssid, peer_role_ssid);
405		model.appendRow(item);
406	}
407
408	return true;
409}
410
411
412void Peers::add_scan_results()
413{
414	int index;
415	char cmd[20];
416
417	index = 0;
418	while (wpagui) {
419		snprintf(cmd, sizeof(cmd), "BSS %d", index++);
420		if (index > 1000)
421			break;
422
423		if (!add_bss(cmd))
424			break;
425	}
426}
427
428
429void Peers::update_peers()
430{
431	model.clear();
432	if (wpagui == NULL)
433		return;
434
435	char reply[20];
436	size_t replylen = sizeof(reply) - 1;
437	wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
438
439	add_stations();
440	add_scan_results();
441}
442
443
444QStandardItem * Peers::find_addr(QString addr)
445{
446	if (model.rowCount() == 0)
447		return NULL;
448
449	QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
450					  addr);
451	if (lst.size() == 0)
452		return NULL;
453	return model.itemFromIndex(lst[0]);
454}
455
456
457QStandardItem * Peers::find_uuid(QString uuid)
458{
459	if (model.rowCount() == 0)
460		return NULL;
461
462	QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
463					  uuid);
464	if (lst.size() == 0)
465		return NULL;
466	return model.itemFromIndex(lst[0]);
467}
468
469
470void Peers::event_notify(WpaMsg msg)
471{
472	QString text = msg.getMsg();
473
474	if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
475		/*
476		 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
477		 * 02:2a:c4:18:5b:f3
478		 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
479		 */
480		QStringList items = text.split(' ');
481		QString uuid = items[1];
482		QString addr = items[2];
483		QString name = "";
484
485		QStandardItem *item = find_addr(addr);
486		if (item)
487			return;
488
489		int pos = text.indexOf('[');
490		if (pos >= 0) {
491			int pos2 = text.lastIndexOf(']');
492			if (pos2 >= pos) {
493				items = text.mid(pos + 1, pos2 - pos - 1).
494					split('|');
495				name = items[0];
496				items.append(addr);
497			}
498		}
499
500		item = new QStandardItem(*laptop_icon, name);
501		if (item) {
502			item->setData(addr, peer_role_address);
503			item->setData(PEER_TYPE_WPS_PIN_NEEDED,
504				      peer_role_type);
505			item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
506			item->setData(items.join("\n"), peer_role_details);
507			item->setData(items[5], peer_role_pri_dev_type);
508			model.appendRow(item);
509		}
510		return;
511	}
512
513	if (text.startsWith(AP_STA_CONNECTED)) {
514		/* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
515		QStringList items = text.split(' ');
516		QString addr = items[1];
517		QStandardItem *item = find_addr(addr);
518		if (item == NULL || item->data(peer_role_type).toInt() !=
519		    PEER_TYPE_ASSOCIATED_STATION)
520			add_single_station(addr.toAscii().constData());
521		return;
522	}
523
524	if (text.startsWith(AP_STA_DISCONNECTED)) {
525		/* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
526		QStringList items = text.split(' ');
527		QString addr = items[1];
528
529		if (model.rowCount() == 0)
530			return;
531
532		QModelIndexList lst = model.match(model.index(0, 0),
533						  peer_role_address, addr);
534		for (int i = 0; i < lst.size(); i++) {
535			QStandardItem *item = model.itemFromIndex(lst[i]);
536			if (item && item->data(peer_role_type).toInt() ==
537			    PEER_TYPE_ASSOCIATED_STATION)
538				model.removeRow(lst[i].row());
539		}
540		return;
541	}
542
543	if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
544		/*
545		 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
546		 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
547		 * |Very friendly name|Company|Long description of the model|
548		 * WAP|http://w1.fi/|http://w1.fi/hostapd/
549		 */
550		QStringList items = text.split(' ');
551		if (items.size() < 5)
552			return;
553		QString uuid = items[1];
554		QString addr = items[2];
555		QString pri_dev_type = items[3].mid(13);
556		int wps_state = items[4].mid(10).toInt();
557
558		int pos = text.indexOf('|');
559		if (pos < 0)
560			return;
561		items = text.mid(pos + 1).split('|');
562		if (items.size() < 1)
563			return;
564
565		QStandardItem *item = find_uuid(uuid);
566		if (item)
567			return;
568
569		item = new QStandardItem(*ap_icon, items[0]);
570		if (item) {
571			item->setData(uuid, peer_role_uuid);
572			item->setData(addr, peer_role_address);
573			int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
574				PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
575			item->setData(type, peer_role_type);
576			item->setToolTip(ItemType(type));
577			item->setData(pri_dev_type, peer_role_pri_dev_type);
578			item->setData(items.join(QString("\n")),
579				      peer_role_details);
580			model.appendRow(item);
581		}
582
583		return;
584	}
585
586	if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
587		/* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
588		QStringList items = text.split(' ');
589		if (items.size() < 2)
590			return;
591		if (model.rowCount() == 0)
592			return;
593
594		QModelIndexList lst = model.match(model.index(0, 0),
595						  peer_role_uuid, items[1]);
596		for (int i = 0; i < lst.size(); i++) {
597			QStandardItem *item = model.itemFromIndex(lst[i]);
598			if (item &&
599			    (item->data(peer_role_type).toInt() ==
600			     PEER_TYPE_WPS_ER_AP ||
601			     item->data(peer_role_type).toInt() ==
602			     PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
603				model.removeRow(lst[i].row());
604		}
605		return;
606	}
607
608	if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
609		/*
610		 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
611		 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
612		 * pri_dev_type=1-0050F204-1
613		 * |Wireless Client|Company|cmodel|123|12345|
614		 */
615		QStringList items = text.split(' ');
616		if (items.size() < 3)
617			return;
618		QString uuid = items[1];
619		QString addr = items[2];
620		QString pri_dev_type = items[6].mid(13);
621		int config_methods = -1;
622		int dev_passwd_id = -1;
623
624		for (int i = 3; i < items.size(); i++) {
625			int pos = items[i].indexOf('=') + 1;
626			if (pos < 1)
627				continue;
628			QString val = items[i].mid(pos);
629			if (items[i].startsWith("config_methods=")) {
630				config_methods = val.toInt(0, 0);
631			} else if (items[i].startsWith("dev_passwd_id=")) {
632				dev_passwd_id = val.toInt();
633			}
634		}
635
636		int pos = text.indexOf('|');
637		if (pos < 0)
638			return;
639		items = text.mid(pos + 1).split('|');
640		if (items.size() < 1)
641			return;
642		QString name = items[0];
643		if (name.length() == 0)
644			name = addr;
645
646		remove_enrollee_uuid(uuid);
647
648		QStandardItem *item;
649		item = new QStandardItem(*laptop_icon, name);
650		if (item) {
651			item->setData(uuid, peer_role_uuid);
652			item->setData(addr, peer_role_address);
653			item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
654				      peer_role_type);
655			item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
656			item->setData(items.join(QString("\n")),
657				      peer_role_details);
658			item->setData(pri_dev_type, peer_role_pri_dev_type);
659			if (config_methods >= 0)
660				item->setData(config_methods,
661					      peer_role_config_methods);
662			if (dev_passwd_id >= 0)
663				item->setData(dev_passwd_id,
664					      peer_role_dev_passwd_id);
665			model.appendRow(item);
666		}
667
668		return;
669	}
670
671	if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
672		/*
673		 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
674		 * 02:66:a0:ee:17:27
675		 */
676		QStringList items = text.split(' ');
677		if (items.size() < 2)
678			return;
679		remove_enrollee_uuid(items[1]);
680		return;
681	}
682
683	if (text.startsWith(WPS_EVENT_ENROLLEE_SEEN)) {
684		/* TODO: need to time out this somehow or remove on successful
685		 * WPS run, etc. */
686		/*
687		 * WPS-ENROLLEE-SEEN 02:00:00:00:01:00
688		 * 572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1
689		 * [Wireless Client]
690		 * (MAC addr, UUID-E, pri dev type, config methods,
691		 * dev passwd id, request type, [dev name])
692		 */
693		QStringList items = text.split(' ');
694		if (items.size() < 7)
695			return;
696		QString addr = items[1];
697		QString uuid = items[2];
698		QString pri_dev_type = items[3];
699		int config_methods = items[4].toInt(0, 0);
700		int dev_passwd_id = items[5].toInt();
701		QString name;
702
703		int pos = text.indexOf('[');
704		if (pos >= 0) {
705			int pos2 = text.lastIndexOf(']');
706			if (pos2 >= pos) {
707				QStringList items2 =
708					text.mid(pos + 1, pos2 - pos - 1).
709					split('|');
710				name = items2[0];
711			}
712		}
713		if (name.isEmpty())
714			name = addr;
715
716		QStandardItem *item;
717
718		item = find_uuid(uuid);
719		if (item) {
720			QVariant var = item->data(peer_role_config_methods);
721			QVariant var2 = item->data(peer_role_dev_passwd_id);
722			if ((var.isValid() && config_methods != var.toInt()) ||
723			    (var2.isValid() && dev_passwd_id != var2.toInt()))
724				remove_enrollee_uuid(uuid);
725			else
726				return;
727		}
728
729		item = new QStandardItem(*laptop_icon, name);
730		if (item) {
731			item->setData(uuid, peer_role_uuid);
732			item->setData(addr, peer_role_address);
733			item->setData(PEER_TYPE_WPS_ENROLLEE,
734				      peer_role_type);
735			item->setToolTip(ItemType(PEER_TYPE_WPS_ENROLLEE));
736			item->setData(items.join(QString("\n")),
737				      peer_role_details);
738			item->setData(pri_dev_type, peer_role_pri_dev_type);
739			item->setData(config_methods,
740				      peer_role_config_methods);
741			item->setData(dev_passwd_id, peer_role_dev_passwd_id);
742			model.appendRow(item);
743		}
744
745		return;
746	}
747
748	if (text.startsWith(WPA_EVENT_BSS_ADDED)) {
749		/* CTRL-EVENT-BSS-ADDED 34 00:11:22:33:44:55 */
750		QStringList items = text.split(' ');
751		if (items.size() < 2)
752			return;
753		char cmd[20];
754		snprintf(cmd, sizeof(cmd), "BSS ID-%d", items[1].toInt());
755		add_bss(cmd);
756		return;
757	}
758
759	if (text.startsWith(WPA_EVENT_BSS_REMOVED)) {
760		/* CTRL-EVENT-BSS-REMOVED 34 00:11:22:33:44:55 */
761		QStringList items = text.split(' ');
762		if (items.size() < 2)
763			return;
764		remove_bss(items[1].toInt());
765		return;
766	}
767}
768
769
770void Peers::closeEvent(QCloseEvent *)
771{
772	if (wpagui) {
773		char reply[20];
774		size_t replylen = sizeof(reply) - 1;
775		wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
776	}
777}
778
779
780void Peers::done(int r)
781{
782	QDialog::done(r);
783	close();
784}
785
786
787void Peers::remove_enrollee_uuid(QString uuid)
788{
789	if (model.rowCount() == 0)
790		return;
791
792	QModelIndexList lst = model.match(model.index(0, 0),
793					  peer_role_uuid, uuid);
794	for (int i = 0; i < lst.size(); i++) {
795		QStandardItem *item = model.itemFromIndex(lst[i]);
796		if (item == NULL)
797			continue;
798		int type = item->data(peer_role_type).toInt();
799		if (type == PEER_TYPE_WPS_ER_ENROLLEE ||
800		    type == PEER_TYPE_WPS_ENROLLEE)
801			model.removeRow(lst[i].row());
802	}
803}
804
805
806void Peers::properties()
807{
808	if (ctx_item == NULL)
809		return;
810
811	QMessageBox msg(this);
812	msg.setStandardButtons(QMessageBox::Ok);
813	msg.setDefaultButton(QMessageBox::Ok);
814	msg.setEscapeButton(QMessageBox::Ok);
815	msg.setWindowTitle(tr("Peer Properties"));
816
817	int type = ctx_item->data(peer_role_type).toInt();
818	QString title = Peers::ItemType(type);
819
820	msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
821
822	QVariant var;
823	QString info;
824
825	var = ctx_item->data(peer_role_address);
826	if (var.isValid())
827		info += tr("Address: ") + var.toString() + QString("\n");
828
829	var = ctx_item->data(peer_role_uuid);
830	if (var.isValid())
831		info += tr("UUID: ") + var.toString() + QString("\n");
832
833	var = ctx_item->data(peer_role_pri_dev_type);
834	if (var.isValid())
835		info += tr("Primary Device Type: ") + var.toString() +
836			QString("\n");
837
838	var = ctx_item->data(peer_role_ssid);
839	if (var.isValid())
840		info += tr("SSID: ") + var.toString() + QString("\n");
841
842	var = ctx_item->data(peer_role_config_methods);
843	if (var.isValid()) {
844		int methods = var.toInt();
845		info += tr("Configuration Methods: ");
846		if (methods & 0x0001)
847			info += tr("[USBA]");
848		if (methods & 0x0002)
849			info += tr("[Ethernet]");
850		if (methods & 0x0004)
851			info += tr("[Label]");
852		if (methods & 0x0008)
853			info += tr("[Display]");
854		if (methods & 0x0010)
855			info += tr("[Ext. NFC Token]");
856		if (methods & 0x0020)
857			info += tr("[Int. NFC Token]");
858		if (methods & 0x0040)
859			info += tr("[NFC Interface]");
860		if (methods & 0x0080)
861			info += tr("[Push Button]");
862		if (methods & 0x0100)
863			info += tr("[Keypad]");
864		info += "\n";
865	}
866
867	var = ctx_item->data(peer_role_dev_passwd_id);
868	if (var.isValid()) {
869		info += tr("Device Password ID: ") + var.toString();
870		switch (var.toInt()) {
871		case 0:
872			info += tr(" (Default PIN)");
873			break;
874		case 1:
875			info += tr(" (User-specified PIN)");
876			break;
877		case 2:
878			info += tr(" (Machine-specified PIN)");
879			break;
880		case 3:
881			info += tr(" (Rekey)");
882			break;
883		case 4:
884			info += tr(" (Push Button)");
885			break;
886		case 5:
887			info += tr(" (Registrar-specified)");
888			break;
889		}
890		info += "\n";
891	}
892
893	msg.setInformativeText(info);
894
895	var = ctx_item->data(peer_role_details);
896	if (var.isValid())
897		msg.setDetailedText(var.toString());
898
899	msg.exec();
900}
901
902
903void Peers::connect_pbc()
904{
905	if (ctx_item == NULL)
906		return;
907
908	char cmd[100];
909	char reply[100];
910	size_t reply_len;
911
912	int peer_type = ctx_item->data(peer_role_type).toInt();
913	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
914		snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
915			 ctx_item->data(peer_role_uuid).toString().toAscii().
916			 constData());
917	} else {
918		snprintf(cmd, sizeof(cmd), "WPS_PBC");
919	}
920	reply_len = sizeof(reply) - 1;
921	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
922		QMessageBox msg;
923		msg.setIcon(QMessageBox::Warning);
924		msg.setText(tr("Failed to start WPS PBC."));
925		msg.exec();
926	}
927}
928
929
930void Peers::learn_ap_config()
931{
932	if (ctx_item == NULL)
933		return;
934
935	QString uuid = ctx_item->data(peer_role_uuid).toString();
936
937	StringQuery input(tr("AP PIN:"));
938	input.setWindowTitle(tr("AP PIN for ") + ctx_item->text());
939	if (input.exec() != QDialog::Accepted)
940		return;
941
942	char cmd[100];
943	char reply[100];
944	size_t reply_len;
945
946	snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
947		 uuid.toAscii().constData(),
948		 input.get_string().toAscii().constData());
949	reply_len = sizeof(reply) - 1;
950	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
951		QMessageBox msg;
952		msg.setIcon(QMessageBox::Warning);
953		msg.setText(tr("Failed to start learning AP configuration."));
954		msg.exec();
955	}
956}
957