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