WPS: Added event callback and M2D notification
[libeap.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         }
808 }
809
810
811 void WpaGui::processCtrlReq(const char *req)
812 {
813         if (udr) {
814                 udr->close();
815                 delete udr;
816         }
817         udr = new UserDataRequest();
818         if (udr == NULL)
819                 return;
820         if (udr->setParams(this, req) < 0) {
821                 delete udr;
822                 udr = NULL;
823                 return;
824         }
825         udr->show();
826         udr->exec();
827 }
828
829
830 void WpaGui::receiveMsgs()
831 {
832         char buf[256];
833         size_t len;
834
835         while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) {
836                 len = sizeof(buf) - 1;
837                 if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) {
838                         buf[len] = '\0';
839                         processMsg(buf);
840                 }
841         }
842 }
843
844
845 void WpaGui::connectB()
846 {
847         char reply[10];
848         size_t reply_len = sizeof(reply);
849         ctrlRequest("REASSOCIATE", reply, &reply_len);
850 }
851
852
853 void WpaGui::selectNetwork( const QString &sel )
854 {
855         QString cmd(sel);
856         char reply[10];
857         size_t reply_len = sizeof(reply);
858
859         if (cmd.startsWith("Select any")) {
860                 cmd = "any";
861         } else {
862                 int pos = cmd.indexOf(':');
863                 if (pos < 0) {
864                         printf("Invalid selectNetwork '%s'\n",
865                                cmd.toAscii().constData());
866                         return;
867                 }
868                 cmd.truncate(pos);
869         }
870         cmd.prepend("SELECT_NETWORK ");
871         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
872         triggerUpdate();
873         stopWpsRun(false);
874 }
875
876
877 void WpaGui::enableNetwork(const QString &sel)
878 {
879         QString cmd(sel);
880         char reply[10];
881         size_t reply_len = sizeof(reply);
882
883         if (!cmd.startsWith("all")) {
884                 int pos = cmd.indexOf(':');
885                 if (pos < 0) {
886                         printf("Invalid enableNetwork '%s'\n",
887                                cmd.toAscii().constData());
888                         return;
889                 }
890                 cmd.truncate(pos);
891         }
892         cmd.prepend("ENABLE_NETWORK ");
893         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
894         triggerUpdate();
895 }
896
897
898 void WpaGui::disableNetwork(const QString &sel)
899 {
900         QString cmd(sel);
901         char reply[10];
902         size_t reply_len = sizeof(reply);
903
904         if (!cmd.startsWith("all")) {
905                 int pos = cmd.indexOf(':');
906                 if (pos < 0) {
907                         printf("Invalid disableNetwork '%s'\n",
908                                cmd.toAscii().constData());
909                         return;
910                 }
911                 cmd.truncate(pos);
912         }
913         cmd.prepend("DISABLE_NETWORK ");
914         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
915         triggerUpdate();
916 }
917
918
919 void WpaGui::editNetwork(const QString &sel)
920 {
921         QString cmd(sel);
922         int id = -1;
923
924         if (!cmd.startsWith("Select any")) {
925                 int pos = sel.indexOf(':');
926                 if (pos < 0) {
927                         printf("Invalid editNetwork '%s'\n",
928                                cmd.toAscii().constData());
929                         return;
930                 }
931                 cmd.truncate(pos);
932                 id = cmd.toInt();
933         }
934
935         NetworkConfig *nc = new NetworkConfig();
936         if (nc == NULL)
937                 return;
938         nc->setWpaGui(this);
939
940         if (id >= 0)
941                 nc->paramsFromConfig(id);
942         else
943                 nc->newNetwork();
944
945         nc->show();
946         nc->exec();
947 }
948
949
950 void WpaGui::editSelectedNetwork()
951 {
952         if (networkSelect->count() < 1) {
953                 QMessageBox::information(this, "No Networks",
954                                          "There are no networks to edit.\n");
955                 return;
956         }
957         QString sel(networkSelect->currentText());
958         editNetwork(sel);
959 }
960
961
962 void WpaGui::editListedNetwork()
963 {
964         if (networkList->currentRow() < 0) {
965                 QMessageBox::information(this, "Select A Network",
966                                          "Select a network from the list to"
967                                          " edit it.\n");
968                 return;
969         }
970         QString sel(networkList->currentItem()->text());
971         editNetwork(sel);
972 }
973
974
975 void WpaGui::triggerUpdate()
976 {
977         updateStatus();
978         networkMayHaveChanged = true;
979         updateNetworks();
980 }
981
982
983 void WpaGui::addNetwork()
984 {
985         NetworkConfig *nc = new NetworkConfig();
986         if (nc == NULL)
987                 return;
988         nc->setWpaGui(this);
989         nc->newNetwork();
990         nc->show();
991         nc->exec();
992 }
993
994
995 void WpaGui::removeNetwork(const QString &sel)
996 {
997         QString cmd(sel);
998         char reply[10];
999         size_t reply_len = sizeof(reply);
1000
1001         if (cmd.startsWith("Select any"))
1002                 return;
1003
1004         if (!cmd.startsWith("all")) {
1005                 int pos = cmd.indexOf(':');
1006                 if (pos < 0) {
1007                         printf("Invalid removeNetwork '%s'\n",
1008                                cmd.toAscii().constData());
1009                         return;
1010                 }
1011                 cmd.truncate(pos);
1012         }
1013         cmd.prepend("REMOVE_NETWORK ");
1014         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
1015         triggerUpdate();
1016 }
1017
1018
1019 void WpaGui::removeSelectedNetwork()
1020 {
1021         if (networkSelect->count() < 1) {
1022                 QMessageBox::information(this, "No Networks",
1023                                          "There are no networks to remove.\n");
1024                 return;
1025         }
1026         QString sel(networkSelect->currentText());
1027         removeNetwork(sel);
1028 }
1029
1030
1031 void WpaGui::removeListedNetwork()
1032 {
1033         if (networkList->currentRow() < 0) {
1034                 QMessageBox::information(this, "Select A Network",
1035                                          "Select a network from the list to"
1036                                          " remove it.\n");
1037                 return;
1038         }
1039         QString sel(networkList->currentItem()->text());
1040         removeNetwork(sel);
1041 }
1042
1043
1044 void WpaGui::enableAllNetworks()
1045 {
1046         QString sel("all");
1047         enableNetwork(sel);
1048 }
1049
1050
1051 void WpaGui::disableAllNetworks()
1052 {
1053         QString sel("all");
1054         disableNetwork(sel);
1055 }
1056
1057
1058 void WpaGui::removeAllNetworks()
1059 {
1060         QString sel("all");
1061         removeNetwork(sel);
1062 }
1063
1064
1065 int WpaGui::getNetworkDisabled(const QString &sel)
1066 {
1067         QString cmd(sel);
1068         char reply[10];
1069         size_t reply_len = sizeof(reply) - 1;
1070         int pos = cmd.indexOf(':');
1071         if (pos < 0) {
1072                 printf("Invalid getNetworkDisabled '%s'\n",
1073                        cmd.toAscii().constData());
1074                 return -1;
1075         }
1076         cmd.truncate(pos);
1077         cmd.prepend("GET_NETWORK ");
1078         cmd.append(" disabled");
1079
1080         if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) >= 0
1081             && reply_len >= 1) {
1082                 reply[reply_len] = '\0';
1083                 if (!str_match(reply, "FAIL"))
1084                         return atoi(reply);
1085         }
1086
1087         return -1;
1088 }
1089
1090
1091 void WpaGui::updateNetworkDisabledStatus()
1092 {
1093         if (networkList->currentRow() < 0)
1094                 return;
1095
1096         QString sel(networkList->currentItem()->text());
1097
1098         switch (getNetworkDisabled(sel)) {
1099         case 0:
1100                 if (!enableRadioButton->isChecked())
1101                         enableRadioButton->setChecked(true);
1102                 return;
1103         case 1:
1104                 if (!disableRadioButton->isChecked())
1105                         disableRadioButton->setChecked(true);
1106                 return;
1107         }
1108 }
1109
1110
1111 void WpaGui::enableListedNetwork(bool enabled)
1112 {
1113         if (networkList->currentRow() < 0 || !enabled)
1114                 return;
1115
1116         QString sel(networkList->currentItem()->text());
1117
1118         if (getNetworkDisabled(sel) == 1)
1119                 enableNetwork(sel);
1120 }
1121
1122
1123 void WpaGui::disableListedNetwork(bool disabled)
1124 {
1125         if (networkList->currentRow() < 0 || !disabled)
1126                 return;
1127
1128         QString sel(networkList->currentItem()->text());
1129
1130         if (getNetworkDisabled(sel) == 0)
1131                 disableNetwork(sel);
1132 }
1133
1134
1135 void WpaGui::saveConfig()
1136 {
1137         char buf[10];
1138         size_t len;
1139
1140         len = sizeof(buf) - 1;
1141         ctrlRequest("SAVE_CONFIG", buf, &len);
1142
1143         buf[len] = '\0';
1144
1145         if (str_match(buf, "FAIL"))
1146                 QMessageBox::warning(this, "Failed to save configuration",
1147                                      "The configuration could not be saved.\n"
1148                                      "\n"
1149                                      "The update_config=1 configuration option\n"
1150                                      "must be used for configuration saving to\n"
1151                                      "be permitted.\n");
1152         else
1153                 QMessageBox::information(this, "Saved configuration",
1154                                          "The current configuration was saved."
1155                                          "\n");
1156 }
1157
1158
1159 void WpaGui::selectAdapter( const QString & sel )
1160 {
1161         if (openCtrlConnection(sel.toAscii().constData()) < 0)
1162                 printf("Failed to open control connection to "
1163                        "wpa_supplicant.\n");
1164         updateStatus();
1165         updateNetworks();
1166 }
1167
1168
1169 void WpaGui::createTrayIcon(bool trayOnly)
1170 {
1171         QApplication::setQuitOnLastWindowClosed(false);
1172
1173         tray_icon = new QSystemTrayIcon(this);
1174         tray_icon->setToolTip(qAppName() + " - wpa_supplicant user interface");
1175         if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
1176                 tray_icon->setIcon(QIcon(":/icons/wpa_gui.svg"));
1177         else
1178                 tray_icon->setIcon(QIcon(":/icons/wpa_gui.png"));
1179
1180         connect(tray_icon,
1181                 SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
1182                 this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
1183
1184         ackTrayIcon = false;
1185
1186         tray_menu = new QMenu(this);
1187
1188         disconnectAction = new QAction("&Disconnect", this);
1189         reconnectAction = new QAction("Re&connect", this);
1190         connect(disconnectAction, SIGNAL(triggered()), this,
1191                 SLOT(disconnect()));
1192         connect(reconnectAction, SIGNAL(triggered()), this,
1193                 SLOT(connectB()));
1194         tray_menu->addAction(disconnectAction);
1195         tray_menu->addAction(reconnectAction);
1196         tray_menu->addSeparator();
1197
1198         eventAction = new QAction("&Event History", this);
1199         scanAction = new QAction("Scan &Results", this);
1200         statAction = new QAction("S&tatus", this);
1201         connect(eventAction, SIGNAL(triggered()), this, SLOT(eventHistory()));
1202         connect(scanAction, SIGNAL(triggered()), this, SLOT(scan()));
1203         connect(statAction, SIGNAL(triggered()), this, SLOT(showTrayStatus()));
1204         tray_menu->addAction(eventAction);
1205         tray_menu->addAction(scanAction);
1206         tray_menu->addAction(statAction);
1207         tray_menu->addSeparator();
1208
1209         showAction = new QAction("&Show Window", this);
1210         hideAction = new QAction("&Hide Window", this);
1211         quitAction = new QAction("&Quit", this);
1212         connect(showAction, SIGNAL(triggered()), this, SLOT(show()));
1213         connect(hideAction, SIGNAL(triggered()), this, SLOT(hide()));
1214         connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
1215         tray_menu->addAction(showAction);
1216         tray_menu->addAction(hideAction);
1217         tray_menu->addSeparator();
1218         tray_menu->addAction(quitAction);
1219
1220         tray_icon->setContextMenu(tray_menu);
1221
1222         tray_icon->show();
1223
1224         if (!trayOnly)
1225                 show();
1226 }
1227
1228
1229 void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec,
1230                              const QString & msg)
1231 {
1232         if (!QSystemTrayIcon::supportsMessages())
1233                 return;
1234
1235         if (isVisible() || !tray_icon || !tray_icon->isVisible())
1236                 return;
1237
1238         tray_icon->showMessage(qAppName(), msg, type, sec * 1000);
1239 }
1240
1241
1242 void WpaGui::trayActivated(QSystemTrayIcon::ActivationReason how)
1243  {
1244         switch (how) {
1245         /* use close() here instead of hide() and allow the
1246          * custom closeEvent handler take care of children */
1247         case QSystemTrayIcon::Trigger:
1248                 ackTrayIcon = true;
1249                 if (isVisible())
1250                         close();
1251                 else
1252                         show();
1253                 break;
1254         case QSystemTrayIcon::MiddleClick:
1255                 showTrayStatus();
1256                 break;
1257         default:
1258                 break;
1259         }
1260 }
1261
1262
1263 void WpaGui::showTrayStatus()
1264 {
1265         char buf[2048];
1266         size_t len;
1267
1268         len = sizeof(buf) - 1;
1269         if (ctrlRequest("STATUS", buf, &len) < 0)
1270                 return;
1271         buf[len] = '\0';
1272
1273         QString msg, status(buf);
1274
1275         QStringList lines = status.split(QRegExp("\\n"));
1276         for (QStringList::Iterator it = lines.begin();
1277              it != lines.end(); it++) {
1278                 int pos = (*it).indexOf('=') + 1;
1279                 if (pos < 1)
1280                         continue;
1281
1282                 if ((*it).startsWith("bssid="))
1283                         msg.append("BSSID:\t" + (*it).mid(pos) + "\n");
1284                 else if ((*it).startsWith("ssid="))
1285                         msg.append("SSID: \t" + (*it).mid(pos) + "\n");
1286                 else if ((*it).startsWith("pairwise_cipher="))
1287                         msg.append("PAIR: \t" + (*it).mid(pos) + "\n");
1288                 else if ((*it).startsWith("group_cipher="))
1289                         msg.append("GROUP:\t" + (*it).mid(pos) + "\n");
1290                 else if ((*it).startsWith("key_mgmt="))
1291                         msg.append("AUTH: \t" + (*it).mid(pos) + "\n");
1292                 else if ((*it).startsWith("wpa_state="))
1293                         msg.append("STATE:\t" + (*it).mid(pos) + "\n");
1294                 else if ((*it).startsWith("ip_address="))
1295                         msg.append("IP:   \t" + (*it).mid(pos) + "\n");
1296                 else if ((*it).startsWith("Supplicant PAE state="))
1297                         msg.append("PAE:  \t" + (*it).mid(pos) + "\n");
1298                 else if ((*it).startsWith("EAP state="))
1299                         msg.append("EAP:  \t" + (*it).mid(pos) + "\n");
1300         }
1301
1302         if (!msg.isEmpty())
1303                 showTrayMessage(QSystemTrayIcon::Information, 10, msg);
1304 }
1305
1306
1307 void WpaGui::closeEvent(QCloseEvent *event)
1308 {
1309         if (eh) {
1310                 eh->close();
1311                 delete eh;
1312                 eh = NULL;
1313         }
1314
1315         if (scanres) {
1316                 scanres->close();
1317                 delete scanres;
1318                 scanres = NULL;
1319         }
1320
1321         if (udr) {
1322                 udr->close();
1323                 delete udr;
1324                 udr = NULL;
1325         }
1326
1327         if (tray_icon && !ackTrayIcon) {
1328                 /* give user a visual hint that the tray icon exists */
1329                 if (QSystemTrayIcon::supportsMessages()) {
1330                         hide();
1331                         showTrayMessage(QSystemTrayIcon::Information, 3,
1332                                         qAppName() + " will keep running in "
1333                                         "the system tray.");
1334                 } else {
1335                         QMessageBox::information(this, qAppName() + " systray",
1336                                                  "The program will keep "
1337                                                  "running in the system "
1338                                                  "tray.");
1339                 }
1340                 ackTrayIcon = true;
1341         }
1342
1343         event->accept();
1344 }
1345
1346
1347 void WpaGui::wpsDialog()
1348 {
1349         wpaguiTab->setCurrentWidget(wpsTab);
1350 }
1351
1352
1353 void WpaGui::tabChanged(int index)
1354 {
1355         if (index != 2)
1356                 return;
1357
1358         if (wpsRunning)
1359                 return;
1360
1361         wpsApPinEdit->setEnabled(!bssFromScan.isEmpty());
1362         if (bssFromScan.isEmpty())
1363                 wpsApPinButton->setEnabled(false);
1364 }
1365
1366
1367 void WpaGui::wpsPbc()
1368 {
1369         char reply[20];
1370         size_t reply_len = sizeof(reply);
1371
1372         if (ctrlRequest("WPS_PBC", reply, &reply_len) < 0)
1373                 return;
1374
1375         wpsPinEdit->setEnabled(false);
1376         if (wpsStatusText->text().compare("WPS AP in active PBC mode found")) {
1377                 wpsInstructions->setText("Press the push button on the AP to "
1378                                          "start the PBC mode.");
1379         } else {
1380                 wpsInstructions->setText("If you have not yet done so, press "
1381                                          "the push button on the AP to start "
1382                                          "the PBC mode.");
1383         }
1384         wpsStatusText->setText("Waiting for Registrar");
1385         wpsRunning = true;
1386 }
1387
1388
1389 void WpaGui::wpsGeneratePin()
1390 {
1391         char reply[20];
1392         size_t reply_len = sizeof(reply) - 1;
1393
1394         if (ctrlRequest("WPS_PIN any", reply, &reply_len) < 0)
1395                 return;
1396
1397         reply[reply_len] = '\0';
1398
1399         wpsPinEdit->setText(reply);
1400         wpsPinEdit->setEnabled(true);
1401         wpsInstructions->setText("Enter the generated PIN into the Registrar "
1402                                  "(either the internal one in the AP or an "
1403                                  "external one).");
1404         wpsStatusText->setText("Waiting for Registrar");
1405         wpsRunning = true;
1406 }
1407
1408
1409 void WpaGui::setBssFromScan(const QString &bssid)
1410 {
1411         bssFromScan = bssid;
1412         wpsApPinEdit->setEnabled(!bssFromScan.isEmpty());
1413         wpsApPinButton->setEnabled(wpsApPinEdit->text().length() == 8);
1414         wpsStatusText->setText("WPS AP selected from scan results");
1415         wpsInstructions->setText("If you want to use an AP device PIN, e.g., "
1416                                  "from a label in the device, enter the eight "
1417                                  "digit AP PIN and click Use AP PIN button.");
1418 }
1419
1420
1421 void WpaGui::wpsApPinChanged(const QString &text)
1422 {
1423         wpsApPinButton->setEnabled(text.length() == 8);
1424 }
1425
1426
1427 void WpaGui::wpsApPin()
1428 {
1429         char reply[20];
1430         size_t reply_len = sizeof(reply);
1431
1432         QString cmd("WPS_REG " + bssFromScan + " " + wpsApPinEdit->text());
1433         if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) < 0)
1434                 return;
1435
1436         wpsStatusText->setText("Waiting for AP/Enrollee");
1437         wpsRunning = true;
1438 }
1439
1440
1441 void WpaGui::stopWpsRun(bool success)
1442 {
1443         if (wpsRunning)
1444                 wpsStatusText->setText(success ? "Connected to the network" :
1445                                        "Stopped");
1446         else
1447                 wpsStatusText->setText("");
1448         wpsPinEdit->setEnabled(false);
1449         wpsInstructions->setText("");
1450         wpsRunning = false;
1451         bssFromScan = "";
1452         wpsApPinEdit->setEnabled(false);
1453         wpsApPinButton->setEnabled(false);
1454 }