2 * wpa_gui - Peers class
3 * Copyright (c) 2009, Atheros Communications
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
16 #include <QImageReader>
17 #include <QMessageBox>
19 #include "common/wpa_ctrl.h"
21 #include "stringquery.h"
26 peer_role_address = Qt::UserRole + 1,
30 peer_role_pri_dev_type,
32 peer_role_config_methods,
33 peer_role_dev_passwd_id
38 * - add current AP info (e.g., from WPS) in station mode
42 PEER_TYPE_ASSOCIATED_STATION,
45 PEER_TYPE_WPS_PIN_NEEDED,
47 PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
48 PEER_TYPE_WPS_ER_ENROLLEE
52 Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
57 if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
59 default_icon = new QIcon(":/icons/wpa_gui.svg");
60 ap_icon = new QIcon(":/icons/ap.svg");
61 laptop_icon = new QIcon(":/icons/laptop.svg");
63 default_icon = new QIcon(":/icons/wpa_gui.png");
64 ap_icon = new QIcon(":/icons/ap.png");
65 laptop_icon = new QIcon(":/icons/laptop.png");
68 peers->setModel(&model);
69 peers->setResizeMode(QListView::Adjust);
71 peers->setContextMenuPolicy(Qt::CustomContextMenu);
72 connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
73 this, SLOT(context_menu(const QPoint &)));
79 void Peers::setWpaGui(WpaGui *_wpagui)
94 void Peers::languageChange()
100 QString Peers::ItemType(int type)
104 case PEER_TYPE_ASSOCIATED_STATION:
105 title = tr("Associated station");
110 case PEER_TYPE_AP_WPS:
111 title = tr("WPS AP");
113 case PEER_TYPE_WPS_PIN_NEEDED:
114 title = tr("WPS PIN needed");
116 case PEER_TYPE_WPS_ER_AP:
117 title = tr("ER: WPS AP");
119 case PEER_TYPE_WPS_ER_AP_UNCONFIGURED:
120 title = tr("ER: WPS AP (Unconfigured)");
122 case PEER_TYPE_WPS_ER_ENROLLEE:
123 title = tr("ER: WPS Enrollee");
130 void Peers::context_menu(const QPoint &pos)
132 QMenu *menu = new QMenu;
136 QModelIndex idx = peers->indexAt(pos);
138 ctx_item = model.itemFromIndex(idx);
139 int type = ctx_item->data(peer_role_type).toInt();
140 menu->addAction(Peers::ItemType(type))->setEnabled(false);
141 menu->addSeparator();
143 int config_methods = -1;
144 QVariant var = ctx_item->data(peer_role_config_methods);
146 config_methods = var.toInt();
148 if ((type == PEER_TYPE_ASSOCIATED_STATION ||
149 type == PEER_TYPE_AP_WPS ||
150 type == PEER_TYPE_WPS_PIN_NEEDED ||
151 type == PEER_TYPE_WPS_ER_ENROLLEE) &&
152 (config_methods == -1 || (config_methods & 0x010c))) {
153 menu->addAction(tr("Enter WPS PIN"), this,
157 if (type == PEER_TYPE_AP_WPS) {
158 menu->addAction(tr("Connect (PBC)"), this,
159 SLOT(connect_pbc()));
162 if ((type == PEER_TYPE_ASSOCIATED_STATION ||
163 type == PEER_TYPE_WPS_ER_ENROLLEE) &&
164 config_methods >= 0 && (config_methods & 0x0080)) {
165 menu->addAction(tr("Enroll (PBC)"), this,
166 SLOT(connect_pbc()));
169 if (type == PEER_TYPE_WPS_ER_AP) {
170 menu->addAction(tr("Learn Configuration"), this,
171 SLOT(learn_ap_config()));
174 menu->addAction(tr("Properties"), this, SLOT(properties()));
177 menu->addAction(QString("Refresh"), this, SLOT(ctx_refresh()));
180 menu->exec(peers->mapToGlobal(pos));
184 void Peers::enter_pin()
186 if (ctx_item == NULL)
189 int peer_type = ctx_item->data(peer_role_type).toInt();
192 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
193 uuid = ctx_item->data(peer_role_uuid).toString();
195 addr = ctx_item->data(peer_role_address).toString();
197 StringQuery input(tr("PIN:"));
198 input.setWindowTitle(tr("PIN for ") + ctx_item->text());
199 if (input.exec() != QDialog::Accepted)
206 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
207 snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
208 uuid.toAscii().constData(),
209 input.get_string().toAscii().constData());
211 snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
212 addr.toAscii().constData(),
213 input.get_string().toAscii().constData());
215 reply_len = sizeof(reply) - 1;
216 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
218 msg.setIcon(QMessageBox::Warning);
219 msg.setText("Failed to set the WPS PIN.");
225 void Peers::ctx_refresh()
231 void Peers::add_station(QString info)
233 QStringList lines = info.split(QRegExp("\\n"));
236 for (QStringList::Iterator it = lines.begin();
237 it != lines.end(); it++) {
238 int pos = (*it).indexOf('=') + 1;
242 if ((*it).startsWith("wpsDeviceName="))
243 name = (*it).mid(pos);
249 QStandardItem *item = new QStandardItem(*laptop_icon, name);
251 item->setData(lines[0], peer_role_address);
252 item->setData(PEER_TYPE_ASSOCIATED_STATION,
254 item->setData(info, peer_role_details);
255 item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
256 model.appendRow(item);
261 void Peers::add_stations()
268 reply_len = sizeof(reply) - 1;
269 if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
273 reply[reply_len] = '\0';
276 while (*txt != '\0' && *txt != '\n')
279 if (strncmp(reply, "FAIL", 4) == 0 ||
280 strncmp(reply, "UNKNOWN", 7) == 0)
285 reply_len = sizeof(reply) - 1;
286 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
287 res = wpagui->ctrlRequest(cmd, reply, &reply_len);
292 void Peers::add_single_station(const char *addr)
298 reply_len = sizeof(reply) - 1;
299 snprintf(cmd, sizeof(cmd), "STA %s", addr);
300 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
303 reply[reply_len] = '\0';
306 while (*txt != '\0' && *txt != '\n')
309 if (strncmp(reply, "FAIL", 4) == 0 ||
310 strncmp(reply, "UNKNOWN", 7) == 0)
317 void Peers::add_scan_results()
326 snprintf(cmd, sizeof(cmd), "BSS %d", index++);
330 reply_len = sizeof(reply) - 1;
331 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
333 reply[reply_len] = '\0';
336 if (bss.isEmpty() || bss.startsWith("FAIL"))
339 QString ssid, bssid, flags, wps_name, pri_dev_type;
341 QStringList lines = bss.split(QRegExp("\\n"));
342 for (QStringList::Iterator it = lines.begin();
343 it != lines.end(); it++) {
344 int pos = (*it).indexOf('=') + 1;
348 if ((*it).startsWith("bssid="))
349 bssid = (*it).mid(pos);
350 else if ((*it).startsWith("flags="))
351 flags = (*it).mid(pos);
352 else if ((*it).startsWith("ssid="))
353 ssid = (*it).mid(pos);
354 else if ((*it).startsWith("wps_device_name="))
355 wps_name = (*it).mid(pos);
356 else if ((*it).startsWith("wps_primary_device_type="))
357 pri_dev_type = (*it).mid(pos);
360 QString name = wps_name;
362 name = ssid + "\n" + bssid;
364 QStandardItem *item = new QStandardItem(*ap_icon, name);
366 item->setData(bssid, peer_role_address);
368 if (flags.contains("[WPS"))
369 type = PEER_TYPE_AP_WPS;
372 item->setData(type, peer_role_type);
374 for (int i = 0; i < lines.size(); i++) {
375 if (lines[i].length() > 60) {
377 60, lines[i].length());
381 item->setToolTip(ItemType(type));
382 item->setData(lines.join("\n"), peer_role_details);
383 if (!pri_dev_type.isEmpty())
384 item->setData(pri_dev_type,
385 peer_role_pri_dev_type);
387 item->setData(ssid, peer_role_ssid);
388 model.appendRow(item);
394 void Peers::update_peers()
401 size_t replylen = sizeof(reply) - 1;
402 wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
409 QStandardItem * Peers::find_addr(QString addr)
411 if (model.rowCount() == 0)
414 QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
418 return model.itemFromIndex(lst[0]);
422 QStandardItem * Peers::find_uuid(QString uuid)
424 if (model.rowCount() == 0)
427 QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
431 return model.itemFromIndex(lst[0]);
435 void Peers::event_notify(WpaMsg msg)
437 QString text = msg.getMsg();
439 if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
441 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
443 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
445 QStringList items = text.split(' ');
446 QString uuid = items[1];
447 QString addr = items[2];
450 QStandardItem *item = find_addr(addr);
454 int pos = text.indexOf('[');
456 int pos2 = text.lastIndexOf(']');
458 items = text.mid(pos + 1, pos2 - pos - 1).
465 item = new QStandardItem(*laptop_icon, name);
467 item->setData(addr, peer_role_address);
468 item->setData(PEER_TYPE_WPS_PIN_NEEDED,
470 item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
471 item->setData(items.join("\n"), peer_role_details);
472 item->setData(items[5], peer_role_pri_dev_type);
473 model.appendRow(item);
478 if (text.startsWith(AP_STA_CONNECTED)) {
479 /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
480 QStringList items = text.split(' ');
481 QString addr = items[1];
482 QStandardItem *item = find_addr(addr);
483 if (item == NULL || item->data(peer_role_type).toInt() !=
484 PEER_TYPE_ASSOCIATED_STATION)
485 add_single_station(addr.toAscii().constData());
489 if (text.startsWith(AP_STA_DISCONNECTED)) {
490 /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
491 QStringList items = text.split(' ');
492 QString addr = items[1];
494 if (model.rowCount() == 0)
497 QModelIndexList lst = model.match(model.index(0, 0),
498 peer_role_address, addr);
499 for (int i = 0; i < lst.size(); i++) {
500 QStandardItem *item = model.itemFromIndex(lst[i]);
501 if (item && item->data(peer_role_type).toInt() ==
502 PEER_TYPE_ASSOCIATED_STATION)
503 model.removeRow(lst[i].row());
508 if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
510 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
511 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
512 * |Very friendly name|Company|Long description of the model|
513 * WAP|http://w1.fi/|http://w1.fi/hostapd/
515 QStringList items = text.split(' ');
516 if (items.size() < 5)
518 QString uuid = items[1];
519 QString addr = items[2];
520 QString pri_dev_type = items[3].mid(13);
521 int wps_state = items[4].mid(10).toInt();
523 int pos = text.indexOf('|');
526 items = text.mid(pos + 1).split('|');
527 if (items.size() < 1)
530 QStandardItem *item = find_uuid(uuid);
534 item = new QStandardItem(*ap_icon, items[0]);
536 item->setData(uuid, peer_role_uuid);
537 item->setData(addr, peer_role_address);
538 int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
539 PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
540 item->setData(type, peer_role_type);
541 item->setToolTip(ItemType(type));
542 item->setData(pri_dev_type, peer_role_pri_dev_type);
543 item->setData(items.join(QString("\n")),
545 model.appendRow(item);
551 if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
552 /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
553 QStringList items = text.split(' ');
554 if (items.size() < 2)
556 if (model.rowCount() == 0)
559 QModelIndexList lst = model.match(model.index(0, 0),
560 peer_role_uuid, items[1]);
561 for (int i = 0; i < lst.size(); i++) {
562 QStandardItem *item = model.itemFromIndex(lst[i]);
564 (item->data(peer_role_type).toInt() ==
565 PEER_TYPE_WPS_ER_AP ||
566 item->data(peer_role_type).toInt() ==
567 PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
568 model.removeRow(lst[i].row());
573 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
575 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
576 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
577 * pri_dev_type=1-0050F204-1
578 * |Wireless Client|Company|cmodel|123|12345|
580 QStringList items = text.split(' ');
581 if (items.size() < 3)
583 QString uuid = items[1];
584 QString addr = items[2];
585 QString pri_dev_type = items[6].mid(13);
586 int config_methods = -1;
587 int dev_passwd_id = -1;
589 for (int i = 3; i < items.size(); i++) {
590 int pos = items[i].indexOf('=') + 1;
593 QString val = items[i].mid(pos);
594 if (items[i].startsWith("config_methods=")) {
595 config_methods = val.toInt(0, 0);
596 } else if (items[i].startsWith("dev_passwd_id=")) {
597 dev_passwd_id = val.toInt();
601 int pos = text.indexOf('|');
604 items = text.mid(pos + 1).split('|');
605 if (items.size() < 1)
607 QString name = items[0];
608 if (name.length() == 0)
611 remove_enrollee_uuid(uuid);
614 item = new QStandardItem(*laptop_icon, name);
616 item->setData(uuid, peer_role_uuid);
617 item->setData(addr, peer_role_address);
618 item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
620 item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
621 item->setData(items.join(QString("\n")),
623 item->setData(pri_dev_type, peer_role_pri_dev_type);
624 if (config_methods >= 0)
625 item->setData(config_methods,
626 peer_role_config_methods);
627 if (dev_passwd_id >= 0)
628 item->setData(dev_passwd_id,
629 peer_role_dev_passwd_id);
630 model.appendRow(item);
636 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
638 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
641 QStringList items = text.split(' ');
642 if (items.size() < 2)
644 remove_enrollee_uuid(items[1]);
650 void Peers::closeEvent(QCloseEvent *)
654 size_t replylen = sizeof(reply) - 1;
655 wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
660 void Peers::done(int r)
667 void Peers::remove_enrollee_uuid(QString uuid)
669 if (model.rowCount() == 0)
672 QModelIndexList lst = model.match(model.index(0, 0),
673 peer_role_uuid, uuid);
674 for (int i = 0; i < lst.size(); i++) {
675 QStandardItem *item = model.itemFromIndex(lst[i]);
676 if (item && item->data(peer_role_type).toInt() ==
677 PEER_TYPE_WPS_ER_ENROLLEE)
678 model.removeRow(lst[i].row());
683 void Peers::properties()
685 if (ctx_item == NULL)
688 QMessageBox msg(this);
689 msg.setStandardButtons(QMessageBox::Ok);
690 msg.setDefaultButton(QMessageBox::Ok);
691 msg.setEscapeButton(QMessageBox::Ok);
692 msg.setWindowTitle(tr("Peer Properties"));
694 int type = ctx_item->data(peer_role_type).toInt();
695 QString title = Peers::ItemType(type);
697 msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
702 var = ctx_item->data(peer_role_address);
704 info += tr("Address: ") + var.toString() + QString("\n");
706 var = ctx_item->data(peer_role_uuid);
708 info += tr("UUID: ") + var.toString() + QString("\n");
710 var = ctx_item->data(peer_role_pri_dev_type);
712 info += tr("Primary Device Type: ") + var.toString() +
715 var = ctx_item->data(peer_role_ssid);
717 info += tr("SSID: ") + var.toString() + QString("\n");
719 var = ctx_item->data(peer_role_config_methods);
721 int methods = var.toInt();
722 info += tr("Configuration Methods: ");
723 if (methods & 0x0001)
724 info += tr("[USBA]");
725 if (methods & 0x0002)
726 info += tr("[Ethernet]");
727 if (methods & 0x0004)
728 info += tr("[Label]");
729 if (methods & 0x0008)
730 info += tr("[Display]");
731 if (methods & 0x0010)
732 info += tr("[Ext. NFC Token]");
733 if (methods & 0x0020)
734 info += tr("[Int. NFC Token]");
735 if (methods & 0x0040)
736 info += tr("[NFC Interface]");
737 if (methods & 0x0080)
738 info += tr("[Push Button]");
739 if (methods & 0x0100)
740 info += tr("[Keypad]");
744 var = ctx_item->data(peer_role_dev_passwd_id);
746 info += tr("Device Password ID: ") + var.toString();
747 switch (var.toInt()) {
749 info += tr(" (Default PIN)");
752 info += tr(" (User-specified PIN)");
755 info += tr(" (Machine-specified PIN)");
758 info += tr(" (Rekey)");
761 info += tr(" (Push Button)");
764 info += tr(" (Registrar-specified)");
770 msg.setInformativeText(info);
772 var = ctx_item->data(peer_role_details);
774 msg.setDetailedText(var.toString());
780 void Peers::connect_pbc()
782 if (ctx_item == NULL)
789 int peer_type = ctx_item->data(peer_role_type).toInt();
790 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
791 snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
792 ctx_item->data(peer_role_uuid).toString().toAscii().
795 snprintf(cmd, sizeof(cmd), "WPS_PBC");
797 reply_len = sizeof(reply) - 1;
798 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
800 msg.setIcon(QMessageBox::Warning);
801 msg.setText("Failed to start WPS PBC.");
807 void Peers::learn_ap_config()
809 if (ctx_item == NULL)
812 QString uuid = ctx_item->data(peer_role_uuid).toString();
814 StringQuery input(tr("AP PIN:"));
815 input.setWindowTitle(tr("AP PIN for ") + ctx_item->text());
816 if (input.exec() != QDialog::Accepted)
823 snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
824 uuid.toAscii().constData(),
825 input.get_string().toAscii().constData());
826 reply_len = sizeof(reply) - 1;
827 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
829 msg.setIcon(QMessageBox::Warning);
830 msg.setText(tr("Failed to start learning AP configuration."));