File indexing completed on 2023-12-03 12:26:49

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