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 if (type == PEER_TYPE_ASSOCIATED_STATION ||
144 type == PEER_TYPE_AP_WPS ||
145 type == PEER_TYPE_WPS_PIN_NEEDED ||
146 type == PEER_TYPE_WPS_ER_ENROLLEE) {
147 /* TODO: only for peers that are requesting WPS PIN
149 menu->addAction(tr("Enter WPS PIN"), this,
153 menu->addAction(tr("Properties"), this, SLOT(properties()));
156 menu->addAction(QString("Refresh"), this, SLOT(ctx_refresh()));
159 menu->exec(peers->mapToGlobal(pos));
163 void Peers::enter_pin()
165 if (ctx_item == NULL)
168 int peer_type = ctx_item->data(peer_role_type).toInt();
171 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
172 uuid = ctx_item->data(peer_role_uuid).toString();
174 addr = ctx_item->data(peer_role_address).toString();
176 StringQuery input(tr("PIN:"));
177 input.setWindowTitle(tr("PIN for ") + ctx_item->text());
178 if (input.exec() != QDialog::Accepted)
185 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
186 snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
187 uuid.toAscii().constData(),
188 input.get_string().toAscii().constData());
190 snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
191 addr.toAscii().constData(),
192 input.get_string().toAscii().constData());
194 reply_len = sizeof(reply) - 1;
195 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
197 msg.setIcon(QMessageBox::Warning);
198 msg.setText("Failed to set the WPS PIN.");
204 void Peers::ctx_refresh()
210 void Peers::add_station(QString info)
212 QStringList lines = info.split(QRegExp("\\n"));
215 for (QStringList::Iterator it = lines.begin();
216 it != lines.end(); it++) {
217 int pos = (*it).indexOf('=') + 1;
221 if ((*it).startsWith("wpsDeviceName="))
222 name = (*it).mid(pos);
228 QStandardItem *item = new QStandardItem(*laptop_icon, name);
230 item->setData(lines[0], peer_role_address);
231 item->setData(PEER_TYPE_ASSOCIATED_STATION,
233 item->setData(info, peer_role_details);
234 item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
235 model.appendRow(item);
240 void Peers::add_stations()
247 reply_len = sizeof(reply) - 1;
248 if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
252 reply[reply_len] = '\0';
255 while (*txt != '\0' && *txt != '\n')
258 if (strncmp(reply, "FAIL", 4) == 0 ||
259 strncmp(reply, "UNKNOWN", 7) == 0)
264 reply_len = sizeof(reply) - 1;
265 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
266 res = wpagui->ctrlRequest(cmd, reply, &reply_len);
271 void Peers::add_single_station(const char *addr)
277 reply_len = sizeof(reply) - 1;
278 snprintf(cmd, sizeof(cmd), "STA %s", addr);
279 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
282 reply[reply_len] = '\0';
285 while (*txt != '\0' && *txt != '\n')
288 if (strncmp(reply, "FAIL", 4) == 0 ||
289 strncmp(reply, "UNKNOWN", 7) == 0)
296 void Peers::add_scan_results()
305 snprintf(cmd, sizeof(cmd), "BSS %d", index++);
309 reply_len = sizeof(reply) - 1;
310 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
312 reply[reply_len] = '\0';
315 if (bss.isEmpty() || bss.startsWith("FAIL"))
318 QString ssid, bssid, flags, wps_name, pri_dev_type;
320 QStringList lines = bss.split(QRegExp("\\n"));
321 for (QStringList::Iterator it = lines.begin();
322 it != lines.end(); it++) {
323 int pos = (*it).indexOf('=') + 1;
327 if ((*it).startsWith("bssid="))
328 bssid = (*it).mid(pos);
329 else if ((*it).startsWith("flags="))
330 flags = (*it).mid(pos);
331 else if ((*it).startsWith("ssid="))
332 ssid = (*it).mid(pos);
333 else if ((*it).startsWith("wps_device_name="))
334 wps_name = (*it).mid(pos);
335 else if ((*it).startsWith("wps_primary_device_type="))
336 pri_dev_type = (*it).mid(pos);
339 QString name = wps_name;
341 name = ssid + "\n" + bssid;
343 QStandardItem *item = new QStandardItem(*ap_icon, name);
345 item->setData(bssid, peer_role_address);
347 if (flags.contains("[WPS"))
348 type = PEER_TYPE_AP_WPS;
351 item->setData(type, peer_role_type);
353 for (int i = 0; i < lines.size(); i++) {
354 if (lines[i].length() > 60) {
356 60, lines[i].length());
360 item->setToolTip(ItemType(type));
361 item->setData(lines.join("\n"), peer_role_details);
362 if (!pri_dev_type.isEmpty())
363 item->setData(pri_dev_type,
364 peer_role_pri_dev_type);
366 item->setData(ssid, peer_role_ssid);
367 model.appendRow(item);
373 void Peers::update_peers()
380 size_t replylen = sizeof(reply) - 1;
381 wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
388 QStandardItem * Peers::find_addr(QString addr)
390 if (model.rowCount() == 0)
393 QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
397 return model.itemFromIndex(lst[0]);
401 QStandardItem * Peers::find_uuid(QString uuid)
403 if (model.rowCount() == 0)
406 QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
410 return model.itemFromIndex(lst[0]);
414 void Peers::event_notify(WpaMsg msg)
416 QString text = msg.getMsg();
418 if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
420 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
422 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
424 QStringList items = text.split(' ');
425 QString uuid = items[1];
426 QString addr = items[2];
429 QStandardItem *item = find_addr(addr);
433 int pos = text.indexOf('[');
435 int pos2 = text.lastIndexOf(']');
437 items = text.mid(pos + 1, pos2 - pos - 1).
444 item = new QStandardItem(*laptop_icon, name);
446 item->setData(addr, peer_role_address);
447 item->setData(PEER_TYPE_WPS_PIN_NEEDED,
449 item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
450 item->setData(items.join("\n"), peer_role_details);
451 item->setData(items[5], peer_role_pri_dev_type);
452 model.appendRow(item);
457 if (text.startsWith(AP_STA_CONNECTED)) {
458 /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
459 QStringList items = text.split(' ');
460 QString addr = items[1];
461 QStandardItem *item = find_addr(addr);
462 if (item == NULL || item->data(peer_role_type).toInt() !=
463 PEER_TYPE_ASSOCIATED_STATION)
464 add_single_station(addr.toAscii().constData());
468 if (text.startsWith(AP_STA_DISCONNECTED)) {
469 /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
470 QStringList items = text.split(' ');
471 QString addr = items[1];
473 if (model.rowCount() == 0)
476 QModelIndexList lst = model.match(model.index(0, 0),
477 peer_role_address, addr);
478 for (int i = 0; i < lst.size(); i++) {
479 QStandardItem *item = model.itemFromIndex(lst[i]);
480 if (item && item->data(peer_role_type).toInt() ==
481 PEER_TYPE_ASSOCIATED_STATION)
482 model.removeRow(lst[i].row());
487 if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
489 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
490 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
491 * |Very friendly name|Company|Long description of the model|
492 * WAP|http://w1.fi/|http://w1.fi/hostapd/
494 QStringList items = text.split(' ');
495 if (items.size() < 5)
497 QString uuid = items[1];
498 QString addr = items[2];
499 QString pri_dev_type = items[3].mid(13);
500 int wps_state = items[4].mid(10).toInt();
502 int pos = text.indexOf('|');
505 items = text.mid(pos + 1).split('|');
506 if (items.size() < 1)
509 QStandardItem *item = find_uuid(uuid);
513 item = new QStandardItem(*ap_icon, items[0]);
515 item->setData(uuid, peer_role_uuid);
516 item->setData(addr, peer_role_address);
517 int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
518 PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
519 item->setData(type, peer_role_type);
520 item->setToolTip(ItemType(type));
521 item->setData(pri_dev_type, peer_role_pri_dev_type);
522 item->setData(items.join(QString("\n")),
524 model.appendRow(item);
530 if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
531 /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
532 QStringList items = text.split(' ');
533 if (items.size() < 2)
535 if (model.rowCount() == 0)
538 QModelIndexList lst = model.match(model.index(0, 0),
539 peer_role_uuid, items[1]);
540 for (int i = 0; i < lst.size(); i++) {
541 QStandardItem *item = model.itemFromIndex(lst[i]);
543 (item->data(peer_role_type).toInt() ==
544 PEER_TYPE_WPS_ER_AP ||
545 item->data(peer_role_type).toInt() ==
546 PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
547 model.removeRow(lst[i].row());
552 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
554 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
555 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
556 * pri_dev_type=1-0050F204-1
557 * |Wireless Client|Company|cmodel|123|12345|
559 QStringList items = text.split(' ');
560 if (items.size() < 3)
562 QString uuid = items[1];
563 QString addr = items[2];
564 QString pri_dev_type = items[6].mid(13);
565 int config_methods = -1;
566 int dev_passwd_id = -1;
568 for (int i = 3; i < items.size(); i++) {
569 int pos = items[i].indexOf('=') + 1;
572 QString val = items[i].mid(pos);
573 if (items[i].startsWith("config_methods=")) {
574 config_methods = val.toInt(0, 0);
575 } else if (items[i].startsWith("dev_passwd_id=")) {
576 dev_passwd_id = val.toInt();
580 int pos = text.indexOf('|');
583 items = text.mid(pos + 1).split('|');
584 if (items.size() < 1)
586 QString name = items[0];
587 if (name.length() == 0)
590 remove_enrollee_uuid(uuid);
593 item = new QStandardItem(*laptop_icon, name);
595 item->setData(uuid, peer_role_uuid);
596 item->setData(addr, peer_role_address);
597 item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
599 item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
600 item->setData(items.join(QString("\n")),
602 item->setData(pri_dev_type, peer_role_pri_dev_type);
603 if (config_methods >= 0)
604 item->setData(config_methods,
605 peer_role_config_methods);
606 if (dev_passwd_id >= 0)
607 item->setData(dev_passwd_id,
608 peer_role_dev_passwd_id);
609 model.appendRow(item);
615 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
617 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
620 QStringList items = text.split(' ');
621 if (items.size() < 2)
623 remove_enrollee_uuid(items[1]);
629 void Peers::closeEvent(QCloseEvent *)
633 size_t replylen = sizeof(reply) - 1;
634 wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
639 void Peers::done(int r)
646 void Peers::remove_enrollee_uuid(QString uuid)
648 if (model.rowCount() == 0)
651 QModelIndexList lst = model.match(model.index(0, 0),
652 peer_role_uuid, uuid);
653 for (int i = 0; i < lst.size(); i++) {
654 QStandardItem *item = model.itemFromIndex(lst[i]);
655 if (item && item->data(peer_role_type).toInt() ==
656 PEER_TYPE_WPS_ER_ENROLLEE)
657 model.removeRow(lst[i].row());
662 void Peers::properties()
664 if (ctx_item == NULL)
667 QMessageBox msg(this);
668 msg.setStandardButtons(QMessageBox::Ok);
669 msg.setDefaultButton(QMessageBox::Ok);
670 msg.setEscapeButton(QMessageBox::Ok);
671 msg.setWindowTitle(tr("Peer Properties"));
673 int type = ctx_item->data(peer_role_type).toInt();
674 QString title = Peers::ItemType(type);
676 msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
681 var = ctx_item->data(peer_role_address);
683 info += tr("Address: ") + var.toString() + QString("\n");
685 var = ctx_item->data(peer_role_uuid);
687 info += tr("UUID: ") + var.toString() + QString("\n");
689 var = ctx_item->data(peer_role_pri_dev_type);
691 info += tr("Primary Device Type: ") + var.toString() +
694 var = ctx_item->data(peer_role_ssid);
696 info += tr("SSID: ") + var.toString() + QString("\n");
698 var = ctx_item->data(peer_role_config_methods);
700 int methods = var.toInt();
701 info += tr("Configuration Methods: ");
702 if (methods & 0x0001)
703 info += tr("[USBA]");
704 if (methods & 0x0002)
705 info += tr("[Ethernet]");
706 if (methods & 0x0004)
707 info += tr("[Label]");
708 if (methods & 0x0008)
709 info += tr("[Display]");
710 if (methods & 0x0010)
711 info += tr("[Ext. NFC Token]");
712 if (methods & 0x0020)
713 info += tr("[Int. NFC Token]");
714 if (methods & 0x0040)
715 info += tr("[NFC Interface]");
716 if (methods & 0x0080)
717 info += tr("[Push Button]");
718 if (methods & 0x0100)
719 info += tr("[Keypad]");
723 var = ctx_item->data(peer_role_dev_passwd_id);
725 info += tr("Device Password ID: ") + var.toString();
726 switch (var.toInt()) {
728 info += tr(" (Default PIN)");
731 info += tr(" (User-specified PIN)");
734 info += tr(" (Machine-specified PIN)");
737 info += tr(" (Rekey)");
740 info += tr(" (Push Button)");
743 info += tr(" (Registrar-specified)");
749 msg.setInformativeText(info);
751 var = ctx_item->data(peer_role_details);
753 msg.setDetailedText(var.toString());