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