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