File indexing completed on 2024-12-01 08:10:02
0001 /* 0002 SPDX-FileCopyrightText: 2013 Lukas Tinkl <ltinkl@redhat.com> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "ipv6widget.h" 0008 #include "intdelegate.h" 0009 #include "ipv6delegate.h" 0010 #include "ui_ipv6.h" 0011 0012 #include <NetworkManagerQt/Manager> 0013 0014 #include <QDialog> 0015 #include <QDialogButtonBox> 0016 #include <QItemSelection> 0017 #include <QNetworkAddressEntry> 0018 #include <QStandardItemModel> 0019 0020 #include <KEditListWidget> 0021 #include <KLocalizedString> 0022 0023 quint32 suggestNetmask(Q_IPV6ADDR ip) 0024 { 0025 Q_UNUSED(ip); 0026 0027 /* 0028 TODO: find out common IPv6-netmasks and make a complete function 0029 0030 */ 0031 quint32 netmask = 64; 0032 0033 return netmask; 0034 } 0035 0036 class IPv6Widget::Private 0037 { 0038 public: 0039 Private() 0040 : model(0, 3) 0041 { 0042 auto headerItem = new QStandardItem(i18nc("Header text for IPv6 address", "Address")); 0043 model.setHorizontalHeaderItem(0, headerItem); 0044 headerItem = new QStandardItem(i18nc("Header text for IPv6 prefix", "Prefix")); 0045 model.setHorizontalHeaderItem(1, headerItem); 0046 headerItem = new QStandardItem(i18nc("Header text for IPv6 gateway", "Gateway")); 0047 model.setHorizontalHeaderItem(2, headerItem); 0048 } 0049 QStandardItemModel model; 0050 }; 0051 0052 IPv6Widget::IPv6Widget(const NetworkManager::Setting::Ptr &setting, QWidget *parent, Qt::WindowFlags f) 0053 : SettingWidget(setting, parent, f) 0054 , m_ui(new Ui::IPv6Widget) 0055 , d(new IPv6Widget::Private()) 0056 { 0057 m_ui->setupUi(this); 0058 0059 m_ui->tableViewAddresses->setModel(&d->model); 0060 m_ui->tableViewAddresses->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); 0061 m_ui->tableViewAddresses->horizontalHeader()->setStretchLastSection(true); 0062 0063 auto ipDelegate = new IpV6Delegate(this); 0064 auto prefixDelegate = new IntDelegate(0, 128, this); 0065 m_ui->tableViewAddresses->setItemDelegateForColumn(0, ipDelegate); 0066 m_ui->tableViewAddresses->setItemDelegateForColumn(1, prefixDelegate); 0067 m_ui->tableViewAddresses->setItemDelegateForColumn(2, ipDelegate); 0068 0069 connect(m_ui->btnAdd, &QPushButton::clicked, this, &IPv6Widget::slotAddIPAddress); 0070 connect(m_ui->btnRemove, &QPushButton::clicked, this, &IPv6Widget::slotRemoveIPAddress); 0071 0072 connect(m_ui->dnsMorePushButton, &QPushButton::clicked, this, &IPv6Widget::slotDnsServers); 0073 connect(m_ui->dnsSearchMorePushButton, &QPushButton::clicked, this, &IPv6Widget::slotDnsDomains); 0074 0075 connect(m_ui->tableViewAddresses->selectionModel(), &QItemSelectionModel::selectionChanged, this, &IPv6Widget::selectionChanged); 0076 0077 connect(&d->model, &QStandardItemModel::itemChanged, this, &IPv6Widget::tableViewItemChanged); 0078 0079 // IPv6Method::Disabled is available in NM 1.20+ 0080 if (!NetworkManager::checkVersion(1, 20, 0)) { 0081 m_ui->method->removeItem(7); 0082 } 0083 0084 if (setting) { 0085 loadConfig(setting); 0086 } 0087 0088 connect(m_ui->method, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &IPv6Widget::slotModeComboChanged); 0089 slotModeComboChanged(m_ui->method->currentIndex()); 0090 0091 connect(m_ui->btnRoutes, &QPushButton::clicked, this, &IPv6Widget::slotRoutesDialog); 0092 0093 // Connect for setting check 0094 watchChangedSetting(); 0095 0096 // Connect for validity check 0097 connect(m_ui->dns, &KLineEdit::textChanged, this, &IPv6Widget::slotWidgetChanged); 0098 connect(m_ui->method, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &IPv6Widget::slotWidgetChanged); 0099 connect(&d->model, &QStandardItemModel::dataChanged, this, &IPv6Widget::slotWidgetChanged); 0100 connect(&d->model, &QStandardItemModel::rowsRemoved, this, &IPv6Widget::slotWidgetChanged); 0101 0102 KAcceleratorManager::manage(this); 0103 } 0104 0105 IPv6Widget::~IPv6Widget() 0106 { 0107 delete d; 0108 delete m_ui; 0109 } 0110 0111 void IPv6Widget::loadConfig(const NetworkManager::Setting::Ptr &setting) 0112 { 0113 NetworkManager::Ipv6Setting::Ptr ipv6Setting = setting.staticCast<NetworkManager::Ipv6Setting>(); 0114 0115 // BUG:406118 0116 // We don't have route-metric in the UI, maybe even won't have for now, but that doesn't mean we 0117 // want to loose it when it's configured manually in a config file 0118 m_tmpIpv6Setting.setRouteMetric(ipv6Setting->routeMetric()); 0119 0120 m_tmpIpv6Setting.setRoutes(ipv6Setting->routes()); 0121 m_tmpIpv6Setting.setNeverDefault(ipv6Setting->neverDefault()); 0122 m_tmpIpv6Setting.setIgnoreAutoRoutes(ipv6Setting->ignoreAutoRoutes()); 0123 0124 // method 0125 switch (ipv6Setting->method()) { 0126 case NetworkManager::Ipv6Setting::Automatic: 0127 if (ipv6Setting->ignoreAutoDns()) { 0128 m_ui->method->setCurrentIndex(AutomaticOnlyIP); 0129 } else { 0130 m_ui->method->setCurrentIndex(Automatic); 0131 } 0132 break; 0133 case NetworkManager::Ipv6Setting::Dhcp: 0134 m_ui->method->setCurrentIndex(AutomaticOnlyDHCP); 0135 break; 0136 case NetworkManager::Ipv6Setting::Manual: 0137 m_ui->method->setCurrentIndex(Manual); 0138 break; 0139 case NetworkManager::Ipv6Setting::LinkLocal: 0140 m_ui->method->setCurrentIndex(LinkLocal); 0141 break; 0142 case NetworkManager::Ipv6Setting::Ignored: 0143 m_ui->method->setCurrentIndex(Ignored); 0144 break; 0145 case NetworkManager::Ipv6Setting::ConfigDisabled: 0146 m_ui->method->setCurrentIndex(Disabled); 0147 break; 0148 } 0149 0150 // dns 0151 QStringList tmp; 0152 for (const QHostAddress &addr : ipv6Setting->dns()) { 0153 tmp.append(addr.toString()); 0154 } 0155 m_ui->dns->setText(tmp.join(QStringLiteral(","))); 0156 m_ui->dnsSearch->setText(ipv6Setting->dnsSearch().join(QStringLiteral(","))); 0157 0158 // addresses 0159 for (const NetworkManager::IpAddress &address : ipv6Setting->addresses()) { 0160 QList<QStandardItem *> item{ 0161 new QStandardItem(address.ip().toString()), 0162 new QStandardItem(QString::number(address.prefixLength(), 10)), 0163 new QStandardItem(address.gateway().toString()), 0164 }; 0165 0166 d->model.appendRow(item); 0167 } 0168 0169 // may-fail 0170 m_ui->ipv6RequiredCB->setChecked(!ipv6Setting->mayFail()); 0171 0172 // privacy 0173 if (ipv6Setting->privacy() != NetworkManager::Ipv6Setting::Unknown) { 0174 m_ui->privacyCombo->setCurrentIndex(static_cast<int>(ipv6Setting->privacy()) + 1); 0175 } 0176 } 0177 0178 QVariantMap IPv6Widget::setting() const 0179 { 0180 NetworkManager::Ipv6Setting ipv6Setting; 0181 0182 // BUG:406118 0183 // We don't have route-metric in the UI, maybe even won't have for now, but that doesn't mean we 0184 // want to loose it when it's configured manually in a config file 0185 ipv6Setting.setRouteMetric(m_tmpIpv6Setting.routeMetric()); 0186 0187 ipv6Setting.setRoutes(m_tmpIpv6Setting.routes()); 0188 ipv6Setting.setNeverDefault(m_tmpIpv6Setting.neverDefault()); 0189 ipv6Setting.setIgnoreAutoRoutes(m_tmpIpv6Setting.ignoreAutoRoutes()); 0190 0191 // method 0192 switch ((MethodIndex)m_ui->method->currentIndex()) { 0193 case Automatic: 0194 ipv6Setting.setMethod(NetworkManager::Ipv6Setting::Automatic); 0195 break; 0196 case AutomaticOnlyIP: 0197 ipv6Setting.setMethod(NetworkManager::Ipv6Setting::Automatic); 0198 ipv6Setting.setIgnoreAutoDns(true); 0199 break; 0200 case IPv6Widget::AutomaticOnlyDHCP: 0201 ipv6Setting.setMethod(NetworkManager::Ipv6Setting::Dhcp); 0202 break; 0203 case Manual: 0204 ipv6Setting.setMethod(NetworkManager::Ipv6Setting::Manual); 0205 break; 0206 case LinkLocal: 0207 ipv6Setting.setMethod(NetworkManager::Ipv6Setting::LinkLocal); 0208 break; 0209 case Ignored: 0210 ipv6Setting.setMethod(NetworkManager::Ipv6Setting::Ignored); 0211 break; 0212 case Disabled: 0213 ipv6Setting.setMethod(NetworkManager::Ipv6Setting::ConfigDisabled); 0214 break; 0215 } 0216 0217 // dns 0218 if (m_ui->dns->isEnabled() && !m_ui->dns->text().isEmpty()) { 0219 QStringList tmp = m_ui->dns->text().split(QLatin1Char(',')); 0220 QList<QHostAddress> tmpAddrList; 0221 for (const QString &str : tmp) { 0222 QHostAddress addr(str); 0223 if (!addr.isNull()) 0224 tmpAddrList.append(addr); 0225 } 0226 ipv6Setting.setDns(tmpAddrList); 0227 } 0228 if (m_ui->dnsSearch->isEnabled() && !m_ui->dnsSearch->text().isEmpty()) { 0229 ipv6Setting.setDnsSearch(m_ui->dnsSearch->text().split(QLatin1Char(','))); 0230 } 0231 0232 // addresses 0233 if (m_ui->tableViewAddresses->isEnabled()) { 0234 QList<NetworkManager::IpAddress> list; 0235 for (int i = 0, rowCount = d->model.rowCount(); i < rowCount; i++) { 0236 NetworkManager::IpAddress address; 0237 address.setIp(QHostAddress(d->model.item(i, 0)->text())); 0238 address.setPrefixLength(d->model.item(i, 1)->text().toInt()); 0239 address.setGateway(QHostAddress(d->model.item(i, 2)->text())); 0240 0241 list << address; 0242 } 0243 ipv6Setting.setAddresses(list); 0244 } 0245 0246 // may-fail 0247 if (m_ui->ipv6RequiredCB->isEnabled()) { 0248 ipv6Setting.setMayFail(!m_ui->ipv6RequiredCB->isChecked()); 0249 } 0250 0251 // privacy 0252 if (m_ui->privacyCombo->isEnabled() && m_ui->privacyCombo->currentIndex()) { 0253 ipv6Setting.setPrivacy(static_cast<NetworkManager::Ipv6Setting::IPv6Privacy>(m_ui->privacyCombo->currentIndex() - 1)); 0254 } 0255 0256 return ipv6Setting.toMap(); 0257 } 0258 0259 void IPv6Widget::slotModeComboChanged(int index) 0260 { 0261 if (index == Automatic) { // Automatic 0262 m_ui->dnsLabel->setText(i18n("Other DNS Servers:")); 0263 m_ui->dns->setEnabled(true); 0264 m_ui->dnsMorePushButton->setEnabled(true); 0265 m_ui->dnsSearch->setEnabled(true); 0266 m_ui->dnsSearchMorePushButton->setEnabled(true); 0267 m_ui->ipv6RequiredCB->setEnabled(true); 0268 m_ui->privacyCombo->setEnabled(true); 0269 m_ui->btnRoutes->setEnabled(true); 0270 m_ui->tableViewAddresses->setEnabled(false); 0271 m_ui->btnAdd->setEnabled(false); 0272 m_ui->btnRemove->setEnabled(false); 0273 } else if (index == AutomaticOnlyIP) { 0274 m_ui->dnsLabel->setText(i18n("DNS Servers:")); 0275 m_ui->dns->setEnabled(true); 0276 m_ui->dnsMorePushButton->setEnabled(true); 0277 m_ui->dnsSearch->setEnabled(true); 0278 m_ui->dnsSearchMorePushButton->setEnabled(true); 0279 m_ui->ipv6RequiredCB->setEnabled(true); 0280 m_ui->privacyCombo->setEnabled(true); 0281 m_ui->btnRoutes->setEnabled(true); 0282 m_ui->tableViewAddresses->setEnabled(false); 0283 m_ui->btnAdd->setEnabled(false); 0284 m_ui->btnRemove->setEnabled(false); 0285 } else if (index == Manual) { // Manual 0286 m_ui->dnsLabel->setText(i18n("DNS Servers:")); 0287 m_ui->dns->setEnabled(true); 0288 m_ui->dnsMorePushButton->setEnabled(true); 0289 m_ui->dnsSearch->setEnabled(true); 0290 m_ui->dnsSearchMorePushButton->setEnabled(true); 0291 m_ui->ipv6RequiredCB->setEnabled(true); 0292 m_ui->privacyCombo->setEnabled(true); 0293 m_ui->btnRoutes->setEnabled(true); 0294 m_ui->tableViewAddresses->setEnabled(true); 0295 m_ui->btnAdd->setEnabled(true); 0296 m_ui->btnRemove->setEnabled(true); 0297 } else if (index == AutomaticOnlyDHCP || index == LinkLocal) { // Link-local or DHCP 0298 m_ui->dnsLabel->setText(i18n("DNS Servers:")); 0299 m_ui->dns->setEnabled(false); 0300 m_ui->dnsMorePushButton->setEnabled(false); 0301 m_ui->dnsSearch->setEnabled(false); 0302 m_ui->dnsSearchMorePushButton->setEnabled(false); 0303 m_ui->ipv6RequiredCB->setEnabled(true); 0304 m_ui->privacyCombo->setEnabled(true); 0305 m_ui->btnRoutes->setEnabled(false); 0306 m_ui->tableViewAddresses->setEnabled(false); 0307 m_ui->btnAdd->setEnabled(false); 0308 m_ui->btnRemove->setEnabled(false); 0309 } else if (index == Ignored || index == Disabled) { // Ignored and Disabled 0310 m_ui->dnsLabel->setText(i18n("DNS Servers:")); 0311 m_ui->dns->setEnabled(false); 0312 m_ui->dnsMorePushButton->setEnabled(false); 0313 m_ui->dnsSearch->setEnabled(false); 0314 m_ui->dnsSearchMorePushButton->setEnabled(false); 0315 m_ui->ipv6RequiredCB->setEnabled(false); 0316 m_ui->privacyCombo->setEnabled(false); 0317 m_ui->btnRoutes->setEnabled(false); 0318 m_ui->tableViewAddresses->setEnabled(false); 0319 m_ui->btnAdd->setEnabled(false); 0320 m_ui->btnRemove->setEnabled(false); 0321 } 0322 } 0323 0324 void IPv6Widget::slotAddIPAddress() 0325 { 0326 QList<QStandardItem *> item{new QStandardItem, new QStandardItem, new QStandardItem}; 0327 d->model.appendRow(item); 0328 0329 const int rowCount = d->model.rowCount(); 0330 if (rowCount > 0) { 0331 m_ui->tableViewAddresses->selectRow(rowCount - 1); 0332 0333 QItemSelectionModel *selectionModel = m_ui->tableViewAddresses->selectionModel(); 0334 QModelIndexList list = selectionModel->selectedIndexes(); 0335 if (list.size()) { 0336 // QTableView is configured to select only rows. 0337 // So, list[0] - IP address. 0338 m_ui->tableViewAddresses->edit(list[0]); 0339 } 0340 } 0341 } 0342 0343 void IPv6Widget::slotRemoveIPAddress() 0344 { 0345 QItemSelectionModel *selectionModel = m_ui->tableViewAddresses->selectionModel(); 0346 if (selectionModel->hasSelection()) { 0347 QModelIndexList indexes = selectionModel->selectedIndexes(); 0348 d->model.takeRow(indexes[0].row()); 0349 } 0350 m_ui->btnRemove->setEnabled(m_ui->tableViewAddresses->selectionModel()->hasSelection()); 0351 } 0352 0353 void IPv6Widget::selectionChanged(const QItemSelection &selected) 0354 { 0355 m_ui->btnRemove->setEnabled(!selected.isEmpty()); 0356 } 0357 0358 void IPv6Widget::tableViewItemChanged(QStandardItem *item) 0359 { 0360 if (item->text().isEmpty()) { 0361 return; 0362 } 0363 0364 const int column = item->column(); 0365 if (column == 0) { // ip 0366 int row = item->row(); 0367 0368 QStandardItem *netmaskItem = d->model.item(row, column + 1); // netmask 0369 if (netmaskItem && netmaskItem->text().isEmpty()) { 0370 QHostAddress addr(item->text()); 0371 const quint32 netmask = suggestNetmask(addr.toIPv6Address()); 0372 if (netmask) { 0373 netmaskItem->setText(QString::number(netmask, 10)); 0374 } 0375 } 0376 } 0377 } 0378 0379 void IPv6Widget::slotRoutesDialog() 0380 { 0381 QPointer<IpV6RoutesWidget> dlg = new IpV6RoutesWidget(this); 0382 dlg->setAttribute(Qt::WA_DeleteOnClose); 0383 0384 dlg->setRoutes(m_tmpIpv6Setting.routes()); 0385 dlg->setNeverDefault(m_tmpIpv6Setting.neverDefault()); 0386 if (m_ui->method->currentIndex() == 3) { // manual 0387 dlg->setIgnoreAutoRoutesCheckboxEnabled(false); 0388 } else { 0389 dlg->setIgnoreAutoRoutes(m_tmpIpv6Setting.ignoreAutoRoutes()); 0390 } 0391 0392 connect(dlg.data(), &QDialog::accepted, [dlg, this]() { 0393 m_tmpIpv6Setting.setRoutes(dlg->routes()); 0394 m_tmpIpv6Setting.setNeverDefault(dlg->neverDefault()); 0395 m_tmpIpv6Setting.setIgnoreAutoRoutes(dlg->ignoreautoroutes()); 0396 }); 0397 dlg->setModal(true); 0398 dlg->show(); 0399 } 0400 0401 void IPv6Widget::slotDnsServers() 0402 { 0403 QPointer<QDialog> dialog = new QDialog(this); 0404 dialog->setAttribute(Qt::WA_DeleteOnClose); 0405 dialog->setWindowTitle(i18n("Edit DNS servers")); 0406 dialog->setLayout(new QVBoxLayout); 0407 auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog); 0408 connect(buttons, &QDialogButtonBox::accepted, dialog.data(), &QDialog::accept); 0409 connect(buttons, &QDialogButtonBox::rejected, dialog.data(), &QDialog::reject); 0410 auto listWidget = new KEditListWidget(dialog); 0411 listWidget->setItems(m_ui->dns->text().split(QLatin1Char(',')).replaceInStrings(QStringLiteral(" "), QLatin1String(""))); 0412 listWidget->lineEdit()->setFocus(Qt::OtherFocusReason); 0413 dialog->layout()->addWidget(listWidget); 0414 dialog->layout()->addWidget(buttons); 0415 connect(dialog.data(), &QDialog::accepted, [listWidget, this]() { 0416 QString text = listWidget->items().join(QStringLiteral(",")); 0417 if (text.endsWith(',')) { 0418 text.chop(1); 0419 } 0420 m_ui->dns->setText(text); 0421 }); 0422 dialog->setModal(true); 0423 dialog->show(); 0424 } 0425 0426 void IPv6Widget::slotDnsDomains() 0427 { 0428 QPointer<QDialog> dialog = new QDialog(this); 0429 dialog->setAttribute(Qt::WA_DeleteOnClose); 0430 dialog->setWindowTitle(i18n("Edit DNS search domains")); 0431 dialog->setLayout(new QVBoxLayout); 0432 auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog); 0433 connect(buttons, &QDialogButtonBox::accepted, dialog.data(), &QDialog::accept); 0434 connect(buttons, &QDialogButtonBox::rejected, dialog.data(), &QDialog::reject); 0435 auto listWidget = new KEditListWidget(dialog); 0436 listWidget->setItems(m_ui->dnsSearch->text().split(QLatin1Char(',')).replaceInStrings(QStringLiteral(" "), QLatin1String(""))); 0437 listWidget->lineEdit()->setFocus(Qt::OtherFocusReason); 0438 dialog->layout()->addWidget(listWidget); 0439 dialog->layout()->addWidget(buttons); 0440 connect(dialog.data(), &QDialog::accepted, [listWidget, this]() { 0441 QString text = listWidget->items().join(QStringLiteral(",")); 0442 if (text.endsWith(',')) { 0443 text.chop(1); 0444 } 0445 m_ui->dnsSearch->setText(text); 0446 }); 0447 dialog->setModal(true); 0448 dialog->show(); 0449 } 0450 0451 bool IPv6Widget::isValid() const 0452 { 0453 if (m_ui->method->currentIndex() == Manual) { 0454 if (!d->model.rowCount()) { 0455 return false; 0456 } 0457 0458 for (int i = 0, rowCount = d->model.rowCount(); i < rowCount; i++) { 0459 QHostAddress ip = QHostAddress(d->model.item(i, 0)->text()); 0460 const int prefix = d->model.item(i, 1)->text().toInt(); 0461 QHostAddress gateway = QHostAddress(d->model.item(i, 2)->text()); 0462 0463 if (ip.isNull() || !(prefix >= 1 && prefix <= 128) || (gateway.isNull() && !d->model.item(i, 2)->text().isEmpty())) { 0464 return false; 0465 } 0466 } 0467 } 0468 0469 if (!m_ui->dns->text().isEmpty() 0470 && (m_ui->method->currentIndex() == Automatic || m_ui->method->currentIndex() == Manual || m_ui->method->currentIndex() == AutomaticOnlyIP)) { 0471 const QStringList tmp = m_ui->dns->text().split(QLatin1Char(',')); 0472 for (const QString &str : tmp) { 0473 QHostAddress addr(str); 0474 if (addr.isNull()) { 0475 return false; 0476 } 0477 } 0478 } 0479 0480 return true; 0481 } 0482 0483 #include "moc_ipv6widget.cpp"