/*
 * wpa_gui - Peers class
 * Copyright (c) 2009-2010, Atheros Communications
 *
 * This software may be distributed under the terms of the BSD license.
 * See README for more details.
 */

#include <cstdio>
#include <QImageReader>
#include <QMessageBox>

#include "common/wpa_ctrl.h"
#include "wpagui.h"
#include "stringquery.h"
#include "peers.h"


enum {
	peer_role_address = Qt::UserRole + 1,
	peer_role_type,
	peer_role_uuid,
	peer_role_details,
	peer_role_ifname,
	peer_role_pri_dev_type,
	peer_role_ssid,
	peer_role_config_methods,
	peer_role_dev_passwd_id,
	peer_role_bss_id,
	peer_role_selected_method,
	peer_role_selected_pin,
	peer_role_requested_method,
	peer_role_network_id
};

enum selected_method {
	SEL_METHOD_NONE,
	SEL_METHOD_PIN_PEER_DISPLAY,
	SEL_METHOD_PIN_LOCAL_DISPLAY
};

/*
 * TODO:
 * - add current AP info (e.g., from WPS) in station mode
 */

enum peer_type {
	PEER_TYPE_ASSOCIATED_STATION,
	PEER_TYPE_AP,
	PEER_TYPE_AP_WPS,
	PEER_TYPE_WPS_PIN_NEEDED,
	PEER_TYPE_P2P,
	PEER_TYPE_P2P_CLIENT,
	PEER_TYPE_P2P_GROUP,
	PEER_TYPE_P2P_PERSISTENT_GROUP_GO,
	PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT,
	PEER_TYPE_P2P_INVITATION,
	PEER_TYPE_WPS_ER_AP,
	PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
	PEER_TYPE_WPS_ER_ENROLLEE,
	PEER_TYPE_WPS_ENROLLEE
};


Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
	: QDialog(parent)
{
	setupUi(this);

	if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
	{
		default_icon = new QIcon(":/icons/wpa_gui.svg");
		ap_icon = new QIcon(":/icons/ap.svg");
		laptop_icon = new QIcon(":/icons/laptop.svg");
		group_icon = new QIcon(":/icons/group.svg");
		invitation_icon = new QIcon(":/icons/invitation.svg");
	} else {
		default_icon = new QIcon(":/icons/wpa_gui.png");
		ap_icon = new QIcon(":/icons/ap.png");
		laptop_icon = new QIcon(":/icons/laptop.png");
		group_icon = new QIcon(":/icons/group.png");
		invitation_icon = new QIcon(":/icons/invitation.png");
	}

	peers->setModel(&model);
	peers->setResizeMode(QListView::Adjust);
	peers->setDragEnabled(false);
	peers->setSelectionMode(QAbstractItemView::NoSelection);

	peers->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
		this, SLOT(context_menu(const QPoint &)));

	wpagui = NULL;
	hide_ap = false;
}


void Peers::setWpaGui(WpaGui *_wpagui)
{
	wpagui = _wpagui;
	update_peers();
}


Peers::~Peers()
{
	delete default_icon;
	delete ap_icon;
	delete laptop_icon;
	delete group_icon;
	delete invitation_icon;
}


void Peers::languageChange()
{
	retranslateUi(this);
}


QString Peers::ItemType(int type)
{
	QString title;
	switch (type) {
	case PEER_TYPE_ASSOCIATED_STATION:
		title = tr("Associated station");
		break;
	case PEER_TYPE_AP:
		title = tr("AP");
		break;
	case PEER_TYPE_AP_WPS:
		title = tr("WPS AP");
		break;
	case PEER_TYPE_WPS_PIN_NEEDED:
		title = tr("WPS PIN needed");
		break;
	case PEER_TYPE_P2P:
		title = tr("P2P Device");
		break;
	case PEER_TYPE_P2P_CLIENT:
		title = tr("P2P Device (group client)");
		break;
	case PEER_TYPE_P2P_GROUP:
		title = tr("P2P Group");
		break;
	case PEER_TYPE_P2P_PERSISTENT_GROUP_GO:
		title = tr("P2P Persistent Group (GO)");
		break;
	case PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT:
		title = tr("P2P Persistent Group (client)");
		break;
	case PEER_TYPE_P2P_INVITATION:
		title = tr("P2P Invitation");
		break;
	case PEER_TYPE_WPS_ER_AP:
		title = tr("ER: WPS AP");
		break;
	case PEER_TYPE_WPS_ER_AP_UNCONFIGURED:
		title = tr("ER: WPS AP (Unconfigured)");
		break;
	case PEER_TYPE_WPS_ER_ENROLLEE:
		title = tr("ER: WPS Enrollee");
		break;
	case PEER_TYPE_WPS_ENROLLEE:
		title = tr("WPS Enrollee");
		break;
	}
	return title;
}


void Peers::context_menu(const QPoint &pos)
{
	QMenu *menu = new QMenu;
	if (menu == NULL)
		return;

	QModelIndex idx = peers->indexAt(pos);
	if (idx.isValid()) {
		ctx_item = model.itemFromIndex(idx);
		int type = ctx_item->data(peer_role_type).toInt();
		menu->addAction(Peers::ItemType(type))->setEnabled(false);
		menu->addSeparator();

		int config_methods = -1;
		QVariant var = ctx_item->data(peer_role_config_methods);
		if (var.isValid())
			config_methods = var.toInt();

		enum selected_method method = SEL_METHOD_NONE;
		var = ctx_item->data(peer_role_selected_method);
		if (var.isValid())
			method = (enum selected_method) var.toInt();

		if ((type == PEER_TYPE_ASSOCIATED_STATION ||
		     type == PEER_TYPE_AP_WPS ||
		     type == PEER_TYPE_WPS_PIN_NEEDED ||
		     type == PEER_TYPE_WPS_ER_ENROLLEE ||
		     type == PEER_TYPE_WPS_ENROLLEE) &&
		    (config_methods == -1 || (config_methods & 0x010c))) {
			menu->addAction(tr("Enter WPS PIN"), this,
					SLOT(enter_pin()));
		}

		if (type == PEER_TYPE_P2P || type == PEER_TYPE_P2P_CLIENT) {
			menu->addAction(tr("P2P Connect"), this,
					SLOT(ctx_p2p_connect()));
			if (method == SEL_METHOD_NONE &&
			    config_methods > -1 &&
			    config_methods & 0x0080 /* PBC */ &&
			    config_methods != 0x0080)
				menu->addAction(tr("P2P Connect (PBC)"), this,
						SLOT(connect_pbc()));
			if (method == SEL_METHOD_NONE) {
				menu->addAction(tr("P2P Request PIN"), this,
						SLOT(ctx_p2p_req_pin()));
				menu->addAction(tr("P2P Show PIN"), this,
						SLOT(ctx_p2p_show_pin()));
			}

			if (config_methods > -1 && (config_methods & 0x0100)) {
				/* Peer has Keypad */
				menu->addAction(tr("P2P Display PIN"), this,
						SLOT(ctx_p2p_display_pin()));
			}

			if (config_methods > -1 && (config_methods & 0x000c)) {
				/* Peer has Label or Display */
				menu->addAction(tr("P2P Enter PIN"), this,
						SLOT(ctx_p2p_enter_pin()));
			}
		}

		if (type == PEER_TYPE_P2P_GROUP) {
			menu->addAction(tr("Show passphrase"), this,
					SLOT(ctx_p2p_show_passphrase()));
			menu->addAction(tr("Remove P2P Group"), this,
					SLOT(ctx_p2p_remove_group()));
		}

		if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
		    type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT ||
		    type == PEER_TYPE_P2P_INVITATION) {
			menu->addAction(tr("Start group"), this,
					SLOT(ctx_p2p_start_persistent()));
		}

		if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
		    type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) {
			menu->addAction(tr("Invite"), this,
					SLOT(ctx_p2p_invite()));
		}

		if (type == PEER_TYPE_P2P_INVITATION) {
			menu->addAction(tr("Ignore"), this,
					SLOT(ctx_p2p_delete()));
		}

		if (type == PEER_TYPE_AP_WPS) {
			menu->addAction(tr("Connect (PBC)"), this,
					SLOT(connect_pbc()));
		}

		if ((type == PEER_TYPE_ASSOCIATED_STATION ||
		     type == PEER_TYPE_WPS_ER_ENROLLEE ||
		     type == PEER_TYPE_WPS_ENROLLEE) &&
		    config_methods >= 0 && (config_methods & 0x0080)) {
			menu->addAction(tr("Enroll (PBC)"), this,
					SLOT(connect_pbc()));
		}

		if (type == PEER_TYPE_WPS_ER_AP) {
			menu->addAction(tr("Learn Configuration"), this,
					SLOT(learn_ap_config()));
		}

		menu->addAction(tr("Properties"), this, SLOT(properties()));
	} else {
		ctx_item = NULL;
		menu->addAction(QString(tr("Refresh")), this,
				SLOT(ctx_refresh()));
		menu->addAction(tr("Start P2P discovery"), this,
				SLOT(ctx_p2p_start()));
		menu->addAction(tr("Stop P2P discovery"), this,
				SLOT(ctx_p2p_stop()));
		menu->addAction(tr("P2P listen only"), this,
				SLOT(ctx_p2p_listen()));
		menu->addAction(tr("Start P2P group"), this,
				SLOT(ctx_p2p_start_group()));
		if (hide_ap)
			menu->addAction(tr("Show AP entries"), this,
					SLOT(ctx_show_ap()));
		else
			menu->addAction(tr("Hide AP entries"), this,
					SLOT(ctx_hide_ap()));
	}

	menu->exec(peers->mapToGlobal(pos));
}


void Peers::enter_pin()
{
	if (ctx_item == NULL)
		return;

	int peer_type = ctx_item->data(peer_role_type).toInt();
	QString uuid;
	QString addr;
	addr = ctx_item->data(peer_role_address).toString();
	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
		uuid = ctx_item->data(peer_role_uuid).toString();

	StringQuery input(tr("PIN:"));
	input.setWindowTitle(tr("PIN for ") + ctx_item->text());
	if (input.exec() != QDialog::Accepted)
		return;

	char cmd[100];
	char reply[100];
	size_t reply_len;

	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
		snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s",
			 uuid.toAscii().constData(),
			 input.get_string().toAscii().constData(),
			 addr.toAscii().constData());
	} else {
		snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
			 addr.toAscii().constData(),
			 input.get_string().toAscii().constData());
	}
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText(tr("Failed to set the WPS PIN."));
		msg.exec();
	}
}


void Peers::ctx_refresh()
{
	update_peers();
}


void Peers::ctx_p2p_start()
{
	char reply[20];
	size_t reply_len;
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest("P2P_FIND", reply, &reply_len) < 0 ||
	    memcmp(reply, "FAIL", 4) == 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText("Failed to start P2P discovery.");
		msg.exec();
	}
}


void Peers::ctx_p2p_stop()
{
	char reply[20];
	size_t reply_len;
	reply_len = sizeof(reply) - 1;
	wpagui->ctrlRequest("P2P_STOP_FIND", reply, &reply_len);
}


void Peers::ctx_p2p_listen()
{
	char reply[20];
	size_t reply_len;
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest("P2P_LISTEN 3600", reply, &reply_len) < 0 ||
	    memcmp(reply, "FAIL", 4) == 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText("Failed to start P2P listen.");
		msg.exec();
	}
}


void Peers::ctx_p2p_start_group()
{
	char reply[20];
	size_t reply_len;
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest("P2P_GROUP_ADD", reply, &reply_len) < 0 ||
	    memcmp(reply, "FAIL", 4) == 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText("Failed to start P2P group.");
		msg.exec();
	}
}


void Peers::add_station(QString info)
{
	QStringList lines = info.split(QRegExp("\\n"));
	QString name;

	for (QStringList::Iterator it = lines.begin();
	     it != lines.end(); it++) {
		int pos = (*it).indexOf('=') + 1;
		if (pos < 1)
			continue;

		if ((*it).startsWith("wpsDeviceName="))
			name = (*it).mid(pos);
		else if ((*it).startsWith("p2p_device_name="))
			name = (*it).mid(pos);
	}

	if (name.isEmpty())
		name = lines[0];

	QStandardItem *item = new QStandardItem(*laptop_icon, name);
	if (item) {
		/* Remove WPS enrollee entry if one is still pending */
		if (model.rowCount() > 0) {
			QModelIndexList lst = model.match(model.index(0, 0),
							  peer_role_address,
							  lines[0]);
			for (int i = 0; i < lst.size(); i++) {
				QStandardItem *item;
				item = model.itemFromIndex(lst[i]);
				if (item == NULL)
					continue;
				int type = item->data(peer_role_type).toInt();
				if (type == PEER_TYPE_WPS_ENROLLEE) {
					model.removeRow(lst[i].row());
					break;
				}
			}
		}

		item->setData(lines[0], peer_role_address);
		item->setData(PEER_TYPE_ASSOCIATED_STATION,
			      peer_role_type);
		item->setData(info, peer_role_details);
		item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
		model.appendRow(item);
	}
}


void Peers::add_stations()
{
	char reply[2048];
	size_t reply_len;
	char cmd[30];
	int res;

	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
		return;

	do {
		reply[reply_len] = '\0';
		QString info(reply);
		char *txt = reply;
		while (*txt != '\0' && *txt != '\n')
			txt++;
		*txt++ = '\0';
		if (strncmp(reply, "FAIL", 4) == 0 ||
		    strncmp(reply, "UNKNOWN", 7) == 0)
			break;

		add_station(info);

		reply_len = sizeof(reply) - 1;
		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
		res = wpagui->ctrlRequest(cmd, reply, &reply_len);
	} while (res >= 0);
}


void Peers::add_single_station(const char *addr)
{
	char reply[2048];
	size_t reply_len;
	char cmd[30];

	reply_len = sizeof(reply) - 1;
	snprintf(cmd, sizeof(cmd), "STA %s", addr);
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
		return;

	reply[reply_len] = '\0';
	QString info(reply);
	char *txt = reply;
	while (*txt != '\0' && *txt != '\n')
		txt++;
	*txt++ = '\0';
	if (strncmp(reply, "FAIL", 4) == 0 ||
	    strncmp(reply, "UNKNOWN", 7) == 0)
		return;

	add_station(info);
}


void Peers::add_p2p_group_client(QStandardItem * /*parent*/, QString params)
{
	/*
	 * dev=02:b5:64:63:30:63 iface=02:b5:64:63:30:63 dev_capab=0x0
	 * dev_type=1-0050f204-1 dev_name='Wireless Client'
	 * config_methods=0x8c
	 */

	QStringList items =
		params.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
	QString addr = "";
	QString name = "";
	int config_methods = 0;
	QString dev_type;

	for (int i = 0; i < items.size(); i++) {
		QString str = items.at(i);
		int pos = str.indexOf('=') + 1;
		if (str.startsWith("dev_name='"))
			name = str.section('\'', 1, -2);
		else if (str.startsWith("config_methods="))
			config_methods =
				str.section('=', 1).toInt(0, 0);
		else if (str.startsWith("dev="))
			addr = str.mid(pos);
		else if (str.startsWith("dev_type=") && dev_type.isEmpty())
			dev_type = str.mid(pos);
	}

	QStandardItem *item = find_addr(addr);
	if (item)
		return;

	item = new QStandardItem(*default_icon, name);
	if (item) {
		/* TODO: indicate somehow the relationship to the group owner
		 * (parent) */
		item->setData(addr, peer_role_address);
		item->setData(config_methods, peer_role_config_methods);
		item->setData(PEER_TYPE_P2P_CLIENT, peer_role_type);
		if (!dev_type.isEmpty())
			item->setData(dev_type, peer_role_pri_dev_type);
		item->setData(items.join(QString("\n")), peer_role_details);
		item->setToolTip(ItemType(PEER_TYPE_P2P_CLIENT));
		model.appendRow(item);
	}
}


void Peers::remove_bss(int id)
{
	if (model.rowCount() == 0)
		return;

	QModelIndexList lst = model.match(model.index(0, 0), peer_role_bss_id,
					  id);
	if (lst.size() == 0)
		return;
	model.removeRow(lst[0].row());
}


bool Peers::add_bss(const char *cmd)
{
	char reply[2048];
	size_t reply_len;

	if (hide_ap)
		return false;

	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
		return false;
	reply[reply_len] = '\0';

	QString bss(reply);
	if (bss.isEmpty() || bss.startsWith("FAIL"))
		return false;

	QString ssid, bssid, flags, wps_name, pri_dev_type;
	int id = -1;

	QStringList lines = bss.split(QRegExp("\\n"));
	for (QStringList::Iterator it = lines.begin();
	     it != lines.end(); it++) {
		int pos = (*it).indexOf('=') + 1;
		if (pos < 1)
			continue;

		if ((*it).startsWith("bssid="))
			bssid = (*it).mid(pos);
		else if ((*it).startsWith("id="))
			id = (*it).mid(pos).toInt();
		else if ((*it).startsWith("flags="))
			flags = (*it).mid(pos);
		else if ((*it).startsWith("ssid="))
			ssid = (*it).mid(pos);
		else if ((*it).startsWith("wps_device_name="))
			wps_name = (*it).mid(pos);
		else if ((*it).startsWith("wps_primary_device_type="))
			pri_dev_type = (*it).mid(pos);
	}

	QString name = wps_name;
	if (name.isEmpty())
		name = ssid + "\n" + bssid;

	QStandardItem *item = new QStandardItem(*ap_icon, name);
	if (item) {
		item->setData(bssid, peer_role_address);
		if (id >= 0)
			item->setData(id, peer_role_bss_id);
		int type;
		if (flags.contains("[WPS"))
			type = PEER_TYPE_AP_WPS;
		else
			type = PEER_TYPE_AP;
		item->setData(type, peer_role_type);

		for (int i = 0; i < lines.size(); i++) {
			if (lines[i].length() > 60) {
				lines[i].remove(60, lines[i].length());
				lines[i] += "..";
			}
		}
		item->setToolTip(ItemType(type));
		item->setData(lines.join("\n"), peer_role_details);
		if (!pri_dev_type.isEmpty())
			item->setData(pri_dev_type,
				      peer_role_pri_dev_type);
		if (!ssid.isEmpty())
			item->setData(ssid, peer_role_ssid);
		model.appendRow(item);

		lines = bss.split(QRegExp("\\n"));
		for (QStringList::Iterator it = lines.begin();
		     it != lines.end(); it++) {
			if ((*it).startsWith("p2p_group_client:"))
				add_p2p_group_client(item,
						     (*it).mid(18));
		}
	}

	return true;
}


void Peers::add_scan_results()
{
	int index;
	char cmd[20];

	index = 0;
	while (wpagui) {
		snprintf(cmd, sizeof(cmd), "BSS %d", index++);
		if (index > 1000)
			break;

		if (!add_bss(cmd))
			break;
	}
}


void Peers::add_persistent(int id, const char *ssid, const char *bssid)
{
	char cmd[100];
	char reply[100];
	size_t reply_len;
	int mode;

	snprintf(cmd, sizeof(cmd), "GET_NETWORK %d mode", id);
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
		return;
	reply[reply_len] = '\0';
	mode = atoi(reply);

	QString name = ssid;
	name = '[' + name + ']';

	QStandardItem *item = new QStandardItem(*group_icon, name);
	if (!item)
		return;

	int type;
	if (mode == 3)
		type = PEER_TYPE_P2P_PERSISTENT_GROUP_GO;
	else
		type = PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT;
	item->setData(type, peer_role_type);
	item->setToolTip(ItemType(type));
	item->setData(ssid, peer_role_ssid);
	if (bssid && strcmp(bssid, "any") == 0)
		bssid = NULL;
	if (bssid)
		item->setData(bssid, peer_role_address);
	item->setData(id, peer_role_network_id);
	item->setBackground(Qt::BDiagPattern);

	model.appendRow(item);
}


void Peers::add_persistent_groups()
{
	char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
	size_t len;

	len = sizeof(buf) - 1;
	if (wpagui->ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
		return;

	buf[len] = '\0';
	start = strchr(buf, '\n');
	if (start == NULL)
		return;
	start++;

	while (*start) {
		bool last = false;
		end = strchr(start, '\n');
		if (end == NULL) {
			last = true;
			end = start;
			while (end[0] && end[1])
				end++;
		}
		*end = '\0';

		id = start;
		ssid = strchr(id, '\t');
		if (ssid == NULL)
			break;
		*ssid++ = '\0';
		bssid = strchr(ssid, '\t');
		if (bssid == NULL)
			break;
		*bssid++ = '\0';
		flags = strchr(bssid, '\t');
		if (flags == NULL)
			break;
		*flags++ = '\0';

		if (strstr(flags, "[DISABLED][P2P-PERSISTENT]"))
			add_persistent(atoi(id), ssid, bssid);

		if (last)
			break;
		start = end + 1;
	}
}


void Peers::update_peers()
{
	model.clear();
	if (wpagui == NULL)
		return;

	char reply[20];
	size_t replylen = sizeof(reply) - 1;
	wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);

	add_stations();
	add_scan_results();
	add_persistent_groups();
}


QStandardItem * Peers::find_addr(QString addr)
{
	if (model.rowCount() == 0)
		return NULL;

	QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
					  addr);
	if (lst.size() == 0)
		return NULL;
	return model.itemFromIndex(lst[0]);
}


QStandardItem * Peers::find_addr_type(QString addr, int type)
{
	if (model.rowCount() == 0)
		return NULL;

	QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
					  addr);
	for (int i = 0; i < lst.size(); i++) {
		QStandardItem *item = model.itemFromIndex(lst[i]);
		if (item->data(peer_role_type).toInt() == type)
			return item;
	}
	return NULL;
}


QStandardItem * Peers::find_uuid(QString uuid)
{
	if (model.rowCount() == 0)
		return NULL;

	QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
					  uuid);
	if (lst.size() == 0)
		return NULL;
	return model.itemFromIndex(lst[0]);
}


void Peers::event_notify(WpaMsg msg)
{
	QString text = msg.getMsg();

	if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
		/*
		 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
		 * 02:2a:c4:18:5b:f3
		 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
		 */
		QStringList items = text.split(' ');
		QString uuid = items[1];
		QString addr = items[2];
		QString name = "";

		QStandardItem *item = find_addr(addr);
		if (item)
			return;

		int pos = text.indexOf('[');
		if (pos >= 0) {
			int pos2 = text.lastIndexOf(']');
			if (pos2 >= pos) {
				items = text.mid(pos + 1, pos2 - pos - 1).
					split('|');
				name = items[0];
				items.append(addr);
			}
		}

		item = new QStandardItem(*laptop_icon, name);
		if (item) {
			item->setData(addr, peer_role_address);
			item->setData(PEER_TYPE_WPS_PIN_NEEDED,
				      peer_role_type);
			item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
			item->setData(items.join("\n"), peer_role_details);
			item->setData(items[5], peer_role_pri_dev_type);
			model.appendRow(item);
		}
		return;
	}

	if (text.startsWith(AP_STA_CONNECTED)) {
		/* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
		QStringList items = text.split(' ');
		QString addr = items[1];
		QStandardItem *item = find_addr(addr);
		if (item == NULL || item->data(peer_role_type).toInt() !=
		    PEER_TYPE_ASSOCIATED_STATION)
			add_single_station(addr.toAscii().constData());
		return;
	}

	if (text.startsWith(AP_STA_DISCONNECTED)) {
		/* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
		QStringList items = text.split(' ');
		QString addr = items[1];

		if (model.rowCount() == 0)
			return;

		QModelIndexList lst = model.match(model.index(0, 0),
						  peer_role_address, addr, -1);
		for (int i = 0; i < lst.size(); i++) {
			QStandardItem *item = model.itemFromIndex(lst[i]);
			if (item && item->data(peer_role_type).toInt() ==
			    PEER_TYPE_ASSOCIATED_STATION) {
				model.removeRow(lst[i].row());
				break;
			}
		}
		return;
	}

	if (text.startsWith(P2P_EVENT_DEVICE_FOUND)) {
		/*
		 * P2P-DEVICE-FOUND 02:b5:64:63:30:63
		 * p2p_dev_addr=02:b5:64:63:30:63 pri_dev_type=1-0050f204-1
		 * name='Wireless Client' config_methods=0x84 dev_capab=0x21
		 * group_capab=0x0
		 */
		QStringList items =
			text.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
		QString addr = items[1];
		QString name = "";
		QString pri_dev_type;
		int config_methods = 0;
		for (int i = 0; i < items.size(); i++) {
			QString str = items.at(i);
			if (str.startsWith("name='"))
				name = str.section('\'', 1, -2);
			else if (str.startsWith("config_methods="))
				config_methods =
					str.section('=', 1).toInt(0, 0);
			else if (str.startsWith("pri_dev_type="))
				pri_dev_type = str.section('=', 1);
		}

		QStandardItem *item = find_addr(addr);
		if (item) {
			int type = item->data(peer_role_type).toInt();
			if (type == PEER_TYPE_P2P)
				return;
		}

		item = new QStandardItem(*default_icon, name);
		if (item) {
			item->setData(addr, peer_role_address);
			item->setData(config_methods,
				      peer_role_config_methods);
			item->setData(PEER_TYPE_P2P, peer_role_type);
			if (!pri_dev_type.isEmpty())
				item->setData(pri_dev_type,
					      peer_role_pri_dev_type);
			item->setData(items.join(QString("\n")),
				      peer_role_details);
			item->setToolTip(ItemType(PEER_TYPE_P2P));
			model.appendRow(item);
		}

		item = find_addr_type(addr,
				      PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT);
		if (item)
			item->setBackground(Qt::NoBrush);
	}

	if (text.startsWith(P2P_EVENT_GROUP_STARTED)) {
		/* P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F"
		 * passphrase="YOyTkxID" go_dev_addr=02:40:61:c2:f3:b7
		 * [PERSISTENT] */
		QStringList items = text.split(' ');
		if (items.size() < 4)
			return;

		int pos = text.indexOf(" ssid=\"");
		if (pos < 0)
			return;
		QString ssid = text.mid(pos + 7);
		pos = ssid.indexOf(" passphrase=\"");
		if (pos < 0)
			pos = ssid.indexOf(" psk=");
		if (pos >= 0)
			ssid.truncate(pos);
		pos = ssid.lastIndexOf('"');
		if (pos >= 0)
			ssid.truncate(pos);

		QStandardItem *item = new QStandardItem(*group_icon, ssid);
		if (item) {
			item->setData(PEER_TYPE_P2P_GROUP, peer_role_type);
			item->setData(items[1], peer_role_ifname);
			QString details;
			if (items[2] == "GO") {
				details = tr("P2P GO for interface ") +
					items[1];
			} else {
				details = tr("P2P client for interface ") +
					items[1];
			}
			if (text.contains(" [PERSISTENT]"))
				details += "\nPersistent group";
			item->setData(details, peer_role_details);
			item->setToolTip(ItemType(PEER_TYPE_P2P_GROUP));
			model.appendRow(item);
		}
	}

	if (text.startsWith(P2P_EVENT_GROUP_REMOVED)) {
		/* P2P-GROUP-REMOVED wlan0-p2p-0 GO */
		QStringList items = text.split(' ');
		if (items.size() < 2)
			return;

		if (model.rowCount() == 0)
			return;

		QModelIndexList lst = model.match(model.index(0, 0),
						  peer_role_ifname, items[1]);
		for (int i = 0; i < lst.size(); i++)
			model.removeRow(lst[i].row());
		return;
	}

	if (text.startsWith(P2P_EVENT_PROV_DISC_SHOW_PIN)) {
		/* P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670 */
		QStringList items = text.split(' ');
		if (items.size() < 3)
			return;
		QString addr = items[1];
		QString pin = items[2];

		QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
		if (item == NULL)
			return;
		item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
			      peer_role_selected_method);
		item->setData(pin, peer_role_selected_pin);
		QVariant var = item->data(peer_role_requested_method);
		if (var.isValid() &&
		    var.toInt() == SEL_METHOD_PIN_LOCAL_DISPLAY) {
			ctx_item = item;
			ctx_p2p_display_pin_pd();
		}
		return;
	}

	if (text.startsWith(P2P_EVENT_PROV_DISC_ENTER_PIN)) {
		/* P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 */
		QStringList items = text.split(' ');
		if (items.size() < 2)
			return;
		QString addr = items[1];

		QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
		if (item == NULL)
			return;
		item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
			      peer_role_selected_method);
		QVariant var = item->data(peer_role_requested_method);
		if (var.isValid() &&
		    var.toInt() == SEL_METHOD_PIN_PEER_DISPLAY) {
			ctx_item = item;
			ctx_p2p_connect();
		}
		return;
	}

	if (text.startsWith(P2P_EVENT_INVITATION_RECEIVED)) {
		/* P2P-INVITATION-RECEIVED sa=02:f0:bc:44:87:62 persistent=4 */
		QStringList items = text.split(' ');
		if (items.size() < 3)
			return;
		if (!items[1].startsWith("sa=") ||
		    !items[2].startsWith("persistent="))
			return;
		QString addr = items[1].mid(3);
		int id = items[2].mid(11).toInt();

		char cmd[100];
		char reply[100];
		size_t reply_len;

		snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", id);
		reply_len = sizeof(reply) - 1;
		if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
			return;
		reply[reply_len] = '\0';
		QString name;
		char *pos = strrchr(reply, '"');
		if (pos && reply[0] == '"') {
			*pos = '\0';
			name = reply + 1;
		} else
			name = reply;

		QStandardItem *item;
		item = find_addr_type(addr, PEER_TYPE_P2P_INVITATION);
		if (item)
			model.removeRow(item->row());

		item = new QStandardItem(*invitation_icon, name);
		if (!item)
			return;
		item->setData(PEER_TYPE_P2P_INVITATION, peer_role_type);
		item->setToolTip(ItemType(PEER_TYPE_P2P_INVITATION));
		item->setData(addr, peer_role_address);
		item->setData(id, peer_role_network_id);

		model.appendRow(item);

		enable_persistent(id);

		return;
	}

	if (text.startsWith(P2P_EVENT_INVITATION_RESULT)) {
		/* P2P-INVITATION-RESULT status=1 */
		/* TODO */
		return;
	}

	if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
		/*
		 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
		 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
		 * |Very friendly name|Company|Long description of the model|
		 * WAP|http://w1.fi/|http://w1.fi/hostapd/
		 */
		QStringList items = text.split(' ');
		if (items.size() < 5)
			return;
		QString uuid = items[1];
		QString addr = items[2];
		QString pri_dev_type = items[3].mid(13);
		int wps_state = items[4].mid(10).toInt();

		int pos = text.indexOf('|');
		if (pos < 0)
			return;
		items = text.mid(pos + 1).split('|');
		if (items.size() < 1)
			return;

		QStandardItem *item = find_uuid(uuid);
		if (item)
			return;

		item = new QStandardItem(*ap_icon, items[0]);
		if (item) {
			item->setData(uuid, peer_role_uuid);
			item->setData(addr, peer_role_address);
			int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
				PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
			item->setData(type, peer_role_type);
			item->setToolTip(ItemType(type));
			item->setData(pri_dev_type, peer_role_pri_dev_type);
			item->setData(items.join(QString("\n")),
				      peer_role_details);
			model.appendRow(item);
		}

		return;
	}

	if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
		/* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
		QStringList items = text.split(' ');
		if (items.size() < 2)
			return;
		if (model.rowCount() == 0)
			return;

		QModelIndexList lst = model.match(model.index(0, 0),
						  peer_role_uuid, items[1]);
		for (int i = 0; i < lst.size(); i++) {
			QStandardItem *item = model.itemFromIndex(lst[i]);
			if (item &&
			    (item->data(peer_role_type).toInt() ==
			     PEER_TYPE_WPS_ER_AP ||
			     item->data(peer_role_type).toInt() ==
			     PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
				model.removeRow(lst[i].row());
		}
		return;
	}

	if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
		/*
		 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
		 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
		 * pri_dev_type=1-0050F204-1
		 * |Wireless Client|Company|cmodel|123|12345|
		 */
		QStringList items = text.split(' ');
		if (items.size() < 3)
			return;
		QString uuid = items[1];
		QString addr = items[2];
		QString pri_dev_type = items[6].mid(13);
		int config_methods = -1;
		int dev_passwd_id = -1;

		for (int i = 3; i < items.size(); i++) {
			int pos = items[i].indexOf('=') + 1;
			if (pos < 1)
				continue;
			QString val = items[i].mid(pos);
			if (items[i].startsWith("config_methods=")) {
				config_methods = val.toInt(0, 0);
			} else if (items[i].startsWith("dev_passwd_id=")) {
				dev_passwd_id = val.toInt();
			}
		}

		int pos = text.indexOf('|');
		if (pos < 0)
			return;
		items = text.mid(pos + 1).split('|');
		if (items.size() < 1)
			return;
		QString name = items[0];
		if (name.length() == 0)
			name = addr;

		remove_enrollee_uuid(uuid);

		QStandardItem *item;
		item = new QStandardItem(*laptop_icon, name);
		if (item) {
			item->setData(uuid, peer_role_uuid);
			item->setData(addr, peer_role_address);
			item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
				      peer_role_type);
			item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
			item->setData(items.join(QString("\n")),
				      peer_role_details);
			item->setData(pri_dev_type, peer_role_pri_dev_type);
			if (config_methods >= 0)
				item->setData(config_methods,
					      peer_role_config_methods);
			if (dev_passwd_id >= 0)
				item->setData(dev_passwd_id,
					      peer_role_dev_passwd_id);
			model.appendRow(item);
		}

		return;
	}

	if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
		/*
		 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
		 * 02:66:a0:ee:17:27
		 */
		QStringList items = text.split(' ');
		if (items.size() < 2)
			return;
		remove_enrollee_uuid(items[1]);
		return;
	}

	if (text.startsWith(WPS_EVENT_ENROLLEE_SEEN)) {
		/* TODO: need to time out this somehow or remove on successful
		 * WPS run, etc. */
		/*
		 * WPS-ENROLLEE-SEEN 02:00:00:00:01:00
		 * 572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1
		 * [Wireless Client]
		 * (MAC addr, UUID-E, pri dev type, config methods,
		 * dev passwd id, request type, [dev name])
		 */
		QStringList items = text.split(' ');
		if (items.size() < 7)
			return;
		QString addr = items[1];
		QString uuid = items[2];
		QString pri_dev_type = items[3];
		int config_methods = items[4].toInt(0, 0);
		int dev_passwd_id = items[5].toInt();
		QString name;

		QStandardItem *item = find_addr(addr);
		if (item) {
			int type = item->data(peer_role_type).toInt();
			if (type == PEER_TYPE_ASSOCIATED_STATION)
				return; /* already associated */
		}

		int pos = text.indexOf('[');
		if (pos >= 0) {
			int pos2 = text.lastIndexOf(']');
			if (pos2 >= pos) {
				QStringList items2 =
					text.mid(pos + 1, pos2 - pos - 1).
					split('|');
				name = items2[0];
			}
		}
		if (name.isEmpty())
			name = addr;

		item = find_uuid(uuid);
		if (item) {
			QVariant var = item->data(peer_role_config_methods);
			QVariant var2 = item->data(peer_role_dev_passwd_id);
			if ((var.isValid() && config_methods != var.toInt()) ||
			    (var2.isValid() && dev_passwd_id != var2.toInt()))
				remove_enrollee_uuid(uuid);
			else
				return;
		}

		item = new QStandardItem(*laptop_icon, name);
		if (item) {
			item->setData(uuid, peer_role_uuid);
			item->setData(addr, peer_role_address);
			item->setData(PEER_TYPE_WPS_ENROLLEE,
				      peer_role_type);
			item->setToolTip(ItemType(PEER_TYPE_WPS_ENROLLEE));
			item->setData(items.join(QString("\n")),
				      peer_role_details);
			item->setData(pri_dev_type, peer_role_pri_dev_type);
			item->setData(config_methods,
				      peer_role_config_methods);
			item->setData(dev_passwd_id, peer_role_dev_passwd_id);
			model.appendRow(item);
		}

		return;
	}

	if (text.startsWith(WPA_EVENT_BSS_ADDED)) {
		/* CTRL-EVENT-BSS-ADDED 34 00:11:22:33:44:55 */
		QStringList items = text.split(' ');
		if (items.size() < 2)
			return;
		char cmd[20];
		snprintf(cmd, sizeof(cmd), "BSS ID-%d", items[1].toInt());
		add_bss(cmd);
		return;
	}

	if (text.startsWith(WPA_EVENT_BSS_REMOVED)) {
		/* CTRL-EVENT-BSS-REMOVED 34 00:11:22:33:44:55 */
		QStringList items = text.split(' ');
		if (items.size() < 2)
			return;
		remove_bss(items[1].toInt());
		return;
	}
}


void Peers::ctx_p2p_connect()
{
	if (ctx_item == NULL)
		return;
	QString addr = ctx_item->data(peer_role_address).toString();
	QString arg;
	int config_methods =
		ctx_item->data(peer_role_config_methods).toInt();
	enum selected_method method = SEL_METHOD_NONE;
	QVariant var = ctx_item->data(peer_role_selected_method);
	if (var.isValid())
		method = (enum selected_method) var.toInt();
	if (method == SEL_METHOD_PIN_LOCAL_DISPLAY) {
		arg = ctx_item->data(peer_role_selected_pin).toString();
		char cmd[100];
		char reply[100];
		size_t reply_len;
		snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
			 addr.toAscii().constData(),
			 arg.toAscii().constData());
		reply_len = sizeof(reply) - 1;
		if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
			QMessageBox msg;
			msg.setIcon(QMessageBox::Warning);
			msg.setText("Failed to initiate P2P connect.");
			msg.exec();
			return;
		}
		QMessageBox::information(this,
					 tr("PIN for ") + ctx_item->text(),
					 tr("Enter the following PIN on the\n"
					    "peer device: ") + arg);
	} else if (method == SEL_METHOD_PIN_PEER_DISPLAY) {
		StringQuery input(tr("PIN from peer display:"));
		input.setWindowTitle(tr("PIN for ") + ctx_item->text());
		if (input.exec() != QDialog::Accepted)
			return;
		arg = input.get_string();
	} else if (config_methods == 0x0080 /* PBC */) {
		arg = "pbc";
	} else {
		StringQuery input(tr("PIN:"));
		input.setWindowTitle(tr("PIN for ") + ctx_item->text());
		if (input.exec() != QDialog::Accepted)
			return;
		arg = input.get_string();
	}

	char cmd[100];
	char reply[100];
	size_t reply_len;
	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s",
		 addr.toAscii().constData(),
		 arg.toAscii().constData());
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText("Failed to initiate P2P connect.");
		msg.exec();
	}
}


void Peers::ctx_p2p_req_pin()
{
	if (ctx_item == NULL)
		return;
	QString addr = ctx_item->data(peer_role_address).toString();
	ctx_item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
			  peer_role_requested_method);

	char cmd[100];
	char reply[100];
	size_t reply_len;
	snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s display",
		 addr.toAscii().constData());
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText(tr("Failed to request PIN from peer."));
		msg.exec();
	}
}


void Peers::ctx_p2p_show_pin()
{
	if (ctx_item == NULL)
		return;
	QString addr = ctx_item->data(peer_role_address).toString();
	ctx_item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
			  peer_role_requested_method);

	char cmd[100];
	char reply[100];
	size_t reply_len;
	snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s keypad",
		 addr.toAscii().constData());
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText(tr("Failed to request peer to enter PIN."));
		msg.exec();
	}
}


void Peers::ctx_p2p_display_pin()
{
	if (ctx_item == NULL)
		return;
	QString addr = ctx_item->data(peer_role_address).toString();

	char cmd[100];
	char reply[100];
	size_t reply_len;
	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pin",
		 addr.toAscii().constData());
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText("Failed to initiate P2P connect.");
		msg.exec();
		return;
	}
	reply[reply_len] = '\0';
	QMessageBox::information(this,
				 tr("PIN for ") + ctx_item->text(),
				 tr("Enter the following PIN on the\n"
				    "peer device: ") + reply);
}


void Peers::ctx_p2p_display_pin_pd()
{
	if (ctx_item == NULL)
		return;
	QString addr = ctx_item->data(peer_role_address).toString();
	QString arg = ctx_item->data(peer_role_selected_pin).toString();

	char cmd[100];
	char reply[100];
	size_t reply_len;
	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
		 addr.toAscii().constData(),
		 arg.toAscii().constData());
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText("Failed to initiate P2P connect.");
		msg.exec();
		return;
	}
	reply[reply_len] = '\0';
	QMessageBox::information(this,
				 tr("PIN for ") + ctx_item->text(),
				 tr("Enter the following PIN on the\n"
				    "peer device: ") + arg);
}


void Peers::ctx_p2p_enter_pin()
{
	if (ctx_item == NULL)
		return;
	QString addr = ctx_item->data(peer_role_address).toString();
	QString arg;

	StringQuery input(tr("PIN from peer:"));
	input.setWindowTitle(tr("PIN for ") + ctx_item->text());
	if (input.exec() != QDialog::Accepted)
		return;
	arg = input.get_string();

	char cmd[100];
	char reply[100];
	size_t reply_len;
	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s keypad",
		 addr.toAscii().constData(),
		 arg.toAscii().constData());
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText("Failed to initiate P2P connect.");
		msg.exec();
	}
}


void Peers::ctx_p2p_remove_group()
{
	if (ctx_item == NULL)
		return;
	char cmd[100];
	char reply[100];
	size_t reply_len;
	snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s",
		 ctx_item->data(peer_role_ifname).toString().toAscii().
		 constData());
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText("Failed to remove P2P Group.");
		msg.exec();
	}
}


void Peers::closeEvent(QCloseEvent *)
{
	if (wpagui) {
		char reply[20];
		size_t replylen = sizeof(reply) - 1;
		wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
	}
}


void Peers::done(int r)
{
	QDialog::done(r);
	close();
}


void Peers::remove_enrollee_uuid(QString uuid)
{
	if (model.rowCount() == 0)
		return;

	QModelIndexList lst = model.match(model.index(0, 0),
					  peer_role_uuid, uuid);
	for (int i = 0; i < lst.size(); i++) {
		QStandardItem *item = model.itemFromIndex(lst[i]);
		if (item == NULL)
			continue;
		int type = item->data(peer_role_type).toInt();
		if (type == PEER_TYPE_WPS_ER_ENROLLEE ||
		    type == PEER_TYPE_WPS_ENROLLEE)
			model.removeRow(lst[i].row());
	}
}


void Peers::properties()
{
	if (ctx_item == NULL)
		return;

	QMessageBox msg(this);
	msg.setStandardButtons(QMessageBox::Ok);
	msg.setDefaultButton(QMessageBox::Ok);
	msg.setEscapeButton(QMessageBox::Ok);
	msg.setWindowTitle(tr("Peer Properties"));

	int type = ctx_item->data(peer_role_type).toInt();
	QString title = Peers::ItemType(type);

	msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());

	QVariant var;
	QString info;

	var = ctx_item->data(peer_role_address);
	if (var.isValid())
		info += tr("Address: ") + var.toString() + QString("\n");

	var = ctx_item->data(peer_role_uuid);
	if (var.isValid())
		info += tr("UUID: ") + var.toString() + QString("\n");

	var = ctx_item->data(peer_role_pri_dev_type);
	if (var.isValid())
		info += tr("Primary Device Type: ") + var.toString() +
			QString("\n");

	var = ctx_item->data(peer_role_ssid);
	if (var.isValid())
		info += tr("SSID: ") + var.toString() + QString("\n");

	var = ctx_item->data(peer_role_config_methods);
	if (var.isValid()) {
		int methods = var.toInt();
		info += tr("Configuration Methods: ");
		if (methods & 0x0001)
			info += tr("[USBA]");
		if (methods & 0x0002)
			info += tr("[Ethernet]");
		if (methods & 0x0004)
			info += tr("[Label]");
		if (methods & 0x0008)
			info += tr("[Display]");
		if (methods & 0x0010)
			info += tr("[Ext. NFC Token]");
		if (methods & 0x0020)
			info += tr("[Int. NFC Token]");
		if (methods & 0x0040)
			info += tr("[NFC Interface]");
		if (methods & 0x0080)
			info += tr("[Push Button]");
		if (methods & 0x0100)
			info += tr("[Keypad]");
		info += "\n";
	}

	var = ctx_item->data(peer_role_selected_method);
	if (var.isValid()) {
		enum selected_method method =
			(enum selected_method) var.toInt();
		switch (method) {
		case SEL_METHOD_NONE:
			break;
		case SEL_METHOD_PIN_PEER_DISPLAY:
			info += tr("Selected Method: PIN on peer display\n");
			break;
		case SEL_METHOD_PIN_LOCAL_DISPLAY:
			info += tr("Selected Method: PIN on local display\n");
			break;
		}
	}

	var = ctx_item->data(peer_role_selected_pin);
	if (var.isValid()) {
		info += tr("PIN to enter on peer: ") + var.toString() + "\n";
	}

	var = ctx_item->data(peer_role_dev_passwd_id);
	if (var.isValid()) {
		info += tr("Device Password ID: ") + var.toString();
		switch (var.toInt()) {
		case 0:
			info += tr(" (Default PIN)");
			break;
		case 1:
			info += tr(" (User-specified PIN)");
			break;
		case 2:
			info += tr(" (Machine-specified PIN)");
			break;
		case 3:
			info += tr(" (Rekey)");
			break;
		case 4:
			info += tr(" (Push Button)");
			break;
		case 5:
			info += tr(" (Registrar-specified)");
			break;
		}
		info += "\n";
	}

	msg.setInformativeText(info);

	var = ctx_item->data(peer_role_details);
	if (var.isValid())
		msg.setDetailedText(var.toString());

	msg.exec();
}


void Peers::connect_pbc()
{
	if (ctx_item == NULL)
		return;

	char cmd[100];
	char reply[100];
	size_t reply_len;

	int peer_type = ctx_item->data(peer_role_type).toInt();
	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
		snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
			 ctx_item->data(peer_role_uuid).toString().toAscii().
			 constData());
	} else if (peer_type == PEER_TYPE_P2P ||
		   peer_type == PEER_TYPE_P2P_CLIENT) {
		snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pbc",
			 ctx_item->data(peer_role_address).toString().
			 toAscii().constData());
	} else {
		snprintf(cmd, sizeof(cmd), "WPS_PBC");
	}
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText(tr("Failed to start WPS PBC."));
		msg.exec();
	}
}


void Peers::learn_ap_config()
{
	if (ctx_item == NULL)
		return;

	QString uuid = ctx_item->data(peer_role_uuid).toString();

	StringQuery input(tr("AP PIN:"));
	input.setWindowTitle(tr("AP PIN for ") + ctx_item->text());
	if (input.exec() != QDialog::Accepted)
		return;

	char cmd[100];
	char reply[100];
	size_t reply_len;

	snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
		 uuid.toAscii().constData(),
		 input.get_string().toAscii().constData());
	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText(tr("Failed to start learning AP configuration."));
		msg.exec();
	}
}


void Peers::ctx_hide_ap()
{
	hide_ap = true;

	if (model.rowCount() == 0)
		return;

	do {
		QModelIndexList lst;
		lst = model.match(model.index(0, 0),
				  peer_role_type, PEER_TYPE_AP);
		if (lst.size() == 0) {
			lst = model.match(model.index(0, 0),
					  peer_role_type, PEER_TYPE_AP_WPS);
			if (lst.size() == 0)
				break;
		}

		model.removeRow(lst[0].row());
	} while (1);
}


void Peers::ctx_show_ap()
{
	hide_ap = false;
	add_scan_results();
}


void Peers::ctx_p2p_show_passphrase()
{
	char reply[64];
	size_t reply_len;

	reply_len = sizeof(reply) - 1;
	if (wpagui->ctrlRequest("P2P_GET_PASSPHRASE", reply, &reply_len) < 0 ||
	    memcmp(reply, "FAIL", 4) == 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText("Failed to get P2P group passphrase.");
		msg.exec();
	} else {
		reply[reply_len] = '\0';
		QMessageBox::information(this, tr("Passphrase"),
					 tr("P2P group passphrase:\n") +
					 reply);
	}
}


void Peers::ctx_p2p_start_persistent()
{
	if (ctx_item == NULL)
		return;

	char cmd[100];
	char reply[100];
	size_t reply_len;

	snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD persistent=%d",
		 ctx_item->data(peer_role_network_id).toInt());
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
	    memcmp(reply, "FAIL", 4) == 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText(tr("Failed to start persistent P2P Group."));
		msg.exec();
	} else if (ctx_item->data(peer_role_type).toInt() ==
		   PEER_TYPE_P2P_INVITATION)
		model.removeRow(ctx_item->row());
}


void Peers::ctx_p2p_invite()
{
	if (ctx_item == NULL)
		return;

	char cmd[100];
	char reply[100];
	size_t reply_len;

	snprintf(cmd, sizeof(cmd), "P2P_INVITE persistent=%d",
		 ctx_item->data(peer_role_network_id).toInt());
	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
	    memcmp(reply, "FAIL", 4) == 0) {
		QMessageBox msg;
		msg.setIcon(QMessageBox::Warning);
		msg.setText(tr("Failed to invite peer to start persistent "
			       "P2P Group."));
		msg.exec();
	}
}


void Peers::ctx_p2p_delete()
{
	if (ctx_item == NULL)
		return;
	model.removeRow(ctx_item->row());
}


void Peers::enable_persistent(int id)
{
	if (model.rowCount() == 0)
		return;

	QModelIndexList lst = model.match(model.index(0, 0),
					  peer_role_network_id, id);
	for (int i = 0; i < lst.size(); i++) {
		QStandardItem *item = model.itemFromIndex(lst[i]);
		int type = item->data(peer_role_type).toInt();
		if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
		    type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT)
			item->setBackground(Qt::NoBrush);
	}
}