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,
49 PEER_TYPE_WPS_ENROLLEE
53 Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
58 if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
60 default_icon = new QIcon(":/icons/wpa_gui.svg");
61 ap_icon = new QIcon(":/icons/ap.svg");
62 laptop_icon = new QIcon(":/icons/laptop.svg");
64 default_icon = new QIcon(":/icons/wpa_gui.png");
65 ap_icon = new QIcon(":/icons/ap.png");
66 laptop_icon = new QIcon(":/icons/laptop.png");
69 peers->setModel(&model);
70 peers->setResizeMode(QListView::Adjust);
72 peers->setContextMenuPolicy(Qt::CustomContextMenu);
73 connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
74 this, SLOT(context_menu(const QPoint &)));
80 void Peers::setWpaGui(WpaGui *_wpagui)
95 void Peers::languageChange()
101 QString Peers::ItemType(int type)
105 case PEER_TYPE_ASSOCIATED_STATION:
106 title = tr("Associated station");
111 case PEER_TYPE_AP_WPS:
112 title = tr("WPS AP");
114 case PEER_TYPE_WPS_PIN_NEEDED:
115 title = tr("WPS PIN needed");
117 case PEER_TYPE_WPS_ER_AP:
118 title = tr("ER: WPS AP");
120 case PEER_TYPE_WPS_ER_AP_UNCONFIGURED:
121 title = tr("ER: WPS AP (Unconfigured)");
123 case PEER_TYPE_WPS_ER_ENROLLEE:
124 title = tr("ER: WPS Enrollee");
126 case PEER_TYPE_WPS_ENROLLEE:
127 title = tr("WPS Enrollee");
134 void Peers::context_menu(const QPoint &pos)
136 QMenu *menu = new QMenu;
140 QModelIndex idx = peers->indexAt(pos);
142 ctx_item = model.itemFromIndex(idx);
143 int type = ctx_item->data(peer_role_type).toInt();
144 menu->addAction(Peers::ItemType(type))->setEnabled(false);
145 menu->addSeparator();
147 int config_methods = -1;
148 QVariant var = ctx_item->data(peer_role_config_methods);
150 config_methods = var.toInt();
152 if ((type == PEER_TYPE_ASSOCIATED_STATION ||
153 type == PEER_TYPE_AP_WPS ||
154 type == PEER_TYPE_WPS_PIN_NEEDED ||
155 type == PEER_TYPE_WPS_ER_ENROLLEE ||
156 type == PEER_TYPE_WPS_ENROLLEE) &&
157 (config_methods == -1 || (config_methods & 0x010c))) {
158 menu->addAction(tr("Enter WPS PIN"), this,
162 if (type == PEER_TYPE_AP_WPS) {
163 menu->addAction(tr("Connect (PBC)"), this,
164 SLOT(connect_pbc()));
167 if ((type == PEER_TYPE_ASSOCIATED_STATION ||
168 type == PEER_TYPE_WPS_ER_ENROLLEE ||
169 type == PEER_TYPE_WPS_ENROLLEE) &&
170 config_methods >= 0 && (config_methods & 0x0080)) {
171 menu->addAction(tr("Enroll (PBC)"), this,
172 SLOT(connect_pbc()));
175 if (type == PEER_TYPE_WPS_ER_AP) {
176 menu->addAction(tr("Learn Configuration"), this,
177 SLOT(learn_ap_config()));
180 menu->addAction(tr("Properties"), this, SLOT(properties()));
183 menu->addAction(QString("Refresh"), this, SLOT(ctx_refresh()));
186 menu->exec(peers->mapToGlobal(pos));
190 void Peers::enter_pin()
192 if (ctx_item == NULL)
195 int peer_type = ctx_item->data(peer_role_type).toInt();
198 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
199 uuid = ctx_item->data(peer_role_uuid).toString();
201 addr = ctx_item->data(peer_role_address).toString();
203 StringQuery input(tr("PIN:"));
204 input.setWindowTitle(tr("PIN for ") + ctx_item->text());
205 if (input.exec() != QDialog::Accepted)
212 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
213 snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
214 uuid.toAscii().constData(),
215 input.get_string().toAscii().constData());
217 snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
218 addr.toAscii().constData(),
219 input.get_string().toAscii().constData());
221 reply_len = sizeof(reply) - 1;
222 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
224 msg.setIcon(QMessageBox::Warning);
225 msg.setText("Failed to set the WPS PIN.");
231 void Peers::ctx_refresh()
237 void Peers::add_station(QString info)
239 QStringList lines = info.split(QRegExp("\\n"));
242 for (QStringList::Iterator it = lines.begin();
243 it != lines.end(); it++) {
244 int pos = (*it).indexOf('=') + 1;
248 if ((*it).startsWith("wpsDeviceName="))
249 name = (*it).mid(pos);
255 QStandardItem *item = new QStandardItem(*laptop_icon, name);
257 item->setData(lines[0], peer_role_address);
258 item->setData(PEER_TYPE_ASSOCIATED_STATION,
260 item->setData(info, peer_role_details);
261 item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
262 model.appendRow(item);
267 void Peers::add_stations()
274 reply_len = sizeof(reply) - 1;
275 if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
279 reply[reply_len] = '\0';
282 while (*txt != '\0' && *txt != '\n')
285 if (strncmp(reply, "FAIL", 4) == 0 ||
286 strncmp(reply, "UNKNOWN", 7) == 0)
291 reply_len = sizeof(reply) - 1;
292 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
293 res = wpagui->ctrlRequest(cmd, reply, &reply_len);
298 void Peers::add_single_station(const char *addr)
304 reply_len = sizeof(reply) - 1;
305 snprintf(cmd, sizeof(cmd), "STA %s", addr);
306 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
309 reply[reply_len] = '\0';
312 while (*txt != '\0' && *txt != '\n')
315 if (strncmp(reply, "FAIL", 4) == 0 ||
316 strncmp(reply, "UNKNOWN", 7) == 0)
323 void Peers::add_scan_results()
332 snprintf(cmd, sizeof(cmd), "BSS %d", index++);
336 reply_len = sizeof(reply) - 1;
337 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
339 reply[reply_len] = '\0';
342 if (bss.isEmpty() || bss.startsWith("FAIL"))
345 QString ssid, bssid, flags, wps_name, pri_dev_type;
347 QStringList lines = bss.split(QRegExp("\\n"));
348 for (QStringList::Iterator it = lines.begin();
349 it != lines.end(); it++) {
350 int pos = (*it).indexOf('=') + 1;
354 if ((*it).startsWith("bssid="))
355 bssid = (*it).mid(pos);
356 else if ((*it).startsWith("flags="))
357 flags = (*it).mid(pos);
358 else if ((*it).startsWith("ssid="))
359 ssid = (*it).mid(pos);
360 else if ((*it).startsWith("wps_device_name="))
361 wps_name = (*it).mid(pos);
362 else if ((*it).startsWith("wps_primary_device_type="))
363 pri_dev_type = (*it).mid(pos);
366 QString name = wps_name;
368 name = ssid + "\n" + bssid;
370 QStandardItem *item = new QStandardItem(*ap_icon, name);
372 item->setData(bssid, peer_role_address);
374 if (flags.contains("[WPS"))
375 type = PEER_TYPE_AP_WPS;
378 item->setData(type, peer_role_type);
380 for (int i = 0; i < lines.size(); i++) {
381 if (lines[i].length() > 60) {
383 60, lines[i].length());
387 item->setToolTip(ItemType(type));
388 item->setData(lines.join("\n"), peer_role_details);
389 if (!pri_dev_type.isEmpty())
390 item->setData(pri_dev_type,
391 peer_role_pri_dev_type);
393 item->setData(ssid, peer_role_ssid);
394 model.appendRow(item);
400 void Peers::update_peers()
407 size_t replylen = sizeof(reply) - 1;
408 wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
415 QStandardItem * Peers::find_addr(QString addr)
417 if (model.rowCount() == 0)
420 QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
424 return model.itemFromIndex(lst[0]);
428 QStandardItem * Peers::find_uuid(QString uuid)
430 if (model.rowCount() == 0)
433 QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
437 return model.itemFromIndex(lst[0]);
441 void Peers::event_notify(WpaMsg msg)
443 QString text = msg.getMsg();
445 if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
447 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
449 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
451 QStringList items = text.split(' ');
452 QString uuid = items[1];
453 QString addr = items[2];
456 QStandardItem *item = find_addr(addr);
460 int pos = text.indexOf('[');
462 int pos2 = text.lastIndexOf(']');
464 items = text.mid(pos + 1, pos2 - pos - 1).
471 item = new QStandardItem(*laptop_icon, name);
473 item->setData(addr, peer_role_address);
474 item->setData(PEER_TYPE_WPS_PIN_NEEDED,
476 item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
477 item->setData(items.join("\n"), peer_role_details);
478 item->setData(items[5], peer_role_pri_dev_type);
479 model.appendRow(item);
484 if (text.startsWith(AP_STA_CONNECTED)) {
485 /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
486 QStringList items = text.split(' ');
487 QString addr = items[1];
488 QStandardItem *item = find_addr(addr);
489 if (item == NULL || item->data(peer_role_type).toInt() !=
490 PEER_TYPE_ASSOCIATED_STATION)
491 add_single_station(addr.toAscii().constData());
495 if (text.startsWith(AP_STA_DISCONNECTED)) {
496 /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
497 QStringList items = text.split(' ');
498 QString addr = items[1];
500 if (model.rowCount() == 0)
503 QModelIndexList lst = model.match(model.index(0, 0),
504 peer_role_address, addr);
505 for (int i = 0; i < lst.size(); i++) {
506 QStandardItem *item = model.itemFromIndex(lst[i]);
507 if (item && item->data(peer_role_type).toInt() ==
508 PEER_TYPE_ASSOCIATED_STATION)
509 model.removeRow(lst[i].row());
514 if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
516 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
517 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
518 * |Very friendly name|Company|Long description of the model|
519 * WAP|http://w1.fi/|http://w1.fi/hostapd/
521 QStringList items = text.split(' ');
522 if (items.size() < 5)
524 QString uuid = items[1];
525 QString addr = items[2];
526 QString pri_dev_type = items[3].mid(13);
527 int wps_state = items[4].mid(10).toInt();
529 int pos = text.indexOf('|');
532 items = text.mid(pos + 1).split('|');
533 if (items.size() < 1)
536 QStandardItem *item = find_uuid(uuid);
540 item = new QStandardItem(*ap_icon, items[0]);
542 item->setData(uuid, peer_role_uuid);
543 item->setData(addr, peer_role_address);
544 int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
545 PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
546 item->setData(type, peer_role_type);
547 item->setToolTip(ItemType(type));
548 item->setData(pri_dev_type, peer_role_pri_dev_type);
549 item->setData(items.join(QString("\n")),
551 model.appendRow(item);
557 if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
558 /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
559 QStringList items = text.split(' ');
560 if (items.size() < 2)
562 if (model.rowCount() == 0)
565 QModelIndexList lst = model.match(model.index(0, 0),
566 peer_role_uuid, items[1]);
567 for (int i = 0; i < lst.size(); i++) {
568 QStandardItem *item = model.itemFromIndex(lst[i]);
570 (item->data(peer_role_type).toInt() ==
571 PEER_TYPE_WPS_ER_AP ||
572 item->data(peer_role_type).toInt() ==
573 PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
574 model.removeRow(lst[i].row());
579 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
581 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
582 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
583 * pri_dev_type=1-0050F204-1
584 * |Wireless Client|Company|cmodel|123|12345|
586 QStringList items = text.split(' ');
587 if (items.size() < 3)
589 QString uuid = items[1];
590 QString addr = items[2];
591 QString pri_dev_type = items[6].mid(13);
592 int config_methods = -1;
593 int dev_passwd_id = -1;
595 for (int i = 3; i < items.size(); i++) {
596 int pos = items[i].indexOf('=') + 1;
599 QString val = items[i].mid(pos);
600 if (items[i].startsWith("config_methods=")) {
601 config_methods = val.toInt(0, 0);
602 } else if (items[i].startsWith("dev_passwd_id=")) {
603 dev_passwd_id = val.toInt();
607 int pos = text.indexOf('|');
610 items = text.mid(pos + 1).split('|');
611 if (items.size() < 1)
613 QString name = items[0];
614 if (name.length() == 0)
617 remove_enrollee_uuid(uuid);
620 item = new QStandardItem(*laptop_icon, name);
622 item->setData(uuid, peer_role_uuid);
623 item->setData(addr, peer_role_address);
624 item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
626 item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
627 item->setData(items.join(QString("\n")),
629 item->setData(pri_dev_type, peer_role_pri_dev_type);
630 if (config_methods >= 0)
631 item->setData(config_methods,
632 peer_role_config_methods);
633 if (dev_passwd_id >= 0)
634 item->setData(dev_passwd_id,
635 peer_role_dev_passwd_id);
636 model.appendRow(item);
642 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
644 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
647 QStringList items = text.split(' ');
648 if (items.size() < 2)
650 remove_enrollee_uuid(items[1]);
654 if (text.startsWith(WPS_EVENT_ENROLLEE_SEEN)) {
655 /* TODO: need to time out this somehow or remove on successful
658 * WPS-ENROLLEE-SEEN 02:00:00:00:01:00
659 * 572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1
661 * (MAC addr, UUID-E, pri dev type, config methods,
662 * dev passwd id, request type, [dev name])
664 QStringList items = text.split(' ');
665 if (items.size() < 7)
667 QString addr = items[1];
668 QString uuid = items[2];
669 QString pri_dev_type = items[3];
670 int config_methods = items[4].toInt(0, 0);
671 int dev_passwd_id = items[5].toInt();
674 int pos = text.indexOf('[');
676 int pos2 = text.lastIndexOf(']');
679 text.mid(pos + 1, pos2 - pos - 1).
689 item = find_uuid(uuid);
691 QVariant var = item->data(peer_role_config_methods);
692 QVariant var2 = item->data(peer_role_dev_passwd_id);
693 if ((var.isValid() && config_methods != var.toInt()) ||
694 (var2.isValid() && dev_passwd_id != var2.toInt()))
695 remove_enrollee_uuid(uuid);
700 item = new QStandardItem(*laptop_icon, name);
702 item->setData(uuid, peer_role_uuid);
703 item->setData(addr, peer_role_address);
704 item->setData(PEER_TYPE_WPS_ENROLLEE,
706 item->setToolTip(ItemType(PEER_TYPE_WPS_ENROLLEE));
707 item->setData(items.join(QString("\n")),
709 item->setData(pri_dev_type, peer_role_pri_dev_type);
710 item->setData(config_methods,
711 peer_role_config_methods);
712 item->setData(dev_passwd_id, peer_role_dev_passwd_id);
713 model.appendRow(item);
721 void Peers::closeEvent(QCloseEvent *)
725 size_t replylen = sizeof(reply) - 1;
726 wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
731 void Peers::done(int r)
738 void Peers::remove_enrollee_uuid(QString uuid)
740 if (model.rowCount() == 0)
743 QModelIndexList lst = model.match(model.index(0, 0),
744 peer_role_uuid, uuid);
745 for (int i = 0; i < lst.size(); i++) {
746 QStandardItem *item = model.itemFromIndex(lst[i]);
749 int type = item->data(peer_role_type).toInt();
750 if (type == PEER_TYPE_WPS_ER_ENROLLEE ||
751 type == PEER_TYPE_WPS_ENROLLEE)
752 model.removeRow(lst[i].row());
757 void Peers::properties()
759 if (ctx_item == NULL)
762 QMessageBox msg(this);
763 msg.setStandardButtons(QMessageBox::Ok);
764 msg.setDefaultButton(QMessageBox::Ok);
765 msg.setEscapeButton(QMessageBox::Ok);
766 msg.setWindowTitle(tr("Peer Properties"));
768 int type = ctx_item->data(peer_role_type).toInt();
769 QString title = Peers::ItemType(type);
771 msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
776 var = ctx_item->data(peer_role_address);
778 info += tr("Address: ") + var.toString() + QString("\n");
780 var = ctx_item->data(peer_role_uuid);
782 info += tr("UUID: ") + var.toString() + QString("\n");
784 var = ctx_item->data(peer_role_pri_dev_type);
786 info += tr("Primary Device Type: ") + var.toString() +
789 var = ctx_item->data(peer_role_ssid);
791 info += tr("SSID: ") + var.toString() + QString("\n");
793 var = ctx_item->data(peer_role_config_methods);
795 int methods = var.toInt();
796 info += tr("Configuration Methods: ");
797 if (methods & 0x0001)
798 info += tr("[USBA]");
799 if (methods & 0x0002)
800 info += tr("[Ethernet]");
801 if (methods & 0x0004)
802 info += tr("[Label]");
803 if (methods & 0x0008)
804 info += tr("[Display]");
805 if (methods & 0x0010)
806 info += tr("[Ext. NFC Token]");
807 if (methods & 0x0020)
808 info += tr("[Int. NFC Token]");
809 if (methods & 0x0040)
810 info += tr("[NFC Interface]");
811 if (methods & 0x0080)
812 info += tr("[Push Button]");
813 if (methods & 0x0100)
814 info += tr("[Keypad]");
818 var = ctx_item->data(peer_role_dev_passwd_id);
820 info += tr("Device Password ID: ") + var.toString();
821 switch (var.toInt()) {
823 info += tr(" (Default PIN)");
826 info += tr(" (User-specified PIN)");
829 info += tr(" (Machine-specified PIN)");
832 info += tr(" (Rekey)");
835 info += tr(" (Push Button)");
838 info += tr(" (Registrar-specified)");
844 msg.setInformativeText(info);
846 var = ctx_item->data(peer_role_details);
848 msg.setDetailedText(var.toString());
854 void Peers::connect_pbc()
856 if (ctx_item == NULL)
863 int peer_type = ctx_item->data(peer_role_type).toInt();
864 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
865 snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
866 ctx_item->data(peer_role_uuid).toString().toAscii().
869 snprintf(cmd, sizeof(cmd), "WPS_PBC");
871 reply_len = sizeof(reply) - 1;
872 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
874 msg.setIcon(QMessageBox::Warning);
875 msg.setText("Failed to start WPS PBC.");
881 void Peers::learn_ap_config()
883 if (ctx_item == NULL)
886 QString uuid = ctx_item->data(peer_role_uuid).toString();
888 StringQuery input(tr("AP PIN:"));
889 input.setWindowTitle(tr("AP PIN for ") + ctx_item->text());
890 if (input.exec() != QDialog::Accepted)
897 snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
898 uuid.toAscii().constData(),
899 input.get_string().toAscii().constData());
900 reply_len = sizeof(reply) - 1;
901 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
903 msg.setIcon(QMessageBox::Warning);
904 msg.setText(tr("Failed to start learning AP configuration."));