3 * Copyright 2014 Roy Marples <roy@marples.name>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <QFormLayout>
32 #include <QGridLayout>
33 #include <QHBoxLayout>
37 #include <QMessageBox>
39 #include <QPushButton>
40 #include <QStandardItemModel>
41 #include <QVBoxLayout>
46 #include "dhcpcd-preferences.h"
47 #include "dhcpcd-ipv4validator.h"
48 #include "dhcpcd-qt.h"
49 #include "dhcpcd-wi.h"
51 DhcpcdPreferences::DhcpcdPreferences(DhcpcdQt *parent)
54 this->parent = parent;
56 setWindowIcon(DhcpcdQt::getIcon("status", "network-transmit-receive"));
57 setWindowTitle(tr("Network Preferences"));
58 QPoint p = QCursor::pos();
63 eWhat = eBlock = NULL;
66 QVBoxLayout *layout = new QVBoxLayout();
68 QGridLayout *topLayout = new QGridLayout();
69 QLabel *conf = new QLabel(tr("Configure:"));
70 topLayout->addWidget(conf, 0, 0);
72 what = new QComboBox();
73 connect(what, SIGNAL(currentIndexChanged(const QString &)),
74 this, SLOT(listBlocks(const QString &)));
75 topLayout->addWidget(what, 0, 1);
76 blocks = new QComboBox();
77 connect(blocks, SIGNAL(currentIndexChanged(const QString &)),
78 this, SLOT(showBlock(const QString &)));
79 topLayout->addWidget(blocks, 0, 2);
81 topLayout->setColumnStretch(2, 10);
83 QWidget *topBox = new QWidget();
84 topBox->setLayout(topLayout);
85 layout->addWidget(topBox);
86 QFrame *topSep = new QFrame();
87 topSep->setFrameShape(QFrame::HLine);
88 topSep->setFrameShadow(QFrame::Sunken);
89 layout->addWidget(topSep);
91 autoConf = new QCheckBox(tr("Automatically configure empty options"));
92 autoConf->setChecked(true);
93 layout->addWidget(autoConf);
95 DhcpcdIPv4Validator *v =
96 new DhcpcdIPv4Validator(DhcpcdIPv4Validator::Plain, this);
97 DhcpcdIPv4Validator *vc =
98 new DhcpcdIPv4Validator(DhcpcdIPv4Validator::CIDR, this);
99 DhcpcdIPv4Validator *vs =
100 new DhcpcdIPv4Validator(DhcpcdIPv4Validator::Spaced, this);
101 ip = new QLineEdit();
102 ip->setValidator(vc);
103 router = new QLineEdit();
104 router->setValidator(v);
105 rdnss = new QLineEdit();
106 rdnss->setValidator(vs);
107 dnssl = new QLineEdit();
108 #if defined(__NetBSD__) || (__OpenBSD__)
109 dnssl->setMaxLength(1024);
111 dnssl->setMaxLength(256);
113 QFormLayout *ipLayout = new QFormLayout();
114 ipLayout->addRow(tr("IP Address:"), ip);
115 ipLayout->addRow(tr("Router:"), router);
116 ipLayout->addRow(tr("DNS Servers:"), rdnss);
117 ipLayout->addRow(tr("DNS Search:"), dnssl);
118 ipSetup = new QWidget();
119 ipSetup->setLayout(ipLayout);
120 layout->addWidget(ipSetup);
122 QHBoxLayout *buttonLayout = new QHBoxLayout();
123 clear = new QPushButton(tr("&Clear"));
124 clear->setIcon(QIcon::fromTheme("edit-clear"));
125 buttonLayout->addWidget(clear);
126 QPushButton *rebind = new QPushButton(tr("&Rebind"));
127 rebind->setIcon(QIcon::fromTheme("application-x-executable"));
128 buttonLayout->addWidget(rebind);
129 QPushButton *close = new QPushButton(tr("&Close"));
130 close->setIcon(QIcon::fromTheme("window-close"));
131 buttonLayout->addWidget(close);
132 QWidget *buttons = new QWidget();
133 buttons->setLayout(buttonLayout);
134 layout->addWidget(buttons);
136 QIcon wired = DhcpcdQt::getIcon("devices", "network-wired");
137 what->addItem(wired, tr("interface"));
138 QIcon wireless = DhcpcdQt::getIcon("devices", "network-wireless");
139 what->addItem(wireless, tr("SSID"));
141 connect(clear, SIGNAL(clicked()), this, SLOT(clearConfig()));
142 connect(rebind, SIGNAL(clicked()), this, SLOT(rebind()));
143 connect(close, SIGNAL(clicked()), this, SLOT(tryClose()));
147 autoConf->setEnabled(false);
148 ipSetup->setEnabled(false);
149 clear->setEnabled(false);
151 DHCPCD_CONNECTION *con = parent->getConnection();
152 if (!dhcpcd_config_writeable(con))
153 QMessageBox::warning(this, tr("Not writeable"),
154 tr("The dhcpcd configuration file is not writeable\n\n%1")
155 .arg(dhcpcd_cffile(con)));
158 DhcpcdPreferences::~DhcpcdPreferences()
167 void DhcpcdPreferences::closeEvent(QCloseEvent *e)
170 parent->dialogClosed(this);
171 QDialog::closeEvent(e);
174 void DhcpcdPreferences::listBlocks(const QString &txt)
179 /* clear and then disconnect so we trigger a save */
181 blocks->disconnect(this);
184 eWhat = strdup(txt.toLower().toAscii());
186 list = dhcpcd_config_blocks(parent->getConnection(),
187 txt.toLower().toAscii());
189 if (txt == "interface") {
192 blocks->addItem(tr("Select an interface"));
193 ifaces = dhcpcd_interface_names_sorted(parent->getConnection());
194 for (i = ifaces; i && *i; i++) {
195 for (lp = list; lp && *lp; lp++) {
196 if (strcmp(*i, *lp) == 0)
199 icon = DhcpcdQt::getIcon("actions",
201 "document-save" : "document-new");
202 blocks->addItem(icon, *i);
204 dhcpcd_freev(ifaces);
206 QList<DhcpcdWi *> *wis = parent->getWis();
208 blocks->addItem(tr("Select a SSID"));
209 for (int i = 0; i < wis->size(); i++) {
210 DHCPCD_WI_SCAN *scan;
211 DhcpcdWi *wi = wis->at(i);
213 for (scan = wi->getScans(); scan; scan = scan->next) {
214 for (lp = list; lp && *lp; lp++) {
215 if (strcmp(scan->ssid, *lp) == 0)
218 icon = DhcpcdQt::getIcon("actions",
220 "document-save" : "document-new");
221 blocks->addItem(icon, scan->ssid);
228 /* Now make the 1st item unselectable and reconnect */
229 qobject_cast<QStandardItemModel *>
230 (blocks->model())->item(0)->setEnabled(false);
231 connect(blocks, SIGNAL(currentIndexChanged(const QString &)),
232 this, SLOT(showBlock(const QString &)));
236 void DhcpcdPreferences::clearConfig()
239 autoConf->setChecked(true);
246 void DhcpcdPreferences::showConfig()
251 if ((val = dhcpcd_config_get_static(config, "ip_address=")) != NULL)
254 a = !((val = dhcpcd_config_get(config, "inform")) == NULL &&
255 (iface && iface->flags & IFF_POINTOPOINT));
256 autoConf->setChecked(a);
258 router->setText(dhcpcd_config_get_static(config, "routers="));
259 rdnss->setText(dhcpcd_config_get_static(config,"domain_name_servers="));
260 dnssl->setText(dhcpcd_config_get_static(config, "domain_search="));
263 bool DhcpcdPreferences::changedConfig()
268 if ((val = dhcpcd_config_get_static(config, "ip_address=")) != NULL)
271 a = !((val = dhcpcd_config_get(config, "inform")) == NULL &&
272 (iface && iface->flags & IFF_POINTOPOINT));
273 if (autoConf->isChecked() != a)
275 if (ip->text().compare(val))
277 val = dhcpcd_config_get_static(config, "routers=");
278 if (router->text().compare(val))
280 val = dhcpcd_config_get_static(config, "domain_name_servers=");
281 if (rdnss->text().compare(val))
283 val = dhcpcd_config_get_static(config, "domain_search=");
284 if (rdnss->text().compare(val))
290 const char *DhcpcdPreferences::getString(QLineEdit *le)
292 if (le->text().isEmpty())
294 return le->text().trimmed().toAscii();
297 bool DhcpcdPreferences::setOption(const char *opt, const char *val, bool *ret)
299 if (opt[strlen(opt) - 1] == '=') {
300 if (!dhcpcd_config_set_static(&config, opt, val))
301 qCritical("dhcpcd_config_set_static: %s",
306 if (!dhcpcd_config_set(&config, opt, val))
307 qCritical("dhcpcd_config_set: %s",
319 bool DhcpcdPreferences::makeConfig()
321 const char ns[] = "", *val;
324 a = autoConf->isChecked();
326 if (iface && iface->flags & IFF_POINTOPOINT)
327 setOption("ip_address=", a ? NULL : ns, &ret);
330 setOption("inform", a ? val : NULL, &ret);
331 setOption("ip_address=", a ? NULL : val, &ret);
334 val = getString(router);
335 setOption("routers=", val, &ret);
337 val = getString(rdnss);
338 setOption("domain_name_servers=", val, &ret);
340 val = getString(dnssl);
341 setOption("domain_search=", val, &ret);
346 bool DhcpcdPreferences::writeConfig(bool *cancel)
349 switch (QMessageBox::question(this, tr("Save Configuration"),
350 tr("Do you want to save your changes?"),
351 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel)) {
352 case QMessageBox::Cancel:
355 case QMessageBox::Discard:
363 DHCPCD_CONNECTION *con = parent->getConnection();
365 qCritical("failed to make config");
368 if (!dhcpcd_config_write(con, eWhat, eBlock, config)) {
369 qCritical("dhcpcd_config_write: %s", strerror(errno));
370 QMessageBox::critical(parent,
371 tr("Failed to write configuration"),
372 tr("Failed to write configuration:\n\n%1: %2")
373 .arg(dhcpcd_cffile(con))
374 .arg(strerror(errno)));
380 /* Reload our config if there is a problem */
381 config = dhcpcd_config_read(con, eWhat, eBlock);
385 void DhcpcdPreferences::showBlock(const QString &txt)
389 if (changedConfig()) {
391 if (!writeConfig(&cancel))
399 eBlock = strdup(txt.toAscii());
401 dhcpcd_config_free(config);
403 DHCPCD_CONNECTION *con = parent->getConnection();
405 if (eBlock && eWhat) {
406 if (strcmp(eWhat, "interface") == 0)
407 iface = dhcpcd_get_if(con, eBlock, "link");
408 ip->setEnabled(iface == NULL ||
409 !(iface->flags & IFF_POINTOPOINT));
411 config = dhcpcd_config_read(con, eWhat, eBlock);
412 if (config == NULL && errno) {
416 qCritical("dhcpcd_config_read: %s", s);
417 QMessageBox::critical(this,
418 tr("Error reading configuration"),
419 tr("Error reading: ") + dhcpcd_cffile(con) +
426 bool enabled = dhcpcd_config_writeable(con) && eBlock != NULL;
427 autoConf->setEnabled(enabled);
428 ipSetup->setEnabled(enabled);
429 clear->setEnabled(enabled);
432 bool DhcpcdPreferences::tryRebind(const char *ifname)
435 if (dhcpcd_rebind(parent->getConnection(), ifname) == 0)
438 qCritical("dhcpcd_rebind: %s", strerror(errno));
439 QMessageBox::critical(this,
441 ifname ? tr("Failed to rebind interface %1: %2")
442 .arg(ifname).arg(strerror(errno)) :
443 tr("Failed to rebind: %1")
444 .arg(strerror(errno)));
448 void DhcpcdPreferences::rebind()
451 if (changedConfig()) {
453 writeConfig(&cancel);
458 DHCPCD_CONNECTION *con = parent->getConnection();
462 if (eBlock == NULL || strcmp(eWhat, "interface") == 0) {
463 worked = tryRebind(iface ? iface->ifname : NULL);
469 for (i = dhcpcd_interfaces(con); i; i = i->next) {
470 if (strcmp(i->type, "link") == 0 &&
471 (i->ssid && strcmp(i->ssid, eBlock) == 0))
474 if (!tryRebind(i->ifname))
479 QMessageBox::information(this,
480 tr("No matching interface"),
481 tr("No interface is bound to this SSID to rebind"));
490 void DhcpcdPreferences::tryClose()
493 if (changedConfig()) {
495 writeConfig(&cancel);