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,
36 * - add current AP info (e.g., from WPS) in station mode
40 PEER_TYPE_ASSOCIATED_STATION,
43 PEER_TYPE_WPS_PIN_NEEDED,
45 PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
46 PEER_TYPE_WPS_ER_ENROLLEE
50 Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
55 if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
57 default_icon = new QIcon(":/icons/wpa_gui.svg");
58 ap_icon = new QIcon(":/icons/ap.svg");
59 laptop_icon = new QIcon(":/icons/laptop.svg");
61 default_icon = new QIcon(":/icons/wpa_gui.png");
62 ap_icon = new QIcon(":/icons/ap.png");
63 laptop_icon = new QIcon(":/icons/laptop.png");
66 peers->setModel(&model);
67 peers->setResizeMode(QListView::Adjust);
69 peers->setContextMenuPolicy(Qt::CustomContextMenu);
70 connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
71 this, SLOT(context_menu(const QPoint &)));
77 void Peers::setWpaGui(WpaGui *_wpagui)
92 void Peers::languageChange()
98 QString Peers::ItemType(int type)
102 case PEER_TYPE_ASSOCIATED_STATION:
103 title = tr("Associated station");
108 case PEER_TYPE_AP_WPS:
109 title = tr("WPS AP");
111 case PEER_TYPE_WPS_PIN_NEEDED:
112 title = tr("WPS PIN needed");
114 case PEER_TYPE_WPS_ER_AP:
115 title = tr("ER: WPS AP");
117 case PEER_TYPE_WPS_ER_AP_UNCONFIGURED:
118 title = tr("ER: WPS AP (Unconfigured)");
120 case PEER_TYPE_WPS_ER_ENROLLEE:
121 title = tr("ER: WPS Enrollee");
128 void Peers::context_menu(const QPoint &pos)
130 QMenu *menu = new QMenu;
134 QModelIndex idx = peers->indexAt(pos);
136 ctx_item = model.itemFromIndex(idx);
137 int type = ctx_item->data(peer_role_type).toInt();
138 menu->addAction(Peers::ItemType(type))->setEnabled(false);
139 menu->addSeparator();
141 if (type == PEER_TYPE_ASSOCIATED_STATION ||
142 type == PEER_TYPE_AP_WPS ||
143 type == PEER_TYPE_WPS_PIN_NEEDED ||
144 type == PEER_TYPE_WPS_ER_ENROLLEE) {
145 /* TODO: only for peers that are requesting WPS PIN
147 menu->addAction(tr("Enter WPS PIN"), this,
151 menu->addAction(tr("Properties"), this, SLOT(properties()));
154 menu->addAction(QString("Refresh"), this, SLOT(ctx_refresh()));
157 menu->exec(peers->mapToGlobal(pos));
161 void Peers::enter_pin()
163 if (ctx_item == NULL)
166 int peer_type = ctx_item->data(peer_role_type).toInt();
169 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
170 uuid = ctx_item->data(peer_role_uuid).toString();
172 addr = ctx_item->data(peer_role_address).toString();
174 StringQuery input(tr("PIN:"));
175 input.setWindowTitle(tr("PIN for ") + ctx_item->text());
176 if (input.exec() != QDialog::Accepted)
183 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
184 snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
185 uuid.toAscii().constData(),
186 input.get_string().toAscii().constData());
188 snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
189 addr.toAscii().constData(),
190 input.get_string().toAscii().constData());
192 reply_len = sizeof(reply) - 1;
193 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
195 msg.setIcon(QMessageBox::Warning);
196 msg.setText("Failed to set the WPS PIN.");
202 void Peers::ctx_refresh()
208 void Peers::add_station(QString info)
210 QStringList lines = info.split(QRegExp("\\n"));
213 for (QStringList::Iterator it = lines.begin();
214 it != lines.end(); it++) {
215 int pos = (*it).indexOf('=') + 1;
219 if ((*it).startsWith("wpsDeviceName="))
220 name = (*it).mid(pos);
226 QStandardItem *item = new QStandardItem(*laptop_icon, name);
228 item->setData(lines[0], peer_role_address);
229 item->setData(PEER_TYPE_ASSOCIATED_STATION,
231 item->setData(info, peer_role_details);
232 item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
233 model.appendRow(item);
238 void Peers::add_stations()
245 reply_len = sizeof(reply) - 1;
246 if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
250 reply[reply_len] = '\0';
253 while (*txt != '\0' && *txt != '\n')
256 if (strncmp(reply, "FAIL", 4) == 0 ||
257 strncmp(reply, "UNKNOWN", 7) == 0)
262 reply_len = sizeof(reply) - 1;
263 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
264 res = wpagui->ctrlRequest(cmd, reply, &reply_len);
269 void Peers::add_single_station(const char *addr)
275 reply_len = sizeof(reply) - 1;
276 snprintf(cmd, sizeof(cmd), "STA %s", addr);
277 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
280 reply[reply_len] = '\0';
283 while (*txt != '\0' && *txt != '\n')
286 if (strncmp(reply, "FAIL", 4) == 0 ||
287 strncmp(reply, "UNKNOWN", 7) == 0)
294 void Peers::add_scan_results()
303 snprintf(cmd, sizeof(cmd), "BSS %d", index++);
307 reply_len = sizeof(reply) - 1;
308 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
310 reply[reply_len] = '\0';
313 if (bss.isEmpty() || bss.startsWith("FAIL"))
316 QString ssid, bssid, flags, wps_name, pri_dev_type;
318 QStringList lines = bss.split(QRegExp("\\n"));
319 for (QStringList::Iterator it = lines.begin();
320 it != lines.end(); it++) {
321 int pos = (*it).indexOf('=') + 1;
325 if ((*it).startsWith("bssid="))
326 bssid = (*it).mid(pos);
327 else if ((*it).startsWith("flags="))
328 flags = (*it).mid(pos);
329 else if ((*it).startsWith("ssid="))
330 ssid = (*it).mid(pos);
331 else if ((*it).startsWith("wps_device_name="))
332 wps_name = (*it).mid(pos);
333 else if ((*it).startsWith("wps_primary_device_type="))
334 pri_dev_type = (*it).mid(pos);
337 QString name = wps_name;
339 name = ssid + "\n" + bssid;
341 QStandardItem *item = new QStandardItem(*ap_icon, name);
343 item->setData(bssid, peer_role_address);
345 if (flags.contains("[WPS"))
346 type = PEER_TYPE_AP_WPS;
349 item->setData(type, peer_role_type);
351 for (int i = 0; i < lines.size(); i++) {
352 if (lines[i].length() > 60) {
354 60, lines[i].length());
358 item->setToolTip(ItemType(type));
359 item->setData(lines.join("\n"), peer_role_details);
360 if (!pri_dev_type.isEmpty())
361 item->setData(pri_dev_type,
362 peer_role_pri_dev_type);
364 item->setData(ssid, peer_role_ssid);
365 model.appendRow(item);
371 void Peers::update_peers()
378 size_t replylen = sizeof(reply) - 1;
379 wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
386 QStandardItem * Peers::find_addr(QString addr)
388 if (model.rowCount() == 0)
391 QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
395 return model.itemFromIndex(lst[0]);
399 QStandardItem * Peers::find_uuid(QString uuid)
401 if (model.rowCount() == 0)
404 QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
408 return model.itemFromIndex(lst[0]);
412 void Peers::event_notify(WpaMsg msg)
414 QString text = msg.getMsg();
416 if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
418 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
420 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
422 QStringList items = text.split(' ');
423 QString uuid = items[1];
424 QString addr = items[2];
427 QStandardItem *item = find_addr(addr);
431 int pos = text.indexOf('[');
433 int pos2 = text.lastIndexOf(']');
435 items = text.mid(pos + 1, pos2 - pos - 1).
442 item = new QStandardItem(*laptop_icon, name);
444 item->setData(addr, peer_role_address);
445 item->setData(PEER_TYPE_WPS_PIN_NEEDED,
447 item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
448 item->setData(items.join("\n"), peer_role_details);
449 item->setData(items[5], peer_role_pri_dev_type);
450 model.appendRow(item);
455 if (text.startsWith(AP_STA_CONNECTED)) {
456 /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
457 QStringList items = text.split(' ');
458 QString addr = items[1];
459 QStandardItem *item = find_addr(addr);
460 if (item == NULL || item->data(peer_role_type).toInt() !=
461 PEER_TYPE_ASSOCIATED_STATION)
462 add_single_station(addr.toAscii().constData());
466 if (text.startsWith(AP_STA_DISCONNECTED)) {
467 /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
468 QStringList items = text.split(' ');
469 QString addr = items[1];
471 if (model.rowCount() == 0)
474 QModelIndexList lst = model.match(model.index(0, 0),
475 peer_role_address, addr);
476 for (int i = 0; i < lst.size(); i++) {
477 QStandardItem *item = model.itemFromIndex(lst[i]);
478 if (item && item->data(peer_role_type).toInt() ==
479 PEER_TYPE_ASSOCIATED_STATION)
480 model.removeRow(lst[i].row());
485 if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
487 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
488 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
489 * |Very friendly name|Company|Long description of the model|
490 * WAP|http://w1.fi/|http://w1.fi/hostapd/
492 QStringList items = text.split(' ');
493 if (items.size() < 5)
495 QString uuid = items[1];
496 QString addr = items[2];
497 QString pri_dev_type = items[3].mid(13);
498 int wps_state = items[4].mid(10).toInt();
500 int pos = text.indexOf('|');
503 items = text.mid(pos + 1).split('|');
504 if (items.size() < 1)
507 QStandardItem *item = find_uuid(uuid);
511 item = new QStandardItem(*ap_icon, items[0]);
513 item->setData(uuid, peer_role_uuid);
514 item->setData(addr, peer_role_address);
515 int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
516 PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
517 item->setData(type, peer_role_type);
518 item->setToolTip(ItemType(type));
519 item->setData(pri_dev_type, peer_role_pri_dev_type);
520 item->setData(items.join(QString("\n")),
522 model.appendRow(item);
528 if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
529 /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
530 QStringList items = text.split(' ');
531 if (items.size() < 2)
533 if (model.rowCount() == 0)
536 QModelIndexList lst = model.match(model.index(0, 0),
537 peer_role_uuid, items[1]);
538 for (int i = 0; i < lst.size(); i++) {
539 QStandardItem *item = model.itemFromIndex(lst[i]);
541 (item->data(peer_role_type).toInt() ==
542 PEER_TYPE_WPS_ER_AP ||
543 item->data(peer_role_type).toInt() ==
544 PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
545 model.removeRow(lst[i].row());
550 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
552 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
553 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
554 * pri_dev_type=1-0050F204-1
555 * |Wireless Client|Company|cmodel|123|12345|
557 QStringList items = text.split(' ');
558 if (items.size() < 3)
560 QString uuid = items[1];
561 QString addr = items[2];
562 QString pri_dev_type = items[6].mid(13);
564 int pos = text.indexOf('|');
567 items = text.mid(pos + 1).split('|');
568 if (items.size() < 1)
570 QString name = items[0];
571 if (name.length() == 0)
574 remove_enrollee_uuid(uuid);
577 item = new QStandardItem(*laptop_icon, name);
579 item->setData(uuid, peer_role_uuid);
580 item->setData(addr, peer_role_address);
581 item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
583 item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
584 item->setData(items.join(QString("\n")),
586 item->setData(pri_dev_type, peer_role_pri_dev_type);
587 model.appendRow(item);
593 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
595 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
598 QStringList items = text.split(' ');
599 if (items.size() < 2)
601 remove_enrollee_uuid(items[1]);
607 void Peers::closeEvent(QCloseEvent *)
611 size_t replylen = sizeof(reply) - 1;
612 wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
617 void Peers::done(int r)
624 void Peers::remove_enrollee_uuid(QString uuid)
626 if (model.rowCount() == 0)
629 QModelIndexList lst = model.match(model.index(0, 0),
630 peer_role_uuid, uuid);
631 for (int i = 0; i < lst.size(); i++) {
632 QStandardItem *item = model.itemFromIndex(lst[i]);
633 if (item && item->data(peer_role_type).toInt() ==
634 PEER_TYPE_WPS_ER_ENROLLEE)
635 model.removeRow(lst[i].row());
640 void Peers::properties()
642 if (ctx_item == NULL)
645 QMessageBox msg(this);
646 msg.setStandardButtons(QMessageBox::Ok);
647 msg.setDefaultButton(QMessageBox::Ok);
648 msg.setEscapeButton(QMessageBox::Ok);
649 msg.setWindowTitle(tr("Peer Properties"));
651 int type = ctx_item->data(peer_role_type).toInt();
652 QString title = Peers::ItemType(type);
654 msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
659 var = ctx_item->data(peer_role_address);
661 info += tr("Address: ") + var.toString() + QString("\n");
663 var = ctx_item->data(peer_role_uuid);
665 info += tr("UUID: ") + var.toString() + QString("\n");
667 var = ctx_item->data(peer_role_pri_dev_type);
669 info += tr("Primary Device Type: ") + var.toString() +
672 var = ctx_item->data(peer_role_ssid);
674 info += tr("SSID: ") + var.toString() + QString("\n");
676 msg.setInformativeText(info);
678 var = ctx_item->data(peer_role_details);
680 msg.setDetailedText(var.toString());