wpa_gui-qt4: Fix WPS AP detection for peer window
[mech_eap.git] / wpa_supplicant / wpa_gui-qt4 / peers.cpp
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
15 #include <cstdio>
16 #include <QImageReader>
17 #include <QMessageBox>
18
19 #include "wpagui.h"
20 #include "stringquery.h"
21 #include "peers.h"
22
23
24 static const int peer_role_address = Qt::UserRole + 1;
25 static const int peer_role_type = Qt::UserRole + 2;
26
27 /*
28  * TODO:
29  * - add pending WPS queries (from M1/PIN, PBC?)
30  * - add current AP info (e.g., from WPS) in station mode
31  * - different icons to indicate peer type
32  */
33
34 enum peer_type {
35         PEER_TYPE_ASSOCIATED_STATION,
36         PEER_TYPE_AP,
37         PEER_TYPE_AP_WPS,
38 };
39
40
41 Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
42         : QDialog(parent)
43 {
44         setupUi(this);
45
46         if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
47                 default_icon = new QIcon(":/icons/wpa_gui.svg");
48         else
49                 default_icon = new QIcon(":/icons/wpa_gui.png");
50
51         peers->setModel(&model);
52         peers->setResizeMode(QListView::Adjust);
53
54         peers->setContextMenuPolicy(Qt::CustomContextMenu);
55         connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
56                 this, SLOT(context_menu(const QPoint &)));
57
58         wpagui = NULL;
59 }
60
61
62 void Peers::setWpaGui(WpaGui *_wpagui)
63 {
64         wpagui = _wpagui;
65         update_peers();
66 }
67
68
69 Peers::~Peers()
70 {
71         delete default_icon;
72 }
73
74
75 void Peers::languageChange()
76 {
77         retranslateUi(this);
78 }
79
80
81 void Peers::context_menu(const QPoint &pos)
82 {
83         QMenu *menu = new QMenu;
84         if (menu == NULL)
85                 return;
86
87         QModelIndex idx = peers->indexAt(pos);
88         if (idx.isValid()) {
89                 ctx_item = model.itemFromIndex(idx);
90                 int type = ctx_item->data(peer_role_type).toInt();
91                 QString title;
92                 switch (type) {
93                 case PEER_TYPE_ASSOCIATED_STATION:
94                         title = tr("Associated station");
95                         break;
96                 case PEER_TYPE_AP:
97                         title = tr("AP");
98                         break;
99                 case PEER_TYPE_AP_WPS:
100                         title = tr("WPS AP");
101                         break;
102                 }
103                 menu->addAction(title)->setEnabled(false);
104                 menu->addSeparator();
105
106                 if (type == PEER_TYPE_ASSOCIATED_STATION ||
107                     type == PEER_TYPE_AP_WPS) {
108                         /* TODO: only for peers that are requesting WPS PIN
109                          * method */
110                         menu->addAction(QString("Enter WPS PIN"), this,
111                                         SLOT(enter_pin()));
112                 }
113         } else {
114                 ctx_item = NULL;
115                 menu->addAction(QString("Refresh"), this, SLOT(ctx_refresh()));
116         }
117
118         menu->exec(peers->mapToGlobal(pos));
119 }
120
121
122 void Peers::enter_pin()
123 {
124         if (ctx_item == NULL)
125                 return;
126         QString addr = ctx_item->data(peer_role_address).toString();
127         StringQuery input(tr("PIN:"));
128         input.setWindowTitle(tr("PIN for ") + ctx_item->text());
129         if (input.exec() != QDialog::Accepted)
130                 return;
131
132         char cmd[100];
133         char reply[100];
134         size_t reply_len;
135         snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
136                  addr.toAscii().constData(),
137                  input.get_string().toAscii().constData());
138         reply_len = sizeof(reply) - 1;
139         if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
140                 QMessageBox msg;
141                 msg.setIcon(QMessageBox::Warning);
142                 msg.setText("Failed to set the WPS PIN.");
143                 msg.exec();
144         }
145 }
146
147
148 void Peers::ctx_refresh()
149 {
150         update_peers();
151 }
152
153
154 void Peers::add_stations()
155 {
156         char reply[2048];
157         size_t reply_len;
158         char cmd[20];
159         int res;
160
161         reply_len = sizeof(reply) - 1;
162         if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
163                 return;
164
165         do {
166                 reply[reply_len] = '\0';
167                 QString info(reply);
168                 char *txt = reply;
169                 while (*txt != '\0' && *txt != '\n')
170                         txt++;
171                 *txt++ = '\0';
172                 if (strncmp(reply, "FAIL", 4) == 0 ||
173                     strncmp(reply, "UNKNOWN", 7) == 0)
174                         break;
175
176                 QStringList lines = info.split(QRegExp("\\n"));
177                 QString name;
178
179                 for (QStringList::Iterator it = lines.begin();
180                      it != lines.end(); it++) {
181                         int pos = (*it).indexOf('=') + 1;
182                         if (pos < 1)
183                                 continue;
184
185                         if ((*it).startsWith("wpsDeviceName="))
186                                 name = (*it).mid(pos);
187                 }
188
189                 if (name.isEmpty())
190                         name = reply;
191
192                 QStandardItem *item = new QStandardItem(*default_icon, name);
193                 if (item) {
194                         item->setData(QString(reply), peer_role_address);
195                         item->setData(PEER_TYPE_ASSOCIATED_STATION,
196                                       peer_role_type);
197                         item->setToolTip(info);
198                         model.appendRow(item);
199                 }
200
201                 reply_len = sizeof(reply) - 1;
202                 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
203                 res = wpagui->ctrlRequest(cmd, reply, &reply_len);
204         } while (res >= 0);
205 }
206
207
208 void Peers::add_scan_results()
209 {
210         char reply[2048];
211         size_t reply_len;
212         int index;
213         char cmd[20];
214
215         index = 0;
216         while (wpagui) {
217                 snprintf(cmd, sizeof(cmd), "BSS %d", index++);
218                 if (index > 1000)
219                         break;
220
221                 reply_len = sizeof(reply) - 1;
222                 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
223                         break;
224                 reply[reply_len] = '\0';
225
226                 QString bss(reply);
227                 if (bss.isEmpty() || bss.startsWith("FAIL"))
228                         break;
229
230                 QString ssid, bssid, flags, wps_name;
231
232                 QStringList lines = bss.split(QRegExp("\\n"));
233                 for (QStringList::Iterator it = lines.begin();
234                      it != lines.end(); it++) {
235                         int pos = (*it).indexOf('=') + 1;
236                         if (pos < 1)
237                                 continue;
238
239                         if ((*it).startsWith("bssid="))
240                                 bssid = (*it).mid(pos);
241                         else if ((*it).startsWith("flags="))
242                                 flags = (*it).mid(pos);
243                         else if ((*it).startsWith("ssid="))
244                                 ssid = (*it).mid(pos);
245                         else if ((*it).startsWith("wps_device_name="))
246                                 wps_name = (*it).mid(pos);
247                 }
248
249                 QString name = wps_name;
250                 if (name.isEmpty())
251                         name = ssid + "\n" + bssid;
252
253                 QStandardItem *item = new QStandardItem(*default_icon, name);
254                 if (item) {
255                         item->setData(QString(reply), peer_role_address);
256                         if (flags.contains("[WPS"))
257                                 item->setData(PEER_TYPE_AP_WPS,
258                                               peer_role_type);
259                         else
260                                 item->setData(PEER_TYPE_AP, peer_role_type);
261
262                         for (int i = 0; i < lines.size(); i++) {
263                                 if (lines[i].length() > 60) {
264                                         lines[i].remove(
265                                                 60, lines[i].length());
266                                         lines[i] += "..";
267                                 }
268                         }
269                         item->setToolTip(lines.join("\n"));
270                         model.appendRow(item);
271                 }
272         }
273 }
274
275
276 void Peers::update_peers()
277 {
278         model.clear();
279         if (wpagui == NULL)
280                 return;
281
282         add_stations();
283         add_scan_results();
284 }