WPS: Added callback for failure-after-M2/M2D
[mech_eap.git] / wpa_supplicant / wpa_gui-qt4 / wpagui.cpp
1 /*
2  * wpa_gui - WpaGui class
3  * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
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 #ifdef __MINGW32__
16 /* Need to get getopt() */
17 #include <unistd.h>
18 #endif
19
20 #include <cstdio>
21 #include <QMessageBox>
22 #include <QCloseEvent>
23 #include <QImageReader>
24
25 #include "wpagui.h"
26 #include "dirent.h"
27 #include "wpa_ctrl.h"
28 #include "userdatarequest.h"
29 #include "networkconfig.h"
30
31 #if 1
32 /* Silence stdout */
33 #define printf wpagui_printf
34 static int wpagui_printf(const char *, ...)
35 {
36         return 0;
37 }
38 #endif
39
40 WpaGui::WpaGui(QWidget *parent, const char *, Qt::WFlags)
41         : QMainWindow(parent)
42 {
43         setupUi(this);
44
45         (void) statusBar();
46
47         connect(fileEventHistoryAction, SIGNAL(triggered()), this,
48                 SLOT(eventHistory()));
49         connect(fileSaveConfigAction, SIGNAL(triggered()), this,
50                 SLOT(saveConfig()));
51         connect(actionWPS, SIGNAL(triggered()), this, SLOT(wpsDialog()));
52         connect(fileExitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
53         connect(networkAddAction, SIGNAL(triggered()), this,
54                 SLOT(addNetwork()));
55         connect(networkEditAction, SIGNAL(triggered()), this,
56                 SLOT(editSelectedNetwork()));
57         connect(networkRemoveAction, SIGNAL(triggered()), this,
58                 SLOT(removeSelectedNetwork()));
59         connect(networkEnableAllAction, SIGNAL(triggered()), this,
60                 SLOT(enableAllNetworks()));
61         connect(networkDisableAllAction, SIGNAL(triggered()), this,
62                 SLOT(disableAllNetworks()));
63         connect(networkRemoveAllAction, SIGNAL(triggered()), this,
64                 SLOT(removeAllNetworks()));
65         connect(helpIndexAction, SIGNAL(triggered()), this, SLOT(helpIndex()));
66         connect(helpContentsAction, SIGNAL(triggered()), this,
67                 SLOT(helpContents()));
68         connect(helpAboutAction, SIGNAL(triggered()), this, SLOT(helpAbout()));
69         connect(disconnectButton, SIGNAL(clicked()), this, SLOT(disconnect()));
70         connect(scanButton, SIGNAL(clicked()), this, SLOT(scan()));
71         connect(connectButton, SIGNAL(clicked()), this, SLOT(connectB()));
72         connect(adapterSelect, SIGNAL(activated(const QString&)), this,
73                 SLOT(selectAdapter(const QString&)));
74         connect(networkSelect, SIGNAL(activated(const QString&)), this,
75                 SLOT(selectNetwork(const QString&)));
76         connect(addNetworkButton, SIGNAL(clicked()), this, SLOT(addNetwork()));
77         connect(editNetworkButton, SIGNAL(clicked()), this,
78                 SLOT(editListedNetwork()));
79         connect(removeNetworkButton, SIGNAL(clicked()), this,
80                 SLOT(removeListedNetwork()));
81         connect(networkList, SIGNAL(itemSelectionChanged()), this,
82                 SLOT(updateNetworkDisabledStatus()));
83         connect(enableRadioButton, SIGNAL(toggled(bool)), this,
84                 SLOT(enableListedNetwork(bool)));
85         connect(disableRadioButton, SIGNAL(toggled(bool)), this,
86                 SLOT(disableListedNetwork(bool)));
87         connect(scanNetworkButton, SIGNAL(clicked()), this, SLOT(scan()));
88         connect(networkList, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
89                 this, SLOT(editListedNetwork()));
90         connect(wpaguiTab, SIGNAL(currentChanged(int)), this,
91                 SLOT(tabChanged(int)));
92         connect(wpsPbcButton, SIGNAL(clicked()), this, SLOT(wpsPbc()));
93         connect(wpsPinButton, SIGNAL(clicked()), this, SLOT(wpsGeneratePin()));
94         connect(wpsApPinEdit, SIGNAL(textChanged(const QString &)), this,
95                 SLOT(wpsApPinChanged(const QString &)));
96         connect(wpsApPinButton, SIGNAL(clicked()), this, SLOT(wpsApPin()));
97
98         eh = NULL;
99         scanres = NULL;
100         udr = NULL;
101         tray_icon = NULL;
102         startInTray = false;
103         ctrl_iface = NULL;
104         ctrl_conn = NULL;
105         monitor_conn = NULL;
106         msgNotifier = NULL;
107         ctrl_iface_dir = strdup("/var/run/wpa_supplicant");
108
109         parse_argv();
110
111         if (QSystemTrayIcon::isSystemTrayAvailable())
112                 createTrayIcon(startInTray);
113         else
114                 show();
115
116         textStatus->setText("connecting to wpa_supplicant");
117         timer = new QTimer(this);
118         connect(timer, SIGNAL(timeout()), SLOT(ping()));
119         timer->setSingleShot(FALSE);
120         timer->start(1000);
121
122         if (openCtrlConnection(ctrl_iface) < 0) {
123                 printf("Failed to open control connection to "
124                        "wpa_supplicant.\n");
125         }
126
127         updateStatus();
128         networkMayHaveChanged = true;
129         updateNetworks();
130 }
131
132
133 WpaGui::~WpaGui()
134 {
135         delete msgNotifier;
136
137         if (monitor_conn) {
138                 wpa_ctrl_detach(monitor_conn);
139                 wpa_ctrl_close(monitor_conn);
140                 monitor_conn = NULL;
141         }
142         if (ctrl_conn) {
143                 wpa_ctrl_close(ctrl_conn);
144                 ctrl_conn = NULL;
145         }
146
147         if (eh) {
148                 eh->close();
149                 delete eh;
150                 eh = NULL;
151         }
152
153         if (scanres) {
154                 scanres->close();
155                 delete scanres;
156                 scanres = NULL;
157         }
158
159         if (udr) {
160                 udr->close();
161                 delete udr;
162                 udr = NULL;
163         }
164
165         free(ctrl_iface);
166         ctrl_iface = NULL;
167
168         free(ctrl_iface_dir);
169         ctrl_iface_dir = NULL;
170 }
171
172
173 void WpaGui::languageChange()
174 {
175         retranslateUi(this);
176 }
177
178
179 void WpaGui::parse_argv()
180 {
181         int c;
182         for (;;) {
183                 c = getopt(qApp->argc(), qApp->argv(), "i:p:t");
184                 if (c < 0)
185                         break;
186                 switch (c) {
187                 case 'i':
188                         free(ctrl_iface);
189                         ctrl_iface = strdup(optarg);
190                         break;
191                 case 'p':
192                         free(ctrl_iface_dir);
193                         ctrl_iface_dir = strdup(optarg);
194                         break;
195                 case 't':
196                         startInTray = true;
197                         break;
198                 }
199         }
200 }
201
202
203 int WpaGui::openCtrlConnection(const char *ifname)
204 {
205         char *cfile;
206         int flen;
207         char buf[2048], *pos, *pos2;
208         size_t len;
209
210         if (ifname) {
211                 if (ifname != ctrl_iface) {
212                         free(ctrl_iface);
213                         ctrl_iface = strdup(ifname);
214                 }
215         } else {
216 #ifdef CONFIG_CTRL_IFACE_UDP
217                 free(ctrl_iface);
218                 ctrl_iface = strdup("udp");
219 #endif /* CONFIG_CTRL_IFACE_UDP */
220 #ifdef CONFIG_CTRL_IFACE_UNIX
221                 struct dirent *dent;
222                 DIR *dir = opendir(ctrl_iface_dir);
223                 free(ctrl_iface);
224                 ctrl_iface = NULL;
225                 if (dir) {
226                         while ((dent = readdir(dir))) {
227 #ifdef _DIRENT_HAVE_D_TYPE
228                                 /* Skip the file if it is not a socket.
229                                  * Also accept DT_UNKNOWN (0) in case
230                                  * the C library or underlying file
231                                  * system does not support d_type. */
232                                 if (dent->d_type != DT_SOCK &&
233                                     dent->d_type != DT_UNKNOWN)
234                                         continue;
235 #endif /* _DIRENT_HAVE_D_TYPE */
236
237                                 if (strcmp(dent->d_name, ".") == 0 ||
238                                     strcmp(dent->d_name, "..") == 0)
239                                         continue;
240                                 printf("Selected interface '%s'\n",
241                                        dent->d_name);
242                                 ctrl_iface = strdup(dent->d_name);
243                                 break;
244                         }
245                         closedir(dir);
246                 }
247 #endif /* CONFIG_CTRL_IFACE_UNIX */
248 #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
249                 struct wpa_ctrl *ctrl;
250                 int ret;
251
252                 free(ctrl_iface);
253                 ctrl_iface = NULL;
254
255                 ctrl = wpa_ctrl_open(NULL);
256                 if (ctrl) {
257                         len = sizeof(buf) - 1;
258                         ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf,
259                                                &len, NULL);
260                         if (ret >= 0) {
261                                 buf[len] = '\0';
262                                 pos = strchr(buf, '\n');
263                                 if (pos)
264                                         *pos = '\0';
265                                 ctrl_iface = strdup(buf);
266                         }
267                         wpa_ctrl_close(ctrl);
268                 }
269 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
270         }
271
272         if (ctrl_iface == NULL)
273                 return -1;
274
275 #ifdef CONFIG_CTRL_IFACE_UNIX
276         flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2;
277         cfile = (char *) malloc(flen);
278         if (cfile == NULL)
279                 return -1;
280         snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface);
281 #else /* CONFIG_CTRL_IFACE_UNIX */
282         flen = strlen(ctrl_iface) + 1;
283         cfile = (char *) malloc(flen);
284         if (cfile == NULL)
285                 return -1;
286         snprintf(cfile, flen, "%s", ctrl_iface);
287 #endif /* CONFIG_CTRL_IFACE_UNIX */
288
289         if (ctrl_conn) {
290                 wpa_ctrl_close(ctrl_conn);
291                 ctrl_conn = NULL;
292         }
293
294         if (monitor_conn) {
295                 delete msgNotifier;
296                 msgNotifier = NULL;
297                 wpa_ctrl_detach(monitor_conn);
298                 wpa_ctrl_close(monitor_conn);
299                 monitor_conn = NULL;
300         }
301
302         printf("Trying to connect to '%s'\n", cfile);
303         ctrl_conn = wpa_ctrl_open(cfile);
304         if (ctrl_conn == NULL) {
305                 free(cfile);
306                 return -1;
307         }
308         monitor_conn = wpa_ctrl_open(cfile);
309         free(cfile);
310         if (monitor_conn == NULL) {
311                 wpa_ctrl_close(ctrl_conn);
312                 return -1;
313         }
314         if (wpa_ctrl_attach(monitor_conn)) {
315                 printf("Failed to attach to wpa_supplicant\n");
316                 wpa_ctrl_close(monitor_conn);
317                 monitor_conn = NULL;
318                 wpa_ctrl_close(ctrl_conn);
319                 ctrl_conn = NULL;
320                 return -1;
321         }
322
323 #if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
324         msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn),
325                                           QSocketNotifier::Read, this);
326         connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs()));
327 #endif
328
329         adapterSelect->clear();
330         adapterSelect->addItem(ctrl_iface);
331         adapterSelect->setCurrentIndex(0);
332
333         len = sizeof(buf) - 1;
334         if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >=
335             0) {
336                 buf[len] = '\0';
337                 pos = buf;
338                 while (*pos) {
339                         pos2 = strchr(pos, '\n');
340                         if (pos2)
341                                 *pos2 = '\0';
342                         if (strcmp(pos, ctrl_iface) != 0)
343                                 adapterSelect->addItem(pos);
344                         if (pos2)
345                                 pos = pos2 + 1;
346                         else
347                                 break;
348                 }
349         }
350
351         len = sizeof(buf) - 1;
352         if (wpa_ctrl_request(ctrl_conn, "GET_CAPABILITY eap", 18, buf, &len,
353                              NULL) >= 0) {
354                 buf[len] = '\0';
355
356                 QString res(buf);
357                 QStringList types = res.split(QChar(' '));
358                 actionWPS->setEnabled(types.contains("WSC"));
359         }
360
361         return 0;
362 }
363
364
365 static void wpa_gui_msg_cb(char *msg, size_t)
366 {
367         /* This should not happen anymore since two control connections are
368          * used. */
369         printf("missed message: %s\n", msg);
370 }
371
372
373 int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen)
374 {
375         int ret;
376
377         if (ctrl_conn == NULL)
378                 return -3;
379         ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen,
380                                wpa_gui_msg_cb);
381         if (ret == -2)
382                 printf("'%s' command timed out.\n", cmd);
383         else if (ret < 0)
384                 printf("'%s' command failed.\n", cmd);
385
386         return ret;
387 }
388
389
390 void WpaGui::updateStatus()
391 {
392         char buf[2048], *start, *end, *pos;
393         size_t len;
394
395         pingsToStatusUpdate = 10;
396
397         len = sizeof(buf) - 1;
398         if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) {
399                 textStatus->setText("Could not get status from "
400                                     "wpa_supplicant");
401                 textAuthentication->clear();
402                 textEncryption->clear();
403                 textSsid->clear();
404                 textBssid->clear();
405                 textIpAddress->clear();
406                 return;
407         }
408
409         buf[len] = '\0';
410
411         bool auth_updated = false, ssid_updated = false;
412         bool bssid_updated = false, ipaddr_updated = false;
413         bool status_updated = false;
414         char *pairwise_cipher = NULL, *group_cipher = NULL;
415
416         start = buf;
417         while (*start) {
418                 bool last = false;
419                 end = strchr(start, '\n');
420                 if (end == NULL) {
421                         last = true;
422                         end = start;
423                         while (end[0] && end[1])
424                                 end++;
425                 }
426                 *end = '\0';
427
428                 pos = strchr(start, '=');
429                 if (pos) {
430                         *pos++ = '\0';
431                         if (strcmp(start, "bssid") == 0) {
432                                 bssid_updated = true;
433                                 textBssid->setText(pos);
434                         } else if (strcmp(start, "ssid") == 0) {
435                                 ssid_updated = true;
436                                 textSsid->setText(pos);
437                         } else if (strcmp(start, "ip_address") == 0) {
438                                 ipaddr_updated = true;
439                                 textIpAddress->setText(pos);
440                         } else if (strcmp(start, "wpa_state") == 0) {
441                                 status_updated = true;
442                                 textStatus->setText(pos);
443                         } else if (strcmp(start, "key_mgmt") == 0) {
444                                 auth_updated = true;
445                                 textAuthentication->setText(pos);
446                                 /* TODO: could add EAP status to this */
447                         } else if (strcmp(start, "pairwise_cipher") == 0) {
448                                 pairwise_cipher = pos;
449                         } else if (strcmp(start, "group_cipher") == 0) {
450                                 group_cipher = pos;
451                         }
452                 }
453
454                 if (last)
455                         break;
456                 start = end + 1;
457         }
458
459         if (pairwise_cipher || group_cipher) {
460                 QString encr;
461                 if (pairwise_cipher && group_cipher &&
462                     strcmp(pairwise_cipher, group_cipher) != 0) {
463                         encr.append(pairwise_cipher);
464                         encr.append(" + ");
465                         encr.append(group_cipher);
466                 } else if (pairwise_cipher) {
467                         encr.append(pairwise_cipher);
468                 } else {
469                         encr.append(group_cipher);
470                         encr.append(" [group key only]");
471                 }
472                 textEncryption->setText(encr);
473         } else
474                 textEncryption->clear();
475
476         if (!status_updated)
477                 textStatus->clear();
478         if (!auth_updated)
479                 textAuthentication->clear();
480         if (!ssid_updated)
481                 textSsid->clear();
482         if (!bssid_updated)
483                 textBssid->clear();
484         if (!ipaddr_updated)
485                 textIpAddress->clear();
486 }
487
488
489 void WpaGui::updateNetworks()
490 {
491         char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
492         size_t len;
493         int first_active = -1;
494         int was_selected = -1;
495         bool current = false;
496
497         if (!networkMayHaveChanged)
498                 return;
499
500         if (networkList->currentRow() >= 0)
501                 was_selected = networkList->currentRow();
502
503         networkSelect->clear();
504         networkList->clear();
505
506         if (ctrl_conn == NULL)
507                 return;
508
509         len = sizeof(buf) - 1;
510         if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
511                 return;
512
513         buf[len] = '\0';
514         start = strchr(buf, '\n');
515         if (start == NULL)
516                 return;
517         start++;
518
519         while (*start) {
520                 bool last = false;
521                 end = strchr(start, '\n');
522                 if (end == NULL) {
523                         last = true;
524                         end = start;
525                         while (end[0] && end[1])
526                                 end++;
527                 }
528                 *end = '\0';
529
530                 id = start;
531                 ssid = strchr(id, '\t');
532                 if (ssid == NULL)
533                         break;
534                 *ssid++ = '\0';
535                 bssid = strchr(ssid, '\t');
536                 if (bssid == NULL)
537                         break;
538                 *bssid++ = '\0';
539                 flags = strchr(bssid, '\t');
540                 if (flags == NULL)
541                         break;
542                 *flags++ = '\0';
543
544                 QString network(id);
545                 network.append(": ");
546                 network.append(ssid);
547                 networkSelect->addItem(network);
548                 networkList->addItem(network);
549
550                 if (strstr(flags, "[CURRENT]")) {
551                         networkSelect->setCurrentIndex(networkSelect->count() -
552                                                       1);
553                         current = true;
554                 } else if (first_active < 0 &&
555                            strstr(flags, "[DISABLED]") == NULL)
556                         first_active = networkSelect->count() - 1;
557
558                 if (last)
559                         break;
560                 start = end + 1;
561         }
562
563         if (networkSelect->count() > 1)
564                 networkSelect->addItem("Select any network");
565
566         if (!current && first_active >= 0)
567                 networkSelect->setCurrentIndex(first_active);
568
569         if (was_selected >= 0 && networkList->count() > 0) {
570                 if (was_selected < networkList->count())
571                         networkList->setCurrentRow(was_selected);
572                 else
573                         networkList->setCurrentRow(networkList->count() - 1);
574         }
575         else
576                 networkList->setCurrentRow(networkSelect->currentIndex());
577
578         networkMayHaveChanged = false;
579 }
580
581
582 void WpaGui::helpIndex()
583 {
584         printf("helpIndex\n");
585 }
586
587
588 void WpaGui::helpContents()
589 {
590         printf("helpContents\n");
591 }
592
593
594 void WpaGui::helpAbout()
595 {
596         QMessageBox::about(this, "wpa_gui for wpa_supplicant",
597                            "Copyright (c) 2003-2008,\n"
598                            "Jouni Malinen <j@w1.fi>\n"
599                            "and contributors.\n"
600                            "\n"
601                            "This program is free software. You can\n"
602                            "distribute it and/or modify it under the terms "
603                            "of\n"
604                            "the GNU General Public License version 2.\n"
605                            "\n"
606                            "Alternatively, this software may be distributed\n"
607                            "under the terms of the BSD license.\n"
608                            "\n"
609                            "This product includes software developed\n"
610                            "by the OpenSSL Project for use in the\n"
611                            "OpenSSL Toolkit (http://www.openssl.org/)\n");
612 }
613
614
615 void WpaGui::disconnect()
616 {
617         char reply[10];
618         size_t reply_len = sizeof(reply);
619         ctrlRequest("DISCONNECT", reply, &reply_len);
620         stopWpsRun(false);
621 }
622
623
624 void WpaGui::scan()
625 {
626         if (scanres) {
627                 scanres->close();
628                 delete scanres;
629         }
630
631         scanres = new ScanResults();
632         if (scanres == NULL)
633                 return;
634         scanres->setWpaGui(this);
635         scanres->show();
636         scanres->exec();
637 }
638
639
640 void WpaGui::eventHistory()
641 {
642         if (eh) {
643                 eh->close();
644                 delete eh;
645         }
646
647         eh = new EventHistory();
648         if (eh == NULL)
649                 return;
650         eh->addEvents(msgs);
651         eh->show();
652         eh->exec();
653 }
654
655
656 void WpaGui::ping()
657 {
658         char buf[10];
659         size_t len;
660
661 #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
662         /*
663          * QSocketNotifier cannot be used with Windows named pipes, so use a
664          * timer to check for received messages for now. This could be
665          * optimized be doing something specific to named pipes or Windows
666          * events, but it is not clear what would be the best way of doing that
667          * in Qt.
668          */
669         receiveMsgs();
670 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
671
672         if (scanres && !scanres->isVisible()) {
673                 delete scanres;
674                 scanres = NULL;
675         }
676
677         if (eh && !eh->isVisible()) {
678                 delete eh;
679                 eh = NULL;
680         }
681
682         if (udr && !udr->isVisible()) {
683                 delete udr;
684                 udr = NULL;
685         }
686
687         len = sizeof(buf) - 1;
688         if (ctrlRequest("PING", buf, &len) < 0) {
689                 printf("PING failed - trying to reconnect\n");
690                 if (openCtrlConnection(ctrl_iface) >= 0) {
691                         printf("Reconnected successfully\n");
692                         pingsToStatusUpdate = 0;
693                 }
694         }
695
696         pingsToStatusUpdate--;
697         if (pingsToStatusUpdate <= 0) {
698                 updateStatus();
699                 updateNetworks();
700         }
701
702 #ifndef CONFIG_CTRL_IFACE_NAMED_PIPE
703         /* Use less frequent pings and status updates when the main window is
704          * hidden (running in taskbar). */
705         int interval = isHidden() ? 5000 : 1000;
706         if (timer->interval() != interval)
707                 timer->setInterval(interval);
708 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
709 }
710
711
712 static int str_match(const char *a, const char *b)
713 {
714         return strncmp(a, b, strlen(b)) == 0;
715 }
716
717
718 void WpaGui::processMsg(char *msg)
719 {
720         char *pos = msg, *pos2;
721         int priority = 2;
722
723         if (*pos == '<') {
724                 /* skip priority */
725                 pos++;
726                 priority = atoi(pos);
727                 pos = strchr(pos, '>');
728                 if (pos)
729                         pos++;
730                 else
731                         pos = msg;
732         }
733
734         WpaMsg wm(pos, priority);
735         if (eh)
736                 eh->addEvent(wm);
737         msgs.append(wm);
738         while (msgs.count() > 100)
739                 msgs.pop_front();
740
741         /* Update last message with truncated version of the event */
742         if (strncmp(pos, "CTRL-", 5) == 0) {
743                 pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' ');
744                 if (pos2)
745                         pos2++;
746                 else
747                         pos2 = pos;
748         } else
749                 pos2 = pos;
750         QString lastmsg = pos2;
751         lastmsg.truncate(40);
752         textLastMessage->setText(lastmsg);
753
754         pingsToStatusUpdate = 0;
755         networkMayHaveChanged = true;
756
757         if (str_match(pos, WPA_CTRL_REQ))
758                 processCtrlReq(pos + strlen(WPA_CTRL_REQ));
759         else if (str_match(pos, WPA_EVENT_SCAN_RESULTS) && scanres)
760                 scanres->updateResults();
761         else if (str_match(pos, WPA_EVENT_DISCONNECTED))
762                 showTrayMessage(QSystemTrayIcon::Information, 3,
763                                 "Disconnected from network.");
764         else if (str_match(pos, WPA_EVENT_CONNECTED)) {
765                 showTrayMessage(QSystemTrayIcon::Information, 3,
766                                 "Connection to network established.");
767                 QTimer::singleShot(5 * 1000, this, SLOT(showTrayStatus()));
768                 stopWpsRun(true);
769         } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PBC)) {
770                 showTrayMessage(QSystemTrayIcon::Information, 3,
771                                 "Wi-Fi Protected Setup (WPS) AP\n"
772                                 "in active PBC mode found.");
773                 wpsStatusText->setText("WPS AP in active PBC mode found");
774                 wpaguiTab->setCurrentWidget(wpsTab);
775                 wpsInstructions->setText("Press the PBC button on the screen "
776                                          "to start registration");
777         } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PIN)) {
778                 showTrayMessage(QSystemTrayIcon::Information, 3,
779                                 "Wi-Fi Protected Setup (WPS) AP\n"
780                                 " in active PIN mode found.");
781                 wpsStatusText->setText("WPS AP with recently selected "
782                                        "registrar");
783                 wpaguiTab->setCurrentWidget(wpsTab);
784         } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) {
785                 showTrayMessage(QSystemTrayIcon::Information, 3,
786                                 "Wi-Fi Protected Setup (WPS)\n"
787                                 "AP detected.");
788                 wpsStatusText->setText("WPS AP detected");
789                 wpaguiTab->setCurrentWidget(wpsTab);
790         } else if (str_match(pos, WPS_EVENT_OVERLAP)) {
791                 showTrayMessage(QSystemTrayIcon::Information, 3,
792                                 "Wi-Fi Protected Setup (WPS)\n"
793                                 "PBC mode overlap detected.");
794                 wpsStatusText->setText("PBC mode overlap detected");
795                 wpsInstructions->setText("More than one AP is currently in "
796                                          "active WPS PBC mode. Wait couple of "
797                                          "minutes and try again");
798                 wpaguiTab->setCurrentWidget(wpsTab);
799         } else if (str_match(pos, WPS_EVENT_CRED_RECEIVED)) {
800                 wpsStatusText->setText("Network configuration received");
801                 wpaguiTab->setCurrentWidget(wpsTab);
802         } else if (str_match(pos, WPA_EVENT_EAP_METHOD)) {
803                 if (strstr(pos, "(WSC)"))
804                         wpsStatusText->setText("Registration started");
805         } else if (str_match(pos, WPS_EVENT_M2D)) {
806                 wpsStatusText->setText("Registrar does not yet know PIN");
807         } else if (str_match(pos, WPS_EVENT_FAIL)) {
808                 wpsStatusText->setText("Registration failed");
809         }
810 }
811
812
813 void WpaGui::processCtrlReq(const char *req)
814 {
815         if (udr) {
816                 udr->close();
817                 delete udr;
818         }
819         udr = new UserDataRequest();
820         if (udr == NULL)
821                 return;
822         if (udr->setParams(this, req) < 0) {
823                 delete udr;
824                 udr = NULL;
825                 return;
826         }
827         udr->show();
828         udr->exec();
829 }
830
831
832 void WpaGui::receiveMsgs()
833 {
834         char buf[256];
835         size_t len;
836
837         while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) {
838                 len = sizeof(buf) - 1;
839                 if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) {
840                         buf[len] = '\0';
841                         processMsg(buf);
842                 }
843         }
844 }
845
846
847 void WpaGui::connectB()
848 {
849         char reply[10];
850         size_t reply_len = sizeof(reply);
851         ctrlRequest("REASSOCIATE", reply, &reply_len);
852 }
853
854
855 void WpaGui::selectNetwork( const QString &sel )
856 {
857         QString cmd(sel);
858         char reply[10];
859         size_t reply_len = sizeof(reply);
860
861         if (cmd.startsWith("Select any")) {
862                 cmd = "any";
863         } else {
864                 int pos = cmd.indexOf(':');
865                 if (pos < 0) {
866                         printf("Invalid selectNetwork '%s'\n",
867                                cmd.toAscii().constData());
868                         return;
869                 }
870                 cmd.truncate(pos);
871         }
872         cmd.prepend("SELECT_NETWORK ");
873         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
874         triggerUpdate();
875         stopWpsRun(false);
876 }
877
878
879 void WpaGui::enableNetwork(const QString &sel)
880 {
881         QString cmd(sel);
882         char reply[10];
883         size_t reply_len = sizeof(reply);
884
885         if (!cmd.startsWith("all")) {
886                 int pos = cmd.indexOf(':');
887                 if (pos < 0) {
888                         printf("Invalid enableNetwork '%s'\n",
889                                cmd.toAscii().constData());
890                         return;
891                 }
892                 cmd.truncate(pos);
893         }
894         cmd.prepend("ENABLE_NETWORK ");
895         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
896         triggerUpdate();
897 }
898
899
900 void WpaGui::disableNetwork(const QString &sel)
901 {
902         QString cmd(sel);
903         char reply[10];
904         size_t reply_len = sizeof(reply);
905
906         if (!cmd.startsWith("all")) {
907                 int pos = cmd.indexOf(':');
908                 if (pos < 0) {
909                         printf("Invalid disableNetwork '%s'\n",
910                                cmd.toAscii().constData());
911                         return;
912                 }
913                 cmd.truncate(pos);
914         }
915         cmd.prepend("DISABLE_NETWORK ");
916         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
917         triggerUpdate();
918 }
919
920
921 void WpaGui::editNetwork(const QString &sel)
922 {
923         QString cmd(sel);
924         int id = -1;
925
926         if (!cmd.startsWith("Select any")) {
927                 int pos = sel.indexOf(':');
928                 if (pos < 0) {
929                         printf("Invalid editNetwork '%s'\n",
930                                cmd.toAscii().constData());
931                         return;
932                 }
933                 cmd.truncate(pos);
934                 id = cmd.toInt();
935         }
936
937         NetworkConfig *nc = new NetworkConfig();
938         if (nc == NULL)
939                 return;
940         nc->setWpaGui(this);
941
942         if (id >= 0)
943                 nc->paramsFromConfig(id);
944         else
945                 nc->newNetwork();
946
947         nc->show();
948         nc->exec();
949 }
950
951
952 void WpaGui::editSelectedNetwork()
953 {
954         if (networkSelect->count() < 1) {
955                 QMessageBox::information(this, "No Networks",
956                                          "There are no networks to edit.\n");
957                 return;
958         }
959         QString sel(networkSelect->currentText());
960         editNetwork(sel);
961 }
962
963
964 void WpaGui::editListedNetwork()
965 {
966         if (networkList->currentRow() < 0) {
967                 QMessageBox::information(this, "Select A Network",
968                                          "Select a network from the list to"
969                                          " edit it.\n");
970                 return;
971         }
972         QString sel(networkList->currentItem()->text());
973         editNetwork(sel);
974 }
975
976
977 void WpaGui::triggerUpdate()
978 {
979         updateStatus();
980         networkMayHaveChanged = true;
981         updateNetworks();
982 }
983
984
985 void WpaGui::addNetwork()
986 {
987         NetworkConfig *nc = new NetworkConfig();
988         if (nc == NULL)
989                 return;
990         nc->setWpaGui(this);
991         nc->newNetwork();
992         nc->show();
993         nc->exec();
994 }
995
996
997 void WpaGui::removeNetwork(const QString &sel)
998 {
999         QString cmd(sel);
1000         char reply[10];
1001         size_t reply_len = sizeof(reply);
1002
1003         if (cmd.startsWith("Select any"))
1004                 return;
1005
1006         if (!cmd.startsWith("all")) {
1007                 int pos = cmd.indexOf(':');
1008                 if (pos < 0) {
1009                         printf("Invalid removeNetwork '%s'\n",
1010                                cmd.toAscii().constData());
1011                         return;
1012                 }
1013                 cmd.truncate(pos);
1014         }
1015         cmd.prepend("REMOVE_NETWORK ");
1016         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
1017         triggerUpdate();
1018 }
1019
1020
1021 void WpaGui::removeSelectedNetwork()
1022 {
1023         if (networkSelect->count() < 1) {
1024                 QMessageBox::information(this, "No Networks",
1025                                          "There are no networks to remove.\n");
1026                 return;
1027         }
1028         QString sel(networkSelect->currentText());
1029         removeNetwork(sel);
1030 }
1031
1032
1033 void WpaGui::removeListedNetwork()
1034 {
1035         if (networkList->currentRow() < 0) {
1036                 QMessageBox::information(this, "Select A Network",
1037                                          "Select a network from the list to"
1038                                          " remove it.\n");
1039                 return;
1040         }
1041         QString sel(networkList->currentItem()->text());
1042         removeNetwork(sel);
1043 }
1044
1045
1046 void WpaGui::enableAllNetworks()
1047 {
1048         QString sel("all");
1049         enableNetwork(sel);
1050 }
1051
1052
1053 void WpaGui::disableAllNetworks()
1054 {
1055         QString sel("all");
1056         disableNetwork(sel);
1057 }
1058
1059
1060 void WpaGui::removeAllNetworks()
1061 {
1062         QString sel("all");
1063         removeNetwork(sel);
1064 }
1065
1066
1067 int WpaGui::getNetworkDisabled(const QString &sel)
1068 {
1069         QString cmd(sel);
1070         char reply[10];
1071         size_t reply_len = sizeof(reply) - 1;
1072         int pos = cmd.indexOf(':');
1073         if (pos < 0) {
1074                 printf("Invalid getNetworkDisabled '%s'\n",
1075                        cmd.toAscii().constData());
1076                 return -1;
1077         }
1078         cmd.truncate(pos);
1079         cmd.prepend("GET_NETWORK ");
1080         cmd.append(" disabled");
1081
1082         if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) >= 0
1083             && reply_len >= 1) {
1084                 reply[reply_len] = '\0';
1085                 if (!str_match(reply, "FAIL"))
1086                         return atoi(reply);
1087         }
1088
1089         return -1;
1090 }
1091
1092
1093 void WpaGui::updateNetworkDisabledStatus()
1094 {
1095         if (networkList->currentRow() < 0)
1096                 return;
1097
1098         QString sel(networkList->currentItem()->text());
1099
1100         switch (getNetworkDisabled(sel)) {
1101         case 0:
1102                 if (!enableRadioButton->isChecked())
1103                         enableRadioButton->setChecked(true);
1104                 return;
1105         case 1:
1106                 if (!disableRadioButton->isChecked())
1107                         disableRadioButton->setChecked(true);
1108                 return;
1109         }
1110 }
1111
1112
1113 void WpaGui::enableListedNetwork(bool enabled)
1114 {
1115         if (networkList->currentRow() < 0 || !enabled)
1116                 return;
1117
1118         QString sel(networkList->currentItem()->text());
1119
1120         if (getNetworkDisabled(sel) == 1)
1121                 enableNetwork(sel);
1122 }
1123
1124
1125 void WpaGui::disableListedNetwork(bool disabled)
1126 {
1127         if (networkList->currentRow() < 0 || !disabled)
1128                 return;
1129
1130         QString sel(networkList->currentItem()->text());
1131
1132         if (getNetworkDisabled(sel) == 0)
1133                 disableNetwork(sel);
1134 }
1135
1136
1137 void WpaGui::saveConfig()
1138 {
1139         char buf[10];
1140         size_t len;
1141
1142         len = sizeof(buf) - 1;
1143         ctrlRequest("SAVE_CONFIG", buf, &len);
1144
1145         buf[len] = '\0';
1146
1147         if (str_match(buf, "FAIL"))
1148                 QMessageBox::warning(this, "Failed to save configuration",
1149                                      "The configuration could not be saved.\n"
1150                                      "\n"
1151                                      "The update_config=1 configuration option\n"
1152                                      "must be used for configuration saving to\n"
1153                                      "be permitted.\n");
1154         else
1155                 QMessageBox::information(this, "Saved configuration",
1156                                          "The current configuration was saved."
1157                                          "\n");
1158 }
1159
1160
1161 void WpaGui::selectAdapter( const QString & sel )
1162 {
1163         if (openCtrlConnection(sel.toAscii().constData()) < 0)
1164                 printf("Failed to open control connection to "
1165                        "wpa_supplicant.\n");
1166         updateStatus();
1167         updateNetworks();
1168 }
1169
1170
1171 void WpaGui::createTrayIcon(bool trayOnly)
1172 {
1173         QApplication::setQuitOnLastWindowClosed(false);
1174
1175         tray_icon = new QSystemTrayIcon(this);
1176         tray_icon->setToolTip(qAppName() + " - wpa_supplicant user interface");
1177         if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
1178                 tray_icon->setIcon(QIcon(":/icons/wpa_gui.svg"));
1179         else
1180                 tray_icon->setIcon(QIcon(":/icons/wpa_gui.png"));
1181
1182         connect(tray_icon,
1183                 SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
1184                 this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
1185
1186         ackTrayIcon = false;
1187
1188         tray_menu = new QMenu(this);
1189
1190         disconnectAction = new QAction("&Disconnect", this);
1191         reconnectAction = new QAction("Re&connect", this);
1192         connect(disconnectAction, SIGNAL(triggered()), this,
1193                 SLOT(disconnect()));
1194         connect(reconnectAction, SIGNAL(triggered()), this,
1195                 SLOT(connectB()));
1196         tray_menu->addAction(disconnectAction);
1197         tray_menu->addAction(reconnectAction);
1198         tray_menu->addSeparator();
1199
1200         eventAction = new QAction("&Event History", this);
1201         scanAction = new QAction("Scan &Results", this);
1202         statAction = new QAction("S&tatus", this);
1203         connect(eventAction, SIGNAL(triggered()), this, SLOT(eventHistory()));
1204         connect(scanAction, SIGNAL(triggered()), this, SLOT(scan()));
1205         connect(statAction, SIGNAL(triggered()), this, SLOT(showTrayStatus()));
1206         tray_menu->addAction(eventAction);
1207         tray_menu->addAction(scanAction);
1208         tray_menu->addAction(statAction);
1209         tray_menu->addSeparator();
1210
1211         showAction = new QAction("&Show Window", this);
1212         hideAction = new QAction("&Hide Window", this);
1213         quitAction = new QAction("&Quit", this);
1214         connect(showAction, SIGNAL(triggered()), this, SLOT(show()));
1215         connect(hideAction, SIGNAL(triggered()), this, SLOT(hide()));
1216         connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
1217         tray_menu->addAction(showAction);
1218         tray_menu->addAction(hideAction);
1219         tray_menu->addSeparator();
1220         tray_menu->addAction(quitAction);
1221
1222         tray_icon->setContextMenu(tray_menu);
1223
1224         tray_icon->show();
1225
1226         if (!trayOnly)
1227                 show();
1228 }
1229
1230
1231 void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec,
1232                              const QString & msg)
1233 {
1234         if (!QSystemTrayIcon::supportsMessages())
1235                 return;
1236
1237         if (isVisible() || !tray_icon || !tray_icon->isVisible())
1238                 return;
1239
1240         tray_icon->showMessage(qAppName(), msg, type, sec * 1000);
1241 }
1242
1243
1244 void WpaGui::trayActivated(QSystemTrayIcon::ActivationReason how)
1245  {
1246         switch (how) {
1247         /* use close() here instead of hide() and allow the
1248          * custom closeEvent handler take care of children */
1249         case QSystemTrayIcon::Trigger:
1250                 ackTrayIcon = true;
1251                 if (isVisible())
1252                         close();
1253                 else
1254                         show();
1255                 break;
1256         case QSystemTrayIcon::MiddleClick:
1257                 showTrayStatus();
1258                 break;
1259         default:
1260                 break;
1261         }
1262 }
1263
1264
1265 void WpaGui::showTrayStatus()
1266 {
1267         char buf[2048];
1268         size_t len;
1269
1270         len = sizeof(buf) - 1;
1271         if (ctrlRequest("STATUS", buf, &len) < 0)
1272                 return;
1273         buf[len] = '\0';
1274
1275         QString msg, status(buf);
1276
1277         QStringList lines = status.split(QRegExp("\\n"));
1278         for (QStringList::Iterator it = lines.begin();
1279              it != lines.end(); it++) {
1280                 int pos = (*it).indexOf('=') + 1;
1281                 if (pos < 1)
1282                         continue;
1283
1284                 if ((*it).startsWith("bssid="))
1285                         msg.append("BSSID:\t" + (*it).mid(pos) + "\n");
1286                 else if ((*it).startsWith("ssid="))
1287                         msg.append("SSID: \t" + (*it).mid(pos) + "\n");
1288                 else if ((*it).startsWith("pairwise_cipher="))
1289                         msg.append("PAIR: \t" + (*it).mid(pos) + "\n");
1290                 else if ((*it).startsWith("group_cipher="))
1291                         msg.append("GROUP:\t" + (*it).mid(pos) + "\n");
1292                 else if ((*it).startsWith("key_mgmt="))
1293                         msg.append("AUTH: \t" + (*it).mid(pos) + "\n");
1294                 else if ((*it).startsWith("wpa_state="))
1295                         msg.append("STATE:\t" + (*it).mid(pos) + "\n");
1296                 else if ((*it).startsWith("ip_address="))
1297                         msg.append("IP:   \t" + (*it).mid(pos) + "\n");
1298                 else if ((*it).startsWith("Supplicant PAE state="))
1299                         msg.append("PAE:  \t" + (*it).mid(pos) + "\n");
1300                 else if ((*it).startsWith("EAP state="))
1301                         msg.append("EAP:  \t" + (*it).mid(pos) + "\n");
1302         }
1303
1304         if (!msg.isEmpty())
1305                 showTrayMessage(QSystemTrayIcon::Information, 10, msg);
1306 }
1307
1308
1309 void WpaGui::closeEvent(QCloseEvent *event)
1310 {
1311         if (eh) {
1312                 eh->close();
1313                 delete eh;
1314                 eh = NULL;
1315         }
1316
1317         if (scanres) {
1318                 scanres->close();
1319                 delete scanres;
1320                 scanres = NULL;
1321         }
1322
1323         if (udr) {
1324                 udr->close();
1325                 delete udr;
1326                 udr = NULL;
1327         }
1328
1329         if (tray_icon && !ackTrayIcon) {
1330                 /* give user a visual hint that the tray icon exists */
1331                 if (QSystemTrayIcon::supportsMessages()) {
1332                         hide();
1333                         showTrayMessage(QSystemTrayIcon::Information, 3,
1334                                         qAppName() + " will keep running in "
1335                                         "the system tray.");
1336                 } else {
1337                         QMessageBox::information(this, qAppName() + " systray",
1338                                                  "The program will keep "
1339                                                  "running in the system "
1340                                                  "tray.");
1341                 }
1342                 ackTrayIcon = true;
1343         }
1344
1345         event->accept();
1346 }
1347
1348
1349 void WpaGui::wpsDialog()
1350 {
1351         wpaguiTab->setCurrentWidget(wpsTab);
1352 }
1353
1354
1355 void WpaGui::tabChanged(int index)
1356 {
1357         if (index != 2)
1358                 return;
1359
1360         if (wpsRunning)
1361                 return;
1362
1363         wpsApPinEdit->setEnabled(!bssFromScan.isEmpty());
1364         if (bssFromScan.isEmpty())
1365                 wpsApPinButton->setEnabled(false);
1366 }
1367
1368
1369 void WpaGui::wpsPbc()
1370 {
1371         char reply[20];
1372         size_t reply_len = sizeof(reply);
1373
1374         if (ctrlRequest("WPS_PBC", reply, &reply_len) < 0)
1375                 return;
1376
1377         wpsPinEdit->setEnabled(false);
1378         if (wpsStatusText->text().compare("WPS AP in active PBC mode found")) {
1379                 wpsInstructions->setText("Press the push button on the AP to "
1380                                          "start the PBC mode.");
1381         } else {
1382                 wpsInstructions->setText("If you have not yet done so, press "
1383                                          "the push button on the AP to start "
1384                                          "the PBC mode.");
1385         }
1386         wpsStatusText->setText("Waiting for Registrar");
1387         wpsRunning = true;
1388 }
1389
1390
1391 void WpaGui::wpsGeneratePin()
1392 {
1393         char reply[20];
1394         size_t reply_len = sizeof(reply) - 1;
1395
1396         if (ctrlRequest("WPS_PIN any", reply, &reply_len) < 0)
1397                 return;
1398
1399         reply[reply_len] = '\0';
1400
1401         wpsPinEdit->setText(reply);
1402         wpsPinEdit->setEnabled(true);
1403         wpsInstructions->setText("Enter the generated PIN into the Registrar "
1404                                  "(either the internal one in the AP or an "
1405                                  "external one).");
1406         wpsStatusText->setText("Waiting for Registrar");
1407         wpsRunning = true;
1408 }
1409
1410
1411 void WpaGui::setBssFromScan(const QString &bssid)
1412 {
1413         bssFromScan = bssid;
1414         wpsApPinEdit->setEnabled(!bssFromScan.isEmpty());
1415         wpsApPinButton->setEnabled(wpsApPinEdit->text().length() == 8);
1416         wpsStatusText->setText("WPS AP selected from scan results");
1417         wpsInstructions->setText("If you want to use an AP device PIN, e.g., "
1418                                  "from a label in the device, enter the eight "
1419                                  "digit AP PIN and click Use AP PIN button.");
1420 }
1421
1422
1423 void WpaGui::wpsApPinChanged(const QString &text)
1424 {
1425         wpsApPinButton->setEnabled(text.length() == 8);
1426 }
1427
1428
1429 void WpaGui::wpsApPin()
1430 {
1431         char reply[20];
1432         size_t reply_len = sizeof(reply);
1433
1434         QString cmd("WPS_REG " + bssFromScan + " " + wpsApPinEdit->text());
1435         if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) < 0)
1436                 return;
1437
1438         wpsStatusText->setText("Waiting for AP/Enrollee");
1439         wpsRunning = true;
1440 }
1441
1442
1443 void WpaGui::stopWpsRun(bool success)
1444 {
1445         if (wpsRunning)
1446                 wpsStatusText->setText(success ? "Connected to the network" :
1447                                        "Stopped");
1448         else
1449                 wpsStatusText->setText("");
1450         wpsPinEdit->setEnabled(false);
1451         wpsInstructions->setText("");
1452         wpsRunning = false;
1453         bssFromScan = "";
1454         wpsApPinEdit->setEnabled(false);
1455         wpsApPinButton->setEnabled(false);
1456 }