]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
update to 0.6.8
[FreeBSD/FreeBSD.git] / contrib / 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
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(helpIndexAction, SIGNAL(activated()), this, SLOT(helpIndex()));
37         connect(helpContentsAction, SIGNAL(activated()), this,
38                 SLOT(helpContents()));
39         connect(helpAboutAction, SIGNAL(activated()), this, SLOT(helpAbout()));
40         connect(fileExitAction, SIGNAL(activated()), this, SLOT(close()));
41         connect(disconnectButton, SIGNAL(clicked()), this, SLOT(disconnect()));
42         connect(scanButton, SIGNAL(clicked()), this, SLOT(scan()));
43         connect(connectButton, SIGNAL(clicked()), this, SLOT(connectB()));
44         connect(fileEventHistoryAction, SIGNAL(activated()), this,
45                 SLOT(eventHistory()));
46         connect(networkSelect, SIGNAL(activated(const QString&)), this,
47                 SLOT(selectNetwork(const QString&)));
48         connect(fileEdit_networkAction, SIGNAL(activated()), this,
49                 SLOT(editNetwork()));
50         connect(fileAdd_NetworkAction, SIGNAL(activated()), this,
51                 SLOT(addNetwork()));
52         connect(adapterSelect, SIGNAL(activated(const QString&)), this,
53                 SLOT(selectAdapter(const QString&)));
54
55         eh = NULL;
56         scanres = NULL;
57         udr = NULL;
58         ctrl_iface = NULL;
59         ctrl_conn = NULL;
60         monitor_conn = NULL;
61         msgNotifier = NULL;
62         ctrl_iface_dir = strdup("/var/run/wpa_supplicant");
63
64         parse_argv();
65
66         textStatus->setText("connecting to wpa_supplicant");
67         timer = new QTimer(this);
68         connect(timer, SIGNAL(timeout()), SLOT(ping()));
69         timer->start(1000, FALSE);
70
71         if (openCtrlConnection(ctrl_iface) < 0) {
72                 printf("Failed to open control connection to "
73                        "wpa_supplicant.\n");
74         }
75
76         updateStatus();
77         networkMayHaveChanged = true;
78         updateNetworks();
79 }
80
81
82 WpaGui::~WpaGui()
83 {
84         delete msgNotifier;
85
86         if (monitor_conn) {
87                 wpa_ctrl_detach(monitor_conn);
88                 wpa_ctrl_close(monitor_conn);
89                 monitor_conn = NULL;
90         }
91         if (ctrl_conn) {
92                 wpa_ctrl_close(ctrl_conn);
93                 ctrl_conn = NULL;
94         }
95
96         if (eh) {
97                 eh->close();
98                 delete eh;
99                 eh = NULL;
100         }
101
102         if (scanres) {
103                 scanres->close();
104                 delete scanres;
105                 scanres = NULL;
106         }
107
108         if (udr) {
109                 udr->close();
110                 delete udr;
111                 udr = NULL;
112         }
113
114         free(ctrl_iface);
115         ctrl_iface = NULL;
116
117         free(ctrl_iface_dir);
118         ctrl_iface_dir = NULL;
119 }
120
121
122 void WpaGui::languageChange()
123 {
124         retranslateUi(this);
125 }
126
127
128 void WpaGui::parse_argv()
129 {
130         int c;
131         for (;;) {
132                 c = getopt(qApp->argc(), qApp->argv(), "i:p:");
133                 if (c < 0)
134                         break;
135                 switch (c) {
136                 case 'i':
137                         free(ctrl_iface);
138                         ctrl_iface = strdup(optarg);
139                         break;
140                 case 'p':
141                         free(ctrl_iface_dir);
142                         ctrl_iface_dir = strdup(optarg);
143                         break;
144                 }
145         }
146 }
147
148
149 int WpaGui::openCtrlConnection(const char *ifname)
150 {
151         char *cfile;
152         int flen;
153         char buf[2048], *pos, *pos2;
154         size_t len;
155
156         if (ifname) {
157                 if (ifname != ctrl_iface) {
158                         free(ctrl_iface);
159                         ctrl_iface = strdup(ifname);
160                 }
161         } else {
162 #ifdef CONFIG_CTRL_IFACE_UDP
163                 free(ctrl_iface);
164                 ctrl_iface = strdup("udp");
165 #endif /* CONFIG_CTRL_IFACE_UDP */
166 #ifdef CONFIG_CTRL_IFACE_UNIX
167                 struct dirent *dent;
168                 DIR *dir = opendir(ctrl_iface_dir);
169                 free(ctrl_iface);
170                 ctrl_iface = NULL;
171                 if (dir) {
172                         while ((dent = readdir(dir))) {
173 #ifdef _DIRENT_HAVE_D_TYPE
174                                 /* Skip the file if it is not a socket.
175                                  * Also accept DT_UNKNOWN (0) in case
176                                  * the C library or underlying file
177                                  * system does not support d_type. */
178                                 if (dent->d_type != DT_SOCK &&
179                                     dent->d_type != DT_UNKNOWN)
180                                         continue;
181 #endif /* _DIRENT_HAVE_D_TYPE */
182
183                                 if (strcmp(dent->d_name, ".") == 0 ||
184                                     strcmp(dent->d_name, "..") == 0)
185                                         continue;
186                                 printf("Selected interface '%s'\n",
187                                        dent->d_name);
188                                 ctrl_iface = strdup(dent->d_name);
189                                 break;
190                         }
191                         closedir(dir);
192                 }
193 #endif /* CONFIG_CTRL_IFACE_UNIX */
194 #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
195                 struct wpa_ctrl *ctrl;
196                 int ret;
197
198                 free(ctrl_iface);
199                 ctrl_iface = NULL;
200
201                 ctrl = wpa_ctrl_open(NULL);
202                 if (ctrl) {
203                         len = sizeof(buf) - 1;
204                         ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf,
205                                                &len, NULL);
206                         if (ret >= 0) {
207                                 buf[len] = '\0';
208                                 pos = strchr(buf, '\n');
209                                 if (pos)
210                                         *pos = '\0';
211                                 ctrl_iface = strdup(buf);
212                         }
213                         wpa_ctrl_close(ctrl);
214                 }
215 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
216         }
217
218         if (ctrl_iface == NULL)
219                 return -1;
220
221 #ifdef CONFIG_CTRL_IFACE_UNIX
222         flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2;
223         cfile = (char *) malloc(flen);
224         if (cfile == NULL)
225                 return -1;
226         snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface);
227 #else /* CONFIG_CTRL_IFACE_UNIX */
228         flen = strlen(ctrl_iface) + 1;
229         cfile = (char *) malloc(flen);
230         if (cfile == NULL)
231                 return -1;
232         snprintf(cfile, flen, "%s", ctrl_iface);
233 #endif /* CONFIG_CTRL_IFACE_UNIX */
234
235         if (ctrl_conn) {
236                 wpa_ctrl_close(ctrl_conn);
237                 ctrl_conn = NULL;
238         }
239
240         if (monitor_conn) {
241                 delete msgNotifier;
242                 msgNotifier = NULL;
243                 wpa_ctrl_detach(monitor_conn);
244                 wpa_ctrl_close(monitor_conn);
245                 monitor_conn = NULL;
246         }
247
248         printf("Trying to connect to '%s'\n", cfile);
249         ctrl_conn = wpa_ctrl_open(cfile);
250         if (ctrl_conn == NULL) {
251                 free(cfile);
252                 return -1;
253         }
254         monitor_conn = wpa_ctrl_open(cfile);
255         free(cfile);
256         if (monitor_conn == NULL) {
257                 wpa_ctrl_close(ctrl_conn);
258                 return -1;
259         }
260         if (wpa_ctrl_attach(monitor_conn)) {
261                 printf("Failed to attach to wpa_supplicant\n");
262                 wpa_ctrl_close(monitor_conn);
263                 monitor_conn = NULL;
264                 wpa_ctrl_close(ctrl_conn);
265                 ctrl_conn = NULL;
266                 return -1;
267         }
268
269 #if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
270         msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn),
271                                           QSocketNotifier::Read, this);
272         connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs()));
273 #endif
274
275         adapterSelect->clear();
276         adapterSelect->insertItem(ctrl_iface);
277         adapterSelect->setCurrentItem(0);
278
279         len = sizeof(buf) - 1;
280         if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >=
281             0) {
282                 buf[len] = '\0';
283                 pos = buf;
284                 while (*pos) {
285                         pos2 = strchr(pos, '\n');
286                         if (pos2)
287                                 *pos2 = '\0';
288                         if (strcmp(pos, ctrl_iface) != 0)
289                                 adapterSelect->insertItem(pos);
290                         if (pos2)
291                                 pos = pos2 + 1;
292                         else
293                                 break;
294                 }
295         }
296
297         return 0;
298 }
299
300
301 static void wpa_gui_msg_cb(char *msg, size_t)
302 {
303         /* This should not happen anymore since two control connections are
304          * used. */
305         printf("missed message: %s\n", msg);
306 }
307
308
309 int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen)
310 {
311         int ret;
312
313         if (ctrl_conn == NULL)
314                 return -3;
315         ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen,
316                                wpa_gui_msg_cb);
317         if (ret == -2)
318                 printf("'%s' command timed out.\n", cmd);
319         else if (ret < 0)
320                 printf("'%s' command failed.\n", cmd);
321
322         return ret;
323 }
324
325
326 void WpaGui::updateStatus()
327 {
328         char buf[2048], *start, *end, *pos;
329         size_t len;
330
331         pingsToStatusUpdate = 10;
332
333         len = sizeof(buf) - 1;
334         if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) {
335                 textStatus->setText("Could not get status from "
336                                     "wpa_supplicant");
337                 textAuthentication->clear();
338                 textEncryption->clear();
339                 textSsid->clear();
340                 textBssid->clear();
341                 textIpAddress->clear();
342                 return;
343         }
344
345         buf[len] = '\0';
346
347         bool auth_updated = false, ssid_updated = false;
348         bool bssid_updated = false, ipaddr_updated = false;
349         bool status_updated = false;
350         char *pairwise_cipher = NULL, *group_cipher = NULL;
351
352         start = buf;
353         while (*start) {
354                 bool last = false;
355                 end = strchr(start, '\n');
356                 if (end == NULL) {
357                         last = true;
358                         end = start;
359                         while (end[0] && end[1])
360                                 end++;
361                 }
362                 *end = '\0';
363
364                 pos = strchr(start, '=');
365                 if (pos) {
366                         *pos++ = '\0';
367                         if (strcmp(start, "bssid") == 0) {
368                                 bssid_updated = true;
369                                 textBssid->setText(pos);
370                         } else if (strcmp(start, "ssid") == 0) {
371                                 ssid_updated = true;
372                                 textSsid->setText(pos);
373                         } else if (strcmp(start, "ip_address") == 0) {
374                                 ipaddr_updated = true;
375                                 textIpAddress->setText(pos);
376                         } else if (strcmp(start, "wpa_state") == 0) {
377                                 status_updated = true;
378                                 textStatus->setText(pos);
379                         } else if (strcmp(start, "key_mgmt") == 0) {
380                                 auth_updated = true;
381                                 textAuthentication->setText(pos);
382                                 /* TODO: could add EAP status to this */
383                         } else if (strcmp(start, "pairwise_cipher") == 0) {
384                                 pairwise_cipher = pos;
385                         } else if (strcmp(start, "group_cipher") == 0) {
386                                 group_cipher = pos;
387                         }
388                 }
389
390                 if (last)
391                         break;
392                 start = end + 1;
393         }
394
395         if (pairwise_cipher || group_cipher) {
396                 QString encr;
397                 if (pairwise_cipher && group_cipher &&
398                     strcmp(pairwise_cipher, group_cipher) != 0) {
399                         encr.append(pairwise_cipher);
400                         encr.append(" + ");
401                         encr.append(group_cipher);
402                 } else if (pairwise_cipher) {
403                         encr.append(pairwise_cipher);
404                 } else if (group_cipher) {
405                         encr.append(group_cipher);
406                         encr.append(" [group key only]");
407                 } else {
408                         encr.append("?");
409                 }
410                 textEncryption->setText(encr);
411         } else
412                 textEncryption->clear();
413
414         if (!status_updated)
415                 textStatus->clear();
416         if (!auth_updated)
417                 textAuthentication->clear();
418         if (!ssid_updated)
419                 textSsid->clear();
420         if (!bssid_updated)
421                 textBssid->clear();
422         if (!ipaddr_updated)
423                 textIpAddress->clear();
424 }
425
426
427 void WpaGui::updateNetworks()
428 {
429         char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
430         size_t len;
431         int first_active = -1;
432         bool selected = false;
433
434         if (!networkMayHaveChanged)
435                 return;
436
437         networkSelect->clear();
438
439         if (ctrl_conn == NULL)
440                 return;
441
442         len = sizeof(buf) - 1;
443         if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
444                 return;
445
446         buf[len] = '\0';
447         start = strchr(buf, '\n');
448         if (start == NULL)
449                 return;
450         start++;
451
452         while (*start) {
453                 bool last = false;
454                 end = strchr(start, '\n');
455                 if (end == NULL) {
456                         last = true;
457                         end = start;
458                         while (end[0] && end[1])
459                                 end++;
460                 }
461                 *end = '\0';
462
463                 id = start;
464                 ssid = strchr(id, '\t');
465                 if (ssid == NULL)
466                         break;
467                 *ssid++ = '\0';
468                 bssid = strchr(ssid, '\t');
469                 if (bssid == NULL)
470                         break;
471                 *bssid++ = '\0';
472                 flags = strchr(bssid, '\t');
473                 if (flags == NULL)
474                         break;
475                 *flags++ = '\0';
476
477                 QString network(id);
478                 network.append(": ");
479                 network.append(ssid);
480                 networkSelect->insertItem(network);
481
482                 if (strstr(flags, "[CURRENT]")) {
483                         networkSelect->setCurrentItem(networkSelect->count() -
484                                                       1);
485                         selected = true;
486                 } else if (first_active < 0 &&
487                            strstr(flags, "[DISABLED]") == NULL)
488                         first_active = networkSelect->count() - 1;
489
490                 if (last)
491                         break;
492                 start = end + 1;
493         }
494
495         if (!selected && first_active >= 0)
496                 networkSelect->setCurrentItem(first_active);
497
498         networkMayHaveChanged = false;
499 }
500
501
502 void WpaGui::helpIndex()
503 {
504         printf("helpIndex\n");
505 }
506
507
508 void WpaGui::helpContents()
509 {
510         printf("helpContents\n");
511 }
512
513
514 void WpaGui::helpAbout()
515 {
516         QMessageBox::about(this, "wpa_gui for wpa_supplicant",
517                            "Copyright (c) 2003-2008,\n"
518                            "Jouni Malinen <j@w1.fi>\n"
519                            "and contributors.\n"
520                            "\n"
521                            "This program is free software. You can\n"
522                            "distribute it and/or modify it under the terms "
523                            "of\n"
524                            "the GNU General Public License version 2.\n"
525                            "\n"
526                            "Alternatively, this software may be distributed\n"
527                            "under the terms of the BSD license.\n"
528                            "\n"
529                            "This product includes software developed\n"
530                            "by the OpenSSL Project for use in the\n"
531                            "OpenSSL Toolkit (http://www.openssl.org/)\n");
532 }
533
534
535 void WpaGui::disconnect()
536 {
537         char reply[10];
538         size_t reply_len = sizeof(reply);
539         ctrlRequest("DISCONNECT", reply, &reply_len);
540 }
541
542
543 void WpaGui::scan()
544 {
545         if (scanres) {
546                 scanres->close();
547                 delete scanres;
548         }
549
550         scanres = new ScanResults();
551         if (scanres == NULL)
552                 return;
553         scanres->setWpaGui(this);
554         scanres->show();
555         scanres->exec();
556 }
557
558
559 void WpaGui::eventHistory()
560 {
561         if (eh) {
562                 eh->close();
563                 delete eh;
564         }
565
566         eh = new EventHistory();
567         if (eh == NULL)
568                 return;
569         eh->addEvents(msgs);
570         eh->show();
571         eh->exec();
572 }
573
574
575 void WpaGui::ping()
576 {
577         char buf[10];
578         size_t len;
579
580 #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
581         /*
582          * QSocketNotifier cannot be used with Windows named pipes, so use a
583          * timer to check for received messages for now. This could be
584          * optimized be doing something specific to named pipes or Windows
585          * events, but it is not clear what would be the best way of doing that
586          * in Qt.
587          */
588         receiveMsgs();
589 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
590
591         if (scanres && !scanres->isVisible()) {
592                 delete scanres;
593                 scanres = NULL;
594         }
595
596         if (eh && !eh->isVisible()) {
597                 delete eh;
598                 eh = NULL;
599         }
600
601         if (udr && !udr->isVisible()) {
602                 delete udr;
603                 udr = NULL;
604         }
605
606         len = sizeof(buf) - 1;
607         if (ctrlRequest("PING", buf, &len) < 0) {
608                 printf("PING failed - trying to reconnect\n");
609                 if (openCtrlConnection(ctrl_iface) >= 0) {
610                         printf("Reconnected successfully\n");
611                         pingsToStatusUpdate = 0;
612                 }
613         }
614
615         pingsToStatusUpdate--;
616         if (pingsToStatusUpdate <= 0) {
617                 updateStatus();
618                 updateNetworks();
619         }
620 }
621
622
623 static int str_match(const char *a, const char *b)
624 {
625         return strncmp(a, b, strlen(b)) == 0;
626 }
627
628
629 void WpaGui::processMsg(char *msg)
630 {
631         char *pos = msg, *pos2;
632         int priority = 2;
633
634         if (*pos == '<') {
635                 /* skip priority */
636                 pos++;
637                 priority = atoi(pos);
638                 pos = strchr(pos, '>');
639                 if (pos)
640                         pos++;
641                 else
642                         pos = msg;
643         }
644
645         WpaMsg wm(pos, priority);
646         if (eh)
647                 eh->addEvent(wm);
648         msgs.append(wm);
649         while (msgs.count() > 100)
650                 msgs.pop_front();
651
652         /* Update last message with truncated version of the event */
653         if (strncmp(pos, "CTRL-", 5) == 0) {
654                 pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' ');
655                 if (pos2)
656                         pos2++;
657                 else
658                         pos2 = pos;
659         } else
660                 pos2 = pos;
661         QString lastmsg = pos2;
662         lastmsg.truncate(40);
663         textLastMessage->setText(lastmsg);
664
665         pingsToStatusUpdate = 0;
666         networkMayHaveChanged = true;
667
668         if (str_match(pos, WPA_CTRL_REQ))
669                 processCtrlReq(pos + strlen(WPA_CTRL_REQ));
670 }
671
672
673 void WpaGui::processCtrlReq(const char *req)
674 {
675         if (udr) {
676                 udr->close();
677                 delete udr;
678         }
679         udr = new UserDataRequest();
680         if (udr == NULL)
681                 return;
682         if (udr->setParams(this, req) < 0) {
683                 delete udr;
684                 udr = NULL;
685                 return;
686         }
687         udr->show();
688         udr->exec();
689 }
690
691
692 void WpaGui::receiveMsgs()
693 {
694         char buf[256];
695         size_t len;
696
697         while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) {
698                 len = sizeof(buf) - 1;
699                 if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) {
700                         buf[len] = '\0';
701                         processMsg(buf);
702                 }
703         }
704 }
705
706
707 void WpaGui::connectB()
708 {
709         char reply[10];
710         size_t reply_len = sizeof(reply);
711         ctrlRequest("REASSOCIATE", reply, &reply_len);
712 }
713
714
715 void WpaGui::selectNetwork( const QString &sel )
716 {
717         QString cmd(sel);
718         char reply[10];
719         size_t reply_len = sizeof(reply);
720
721         int pos = cmd.find(':');
722         if (pos < 0) {
723                 printf("Invalid selectNetwork '%s'\n", cmd.ascii());
724                 return;
725         }
726         cmd.truncate(pos);
727         cmd.prepend("SELECT_NETWORK ");
728         ctrlRequest(cmd.ascii(), reply, &reply_len);
729 }
730
731
732 void WpaGui::editNetwork()
733 {
734         QString sel(networkSelect->currentText());
735         int pos = sel.find(':');
736         if (pos < 0) {
737                 printf("Invalid selectNetwork '%s'\n", sel.ascii());
738                 return;
739         }
740         sel.truncate(pos);
741
742         NetworkConfig *nc = new NetworkConfig();
743         if (nc == NULL)
744                 return;
745         nc->setWpaGui(this);
746
747         nc->paramsFromConfig(sel.toInt());
748         nc->show();
749         nc->exec();
750 }
751
752
753 void WpaGui::triggerUpdate()
754 {
755         updateStatus();
756         networkMayHaveChanged = true;
757         updateNetworks();
758 }
759
760
761 void WpaGui::addNetwork()
762 {
763         NetworkConfig *nc = new NetworkConfig();
764         if (nc == NULL)
765                 return;
766         nc->setWpaGui(this);
767         nc->newNetwork();
768         nc->show();
769         nc->exec();
770 }
771
772
773 void WpaGui::selectAdapter( const QString & sel )
774 {
775         if (openCtrlConnection(sel.ascii()) < 0)
776                 printf("Failed to open control connection to "
777                        "wpa_supplicant.\n");
778         updateStatus();
779         updateNetworks();
780 }