wpa_gui-qt4: add system tray support
[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()), this, SLOT(fileExit()));
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         if (!isVisible())
599                 show();
600
601         scanres = new ScanResults();
602         if (scanres == NULL)
603                 return;
604         scanres->setWpaGui(this);
605         scanres->show();
606         scanres->exec();
607 }
608
609
610 void WpaGui::eventHistory()
611 {
612         if (eh) {
613                 eh->close();
614                 delete eh;
615         }
616
617         if (!isVisible())
618                 show();
619
620         eh = new EventHistory();
621         if (eh == NULL)
622                 return;
623         eh->addEvents(msgs);
624         eh->show();
625         eh->exec();
626 }
627
628
629 void WpaGui::ping()
630 {
631         char buf[10];
632         size_t len;
633
634 #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
635         /*
636          * QSocketNotifier cannot be used with Windows named pipes, so use a
637          * timer to check for received messages for now. This could be
638          * optimized be doing something specific to named pipes or Windows
639          * events, but it is not clear what would be the best way of doing that
640          * in Qt.
641          */
642         receiveMsgs();
643 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
644
645         if (scanres && !scanres->isVisible()) {
646                 delete scanres;
647                 scanres = NULL;
648         }
649
650         if (eh && !eh->isVisible()) {
651                 delete eh;
652                 eh = NULL;
653         }
654
655         if (udr && !udr->isVisible()) {
656                 delete udr;
657                 udr = NULL;
658         }
659
660         len = sizeof(buf) - 1;
661         if (ctrlRequest("PING", buf, &len) < 0) {
662                 printf("PING failed - trying to reconnect\n");
663                 if (openCtrlConnection(ctrl_iface) >= 0) {
664                         printf("Reconnected successfully\n");
665                         pingsToStatusUpdate = 0;
666                 }
667         }
668
669         pingsToStatusUpdate--;
670         if (pingsToStatusUpdate <= 0) {
671                 updateStatus();
672                 updateNetworks();
673         }
674 }
675
676
677 static int str_match(const char *a, const char *b)
678 {
679         return strncmp(a, b, strlen(b)) == 0;
680 }
681
682
683 void WpaGui::processMsg(char *msg)
684 {
685         char *pos = msg, *pos2;
686         int priority = 2;
687
688         if (*pos == '<') {
689                 /* skip priority */
690                 pos++;
691                 priority = atoi(pos);
692                 pos = strchr(pos, '>');
693                 if (pos)
694                         pos++;
695                 else
696                         pos = msg;
697         }
698
699         WpaMsg wm(pos, priority);
700         if (eh)
701                 eh->addEvent(wm);
702         msgs.append(wm);
703         while (msgs.count() > 100)
704                 msgs.pop_front();
705
706         /* Update last message with truncated version of the event */
707         if (strncmp(pos, "CTRL-", 5) == 0) {
708                 pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' ');
709                 if (pos2)
710                         pos2++;
711                 else
712                         pos2 = pos;
713         } else
714                 pos2 = pos;
715         QString lastmsg = pos2;
716         lastmsg.truncate(40);
717         textLastMessage->setText(lastmsg);
718
719         pingsToStatusUpdate = 0;
720         networkMayHaveChanged = true;
721
722         if (str_match(pos, WPA_CTRL_REQ))
723                 processCtrlReq(pos + strlen(WPA_CTRL_REQ));
724         else if (str_match(pos, WPA_EVENT_SCAN_RESULTS) && scanres)
725                 scanres->updateResults();
726         else if (str_match(pos, WPA_EVENT_DISCONNECTED))
727                 showTrayMessage(QSystemTrayIcon::Information, 3,
728                                 "Disconnected from network.");
729         else if (str_match(pos, WPA_EVENT_CONNECTED)) {
730                 showTrayMessage(QSystemTrayIcon::Information, 3,
731                                 "Connection to network established.");
732                 QTimer::singleShot(5 * 1000, this, SLOT(showTrayStatus()));
733         }
734 }
735
736
737 void WpaGui::processCtrlReq(const char *req)
738 {
739         if (udr) {
740                 udr->close();
741                 delete udr;
742         }
743         udr = new UserDataRequest();
744         if (udr == NULL)
745                 return;
746         if (udr->setParams(this, req) < 0) {
747                 delete udr;
748                 udr = NULL;
749                 return;
750         }
751         udr->show();
752         udr->exec();
753 }
754
755
756 void WpaGui::receiveMsgs()
757 {
758         char buf[256];
759         size_t len;
760
761         while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) {
762                 len = sizeof(buf) - 1;
763                 if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) {
764                         buf[len] = '\0';
765                         processMsg(buf);
766                 }
767         }
768 }
769
770
771 void WpaGui::connectB()
772 {
773         char reply[10];
774         size_t reply_len = sizeof(reply);
775         ctrlRequest("REASSOCIATE", reply, &reply_len);
776 }
777
778
779 void WpaGui::selectNetwork( const QString &sel )
780 {
781         QString cmd(sel);
782         char reply[10];
783         size_t reply_len = sizeof(reply);
784
785         if (cmd.startsWith("Select any")) {
786                 cmd = "any";
787         } else {
788                 int pos = cmd.indexOf(':');
789                 if (pos < 0) {
790                         printf("Invalid selectNetwork '%s'\n",
791                                cmd.toAscii().constData());
792                         return;
793                 }
794                 cmd.truncate(pos);
795         }
796         cmd.prepend("SELECT_NETWORK ");
797         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
798         triggerUpdate();
799 }
800
801
802 void WpaGui::enableNetwork(const QString &sel)
803 {
804         QString cmd(sel);
805         char reply[10];
806         size_t reply_len = sizeof(reply);
807
808         if (!cmd.startsWith("all")) {
809                 int pos = cmd.indexOf(':');
810                 if (pos < 0) {
811                         printf("Invalid enableNetwork '%s'\n",
812                                cmd.toAscii().constData());
813                         return;
814                 }
815                 cmd.truncate(pos);
816         }
817         cmd.prepend("ENABLE_NETWORK ");
818         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
819         triggerUpdate();
820 }
821
822
823 void WpaGui::disableNetwork(const QString &sel)
824 {
825         QString cmd(sel);
826         char reply[10];
827         size_t reply_len = sizeof(reply);
828
829         if (!cmd.startsWith("all")) {
830                 int pos = cmd.indexOf(':');
831                 if (pos < 0) {
832                         printf("Invalid disableNetwork '%s'\n",
833                                cmd.toAscii().constData());
834                         return;
835                 }
836                 cmd.truncate(pos);
837         }
838         cmd.prepend("DISABLE_NETWORK ");
839         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
840         triggerUpdate();
841 }
842
843
844 void WpaGui::editNetwork(const QString &sel)
845 {
846         QString cmd(sel);
847         int id = -1;
848
849         if (!cmd.startsWith("Select any")) {
850                 int pos = sel.indexOf(':');
851                 if (pos < 0) {
852                         printf("Invalid editNetwork '%s'\n",
853                                cmd.toAscii().constData());
854                         return;
855                 }
856                 cmd.truncate(pos);
857                 id = cmd.toInt();
858         }
859
860         NetworkConfig *nc = new NetworkConfig();
861         if (nc == NULL)
862                 return;
863         nc->setWpaGui(this);
864
865         if (id >= 0)
866                 nc->paramsFromConfig(id);
867         else
868                 nc->newNetwork();
869
870         nc->show();
871         nc->exec();
872 }
873
874
875 void WpaGui::editSelectedNetwork()
876 {
877         if (networkSelect->count() < 1) {
878                 QMessageBox::information(this, "No Networks",
879                                          "There are no networks to edit.\n");
880                 return;
881         }
882         QString sel(networkSelect->currentText());
883         editNetwork(sel);
884 }
885
886
887 void WpaGui::editListedNetwork()
888 {
889         if (networkList->currentRow() < 0) {
890                 QMessageBox::information(this, "Select A Network",
891                                          "Select a network from the list to"
892                                          " edit it.\n");
893                 return;
894         }
895         QString sel(networkList->currentItem()->text());
896         editNetwork(sel);
897 }
898
899
900 void WpaGui::triggerUpdate()
901 {
902         updateStatus();
903         networkMayHaveChanged = true;
904         updateNetworks();
905 }
906
907
908 void WpaGui::addNetwork()
909 {
910         NetworkConfig *nc = new NetworkConfig();
911         if (nc == NULL)
912                 return;
913         nc->setWpaGui(this);
914         nc->newNetwork();
915         nc->show();
916         nc->exec();
917 }
918
919
920 void WpaGui::removeNetwork(const QString &sel)
921 {
922         QString cmd(sel);
923         char reply[10];
924         size_t reply_len = sizeof(reply);
925
926         if (cmd.startsWith("Select any"))
927                 return;
928
929         if (!cmd.startsWith("all")) {
930                 int pos = cmd.indexOf(':');
931                 if (pos < 0) {
932                         printf("Invalid removeNetwork '%s'\n",
933                                cmd.toAscii().constData());
934                         return;
935                 }
936                 cmd.truncate(pos);
937         }
938         cmd.prepend("REMOVE_NETWORK ");
939         ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
940         triggerUpdate();
941 }
942
943
944 void WpaGui::removeSelectedNetwork()
945 {
946         if (networkSelect->count() < 1) {
947                 QMessageBox::information(this, "No Networks",
948                                          "There are no networks to remove.\n");
949                 return;
950         }
951         QString sel(networkSelect->currentText());
952         removeNetwork(sel);
953 }
954
955
956 void WpaGui::removeListedNetwork()
957 {
958         if (networkList->currentRow() < 0) {
959                 QMessageBox::information(this, "Select A Network",
960                                          "Select a network from the list to"
961                                          " remove it.\n");
962                 return;
963         }
964         QString sel(networkList->currentItem()->text());
965         removeNetwork(sel);
966 }
967
968
969 void WpaGui::enableAllNetworks()
970 {
971         QString sel("all");
972         enableNetwork(sel);
973 }
974
975
976 void WpaGui::disableAllNetworks()
977 {
978         QString sel("all");
979         disableNetwork(sel);
980 }
981
982
983 void WpaGui::removeAllNetworks()
984 {
985         QString sel("all");
986         removeNetwork(sel);
987 }
988
989
990 int WpaGui::getNetworkDisabled(const QString &sel)
991 {
992         QString cmd(sel);
993         char reply[10];
994         size_t reply_len = sizeof(reply) - 1;
995         int pos = cmd.indexOf(':');
996         if (pos < 0) {
997                 printf("Invalid getNetworkDisabled '%s'\n",
998                        cmd.toAscii().constData());
999                 return -1;
1000         }
1001         cmd.truncate(pos);
1002         cmd.prepend("GET_NETWORK ");
1003         cmd.append(" disabled");
1004
1005         if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) >= 0
1006             && reply_len >= 1) {
1007                 reply[reply_len] = '\0';
1008                 if (!str_match(reply, "FAIL"))
1009                         return atoi(reply);
1010         }
1011
1012         return -1;
1013 }
1014
1015
1016 void WpaGui::updateNetworkDisabledStatus()
1017 {
1018         if (networkList->currentRow() < 0)
1019                 return;
1020
1021         QString sel(networkList->currentItem()->text());
1022
1023         switch (getNetworkDisabled(sel)) {
1024         case 0:
1025                 if (!enableRadioButton->isChecked())
1026                         enableRadioButton->setChecked(true);
1027                 return;
1028         case 1:
1029                 if (!disableRadioButton->isChecked())
1030                         disableRadioButton->setChecked(true);
1031                 return;
1032         }
1033 }
1034
1035
1036 void WpaGui::enableListedNetwork(bool enabled)
1037 {
1038         if (networkList->currentRow() < 0 || !enabled)
1039                 return;
1040
1041         QString sel(networkList->currentItem()->text());
1042
1043         if (getNetworkDisabled(sel) == 1)
1044                 enableNetwork(sel);
1045 }
1046
1047
1048 void WpaGui::disableListedNetwork(bool disabled)
1049 {
1050         if (networkList->currentRow() < 0 || !disabled)
1051                 return;
1052
1053         QString sel(networkList->currentItem()->text());
1054
1055         if (getNetworkDisabled(sel) == 0)
1056                 disableNetwork(sel);
1057 }
1058
1059
1060 void WpaGui::saveConfig()
1061 {
1062         char buf[10];
1063         size_t len;
1064
1065         len = sizeof(buf) - 1;
1066         ctrlRequest("SAVE_CONFIG", buf, &len);
1067
1068         buf[len] = '\0';
1069
1070         if (str_match(buf, "FAIL"))
1071                 QMessageBox::warning(this, "Failed to save configuration",
1072                                      "The configuration could not be saved.\n"
1073                                      "\n"
1074                                      "The update_config=1 configuration option\n"
1075                                      "must be used for configuration saving to\n"
1076                                      "be permitted.\n");
1077         else
1078                 QMessageBox::information(this, "Saved configuration",
1079                                          "The current configuration was saved."
1080                                          "\n");
1081 }
1082
1083
1084 void WpaGui::selectAdapter( const QString & sel )
1085 {
1086         if (openCtrlConnection(sel.toAscii().constData()) < 0)
1087                 printf("Failed to open control connection to "
1088                        "wpa_supplicant.\n");
1089         updateStatus();
1090         updateNetworks();
1091 }
1092
1093
1094 void WpaGui::createTrayIcon()
1095 {
1096         tray_icon = new QSystemTrayIcon(this);
1097         tray_icon->setToolTip(qAppName() + " - wpa_supplicant user interface");
1098         tray_icon->setIcon(QIcon(":/icons/wpa_gui.svg"));
1099
1100         connect(tray_icon,
1101                 SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
1102                 this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
1103
1104         ackTrayIcon = false;
1105
1106         tray_menu = new QMenu(this);
1107
1108         disconnectAction = new QAction("&Disconnect", this);
1109         reconnectAction = new QAction("Re&connect", this);
1110         connect(disconnectAction, SIGNAL(triggered()), this,
1111                 SLOT(disconnect()));
1112         connect(reconnectAction, SIGNAL(triggered()), this,
1113                 SLOT(connectB()));
1114         tray_menu->addAction(disconnectAction);
1115         tray_menu->addAction(reconnectAction);
1116         tray_menu->addSeparator();
1117
1118         eventAction = new QAction("&Event History", this);
1119         scanAction = new QAction("Scan &Results", this);
1120         statAction = new QAction("S&tatus", this);
1121         connect(eventAction, SIGNAL(triggered()), this, SLOT(eventHistory()));
1122         connect(scanAction, SIGNAL(triggered()), this, SLOT(scan()));
1123         connect(statAction, SIGNAL(triggered()), this, SLOT(showTrayStatus()));
1124         tray_menu->addAction(eventAction);
1125         tray_menu->addAction(scanAction);
1126         tray_menu->addAction(statAction);
1127         tray_menu->addSeparator();
1128
1129         showAction = new QAction("&Show Window", this);
1130         hideAction = new QAction("&Hide Window", this);
1131         quitAction = new QAction("&Quit", this);
1132         connect(showAction, SIGNAL(triggered()), this, SLOT(show()));
1133         connect(hideAction, SIGNAL(triggered()), this, SLOT(hide()));
1134         connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
1135         tray_menu->addAction(showAction);
1136         tray_menu->addAction(hideAction);
1137         tray_menu->addSeparator();
1138         tray_menu->addAction(quitAction);
1139
1140         tray_icon->setContextMenu(tray_menu);
1141 }
1142
1143
1144 void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec,
1145                              const QString & msg)
1146 {
1147         if (!QSystemTrayIcon::supportsMessages())
1148                 return;
1149
1150         if (isVisible() || !tray_icon || !tray_icon->isVisible())
1151                 return;
1152
1153         tray_icon->showMessage(qAppName(), msg, type, sec * 1000);
1154 }
1155
1156
1157 void WpaGui::trayActivated(QSystemTrayIcon::ActivationReason how)
1158  {
1159         switch (how) {
1160         /* use close() here instead of hide() and allow the
1161          * custom closeEvent handler take care of children */
1162         case QSystemTrayIcon::Trigger:
1163                 if (isVisible())
1164                         close();
1165                 else
1166                         show();
1167                 break;
1168         case QSystemTrayIcon::MiddleClick:
1169                 showTrayStatus();
1170                 break;
1171         default:
1172                 break;
1173         }
1174 }
1175
1176
1177 void WpaGui::showTrayStatus()
1178 {
1179         char buf[2048];
1180         size_t len;
1181
1182         len = sizeof(buf) - 1;
1183         if (ctrlRequest("STATUS", buf, &len) < 0)
1184                 return;
1185         buf[len] = '\0';
1186
1187         QString msg, status(buf);
1188
1189         QStringList lines = status.split(QRegExp("\\n"));
1190         for (QStringList::Iterator it = lines.begin();
1191              it != lines.end(); it++) {
1192                 int pos = (*it).indexOf('=') + 1;
1193                 if (pos < 1)
1194                         continue;
1195
1196                 if ((*it).startsWith("bssid="))
1197                         msg.append("BSSID:\t" + (*it).mid(pos) + "\n");
1198                 else if ((*it).startsWith("ssid="))
1199                         msg.append("SSID: \t" + (*it).mid(pos) + "\n");
1200                 else if ((*it).startsWith("pairwise_cipher="))
1201                         msg.append("PAIR: \t" + (*it).mid(pos) + "\n");
1202                 else if ((*it).startsWith("group_cipher="))
1203                         msg.append("GROUP:\t" + (*it).mid(pos) + "\n");
1204                 else if ((*it).startsWith("key_mgmt="))
1205                         msg.append("AUTH: \t" + (*it).mid(pos) + "\n");
1206                 else if ((*it).startsWith("ip_address="))
1207                         msg.append("IP:   \t" + (*it).mid(pos) + "\n");
1208         }
1209
1210         showTrayMessage(QSystemTrayIcon::Information, 10, msg);
1211 }
1212
1213 void WpaGui::fileExit()
1214 {
1215         if (tray_icon)
1216                 tray_icon->hide();
1217
1218         close();
1219 }
1220
1221
1222 void WpaGui::closeEvent(QCloseEvent *event)
1223 {
1224         if (eh) {
1225                 eh->close();
1226                 delete eh;
1227                 eh = NULL;
1228         }
1229
1230         if (scanres) {
1231                 scanres->close();
1232                 delete scanres;
1233                 scanres = NULL;
1234         }
1235
1236         if (udr) {
1237                 udr->close();
1238                 delete udr;
1239                 udr = NULL;
1240         }
1241
1242         if (tray_icon && tray_icon->isVisible()) {
1243                 /* give user a visual hint that the tray icon exists */
1244                 if (QSystemTrayIcon::supportsMessages()) {
1245                         hide();
1246                         QTimer::singleShot(1 * 1000, this,
1247                                            SLOT(showTrayStatus()));
1248                 } else if (!ackTrayIcon) {
1249                         QMessageBox::information(this, qAppName() + " systray",
1250                                                  "The program will keep "
1251                                                  "running in the system tray."
1252                                                  " To terminate the program, "
1253                                                  "choose <b>Quit</b> in the "
1254                                                  "context menu of the system "
1255                                                  "tray icon.");
1256                         ackTrayIcon = true;
1257                         hide();
1258                 }
1259                 event->ignore();
1260                 return;
1261         }
1262
1263         event->accept();
1264 }