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"
25 static const int peer_role_address = Qt::UserRole + 1;
26 static const int peer_role_type = Qt::UserRole + 2;
27 static const int peer_role_uuid = Qt::UserRole + 3;
31 * - add current AP info (e.g., from WPS) in station mode
32 * - different icons to indicate peer type
36 PEER_TYPE_ASSOCIATED_STATION,
39 PEER_TYPE_WPS_PIN_NEEDED,
41 PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
42 PEER_TYPE_WPS_ER_ENROLLEE
46 Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
51 if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
53 default_icon = new QIcon(":/icons/wpa_gui.svg");
54 ap_icon = new QIcon(":/icons/ap.svg");
55 laptop_icon = new QIcon(":/icons/laptop.svg");
57 default_icon = new QIcon(":/icons/wpa_gui.png");
58 ap_icon = new QIcon(":/icons/ap.png");
59 laptop_icon = new QIcon(":/icons/laptop.png");
62 peers->setModel(&model);
63 peers->setResizeMode(QListView::Adjust);
65 peers->setContextMenuPolicy(Qt::CustomContextMenu);
66 connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
67 this, SLOT(context_menu(const QPoint &)));
73 void Peers::setWpaGui(WpaGui *_wpagui)
88 void Peers::languageChange()
94 void Peers::context_menu(const QPoint &pos)
96 QMenu *menu = new QMenu;
100 QModelIndex idx = peers->indexAt(pos);
102 ctx_item = model.itemFromIndex(idx);
103 int type = ctx_item->data(peer_role_type).toInt();
106 case PEER_TYPE_ASSOCIATED_STATION:
107 title = tr("Associated station");
112 case PEER_TYPE_AP_WPS:
113 title = tr("WPS AP");
115 case PEER_TYPE_WPS_PIN_NEEDED:
116 title = tr("WPS PIN needed");
118 case PEER_TYPE_WPS_ER_AP:
119 title = tr("ER: WPS AP");
121 case PEER_TYPE_WPS_ER_AP_UNCONFIGURED:
122 title = tr("ER: WPS AP (Unconfigured)");
124 case PEER_TYPE_WPS_ER_ENROLLEE:
125 title = tr("ER: WPS Enrollee");
128 menu->addAction(title)->setEnabled(false);
129 menu->addSeparator();
131 if (type == PEER_TYPE_ASSOCIATED_STATION ||
132 type == PEER_TYPE_AP_WPS ||
133 type == PEER_TYPE_WPS_PIN_NEEDED ||
134 type == PEER_TYPE_WPS_ER_ENROLLEE) {
135 /* TODO: only for peers that are requesting WPS PIN
137 menu->addAction(QString("Enter WPS PIN"), this,
142 menu->addAction(QString("Refresh"), this, SLOT(ctx_refresh()));
145 menu->exec(peers->mapToGlobal(pos));
149 void Peers::enter_pin()
151 if (ctx_item == NULL)
154 int peer_type = ctx_item->data(peer_role_type).toInt();
157 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
158 uuid = ctx_item->data(peer_role_uuid).toString();
160 addr = ctx_item->data(peer_role_address).toString();
162 StringQuery input(tr("PIN:"));
163 input.setWindowTitle(tr("PIN for ") + ctx_item->text());
164 if (input.exec() != QDialog::Accepted)
171 if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
172 snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
173 uuid.toAscii().constData(),
174 input.get_string().toAscii().constData());
176 snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
177 addr.toAscii().constData(),
178 input.get_string().toAscii().constData());
180 reply_len = sizeof(reply) - 1;
181 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
183 msg.setIcon(QMessageBox::Warning);
184 msg.setText("Failed to set the WPS PIN.");
190 void Peers::ctx_refresh()
196 void Peers::add_station(QString info)
198 QStringList lines = info.split(QRegExp("\\n"));
201 for (QStringList::Iterator it = lines.begin();
202 it != lines.end(); it++) {
203 int pos = (*it).indexOf('=') + 1;
207 if ((*it).startsWith("wpsDeviceName="))
208 name = (*it).mid(pos);
214 QStandardItem *item = new QStandardItem(*laptop_icon, name);
216 item->setData(lines[0], peer_role_address);
217 item->setData(PEER_TYPE_ASSOCIATED_STATION,
219 item->setToolTip(info);
220 model.appendRow(item);
225 void Peers::add_stations()
232 reply_len = sizeof(reply) - 1;
233 if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
237 reply[reply_len] = '\0';
240 while (*txt != '\0' && *txt != '\n')
243 if (strncmp(reply, "FAIL", 4) == 0 ||
244 strncmp(reply, "UNKNOWN", 7) == 0)
249 reply_len = sizeof(reply) - 1;
250 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
251 res = wpagui->ctrlRequest(cmd, reply, &reply_len);
256 void Peers::add_single_station(const char *addr)
262 reply_len = sizeof(reply) - 1;
263 snprintf(cmd, sizeof(cmd), "STA %s", addr);
264 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
267 reply[reply_len] = '\0';
270 while (*txt != '\0' && *txt != '\n')
273 if (strncmp(reply, "FAIL", 4) == 0 ||
274 strncmp(reply, "UNKNOWN", 7) == 0)
281 void Peers::add_scan_results()
290 snprintf(cmd, sizeof(cmd), "BSS %d", index++);
294 reply_len = sizeof(reply) - 1;
295 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
297 reply[reply_len] = '\0';
300 if (bss.isEmpty() || bss.startsWith("FAIL"))
303 QString ssid, bssid, flags, wps_name;
305 QStringList lines = bss.split(QRegExp("\\n"));
306 for (QStringList::Iterator it = lines.begin();
307 it != lines.end(); it++) {
308 int pos = (*it).indexOf('=') + 1;
312 if ((*it).startsWith("bssid="))
313 bssid = (*it).mid(pos);
314 else if ((*it).startsWith("flags="))
315 flags = (*it).mid(pos);
316 else if ((*it).startsWith("ssid="))
317 ssid = (*it).mid(pos);
318 else if ((*it).startsWith("wps_device_name="))
319 wps_name = (*it).mid(pos);
322 QString name = wps_name;
324 name = ssid + "\n" + bssid;
326 QStandardItem *item = new QStandardItem(*ap_icon, name);
328 item->setData(bssid, peer_role_address);
329 if (flags.contains("[WPS"))
330 item->setData(PEER_TYPE_AP_WPS,
333 item->setData(PEER_TYPE_AP, peer_role_type);
335 for (int i = 0; i < lines.size(); i++) {
336 if (lines[i].length() > 60) {
338 60, lines[i].length());
342 item->setToolTip(lines.join("\n"));
343 model.appendRow(item);
349 void Peers::update_peers()
356 size_t replylen = sizeof(reply) - 1;
357 wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
364 QStandardItem * Peers::find_addr(QString addr)
366 if (model.rowCount() == 0)
369 QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
373 return model.itemFromIndex(lst[0]);
377 QStandardItem * Peers::find_uuid(QString uuid)
379 if (model.rowCount() == 0)
382 QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
386 return model.itemFromIndex(lst[0]);
390 void Peers::event_notify(WpaMsg msg)
392 QString text = msg.getMsg();
394 if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
396 * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
398 * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
400 QStringList items = text.split(' ');
401 QString uuid = items[1];
402 QString addr = items[2];
405 QStandardItem *item = find_addr(addr);
409 int pos = text.indexOf('[');
411 int pos2 = text.lastIndexOf(']');
413 items = text.mid(pos + 1, pos2 - pos - 1).
420 item = new QStandardItem(*laptop_icon, name);
422 item->setData(addr, peer_role_address);
423 item->setData(PEER_TYPE_WPS_PIN_NEEDED,
425 item->setToolTip(items.join(QString("\n")));
426 model.appendRow(item);
431 if (text.startsWith(AP_STA_CONNECTED)) {
432 /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
433 QStringList items = text.split(' ');
434 QString addr = items[1];
435 QStandardItem *item = find_addr(addr);
436 if (item == NULL || item->data(peer_role_type).toInt() !=
437 PEER_TYPE_ASSOCIATED_STATION)
438 add_single_station(addr.toAscii().constData());
442 if (text.startsWith(AP_STA_DISCONNECTED)) {
443 /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
444 QStringList items = text.split(' ');
445 QString addr = items[1];
447 if (model.rowCount() == 0)
450 QModelIndexList lst = model.match(model.index(0, 0),
451 peer_role_address, addr);
452 for (int i = 0; i < lst.size(); i++) {
453 QStandardItem *item = model.itemFromIndex(lst[i]);
454 if (item && item->data(peer_role_type).toInt() ==
455 PEER_TYPE_ASSOCIATED_STATION)
456 model.removeRow(lst[i].row());
461 if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
463 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
464 * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
465 * |Very friendly name|Company|Long description of the model|
466 * WAP|http://w1.fi/|http://w1.fi/hostapd/
468 QStringList items = text.split(' ');
469 if (items.size() < 5)
471 QString uuid = items[1];
472 QString addr = items[2];
473 QString pri_dev_type = items[3];
474 int wps_state = items[4].mid(10).toInt();
476 int pos = text.indexOf('|');
479 items = text.mid(pos + 1).split('|');
480 if (items.size() < 1)
483 QStandardItem *item = find_uuid(uuid);
487 item = new QStandardItem(*ap_icon, items[0]);
489 item->setData(uuid, peer_role_uuid);
490 item->setData(addr, peer_role_address);
491 item->setData(wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
492 PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
494 item->setToolTip(addr + QString("\n") +
495 pri_dev_type + QString("\n") +
496 items.join(QString("\n")));
497 model.appendRow(item);
503 if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
504 /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
505 QStringList items = text.split(' ');
506 if (items.size() < 2)
508 if (model.rowCount() == 0)
511 QModelIndexList lst = model.match(model.index(0, 0),
512 peer_role_uuid, items[1]);
513 for (int i = 0; i < lst.size(); i++) {
514 QStandardItem *item = model.itemFromIndex(lst[i]);
516 (item->data(peer_role_type).toInt() ==
517 PEER_TYPE_WPS_ER_AP ||
518 item->data(peer_role_type).toInt() ==
519 PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
520 model.removeRow(lst[i].row());
525 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
527 * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
528 * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
529 * pri_dev_type=1-0050F204-1
530 * |Wireless Client|Company|cmodel|123|12345|
532 QStringList items = text.split(' ');
533 if (items.size() < 3)
535 QString uuid = items[1];
536 QString addr = items[2];
538 int pos = text.indexOf('|');
541 items = text.mid(pos + 1).split('|');
542 if (items.size() < 1)
544 QString name = items[0];
545 if (name.length() == 0)
548 remove_enrollee_uuid(uuid);
551 item = new QStandardItem(*laptop_icon, name);
553 item->setData(uuid, peer_role_uuid);
554 item->setData(addr, peer_role_address);
555 item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
557 item->setToolTip(items.join(QString("\n")));
558 model.appendRow(item);
564 if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
566 * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
569 QStringList items = text.split(' ');
570 if (items.size() < 2)
572 remove_enrollee_uuid(items[1]);
578 void Peers::closeEvent(QCloseEvent *)
582 size_t replylen = sizeof(reply) - 1;
583 wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
588 void Peers::done(int r)
595 void Peers::remove_enrollee_uuid(QString uuid)
597 if (model.rowCount() == 0)
600 QModelIndexList lst = model.match(model.index(0, 0),
601 peer_role_uuid, uuid);
602 for (int i = 0; i < lst.size(); i++) {
603 QStandardItem *item = model.itemFromIndex(lst[i]);
604 if (item && item->data(peer_role_type).toInt() ==
605 PEER_TYPE_WPS_ER_ENROLLEE)
606 model.removeRow(lst[i].row());