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>
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 menu->addAction(tr("Properties"), this, SLOT(properties()));
172 menu->addAction(QString("Refresh"), this, SLOT(ctx_refresh()));
175 menu->exec(peers->mapToGlobal(pos));
179 void Peers::enter_pin()
181 if (ctx_item == NULL)
184 int peer_type = ctx_item->data(peer_role_type).toInt();
187 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
188 uuid = ctx_item->data(peer_role_uuid).toString();
190 addr = ctx_item->data(peer_role_address).toString();
192 StringQuery input(tr("PIN:"));
193 input.setWindowTitle(tr("PIN for ") + ctx_item->text());
194 if (input.exec() != QDialog::Accepted)
201 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
202 snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
203 uuid.toAscii().constData(),
204 input.get_string().toAscii().constData());
206 snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
207 addr.toAscii().constData(),
208 input.get_string().toAscii().constData());
210 reply_len = sizeof(reply) - 1;
211 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
213 msg.setIcon(QMessageBox::Warning);
214 msg.setText("Failed to set the WPS PIN.");
220 void Peers::ctx_refresh()
226 void Peers::add_station(QString info)
228 QStringList lines = info.split(QRegExp("\\n"));
231 for (QStringList::Iterator it = lines.begin();
232 it != lines.end(); it++) {
233 int pos = (*it).indexOf('=') + 1;
237 if ((*it).startsWith("wpsDeviceName="))
238 name = (*it).mid(pos);
244 QStandardItem *item = new QStandardItem(*laptop_icon, name);
246 item->setData(lines[0], peer_role_address);
247 item->setData(PEER_TYPE_ASSOCIATED_STATION,
249 item->setData(info, peer_role_details);
250 item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
251 model.appendRow(item);
256 void Peers::add_stations()
263 reply_len = sizeof(reply) - 1;
264 if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
268 reply[reply_len] = '\0';
271 while (*txt != '\0' && *txt != '\n')
274 if (strncmp(reply, "FAIL", 4) == 0 ||
275 strncmp(reply, "UNKNOWN", 7) == 0)
280 reply_len = sizeof(reply) - 1;
281 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
282 res = wpagui->ctrlRequest(cmd, reply, &reply_len);
287 void Peers::add_single_station(const char *addr)
293 reply_len = sizeof(reply) - 1;
294 snprintf(cmd, sizeof(cmd), "STA %s", addr);
295 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
298 reply[reply_len] = '\0';
301 while (*txt != '\0' && *txt != '\n')
304 if (strncmp(reply, "FAIL", 4) == 0 ||
305 strncmp(reply, "UNKNOWN", 7) == 0)
312 void Peers::add_scan_results()
321 snprintf(cmd, sizeof(cmd), "BSS %d", index++);
325 reply_len = sizeof(reply) - 1;
326 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
328 reply[reply_len] = '\0';
331 if (bss.isEmpty() || bss.startsWith("FAIL"))
334 QString ssid, bssid, flags, wps_name, pri_dev_type;
336 QStringList lines = bss.split(QRegExp("\\n"));
337 for (QStringList::Iterator it = lines.begin();
338 it != lines.end(); it++) {
339 int pos = (*it).indexOf('=') + 1;
343 if ((*it).startsWith("bssid="))
344 bssid = (*it).mid(pos);
345 else if ((*it).startsWith("flags="))
346 flags = (*it).mid(pos);
347 else if ((*it).startsWith("ssid="))
348 ssid = (*it).mid(pos);
349 else if ((*it).startsWith("wps_device_name="))
350 wps_name = (*it).mid(pos);
351 else if ((*it).startsWith("wps_primary_device_type="))
352 pri_dev_type = (*it).mid(pos);
355 QString name = wps_name;
357 name = ssid + "\n" + bssid;
359 QStandardItem *item = new QStandardItem(*ap_icon, name);
361 item->setData(bssid, peer_role_address);
363 if (flags.contains("[WPS"))
364 type = PEER_TYPE_AP_WPS;
367 item->setData(type, peer_role_type);
369 for (int i = 0; i < lines.size(); i++) {
370 if (lines[i].length() > 60) {
372 60, lines[i].length());
376 item->setToolTip(ItemType(type));
377 item->setData(lines.join("\n"), peer_role_details);
378 if (!pri_dev_type.isEmpty())
379 item->setData(pri_dev_type,
380 peer_role_pri_dev_type);
382 item->setData(ssid, peer_role_ssid);
383 model.appendRow(item);
389 void Peers::update_peers()
396 size_t replylen = sizeof(reply) - 1;
397 wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
404 QStandardItem * Peers::find_addr(QString addr)
406 if (model.rowCount() == 0)
409 QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
413 return model.itemFromIndex(lst[0]);
417 QStandardItem * Peers::find_uuid(QString uuid)
419 if (model.rowCount() == 0)
422 QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
426 return model.itemFromIndex(lst[0]);
430 void Peers::event_notify(WpaMsg msg)
432 QString text = msg.getMsg();
434 if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
436 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
438 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
440 QStringList items = text.split(' ');
441 QString uuid = items[1];
442 QString addr = items[2];
445 QStandardItem *item = find_addr(addr);
449 int pos = text.indexOf('[');
451 int pos2 = text.lastIndexOf(']');
453 items = text.mid(pos + 1, pos2 - pos - 1).
460 item = new QStandardItem(*laptop_icon, name);
462 item->setData(addr, peer_role_address);
463 item->setData(PEER_TYPE_WPS_PIN_NEEDED,
465 item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
466 item->setData(items.join("\n"), peer_role_details);
467 item->setData(items[5], peer_role_pri_dev_type);
468 model.appendRow(item);
473 if (text.startsWith(AP_STA_CONNECTED)) {
474 /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
475 QStringList items = text.split(' ');
476 QString addr = items[1];
477 QStandardItem *item = find_addr(addr);
478 if (item == NULL || item->data(peer_role_type).toInt() !=
479 PEER_TYPE_ASSOCIATED_STATION)
480 add_single_station(addr.toAscii().constData());
484 if (text.startsWith(AP_STA_DISCONNECTED)) {
485 /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
486 QStringList items = text.split(' ');
487 QString addr = items[1];
489 if (model.rowCount() == 0)
492 QModelIndexList lst = model.match(model.index(0, 0),
493 peer_role_address, addr);
494 for (int i = 0; i < lst.size(); i++) {
495 QStandardItem *item = model.itemFromIndex(lst[i]);
496 if (item && item->data(peer_role_type).toInt() ==
497 PEER_TYPE_ASSOCIATED_STATION)
498 model.removeRow(lst[i].row());
503 if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
505 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
506 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
507 * |Very friendly name|Company|Long description of the model|
508 * WAP|http://w1.fi/|http://w1.fi/hostapd/
510 QStringList items = text.split(' ');
511 if (items.size() < 5)
513 QString uuid = items[1];
514 QString addr = items[2];
515 QString pri_dev_type = items[3].mid(13);
516 int wps_state = items[4].mid(10).toInt();
518 int pos = text.indexOf('|');
521 items = text.mid(pos + 1).split('|');
522 if (items.size() < 1)
525 QStandardItem *item = find_uuid(uuid);
529 item = new QStandardItem(*ap_icon, items[0]);
531 item->setData(uuid, peer_role_uuid);
532 item->setData(addr, peer_role_address);
533 int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
534 PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
535 item->setData(type, peer_role_type);
536 item->setToolTip(ItemType(type));
537 item->setData(pri_dev_type, peer_role_pri_dev_type);
538 item->setData(items.join(QString("\n")),
540 model.appendRow(item);
546 if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
547 /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
548 QStringList items = text.split(' ');
549 if (items.size() < 2)
551 if (model.rowCount() == 0)
554 QModelIndexList lst = model.match(model.index(0, 0),
555 peer_role_uuid, items[1]);
556 for (int i = 0; i < lst.size(); i++) {
557 QStandardItem *item = model.itemFromIndex(lst[i]);
559 (item->data(peer_role_type).toInt() ==
560 PEER_TYPE_WPS_ER_AP ||
561 item->data(peer_role_type).toInt() ==
562 PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
563 model.removeRow(lst[i].row());
568 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
570 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
571 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
572 * pri_dev_type=1-0050F204-1
573 * |Wireless Client|Company|cmodel|123|12345|
575 QStringList items = text.split(' ');
576 if (items.size() < 3)
578 QString uuid = items[1];
579 QString addr = items[2];
580 QString pri_dev_type = items[6].mid(13);
581 int config_methods = -1;
582 int dev_passwd_id = -1;
584 for (int i = 3; i < items.size(); i++) {
585 int pos = items[i].indexOf('=') + 1;
588 QString val = items[i].mid(pos);
589 if (items[i].startsWith("config_methods=")) {
590 config_methods = val.toInt(0, 0);
591 } else if (items[i].startsWith("dev_passwd_id=")) {
592 dev_passwd_id = val.toInt();
596 int pos = text.indexOf('|');
599 items = text.mid(pos + 1).split('|');
600 if (items.size() < 1)
602 QString name = items[0];
603 if (name.length() == 0)
606 remove_enrollee_uuid(uuid);
609 item = new QStandardItem(*laptop_icon, name);
611 item->setData(uuid, peer_role_uuid);
612 item->setData(addr, peer_role_address);
613 item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
615 item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
616 item->setData(items.join(QString("\n")),
618 item->setData(pri_dev_type, peer_role_pri_dev_type);
619 if (config_methods >= 0)
620 item->setData(config_methods,
621 peer_role_config_methods);
622 if (dev_passwd_id >= 0)
623 item->setData(dev_passwd_id,
624 peer_role_dev_passwd_id);
625 model.appendRow(item);
631 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
633 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
636 QStringList items = text.split(' ');
637 if (items.size() < 2)
639 remove_enrollee_uuid(items[1]);
645 void Peers::closeEvent(QCloseEvent *)
649 size_t replylen = sizeof(reply) - 1;
650 wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
655 void Peers::done(int r)
662 void Peers::remove_enrollee_uuid(QString uuid)
664 if (model.rowCount() == 0)
667 QModelIndexList lst = model.match(model.index(0, 0),
668 peer_role_uuid, uuid);
669 for (int i = 0; i < lst.size(); i++) {
670 QStandardItem *item = model.itemFromIndex(lst[i]);
671 if (item && item->data(peer_role_type).toInt() ==
672 PEER_TYPE_WPS_ER_ENROLLEE)
673 model.removeRow(lst[i].row());
678 void Peers::properties()
680 if (ctx_item == NULL)
683 QMessageBox msg(this);
684 msg.setStandardButtons(QMessageBox::Ok);
685 msg.setDefaultButton(QMessageBox::Ok);
686 msg.setEscapeButton(QMessageBox::Ok);
687 msg.setWindowTitle(tr("Peer Properties"));
689 int type = ctx_item->data(peer_role_type).toInt();
690 QString title = Peers::ItemType(type);
692 msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
697 var = ctx_item->data(peer_role_address);
699 info += tr("Address: ") + var.toString() + QString("\n");
701 var = ctx_item->data(peer_role_uuid);
703 info += tr("UUID: ") + var.toString() + QString("\n");
705 var = ctx_item->data(peer_role_pri_dev_type);
707 info += tr("Primary Device Type: ") + var.toString() +
710 var = ctx_item->data(peer_role_ssid);
712 info += tr("SSID: ") + var.toString() + QString("\n");
714 var = ctx_item->data(peer_role_config_methods);
716 int methods = var.toInt();
717 info += tr("Configuration Methods: ");
718 if (methods & 0x0001)
719 info += tr("[USBA]");
720 if (methods & 0x0002)
721 info += tr("[Ethernet]");
722 if (methods & 0x0004)
723 info += tr("[Label]");
724 if (methods & 0x0008)
725 info += tr("[Display]");
726 if (methods & 0x0010)
727 info += tr("[Ext. NFC Token]");
728 if (methods & 0x0020)
729 info += tr("[Int. NFC Token]");
730 if (methods & 0x0040)
731 info += tr("[NFC Interface]");
732 if (methods & 0x0080)
733 info += tr("[Push Button]");
734 if (methods & 0x0100)
735 info += tr("[Keypad]");
739 var = ctx_item->data(peer_role_dev_passwd_id);
741 info += tr("Device Password ID: ") + var.toString();
742 switch (var.toInt()) {
744 info += tr(" (Default PIN)");
747 info += tr(" (User-specified PIN)");
750 info += tr(" (Machine-specified PIN)");
753 info += tr(" (Rekey)");
756 info += tr(" (Push Button)");
759 info += tr(" (Registrar-specified)");
765 msg.setInformativeText(info);
767 var = ctx_item->data(peer_role_details);
769 msg.setDetailedText(var.toString());
775 void Peers::connect_pbc()
777 if (ctx_item == NULL)
784 int peer_type = ctx_item->data(peer_role_type).toInt();
785 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
786 snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
787 ctx_item->data(peer_role_uuid).toString().toAscii().
790 snprintf(cmd, sizeof(cmd), "WPS_PBC");
792 reply_len = sizeof(reply) - 1;
793 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
795 msg.setIcon(QMessageBox::Warning);
796 msg.setText("Failed to start WPS PBC.");