peers.cpp revision 8d520ff1dc2da35cdca849e982051b86468016d8
1/*
2 * wpa_gui - Peers class
3 * Copyright (c) 2009-2010, 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_ifname,
31	peer_role_pri_dev_type,
32	peer_role_ssid,
33	peer_role_config_methods,
34	peer_role_dev_passwd_id,
35	peer_role_bss_id,
36	peer_role_selected_method,
37	peer_role_selected_pin,
38	peer_role_requested_method,
39	peer_role_network_id
40};
41
42enum selected_method {
43	SEL_METHOD_NONE,
44	SEL_METHOD_PIN_PEER_DISPLAY,
45	SEL_METHOD_PIN_LOCAL_DISPLAY
46};
47
48/*
49 * TODO:
50 * - add current AP info (e.g., from WPS) in station mode
51 */
52
53enum peer_type {
54	PEER_TYPE_ASSOCIATED_STATION,
55	PEER_TYPE_AP,
56	PEER_TYPE_AP_WPS,
57	PEER_TYPE_WPS_PIN_NEEDED,
58	PEER_TYPE_P2P,
59	PEER_TYPE_P2P_CLIENT,
60	PEER_TYPE_P2P_GROUP,
61	PEER_TYPE_P2P_PERSISTENT_GROUP_GO,
62	PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT,
63	PEER_TYPE_P2P_INVITATION,
64	PEER_TYPE_WPS_ER_AP,
65	PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
66	PEER_TYPE_WPS_ER_ENROLLEE,
67	PEER_TYPE_WPS_ENROLLEE
68};
69
70
71Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
72	: QDialog(parent)
73{
74	setupUi(this);
75
76	if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
77	{
78		default_icon = new QIcon(":/icons/wpa_gui.svg");
79		ap_icon = new QIcon(":/icons/ap.svg");
80		laptop_icon = new QIcon(":/icons/laptop.svg");
81		group_icon = new QIcon(":/icons/group.svg");
82		invitation_icon = new QIcon(":/icons/invitation.svg");
83	} else {
84		default_icon = new QIcon(":/icons/wpa_gui.png");
85		ap_icon = new QIcon(":/icons/ap.png");
86		laptop_icon = new QIcon(":/icons/laptop.png");
87		group_icon = new QIcon(":/icons/group.png");
88		invitation_icon = new QIcon(":/icons/invitation.png");
89	}
90
91	peers->setModel(&model);
92	peers->setResizeMode(QListView::Adjust);
93	peers->setDragEnabled(false);
94	peers->setSelectionMode(QAbstractItemView::NoSelection);
95
96	peers->setContextMenuPolicy(Qt::CustomContextMenu);
97	connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
98		this, SLOT(context_menu(const QPoint &)));
99
100	wpagui = NULL;
101	hide_ap = false;
102}
103
104
105void Peers::setWpaGui(WpaGui *_wpagui)
106{
107	wpagui = _wpagui;
108	update_peers();
109}
110
111
112Peers::~Peers()
113{
114	delete default_icon;
115	delete ap_icon;
116	delete laptop_icon;
117	delete group_icon;
118	delete invitation_icon;
119}
120
121
122void Peers::languageChange()
123{
124	retranslateUi(this);
125}
126
127
128QString Peers::ItemType(int type)
129{
130	QString title;
131	switch (type) {
132	case PEER_TYPE_ASSOCIATED_STATION:
133		title = tr("Associated station");
134		break;
135	case PEER_TYPE_AP:
136		title = tr("AP");
137		break;
138	case PEER_TYPE_AP_WPS:
139		title = tr("WPS AP");
140		break;
141	case PEER_TYPE_WPS_PIN_NEEDED:
142		title = tr("WPS PIN needed");
143		break;
144	case PEER_TYPE_P2P:
145		title = tr("P2P Device");
146		break;
147	case PEER_TYPE_P2P_CLIENT:
148		title = tr("P2P Device (group client)");
149		break;
150	case PEER_TYPE_P2P_GROUP:
151		title = tr("P2P Group");
152		break;
153	case PEER_TYPE_P2P_PERSISTENT_GROUP_GO:
154		title = tr("P2P Persistent Group (GO)");
155		break;
156	case PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT:
157		title = tr("P2P Persistent Group (client)");
158		break;
159	case PEER_TYPE_P2P_INVITATION:
160		title = tr("P2P Invitation");
161		break;
162	case PEER_TYPE_WPS_ER_AP:
163		title = tr("ER: WPS AP");
164		break;
165	case PEER_TYPE_WPS_ER_AP_UNCONFIGURED:
166		title = tr("ER: WPS AP (Unconfigured)");
167		break;
168	case PEER_TYPE_WPS_ER_ENROLLEE:
169		title = tr("ER: WPS Enrollee");
170		break;
171	case PEER_TYPE_WPS_ENROLLEE:
172		title = tr("WPS Enrollee");
173		break;
174	}
175	return title;
176}
177
178
179void Peers::context_menu(const QPoint &pos)
180{
181	QMenu *menu = new QMenu;
182	if (menu == NULL)
183		return;
184
185	QModelIndex idx = peers->indexAt(pos);
186	if (idx.isValid()) {
187		ctx_item = model.itemFromIndex(idx);
188		int type = ctx_item->data(peer_role_type).toInt();
189		menu->addAction(Peers::ItemType(type))->setEnabled(false);
190		menu->addSeparator();
191
192		int config_methods = -1;
193		QVariant var = ctx_item->data(peer_role_config_methods);
194		if (var.isValid())
195			config_methods = var.toInt();
196
197		enum selected_method method = SEL_METHOD_NONE;
198		var = ctx_item->data(peer_role_selected_method);
199		if (var.isValid())
200			method = (enum selected_method) var.toInt();
201
202		if ((type == PEER_TYPE_ASSOCIATED_STATION ||
203		     type == PEER_TYPE_AP_WPS ||
204		     type == PEER_TYPE_WPS_PIN_NEEDED ||
205		     type == PEER_TYPE_WPS_ER_ENROLLEE ||
206		     type == PEER_TYPE_WPS_ENROLLEE) &&
207		    (config_methods == -1 || (config_methods & 0x010c))) {
208			menu->addAction(tr("Enter WPS PIN"), this,
209					SLOT(enter_pin()));
210		}
211
212		if (type == PEER_TYPE_P2P || type == PEER_TYPE_P2P_CLIENT) {
213			menu->addAction(tr("P2P Connect"), this,
214					SLOT(ctx_p2p_connect()));
215			if (method == SEL_METHOD_NONE &&
216			    config_methods > -1 &&
217			    config_methods & 0x0080 /* PBC */ &&
218			    config_methods != 0x0080)
219				menu->addAction(tr("P2P Connect (PBC)"), this,
220						SLOT(connect_pbc()));
221			if (method == SEL_METHOD_NONE) {
222				menu->addAction(tr("P2P Request PIN"), this,
223						SLOT(ctx_p2p_req_pin()));
224				menu->addAction(tr("P2P Show PIN"), this,
225						SLOT(ctx_p2p_show_pin()));
226			}
227
228			if (config_methods > -1 && (config_methods & 0x0100)) {
229				/* Peer has Keypad */
230				menu->addAction(tr("P2P Display PIN"), this,
231						SLOT(ctx_p2p_display_pin()));
232			}
233
234			if (config_methods > -1 && (config_methods & 0x000c)) {
235				/* Peer has Label or Display */
236				menu->addAction(tr("P2P Enter PIN"), this,
237						SLOT(ctx_p2p_enter_pin()));
238			}
239		}
240
241		if (type == PEER_TYPE_P2P_GROUP) {
242			menu->addAction(tr("Show passphrase"), this,
243					SLOT(ctx_p2p_show_passphrase()));
244			menu->addAction(tr("Remove P2P Group"), this,
245					SLOT(ctx_p2p_remove_group()));
246		}
247
248		if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
249		    type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT ||
250		    type == PEER_TYPE_P2P_INVITATION) {
251			menu->addAction(tr("Start group"), this,
252					SLOT(ctx_p2p_start_persistent()));
253		}
254
255		if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
256		    type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) {
257			menu->addAction(tr("Invite"), this,
258					SLOT(ctx_p2p_invite()));
259		}
260
261		if (type == PEER_TYPE_P2P_INVITATION) {
262			menu->addAction(tr("Ignore"), this,
263					SLOT(ctx_p2p_delete()));
264		}
265
266		if (type == PEER_TYPE_AP_WPS) {
267			menu->addAction(tr("Connect (PBC)"), this,
268					SLOT(connect_pbc()));
269		}
270
271		if ((type == PEER_TYPE_ASSOCIATED_STATION ||
272		     type == PEER_TYPE_WPS_ER_ENROLLEE ||
273		     type == PEER_TYPE_WPS_ENROLLEE) &&
274		    config_methods >= 0 && (config_methods & 0x0080)) {
275			menu->addAction(tr("Enroll (PBC)"), this,
276					SLOT(connect_pbc()));
277		}
278
279		if (type == PEER_TYPE_WPS_ER_AP) {
280			menu->addAction(tr("Learn Configuration"), this,
281					SLOT(learn_ap_config()));
282		}
283
284		menu->addAction(tr("Properties"), this, SLOT(properties()));
285	} else {
286		ctx_item = NULL;
287		menu->addAction(QString(tr("Refresh")), this,
288				SLOT(ctx_refresh()));
289		menu->addAction(tr("Start P2P discovery"), this,
290				SLOT(ctx_p2p_start()));
291		menu->addAction(tr("Stop P2P discovery"), this,
292				SLOT(ctx_p2p_stop()));
293		menu->addAction(tr("P2P listen only"), this,
294				SLOT(ctx_p2p_listen()));
295		menu->addAction(tr("Start P2P group"), this,
296				SLOT(ctx_p2p_start_group()));
297		if (hide_ap)
298			menu->addAction(tr("Show AP entries"), this,
299					SLOT(ctx_show_ap()));
300		else
301			menu->addAction(tr("Hide AP entries"), this,
302					SLOT(ctx_hide_ap()));
303	}
304
305	menu->exec(peers->mapToGlobal(pos));
306}
307
308
309void Peers::enter_pin()
310{
311	if (ctx_item == NULL)
312		return;
313
314	int peer_type = ctx_item->data(peer_role_type).toInt();
315	QString uuid;
316	QString addr;
317	addr = ctx_item->data(peer_role_address).toString();
318	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
319		uuid = ctx_item->data(peer_role_uuid).toString();
320
321	StringQuery input(tr("PIN:"));
322	input.setWindowTitle(tr("PIN for ") + ctx_item->text());
323	if (input.exec() != QDialog::Accepted)
324		return;
325
326	char cmd[100];
327	char reply[100];
328	size_t reply_len;
329
330	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
331		snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s",
332			 uuid.toAscii().constData(),
333			 input.get_string().toAscii().constData(),
334			 addr.toAscii().constData());
335	} else {
336		snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
337			 addr.toAscii().constData(),
338			 input.get_string().toAscii().constData());
339	}
340	reply_len = sizeof(reply) - 1;
341	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
342		QMessageBox msg;
343		msg.setIcon(QMessageBox::Warning);
344		msg.setText(tr("Failed to set the WPS PIN."));
345		msg.exec();
346	}
347}
348
349
350void Peers::ctx_refresh()
351{
352	update_peers();
353}
354
355
356void Peers::ctx_p2p_start()
357{
358	char reply[20];
359	size_t reply_len;
360	reply_len = sizeof(reply) - 1;
361	if (wpagui->ctrlRequest("P2P_FIND", reply, &reply_len) < 0 ||
362	    memcmp(reply, "FAIL", 4) == 0) {
363		QMessageBox msg;
364		msg.setIcon(QMessageBox::Warning);
365		msg.setText("Failed to start P2P discovery.");
366		msg.exec();
367	}
368}
369
370
371void Peers::ctx_p2p_stop()
372{
373	char reply[20];
374	size_t reply_len;
375	reply_len = sizeof(reply) - 1;
376	wpagui->ctrlRequest("P2P_STOP_FIND", reply, &reply_len);
377}
378
379
380void Peers::ctx_p2p_listen()
381{
382	char reply[20];
383	size_t reply_len;
384	reply_len = sizeof(reply) - 1;
385	if (wpagui->ctrlRequest("P2P_LISTEN 3600", reply, &reply_len) < 0 ||
386	    memcmp(reply, "FAIL", 4) == 0) {
387		QMessageBox msg;
388		msg.setIcon(QMessageBox::Warning);
389		msg.setText("Failed to start P2P listen.");
390		msg.exec();
391	}
392}
393
394
395void Peers::ctx_p2p_start_group()
396{
397	char reply[20];
398	size_t reply_len;
399	reply_len = sizeof(reply) - 1;
400	if (wpagui->ctrlRequest("P2P_GROUP_ADD", reply, &reply_len) < 0 ||
401	    memcmp(reply, "FAIL", 4) == 0) {
402		QMessageBox msg;
403		msg.setIcon(QMessageBox::Warning);
404		msg.setText("Failed to start P2P group.");
405		msg.exec();
406	}
407}
408
409
410void Peers::add_station(QString info)
411{
412	QStringList lines = info.split(QRegExp("\\n"));
413	QString name;
414
415	for (QStringList::Iterator it = lines.begin();
416	     it != lines.end(); it++) {
417		int pos = (*it).indexOf('=') + 1;
418		if (pos < 1)
419			continue;
420
421		if ((*it).startsWith("wpsDeviceName="))
422			name = (*it).mid(pos);
423		else if ((*it).startsWith("p2p_device_name="))
424			name = (*it).mid(pos);
425	}
426
427	if (name.isEmpty())
428		name = lines[0];
429
430	QStandardItem *item = new QStandardItem(*laptop_icon, name);
431	if (item) {
432		/* Remove WPS enrollee entry if one is still pending */
433		if (model.rowCount() > 0) {
434			QModelIndexList lst = model.match(model.index(0, 0),
435							  peer_role_address,
436							  lines[0]);
437			for (int i = 0; i < lst.size(); i++) {
438				QStandardItem *item;
439				item = model.itemFromIndex(lst[i]);
440				if (item == NULL)
441					continue;
442				int type = item->data(peer_role_type).toInt();
443				if (type == PEER_TYPE_WPS_ENROLLEE) {
444					model.removeRow(lst[i].row());
445					break;
446				}
447			}
448		}
449
450		item->setData(lines[0], peer_role_address);
451		item->setData(PEER_TYPE_ASSOCIATED_STATION,
452			      peer_role_type);
453		item->setData(info, peer_role_details);
454		item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
455		model.appendRow(item);
456	}
457}
458
459
460void Peers::add_stations()
461{
462	char reply[2048];
463	size_t reply_len;
464	char cmd[30];
465	int res;
466
467	reply_len = sizeof(reply) - 1;
468	if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
469		return;
470
471	do {
472		reply[reply_len] = '\0';
473		QString info(reply);
474		char *txt = reply;
475		while (*txt != '\0' && *txt != '\n')
476			txt++;
477		*txt++ = '\0';
478		if (strncmp(reply, "FAIL", 4) == 0 ||
479		    strncmp(reply, "UNKNOWN", 7) == 0)
480			break;
481
482		add_station(info);
483
484		reply_len = sizeof(reply) - 1;
485		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
486		res = wpagui->ctrlRequest(cmd, reply, &reply_len);
487	} while (res >= 0);
488}
489
490
491void Peers::add_single_station(const char *addr)
492{
493	char reply[2048];
494	size_t reply_len;
495	char cmd[30];
496
497	reply_len = sizeof(reply) - 1;
498	snprintf(cmd, sizeof(cmd), "STA %s", addr);
499	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
500		return;
501
502	reply[reply_len] = '\0';
503	QString info(reply);
504	char *txt = reply;
505	while (*txt != '\0' && *txt != '\n')
506		txt++;
507	*txt++ = '\0';
508	if (strncmp(reply, "FAIL", 4) == 0 ||
509	    strncmp(reply, "UNKNOWN", 7) == 0)
510		return;
511
512	add_station(info);
513}
514
515
516void Peers::add_p2p_group_client(QStandardItem * /*parent*/, QString params)
517{
518	/*
519	 * dev=02:b5:64:63:30:63 iface=02:b5:64:63:30:63 dev_capab=0x0
520	 * dev_type=1-0050f204-1 dev_name='Wireless Client'
521	 * config_methods=0x8c
522	 */
523
524	QStringList items =
525		params.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
526	QString addr = "";
527	QString name = "";
528	int config_methods = 0;
529	QString dev_type;
530
531	for (int i = 0; i < items.size(); i++) {
532		QString str = items.at(i);
533		int pos = str.indexOf('=') + 1;
534		if (str.startsWith("dev_name='"))
535			name = str.section('\'', 1, -2);
536		else if (str.startsWith("config_methods="))
537			config_methods =
538				str.section('=', 1).toInt(0, 0);
539		else if (str.startsWith("dev="))
540			addr = str.mid(pos);
541		else if (str.startsWith("dev_type=") && dev_type.isEmpty())
542			dev_type = str.mid(pos);
543	}
544
545	QStandardItem *item = find_addr(addr);
546	if (item)
547		return;
548
549	item = new QStandardItem(*default_icon, name);
550	if (item) {
551		/* TODO: indicate somehow the relationship to the group owner
552		 * (parent) */
553		item->setData(addr, peer_role_address);
554		item->setData(config_methods, peer_role_config_methods);
555		item->setData(PEER_TYPE_P2P_CLIENT, peer_role_type);
556		if (!dev_type.isEmpty())
557			item->setData(dev_type, peer_role_pri_dev_type);
558		item->setData(items.join(QString("\n")), peer_role_details);
559		item->setToolTip(ItemType(PEER_TYPE_P2P_CLIENT));
560		model.appendRow(item);
561	}
562}
563
564
565void Peers::remove_bss(int id)
566{
567	if (model.rowCount() == 0)
568		return;
569
570	QModelIndexList lst = model.match(model.index(0, 0), peer_role_bss_id,
571					  id);
572	if (lst.size() == 0)
573		return;
574	model.removeRow(lst[0].row());
575}
576
577
578bool Peers::add_bss(const char *cmd)
579{
580	char reply[2048];
581	size_t reply_len;
582
583	if (hide_ap)
584		return false;
585
586	reply_len = sizeof(reply) - 1;
587	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
588		return false;
589	reply[reply_len] = '\0';
590
591	QString bss(reply);
592	if (bss.isEmpty() || bss.startsWith("FAIL"))
593		return false;
594
595	QString ssid, bssid, flags, wps_name, pri_dev_type;
596	int id = -1;
597
598	QStringList lines = bss.split(QRegExp("\\n"));
599	for (QStringList::Iterator it = lines.begin();
600	     it != lines.end(); it++) {
601		int pos = (*it).indexOf('=') + 1;
602		if (pos < 1)
603			continue;
604
605		if ((*it).startsWith("bssid="))
606			bssid = (*it).mid(pos);
607		else if ((*it).startsWith("id="))
608			id = (*it).mid(pos).toInt();
609		else if ((*it).startsWith("flags="))
610			flags = (*it).mid(pos);
611		else if ((*it).startsWith("ssid="))
612			ssid = (*it).mid(pos);
613		else if ((*it).startsWith("wps_device_name="))
614			wps_name = (*it).mid(pos);
615		else if ((*it).startsWith("wps_primary_device_type="))
616			pri_dev_type = (*it).mid(pos);
617	}
618
619	QString name = wps_name;
620	if (name.isEmpty())
621		name = ssid + "\n" + bssid;
622
623	QStandardItem *item = new QStandardItem(*ap_icon, name);
624	if (item) {
625		item->setData(bssid, peer_role_address);
626		if (id >= 0)
627			item->setData(id, peer_role_bss_id);
628		int type;
629		if (flags.contains("[WPS"))
630			type = PEER_TYPE_AP_WPS;
631		else
632			type = PEER_TYPE_AP;
633		item->setData(type, peer_role_type);
634
635		for (int i = 0; i < lines.size(); i++) {
636			if (lines[i].length() > 60) {
637				lines[i].remove(60, lines[i].length());
638				lines[i] += "..";
639			}
640		}
641		item->setToolTip(ItemType(type));
642		item->setData(lines.join("\n"), peer_role_details);
643		if (!pri_dev_type.isEmpty())
644			item->setData(pri_dev_type,
645				      peer_role_pri_dev_type);
646		if (!ssid.isEmpty())
647			item->setData(ssid, peer_role_ssid);
648		model.appendRow(item);
649
650		lines = bss.split(QRegExp("\\n"));
651		for (QStringList::Iterator it = lines.begin();
652		     it != lines.end(); it++) {
653			if ((*it).startsWith("p2p_group_client:"))
654				add_p2p_group_client(item,
655						     (*it).mid(18));
656		}
657	}
658
659	return true;
660}
661
662
663void Peers::add_scan_results()
664{
665	int index;
666	char cmd[20];
667
668	index = 0;
669	while (wpagui) {
670		snprintf(cmd, sizeof(cmd), "BSS %d", index++);
671		if (index > 1000)
672			break;
673
674		if (!add_bss(cmd))
675			break;
676	}
677}
678
679
680void Peers::add_persistent(int id, const char *ssid, const char *bssid)
681{
682	char cmd[100];
683	char reply[100];
684	size_t reply_len;
685	int mode;
686
687	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d mode", id);
688	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
689		return;
690	reply[reply_len] = '\0';
691	mode = atoi(reply);
692
693	QString name = ssid;
694	name = '[' + name + ']';
695
696	QStandardItem *item = new QStandardItem(*group_icon, name);
697	if (!item)
698		return;
699
700	int type;
701	if (mode == 3)
702		type = PEER_TYPE_P2P_PERSISTENT_GROUP_GO;
703	else
704		type = PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT;
705	item->setData(type, peer_role_type);
706	item->setToolTip(ItemType(type));
707	item->setData(ssid, peer_role_ssid);
708	if (bssid && strcmp(bssid, "any") == 0)
709		bssid = NULL;
710	if (bssid)
711		item->setData(bssid, peer_role_address);
712	item->setData(id, peer_role_network_id);
713	item->setBackground(Qt::BDiagPattern);
714
715	model.appendRow(item);
716}
717
718
719void Peers::add_persistent_groups()
720{
721	char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
722	size_t len;
723
724	len = sizeof(buf) - 1;
725	if (wpagui->ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
726		return;
727
728	buf[len] = '\0';
729	start = strchr(buf, '\n');
730	if (start == NULL)
731		return;
732	start++;
733
734	while (*start) {
735		bool last = false;
736		end = strchr(start, '\n');
737		if (end == NULL) {
738			last = true;
739			end = start;
740			while (end[0] && end[1])
741				end++;
742		}
743		*end = '\0';
744
745		id = start;
746		ssid = strchr(id, '\t');
747		if (ssid == NULL)
748			break;
749		*ssid++ = '\0';
750		bssid = strchr(ssid, '\t');
751		if (bssid == NULL)
752			break;
753		*bssid++ = '\0';
754		flags = strchr(bssid, '\t');
755		if (flags == NULL)
756			break;
757		*flags++ = '\0';
758
759		if (strstr(flags, "[DISABLED][P2P-PERSISTENT]"))
760			add_persistent(atoi(id), ssid, bssid);
761
762		if (last)
763			break;
764		start = end + 1;
765	}
766}
767
768
769void Peers::update_peers()
770{
771	model.clear();
772	if (wpagui == NULL)
773		return;
774
775	char reply[20];
776	size_t replylen = sizeof(reply) - 1;
777	wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
778
779	add_stations();
780	add_scan_results();
781	add_persistent_groups();
782}
783
784
785QStandardItem * Peers::find_addr(QString addr)
786{
787	if (model.rowCount() == 0)
788		return NULL;
789
790	QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
791					  addr);
792	if (lst.size() == 0)
793		return NULL;
794	return model.itemFromIndex(lst[0]);
795}
796
797
798QStandardItem * Peers::find_addr_type(QString addr, int type)
799{
800	if (model.rowCount() == 0)
801		return NULL;
802
803	QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
804					  addr);
805	for (int i = 0; i < lst.size(); i++) {
806		QStandardItem *item = model.itemFromIndex(lst[i]);
807		if (item->data(peer_role_type).toInt() == type)
808			return item;
809	}
810	return NULL;
811}
812
813
814QStandardItem * Peers::find_uuid(QString uuid)
815{
816	if (model.rowCount() == 0)
817		return NULL;
818
819	QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
820					  uuid);
821	if (lst.size() == 0)
822		return NULL;
823	return model.itemFromIndex(lst[0]);
824}
825
826
827void Peers::event_notify(WpaMsg msg)
828{
829	QString text = msg.getMsg();
830
831	if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
832		/*
833		 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
834		 * 02:2a:c4:18:5b:f3
835		 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
836		 */
837		QStringList items = text.split(' ');
838		QString uuid = items[1];
839		QString addr = items[2];
840		QString name = "";
841
842		QStandardItem *item = find_addr(addr);
843		if (item)
844			return;
845
846		int pos = text.indexOf('[');
847		if (pos >= 0) {
848			int pos2 = text.lastIndexOf(']');
849			if (pos2 >= pos) {
850				items = text.mid(pos + 1, pos2 - pos - 1).
851					split('|');
852				name = items[0];
853				items.append(addr);
854			}
855		}
856
857		item = new QStandardItem(*laptop_icon, name);
858		if (item) {
859			item->setData(addr, peer_role_address);
860			item->setData(PEER_TYPE_WPS_PIN_NEEDED,
861				      peer_role_type);
862			item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
863			item->setData(items.join("\n"), peer_role_details);
864			item->setData(items[5], peer_role_pri_dev_type);
865			model.appendRow(item);
866		}
867		return;
868	}
869
870	if (text.startsWith(AP_STA_CONNECTED)) {
871		/* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
872		QStringList items = text.split(' ');
873		QString addr = items[1];
874		QStandardItem *item = find_addr(addr);
875		if (item == NULL || item->data(peer_role_type).toInt() !=
876		    PEER_TYPE_ASSOCIATED_STATION)
877			add_single_station(addr.toAscii().constData());
878		return;
879	}
880
881	if (text.startsWith(AP_STA_DISCONNECTED)) {
882		/* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
883		QStringList items = text.split(' ');
884		QString addr = items[1];
885
886		if (model.rowCount() == 0)
887			return;
888
889		QModelIndexList lst = model.match(model.index(0, 0),
890						  peer_role_address, addr, -1);
891		for (int i = 0; i < lst.size(); i++) {
892			QStandardItem *item = model.itemFromIndex(lst[i]);
893			if (item && item->data(peer_role_type).toInt() ==
894			    PEER_TYPE_ASSOCIATED_STATION) {
895				model.removeRow(lst[i].row());
896				break;
897			}
898		}
899		return;
900	}
901
902	if (text.startsWith(P2P_EVENT_DEVICE_FOUND)) {
903		/*
904		 * P2P-DEVICE-FOUND 02:b5:64:63:30:63
905		 * p2p_dev_addr=02:b5:64:63:30:63 pri_dev_type=1-0050f204-1
906		 * name='Wireless Client' config_methods=0x84 dev_capab=0x21
907		 * group_capab=0x0
908		 */
909		QStringList items =
910			text.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
911		QString addr = items[1];
912		QString name = "";
913		QString pri_dev_type;
914		int config_methods = 0;
915		for (int i = 0; i < items.size(); i++) {
916			QString str = items.at(i);
917			if (str.startsWith("name='"))
918				name = str.section('\'', 1, -2);
919			else if (str.startsWith("config_methods="))
920				config_methods =
921					str.section('=', 1).toInt(0, 0);
922			else if (str.startsWith("pri_dev_type="))
923				pri_dev_type = str.section('=', 1);
924		}
925
926		QStandardItem *item = find_addr(addr);
927		if (item) {
928			int type = item->data(peer_role_type).toInt();
929			if (type == PEER_TYPE_P2P)
930				return;
931		}
932
933		item = new QStandardItem(*default_icon, name);
934		if (item) {
935			item->setData(addr, peer_role_address);
936			item->setData(config_methods,
937				      peer_role_config_methods);
938			item->setData(PEER_TYPE_P2P, peer_role_type);
939			if (!pri_dev_type.isEmpty())
940				item->setData(pri_dev_type,
941					      peer_role_pri_dev_type);
942			item->setData(items.join(QString("\n")),
943				      peer_role_details);
944			item->setToolTip(ItemType(PEER_TYPE_P2P));
945			model.appendRow(item);
946		}
947
948		item = find_addr_type(addr,
949				      PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT);
950		if (item)
951			item->setBackground(Qt::NoBrush);
952	}
953
954	if (text.startsWith(P2P_EVENT_GROUP_STARTED)) {
955		/* P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F"
956		 * passphrase="YOyTkxID" go_dev_addr=02:40:61:c2:f3:b7
957		 * [PERSISTENT] */
958		QStringList items = text.split(' ');
959		if (items.size() < 4)
960			return;
961
962		int pos = text.indexOf(" ssid=\"");
963		if (pos < 0)
964			return;
965		QString ssid = text.mid(pos + 7);
966		pos = ssid.indexOf(" passphrase=\"");
967		if (pos < 0)
968			pos = ssid.indexOf(" psk=");
969		if (pos >= 0)
970			ssid.truncate(pos);
971		pos = ssid.lastIndexOf('"');
972		if (pos >= 0)
973			ssid.truncate(pos);
974
975		QStandardItem *item = new QStandardItem(*group_icon, ssid);
976		if (item) {
977			item->setData(PEER_TYPE_P2P_GROUP, peer_role_type);
978			item->setData(items[1], peer_role_ifname);
979			QString details;
980			if (items[2] == "GO") {
981				details = tr("P2P GO for interface ") +
982					items[1];
983			} else {
984				details = tr("P2P client for interface ") +
985					items[1];
986			}
987			if (text.contains(" [PERSISTENT]"))
988				details += "\nPersistent group";
989			item->setData(details, peer_role_details);
990			item->setToolTip(ItemType(PEER_TYPE_P2P_GROUP));
991			model.appendRow(item);
992		}
993	}
994
995	if (text.startsWith(P2P_EVENT_GROUP_REMOVED)) {
996		/* P2P-GROUP-REMOVED wlan0-p2p-0 GO */
997		QStringList items = text.split(' ');
998		if (items.size() < 2)
999			return;
1000
1001		if (model.rowCount() == 0)
1002			return;
1003
1004		QModelIndexList lst = model.match(model.index(0, 0),
1005						  peer_role_ifname, items[1]);
1006		for (int i = 0; i < lst.size(); i++)
1007			model.removeRow(lst[i].row());
1008		return;
1009	}
1010
1011	if (text.startsWith(P2P_EVENT_PROV_DISC_SHOW_PIN)) {
1012		/* P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670 */
1013		QStringList items = text.split(' ');
1014		if (items.size() < 3)
1015			return;
1016		QString addr = items[1];
1017		QString pin = items[2];
1018
1019		QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
1020		if (item == NULL)
1021			return;
1022		item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
1023			      peer_role_selected_method);
1024		item->setData(pin, peer_role_selected_pin);
1025		QVariant var = item->data(peer_role_requested_method);
1026		if (var.isValid() &&
1027		    var.toInt() == SEL_METHOD_PIN_LOCAL_DISPLAY) {
1028			ctx_item = item;
1029			ctx_p2p_display_pin_pd();
1030		}
1031		return;
1032	}
1033
1034	if (text.startsWith(P2P_EVENT_PROV_DISC_ENTER_PIN)) {
1035		/* P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 */
1036		QStringList items = text.split(' ');
1037		if (items.size() < 2)
1038			return;
1039		QString addr = items[1];
1040
1041		QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
1042		if (item == NULL)
1043			return;
1044		item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
1045			      peer_role_selected_method);
1046		QVariant var = item->data(peer_role_requested_method);
1047		if (var.isValid() &&
1048		    var.toInt() == SEL_METHOD_PIN_PEER_DISPLAY) {
1049			ctx_item = item;
1050			ctx_p2p_connect();
1051		}
1052		return;
1053	}
1054
1055	if (text.startsWith(P2P_EVENT_INVITATION_RECEIVED)) {
1056		/* P2P-INVITATION-RECEIVED sa=02:f0:bc:44:87:62 persistent=4 */
1057		QStringList items = text.split(' ');
1058		if (items.size() < 3)
1059			return;
1060		if (!items[1].startsWith("sa=") ||
1061		    !items[2].startsWith("persistent="))
1062			return;
1063		QString addr = items[1].mid(3);
1064		int id = items[2].mid(11).toInt();
1065
1066		char cmd[100];
1067		char reply[100];
1068		size_t reply_len;
1069
1070		snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", id);
1071		reply_len = sizeof(reply) - 1;
1072		if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
1073			return;
1074		reply[reply_len] = '\0';
1075		QString name;
1076		char *pos = strrchr(reply, '"');
1077		if (pos && reply[0] == '"') {
1078			*pos = '\0';
1079			name = reply + 1;
1080		} else
1081			name = reply;
1082
1083		QStandardItem *item;
1084		item = find_addr_type(addr, PEER_TYPE_P2P_INVITATION);
1085		if (item)
1086			model.removeRow(item->row());
1087
1088		item = new QStandardItem(*invitation_icon, name);
1089		if (!item)
1090			return;
1091		item->setData(PEER_TYPE_P2P_INVITATION, peer_role_type);
1092		item->setToolTip(ItemType(PEER_TYPE_P2P_INVITATION));
1093		item->setData(addr, peer_role_address);
1094		item->setData(id, peer_role_network_id);
1095
1096		model.appendRow(item);
1097
1098		enable_persistent(id);
1099
1100		return;
1101	}
1102
1103	if (text.startsWith(P2P_EVENT_INVITATION_RESULT)) {
1104		/* P2P-INVITATION-RESULT status=1 */
1105		/* TODO */
1106		return;
1107	}
1108
1109	if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
1110		/*
1111		 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
1112		 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
1113		 * |Very friendly name|Company|Long description of the model|
1114		 * WAP|http://w1.fi/|http://w1.fi/hostapd/
1115		 */
1116		QStringList items = text.split(' ');
1117		if (items.size() < 5)
1118			return;
1119		QString uuid = items[1];
1120		QString addr = items[2];
1121		QString pri_dev_type = items[3].mid(13);
1122		int wps_state = items[4].mid(10).toInt();
1123
1124		int pos = text.indexOf('|');
1125		if (pos < 0)
1126			return;
1127		items = text.mid(pos + 1).split('|');
1128		if (items.size() < 1)
1129			return;
1130
1131		QStandardItem *item = find_uuid(uuid);
1132		if (item)
1133			return;
1134
1135		item = new QStandardItem(*ap_icon, items[0]);
1136		if (item) {
1137			item->setData(uuid, peer_role_uuid);
1138			item->setData(addr, peer_role_address);
1139			int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
1140				PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
1141			item->setData(type, peer_role_type);
1142			item->setToolTip(ItemType(type));
1143			item->setData(pri_dev_type, peer_role_pri_dev_type);
1144			item->setData(items.join(QString("\n")),
1145				      peer_role_details);
1146			model.appendRow(item);
1147		}
1148
1149		return;
1150	}
1151
1152	if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
1153		/* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
1154		QStringList items = text.split(' ');
1155		if (items.size() < 2)
1156			return;
1157		if (model.rowCount() == 0)
1158			return;
1159
1160		QModelIndexList lst = model.match(model.index(0, 0),
1161						  peer_role_uuid, items[1]);
1162		for (int i = 0; i < lst.size(); i++) {
1163			QStandardItem *item = model.itemFromIndex(lst[i]);
1164			if (item &&
1165			    (item->data(peer_role_type).toInt() ==
1166			     PEER_TYPE_WPS_ER_AP ||
1167			     item->data(peer_role_type).toInt() ==
1168			     PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
1169				model.removeRow(lst[i].row());
1170		}
1171		return;
1172	}
1173
1174	if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
1175		/*
1176		 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
1177		 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
1178		 * pri_dev_type=1-0050F204-1
1179		 * |Wireless Client|Company|cmodel|123|12345|
1180		 */
1181		QStringList items = text.split(' ');
1182		if (items.size() < 3)
1183			return;
1184		QString uuid = items[1];
1185		QString addr = items[2];
1186		QString pri_dev_type = items[6].mid(13);
1187		int config_methods = -1;
1188		int dev_passwd_id = -1;
1189
1190		for (int i = 3; i < items.size(); i++) {
1191			int pos = items[i].indexOf('=') + 1;
1192			if (pos < 1)
1193				continue;
1194			QString val = items[i].mid(pos);
1195			if (items[i].startsWith("config_methods=")) {
1196				config_methods = val.toInt(0, 0);
1197			} else if (items[i].startsWith("dev_passwd_id=")) {
1198				dev_passwd_id = val.toInt();
1199			}
1200		}
1201
1202		int pos = text.indexOf('|');
1203		if (pos < 0)
1204			return;
1205		items = text.mid(pos + 1).split('|');
1206		if (items.size() < 1)
1207			return;
1208		QString name = items[0];
1209		if (name.length() == 0)
1210			name = addr;
1211
1212		remove_enrollee_uuid(uuid);
1213
1214		QStandardItem *item;
1215		item = new QStandardItem(*laptop_icon, name);
1216		if (item) {
1217			item->setData(uuid, peer_role_uuid);
1218			item->setData(addr, peer_role_address);
1219			item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
1220				      peer_role_type);
1221			item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
1222			item->setData(items.join(QString("\n")),
1223				      peer_role_details);
1224			item->setData(pri_dev_type, peer_role_pri_dev_type);
1225			if (config_methods >= 0)
1226				item->setData(config_methods,
1227					      peer_role_config_methods);
1228			if (dev_passwd_id >= 0)
1229				item->setData(dev_passwd_id,
1230					      peer_role_dev_passwd_id);
1231			model.appendRow(item);
1232		}
1233
1234		return;
1235	}
1236
1237	if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
1238		/*
1239		 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
1240		 * 02:66:a0:ee:17:27
1241		 */
1242		QStringList items = text.split(' ');
1243		if (items.size() < 2)
1244			return;
1245		remove_enrollee_uuid(items[1]);
1246		return;
1247	}
1248
1249	if (text.startsWith(WPS_EVENT_ENROLLEE_SEEN)) {
1250		/* TODO: need to time out this somehow or remove on successful
1251		 * WPS run, etc. */
1252		/*
1253		 * WPS-ENROLLEE-SEEN 02:00:00:00:01:00
1254		 * 572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1
1255		 * [Wireless Client]
1256		 * (MAC addr, UUID-E, pri dev type, config methods,
1257		 * dev passwd id, request type, [dev name])
1258		 */
1259		QStringList items = text.split(' ');
1260		if (items.size() < 7)
1261			return;
1262		QString addr = items[1];
1263		QString uuid = items[2];
1264		QString pri_dev_type = items[3];
1265		int config_methods = items[4].toInt(0, 0);
1266		int dev_passwd_id = items[5].toInt();
1267		QString name;
1268
1269		QStandardItem *item = find_addr(addr);
1270		if (item) {
1271			int type = item->data(peer_role_type).toInt();
1272			if (type == PEER_TYPE_ASSOCIATED_STATION)
1273				return; /* already associated */
1274		}
1275
1276		int pos = text.indexOf('[');
1277		if (pos >= 0) {
1278			int pos2 = text.lastIndexOf(']');
1279			if (pos2 >= pos) {
1280				QStringList items2 =
1281					text.mid(pos + 1, pos2 - pos - 1).
1282					split('|');
1283				name = items2[0];
1284			}
1285		}
1286		if (name.isEmpty())
1287			name = addr;
1288
1289		item = find_uuid(uuid);
1290		if (item) {
1291			QVariant var = item->data(peer_role_config_methods);
1292			QVariant var2 = item->data(peer_role_dev_passwd_id);
1293			if ((var.isValid() && config_methods != var.toInt()) ||
1294			    (var2.isValid() && dev_passwd_id != var2.toInt()))
1295				remove_enrollee_uuid(uuid);
1296			else
1297				return;
1298		}
1299
1300		item = new QStandardItem(*laptop_icon, name);
1301		if (item) {
1302			item->setData(uuid, peer_role_uuid);
1303			item->setData(addr, peer_role_address);
1304			item->setData(PEER_TYPE_WPS_ENROLLEE,
1305				      peer_role_type);
1306			item->setToolTip(ItemType(PEER_TYPE_WPS_ENROLLEE));
1307			item->setData(items.join(QString("\n")),
1308				      peer_role_details);
1309			item->setData(pri_dev_type, peer_role_pri_dev_type);
1310			item->setData(config_methods,
1311				      peer_role_config_methods);
1312			item->setData(dev_passwd_id, peer_role_dev_passwd_id);
1313			model.appendRow(item);
1314		}
1315
1316		return;
1317	}
1318
1319	if (text.startsWith(WPA_EVENT_BSS_ADDED)) {
1320		/* CTRL-EVENT-BSS-ADDED 34 00:11:22:33:44:55 */
1321		QStringList items = text.split(' ');
1322		if (items.size() < 2)
1323			return;
1324		char cmd[20];
1325		snprintf(cmd, sizeof(cmd), "BSS ID-%d", items[1].toInt());
1326		add_bss(cmd);
1327		return;
1328	}
1329
1330	if (text.startsWith(WPA_EVENT_BSS_REMOVED)) {
1331		/* CTRL-EVENT-BSS-REMOVED 34 00:11:22:33:44:55 */
1332		QStringList items = text.split(' ');
1333		if (items.size() < 2)
1334			return;
1335		remove_bss(items[1].toInt());
1336		return;
1337	}
1338}
1339
1340
1341void Peers::ctx_p2p_connect()
1342{
1343	if (ctx_item == NULL)
1344		return;
1345	QString addr = ctx_item->data(peer_role_address).toString();
1346	QString arg;
1347	int config_methods =
1348		ctx_item->data(peer_role_config_methods).toInt();
1349	enum selected_method method = SEL_METHOD_NONE;
1350	QVariant var = ctx_item->data(peer_role_selected_method);
1351	if (var.isValid())
1352		method = (enum selected_method) var.toInt();
1353	if (method == SEL_METHOD_PIN_LOCAL_DISPLAY) {
1354		arg = ctx_item->data(peer_role_selected_pin).toString();
1355		char cmd[100];
1356		char reply[100];
1357		size_t reply_len;
1358		snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
1359			 addr.toAscii().constData(),
1360			 arg.toAscii().constData());
1361		reply_len = sizeof(reply) - 1;
1362		if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1363			QMessageBox msg;
1364			msg.setIcon(QMessageBox::Warning);
1365			msg.setText("Failed to initiate P2P connect.");
1366			msg.exec();
1367			return;
1368		}
1369		QMessageBox::information(this,
1370					 tr("PIN for ") + ctx_item->text(),
1371					 tr("Enter the following PIN on the\n"
1372					    "peer device: ") + arg);
1373	} else if (method == SEL_METHOD_PIN_PEER_DISPLAY) {
1374		StringQuery input(tr("PIN from peer display:"));
1375		input.setWindowTitle(tr("PIN for ") + ctx_item->text());
1376		if (input.exec() != QDialog::Accepted)
1377			return;
1378		arg = input.get_string();
1379	} else if (config_methods == 0x0080 /* PBC */) {
1380		arg = "pbc";
1381	} else {
1382		StringQuery input(tr("PIN:"));
1383		input.setWindowTitle(tr("PIN for ") + ctx_item->text());
1384		if (input.exec() != QDialog::Accepted)
1385			return;
1386		arg = input.get_string();
1387	}
1388
1389	char cmd[100];
1390	char reply[100];
1391	size_t reply_len;
1392	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s",
1393		 addr.toAscii().constData(),
1394		 arg.toAscii().constData());
1395	reply_len = sizeof(reply) - 1;
1396	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1397		QMessageBox msg;
1398		msg.setIcon(QMessageBox::Warning);
1399		msg.setText("Failed to initiate P2P connect.");
1400		msg.exec();
1401	}
1402}
1403
1404
1405void Peers::ctx_p2p_req_pin()
1406{
1407	if (ctx_item == NULL)
1408		return;
1409	QString addr = ctx_item->data(peer_role_address).toString();
1410	ctx_item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
1411			  peer_role_requested_method);
1412
1413	char cmd[100];
1414	char reply[100];
1415	size_t reply_len;
1416	snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s display",
1417		 addr.toAscii().constData());
1418	reply_len = sizeof(reply) - 1;
1419	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1420		QMessageBox msg;
1421		msg.setIcon(QMessageBox::Warning);
1422		msg.setText(tr("Failed to request PIN from peer."));
1423		msg.exec();
1424	}
1425}
1426
1427
1428void Peers::ctx_p2p_show_pin()
1429{
1430	if (ctx_item == NULL)
1431		return;
1432	QString addr = ctx_item->data(peer_role_address).toString();
1433	ctx_item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
1434			  peer_role_requested_method);
1435
1436	char cmd[100];
1437	char reply[100];
1438	size_t reply_len;
1439	snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s keypad",
1440		 addr.toAscii().constData());
1441	reply_len = sizeof(reply) - 1;
1442	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1443		QMessageBox msg;
1444		msg.setIcon(QMessageBox::Warning);
1445		msg.setText(tr("Failed to request peer to enter PIN."));
1446		msg.exec();
1447	}
1448}
1449
1450
1451void Peers::ctx_p2p_display_pin()
1452{
1453	if (ctx_item == NULL)
1454		return;
1455	QString addr = ctx_item->data(peer_role_address).toString();
1456
1457	char cmd[100];
1458	char reply[100];
1459	size_t reply_len;
1460	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pin",
1461		 addr.toAscii().constData());
1462	reply_len = sizeof(reply) - 1;
1463	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1464		QMessageBox msg;
1465		msg.setIcon(QMessageBox::Warning);
1466		msg.setText("Failed to initiate P2P connect.");
1467		msg.exec();
1468		return;
1469	}
1470	reply[reply_len] = '\0';
1471	QMessageBox::information(this,
1472				 tr("PIN for ") + ctx_item->text(),
1473				 tr("Enter the following PIN on the\n"
1474				    "peer device: ") + reply);
1475}
1476
1477
1478void Peers::ctx_p2p_display_pin_pd()
1479{
1480	if (ctx_item == NULL)
1481		return;
1482	QString addr = ctx_item->data(peer_role_address).toString();
1483	QString arg = ctx_item->data(peer_role_selected_pin).toString();
1484
1485	char cmd[100];
1486	char reply[100];
1487	size_t reply_len;
1488	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
1489		 addr.toAscii().constData(),
1490		 arg.toAscii().constData());
1491	reply_len = sizeof(reply) - 1;
1492	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1493		QMessageBox msg;
1494		msg.setIcon(QMessageBox::Warning);
1495		msg.setText("Failed to initiate P2P connect.");
1496		msg.exec();
1497		return;
1498	}
1499	reply[reply_len] = '\0';
1500	QMessageBox::information(this,
1501				 tr("PIN for ") + ctx_item->text(),
1502				 tr("Enter the following PIN on the\n"
1503				    "peer device: ") + arg);
1504}
1505
1506
1507void Peers::ctx_p2p_enter_pin()
1508{
1509	if (ctx_item == NULL)
1510		return;
1511	QString addr = ctx_item->data(peer_role_address).toString();
1512	QString arg;
1513
1514	StringQuery input(tr("PIN from peer:"));
1515	input.setWindowTitle(tr("PIN for ") + ctx_item->text());
1516	if (input.exec() != QDialog::Accepted)
1517		return;
1518	arg = input.get_string();
1519
1520	char cmd[100];
1521	char reply[100];
1522	size_t reply_len;
1523	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s keypad",
1524		 addr.toAscii().constData(),
1525		 arg.toAscii().constData());
1526	reply_len = sizeof(reply) - 1;
1527	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1528		QMessageBox msg;
1529		msg.setIcon(QMessageBox::Warning);
1530		msg.setText("Failed to initiate P2P connect.");
1531		msg.exec();
1532	}
1533}
1534
1535
1536void Peers::ctx_p2p_remove_group()
1537{
1538	if (ctx_item == NULL)
1539		return;
1540	char cmd[100];
1541	char reply[100];
1542	size_t reply_len;
1543	snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s",
1544		 ctx_item->data(peer_role_ifname).toString().toAscii().
1545		 constData());
1546	reply_len = sizeof(reply) - 1;
1547	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1548		QMessageBox msg;
1549		msg.setIcon(QMessageBox::Warning);
1550		msg.setText("Failed to remove P2P Group.");
1551		msg.exec();
1552	}
1553}
1554
1555
1556void Peers::closeEvent(QCloseEvent *)
1557{
1558	if (wpagui) {
1559		char reply[20];
1560		size_t replylen = sizeof(reply) - 1;
1561		wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
1562	}
1563}
1564
1565
1566void Peers::done(int r)
1567{
1568	QDialog::done(r);
1569	close();
1570}
1571
1572
1573void Peers::remove_enrollee_uuid(QString uuid)
1574{
1575	if (model.rowCount() == 0)
1576		return;
1577
1578	QModelIndexList lst = model.match(model.index(0, 0),
1579					  peer_role_uuid, uuid);
1580	for (int i = 0; i < lst.size(); i++) {
1581		QStandardItem *item = model.itemFromIndex(lst[i]);
1582		if (item == NULL)
1583			continue;
1584		int type = item->data(peer_role_type).toInt();
1585		if (type == PEER_TYPE_WPS_ER_ENROLLEE ||
1586		    type == PEER_TYPE_WPS_ENROLLEE)
1587			model.removeRow(lst[i].row());
1588	}
1589}
1590
1591
1592void Peers::properties()
1593{
1594	if (ctx_item == NULL)
1595		return;
1596
1597	QMessageBox msg(this);
1598	msg.setStandardButtons(QMessageBox::Ok);
1599	msg.setDefaultButton(QMessageBox::Ok);
1600	msg.setEscapeButton(QMessageBox::Ok);
1601	msg.setWindowTitle(tr("Peer Properties"));
1602
1603	int type = ctx_item->data(peer_role_type).toInt();
1604	QString title = Peers::ItemType(type);
1605
1606	msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
1607
1608	QVariant var;
1609	QString info;
1610
1611	var = ctx_item->data(peer_role_address);
1612	if (var.isValid())
1613		info += tr("Address: ") + var.toString() + QString("\n");
1614
1615	var = ctx_item->data(peer_role_uuid);
1616	if (var.isValid())
1617		info += tr("UUID: ") + var.toString() + QString("\n");
1618
1619	var = ctx_item->data(peer_role_pri_dev_type);
1620	if (var.isValid())
1621		info += tr("Primary Device Type: ") + var.toString() +
1622			QString("\n");
1623
1624	var = ctx_item->data(peer_role_ssid);
1625	if (var.isValid())
1626		info += tr("SSID: ") + var.toString() + QString("\n");
1627
1628	var = ctx_item->data(peer_role_config_methods);
1629	if (var.isValid()) {
1630		int methods = var.toInt();
1631		info += tr("Configuration Methods: ");
1632		if (methods & 0x0001)
1633			info += tr("[USBA]");
1634		if (methods & 0x0002)
1635			info += tr("[Ethernet]");
1636		if (methods & 0x0004)
1637			info += tr("[Label]");
1638		if (methods & 0x0008)
1639			info += tr("[Display]");
1640		if (methods & 0x0010)
1641			info += tr("[Ext. NFC Token]");
1642		if (methods & 0x0020)
1643			info += tr("[Int. NFC Token]");
1644		if (methods & 0x0040)
1645			info += tr("[NFC Interface]");
1646		if (methods & 0x0080)
1647			info += tr("[Push Button]");
1648		if (methods & 0x0100)
1649			info += tr("[Keypad]");
1650		info += "\n";
1651	}
1652
1653	var = ctx_item->data(peer_role_selected_method);
1654	if (var.isValid()) {
1655		enum selected_method method =
1656			(enum selected_method) var.toInt();
1657		switch (method) {
1658		case SEL_METHOD_NONE:
1659			break;
1660		case SEL_METHOD_PIN_PEER_DISPLAY:
1661			info += tr("Selected Method: PIN on peer display\n");
1662			break;
1663		case SEL_METHOD_PIN_LOCAL_DISPLAY:
1664			info += tr("Selected Method: PIN on local display\n");
1665			break;
1666		}
1667	}
1668
1669	var = ctx_item->data(peer_role_selected_pin);
1670	if (var.isValid()) {
1671		info += tr("PIN to enter on peer: ") + var.toString() + "\n";
1672	}
1673
1674	var = ctx_item->data(peer_role_dev_passwd_id);
1675	if (var.isValid()) {
1676		info += tr("Device Password ID: ") + var.toString();
1677		switch (var.toInt()) {
1678		case 0:
1679			info += tr(" (Default PIN)");
1680			break;
1681		case 1:
1682			info += tr(" (User-specified PIN)");
1683			break;
1684		case 2:
1685			info += tr(" (Machine-specified PIN)");
1686			break;
1687		case 3:
1688			info += tr(" (Rekey)");
1689			break;
1690		case 4:
1691			info += tr(" (Push Button)");
1692			break;
1693		case 5:
1694			info += tr(" (Registrar-specified)");
1695			break;
1696		}
1697		info += "\n";
1698	}
1699
1700	msg.setInformativeText(info);
1701
1702	var = ctx_item->data(peer_role_details);
1703	if (var.isValid())
1704		msg.setDetailedText(var.toString());
1705
1706	msg.exec();
1707}
1708
1709
1710void Peers::connect_pbc()
1711{
1712	if (ctx_item == NULL)
1713		return;
1714
1715	char cmd[100];
1716	char reply[100];
1717	size_t reply_len;
1718
1719	int peer_type = ctx_item->data(peer_role_type).toInt();
1720	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
1721		snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
1722			 ctx_item->data(peer_role_uuid).toString().toAscii().
1723			 constData());
1724	} else if (peer_type == PEER_TYPE_P2P ||
1725		   peer_type == PEER_TYPE_P2P_CLIENT) {
1726		snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pbc",
1727			 ctx_item->data(peer_role_address).toString().
1728			 toAscii().constData());
1729	} else {
1730		snprintf(cmd, sizeof(cmd), "WPS_PBC");
1731	}
1732	reply_len = sizeof(reply) - 1;
1733	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1734		QMessageBox msg;
1735		msg.setIcon(QMessageBox::Warning);
1736		msg.setText(tr("Failed to start WPS PBC."));
1737		msg.exec();
1738	}
1739}
1740
1741
1742void Peers::learn_ap_config()
1743{
1744	if (ctx_item == NULL)
1745		return;
1746
1747	QString uuid = ctx_item->data(peer_role_uuid).toString();
1748
1749	StringQuery input(tr("AP PIN:"));
1750	input.setWindowTitle(tr("AP PIN for ") + ctx_item->text());
1751	if (input.exec() != QDialog::Accepted)
1752		return;
1753
1754	char cmd[100];
1755	char reply[100];
1756	size_t reply_len;
1757
1758	snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
1759		 uuid.toAscii().constData(),
1760		 input.get_string().toAscii().constData());
1761	reply_len = sizeof(reply) - 1;
1762	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1763		QMessageBox msg;
1764		msg.setIcon(QMessageBox::Warning);
1765		msg.setText(tr("Failed to start learning AP configuration."));
1766		msg.exec();
1767	}
1768}
1769
1770
1771void Peers::ctx_hide_ap()
1772{
1773	hide_ap = true;
1774
1775	if (model.rowCount() == 0)
1776		return;
1777
1778	do {
1779		QModelIndexList lst;
1780		lst = model.match(model.index(0, 0),
1781				  peer_role_type, PEER_TYPE_AP);
1782		if (lst.size() == 0) {
1783			lst = model.match(model.index(0, 0),
1784					  peer_role_type, PEER_TYPE_AP_WPS);
1785			if (lst.size() == 0)
1786				break;
1787		}
1788
1789		model.removeRow(lst[0].row());
1790	} while (1);
1791}
1792
1793
1794void Peers::ctx_show_ap()
1795{
1796	hide_ap = false;
1797	add_scan_results();
1798}
1799
1800
1801void Peers::ctx_p2p_show_passphrase()
1802{
1803	char reply[64];
1804	size_t reply_len;
1805
1806	reply_len = sizeof(reply) - 1;
1807	if (wpagui->ctrlRequest("P2P_GET_PASSPHRASE", reply, &reply_len) < 0 ||
1808	    memcmp(reply, "FAIL", 4) == 0) {
1809		QMessageBox msg;
1810		msg.setIcon(QMessageBox::Warning);
1811		msg.setText("Failed to get P2P group passphrase.");
1812		msg.exec();
1813	} else {
1814		reply[reply_len] = '\0';
1815		QMessageBox::information(this, tr("Passphrase"),
1816					 tr("P2P group passphrase:\n") +
1817					 reply);
1818	}
1819}
1820
1821
1822void Peers::ctx_p2p_start_persistent()
1823{
1824	if (ctx_item == NULL)
1825		return;
1826
1827	char cmd[100];
1828	char reply[100];
1829	size_t reply_len;
1830
1831	snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD persistent=%d",
1832		 ctx_item->data(peer_role_network_id).toInt());
1833	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
1834	    memcmp(reply, "FAIL", 4) == 0) {
1835		QMessageBox msg;
1836		msg.setIcon(QMessageBox::Warning);
1837		msg.setText(tr("Failed to start persistent P2P Group."));
1838		msg.exec();
1839	} else if (ctx_item->data(peer_role_type).toInt() ==
1840		   PEER_TYPE_P2P_INVITATION)
1841		model.removeRow(ctx_item->row());
1842}
1843
1844
1845void Peers::ctx_p2p_invite()
1846{
1847	if (ctx_item == NULL)
1848		return;
1849
1850	char cmd[100];
1851	char reply[100];
1852	size_t reply_len;
1853
1854	snprintf(cmd, sizeof(cmd), "P2P_INVITE persistent=%d",
1855		 ctx_item->data(peer_role_network_id).toInt());
1856	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
1857	    memcmp(reply, "FAIL", 4) == 0) {
1858		QMessageBox msg;
1859		msg.setIcon(QMessageBox::Warning);
1860		msg.setText(tr("Failed to invite peer to start persistent "
1861			       "P2P Group."));
1862		msg.exec();
1863	}
1864}
1865
1866
1867void Peers::ctx_p2p_delete()
1868{
1869	if (ctx_item == NULL)
1870		return;
1871	model.removeRow(ctx_item->row());
1872}
1873
1874
1875void Peers::enable_persistent(int id)
1876{
1877	if (model.rowCount() == 0)
1878		return;
1879
1880	QModelIndexList lst = model.match(model.index(0, 0),
1881					  peer_role_network_id, id);
1882	for (int i = 0; i < lst.size(); i++) {
1883		QStandardItem *item = model.itemFromIndex(lst[i]);
1884		int type = item->data(peer_role_type).toInt();
1885		if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
1886		    type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT)
1887			item->setBackground(Qt::NoBrush);
1888	}
1889}
1890