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