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(QRegularExpression("\\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  		res = snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
480  		if (res < 0 || (size_t) res >= sizeof(cmd))
481  			break;
482  		res = wpagui->ctrlRequest(cmd, reply, &reply_len);
483  	} while (res >= 0);
484  }
485  
486  
add_single_station(const char * addr)487  void Peers::add_single_station(const char *addr)
488  {
489  	char reply[2048];
490  	size_t reply_len;
491  	char cmd[30];
492  
493  	reply_len = sizeof(reply) - 1;
494  	snprintf(cmd, sizeof(cmd), "STA %s", addr);
495  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
496  		return;
497  
498  	reply[reply_len] = '\0';
499  	QString info(reply);
500  	char *txt = reply;
501  	while (*txt != '\0' && *txt != '\n')
502  		txt++;
503  	*txt++ = '\0';
504  	if (strncmp(reply, "FAIL", 4) == 0 ||
505  	    strncmp(reply, "UNKNOWN", 7) == 0)
506  		return;
507  
508  	add_station(info);
509  }
510  
511  
add_p2p_group_client(QStandardItem *,QString params)512  void Peers::add_p2p_group_client(QStandardItem * /*parent*/, QString params)
513  {
514  	/*
515  	 * dev=02:b5:64:63:30:63 iface=02:b5:64:63:30:63 dev_capab=0x0
516  	 * dev_type=1-0050f204-1 dev_name='Wireless Client'
517  	 * config_methods=0x8c
518  	 */
519  
520  	QStringList items =
521  		params.split(QRegularExpression(" (?=[^']*('[^']*'[^']*)*$)"));
522  	QString addr = "";
523  	QString name = "";
524  	int config_methods = 0;
525  	QString dev_type;
526  
527  	for (int i = 0; i < items.size(); i++) {
528  		QString str = items.at(i);
529  		int pos = str.indexOf('=') + 1;
530  		if (str.startsWith("dev_name='"))
531  			name = str.section('\'', 1, -2);
532  		else if (str.startsWith("config_methods="))
533  			config_methods =
534  				str.section('=', 1).toInt(0, 0);
535  		else if (str.startsWith("dev="))
536  			addr = str.mid(pos);
537  		else if (str.startsWith("dev_type=") && dev_type.isEmpty())
538  			dev_type = str.mid(pos);
539  	}
540  
541  	QStandardItem *item = find_addr(addr);
542  	if (item)
543  		return;
544  
545  	item = new QStandardItem(*default_icon, name);
546  	if (item) {
547  		/* TODO: indicate somehow the relationship to the group owner
548  		 * (parent) */
549  		item->setData(addr, peer_role_address);
550  		item->setData(config_methods, peer_role_config_methods);
551  		item->setData(PEER_TYPE_P2P_CLIENT, peer_role_type);
552  		if (!dev_type.isEmpty())
553  			item->setData(dev_type, peer_role_pri_dev_type);
554  		item->setData(items.join(QString("\n")), peer_role_details);
555  		item->setToolTip(ItemType(PEER_TYPE_P2P_CLIENT));
556  		model.appendRow(item);
557  	}
558  }
559  
560  
remove_bss(int id)561  void Peers::remove_bss(int id)
562  {
563  	if (model.rowCount() == 0)
564  		return;
565  
566  	QModelIndexList lst = model.match(model.index(0, 0), peer_role_bss_id,
567  					  id);
568  	if (lst.size() == 0)
569  		return;
570  	model.removeRow(lst[0].row());
571  }
572  
573  
add_bss(const char * cmd)574  bool Peers::add_bss(const char *cmd)
575  {
576  	char reply[2048];
577  	size_t reply_len;
578  
579  	if (hide_ap)
580  		return false;
581  
582  	reply_len = sizeof(reply) - 1;
583  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
584  		return false;
585  	reply[reply_len] = '\0';
586  
587  	QString bss(reply);
588  	if (bss.isEmpty() || bss.startsWith("FAIL"))
589  		return false;
590  
591  	QString ssid, bssid, flags, wps_name, pri_dev_type;
592  	int id = -1;
593  
594  	QStringList lines = bss.split(QRegularExpression("\\n"));
595  	for (QStringList::Iterator it = lines.begin();
596  	     it != lines.end(); it++) {
597  		int pos = (*it).indexOf('=') + 1;
598  		if (pos < 1)
599  			continue;
600  
601  		if ((*it).startsWith("bssid="))
602  			bssid = (*it).mid(pos);
603  		else if ((*it).startsWith("id="))
604  			id = (*it).mid(pos).toInt();
605  		else if ((*it).startsWith("flags="))
606  			flags = (*it).mid(pos);
607  		else if ((*it).startsWith("ssid="))
608  			ssid = (*it).mid(pos);
609  		else if ((*it).startsWith("wps_device_name="))
610  			wps_name = (*it).mid(pos);
611  		else if ((*it).startsWith("wps_primary_device_type="))
612  			pri_dev_type = (*it).mid(pos);
613  	}
614  
615  	QString name = wps_name;
616  	if (name.isEmpty())
617  		name = ssid + "\n" + bssid;
618  
619  	QStandardItem *item = new QStandardItem(*ap_icon, name);
620  	if (item) {
621  		item->setData(bssid, peer_role_address);
622  		if (id >= 0)
623  			item->setData(id, peer_role_bss_id);
624  		int type;
625  		if (flags.contains("[WPS"))
626  			type = PEER_TYPE_AP_WPS;
627  		else
628  			type = PEER_TYPE_AP;
629  		item->setData(type, peer_role_type);
630  
631  		for (int i = 0; i < lines.size(); i++) {
632  			if (lines[i].length() > 60) {
633  				lines[i].remove(60, lines[i].length());
634  				lines[i] += "..";
635  			}
636  		}
637  		item->setToolTip(ItemType(type));
638  		item->setData(lines.join("\n"), peer_role_details);
639  		if (!pri_dev_type.isEmpty())
640  			item->setData(pri_dev_type,
641  				      peer_role_pri_dev_type);
642  		if (!ssid.isEmpty())
643  			item->setData(ssid, peer_role_ssid);
644  		model.appendRow(item);
645  
646  		lines = bss.split(QRegularExpression("\\n"));
647  		for (QStringList::Iterator it = lines.begin();
648  		     it != lines.end(); it++) {
649  			if ((*it).startsWith("p2p_group_client:"))
650  				add_p2p_group_client(item,
651  						     (*it).mid(18));
652  		}
653  	}
654  
655  	return true;
656  }
657  
658  
add_scan_results()659  void Peers::add_scan_results()
660  {
661  	int index;
662  	char cmd[20];
663  
664  	index = 0;
665  	while (wpagui) {
666  		snprintf(cmd, sizeof(cmd), "BSS %d", index++);
667  		if (index > 1000)
668  			break;
669  
670  		if (!add_bss(cmd))
671  			break;
672  	}
673  }
674  
675  
add_persistent(int id,const char * ssid,const char * bssid)676  void Peers::add_persistent(int id, const char *ssid, const char *bssid)
677  {
678  	char cmd[100];
679  	char reply[100];
680  	size_t reply_len;
681  	int mode;
682  
683  	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d mode", id);
684  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
685  		return;
686  	reply[reply_len] = '\0';
687  	mode = atoi(reply);
688  
689  	QString name = ssid;
690  	name = '[' + name + ']';
691  
692  	QStandardItem *item = new QStandardItem(*group_icon, name);
693  	if (!item)
694  		return;
695  
696  	int type;
697  	if (mode == 3)
698  		type = PEER_TYPE_P2P_PERSISTENT_GROUP_GO;
699  	else
700  		type = PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT;
701  	item->setData(type, peer_role_type);
702  	item->setToolTip(ItemType(type));
703  	item->setData(ssid, peer_role_ssid);
704  	if (bssid && strcmp(bssid, "any") == 0)
705  		bssid = NULL;
706  	if (bssid)
707  		item->setData(bssid, peer_role_address);
708  	item->setData(id, peer_role_network_id);
709  	item->setBackground(Qt::BDiagPattern);
710  
711  	model.appendRow(item);
712  }
713  
714  
add_persistent_groups()715  void Peers::add_persistent_groups()
716  {
717  	char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
718  	size_t len;
719  
720  	len = sizeof(buf) - 1;
721  	if (wpagui->ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
722  		return;
723  
724  	buf[len] = '\0';
725  	start = strchr(buf, '\n');
726  	if (start == NULL)
727  		return;
728  	start++;
729  
730  	while (*start) {
731  		bool last = false;
732  		end = strchr(start, '\n');
733  		if (end == NULL) {
734  			last = true;
735  			end = start;
736  			while (end[0] && end[1])
737  				end++;
738  		}
739  		*end = '\0';
740  
741  		id = start;
742  		ssid = strchr(id, '\t');
743  		if (ssid == NULL)
744  			break;
745  		*ssid++ = '\0';
746  		bssid = strchr(ssid, '\t');
747  		if (bssid == NULL)
748  			break;
749  		*bssid++ = '\0';
750  		flags = strchr(bssid, '\t');
751  		if (flags == NULL)
752  			break;
753  		*flags++ = '\0';
754  
755  		if (strstr(flags, "[DISABLED][P2P-PERSISTENT]"))
756  			add_persistent(atoi(id), ssid, bssid);
757  
758  		if (last)
759  			break;
760  		start = end + 1;
761  	}
762  }
763  
764  
update_peers()765  void Peers::update_peers()
766  {
767  	model.clear();
768  	if (wpagui == NULL)
769  		return;
770  
771  	char reply[20];
772  	size_t replylen = sizeof(reply) - 1;
773  	wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
774  
775  	add_stations();
776  	add_scan_results();
777  	add_persistent_groups();
778  }
779  
780  
find_addr(QString addr)781  QStandardItem * Peers::find_addr(QString addr)
782  {
783  	if (model.rowCount() == 0)
784  		return NULL;
785  
786  	QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
787  					  addr);
788  	if (lst.size() == 0)
789  		return NULL;
790  	return model.itemFromIndex(lst[0]);
791  }
792  
793  
find_addr_type(QString addr,int type)794  QStandardItem * Peers::find_addr_type(QString addr, int type)
795  {
796  	if (model.rowCount() == 0)
797  		return NULL;
798  
799  	QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
800  					  addr);
801  	for (int i = 0; i < lst.size(); i++) {
802  		QStandardItem *item = model.itemFromIndex(lst[i]);
803  		if (item->data(peer_role_type).toInt() == type)
804  			return item;
805  	}
806  	return NULL;
807  }
808  
809  
find_uuid(QString uuid)810  QStandardItem * Peers::find_uuid(QString uuid)
811  {
812  	if (model.rowCount() == 0)
813  		return NULL;
814  
815  	QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
816  					  uuid);
817  	if (lst.size() == 0)
818  		return NULL;
819  	return model.itemFromIndex(lst[0]);
820  }
821  
822  
event_notify(WpaMsg msg)823  void Peers::event_notify(WpaMsg msg)
824  {
825  	QString text = msg.getMsg();
826  
827  	if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
828  		/*
829  		 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
830  		 * 02:2a:c4:18:5b:f3
831  		 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
832  		 */
833  		QStringList items = text.split(' ');
834  		QString uuid = items[1];
835  		QString addr = items[2];
836  		QString name = "";
837  
838  		QStandardItem *item = find_addr(addr);
839  		if (item)
840  			return;
841  
842  		int pos = text.indexOf('[');
843  		if (pos >= 0) {
844  			int pos2 = text.lastIndexOf(']');
845  			if (pos2 >= pos) {
846  				items = text.mid(pos + 1, pos2 - pos - 1).
847  					split('|');
848  				name = items[0];
849  				items.append(addr);
850  			}
851  		}
852  
853  		item = new QStandardItem(*laptop_icon, name);
854  		if (item) {
855  			item->setData(addr, peer_role_address);
856  			item->setData(PEER_TYPE_WPS_PIN_NEEDED,
857  				      peer_role_type);
858  			item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
859  			item->setData(items.join("\n"), peer_role_details);
860  			item->setData(items[5], peer_role_pri_dev_type);
861  			model.appendRow(item);
862  		}
863  		return;
864  	}
865  
866  	if (text.startsWith(AP_STA_CONNECTED)) {
867  		/* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
868  		QStringList items = text.split(' ');
869  		QString addr = items[1];
870  		QStandardItem *item = find_addr(addr);
871  		if (item == NULL || item->data(peer_role_type).toInt() !=
872  		    PEER_TYPE_ASSOCIATED_STATION)
873  			add_single_station(addr.toLocal8Bit().constData());
874  		return;
875  	}
876  
877  	if (text.startsWith(AP_STA_DISCONNECTED)) {
878  		/* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
879  		QStringList items = text.split(' ');
880  		QString addr = items[1];
881  
882  		if (model.rowCount() == 0)
883  			return;
884  
885  		QModelIndexList lst = model.match(model.index(0, 0),
886  						  peer_role_address, addr, -1);
887  		for (int i = 0; i < lst.size(); i++) {
888  			QStandardItem *item = model.itemFromIndex(lst[i]);
889  			if (item && item->data(peer_role_type).toInt() ==
890  			    PEER_TYPE_ASSOCIATED_STATION) {
891  				model.removeRow(lst[i].row());
892  				break;
893  			}
894  		}
895  		return;
896  	}
897  
898  	if (text.startsWith(P2P_EVENT_DEVICE_FOUND)) {
899  		/*
900  		 * P2P-DEVICE-FOUND 02:b5:64:63:30:63
901  		 * p2p_dev_addr=02:b5:64:63:30:63 pri_dev_type=1-0050f204-1
902  		 * name='Wireless Client' config_methods=0x84 dev_capab=0x21
903  		 * group_capab=0x0
904  		 */
905  		QStringList items =
906  			text.split(QRegularExpression(" (?=[^']*('[^']*'[^']*)*$)"));
907  		QString addr = items[1];
908  		QString name = "";
909  		QString pri_dev_type;
910  		int config_methods = 0;
911  		for (int i = 0; i < items.size(); i++) {
912  			QString str = items.at(i);
913  			if (str.startsWith("name='"))
914  				name = str.section('\'', 1, -2);
915  			else if (str.startsWith("config_methods="))
916  				config_methods =
917  					str.section('=', 1).toInt(0, 0);
918  			else if (str.startsWith("pri_dev_type="))
919  				pri_dev_type = str.section('=', 1);
920  		}
921  
922  		QStandardItem *item = find_addr(addr);
923  		if (item) {
924  			int type = item->data(peer_role_type).toInt();
925  			if (type == PEER_TYPE_P2P)
926  				return;
927  		}
928  
929  		item = new QStandardItem(*default_icon, name);
930  		if (item) {
931  			item->setData(addr, peer_role_address);
932  			item->setData(config_methods,
933  				      peer_role_config_methods);
934  			item->setData(PEER_TYPE_P2P, peer_role_type);
935  			if (!pri_dev_type.isEmpty())
936  				item->setData(pri_dev_type,
937  					      peer_role_pri_dev_type);
938  			item->setData(items.join(QString("\n")),
939  				      peer_role_details);
940  			item->setToolTip(ItemType(PEER_TYPE_P2P));
941  			model.appendRow(item);
942  		}
943  
944  		item = find_addr_type(addr,
945  				      PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT);
946  		if (item)
947  			item->setBackground(Qt::NoBrush);
948  	}
949  
950  	if (text.startsWith(P2P_EVENT_GROUP_STARTED)) {
951  		/* P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F"
952  		 * passphrase="YOyTkxID" go_dev_addr=02:40:61:c2:f3:b7
953  		 * [PERSISTENT] */
954  		QStringList items = text.split(' ');
955  		if (items.size() < 4)
956  			return;
957  
958  		int pos = text.indexOf(" ssid=\"");
959  		if (pos < 0)
960  			return;
961  		QString ssid = text.mid(pos + 7);
962  		pos = ssid.indexOf(" passphrase=\"");
963  		if (pos < 0)
964  			pos = ssid.indexOf(" psk=");
965  		if (pos >= 0)
966  			ssid.truncate(pos);
967  		pos = ssid.lastIndexOf('"');
968  		if (pos >= 0)
969  			ssid.truncate(pos);
970  
971  		QStandardItem *item = new QStandardItem(*group_icon, ssid);
972  		if (item) {
973  			item->setData(PEER_TYPE_P2P_GROUP, peer_role_type);
974  			item->setData(items[1], peer_role_ifname);
975  			QString details;
976  			if (items[2] == "GO") {
977  				details = tr("P2P GO for interface ") +
978  					items[1];
979  			} else {
980  				details = tr("P2P client for interface ") +
981  					items[1];
982  			}
983  			if (text.contains(" [PERSISTENT]"))
984  				details += "\nPersistent group";
985  			item->setData(details, peer_role_details);
986  			item->setToolTip(ItemType(PEER_TYPE_P2P_GROUP));
987  			model.appendRow(item);
988  		}
989  	}
990  
991  	if (text.startsWith(P2P_EVENT_GROUP_REMOVED)) {
992  		/* P2P-GROUP-REMOVED wlan0-p2p-0 GO */
993  		QStringList items = text.split(' ');
994  		if (items.size() < 2)
995  			return;
996  
997  		if (model.rowCount() == 0)
998  			return;
999  
1000  		QModelIndexList lst = model.match(model.index(0, 0),
1001  						  peer_role_ifname, items[1]);
1002  		for (int i = 0; i < lst.size(); i++)
1003  			model.removeRow(lst[i].row());
1004  		return;
1005  	}
1006  
1007  	if (text.startsWith(P2P_EVENT_PROV_DISC_SHOW_PIN)) {
1008  		/* P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670 */
1009  		QStringList items = text.split(' ');
1010  		if (items.size() < 3)
1011  			return;
1012  		QString addr = items[1];
1013  		QString pin = items[2];
1014  
1015  		QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
1016  		if (item == NULL)
1017  			return;
1018  		item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
1019  			      peer_role_selected_method);
1020  		item->setData(pin, peer_role_selected_pin);
1021  		QVariant var = item->data(peer_role_requested_method);
1022  		if (var.isValid() &&
1023  		    var.toInt() == SEL_METHOD_PIN_LOCAL_DISPLAY) {
1024  			ctx_item = item;
1025  			ctx_p2p_display_pin_pd();
1026  		}
1027  		return;
1028  	}
1029  
1030  	if (text.startsWith(P2P_EVENT_PROV_DISC_ENTER_PIN)) {
1031  		/* P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 */
1032  		QStringList items = text.split(' ');
1033  		if (items.size() < 2)
1034  			return;
1035  		QString addr = items[1];
1036  
1037  		QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
1038  		if (item == NULL)
1039  			return;
1040  		item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
1041  			      peer_role_selected_method);
1042  		QVariant var = item->data(peer_role_requested_method);
1043  		if (var.isValid() &&
1044  		    var.toInt() == SEL_METHOD_PIN_PEER_DISPLAY) {
1045  			ctx_item = item;
1046  			ctx_p2p_connect();
1047  		}
1048  		return;
1049  	}
1050  
1051  	if (text.startsWith(P2P_EVENT_INVITATION_RECEIVED)) {
1052  		/* P2P-INVITATION-RECEIVED sa=02:f0:bc:44:87:62 persistent=4 */
1053  		QStringList items = text.split(' ');
1054  		if (items.size() < 3)
1055  			return;
1056  		if (!items[1].startsWith("sa=") ||
1057  		    !items[2].startsWith("persistent="))
1058  			return;
1059  		QString addr = items[1].mid(3);
1060  		int id = items[2].mid(11).toInt();
1061  
1062  		char cmd[100];
1063  		char reply[100];
1064  		size_t reply_len;
1065  
1066  		snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", id);
1067  		reply_len = sizeof(reply) - 1;
1068  		if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
1069  			return;
1070  		reply[reply_len] = '\0';
1071  		QString name;
1072  		char *pos = strrchr(reply, '"');
1073  		if (pos && reply[0] == '"') {
1074  			*pos = '\0';
1075  			name = reply + 1;
1076  		} else
1077  			name = reply;
1078  
1079  		QStandardItem *item;
1080  		item = find_addr_type(addr, PEER_TYPE_P2P_INVITATION);
1081  		if (item)
1082  			model.removeRow(item->row());
1083  
1084  		item = new QStandardItem(*invitation_icon, name);
1085  		if (!item)
1086  			return;
1087  		item->setData(PEER_TYPE_P2P_INVITATION, peer_role_type);
1088  		item->setToolTip(ItemType(PEER_TYPE_P2P_INVITATION));
1089  		item->setData(addr, peer_role_address);
1090  		item->setData(id, peer_role_network_id);
1091  
1092  		model.appendRow(item);
1093  
1094  		enable_persistent(id);
1095  
1096  		return;
1097  	}
1098  
1099  	if (text.startsWith(P2P_EVENT_INVITATION_RESULT)) {
1100  		/* P2P-INVITATION-RESULT status=1 */
1101  		/* TODO */
1102  		return;
1103  	}
1104  
1105  	if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
1106  		/*
1107  		 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
1108  		 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
1109  		 * |Very friendly name|Company|Long description of the model|
1110  		 * WAP|http://w1.fi/|http://w1.fi/hostapd/
1111  		 */
1112  		QStringList items = text.split(' ');
1113  		if (items.size() < 5)
1114  			return;
1115  		QString uuid = items[1];
1116  		QString addr = items[2];
1117  		QString pri_dev_type = items[3].mid(13);
1118  		int wps_state = items[4].mid(10).toInt();
1119  
1120  		int pos = text.indexOf('|');
1121  		if (pos < 0)
1122  			return;
1123  		items = text.mid(pos + 1).split('|');
1124  		if (items.size() < 1)
1125  			return;
1126  
1127  		QStandardItem *item = find_uuid(uuid);
1128  		if (item)
1129  			return;
1130  
1131  		item = new QStandardItem(*ap_icon, items[0]);
1132  		if (item) {
1133  			item->setData(uuid, peer_role_uuid);
1134  			item->setData(addr, peer_role_address);
1135  			int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
1136  				PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
1137  			item->setData(type, peer_role_type);
1138  			item->setToolTip(ItemType(type));
1139  			item->setData(pri_dev_type, peer_role_pri_dev_type);
1140  			item->setData(items.join(QString("\n")),
1141  				      peer_role_details);
1142  			model.appendRow(item);
1143  		}
1144  
1145  		return;
1146  	}
1147  
1148  	if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
1149  		/* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
1150  		QStringList items = text.split(' ');
1151  		if (items.size() < 2)
1152  			return;
1153  		if (model.rowCount() == 0)
1154  			return;
1155  
1156  		QModelIndexList lst = model.match(model.index(0, 0),
1157  						  peer_role_uuid, items[1]);
1158  		for (int i = 0; i < lst.size(); i++) {
1159  			QStandardItem *item = model.itemFromIndex(lst[i]);
1160  			if (item &&
1161  			    (item->data(peer_role_type).toInt() ==
1162  			     PEER_TYPE_WPS_ER_AP ||
1163  			     item->data(peer_role_type).toInt() ==
1164  			     PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
1165  				model.removeRow(lst[i].row());
1166  		}
1167  		return;
1168  	}
1169  
1170  	if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
1171  		/*
1172  		 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
1173  		 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
1174  		 * pri_dev_type=1-0050F204-1
1175  		 * |Wireless Client|Company|cmodel|123|12345|
1176  		 */
1177  		QStringList items = text.split(' ');
1178  		if (items.size() < 3)
1179  			return;
1180  		QString uuid = items[1];
1181  		QString addr = items[2];
1182  		QString pri_dev_type = items[6].mid(13);
1183  		int config_methods = -1;
1184  		int dev_passwd_id = -1;
1185  
1186  		for (int i = 3; i < items.size(); i++) {
1187  			int pos = items[i].indexOf('=') + 1;
1188  			if (pos < 1)
1189  				continue;
1190  			QString val = items[i].mid(pos);
1191  			if (items[i].startsWith("config_methods=")) {
1192  				config_methods = val.toInt(0, 0);
1193  			} else if (items[i].startsWith("dev_passwd_id=")) {
1194  				dev_passwd_id = val.toInt();
1195  			}
1196  		}
1197  
1198  		int pos = text.indexOf('|');
1199  		if (pos < 0)
1200  			return;
1201  		items = text.mid(pos + 1).split('|');
1202  		if (items.size() < 1)
1203  			return;
1204  		QString name = items[0];
1205  		if (name.length() == 0)
1206  			name = addr;
1207  
1208  		remove_enrollee_uuid(uuid);
1209  
1210  		QStandardItem *item;
1211  		item = new QStandardItem(*laptop_icon, name);
1212  		if (item) {
1213  			item->setData(uuid, peer_role_uuid);
1214  			item->setData(addr, peer_role_address);
1215  			item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
1216  				      peer_role_type);
1217  			item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
1218  			item->setData(items.join(QString("\n")),
1219  				      peer_role_details);
1220  			item->setData(pri_dev_type, peer_role_pri_dev_type);
1221  			if (config_methods >= 0)
1222  				item->setData(config_methods,
1223  					      peer_role_config_methods);
1224  			if (dev_passwd_id >= 0)
1225  				item->setData(dev_passwd_id,
1226  					      peer_role_dev_passwd_id);
1227  			model.appendRow(item);
1228  		}
1229  
1230  		return;
1231  	}
1232  
1233  	if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
1234  		/*
1235  		 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
1236  		 * 02:66:a0:ee:17:27
1237  		 */
1238  		QStringList items = text.split(' ');
1239  		if (items.size() < 2)
1240  			return;
1241  		remove_enrollee_uuid(items[1]);
1242  		return;
1243  	}
1244  
1245  	if (text.startsWith(WPS_EVENT_ENROLLEE_SEEN)) {
1246  		/* TODO: need to time out this somehow or remove on successful
1247  		 * WPS run, etc. */
1248  		/*
1249  		 * WPS-ENROLLEE-SEEN 02:00:00:00:01:00
1250  		 * 572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1
1251  		 * [Wireless Client]
1252  		 * (MAC addr, UUID-E, pri dev type, config methods,
1253  		 * dev passwd id, request type, [dev name])
1254  		 */
1255  		QStringList items = text.split(' ');
1256  		if (items.size() < 7)
1257  			return;
1258  		QString addr = items[1];
1259  		QString uuid = items[2];
1260  		QString pri_dev_type = items[3];
1261  		int config_methods = items[4].toInt(0, 0);
1262  		int dev_passwd_id = items[5].toInt();
1263  		QString name;
1264  
1265  		QStandardItem *item = find_addr(addr);
1266  		if (item) {
1267  			int type = item->data(peer_role_type).toInt();
1268  			if (type == PEER_TYPE_ASSOCIATED_STATION)
1269  				return; /* already associated */
1270  		}
1271  
1272  		int pos = text.indexOf('[');
1273  		if (pos >= 0) {
1274  			int pos2 = text.lastIndexOf(']');
1275  			if (pos2 >= pos) {
1276  				QStringList items2 =
1277  					text.mid(pos + 1, pos2 - pos - 1).
1278  					split('|');
1279  				name = items2[0];
1280  			}
1281  		}
1282  		if (name.isEmpty())
1283  			name = addr;
1284  
1285  		item = find_uuid(uuid);
1286  		if (item) {
1287  			QVariant var = item->data(peer_role_config_methods);
1288  			QVariant var2 = item->data(peer_role_dev_passwd_id);
1289  			if ((var.isValid() && config_methods != var.toInt()) ||
1290  			    (var2.isValid() && dev_passwd_id != var2.toInt()))
1291  				remove_enrollee_uuid(uuid);
1292  			else
1293  				return;
1294  		}
1295  
1296  		item = new QStandardItem(*laptop_icon, name);
1297  		if (item) {
1298  			item->setData(uuid, peer_role_uuid);
1299  			item->setData(addr, peer_role_address);
1300  			item->setData(PEER_TYPE_WPS_ENROLLEE,
1301  				      peer_role_type);
1302  			item->setToolTip(ItemType(PEER_TYPE_WPS_ENROLLEE));
1303  			item->setData(items.join(QString("\n")),
1304  				      peer_role_details);
1305  			item->setData(pri_dev_type, peer_role_pri_dev_type);
1306  			item->setData(config_methods,
1307  				      peer_role_config_methods);
1308  			item->setData(dev_passwd_id, peer_role_dev_passwd_id);
1309  			model.appendRow(item);
1310  		}
1311  
1312  		return;
1313  	}
1314  
1315  	if (text.startsWith(WPA_EVENT_BSS_ADDED)) {
1316  		/* CTRL-EVENT-BSS-ADDED 34 00:11:22:33:44:55 */
1317  		QStringList items = text.split(' ');
1318  		if (items.size() < 2)
1319  			return;
1320  		char cmd[20];
1321  		snprintf(cmd, sizeof(cmd), "BSS ID-%d", items[1].toInt());
1322  		add_bss(cmd);
1323  		return;
1324  	}
1325  
1326  	if (text.startsWith(WPA_EVENT_BSS_REMOVED)) {
1327  		/* CTRL-EVENT-BSS-REMOVED 34 00:11:22:33:44:55 */
1328  		QStringList items = text.split(' ');
1329  		if (items.size() < 2)
1330  			return;
1331  		remove_bss(items[1].toInt());
1332  		return;
1333  	}
1334  }
1335  
1336  
ctx_p2p_connect()1337  void Peers::ctx_p2p_connect()
1338  {
1339  	if (ctx_item == NULL)
1340  		return;
1341  	QString addr = ctx_item->data(peer_role_address).toString();
1342  	QString arg;
1343  	int config_methods =
1344  		ctx_item->data(peer_role_config_methods).toInt();
1345  	enum selected_method method = SEL_METHOD_NONE;
1346  	QVariant var = ctx_item->data(peer_role_selected_method);
1347  	if (var.isValid())
1348  		method = (enum selected_method) var.toInt();
1349  	if (method == SEL_METHOD_PIN_LOCAL_DISPLAY) {
1350  		arg = ctx_item->data(peer_role_selected_pin).toString();
1351  		char cmd[100];
1352  		char reply[100];
1353  		size_t reply_len;
1354  		snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
1355  			 addr.toLocal8Bit().constData(),
1356  			 arg.toLocal8Bit().constData());
1357  		reply_len = sizeof(reply) - 1;
1358  		if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1359  			QMessageBox msg;
1360  			msg.setIcon(QMessageBox::Warning);
1361  			msg.setText("Failed to initiate P2P connect.");
1362  			msg.exec();
1363  			return;
1364  		}
1365  		QMessageBox::information(this,
1366  					 tr("PIN for ") + ctx_item->text(),
1367  					 tr("Enter the following PIN on the\n"
1368  					    "peer device: ") + arg);
1369  	} else if (method == SEL_METHOD_PIN_PEER_DISPLAY) {
1370  		StringQuery input(tr("PIN from peer display:"));
1371  		input.setWindowTitle(tr("PIN for ") + ctx_item->text());
1372  		if (input.exec() != QDialog::Accepted)
1373  			return;
1374  		arg = input.get_string();
1375  	} else if (config_methods == 0x0080 /* PBC */) {
1376  		arg = "pbc";
1377  	} else {
1378  		StringQuery input(tr("PIN:"));
1379  		input.setWindowTitle(tr("PIN for ") + ctx_item->text());
1380  		if (input.exec() != QDialog::Accepted)
1381  			return;
1382  		arg = input.get_string();
1383  	}
1384  
1385  	char cmd[100];
1386  	char reply[100];
1387  	size_t reply_len;
1388  	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s",
1389  		 addr.toLocal8Bit().constData(),
1390  		 arg.toLocal8Bit().constData());
1391  	reply_len = sizeof(reply) - 1;
1392  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1393  		QMessageBox msg;
1394  		msg.setIcon(QMessageBox::Warning);
1395  		msg.setText("Failed to initiate P2P connect.");
1396  		msg.exec();
1397  	}
1398  }
1399  
1400  
ctx_p2p_req_pin()1401  void Peers::ctx_p2p_req_pin()
1402  {
1403  	if (ctx_item == NULL)
1404  		return;
1405  	QString addr = ctx_item->data(peer_role_address).toString();
1406  	ctx_item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
1407  			  peer_role_requested_method);
1408  
1409  	char cmd[100];
1410  	char reply[100];
1411  	size_t reply_len;
1412  	snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s display",
1413  		 addr.toLocal8Bit().constData());
1414  	reply_len = sizeof(reply) - 1;
1415  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1416  		QMessageBox msg;
1417  		msg.setIcon(QMessageBox::Warning);
1418  		msg.setText(tr("Failed to request PIN from peer."));
1419  		msg.exec();
1420  	}
1421  }
1422  
1423  
ctx_p2p_show_pin()1424  void Peers::ctx_p2p_show_pin()
1425  {
1426  	if (ctx_item == NULL)
1427  		return;
1428  	QString addr = ctx_item->data(peer_role_address).toString();
1429  	ctx_item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
1430  			  peer_role_requested_method);
1431  
1432  	char cmd[100];
1433  	char reply[100];
1434  	size_t reply_len;
1435  	snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s keypad",
1436  		 addr.toLocal8Bit().constData());
1437  	reply_len = sizeof(reply) - 1;
1438  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1439  		QMessageBox msg;
1440  		msg.setIcon(QMessageBox::Warning);
1441  		msg.setText(tr("Failed to request peer to enter PIN."));
1442  		msg.exec();
1443  	}
1444  }
1445  
1446  
ctx_p2p_display_pin()1447  void Peers::ctx_p2p_display_pin()
1448  {
1449  	if (ctx_item == NULL)
1450  		return;
1451  	QString addr = ctx_item->data(peer_role_address).toString();
1452  
1453  	char cmd[100];
1454  	char reply[100];
1455  	size_t reply_len;
1456  	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pin",
1457  		 addr.toLocal8Bit().constData());
1458  	reply_len = sizeof(reply) - 1;
1459  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1460  		QMessageBox msg;
1461  		msg.setIcon(QMessageBox::Warning);
1462  		msg.setText("Failed to initiate P2P connect.");
1463  		msg.exec();
1464  		return;
1465  	}
1466  	reply[reply_len] = '\0';
1467  	QMessageBox::information(this,
1468  				 tr("PIN for ") + ctx_item->text(),
1469  				 tr("Enter the following PIN on the\n"
1470  				    "peer device: ") + reply);
1471  }
1472  
1473  
ctx_p2p_display_pin_pd()1474  void Peers::ctx_p2p_display_pin_pd()
1475  {
1476  	if (ctx_item == NULL)
1477  		return;
1478  	QString addr = ctx_item->data(peer_role_address).toString();
1479  	QString arg = ctx_item->data(peer_role_selected_pin).toString();
1480  
1481  	char cmd[100];
1482  	char reply[100];
1483  	size_t reply_len;
1484  	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
1485  		 addr.toLocal8Bit().constData(),
1486  		 arg.toLocal8Bit().constData());
1487  	reply_len = sizeof(reply) - 1;
1488  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1489  		QMessageBox msg;
1490  		msg.setIcon(QMessageBox::Warning);
1491  		msg.setText("Failed to initiate P2P connect.");
1492  		msg.exec();
1493  		return;
1494  	}
1495  	reply[reply_len] = '\0';
1496  	QMessageBox::information(this,
1497  				 tr("PIN for ") + ctx_item->text(),
1498  				 tr("Enter the following PIN on the\n"
1499  				    "peer device: ") + arg);
1500  }
1501  
1502  
ctx_p2p_enter_pin()1503  void Peers::ctx_p2p_enter_pin()
1504  {
1505  	if (ctx_item == NULL)
1506  		return;
1507  	QString addr = ctx_item->data(peer_role_address).toString();
1508  	QString arg;
1509  
1510  	StringQuery input(tr("PIN from peer:"));
1511  	input.setWindowTitle(tr("PIN for ") + ctx_item->text());
1512  	if (input.exec() != QDialog::Accepted)
1513  		return;
1514  	arg = input.get_string();
1515  
1516  	char cmd[100];
1517  	char reply[100];
1518  	size_t reply_len;
1519  	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s keypad",
1520  		 addr.toLocal8Bit().constData(),
1521  		 arg.toLocal8Bit().constData());
1522  	reply_len = sizeof(reply) - 1;
1523  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1524  		QMessageBox msg;
1525  		msg.setIcon(QMessageBox::Warning);
1526  		msg.setText("Failed to initiate P2P connect.");
1527  		msg.exec();
1528  	}
1529  }
1530  
1531  
ctx_p2p_remove_group()1532  void Peers::ctx_p2p_remove_group()
1533  {
1534  	if (ctx_item == NULL)
1535  		return;
1536  	char cmd[100];
1537  	char reply[100];
1538  	size_t reply_len;
1539  	snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s",
1540  		 ctx_item->data(peer_role_ifname).toString().toLocal8Bit().
1541  		 constData());
1542  	reply_len = sizeof(reply) - 1;
1543  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1544  		QMessageBox msg;
1545  		msg.setIcon(QMessageBox::Warning);
1546  		msg.setText("Failed to remove P2P Group.");
1547  		msg.exec();
1548  	}
1549  }
1550  
1551  
closeEvent(QCloseEvent *)1552  void Peers::closeEvent(QCloseEvent *)
1553  {
1554  	if (wpagui) {
1555  		char reply[20];
1556  		size_t replylen = sizeof(reply) - 1;
1557  		wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
1558  	}
1559  }
1560  
1561  
done(int r)1562  void Peers::done(int r)
1563  {
1564  	QDialog::done(r);
1565  	close();
1566  }
1567  
1568  
remove_enrollee_uuid(QString uuid)1569  void Peers::remove_enrollee_uuid(QString uuid)
1570  {
1571  	if (model.rowCount() == 0)
1572  		return;
1573  
1574  	QModelIndexList lst = model.match(model.index(0, 0),
1575  					  peer_role_uuid, uuid);
1576  	for (int i = 0; i < lst.size(); i++) {
1577  		QStandardItem *item = model.itemFromIndex(lst[i]);
1578  		if (item == NULL)
1579  			continue;
1580  		int type = item->data(peer_role_type).toInt();
1581  		if (type == PEER_TYPE_WPS_ER_ENROLLEE ||
1582  		    type == PEER_TYPE_WPS_ENROLLEE)
1583  			model.removeRow(lst[i].row());
1584  	}
1585  }
1586  
1587  
properties()1588  void Peers::properties()
1589  {
1590  	if (ctx_item == NULL)
1591  		return;
1592  
1593  	QMessageBox msg(this);
1594  	msg.setStandardButtons(QMessageBox::Ok);
1595  	msg.setDefaultButton(QMessageBox::Ok);
1596  	msg.setEscapeButton(QMessageBox::Ok);
1597  	msg.setWindowTitle(tr("Peer Properties"));
1598  
1599  	int type = ctx_item->data(peer_role_type).toInt();
1600  	QString title = Peers::ItemType(type);
1601  
1602  	msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
1603  
1604  	QVariant var;
1605  	QString info;
1606  
1607  	var = ctx_item->data(peer_role_address);
1608  	if (var.isValid())
1609  		info += tr("Address: ") + var.toString() + QString("\n");
1610  
1611  	var = ctx_item->data(peer_role_uuid);
1612  	if (var.isValid())
1613  		info += tr("UUID: ") + var.toString() + QString("\n");
1614  
1615  	var = ctx_item->data(peer_role_pri_dev_type);
1616  	if (var.isValid())
1617  		info += tr("Primary Device Type: ") + var.toString() +
1618  			QString("\n");
1619  
1620  	var = ctx_item->data(peer_role_ssid);
1621  	if (var.isValid())
1622  		info += tr("SSID: ") + var.toString() + QString("\n");
1623  
1624  	var = ctx_item->data(peer_role_config_methods);
1625  	if (var.isValid()) {
1626  		int methods = var.toInt();
1627  		info += tr("Configuration Methods: ");
1628  		if (methods & 0x0001)
1629  			info += tr("[USBA]");
1630  		if (methods & 0x0002)
1631  			info += tr("[Ethernet]");
1632  		if (methods & 0x0004)
1633  			info += tr("[Label]");
1634  		if (methods & 0x0008)
1635  			info += tr("[Display]");
1636  		if (methods & 0x0010)
1637  			info += tr("[Ext. NFC Token]");
1638  		if (methods & 0x0020)
1639  			info += tr("[Int. NFC Token]");
1640  		if (methods & 0x0040)
1641  			info += tr("[NFC Interface]");
1642  		if (methods & 0x0080)
1643  			info += tr("[Push Button]");
1644  		if (methods & 0x0100)
1645  			info += tr("[Keypad]");
1646  		info += "\n";
1647  	}
1648  
1649  	var = ctx_item->data(peer_role_selected_method);
1650  	if (var.isValid()) {
1651  		enum selected_method method =
1652  			(enum selected_method) var.toInt();
1653  		switch (method) {
1654  		case SEL_METHOD_NONE:
1655  			break;
1656  		case SEL_METHOD_PIN_PEER_DISPLAY:
1657  			info += tr("Selected Method: PIN on peer display\n");
1658  			break;
1659  		case SEL_METHOD_PIN_LOCAL_DISPLAY:
1660  			info += tr("Selected Method: PIN on local display\n");
1661  			break;
1662  		}
1663  	}
1664  
1665  	var = ctx_item->data(peer_role_selected_pin);
1666  	if (var.isValid()) {
1667  		info += tr("PIN to enter on peer: ") + var.toString() + "\n";
1668  	}
1669  
1670  	var = ctx_item->data(peer_role_dev_passwd_id);
1671  	if (var.isValid()) {
1672  		info += tr("Device Password ID: ") + var.toString();
1673  		switch (var.toInt()) {
1674  		case 0:
1675  			info += tr(" (Default PIN)");
1676  			break;
1677  		case 1:
1678  			info += tr(" (User-specified PIN)");
1679  			break;
1680  		case 2:
1681  			info += tr(" (Machine-specified PIN)");
1682  			break;
1683  		case 3:
1684  			info += tr(" (Rekey)");
1685  			break;
1686  		case 4:
1687  			info += tr(" (Push Button)");
1688  			break;
1689  		case 5:
1690  			info += tr(" (Registrar-specified)");
1691  			break;
1692  		}
1693  		info += "\n";
1694  	}
1695  
1696  	msg.setInformativeText(info);
1697  
1698  	var = ctx_item->data(peer_role_details);
1699  	if (var.isValid())
1700  		msg.setDetailedText(var.toString());
1701  
1702  	msg.exec();
1703  }
1704  
1705  
connect_pbc()1706  void Peers::connect_pbc()
1707  {
1708  	if (ctx_item == NULL)
1709  		return;
1710  
1711  	char cmd[100];
1712  	char reply[100];
1713  	size_t reply_len;
1714  
1715  	int peer_type = ctx_item->data(peer_role_type).toInt();
1716  	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
1717  		snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
1718  			 ctx_item->data(peer_role_uuid).toString().toLocal8Bit().
1719  			 constData());
1720  	} else if (peer_type == PEER_TYPE_P2P ||
1721  		   peer_type == PEER_TYPE_P2P_CLIENT) {
1722  		snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pbc",
1723  			 ctx_item->data(peer_role_address).toString().
1724  			 toLocal8Bit().constData());
1725  	} else {
1726  		snprintf(cmd, sizeof(cmd), "WPS_PBC");
1727  	}
1728  	reply_len = sizeof(reply) - 1;
1729  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1730  		QMessageBox msg;
1731  		msg.setIcon(QMessageBox::Warning);
1732  		msg.setText(tr("Failed to start WPS PBC."));
1733  		msg.exec();
1734  	}
1735  }
1736  
1737  
learn_ap_config()1738  void Peers::learn_ap_config()
1739  {
1740  	if (ctx_item == NULL)
1741  		return;
1742  
1743  	QString uuid = ctx_item->data(peer_role_uuid).toString();
1744  
1745  	StringQuery input(tr("AP PIN:"));
1746  	input.setWindowTitle(tr("AP PIN for ") + ctx_item->text());
1747  	if (input.exec() != QDialog::Accepted)
1748  		return;
1749  
1750  	char cmd[100];
1751  	char reply[100];
1752  	size_t reply_len;
1753  
1754  	snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
1755  		 uuid.toLocal8Bit().constData(),
1756  		 input.get_string().toLocal8Bit().constData());
1757  	reply_len = sizeof(reply) - 1;
1758  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
1759  		QMessageBox msg;
1760  		msg.setIcon(QMessageBox::Warning);
1761  		msg.setText(tr("Failed to start learning AP configuration."));
1762  		msg.exec();
1763  	}
1764  }
1765  
1766  
ctx_hide_ap()1767  void Peers::ctx_hide_ap()
1768  {
1769  	hide_ap = true;
1770  
1771  	if (model.rowCount() == 0)
1772  		return;
1773  
1774  	do {
1775  		QModelIndexList lst;
1776  		lst = model.match(model.index(0, 0),
1777  				  peer_role_type, PEER_TYPE_AP);
1778  		if (lst.size() == 0) {
1779  			lst = model.match(model.index(0, 0),
1780  					  peer_role_type, PEER_TYPE_AP_WPS);
1781  			if (lst.size() == 0)
1782  				break;
1783  		}
1784  
1785  		model.removeRow(lst[0].row());
1786  	} while (1);
1787  }
1788  
1789  
ctx_show_ap()1790  void Peers::ctx_show_ap()
1791  {
1792  	hide_ap = false;
1793  	add_scan_results();
1794  }
1795  
1796  
ctx_p2p_show_passphrase()1797  void Peers::ctx_p2p_show_passphrase()
1798  {
1799  	char reply[64];
1800  	size_t reply_len;
1801  
1802  	reply_len = sizeof(reply) - 1;
1803  	if (wpagui->ctrlRequest("P2P_GET_PASSPHRASE", reply, &reply_len) < 0 ||
1804  	    memcmp(reply, "FAIL", 4) == 0) {
1805  		QMessageBox msg;
1806  		msg.setIcon(QMessageBox::Warning);
1807  		msg.setText("Failed to get P2P group passphrase.");
1808  		msg.exec();
1809  	} else {
1810  		reply[reply_len] = '\0';
1811  		QMessageBox::information(this, tr("Passphrase"),
1812  					 tr("P2P group passphrase:\n") +
1813  					 reply);
1814  	}
1815  }
1816  
1817  
ctx_p2p_start_persistent()1818  void Peers::ctx_p2p_start_persistent()
1819  {
1820  	if (ctx_item == NULL)
1821  		return;
1822  
1823  	char cmd[100];
1824  	char reply[100];
1825  	size_t reply_len;
1826  
1827  	snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD persistent=%d",
1828  		 ctx_item->data(peer_role_network_id).toInt());
1829  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
1830  	    memcmp(reply, "FAIL", 4) == 0) {
1831  		QMessageBox msg;
1832  		msg.setIcon(QMessageBox::Warning);
1833  		msg.setText(tr("Failed to start persistent P2P Group."));
1834  		msg.exec();
1835  	} else if (ctx_item->data(peer_role_type).toInt() ==
1836  		   PEER_TYPE_P2P_INVITATION)
1837  		model.removeRow(ctx_item->row());
1838  }
1839  
1840  
ctx_p2p_invite()1841  void Peers::ctx_p2p_invite()
1842  {
1843  	if (ctx_item == NULL)
1844  		return;
1845  
1846  	char cmd[100];
1847  	char reply[100];
1848  	size_t reply_len;
1849  
1850  	snprintf(cmd, sizeof(cmd), "P2P_INVITE persistent=%d",
1851  		 ctx_item->data(peer_role_network_id).toInt());
1852  	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
1853  	    memcmp(reply, "FAIL", 4) == 0) {
1854  		QMessageBox msg;
1855  		msg.setIcon(QMessageBox::Warning);
1856  		msg.setText(tr("Failed to invite peer to start persistent "
1857  			       "P2P Group."));
1858  		msg.exec();
1859  	}
1860  }
1861  
1862  
ctx_p2p_delete()1863  void Peers::ctx_p2p_delete()
1864  {
1865  	if (ctx_item == NULL)
1866  		return;
1867  	model.removeRow(ctx_item->row());
1868  }
1869  
1870  
enable_persistent(int id)1871  void Peers::enable_persistent(int id)
1872  {
1873  	if (model.rowCount() == 0)
1874  		return;
1875  
1876  	QModelIndexList lst = model.match(model.index(0, 0),
1877  					  peer_role_network_id, id);
1878  	for (int i = 0; i < lst.size(); i++) {
1879  		QStandardItem *item = model.itemFromIndex(lst[i]);
1880  		int type = item->data(peer_role_type).toInt();
1881  		if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
1882  		    type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT)
1883  			item->setBackground(Qt::NoBrush);
1884  	}
1885  }
1886