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