peers.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  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. /*
  24. * TODO:
  25. * - add current AP info (e.g., from WPS) in station mode
  26. * - different icons to indicate peer type
  27. */
  28. enum peer_type {
  29. PEER_TYPE_ASSOCIATED_STATION,
  30. PEER_TYPE_AP,
  31. PEER_TYPE_AP_WPS,
  32. PEER_TYPE_WPS_PIN_NEEDED,
  33. };
  34. Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
  35. : QDialog(parent)
  36. {
  37. setupUi(this);
  38. if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
  39. default_icon = new QIcon(":/icons/wpa_gui.svg");
  40. else
  41. default_icon = new QIcon(":/icons/wpa_gui.png");
  42. peers->setModel(&model);
  43. peers->setResizeMode(QListView::Adjust);
  44. peers->setContextMenuPolicy(Qt::CustomContextMenu);
  45. connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
  46. this, SLOT(context_menu(const QPoint &)));
  47. wpagui = NULL;
  48. }
  49. void Peers::setWpaGui(WpaGui *_wpagui)
  50. {
  51. wpagui = _wpagui;
  52. update_peers();
  53. }
  54. Peers::~Peers()
  55. {
  56. delete default_icon;
  57. }
  58. void Peers::languageChange()
  59. {
  60. retranslateUi(this);
  61. }
  62. void Peers::context_menu(const QPoint &pos)
  63. {
  64. QMenu *menu = new QMenu;
  65. if (menu == NULL)
  66. return;
  67. QModelIndex idx = peers->indexAt(pos);
  68. if (idx.isValid()) {
  69. ctx_item = model.itemFromIndex(idx);
  70. int type = ctx_item->data(peer_role_type).toInt();
  71. QString title;
  72. switch (type) {
  73. case PEER_TYPE_ASSOCIATED_STATION:
  74. title = tr("Associated station");
  75. break;
  76. case PEER_TYPE_AP:
  77. title = tr("AP");
  78. break;
  79. case PEER_TYPE_AP_WPS:
  80. title = tr("WPS AP");
  81. break;
  82. case PEER_TYPE_WPS_PIN_NEEDED:
  83. title = tr("WPS PIN needed");
  84. break;
  85. }
  86. menu->addAction(title)->setEnabled(false);
  87. menu->addSeparator();
  88. if (type == PEER_TYPE_ASSOCIATED_STATION ||
  89. type == PEER_TYPE_AP_WPS ||
  90. type == PEER_TYPE_WPS_PIN_NEEDED) {
  91. /* TODO: only for peers that are requesting WPS PIN
  92. * method */
  93. menu->addAction(QString("Enter WPS PIN"), this,
  94. SLOT(enter_pin()));
  95. }
  96. } else {
  97. ctx_item = NULL;
  98. menu->addAction(QString("Refresh"), this, SLOT(ctx_refresh()));
  99. }
  100. menu->exec(peers->mapToGlobal(pos));
  101. }
  102. void Peers::enter_pin()
  103. {
  104. if (ctx_item == NULL)
  105. return;
  106. QString addr = ctx_item->data(peer_role_address).toString();
  107. StringQuery input(tr("PIN:"));
  108. input.setWindowTitle(tr("PIN for ") + ctx_item->text());
  109. if (input.exec() != QDialog::Accepted)
  110. return;
  111. char cmd[100];
  112. char reply[100];
  113. size_t reply_len;
  114. snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
  115. addr.toAscii().constData(),
  116. input.get_string().toAscii().constData());
  117. reply_len = sizeof(reply) - 1;
  118. if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
  119. QMessageBox msg;
  120. msg.setIcon(QMessageBox::Warning);
  121. msg.setText("Failed to set the WPS PIN.");
  122. msg.exec();
  123. }
  124. }
  125. void Peers::ctx_refresh()
  126. {
  127. update_peers();
  128. }
  129. void Peers::add_station(QString info)
  130. {
  131. QStringList lines = info.split(QRegExp("\\n"));
  132. QString name;
  133. for (QStringList::Iterator it = lines.begin();
  134. it != lines.end(); it++) {
  135. int pos = (*it).indexOf('=') + 1;
  136. if (pos < 1)
  137. continue;
  138. if ((*it).startsWith("wpsDeviceName="))
  139. name = (*it).mid(pos);
  140. }
  141. if (name.isEmpty())
  142. name = lines[0];
  143. QStandardItem *item = new QStandardItem(*default_icon, name);
  144. if (item) {
  145. item->setData(lines[0], peer_role_address);
  146. item->setData(PEER_TYPE_ASSOCIATED_STATION,
  147. peer_role_type);
  148. item->setToolTip(info);
  149. model.appendRow(item);
  150. }
  151. }
  152. void Peers::add_stations()
  153. {
  154. char reply[2048];
  155. size_t reply_len;
  156. char cmd[30];
  157. int res;
  158. reply_len = sizeof(reply) - 1;
  159. if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
  160. return;
  161. do {
  162. reply[reply_len] = '\0';
  163. QString info(reply);
  164. char *txt = reply;
  165. while (*txt != '\0' && *txt != '\n')
  166. txt++;
  167. *txt++ = '\0';
  168. if (strncmp(reply, "FAIL", 4) == 0 ||
  169. strncmp(reply, "UNKNOWN", 7) == 0)
  170. break;
  171. add_station(info);
  172. reply_len = sizeof(reply) - 1;
  173. snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
  174. res = wpagui->ctrlRequest(cmd, reply, &reply_len);
  175. } while (res >= 0);
  176. }
  177. void Peers::add_single_station(const char *addr)
  178. {
  179. char reply[2048];
  180. size_t reply_len;
  181. char cmd[30];
  182. reply_len = sizeof(reply) - 1;
  183. snprintf(cmd, sizeof(cmd), "STA %s", addr);
  184. if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
  185. return;
  186. reply[reply_len] = '\0';
  187. QString info(reply);
  188. char *txt = reply;
  189. while (*txt != '\0' && *txt != '\n')
  190. txt++;
  191. *txt++ = '\0';
  192. if (strncmp(reply, "FAIL", 4) == 0 ||
  193. strncmp(reply, "UNKNOWN", 7) == 0)
  194. return;
  195. add_station(info);
  196. }
  197. void Peers::add_scan_results()
  198. {
  199. char reply[2048];
  200. size_t reply_len;
  201. int index;
  202. char cmd[20];
  203. index = 0;
  204. while (wpagui) {
  205. snprintf(cmd, sizeof(cmd), "BSS %d", index++);
  206. if (index > 1000)
  207. break;
  208. reply_len = sizeof(reply) - 1;
  209. if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
  210. break;
  211. reply[reply_len] = '\0';
  212. QString bss(reply);
  213. if (bss.isEmpty() || bss.startsWith("FAIL"))
  214. break;
  215. QString ssid, bssid, flags, wps_name;
  216. QStringList lines = bss.split(QRegExp("\\n"));
  217. for (QStringList::Iterator it = lines.begin();
  218. it != lines.end(); it++) {
  219. int pos = (*it).indexOf('=') + 1;
  220. if (pos < 1)
  221. continue;
  222. if ((*it).startsWith("bssid="))
  223. bssid = (*it).mid(pos);
  224. else if ((*it).startsWith("flags="))
  225. flags = (*it).mid(pos);
  226. else if ((*it).startsWith("ssid="))
  227. ssid = (*it).mid(pos);
  228. else if ((*it).startsWith("wps_device_name="))
  229. wps_name = (*it).mid(pos);
  230. }
  231. QString name = wps_name;
  232. if (name.isEmpty())
  233. name = ssid + "\n" + bssid;
  234. QStandardItem *item = new QStandardItem(*default_icon, name);
  235. if (item) {
  236. item->setData(bssid, peer_role_address);
  237. if (flags.contains("[WPS"))
  238. item->setData(PEER_TYPE_AP_WPS,
  239. peer_role_type);
  240. else
  241. item->setData(PEER_TYPE_AP, peer_role_type);
  242. for (int i = 0; i < lines.size(); i++) {
  243. if (lines[i].length() > 60) {
  244. lines[i].remove(
  245. 60, lines[i].length());
  246. lines[i] += "..";
  247. }
  248. }
  249. item->setToolTip(lines.join("\n"));
  250. model.appendRow(item);
  251. }
  252. }
  253. }
  254. void Peers::update_peers()
  255. {
  256. model.clear();
  257. if (wpagui == NULL)
  258. return;
  259. add_stations();
  260. add_scan_results();
  261. }
  262. QStandardItem * Peers::find_addr(QString addr)
  263. {
  264. if (model.rowCount() == 0)
  265. return NULL;
  266. QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
  267. addr);
  268. if (lst.size() == 0)
  269. return NULL;
  270. return model.itemFromIndex(lst[0]);
  271. }
  272. void Peers::event_notify(WpaMsg msg)
  273. {
  274. QString text = msg.getMsg();
  275. if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
  276. /*
  277. * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
  278. * 02:2a:c4:18:5b:f3
  279. * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
  280. */
  281. QStringList items = text.split(' ');
  282. QString uuid = items[1];
  283. QString addr = items[2];
  284. QString name = "";
  285. QStandardItem *item = find_addr(addr);
  286. if (item)
  287. return;
  288. int pos = text.indexOf('[');
  289. if (pos >= 0) {
  290. int pos2 = text.lastIndexOf(']');
  291. if (pos2 >= pos) {
  292. items = text.mid(pos + 1, pos2 - pos - 1).
  293. split('|');
  294. name = items[0];
  295. items.append(addr);
  296. }
  297. }
  298. item = new QStandardItem(*default_icon, name);
  299. if (item) {
  300. item->setData(addr, peer_role_address);
  301. item->setData(PEER_TYPE_WPS_PIN_NEEDED,
  302. peer_role_type);
  303. item->setToolTip(items.join(QString("\n")));
  304. model.appendRow(item);
  305. }
  306. return;
  307. }
  308. if (text.startsWith(AP_STA_CONNECTED)) {
  309. /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
  310. QStringList items = text.split(' ');
  311. QString addr = items[1];
  312. QStandardItem *item = find_addr(addr);
  313. if (item == NULL || item->data(peer_role_type).toInt() !=
  314. PEER_TYPE_ASSOCIATED_STATION)
  315. add_single_station(addr.toAscii().constData());
  316. return;
  317. }
  318. if (text.startsWith(AP_STA_DISCONNECTED)) {
  319. /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
  320. QStringList items = text.split(' ');
  321. QString addr = items[1];
  322. if (model.rowCount() == 0)
  323. return;
  324. QModelIndexList lst = model.match(model.index(0, 0),
  325. peer_role_address, addr);
  326. for (int i = 0; i < lst.size(); i++) {
  327. QStandardItem *item = model.itemFromIndex(lst[i]);
  328. if (item && item->data(peer_role_type).toInt() ==
  329. PEER_TYPE_ASSOCIATED_STATION)
  330. model.removeRow(lst[i].row());
  331. }
  332. return;
  333. }
  334. }