WPS: Add Enrollee-seen event message and wpa_gui-qt4 Peers entry
[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 "common/wpa_ctrl.h"
20 #include "wpagui.h"
21 #include "stringquery.h"
22 #include "peers.h"
23
24
25 enum {
26         peer_role_address = Qt::UserRole + 1,
27         peer_role_type,
28         peer_role_uuid,
29         peer_role_details,
30         peer_role_pri_dev_type,
31         peer_role_ssid,
32         peer_role_config_methods,
33         peer_role_dev_passwd_id
34 };
35
36 /*
37  * TODO:
38  * - add current AP info (e.g., from WPS) in station mode
39  */
40
41 enum peer_type {
42         PEER_TYPE_ASSOCIATED_STATION,
43         PEER_TYPE_AP,
44         PEER_TYPE_AP_WPS,
45         PEER_TYPE_WPS_PIN_NEEDED,
46         PEER_TYPE_WPS_ER_AP,
47         PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
48         PEER_TYPE_WPS_ER_ENROLLEE,
49         PEER_TYPE_WPS_ENROLLEE
50 };
51
52
53 Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
54         : QDialog(parent)
55 {
56         setupUi(this);
57
58         if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
59         {
60                 default_icon = new QIcon(":/icons/wpa_gui.svg");
61                 ap_icon = new QIcon(":/icons/ap.svg");
62                 laptop_icon = new QIcon(":/icons/laptop.svg");
63         } else {
64                 default_icon = new QIcon(":/icons/wpa_gui.png");
65                 ap_icon = new QIcon(":/icons/ap.png");
66                 laptop_icon = new QIcon(":/icons/laptop.png");
67         }
68
69         peers->setModel(&model);
70         peers->setResizeMode(QListView::Adjust);
71
72         peers->setContextMenuPolicy(Qt::CustomContextMenu);
73         connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
74                 this, SLOT(context_menu(const QPoint &)));
75
76         wpagui = NULL;
77 }
78
79
80 void Peers::setWpaGui(WpaGui *_wpagui)
81 {
82         wpagui = _wpagui;
83         update_peers();
84 }
85
86
87 Peers::~Peers()
88 {
89         delete default_icon;
90         delete ap_icon;
91         delete laptop_icon;
92 }
93
94
95 void Peers::languageChange()
96 {
97         retranslateUi(this);
98 }
99
100
101 QString Peers::ItemType(int type)
102 {
103         QString title;
104         switch (type) {
105         case PEER_TYPE_ASSOCIATED_STATION:
106                 title = tr("Associated station");
107                 break;
108         case PEER_TYPE_AP:
109                 title = tr("AP");
110                 break;
111         case PEER_TYPE_AP_WPS:
112                 title = tr("WPS AP");
113                 break;
114         case PEER_TYPE_WPS_PIN_NEEDED:
115                 title = tr("WPS PIN needed");
116                 break;
117         case PEER_TYPE_WPS_ER_AP:
118                 title = tr("ER: WPS AP");
119                 break;
120         case PEER_TYPE_WPS_ER_AP_UNCONFIGURED:
121                 title = tr("ER: WPS AP (Unconfigured)");
122                 break;
123         case PEER_TYPE_WPS_ER_ENROLLEE:
124                 title = tr("ER: WPS Enrollee");
125                 break;
126         case PEER_TYPE_WPS_ENROLLEE:
127                 title = tr("WPS Enrollee");
128                 break;
129         }
130         return title;
131 }
132
133
134 void Peers::context_menu(const QPoint &pos)
135 {
136         QMenu *menu = new QMenu;
137         if (menu == NULL)
138                 return;
139
140         QModelIndex idx = peers->indexAt(pos);
141         if (idx.isValid()) {
142                 ctx_item = model.itemFromIndex(idx);
143                 int type = ctx_item->data(peer_role_type).toInt();
144                 menu->addAction(Peers::ItemType(type))->setEnabled(false);
145                 menu->addSeparator();
146
147                 int config_methods = -1;
148                 QVariant var = ctx_item->data(peer_role_config_methods);
149                 if (var.isValid())
150                         config_methods = var.toInt();
151
152                 if ((type == PEER_TYPE_ASSOCIATED_STATION ||
153                      type == PEER_TYPE_AP_WPS ||
154                      type == PEER_TYPE_WPS_PIN_NEEDED ||
155                      type == PEER_TYPE_WPS_ER_ENROLLEE ||
156                      type == PEER_TYPE_WPS_ENROLLEE) &&
157                     (config_methods == -1 || (config_methods & 0x010c))) {
158                         menu->addAction(tr("Enter WPS PIN"), this,
159                                         SLOT(enter_pin()));
160                 }
161
162                 if (type == PEER_TYPE_AP_WPS) {
163                         menu->addAction(tr("Connect (PBC)"), this,
164                                         SLOT(connect_pbc()));
165                 }
166
167                 if ((type == PEER_TYPE_ASSOCIATED_STATION ||
168                      type == PEER_TYPE_WPS_ER_ENROLLEE ||
169                      type == PEER_TYPE_WPS_ENROLLEE) &&
170                     config_methods >= 0 && (config_methods & 0x0080)) {
171                         menu->addAction(tr("Enroll (PBC)"), this,
172                                         SLOT(connect_pbc()));
173                 }
174
175                 if (type == PEER_TYPE_WPS_ER_AP) {
176                         menu->addAction(tr("Learn Configuration"), this,
177                                         SLOT(learn_ap_config()));
178                 }
179
180                 menu->addAction(tr("Properties"), this, SLOT(properties()));
181         } else {
182                 ctx_item = NULL;
183                 menu->addAction(QString("Refresh"), this, SLOT(ctx_refresh()));
184         }
185
186         menu->exec(peers->mapToGlobal(pos));
187 }
188
189
190 void Peers::enter_pin()
191 {
192         if (ctx_item == NULL)
193                 return;
194
195         int peer_type = ctx_item->data(peer_role_type).toInt();
196         QString uuid;
197         QString addr;
198         if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
199                 uuid = ctx_item->data(peer_role_uuid).toString();
200         else
201                 addr = ctx_item->data(peer_role_address).toString();
202
203         StringQuery input(tr("PIN:"));
204         input.setWindowTitle(tr("PIN for ") + ctx_item->text());
205         if (input.exec() != QDialog::Accepted)
206                 return;
207
208         char cmd[100];
209         char reply[100];
210         size_t reply_len;
211
212         if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
213                 snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
214                          uuid.toAscii().constData(),
215                          input.get_string().toAscii().constData());
216         } else {
217                 snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
218                          addr.toAscii().constData(),
219                          input.get_string().toAscii().constData());
220         }
221         reply_len = sizeof(reply) - 1;
222         if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
223                 QMessageBox msg;
224                 msg.setIcon(QMessageBox::Warning);
225                 msg.setText("Failed to set the WPS PIN.");
226                 msg.exec();
227         }
228 }
229
230
231 void Peers::ctx_refresh()
232 {
233         update_peers();
234 }
235
236
237 void Peers::add_station(QString info)
238 {
239         QStringList lines = info.split(QRegExp("\\n"));
240         QString name;
241
242         for (QStringList::Iterator it = lines.begin();
243              it != lines.end(); it++) {
244                 int pos = (*it).indexOf('=') + 1;
245                 if (pos < 1)
246                         continue;
247
248                 if ((*it).startsWith("wpsDeviceName="))
249                         name = (*it).mid(pos);
250         }
251
252         if (name.isEmpty())
253                 name = lines[0];
254
255         QStandardItem *item = new QStandardItem(*laptop_icon, name);
256         if (item) {
257                 item->setData(lines[0], peer_role_address);
258                 item->setData(PEER_TYPE_ASSOCIATED_STATION,
259                               peer_role_type);
260                 item->setData(info, peer_role_details);
261                 item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
262                 model.appendRow(item);
263         }
264 }
265
266
267 void Peers::add_stations()
268 {
269         char reply[2048];
270         size_t reply_len;
271         char cmd[30];
272         int res;
273
274         reply_len = sizeof(reply) - 1;
275         if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
276                 return;
277
278         do {
279                 reply[reply_len] = '\0';
280                 QString info(reply);
281                 char *txt = reply;
282                 while (*txt != '\0' && *txt != '\n')
283                         txt++;
284                 *txt++ = '\0';
285                 if (strncmp(reply, "FAIL", 4) == 0 ||
286                     strncmp(reply, "UNKNOWN", 7) == 0)
287                         break;
288
289                 add_station(info);
290
291                 reply_len = sizeof(reply) - 1;
292                 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
293                 res = wpagui->ctrlRequest(cmd, reply, &reply_len);
294         } while (res >= 0);
295 }
296
297
298 void Peers::add_single_station(const char *addr)
299 {
300         char reply[2048];
301         size_t reply_len;
302         char cmd[30];
303
304         reply_len = sizeof(reply) - 1;
305         snprintf(cmd, sizeof(cmd), "STA %s", addr);
306         if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
307                 return;
308
309         reply[reply_len] = '\0';
310         QString info(reply);
311         char *txt = reply;
312         while (*txt != '\0' && *txt != '\n')
313                 txt++;
314         *txt++ = '\0';
315         if (strncmp(reply, "FAIL", 4) == 0 ||
316             strncmp(reply, "UNKNOWN", 7) == 0)
317                 return;
318
319         add_station(info);
320 }
321
322
323 void Peers::add_scan_results()
324 {
325         char reply[2048];
326         size_t reply_len;
327         int index;
328         char cmd[20];
329
330         index = 0;
331         while (wpagui) {
332                 snprintf(cmd, sizeof(cmd), "BSS %d", index++);
333                 if (index > 1000)
334                         break;
335
336                 reply_len = sizeof(reply) - 1;
337                 if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
338                         break;
339                 reply[reply_len] = '\0';
340
341                 QString bss(reply);
342                 if (bss.isEmpty() || bss.startsWith("FAIL"))
343                         break;
344
345                 QString ssid, bssid, flags, wps_name, pri_dev_type;
346
347                 QStringList lines = bss.split(QRegExp("\\n"));
348                 for (QStringList::Iterator it = lines.begin();
349                      it != lines.end(); it++) {
350                         int pos = (*it).indexOf('=') + 1;
351                         if (pos < 1)
352                                 continue;
353
354                         if ((*it).startsWith("bssid="))
355                                 bssid = (*it).mid(pos);
356                         else if ((*it).startsWith("flags="))
357                                 flags = (*it).mid(pos);
358                         else if ((*it).startsWith("ssid="))
359                                 ssid = (*it).mid(pos);
360                         else if ((*it).startsWith("wps_device_name="))
361                                 wps_name = (*it).mid(pos);
362                         else if ((*it).startsWith("wps_primary_device_type="))
363                                 pri_dev_type = (*it).mid(pos);
364                 }
365
366                 QString name = wps_name;
367                 if (name.isEmpty())
368                         name = ssid + "\n" + bssid;
369
370                 QStandardItem *item = new QStandardItem(*ap_icon, name);
371                 if (item) {
372                         item->setData(bssid, peer_role_address);
373                         int type;
374                         if (flags.contains("[WPS"))
375                                 type = PEER_TYPE_AP_WPS;
376                         else
377                                 type = PEER_TYPE_AP;
378                         item->setData(type, peer_role_type);
379
380                         for (int i = 0; i < lines.size(); i++) {
381                                 if (lines[i].length() > 60) {
382                                         lines[i].remove(
383                                                 60, lines[i].length());
384                                         lines[i] += "..";
385                                 }
386                         }
387                         item->setToolTip(ItemType(type));
388                         item->setData(lines.join("\n"), peer_role_details);
389                         if (!pri_dev_type.isEmpty())
390                                 item->setData(pri_dev_type,
391                                               peer_role_pri_dev_type);
392                         if (!ssid.isEmpty())
393                                 item->setData(ssid, peer_role_ssid);
394                         model.appendRow(item);
395                 }
396         }
397 }
398
399
400 void Peers::update_peers()
401 {
402         model.clear();
403         if (wpagui == NULL)
404                 return;
405
406         char reply[20];
407         size_t replylen = sizeof(reply) - 1;
408         wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
409
410         add_stations();
411         add_scan_results();
412 }
413
414
415 QStandardItem * Peers::find_addr(QString addr)
416 {
417         if (model.rowCount() == 0)
418                 return NULL;
419
420         QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
421                                           addr);
422         if (lst.size() == 0)
423                 return NULL;
424         return model.itemFromIndex(lst[0]);
425 }
426
427
428 QStandardItem * Peers::find_uuid(QString uuid)
429 {
430         if (model.rowCount() == 0)
431                 return NULL;
432
433         QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
434                                           uuid);
435         if (lst.size() == 0)
436                 return NULL;
437         return model.itemFromIndex(lst[0]);
438 }
439
440
441 void Peers::event_notify(WpaMsg msg)
442 {
443         QString text = msg.getMsg();
444
445         if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
446                 /*
447                  * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
448                  * 02:2a:c4:18:5b:f3
449                  * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
450                  */
451                 QStringList items = text.split(' ');
452                 QString uuid = items[1];
453                 QString addr = items[2];
454                 QString name = "";
455
456                 QStandardItem *item = find_addr(addr);
457                 if (item)
458                         return;
459
460                 int pos = text.indexOf('[');
461                 if (pos >= 0) {
462                         int pos2 = text.lastIndexOf(']');
463                         if (pos2 >= pos) {
464                                 items = text.mid(pos + 1, pos2 - pos - 1).
465                                         split('|');
466                                 name = items[0];
467                                 items.append(addr);
468                         }
469                 }
470
471                 item = new QStandardItem(*laptop_icon, name);
472                 if (item) {
473                         item->setData(addr, peer_role_address);
474                         item->setData(PEER_TYPE_WPS_PIN_NEEDED,
475                                       peer_role_type);
476                         item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
477                         item->setData(items.join("\n"), peer_role_details);
478                         item->setData(items[5], peer_role_pri_dev_type);
479                         model.appendRow(item);
480                 }
481                 return;
482         }
483
484         if (text.startsWith(AP_STA_CONNECTED)) {
485                 /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
486                 QStringList items = text.split(' ');
487                 QString addr = items[1];
488                 QStandardItem *item = find_addr(addr);
489                 if (item == NULL || item->data(peer_role_type).toInt() !=
490                     PEER_TYPE_ASSOCIATED_STATION)
491                         add_single_station(addr.toAscii().constData());
492                 return;
493         }
494
495         if (text.startsWith(AP_STA_DISCONNECTED)) {
496                 /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
497                 QStringList items = text.split(' ');
498                 QString addr = items[1];
499
500                 if (model.rowCount() == 0)
501                         return;
502
503                 QModelIndexList lst = model.match(model.index(0, 0),
504                                                   peer_role_address, addr);
505                 for (int i = 0; i < lst.size(); i++) {
506                         QStandardItem *item = model.itemFromIndex(lst[i]);
507                         if (item && item->data(peer_role_type).toInt() ==
508                             PEER_TYPE_ASSOCIATED_STATION)
509                                 model.removeRow(lst[i].row());
510                 }
511                 return;
512         }
513
514         if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
515                 /*
516                  * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
517                  * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
518                  * |Very friendly name|Company|Long description of the model|
519                  * WAP|http://w1.fi/|http://w1.fi/hostapd/
520                  */
521                 QStringList items = text.split(' ');
522                 if (items.size() < 5)
523                         return;
524                 QString uuid = items[1];
525                 QString addr = items[2];
526                 QString pri_dev_type = items[3].mid(13);
527                 int wps_state = items[4].mid(10).toInt();
528
529                 int pos = text.indexOf('|');
530                 if (pos < 0)
531                         return;
532                 items = text.mid(pos + 1).split('|');
533                 if (items.size() < 1)
534                         return;
535
536                 QStandardItem *item = find_uuid(uuid);
537                 if (item)
538                         return;
539
540                 item = new QStandardItem(*ap_icon, items[0]);
541                 if (item) {
542                         item->setData(uuid, peer_role_uuid);
543                         item->setData(addr, peer_role_address);
544                         int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
545                                 PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
546                         item->setData(type, peer_role_type);
547                         item->setToolTip(ItemType(type));
548                         item->setData(pri_dev_type, peer_role_pri_dev_type);
549                         item->setData(items.join(QString("\n")),
550                                       peer_role_details);
551                         model.appendRow(item);
552                 }
553
554                 return;
555         }
556
557         if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
558                 /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
559                 QStringList items = text.split(' ');
560                 if (items.size() < 2)
561                         return;
562                 if (model.rowCount() == 0)
563                         return;
564
565                 QModelIndexList lst = model.match(model.index(0, 0),
566                                                   peer_role_uuid, items[1]);
567                 for (int i = 0; i < lst.size(); i++) {
568                         QStandardItem *item = model.itemFromIndex(lst[i]);
569                         if (item &&
570                             (item->data(peer_role_type).toInt() ==
571                              PEER_TYPE_WPS_ER_AP ||
572                              item->data(peer_role_type).toInt() ==
573                              PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
574                                 model.removeRow(lst[i].row());
575                 }
576                 return;
577         }
578
579         if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
580                 /*
581                  * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
582                  * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
583                  * pri_dev_type=1-0050F204-1
584                  * |Wireless Client|Company|cmodel|123|12345|
585                  */
586                 QStringList items = text.split(' ');
587                 if (items.size() < 3)
588                         return;
589                 QString uuid = items[1];
590                 QString addr = items[2];
591                 QString pri_dev_type = items[6].mid(13);
592                 int config_methods = -1;
593                 int dev_passwd_id = -1;
594
595                 for (int i = 3; i < items.size(); i++) {
596                         int pos = items[i].indexOf('=') + 1;
597                         if (pos < 1)
598                                 continue;
599                         QString val = items[i].mid(pos);
600                         if (items[i].startsWith("config_methods=")) {
601                                 config_methods = val.toInt(0, 0);
602                         } else if (items[i].startsWith("dev_passwd_id=")) {
603                                 dev_passwd_id = val.toInt();
604                         }
605                 }
606
607                 int pos = text.indexOf('|');
608                 if (pos < 0)
609                         return;
610                 items = text.mid(pos + 1).split('|');
611                 if (items.size() < 1)
612                         return;
613                 QString name = items[0];
614                 if (name.length() == 0)
615                         name = addr;
616
617                 remove_enrollee_uuid(uuid);
618
619                 QStandardItem *item;
620                 item = new QStandardItem(*laptop_icon, name);
621                 if (item) {
622                         item->setData(uuid, peer_role_uuid);
623                         item->setData(addr, peer_role_address);
624                         item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
625                                       peer_role_type);
626                         item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
627                         item->setData(items.join(QString("\n")),
628                                       peer_role_details);
629                         item->setData(pri_dev_type, peer_role_pri_dev_type);
630                         if (config_methods >= 0)
631                                 item->setData(config_methods,
632                                               peer_role_config_methods);
633                         if (dev_passwd_id >= 0)
634                                 item->setData(dev_passwd_id,
635                                               peer_role_dev_passwd_id);
636                         model.appendRow(item);
637                 }
638
639                 return;
640         }
641
642         if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
643                 /*
644                  * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
645                  * 02:66:a0:ee:17:27
646                  */
647                 QStringList items = text.split(' ');
648                 if (items.size() < 2)
649                         return;
650                 remove_enrollee_uuid(items[1]);
651                 return;
652         }
653
654         if (text.startsWith(WPS_EVENT_ENROLLEE_SEEN)) {
655                 /* TODO: need to time out this somehow or remove on successful
656                  * WPS run, etc. */
657                 /*
658                  * WPS-ENROLLEE-SEEN 02:00:00:00:01:00
659                  * 572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1
660                  * [Wireless Client]
661                  * (MAC addr, UUID-E, pri dev type, config methods,
662                  * dev passwd id, request type, [dev name])
663                  */
664                 QStringList items = text.split(' ');
665                 if (items.size() < 7)
666                         return;
667                 QString addr = items[1];
668                 QString uuid = items[2];
669                 QString pri_dev_type = items[3];
670                 int config_methods = items[4].toInt(0, 0);
671                 int dev_passwd_id = items[5].toInt();
672                 QString name;
673
674                 int pos = text.indexOf('[');
675                 if (pos >= 0) {
676                         int pos2 = text.lastIndexOf(']');
677                         if (pos2 >= pos) {
678                                 QStringList items2 =
679                                         text.mid(pos + 1, pos2 - pos - 1).
680                                         split('|');
681                                 name = items2[0];
682                         }
683                 }
684                 if (name.isEmpty())
685                         name = addr;
686
687                 QStandardItem *item;
688
689                 item = find_uuid(uuid);
690                 if (item) {
691                         QVariant var = item->data(peer_role_config_methods);
692                         QVariant var2 = item->data(peer_role_dev_passwd_id);
693                         if ((var.isValid() && config_methods != var.toInt()) ||
694                             (var2.isValid() && dev_passwd_id != var2.toInt()))
695                                 remove_enrollee_uuid(uuid);
696                         else
697                                 return;
698                 }
699
700                 item = new QStandardItem(*laptop_icon, name);
701                 if (item) {
702                         item->setData(uuid, peer_role_uuid);
703                         item->setData(addr, peer_role_address);
704                         item->setData(PEER_TYPE_WPS_ENROLLEE,
705                                       peer_role_type);
706                         item->setToolTip(ItemType(PEER_TYPE_WPS_ENROLLEE));
707                         item->setData(items.join(QString("\n")),
708                                       peer_role_details);
709                         item->setData(pri_dev_type, peer_role_pri_dev_type);
710                         item->setData(config_methods,
711                                       peer_role_config_methods);
712                         item->setData(dev_passwd_id, peer_role_dev_passwd_id);
713                         model.appendRow(item);
714                 }
715
716                 return;
717         }
718 }
719
720
721 void Peers::closeEvent(QCloseEvent *)
722 {
723         if (wpagui) {
724                 char reply[20];
725                 size_t replylen = sizeof(reply) - 1;
726                 wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
727         }
728 }
729
730
731 void Peers::done(int r)
732 {
733         QDialog::done(r);
734         close();
735 }
736
737
738 void Peers::remove_enrollee_uuid(QString uuid)
739 {
740         if (model.rowCount() == 0)
741                 return;
742
743         QModelIndexList lst = model.match(model.index(0, 0),
744                                           peer_role_uuid, uuid);
745         for (int i = 0; i < lst.size(); i++) {
746                 QStandardItem *item = model.itemFromIndex(lst[i]);
747                 if (item == NULL)
748                         continue;
749                 int type = item->data(peer_role_type).toInt();
750                 if (type == PEER_TYPE_WPS_ER_ENROLLEE ||
751                     type == PEER_TYPE_WPS_ENROLLEE)
752                         model.removeRow(lst[i].row());
753         }
754 }
755
756
757 void Peers::properties()
758 {
759         if (ctx_item == NULL)
760                 return;
761
762         QMessageBox msg(this);
763         msg.setStandardButtons(QMessageBox::Ok);
764         msg.setDefaultButton(QMessageBox::Ok);
765         msg.setEscapeButton(QMessageBox::Ok);
766         msg.setWindowTitle(tr("Peer Properties"));
767
768         int type = ctx_item->data(peer_role_type).toInt();
769         QString title = Peers::ItemType(type);
770
771         msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
772
773         QVariant var;
774         QString info;
775
776         var = ctx_item->data(peer_role_address);
777         if (var.isValid())
778                 info += tr("Address: ") + var.toString() + QString("\n");
779
780         var = ctx_item->data(peer_role_uuid);
781         if (var.isValid())
782                 info += tr("UUID: ") + var.toString() + QString("\n");
783
784         var = ctx_item->data(peer_role_pri_dev_type);
785         if (var.isValid())
786                 info += tr("Primary Device Type: ") + var.toString() +
787                         QString("\n");
788
789         var = ctx_item->data(peer_role_ssid);
790         if (var.isValid())
791                 info += tr("SSID: ") + var.toString() + QString("\n");
792
793         var = ctx_item->data(peer_role_config_methods);
794         if (var.isValid()) {
795                 int methods = var.toInt();
796                 info += tr("Configuration Methods: ");
797                 if (methods & 0x0001)
798                         info += tr("[USBA]");
799                 if (methods & 0x0002)
800                         info += tr("[Ethernet]");
801                 if (methods & 0x0004)
802                         info += tr("[Label]");
803                 if (methods & 0x0008)
804                         info += tr("[Display]");
805                 if (methods & 0x0010)
806                         info += tr("[Ext. NFC Token]");
807                 if (methods & 0x0020)
808                         info += tr("[Int. NFC Token]");
809                 if (methods & 0x0040)
810                         info += tr("[NFC Interface]");
811                 if (methods & 0x0080)
812                         info += tr("[Push Button]");
813                 if (methods & 0x0100)
814                         info += tr("[Keypad]");
815                 info += "\n";
816         }
817
818         var = ctx_item->data(peer_role_dev_passwd_id);
819         if (var.isValid()) {
820                 info += tr("Device Password ID: ") + var.toString();
821                 switch (var.toInt()) {
822                 case 0:
823                         info += tr(" (Default PIN)");
824                         break;
825                 case 1:
826                         info += tr(" (User-specified PIN)");
827                         break;
828                 case 2:
829                         info += tr(" (Machine-specified PIN)");
830                         break;
831                 case 3:
832                         info += tr(" (Rekey)");
833                         break;
834                 case 4:
835                         info += tr(" (Push Button)");
836                         break;
837                 case 5:
838                         info += tr(" (Registrar-specified)");
839                         break;
840                 }
841                 info += "\n";
842         }
843
844         msg.setInformativeText(info);
845
846         var = ctx_item->data(peer_role_details);
847         if (var.isValid())
848                 msg.setDetailedText(var.toString());
849
850         msg.exec();
851 }
852
853
854 void Peers::connect_pbc()
855 {
856         if (ctx_item == NULL)
857                 return;
858
859         char cmd[100];
860         char reply[100];
861         size_t reply_len;
862
863         int peer_type = ctx_item->data(peer_role_type).toInt();
864         if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
865                 snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
866                          ctx_item->data(peer_role_uuid).toString().toAscii().
867                          constData());
868         } else {
869                 snprintf(cmd, sizeof(cmd), "WPS_PBC");
870         }
871         reply_len = sizeof(reply) - 1;
872         if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
873                 QMessageBox msg;
874                 msg.setIcon(QMessageBox::Warning);
875                 msg.setText("Failed to start WPS PBC.");
876                 msg.exec();
877         }
878 }
879
880
881 void Peers::learn_ap_config()
882 {
883         if (ctx_item == NULL)
884                 return;
885
886         QString uuid = ctx_item->data(peer_role_uuid).toString();
887
888         StringQuery input(tr("AP PIN:"));
889         input.setWindowTitle(tr("AP PIN for ") + ctx_item->text());
890         if (input.exec() != QDialog::Accepted)
891                 return;
892
893         char cmd[100];
894         char reply[100];
895         size_t reply_len;
896
897         snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
898                  uuid.toAscii().constData(),
899                  input.get_string().toAscii().constData());
900         reply_len = sizeof(reply) - 1;
901         if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
902                 QMessageBox msg;
903                 msg.setIcon(QMessageBox::Warning);
904                 msg.setText(tr("Failed to start learning AP configuration."));
905                 msg.exec();
906         }
907 }