peers.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  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. StringQuery input(tr("PIN:"));
  125. input.setWindowTitle(tr("PIN for ") + ctx_item->text());
  126. if (input.exec() != QDialog::Accepted)
  127. return;
  128. char cmd[100];
  129. char reply[100];
  130. size_t reply_len;
  131. if (ctx_item->data(peer_role_type).toInt() ==
  132. PEER_TYPE_WPS_ER_ENROLLEE) {
  133. QString uuid = ctx_item->data(peer_role_uuid).toString();
  134. snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
  135. uuid.toAscii().constData(),
  136. input.get_string().toAscii().constData());
  137. } else {
  138. QString addr = ctx_item->data(peer_role_address).toString();
  139. snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
  140. addr.toAscii().constData(),
  141. input.get_string().toAscii().constData());
  142. }
  143. reply_len = sizeof(reply) - 1;
  144. if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
  145. QMessageBox msg;
  146. msg.setIcon(QMessageBox::Warning);
  147. msg.setText("Failed to set the WPS PIN.");
  148. msg.exec();
  149. }
  150. }
  151. void Peers::ctx_refresh()
  152. {
  153. update_peers();
  154. }
  155. void Peers::add_station(QString info)
  156. {
  157. QStringList lines = info.split(QRegExp("\\n"));
  158. QString name;
  159. for (QStringList::Iterator it = lines.begin();
  160. it != lines.end(); it++) {
  161. int pos = (*it).indexOf('=') + 1;
  162. if (pos < 1)
  163. continue;
  164. if ((*it).startsWith("wpsDeviceName="))
  165. name = (*it).mid(pos);
  166. }
  167. if (name.isEmpty())
  168. name = lines[0];
  169. QStandardItem *item = new QStandardItem(*laptop_icon, name);
  170. if (item) {
  171. item->setData(lines[0], peer_role_address);
  172. item->setData(PEER_TYPE_ASSOCIATED_STATION,
  173. peer_role_type);
  174. item->setToolTip(info);
  175. model.appendRow(item);
  176. }
  177. }
  178. void Peers::add_stations()
  179. {
  180. char reply[2048];
  181. size_t reply_len;
  182. char cmd[30];
  183. int res;
  184. reply_len = sizeof(reply) - 1;
  185. if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
  186. return;
  187. do {
  188. reply[reply_len] = '\0';
  189. QString info(reply);
  190. char *txt = reply;
  191. while (*txt != '\0' && *txt != '\n')
  192. txt++;
  193. *txt++ = '\0';
  194. if (strncmp(reply, "FAIL", 4) == 0 ||
  195. strncmp(reply, "UNKNOWN", 7) == 0)
  196. break;
  197. add_station(info);
  198. reply_len = sizeof(reply) - 1;
  199. snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
  200. res = wpagui->ctrlRequest(cmd, reply, &reply_len);
  201. } while (res >= 0);
  202. }
  203. void Peers::add_single_station(const char *addr)
  204. {
  205. char reply[2048];
  206. size_t reply_len;
  207. char cmd[30];
  208. reply_len = sizeof(reply) - 1;
  209. snprintf(cmd, sizeof(cmd), "STA %s", addr);
  210. if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
  211. return;
  212. reply[reply_len] = '\0';
  213. QString info(reply);
  214. char *txt = reply;
  215. while (*txt != '\0' && *txt != '\n')
  216. txt++;
  217. *txt++ = '\0';
  218. if (strncmp(reply, "FAIL", 4) == 0 ||
  219. strncmp(reply, "UNKNOWN", 7) == 0)
  220. return;
  221. add_station(info);
  222. }
  223. void Peers::add_scan_results()
  224. {
  225. char reply[2048];
  226. size_t reply_len;
  227. int index;
  228. char cmd[20];
  229. index = 0;
  230. while (wpagui) {
  231. snprintf(cmd, sizeof(cmd), "BSS %d", index++);
  232. if (index > 1000)
  233. break;
  234. reply_len = sizeof(reply) - 1;
  235. if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
  236. break;
  237. reply[reply_len] = '\0';
  238. QString bss(reply);
  239. if (bss.isEmpty() || bss.startsWith("FAIL"))
  240. break;
  241. QString ssid, bssid, flags, wps_name;
  242. QStringList lines = bss.split(QRegExp("\\n"));
  243. for (QStringList::Iterator it = lines.begin();
  244. it != lines.end(); it++) {
  245. int pos = (*it).indexOf('=') + 1;
  246. if (pos < 1)
  247. continue;
  248. if ((*it).startsWith("bssid="))
  249. bssid = (*it).mid(pos);
  250. else if ((*it).startsWith("flags="))
  251. flags = (*it).mid(pos);
  252. else if ((*it).startsWith("ssid="))
  253. ssid = (*it).mid(pos);
  254. else if ((*it).startsWith("wps_device_name="))
  255. wps_name = (*it).mid(pos);
  256. }
  257. QString name = wps_name;
  258. if (name.isEmpty())
  259. name = ssid + "\n" + bssid;
  260. QStandardItem *item = new QStandardItem(*ap_icon, name);
  261. if (item) {
  262. item->setData(bssid, peer_role_address);
  263. if (flags.contains("[WPS"))
  264. item->setData(PEER_TYPE_AP_WPS,
  265. peer_role_type);
  266. else
  267. item->setData(PEER_TYPE_AP, peer_role_type);
  268. for (int i = 0; i < lines.size(); i++) {
  269. if (lines[i].length() > 60) {
  270. lines[i].remove(
  271. 60, lines[i].length());
  272. lines[i] += "..";
  273. }
  274. }
  275. item->setToolTip(lines.join("\n"));
  276. model.appendRow(item);
  277. }
  278. }
  279. }
  280. void Peers::update_peers()
  281. {
  282. model.clear();
  283. if (wpagui == NULL)
  284. return;
  285. char reply[20];
  286. size_t replylen = sizeof(reply) - 1;
  287. wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
  288. add_stations();
  289. add_scan_results();
  290. }
  291. QStandardItem * Peers::find_addr(QString addr)
  292. {
  293. if (model.rowCount() == 0)
  294. return NULL;
  295. QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
  296. addr);
  297. if (lst.size() == 0)
  298. return NULL;
  299. return model.itemFromIndex(lst[0]);
  300. }
  301. QStandardItem * Peers::find_uuid(QString uuid)
  302. {
  303. if (model.rowCount() == 0)
  304. return NULL;
  305. QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
  306. uuid);
  307. if (lst.size() == 0)
  308. return NULL;
  309. return model.itemFromIndex(lst[0]);
  310. }
  311. void Peers::event_notify(WpaMsg msg)
  312. {
  313. QString text = msg.getMsg();
  314. if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
  315. /*
  316. * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
  317. * 02:2a:c4:18:5b:f3
  318. * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
  319. */
  320. QStringList items = text.split(' ');
  321. QString uuid = items[1];
  322. QString addr = items[2];
  323. QString name = "";
  324. QStandardItem *item = find_addr(addr);
  325. if (item)
  326. return;
  327. int pos = text.indexOf('[');
  328. if (pos >= 0) {
  329. int pos2 = text.lastIndexOf(']');
  330. if (pos2 >= pos) {
  331. items = text.mid(pos + 1, pos2 - pos - 1).
  332. split('|');
  333. name = items[0];
  334. items.append(addr);
  335. }
  336. }
  337. item = new QStandardItem(*laptop_icon, name);
  338. if (item) {
  339. item->setData(addr, peer_role_address);
  340. item->setData(PEER_TYPE_WPS_PIN_NEEDED,
  341. peer_role_type);
  342. item->setToolTip(items.join(QString("\n")));
  343. model.appendRow(item);
  344. }
  345. return;
  346. }
  347. if (text.startsWith(AP_STA_CONNECTED)) {
  348. /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
  349. QStringList items = text.split(' ');
  350. QString addr = items[1];
  351. QStandardItem *item = find_addr(addr);
  352. if (item == NULL || item->data(peer_role_type).toInt() !=
  353. PEER_TYPE_ASSOCIATED_STATION)
  354. add_single_station(addr.toAscii().constData());
  355. return;
  356. }
  357. if (text.startsWith(AP_STA_DISCONNECTED)) {
  358. /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
  359. QStringList items = text.split(' ');
  360. QString addr = items[1];
  361. if (model.rowCount() == 0)
  362. return;
  363. QModelIndexList lst = model.match(model.index(0, 0),
  364. peer_role_address, addr);
  365. for (int i = 0; i < lst.size(); i++) {
  366. QStandardItem *item = model.itemFromIndex(lst[i]);
  367. if (item && item->data(peer_role_type).toInt() ==
  368. PEER_TYPE_ASSOCIATED_STATION)
  369. model.removeRow(lst[i].row());
  370. }
  371. return;
  372. }
  373. if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
  374. /*
  375. * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002|
  376. * Very friendly name|Company|Long description of the model|
  377. * WAP|http://w1.fi/|http://w1.fi/hostapd/
  378. */
  379. int pos = text.indexOf(' ');
  380. if (pos < 0)
  381. return;
  382. QStringList items = text.mid(pos + 1).split('|');
  383. if (items.size() < 2)
  384. return;
  385. QStandardItem *item = find_uuid(items[0]);
  386. if (item)
  387. return;
  388. item = new QStandardItem(*ap_icon, items[1]);
  389. if (item) {
  390. item->setData(items[0], peer_role_uuid);
  391. item->setData(PEER_TYPE_WPS_ER_AP, peer_role_type);
  392. item->setToolTip(items.join(QString("\n")));
  393. model.appendRow(item);
  394. }
  395. return;
  396. }
  397. if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
  398. /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
  399. QStringList items = text.split(' ');
  400. if (items.size() < 2)
  401. return;
  402. if (model.rowCount() == 0)
  403. return;
  404. QModelIndexList lst = model.match(model.index(0, 0),
  405. peer_role_uuid, items[1]);
  406. for (int i = 0; i < lst.size(); i++) {
  407. QStandardItem *item = model.itemFromIndex(lst[i]);
  408. if (item && item->data(peer_role_type).toInt() ==
  409. PEER_TYPE_WPS_ER_AP)
  410. model.removeRow(lst[i].row());
  411. }
  412. return;
  413. }
  414. if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
  415. /*
  416. * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
  417. * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
  418. * pri_dev_type=1-0050F204-1
  419. * |Wireless Client|Company|cmodel|123|12345|
  420. */
  421. QStringList items = text.split(' ');
  422. if (items.size() < 3)
  423. return;
  424. QString uuid = items[1];
  425. QString addr = items[2];
  426. int pos = text.indexOf('|');
  427. if (pos < 0)
  428. return;
  429. items = text.mid(pos + 1).split('|');
  430. if (items.size() < 1)
  431. return;
  432. QString name = items[0];
  433. if (name.length() == 0)
  434. name = addr;
  435. remove_enrollee_uuid(uuid);
  436. QStandardItem *item;
  437. item = new QStandardItem(*laptop_icon, name);
  438. if (item) {
  439. item->setData(uuid, peer_role_uuid);
  440. item->setData(addr, peer_role_address);
  441. item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
  442. peer_role_type);
  443. item->setToolTip(items.join(QString("\n")));
  444. model.appendRow(item);
  445. }
  446. return;
  447. }
  448. if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
  449. /*
  450. * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
  451. * 02:66:a0:ee:17:27
  452. */
  453. QStringList items = text.split(' ');
  454. if (items.size() < 2)
  455. return;
  456. remove_enrollee_uuid(items[1]);
  457. return;
  458. }
  459. }
  460. void Peers::closeEvent(QCloseEvent *)
  461. {
  462. if (wpagui) {
  463. char reply[20];
  464. size_t replylen = sizeof(reply) - 1;
  465. wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
  466. }
  467. }
  468. void Peers::done(int r)
  469. {
  470. QDialog::done(r);
  471. close();
  472. }
  473. void Peers::remove_enrollee_uuid(QString uuid)
  474. {
  475. if (model.rowCount() == 0)
  476. return;
  477. QModelIndexList lst = model.match(model.index(0, 0),
  478. peer_role_uuid, uuid);
  479. for (int i = 0; i < lst.size(); i++) {
  480. QStandardItem *item = model.itemFromIndex(lst[i]);
  481. if (item && item->data(peer_role_type).toInt() ==
  482. PEER_TYPE_WPS_ER_ENROLLEE)
  483. model.removeRow(lst[i].row());
  484. }
  485. }