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