File indexing completed on 2024-04-21 16:20:05

0001 /*
0002     SPDX-FileCopyrightText: 2016 Jan Grulich <jgrulich@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 "kcm.h"
0008 
0009 #include "configuration.h"
0010 #include "connectioneditordialog.h"
0011 #include "mobileconnectionwizard.h"
0012 #include "plasma_nm_kcm.h"
0013 #include "settings/wireguardinterfacewidget.h"
0014 #include "uiutils.h"
0015 #include "vpnuiplugin.h"
0016 
0017 // KDE
0018 #include <KLocalizedContext>
0019 #include <KMessageBox>
0020 #include <KMessageWidget>
0021 #include <KPluginFactory>
0022 #include <KPluginMetaData>
0023 #include <KSharedConfig>
0024 #include <kwidgetsaddons_version.h>
0025 
0026 #include <NetworkManagerQt/ActiveConnection>
0027 #include <NetworkManagerQt/Connection>
0028 #include <NetworkManagerQt/ConnectionSettings>
0029 #include <NetworkManagerQt/GsmSetting>
0030 #include <NetworkManagerQt/Ipv4Setting>
0031 #include <NetworkManagerQt/Ipv6Setting>
0032 #include <NetworkManagerQt/Settings>
0033 #include <NetworkManagerQt/Utils>
0034 #include <NetworkManagerQt/VpnSetting>
0035 #include <NetworkManagerQt/WiredSetting>
0036 #include <NetworkManagerQt/WireguardSetting>
0037 #include <NetworkManagerQt/WirelessDevice>
0038 #include <NetworkManagerQt/WirelessSetting>
0039 
0040 // Qt
0041 #include <QFileDialog>
0042 #include <QMenu>
0043 #include <QQmlContext>
0044 #include <QQmlEngine>
0045 #include <QQuickItem>
0046 #include <QQuickView>
0047 #include <QQuickWidget>
0048 #include <QStringBuilder>
0049 #include <QTimer>
0050 #include <QVBoxLayout>
0051 
0052 K_PLUGIN_CLASS_WITH_JSON(KCMNetworkmanagement, "kcm_networkmanagement.json")
0053 
0054 KCMNetworkmanagement::KCMNetworkmanagement(QWidget *parent, const QVariantList &args)
0055     : KCModule(parent, args)
0056     , m_handler(new Handler(this))
0057     , m_ui(new Ui::KCMForm)
0058 {
0059     auto mainWidget = new QWidget(this);
0060     m_ui->setupUi(mainWidget);
0061 
0062     KLocalizedContext *l10nContext = new KLocalizedContext(m_ui->connectionView->engine());
0063     l10nContext->setTranslationDomain(QStringLiteral(TRANSLATION_DOMAIN));
0064     m_ui->connectionView->engine()->rootContext()->setContextObject(l10nContext);
0065 
0066     // Check if we can use AP mode to identify security type
0067     bool useApMode = false;
0068     bool foundInactive = false;
0069 
0070     NetworkManager::WirelessDevice::Ptr wifiDev;
0071 
0072     for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0073         if (device->type() == NetworkManager::Device::Wifi) {
0074             wifiDev = device.objectCast<NetworkManager::WirelessDevice>();
0075             if (wifiDev) {
0076                 if (!wifiDev->isActive()) {
0077                     foundInactive = true;
0078                 } else {
0079                     // Prefer previous device if it was inactive
0080                     if (foundInactive) {
0081                         break;
0082                     }
0083                 }
0084 
0085                 if (wifiDev->wirelessCapabilities().testFlag(NetworkManager::WirelessDevice::ApCap)) {
0086                     useApMode = true;
0087                 }
0088 
0089                 // We prefer inactive wireless card with AP capabilities
0090                 if (foundInactive && useApMode) {
0091                     break;
0092                 }
0093             }
0094         }
0095     }
0096 
0097     m_ui->connectionView->setMinimumWidth(300);
0098     m_ui->connectionView->rootContext()->setContextProperty("alternateBaseColor", mainWidget->palette().color(QPalette::Active, QPalette::AlternateBase));
0099     m_ui->connectionView->rootContext()->setContextProperty("backgroundColor", mainWidget->palette().color(QPalette::Active, QPalette::Window));
0100     m_ui->connectionView->rootContext()->setContextProperty("baseColor", mainWidget->palette().color(QPalette::Active, QPalette::Base));
0101     m_ui->connectionView->rootContext()->setContextProperty("highlightColor", mainWidget->palette().color(QPalette::Active, QPalette::Highlight));
0102     m_ui->connectionView->rootContext()->setContextProperty("textColor", mainWidget->palette().color(QPalette::Active, QPalette::Text));
0103     m_ui->connectionView->rootContext()->setContextProperty("connectionModified", false);
0104     m_ui->connectionView->rootContext()->setContextProperty("useApMode", useApMode);
0105     m_ui->connectionView->setClearColor(Qt::transparent);
0106     m_ui->connectionView->setResizeMode(QQuickWidget::SizeRootObjectToView);
0107     m_ui->connectionView->setSource(
0108         QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kcm_networkmanagement/qml/main.qml"))));
0109 
0110     QObject *rootItem = m_ui->connectionView->rootObject();
0111     connect(rootItem, SIGNAL(selectedConnectionChanged(QString)), this, SLOT(onSelectedConnectionChanged(QString)));
0112     connect(rootItem, SIGNAL(requestCreateConnection(int, QString, QString, bool)), this, SLOT(onRequestCreateConnection(int, QString, QString, bool)));
0113     connect(rootItem, SIGNAL(requestExportConnection(QString)), this, SLOT(onRequestExportConnection(QString)));
0114     connect(rootItem, SIGNAL(requestToChangeConnection(QString, QString)), this, SLOT(onRequestToChangeConnection(QString, QString)));
0115 
0116     auto l = new QVBoxLayout(this);
0117 
0118     m_errorWidget = new KMessageWidget(this);
0119     m_errorWidget->setVisible(false);
0120     m_errorWidget->setMessageType(KMessageWidget::Error);
0121     l->addWidget(m_errorWidget);
0122 
0123     l->addWidget(mainWidget);
0124 
0125     setButtons(Button::Apply);
0126 
0127     NetworkManager::Connection::Ptr selectedConnection;
0128 
0129     // Look in the arguments for a connection ID to preselect
0130     static const QLatin1String uuidArgumentMarker{"Uuid="};
0131     for (QVariant arg : args) {
0132         if (arg.canConvert(QMetaType::QString)) {
0133             QString uuid = arg.toString();
0134             if (uuid.startsWith(uuidArgumentMarker)) {
0135                 uuid = uuid.replace(uuidArgumentMarker, QString());
0136                 selectedConnection = NetworkManager::findConnectionByUuid(uuid);
0137                 qDebug(PLASMA_NM_KCM_LOG) << "Selecting user connection:" << uuid;
0138                 break;
0139             }
0140         }
0141     }
0142 
0143     // Pre-select the currently active primary connection
0144     if (!selectedConnection || !selectedConnection->isValid()) {
0145         NetworkManager::ActiveConnection::Ptr activeConnection = NetworkManager::primaryConnection();
0146         if (activeConnection && activeConnection->isValid()) {
0147             selectedConnection = activeConnection->connection();
0148             qDebug(PLASMA_NM_KCM_LOG) << "Selecting active connection:" << selectedConnection->uuid();
0149         }
0150     }
0151 
0152     // Select the very first connection as a fallback
0153     if (!selectedConnection || !selectedConnection->isValid()) {
0154         NetworkManager::Connection::List connectionList = NetworkManager::listConnections();
0155         std::sort(connectionList.begin(), connectionList.end(), [](const NetworkManager::Connection::Ptr &left, const NetworkManager::Connection::Ptr &right) {
0156             const QString leftName = left->settings()->id();
0157             const UiUtils::SortedConnectionType leftType = UiUtils::connectionTypeToSortedType(left->settings()->connectionType());
0158             const QDateTime leftDate = left->settings()->timestamp();
0159 
0160             const QString rightName = right->settings()->id();
0161             const UiUtils::SortedConnectionType rightType = UiUtils::connectionTypeToSortedType(right->settings()->connectionType());
0162             const QDateTime rightDate = right->settings()->timestamp();
0163 
0164             if (leftType < rightType) {
0165                 return true;
0166             } else if (leftType > rightType) {
0167                 return false;
0168             }
0169 
0170             if (leftDate > rightDate) {
0171                 return true;
0172             } else if (leftDate < rightDate) {
0173                 return false;
0174             }
0175 
0176             if (QString::localeAwareCompare(leftName, rightName) > 0) {
0177                 return true;
0178             } else {
0179                 return false;
0180             }
0181         });
0182 
0183         for (const NetworkManager::Connection::Ptr &connection : connectionList) {
0184             const NetworkManager::ConnectionSettings::ConnectionType type = connection->settings()->connectionType();
0185             if (UiUtils::isConnectionTypeSupported(type)) {
0186                 selectedConnection = connection;
0187                 qDebug(PLASMA_NM_KCM_LOG) << "Selecting first connection:" << connection->uuid();
0188                 break;
0189             }
0190         }
0191     }
0192 
0193     if (selectedConnection && selectedConnection->isValid()) {
0194         const NetworkManager::ConnectionSettings::Ptr settings = selectedConnection->settings();
0195         if (UiUtils::isConnectionTypeSupported(settings->connectionType())) {
0196             QMetaObject::invokeMethod(rootItem, "selectConnection", Q_ARG(QVariant, settings->id()), Q_ARG(QVariant, selectedConnection->path()));
0197         }
0198     } else {
0199         qDebug(PLASMA_NM_KCM_LOG) << "Cannot preselect a connection";
0200     }
0201 
0202     connect(NetworkManager::settingsNotifier(),
0203             &NetworkManager::SettingsNotifier::connectionAdded,
0204             this,
0205             &KCMNetworkmanagement::onConnectionAdded,
0206             Qt::UniqueConnection);
0207 
0208     // Initialize first scan and then scan every 15 seconds
0209     m_handler->requestScan();
0210 
0211     m_timer = new QTimer(this);
0212     m_timer->setInterval(15000);
0213     connect(m_timer, &QTimer::timeout, [this]() {
0214         m_handler->requestScan();
0215     });
0216     m_timer->start();
0217 }
0218 
0219 KCMNetworkmanagement::~KCMNetworkmanagement()
0220 {
0221     delete m_handler;
0222     delete m_ui;
0223 }
0224 
0225 void KCMNetworkmanagement::defaults()
0226 {
0227     KCModule::defaults();
0228 }
0229 
0230 void KCMNetworkmanagement::load()
0231 {
0232     // If there is no loaded connection do nothing
0233     if (m_currentConnectionPath.isEmpty()) {
0234         return;
0235     }
0236 
0237     NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(m_currentConnectionPath);
0238     if (connection) {
0239         NetworkManager::ConnectionSettings::Ptr connectionSettings = connection->settings();
0240         // Re-load the connection again to load stored values
0241         if (m_tabWidget) {
0242             m_tabWidget->setConnection(connectionSettings);
0243         }
0244     }
0245 
0246     KCModule::load();
0247 }
0248 
0249 void KCMNetworkmanagement::save()
0250 {
0251     NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(m_currentConnectionPath);
0252 
0253     if (connection) {
0254         m_handler->updateConnection(connection, m_tabWidget->setting());
0255     }
0256 
0257     kcmChanged(false);
0258 
0259     KCModule::save();
0260 }
0261 
0262 void KCMNetworkmanagement::onConnectionAdded(const QString &connection)
0263 {
0264     if (m_createdConnectionUuid.isEmpty()) {
0265         return;
0266     }
0267 
0268     NetworkManager::Connection::Ptr newConnection = NetworkManager::findConnection(connection);
0269     if (newConnection) {
0270         NetworkManager::ConnectionSettings::Ptr connectionSettings = newConnection->settings();
0271         if (connectionSettings && connectionSettings->uuid() == m_createdConnectionUuid) {
0272             QObject *rootItem = m_ui->connectionView->rootObject();
0273             loadConnectionSettings(connectionSettings);
0274             QMetaObject::invokeMethod(rootItem, "selectConnection", Q_ARG(QVariant, connectionSettings->id()), Q_ARG(QVariant, newConnection->path()));
0275             m_createdConnectionUuid.clear();
0276         }
0277     }
0278 }
0279 
0280 void KCMNetworkmanagement::onRequestCreateConnection(int connectionType, const QString &vpnType, const QString &specificType, bool shared)
0281 {
0282     auto type = static_cast<NetworkManager::ConnectionSettings::ConnectionType>(connectionType);
0283 
0284     if (type == NetworkManager::ConnectionSettings::Vpn && vpnType == "imported") {
0285         ImportResult result = importVpn();
0286 
0287         if (!result.success) {
0288             m_errorWidget->setText(i18n("Failed to import VPN connection: %1", result.errorMessage));
0289             m_errorWidget->setVisible(true);
0290         } else {
0291             m_errorWidget->setVisible(false);
0292         }
0293 
0294     } else if (type == NetworkManager::ConnectionSettings::Gsm) { // launch the mobile broadband wizard, both gsm/cdma
0295         QPointer<MobileConnectionWizard> wizard = new MobileConnectionWizard(NetworkManager::ConnectionSettings::Unknown, this);
0296         wizard->setAttribute(Qt::WA_DeleteOnClose);
0297         connect(wizard.data(), &MobileConnectionWizard::accepted, [wizard, this]() {
0298             if (wizard->getError() == MobileProviders::Success) {
0299                 qCDebug(PLASMA_NM_KCM_LOG) << "Mobile broadband wizard finished:" << wizard->type() << wizard->args();
0300 
0301                 if (wizard->args().count() == 2) {
0302                     auto tmp = qdbus_cast<QVariantMap>(wizard->args().value(1));
0303 
0304                     NetworkManager::ConnectionSettings::Ptr connectionSettings;
0305                     connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(wizard->type()));
0306                     connectionSettings->setId(wizard->args().value(0).toString());
0307                     if (wizard->type() == NetworkManager::ConnectionSettings::Gsm) {
0308                         NetworkManager::GsmSetting::Ptr gsmSetting =
0309                             connectionSettings->setting(NetworkManager::Setting::Gsm).staticCast<NetworkManager::GsmSetting>();
0310                         gsmSetting->fromMap(tmp);
0311                         gsmSetting->setPasswordFlags(NetworkManager::Setting::NotRequired);
0312                         gsmSetting->setPinFlags(NetworkManager::Setting::NotRequired);
0313                     } else if (wizard->type() == NetworkManager::ConnectionSettings::Cdma) {
0314                         connectionSettings->setting(NetworkManager::Setting::Cdma)->fromMap(tmp);
0315                     } else {
0316                         qCWarning(PLASMA_NM_KCM_LOG) << Q_FUNC_INFO << "Unhandled setting type";
0317                     }
0318                     // Generate new UUID
0319                     connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid());
0320                     addConnection(connectionSettings);
0321                 } else {
0322                     qCWarning(PLASMA_NM_KCM_LOG) << Q_FUNC_INFO << "Unexpected number of args to parse";
0323                 }
0324             }
0325         });
0326         wizard->setModal(true);
0327         wizard->show();
0328     } else {
0329         NetworkManager::ConnectionSettings::Ptr connectionSettings;
0330         connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(type));
0331 
0332         if (type == NetworkManager::ConnectionSettings::Vpn) {
0333             NetworkManager::VpnSetting::Ptr vpnSetting = connectionSettings->setting(NetworkManager::Setting::Vpn).dynamicCast<NetworkManager::VpnSetting>();
0334             vpnSetting->setServiceType(vpnType);
0335             // Set VPN subtype in case of Openconnect to add support for juniper
0336             if (vpnType == QLatin1String("org.freedesktop.NetworkManager.openconnect")) {
0337                 NMStringMap data = vpnSetting->data();
0338                 data.insert(QLatin1String("protocol"), specificType);
0339                 vpnSetting->setData(data);
0340             }
0341         }
0342 
0343         if (type == NetworkManager::ConnectionSettings::Wired || type == NetworkManager::ConnectionSettings::Wireless) {
0344             // Set auto-negotiate to true, NM sets it to false by default, but we used to have this before and also
0345             // I don't think it's wise to request users to specify speed and duplex as most of them don't know what is that
0346             // and what to set
0347             if (type == NetworkManager::ConnectionSettings::Wired) {
0348                 NetworkManager::WiredSetting::Ptr wiredSetting =
0349                     connectionSettings->setting(NetworkManager::Setting::Wired).dynamicCast<NetworkManager::WiredSetting>();
0350                 wiredSetting->setAutoNegotiate(true);
0351             }
0352 
0353             if (shared) {
0354                 if (type == NetworkManager::ConnectionSettings::Wireless) {
0355                     NetworkManager::WirelessSetting::Ptr wifiSetting =
0356                         connectionSettings->setting(NetworkManager::Setting::Wireless).dynamicCast<NetworkManager::WirelessSetting>();
0357                     wifiSetting->setMode(NetworkManager::WirelessSetting::Adhoc);
0358                     wifiSetting->setSsid(i18n("my_shared_connection").toUtf8());
0359 
0360                     for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0361                         if (device->type() == NetworkManager::Device::Wifi) {
0362                             NetworkManager::WirelessDevice::Ptr wifiDev = device.objectCast<NetworkManager::WirelessDevice>();
0363                             if (wifiDev) {
0364                                 if (wifiDev->wirelessCapabilities().testFlag(NetworkManager::WirelessDevice::ApCap)) {
0365                                     wifiSetting->setMode(NetworkManager::WirelessSetting::Ap);
0366                                     wifiSetting->setMacAddress(NetworkManager::macAddressFromString(wifiDev->permanentHardwareAddress()));
0367                                 }
0368                             }
0369                         }
0370                     }
0371                 }
0372 
0373                 NetworkManager::Ipv4Setting::Ptr ipv4Setting =
0374                     connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast<NetworkManager::Ipv4Setting>();
0375                 ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Shared);
0376                 connectionSettings->setAutoconnect(false);
0377             }
0378         }
0379         if (type == NetworkManager::ConnectionSettings::WireGuard) {
0380             NetworkManager::WireGuardSetting::Ptr wireguardSetting =
0381                 connectionSettings->setting(NetworkManager::Setting::WireGuard).dynamicCast<NetworkManager::WireGuardSetting>();
0382             NetworkManager::Ipv4Setting::Ptr ipv4Setting =
0383                 connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast<NetworkManager::Ipv4Setting>();
0384             NetworkManager::Ipv6Setting::Ptr ipv6Setting =
0385                 connectionSettings->setting(NetworkManager::Setting::Ipv6).dynamicCast<NetworkManager::Ipv6Setting>();
0386             connectionSettings->setAutoconnect(false);
0387             ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Disabled);
0388             ipv6Setting->setMethod(NetworkManager::Ipv6Setting::Ignored);
0389         }
0390         // Generate new UUID
0391         connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid());
0392         addConnection(connectionSettings);
0393     }
0394 
0395     // Automatically enable virtual connection management if one is created
0396     if (type == NetworkManager::ConnectionSettings::Vlan || type == NetworkManager::ConnectionSettings::Bridge
0397         || type == NetworkManager::ConnectionSettings::Bond || type == NetworkManager::ConnectionSettings::Team) {
0398         Configuration::self().setManageVirtualConnections(true);
0399     }
0400 }
0401 
0402 void KCMNetworkmanagement::onRequestExportConnection(const QString &connectionPath)
0403 {
0404     NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(connectionPath);
0405     if (!connection) {
0406         return;
0407     }
0408 
0409     NetworkManager::ConnectionSettings::Ptr connSettings = connection->settings();
0410 
0411     if (connSettings->connectionType() != NetworkManager::ConnectionSettings::Vpn)
0412         return;
0413 
0414     NetworkManager::VpnSetting::Ptr vpnSetting = connSettings->setting(NetworkManager::Setting::Vpn).dynamicCast<NetworkManager::VpnSetting>();
0415 
0416     qCDebug(PLASMA_NM_KCM_LOG) << "Exporting VPN connection" << connection->name() << "type:" << vpnSetting->serviceType();
0417 
0418     const auto result = VpnUiPlugin::loadPluginForType(nullptr, vpnSetting->serviceType());
0419 
0420     if (result) {
0421         std::unique_ptr<VpnUiPlugin> vpnPlugin(result.plugin);
0422         if (vpnPlugin->suggestedFileName(connSettings).isEmpty()) { // this VPN doesn't support export
0423             qCWarning(PLASMA_NM_KCM_LOG) << "This VPN doesn't support export";
0424             return;
0425         }
0426 
0427         const QString url =
0428             QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + QDir::separator() + vpnPlugin->suggestedFileName(connSettings);
0429         const QString filename =
0430             QFileDialog::getSaveFileName(this, i18n("Export VPN Connection"), url, vpnPlugin->supportedFileExtensions().join(QLatin1Char(' ')));
0431         if (!filename.isEmpty()) {
0432             if (auto result = vpnPlugin->exportConnectionSettings(connSettings, filename)) {
0433                 // TODO display success
0434             } else {
0435                 // TODO display failure
0436                 qCWarning(PLASMA_NM_KCM_LOG) << "Failed to export VPN connection" << result.errorMessage();
0437             }
0438         }
0439     } else {
0440         qCWarning(PLASMA_NM_KCM_LOG) << "Error getting VpnUiPlugin for export:" << result.errorText;
0441     }
0442 }
0443 
0444 void KCMNetworkmanagement::onRequestToChangeConnection(const QString &connectionName, const QString &connectionPath)
0445 {
0446     NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(m_currentConnectionPath);
0447 
0448     if (connection) {
0449 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0450         if (KMessageBox::questionTwoActions(this,
0451 #else
0452         if (KMessageBox::questionYesNo(this,
0453 
0454 #endif
0455                                             i18n("Do you want to save changes made to the connection '%1'?", connection->name()),
0456                                             i18nc("@title:window", "Save Changes"),
0457                                             KStandardGuiItem::save(),
0458                                             KStandardGuiItem::discard(),
0459                                             QString(),
0460                                             KMessageBox::Notify)
0461 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0462             == KMessageBox::ButtonCode::PrimaryAction) {
0463 #else
0464             == KMessageBox::Yes) {
0465 #endif
0466             save();
0467         }
0468     }
0469 
0470     QObject *rootItem = m_ui->connectionView->rootObject();
0471     QMetaObject::invokeMethod(rootItem, "selectConnection", Q_ARG(QVariant, connectionName), Q_ARG(QVariant, connectionPath));
0472 }
0473 
0474 void KCMNetworkmanagement::onSelectedConnectionChanged(const QString &connectionPath)
0475 {
0476     if (connectionPath.isEmpty()) {
0477         resetSelection();
0478         return;
0479     }
0480 
0481     m_currentConnectionPath = connectionPath;
0482 
0483     NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(m_currentConnectionPath);
0484     if (connection) {
0485         NetworkManager::ConnectionSettings::Ptr connectionSettings = connection->settings();
0486         loadConnectionSettings(connectionSettings);
0487     }
0488 }
0489 
0490 void KCMNetworkmanagement::addConnection(const NetworkManager::ConnectionSettings::Ptr &connectionSettings)
0491 {
0492     QPointer<ConnectionEditorDialog> editor = new ConnectionEditorDialog(connectionSettings);
0493     editor->setAttribute(Qt::WA_DeleteOnClose);
0494     connect(editor.data(), &ConnectionEditorDialog::accepted, [connectionSettings, editor, this]() {
0495         // We got confirmation so watch this connection and select it once it is created
0496         m_createdConnectionUuid = connectionSettings->uuid();
0497         m_handler->addConnection(editor->setting());
0498     });
0499     editor->setModal(true);
0500     editor->show();
0501 }
0502 
0503 void KCMNetworkmanagement::kcmChanged(bool kcmChanged)
0504 {
0505     m_ui->connectionView->rootContext()->setContextProperty("connectionModified", kcmChanged);
0506     Q_EMIT changed(kcmChanged);
0507 }
0508 
0509 void KCMNetworkmanagement::loadConnectionSettings(const NetworkManager::ConnectionSettings::Ptr &connectionSettings)
0510 {
0511     if (m_tabWidget) {
0512         m_tabWidget->setConnection(connectionSettings);
0513     } else {
0514         m_tabWidget = new ConnectionEditorTabWidget(connectionSettings);
0515         connect(m_tabWidget, &ConnectionEditorTabWidget::settingChanged, [this]() {
0516             if (m_tabWidget->isInitialized() && m_tabWidget->isValid()) {
0517                 kcmChanged(true);
0518             }
0519         });
0520         connect(m_tabWidget, &ConnectionEditorTabWidget::validityChanged, [this](bool valid) {
0521             if (m_tabWidget->isInitialized() && m_tabWidget->isValid() != valid) {
0522                 kcmChanged(valid);
0523             }
0524         });
0525         m_ui->horizontalLayout->addWidget(m_tabWidget);
0526     }
0527 
0528     kcmChanged(false);
0529 }
0530 
0531 KCMNetworkmanagement::ImportResult KCMNetworkmanagement::ImportResult::pass()
0532 {
0533     return {true, QString()};
0534 }
0535 
0536 KCMNetworkmanagement::ImportResult KCMNetworkmanagement::ImportResult::fail(const QString &message)
0537 {
0538     return {false, message};
0539 }
0540 
0541 KCMNetworkmanagement::ImportResult KCMNetworkmanagement::importVpn()
0542 {
0543     // get the list of supported extensions
0544     const QVector<KPluginMetaData> services = KPluginMetaData::findPlugins(QStringLiteral("plasma/network/vpn"));
0545     QStringList extensions;
0546     for (const KPluginMetaData &service : services) {
0547         const auto result = KPluginFactory::instantiatePlugin<VpnUiPlugin>(service);
0548         if (result) {
0549             extensions += result.plugin->supportedFileExtensions();
0550             delete result.plugin;
0551         }
0552     }
0553 
0554     const QString &filename =
0555         QFileDialog::getOpenFileName(this, i18n("Import VPN Connection"), QDir::homePath(), i18n("VPN connections (%1)", extensions.join(QLatin1Char(' '))));
0556 
0557     if (filename.isEmpty()) {
0558         return ImportResult::fail(i18n("No file was provided"));
0559     }
0560 
0561     QFileInfo fi(filename);
0562     const QString ext = QStringLiteral("*.") % fi.suffix();
0563     qCDebug(PLASMA_NM_KCM_LOG) << "Importing VPN connection " << filename << "extension:" << ext;
0564 
0565     // Handle WireGuard separately because it is different than all the other VPNs
0566     if (WireGuardInterfaceWidget::supportedFileExtensions().contains(ext)) {
0567 #if NM_CHECK_VERSION(1, 40, 0)
0568         GError *error = nullptr;
0569         NMConnection *conn = nm_conn_wireguard_import(filename.toUtf8().constData(), &error);
0570 
0571         if (error) {
0572             qCDebug(PLASMA_NM_KCM_LOG) << "Could not import WireGuard connection" << error->message;
0573         } else {
0574             m_handler->addConnection(conn);
0575             return ImportResult::pass();
0576         }
0577 #else
0578         NMVariantMapMap connection = WireGuardInterfaceWidget::importConnectionSettings(filename);
0579         NetworkManager::ConnectionSettings connectionSettings;
0580         connectionSettings.fromMap(connection);
0581         connectionSettings.setUuid(NetworkManager::ConnectionSettings::createNewUuid());
0582 
0583         // qCDebug(PLASMA_NM_KCM_LOG) << "Converted connection:" << connectionSettings;
0584 
0585         m_handler->addConnection(connectionSettings.toMap());
0586         // qCDebug(PLASMA_NM_KCM_LOG) << "Adding imported connection under id:" << conId;
0587 
0588         if (!connection.isEmpty()) {
0589             return ImportResult::pass(); // get out if the import produced at least some output
0590         }
0591 #endif
0592     }
0593     for (const KPluginMetaData &service : services) {
0594         const auto result = KPluginFactory::instantiatePlugin<VpnUiPlugin>(service);
0595 
0596         if (!result) {
0597             continue;
0598         }
0599 
0600         std::unique_ptr<VpnUiPlugin> vpnPlugin(result.plugin);
0601 
0602         if (vpnPlugin->supportedFileExtensions().contains(ext)) {
0603             qCDebug(PLASMA_NM_KCM_LOG) << "Found VPN plugin" << service.name() << ", type:" << service.value("X-NetworkManager-Services");
0604 
0605             VpnUiPlugin::ImportResult result = vpnPlugin->importConnectionSettings(filename);
0606 
0607             if (!result) {
0608                 qWarning(PLASMA_NM_KCM_LOG) << "Failed to import" << filename << result.errorMessage();
0609                 return ImportResult::fail(result.errorMessage());
0610             }
0611 
0612             m_handler->addConnection(result.connection());
0613 
0614             // qCDebug(PLASMA_NM_KCM_LOG) << "Adding imported connection under id:" << conId;
0615             return ImportResult::pass();
0616         }
0617     }
0618 
0619     return ImportResult::fail(i18n("Unknown VPN type"));
0620 }
0621 
0622 void KCMNetworkmanagement::resetSelection()
0623 {
0624     // Reset selected connections
0625     m_currentConnectionPath.clear();
0626     QObject *rootItem = m_ui->connectionView->rootObject();
0627     QMetaObject::invokeMethod(rootItem, "deselectConnections");
0628     if (m_tabWidget) {
0629         delete m_tabWidget;
0630         m_tabWidget = nullptr;
0631     }
0632     Q_EMIT changed(false);
0633 }
0634 
0635 #include "kcm.moc"