xref: /netbsd-src/external/bsd/wpa/dist/wpa_supplicant/wpa_gui-qt4/peers.cpp (revision 4391d5e9d4f291db41e3b3ba26a01b5e51364aae)
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 
25 enum {
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 
42 enum 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 
53 enum 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 
71 Peers::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 
105 void Peers::setWpaGui(WpaGui *_wpagui)
106 {
107 	wpagui = _wpagui;
108 	update_peers();
109 }
110 
111 
112 Peers::~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 
122 void Peers::languageChange()
123 {
124 	retranslateUi(this);
125 }
126 
127 
128 QString 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 
179 void 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 
309 void 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 
350 void Peers::ctx_refresh()
351 {
352 	update_peers();
353 }
354 
355 
356 void 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 
371 void 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 
380 void 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 
395 void 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 
410 void 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 
460 void 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 
491 void 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 
516 void 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 
565 void 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 
578 bool 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 
663 void 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 
680 void 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 
719 void 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 
769 void 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 
785 QStandardItem * 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 
798 QStandardItem * 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 
814 QStandardItem * 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 
827 void 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 
1341 void 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 
1405 void 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 
1428 void 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 
1451 void 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 
1478 void 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 
1507 void 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 
1536 void 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 
1556 void 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 
1566 void Peers::done(int r)
1567 {
1568 	QDialog::done(r);
1569 	close();
1570 }
1571 
1572 
1573 void 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 
1592 void 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 
1710 void 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 
1742 void 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 
1771 void 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 
1794 void Peers::ctx_show_ap()
1795 {
1796 	hide_ap = false;
1797 	add_scan_results();
1798 }
1799 
1800 
1801 void 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 
1822 void 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 
1845 void 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 
1867 void Peers::ctx_p2p_delete()
1868 {
1869 	if (ctx_item == NULL)
1870 		return;
1871 	model.removeRow(ctx_item->row());
1872 }
1873 
1874 
1875 void 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