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