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