peers.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /*
  2. * wpa_gui - Peers class
  3. * Copyright (c) 2009, Atheros Communications
  4. *
  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.
  8. *
  9. * Alternatively, this software may be distributed under the terms of BSD
  10. * license.
  11. *
  12. * See README and COPYING for more details.
  13. */
  14. #include <cstdio>
  15. #include <QImageReader>
  16. #include <QMessageBox>
  17. #include "wpa_ctrl.h"
  18. #include "wpagui.h"
  19. #include "stringquery.h"
  20. #include "peers.h"
  21. static const int peer_role_address = Qt::UserRole + 1;
  22. static const int peer_role_type = Qt::UserRole + 2;
  23. static const int peer_role_uuid = Qt::UserRole + 3;
  24. /*
  25. * TODO:
  26. * - add current AP info (e.g., from WPS) in station mode
  27. * - different icons to indicate peer type
  28. */
  29. enum peer_type {
  30. PEER_TYPE_ASSOCIATED_STATION,
  31. PEER_TYPE_AP,
  32. PEER_TYPE_AP_WPS,
  33. PEER_TYPE_WPS_PIN_NEEDED,
  34. PEER_TYPE_WPS_ER_AP,
  35. PEER_TYPE_WPS_ER_ENROLLEE
  36. };
  37. Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
  38. : QDialog(parent)
  39. {
  40. setupUi(this);
  41. if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
  42. {
  43. default_icon = new QIcon(":/icons/wpa_gui.svg");
  44. ap_icon = new QIcon(":/icons/ap.svg");
  45. laptop_icon = new QIcon(":/icons/laptop.svg");
  46. } else {
  47. default_icon = new QIcon(":/icons/wpa_gui.png");
  48. ap_icon = new QIcon(":/icons/ap.png");
  49. laptop_icon = new QIcon(":/icons/laptop.png");
  50. }
  51. peers->setModel(&model);
  52. peers->setResizeMode(QListView::Adjust);
  53. peers->setContextMenuPolicy(Qt::CustomContextMenu);
  54. connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
  55. this, SLOT(context_menu(const QPoint &)));
  56. wpagui = NULL;
  57. }
  58. void Peers::setWpaGui(WpaGui *_wpagui)
  59. {
  60. wpagui = _wpagui;
  61. update_peers();
  62. }
  63. Peers::~Peers()
  64. {
  65. delete default_icon;
  66. delete ap_icon;
  67. delete laptop_icon;
  68. }
  69. void Peers::languageChange()
  70. {
  71. retranslateUi(this);
  72. }
  73. void Peers::context_menu(const QPoint &pos)
  74. {
  75. QMenu *menu = new QMenu;
  76. if (menu == NULL)
  77. return;
  78. QModelIndex idx = peers->indexAt(pos);
  79. if (idx.isValid()) {
  80. ctx_item = model.itemFromIndex(idx);
  81. int type = ctx_item->data(peer_role_type).toInt();
  82. QString title;
  83. switch (type) {
  84. case PEER_TYPE_ASSOCIATED_STATION:
  85. title = tr("Associated station");
  86. break;
  87. case PEER_TYPE_AP:
  88. title = tr("AP");
  89. break;
  90. case PEER_TYPE_AP_WPS:
  91. title = tr("WPS AP");
  92. break;
  93. case PEER_TYPE_WPS_PIN_NEEDED:
  94. title = tr("WPS PIN needed");
  95. break;
  96. case PEER_TYPE_WPS_ER_AP:
  97. title = tr("ER: WPS AP");
  98. break;
  99. case PEER_TYPE_WPS_ER_ENROLLEE:
  100. title = tr("ER: WPS Enrollee");
  101. break;
  102. }
  103. menu->addAction(title)->setEnabled(false);
  104. menu->addSeparator();
  105. if (type == PEER_TYPE_ASSOCIATED_STATION ||
  106. type == PEER_TYPE_AP_WPS ||
  107. type == PEER_TYPE_WPS_PIN_NEEDED ||
  108. type == PEER_TYPE_WPS_ER_ENROLLEE) {
  109. /* TODO: only for peers that are requesting WPS PIN
  110. * method */
  111. menu->addAction(QString("Enter WPS PIN"), this,
  112. SLOT(enter_pin()));
  113. }
  114. } else {
  115. ctx_item = NULL;
  116. menu->addAction(QString("Refresh"), this, SLOT(ctx_refresh()));
  117. }
  118. menu->exec(peers->mapToGlobal(pos));
  119. }
  120. void Peers::enter_pin()
  121. {
  122. if (ctx_item == NULL)
  123. return;
  124. int peer_type = ctx_item->data(peer_role_type).toInt();
  125. QString uuid;
  126. QString addr;
  127. if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
  128. uuid = ctx_item->data(peer_role_uuid).toString();
  129. else
  130. addr = ctx_item->data(peer_role_address).toString();
  131. StringQuery input(tr("PIN:"));
  132. input.setWindowTitle(tr("PIN for ") + ctx_item->text());
  133. if (input.exec() != QDialog::Accepted)
  134. return;
  135. char cmd[100];
  136. char reply[100];
  137. size_t reply_len;
  138. if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
  139. snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
  140. uuid.toAscii().constData(),
  141. input.get_string().toAscii().constData());
  142. } else {
  143. snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
  144. addr.toAscii().constData(),
  145. input.get_string().toAscii().constData());
  146. }
  147. reply_len = sizeof(reply) - 1;
  148. if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
  149. QMessageBox msg;
  150. msg.setIcon(QMessageBox::Warning);
  151. msg.setText("Failed to set the WPS PIN.");
  152. msg.exec();
  153. }
  154. }
  155. void Peers::ctx_refresh()
  156. {
  157. update_peers();
  158. }
  159. void Peers::add_station(QString info)
  160. {
  161. QStringList lines = info.split(QRegExp("\\n"));
  162. QString name;
  163. for (QStringList::Iterator it = lines.begin();
  164. it != lines.end(); it++) {
  165. int pos = (*it).indexOf('=') + 1;
  166. if (pos < 1)
  167. continue;
  168. if ((*it).startsWith("wpsDeviceName="))
  169. name = (*it).mid(pos);
  170. }
  171. if (name.isEmpty())
  172. name = lines[0];
  173. QStandardItem *item = new QStandardItem(*laptop_icon, name);
  174. if (item) {
  175. item->setData(lines[0], peer_role_address);
  176. item->setData(PEER_TYPE_ASSOCIATED_STATION,
  177. peer_role_type);
  178. item->setToolTip(info);
  179. model.appendRow(item);
  180. }
  181. }
  182. void Peers::add_stations()
  183. {
  184. char reply[2048];
  185. size_t reply_len;
  186. char cmd[30];
  187. int res;
  188. reply_len = sizeof(reply) - 1;
  189. if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
  190. return;
  191. do {
  192. reply[reply_len] = '\0';
  193. QString info(reply);
  194. char *txt = reply;
  195. while (*txt != '\0' && *txt != '\n')
  196. txt++;
  197. *txt++ = '\0';
  198. if (strncmp(reply, "FAIL", 4) == 0 ||
  199. strncmp(reply, "UNKNOWN", 7) == 0)
  200. break;
  201. add_station(info);
  202. reply_len = sizeof(reply) - 1;
  203. snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
  204. res = wpagui->ctrlRequest(cmd, reply, &reply_len);
  205. } while (res >= 0);
  206. }
  207. void Peers::add_single_station(const char *addr)
  208. {
  209. char reply[2048];
  210. size_t reply_len;
  211. char cmd[30];
  212. reply_len = sizeof(reply) - 1;
  213. snprintf(cmd, sizeof(cmd), "STA %s", addr);
  214. if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
  215. return;
  216. reply[reply_len] = '\0';
  217. QString info(reply);
  218. char *txt = reply;
  219. while (*txt != '\0' && *txt != '\n')
  220. txt++;
  221. *txt++ = '\0';
  222. if (strncmp(reply, "FAIL", 4) == 0 ||
  223. strncmp(reply, "UNKNOWN", 7) == 0)
  224. return;
  225. add_station(info);
  226. }
  227. void Peers::add_scan_results()
  228. {
  229. char reply[2048];
  230. size_t reply_len;
  231. int index;
  232. char cmd[20];
  233. index = 0;
  234. while (wpagui) {
  235. snprintf(cmd, sizeof(cmd), "BSS %d", index++);
  236. if (index > 1000)
  237. break;
  238. reply_len = sizeof(reply) - 1;
  239. if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
  240. break;
  241. reply[reply_len] = '\0';
  242. QString bss(reply);
  243. if (bss.isEmpty() || bss.startsWith("FAIL"))
  244. break;
  245. QString ssid, bssid, flags, wps_name;
  246. QStringList lines = bss.split(QRegExp("\\n"));
  247. for (QStringList::Iterator it = lines.begin();
  248. it != lines.end(); it++) {
  249. int pos = (*it).indexOf('=') + 1;
  250. if (pos < 1)
  251. continue;
  252. if ((*it).startsWith("bssid="))
  253. bssid = (*it).mid(pos);
  254. else if ((*it).startsWith("flags="))
  255. flags = (*it).mid(pos);
  256. else if ((*it).startsWith("ssid="))
  257. ssid = (*it).mid(pos);
  258. else if ((*it).startsWith("wps_device_name="))
  259. wps_name = (*it).mid(pos);
  260. }
  261. QString name = wps_name;
  262. if (name.isEmpty())
  263. name = ssid + "\n" + bssid;
  264. QStandardItem *item = new QStandardItem(*ap_icon, name);
  265. if (item) {
  266. item->setData(bssid, peer_role_address);
  267. if (flags.contains("[WPS"))
  268. item->setData(PEER_TYPE_AP_WPS,
  269. peer_role_type);
  270. else
  271. item->setData(PEER_TYPE_AP, peer_role_type);
  272. for (int i = 0; i < lines.size(); i++) {
  273. if (lines[i].length() > 60) {
  274. lines[i].remove(
  275. 60, lines[i].length());
  276. lines[i] += "..";
  277. }
  278. }
  279. item->setToolTip(lines.join("\n"));
  280. model.appendRow(item);
  281. }
  282. }
  283. }
  284. void Peers::update_peers()
  285. {
  286. model.clear();
  287. if (wpagui == NULL)
  288. return;
  289. char reply[20];
  290. size_t replylen = sizeof(reply) - 1;
  291. wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
  292. add_stations();
  293. add_scan_results();
  294. }
  295. QStandardItem * Peers::find_addr(QString addr)
  296. {
  297. if (model.rowCount() == 0)
  298. return NULL;
  299. QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
  300. addr);
  301. if (lst.size() == 0)
  302. return NULL;
  303. return model.itemFromIndex(lst[0]);
  304. }
  305. QStandardItem * Peers::find_uuid(QString uuid)
  306. {
  307. if (model.rowCount() == 0)
  308. return NULL;
  309. QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
  310. uuid);
  311. if (lst.size() == 0)
  312. return NULL;
  313. return model.itemFromIndex(lst[0]);
  314. }
  315. void Peers::event_notify(WpaMsg msg)
  316. {
  317. QString text = msg.getMsg();
  318. if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
  319. /*
  320. * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
  321. * 02:2a:c4:18:5b:f3
  322. * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
  323. */
  324. QStringList items = text.split(' ');
  325. QString uuid = items[1];
  326. QString addr = items[2];
  327. QString name = "";
  328. QStandardItem *item = find_addr(addr);
  329. if (item)
  330. return;
  331. int pos = text.indexOf('[');
  332. if (pos >= 0) {
  333. int pos2 = text.lastIndexOf(']');
  334. if (pos2 >= pos) {
  335. items = text.mid(pos + 1, pos2 - pos - 1).
  336. split('|');
  337. name = items[0];
  338. items.append(addr);
  339. }
  340. }
  341. item = new QStandardItem(*laptop_icon, name);
  342. if (item) {
  343. item->setData(addr, peer_role_address);
  344. item->setData(PEER_TYPE_WPS_PIN_NEEDED,
  345. peer_role_type);
  346. item->setToolTip(items.join(QString("\n")));
  347. model.appendRow(item);
  348. }
  349. return;
  350. }
  351. if (text.startsWith(AP_STA_CONNECTED)) {
  352. /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
  353. QStringList items = text.split(' ');
  354. QString addr = items[1];
  355. QStandardItem *item = find_addr(addr);
  356. if (item == NULL || item->data(peer_role_type).toInt() !=
  357. PEER_TYPE_ASSOCIATED_STATION)
  358. add_single_station(addr.toAscii().constData());
  359. return;
  360. }
  361. if (text.startsWith(AP_STA_DISCONNECTED)) {
  362. /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
  363. QStringList items = text.split(' ');
  364. QString addr = items[1];
  365. if (model.rowCount() == 0)
  366. return;
  367. QModelIndexList lst = model.match(model.index(0, 0),
  368. peer_role_address, addr);
  369. for (int i = 0; i < lst.size(); i++) {
  370. QStandardItem *item = model.itemFromIndex(lst[i]);
  371. if (item && item->data(peer_role_type).toInt() ==
  372. PEER_TYPE_ASSOCIATED_STATION)
  373. model.removeRow(lst[i].row());
  374. }
  375. return;
  376. }
  377. if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
  378. /*
  379. * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002|
  380. * Very friendly name|Company|Long description of the model|
  381. * WAP|http://w1.fi/|http://w1.fi/hostapd/
  382. */
  383. int pos = text.indexOf(' ');
  384. if (pos < 0)
  385. return;
  386. QStringList items = text.mid(pos + 1).split('|');
  387. if (items.size() < 2)
  388. return;
  389. QStandardItem *item = find_uuid(items[0]);
  390. if (item)
  391. return;
  392. item = new QStandardItem(*ap_icon, items[1]);
  393. if (item) {
  394. item->setData(items[0], peer_role_uuid);
  395. item->setData(PEER_TYPE_WPS_ER_AP, peer_role_type);
  396. item->setToolTip(items.join(QString("\n")));
  397. model.appendRow(item);
  398. }
  399. return;
  400. }
  401. if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
  402. /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
  403. QStringList items = text.split(' ');
  404. if (items.size() < 2)
  405. return;
  406. if (model.rowCount() == 0)
  407. return;
  408. QModelIndexList lst = model.match(model.index(0, 0),
  409. peer_role_uuid, items[1]);
  410. for (int i = 0; i < lst.size(); i++) {
  411. QStandardItem *item = model.itemFromIndex(lst[i]);
  412. if (item && item->data(peer_role_type).toInt() ==
  413. PEER_TYPE_WPS_ER_AP)
  414. model.removeRow(lst[i].row());
  415. }
  416. return;
  417. }
  418. if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
  419. /*
  420. * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
  421. * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
  422. * pri_dev_type=1-0050F204-1
  423. * |Wireless Client|Company|cmodel|123|12345|
  424. */
  425. QStringList items = text.split(' ');
  426. if (items.size() < 3)
  427. return;
  428. QString uuid = items[1];
  429. QString addr = items[2];
  430. int pos = text.indexOf('|');
  431. if (pos < 0)
  432. return;
  433. items = text.mid(pos + 1).split('|');
  434. if (items.size() < 1)
  435. return;
  436. QString name = items[0];
  437. if (name.length() == 0)
  438. name = addr;
  439. remove_enrollee_uuid(uuid);
  440. QStandardItem *item;
  441. item = new QStandardItem(*laptop_icon, name);
  442. if (item) {
  443. item->setData(uuid, peer_role_uuid);
  444. item->setData(addr, peer_role_address);
  445. item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
  446. peer_role_type);
  447. item->setToolTip(items.join(QString("\n")));
  448. model.appendRow(item);
  449. }
  450. return;
  451. }
  452. if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
  453. /*
  454. * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
  455. * 02:66:a0:ee:17:27
  456. */
  457. QStringList items = text.split(' ');
  458. if (items.size() < 2)
  459. return;
  460. remove_enrollee_uuid(items[1]);
  461. return;
  462. }
  463. }
  464. void Peers::closeEvent(QCloseEvent *)
  465. {
  466. if (wpagui) {
  467. char reply[20];
  468. size_t replylen = sizeof(reply) - 1;
  469. wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
  470. }
  471. }
  472. void Peers::done(int r)
  473. {
  474. QDialog::done(r);
  475. close();
  476. }
  477. void Peers::remove_enrollee_uuid(QString uuid)
  478. {
  479. if (model.rowCount() == 0)
  480. return;
  481. QModelIndexList lst = model.match(model.index(0, 0),
  482. peer_role_uuid, uuid);
  483. for (int i = 0; i < lst.size(); i++) {
  484. QStandardItem *item = model.itemFromIndex(lst[i]);
  485. if (item && item->data(peer_role_type).toInt() ==
  486. PEER_TYPE_WPS_ER_ENROLLEE)
  487. model.removeRow(lst[i].row());
  488. }
  489. }