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 
19 enum {
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 
36 enum 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 
47 enum 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 
Peers(QWidget * parent,const char *,bool,Qt::WindowFlags)65 Peers::Peers(QWidget *parent, const char *, bool, Qt::WindowFlags)
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 
setWpaGui(WpaGui * _wpagui)99 void Peers::setWpaGui(WpaGui *_wpagui)
100 {
101 	wpagui = _wpagui;
102 	update_peers();
103 }
104 
105 
~Peers()106 Peers::~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 
languageChange()116 void Peers::languageChange()
117 {
118 	retranslateUi(this);
119 }
120 
121 
ItemType(int type)122 QString 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 
context_menu(const QPoint & pos)173 void 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 
enter_pin()303 void 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.toLocal8Bit().constData(),
327 			 input.get_string().toLocal8Bit().constData(),
328 			 addr.toLocal8Bit().constData());
329 	} else {
330 		snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
331 			 addr.toLocal8Bit().constData(),
332 			 input.get_string().toLocal8Bit().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 
ctx_refresh()344 void Peers::ctx_refresh()
345 {
346 	update_peers();
347 }
348 
349 
ctx_p2p_start()350 void 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 
ctx_p2p_stop()365 void 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 
ctx_p2p_listen()374 void 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 
ctx_p2p_start_group()389 void 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 
add_station(QString info)404 void 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 
add_stations()454 void 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 
add_single_station(const char * addr)485 void 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 
add_p2p_group_client(QStandardItem *,QString params)510 void 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 
remove_bss(int id)559 void 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 
add_bss(const char * cmd)572 bool 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 
add_scan_results()657 void 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 
add_persistent(int id,const char * ssid,const char * bssid)674 void 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 
add_persistent_groups()713 void 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 
update_peers()763 void 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 
find_addr(QString addr)779 QStandardItem * 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 
find_addr_type(QString addr,int type)792 QStandardItem * 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 
find_uuid(QString uuid)808 QStandardItem * 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 
event_notify(WpaMsg msg)821 void 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.toLocal8Bit().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 
ctx_p2p_connect()1335 void 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.toLocal8Bit().constData(),
1354 			 arg.toLocal8Bit().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.toLocal8Bit().constData(),
1388 		 arg.toLocal8Bit().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 
ctx_p2p_req_pin()1399 void 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.toLocal8Bit().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 
ctx_p2p_show_pin()1422 void 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.toLocal8Bit().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 
ctx_p2p_display_pin()1445 void 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.toLocal8Bit().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 
ctx_p2p_display_pin_pd()1472 void 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.toLocal8Bit().constData(),
1484 		 arg.toLocal8Bit().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 
ctx_p2p_enter_pin()1501 void 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.toLocal8Bit().constData(),
1519 		 arg.toLocal8Bit().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 
ctx_p2p_remove_group()1530 void 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().toLocal8Bit().
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 
closeEvent(QCloseEvent *)1550 void 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 
done(int r)1560 void Peers::done(int r)
1561 {
1562 	QDialog::done(r);
1563 	close();
1564 }
1565 
1566 
remove_enrollee_uuid(QString uuid)1567 void 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 
properties()1586 void 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 
connect_pbc()1704 void 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().toLocal8Bit().
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 			 toLocal8Bit().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 
learn_ap_config()1736 void 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.toLocal8Bit().constData(),
1754 		 input.get_string().toLocal8Bit().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 
ctx_hide_ap()1765 void 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 
ctx_show_ap()1788 void Peers::ctx_show_ap()
1789 {
1790 	hide_ap = false;
1791 	add_scan_results();
1792 }
1793 
1794 
ctx_p2p_show_passphrase()1795 void 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 
ctx_p2p_start_persistent()1816 void 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 
ctx_p2p_invite()1839 void 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 
ctx_p2p_delete()1861 void Peers::ctx_p2p_delete()
1862 {
1863 	if (ctx_item == NULL)
1864 		return;
1865 	model.removeRow(ctx_item->row());
1866 }
1867 
1868 
enable_persistent(int id)1869 void 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