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

0001 /*
0002     SPDX-FileCopyrightText: 2009 Will Stephenson <wstephenson@kde.org>
0003     SPDX-FileCopyrightText: 2013 Daniel Nicoletti <dantti12@gmail.com>
0004     SPDX-FileCopyrightText: 2013 Lukas Tinkl <ltinkl@redhat.com>
0005     SPDX-FileCopyrightText: 2013 Jan Grulich <jgrulich@redhat.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0008 */
0009 
0010 #include "notification.h"
0011 #include "plasma_nm_kded.h"
0012 
0013 #include <uiutils.h>
0014 
0015 #include <NetworkManagerQt/Manager>
0016 
0017 #include <KLocalizedString>
0018 #include <KNotification>
0019 
0020 #include <QDBusConnection>
0021 #include <QIcon>
0022 #include <QTimer>
0023 
0024 #include <algorithm>
0025 #include <chrono>
0026 
0027 using namespace std::chrono_literals;
0028 
0029 Notification::Notification(QObject *parent)
0030     : QObject(parent)
0031 {
0032     // devices
0033     for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0034         addDevice(device);
0035     }
0036 
0037     connect(NetworkManager::notifier(), &NetworkManager::Notifier::deviceAdded, this, &Notification::deviceAdded);
0038 
0039     // connections
0040     for (const NetworkManager::ActiveConnection::Ptr &ac : NetworkManager::activeConnections()) {
0041         addActiveConnection(ac);
0042     }
0043 
0044     connect(NetworkManager::notifier(),
0045             &NetworkManager::Notifier::activeConnectionAdded,
0046             this,
0047             QOverload<const QString &>::of(&Notification::addActiveConnection));
0048 
0049     QDBusConnection::systemBus().connect(QStringLiteral("org.freedesktop.login1"),
0050                                          QStringLiteral("/org/freedesktop/login1"),
0051                                          QStringLiteral("org.freedesktop.login1.Manager"),
0052                                          QStringLiteral("PrepareForSleep"),
0053                                          this,
0054                                          SLOT(onPrepareForSleep(bool)));
0055 
0056     // After 10 seconds we can consider it to not have "just launched" anymore
0057     QTimer::singleShot(10s, this, [this]() {
0058         m_justLaunched = false;
0059     });
0060 }
0061 
0062 void Notification::deviceAdded(const QString &uni)
0063 {
0064     NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(uni);
0065     addDevice(device);
0066 }
0067 
0068 void Notification::addDevice(const NetworkManager::Device::Ptr &device)
0069 {
0070     connect(device.data(), &NetworkManager::Device::stateChanged, this, &Notification::stateChanged);
0071 }
0072 
0073 void Notification::stateChanged(NetworkManager::Device::State newstate,
0074                                 NetworkManager::Device::State oldstate,
0075                                 NetworkManager::Device::StateChangeReason reason)
0076 {
0077     Q_UNUSED(oldstate)
0078 
0079     auto device = qobject_cast<NetworkManager::Device *>(sender());
0080     if (newstate == NetworkManager::Device::Activated && m_notifications.contains(device->uni())) {
0081         KNotification *notify = m_notifications.value(device->uni());
0082         notify->close();
0083         return;
0084     } else if (newstate != NetworkManager::Device::Failed) {
0085         return;
0086     }
0087 
0088     const QString identifier = UiUtils::prettyInterfaceName(device->type(), device->interfaceName());
0089     QString text;
0090     switch (reason) {
0091     case NetworkManager::Device::NoReason:
0092     case NetworkManager::Device::UnknownReason:
0093     case NetworkManager::Device::NowManagedReason:
0094     case NetworkManager::Device::NowUnmanagedReason:
0095         return;
0096     case NetworkManager::Device::ConfigFailedReason:
0097         text = i18nc("@info:status Notification when the device failed due to ConfigFailedReason", "The device could not be configured");
0098         break;
0099     case NetworkManager::Device::ConfigUnavailableReason:
0100         text = i18nc("@info:status Notification when the device failed due to ConfigUnavailableReason", "IP configuration was unavailable");
0101         break;
0102     case NetworkManager::Device::ConfigExpiredReason:
0103         text = i18nc("@info:status Notification when the device failed due to ConfigExpiredReason", "IP configuration expired");
0104         break;
0105     case NetworkManager::Device::NoSecretsReason:
0106         text = i18nc("@info:status Notification when the device failed due to NoSecretsReason", "No secrets were provided");
0107         break;
0108     case NetworkManager::Device::AuthSupplicantDisconnectReason:
0109         text = i18nc("@info:status Notification when the device failed due to AuthSupplicantDisconnectReason", "Authorization supplicant disconnected");
0110         break;
0111     case NetworkManager::Device::AuthSupplicantConfigFailedReason:
0112         text = i18nc("@info:status Notification when the device failed due to AuthSupplicantConfigFailedReason",
0113                      "Authorization supplicant's configuration failed");
0114         break;
0115     case NetworkManager::Device::AuthSupplicantFailedReason:
0116         text = i18nc("@info:status Notification when the device failed due to AuthSupplicantFailedReason", "Authorization supplicant failed");
0117         break;
0118     case NetworkManager::Device::AuthSupplicantTimeoutReason:
0119         text = i18nc("@info:status Notification when the device failed due to AuthSupplicantTimeoutReason", "Authorization supplicant timed out");
0120         break;
0121     case NetworkManager::Device::PppStartFailedReason:
0122         text = i18nc("@info:status Notification when the device failed due to PppStartFailedReason", "PPP failed to start");
0123         break;
0124     case NetworkManager::Device::PppDisconnectReason:
0125         text = i18nc("@info:status Notification when the device failed due to PppDisconnectReason", "PPP disconnected");
0126         break;
0127     case NetworkManager::Device::PppFailedReason:
0128         text = i18nc("@info:status Notification when the device failed due to PppFailedReason", "PPP failed");
0129         break;
0130     case NetworkManager::Device::DhcpStartFailedReason:
0131         text = i18nc("@info:status Notification when the device failed due to DhcpStartFailedReason", "DHCP failed to start");
0132         break;
0133     case NetworkManager::Device::DhcpErrorReason:
0134         text = i18nc("@info:status Notification when the device failed due to DhcpErrorReason", "A DHCP error occurred");
0135         break;
0136     case NetworkManager::Device::DhcpFailedReason:
0137         text = i18nc("@info:status Notification when the device failed due to DhcpFailedReason", "DHCP failed ");
0138         break;
0139     case NetworkManager::Device::SharedStartFailedReason:
0140         text = i18nc("@info:status Notification when the device failed due to SharedStartFailedReason", "The shared service failed to start");
0141         break;
0142     case NetworkManager::Device::SharedFailedReason:
0143         text = i18nc("@info:status Notification when the device failed due to SharedFailedReason", "The shared service failed");
0144         break;
0145     case NetworkManager::Device::AutoIpStartFailedReason:
0146         text = i18nc("@info:status Notification when the device failed due to AutoIpStartFailedReason", "The auto IP service failed to start");
0147         break;
0148     case NetworkManager::Device::AutoIpErrorReason:
0149         text = i18nc("@info:status Notification when the device failed due to AutoIpErrorReason", "The auto IP service reported an error");
0150         break;
0151     case NetworkManager::Device::AutoIpFailedReason:
0152         text = i18nc("@info:status Notification when the device failed due to AutoIpFailedReason", "The auto IP service failed");
0153         break;
0154     case NetworkManager::Device::ModemBusyReason:
0155         text = i18nc("@info:status Notification when the device failed due to ModemBusyReason", "The modem is busy");
0156         break;
0157     case NetworkManager::Device::ModemNoDialToneReason:
0158         text = i18nc("@info:status Notification when the device failed due to ModemNoDialToneReason", "The modem has no dial tone");
0159         break;
0160     case NetworkManager::Device::ModemNoCarrierReason:
0161         text = i18nc("@info:status Notification when the device failed due to ModemNoCarrierReason", "The modem shows no carrier");
0162         break;
0163     case NetworkManager::Device::ModemDialTimeoutReason:
0164         text = i18nc("@info:status Notification when the device failed due to ModemDialTimeoutReason", "The modem dial timed out");
0165         break;
0166     case NetworkManager::Device::ModemDialFailedReason:
0167         text = i18nc("@info:status Notification when the device failed due to ModemDialFailedReason", "The modem dial failed");
0168         break;
0169     case NetworkManager::Device::ModemInitFailedReason:
0170         text = i18nc("@info:status Notification when the device failed due to ModemInitFailedReason", "The modem could not be initialized");
0171         break;
0172     case NetworkManager::Device::GsmApnSelectFailedReason:
0173         text = i18nc("@info:status Notification when the device failed due to GsmApnSelectFailedReason", "The GSM APN could not be selected");
0174         break;
0175     case NetworkManager::Device::GsmNotSearchingReason:
0176         text = i18nc("@info:status Notification when the device failed due to GsmNotSearchingReason", "The GSM modem is not searching");
0177         break;
0178     case NetworkManager::Device::GsmRegistrationDeniedReason:
0179         text = i18nc("@info:status Notification when the device failed due to GsmRegistrationDeniedReason", "GSM network registration was denied");
0180         break;
0181     case NetworkManager::Device::GsmRegistrationTimeoutReason:
0182         text = i18nc("@info:status Notification when the device failed due to GsmRegistrationTimeoutReason", "GSM network registration timed out");
0183         break;
0184     case NetworkManager::Device::GsmRegistrationFailedReason:
0185         text = i18nc("@info:status Notification when the device failed due to GsmRegistrationFailedReason", "GSM registration failed");
0186         break;
0187     case NetworkManager::Device::GsmPinCheckFailedReason:
0188         text = i18nc("@info:status Notification when the device failed due to GsmPinCheckFailedReason", "The GSM PIN check failed");
0189         break;
0190     case NetworkManager::Device::FirmwareMissingReason:
0191         text = i18nc("@info:status Notification when the device failed due to FirmwareMissingReason", "Device firmware is missing");
0192         break;
0193     case NetworkManager::Device::DeviceRemovedReason:
0194         text = i18nc("@info:status Notification when the device failed due to DeviceRemovedReason", "The device was removed");
0195         break;
0196     case NetworkManager::Device::SleepingReason:
0197         text = i18nc("@info:status Notification when the device failed due to SleepingReason", "The networking system is now sleeping");
0198         break;
0199     case NetworkManager::Device::ConnectionRemovedReason:
0200         text = i18nc("@info:status Notification when the device failed due to ConnectionRemovedReason", "The connection was removed");
0201         break;
0202     case NetworkManager::Device::UserRequestedReason:
0203         return;
0204     case NetworkManager::Device::CarrierReason:
0205         text = i18nc("@info:status Notification when the device failed due to CarrierReason", "The cable was disconnected");
0206         break;
0207     case NetworkManager::Device::ConnectionAssumedReason:
0208     case NetworkManager::Device::SupplicantAvailableReason:
0209         return;
0210     case NetworkManager::Device::ModemNotFoundReason:
0211         text = i18nc("@info:status Notification when the device failed due to ModemNotFoundReason", "The modem could not be found");
0212         break;
0213     case NetworkManager::Device::BluetoothFailedReason:
0214         text = i18nc("@info:status Notification when the device failed due to BluetoothFailedReason", "The bluetooth connection failed or timed out");
0215         break;
0216     case NetworkManager::Device::GsmSimNotInserted:
0217         text = i18nc("@info:status Notification when the device failed due to GsmSimNotInserted", "GSM Modem's SIM Card not inserted");
0218         break;
0219     case NetworkManager::Device::GsmSimPinRequired:
0220         text = i18nc("@info:status Notification when the device failed due to GsmSimPinRequired", "GSM Modem's SIM Pin required");
0221         break;
0222     case NetworkManager::Device::GsmSimPukRequired:
0223         text = i18nc("@info:status Notification when the device failed due to GsmSimPukRequired", "GSM Modem's SIM Puk required");
0224         break;
0225     case NetworkManager::Device::GsmSimWrong:
0226         text = i18nc("@info:status Notification when the device failed due to GsmSimWrong", "GSM Modem's SIM wrong");
0227         break;
0228     case NetworkManager::Device::InfiniBandMode:
0229         text = i18nc("@info:status Notification when the device failed due to InfiniBandMode", "InfiniBand device does not support connected mode");
0230         break;
0231     case NetworkManager::Device::DependencyFailed:
0232         text = i18nc("@info:status Notification when the device failed due to DependencyFailed", "A dependency of the connection failed");
0233         break;
0234     case NetworkManager::Device::Br2684Failed:
0235         text = i18nc("@info:status Notification when the device failed due to Br2684Failed", "Problem with the RFC 2684 Ethernet over ADSL bridge");
0236         break;
0237     case NetworkManager::Device::ModemManagerUnavailable:
0238         text = i18nc("@info:status Notification when the device failed due to ModemManagerUnavailable", "ModemManager not running");
0239         break;
0240     case NetworkManager::Device::SsidNotFound:
0241         text = i18nc("@info:status Notification when the device failed due to SsidNotFound", "The WiFi network could not be found");
0242         break;
0243     case NetworkManager::Device::SecondaryConnectionFailed:
0244         text =
0245             i18nc("@info:status Notification when the device failed due to SecondaryConnectionFailed", "A secondary connection of the base connection failed");
0246         break;
0247     case NetworkManager::Device::DcbFcoeFailed:
0248         text = i18nc("@info:status Notification when the device failed due to DcbFcoeFailed", "DCB or FCoE setup failed");
0249         break;
0250     case NetworkManager::Device::TeamdControlFailed:
0251         text = i18nc("@info:status Notification when the device failed due to TeamdControlFailed", "teamd control failed");
0252         break;
0253     case NetworkManager::Device::ModemFailed:
0254         text = i18nc("@info:status Notification when the device failed due to ModemFailed", "Modem failed or no longer available");
0255         break;
0256     case NetworkManager::Device::ModemAvailable:
0257         text = i18nc("@info:status Notification when the device failed due to ModemAvailable", "Modem now ready and available");
0258         break;
0259     case NetworkManager::Device::SimPinIncorrect:
0260         text = i18nc("@info:status Notification when the device failed due to SimPinIncorrect", "The SIM PIN was incorrect");
0261         break;
0262     case NetworkManager::Device::NewActivation:
0263         text = i18nc("@info:status Notification when the device failed due to NewActivation", "A new connection activation was enqueued");
0264         break;
0265     case NetworkManager::Device::ParentChanged:
0266         text = i18nc("@info:status Notification when the device failed due to ParentChanged", "The device's parent changed");
0267         break;
0268     case NetworkManager::Device::ParentManagedChanged:
0269         text = i18nc("@info:status Notification when the device failed due to ParentManagedChanged", "The device parent's management changed");
0270         break;
0271     case NetworkManager::Device::Reserved:
0272         return;
0273     }
0274 
0275     if (m_notifications.contains(device->uni())) {
0276         KNotification *notify = m_notifications.value(device->uni());
0277         notify->setText(text.toHtmlEscaped());
0278         notify->update();
0279     } else {
0280         auto notify = new KNotification(QStringLiteral("DeviceFailed"), KNotification::CloseOnTimeout);
0281         connect(notify, &KNotification::closed, this, &Notification::notificationClosed);
0282         notify->setProperty("uni", device->uni());
0283         notify->setComponentName(QStringLiteral("networkmanagement"));
0284         notify->setIconName(QStringLiteral("dialog-warning"));
0285         notify->setTitle(identifier);
0286         notify->setText(text.toHtmlEscaped());
0287         m_notifications[device->uni()] = notify;
0288         notify->sendEvent();
0289     }
0290 }
0291 
0292 void Notification::addActiveConnection(const QString &path)
0293 {
0294     NetworkManager::ActiveConnection::Ptr ac = NetworkManager::findActiveConnection(path);
0295     if (ac && ac->isValid()) {
0296         addActiveConnection(ac);
0297     }
0298 }
0299 
0300 void Notification::addActiveConnection(const NetworkManager::ActiveConnection::Ptr &ac)
0301 {
0302     if (ac->vpn()) {
0303         NetworkManager::VpnConnection::Ptr vpnConnection = ac.objectCast<NetworkManager::VpnConnection>();
0304         connect(vpnConnection.data(), &NetworkManager::VpnConnection::stateChanged, this, &Notification::onVpnConnectionStateChanged);
0305     } else if (ac->type() != NetworkManager::ConnectionSettings::Bond //
0306                && ac->type() != NetworkManager::ConnectionSettings::Bridge //
0307                && ac->type() != NetworkManager::ConnectionSettings::Generic //
0308                && ac->type() != NetworkManager::ConnectionSettings::Infiniband //
0309                && ac->type() != NetworkManager::ConnectionSettings::Team //
0310                && ac->type() != NetworkManager::ConnectionSettings::Vlan //
0311                && ac->type() != NetworkManager::ConnectionSettings::Tun) {
0312         connect(ac.data(), &NetworkManager::ActiveConnection::stateChanged, this, &Notification::onActiveConnectionStateChanged);
0313     }
0314 }
0315 
0316 void Notification::onActiveConnectionStateChanged(NetworkManager::ActiveConnection::State state)
0317 {
0318     auto ac = qobject_cast<NetworkManager::ActiveConnection *>(sender());
0319 
0320     QString eventId, text, iconName;
0321     const QString acName = ac->id();
0322     const QString connectionId = ac->id();
0323 
0324     if (state == NetworkManager::ActiveConnection::Activated) {
0325         // Don't send notifications about activated connections just because the
0326         // daemon was launched as these were not explicitly user-initiated actions,
0327         // and notifications for automatic actions and background processes are
0328         // annoying and unnecessary
0329         if (m_justLaunched) {
0330             qCDebug(PLASMA_NM_KDED_LOG) << "Not emitting connection activated notification as the daemon was just launched";
0331             return;
0332         }
0333 
0334         // Also don't notify for re-made connections after waking up from sleep
0335         auto foundConnection = std::find_if(m_activeConnectionsBeforeSleep.constBegin(), m_activeConnectionsBeforeSleep.constEnd(), [ac](const QString &uuid) {
0336             return uuid == ac->uuid();
0337         });
0338 
0339         if (foundConnection != m_activeConnectionsBeforeSleep.constEnd()) {
0340             qCDebug(PLASMA_NM_KDED_LOG) << "Not emitting connection activated notification as the connection was active prior to suspend";
0341             return;
0342         }
0343 
0344         eventId = QStringLiteral("ConnectionActivated");
0345         text = i18n("Connection '%1' activated.", acName);
0346 
0347         switch (ac->type()) {
0348         case NetworkManager::ConnectionSettings::Wireless:
0349             iconName = QStringLiteral("network-wireless-on");
0350             break;
0351         case NetworkManager::ConnectionSettings::Wired:
0352             iconName = QStringLiteral("network-wired-activated");
0353             break;
0354         case NetworkManager::ConnectionSettings::WireGuard:
0355             iconName = QStringLiteral("network-vpn");
0356             break;
0357         default: // silence warning
0358             break;
0359         }
0360     } else if (state == NetworkManager::ActiveConnection::Deactivated) {
0361         if (m_preparingForSleep) {
0362             qCDebug(PLASMA_NM_KDED_LOG) << "Not emitting connection deactivated notification as we're about to suspend";
0363             return;
0364         }
0365 
0366         if (m_checkActiveConnectionOnResumeTimer && m_checkActiveConnectionOnResumeTimer->isActive()) {
0367             qCDebug(PLASMA_NM_KDED_LOG) << "Not emitting connection deactivated notification as we've just woken up from suspend";
0368             return;
0369         }
0370 
0371         eventId = QStringLiteral("ConnectionDeactivated");
0372         text = i18n("Connection '%1' deactivated.", acName);
0373 
0374         switch (ac->type()) {
0375         case NetworkManager::ConnectionSettings::Wireless:
0376             iconName = QStringLiteral("network-wireless-disconnected");
0377             break;
0378         case NetworkManager::ConnectionSettings::Wired:
0379             iconName = QStringLiteral("network-unavailable");
0380             break;
0381         default: // silence warning
0382             break;
0383         }
0384     } else {
0385         qCWarning(PLASMA_NM_KDED_LOG) << "Unhandled active connection state change: " << state;
0386         return;
0387     }
0388 
0389     KNotification *notify = m_notifications.value(connectionId);
0390 
0391     if (!notify) {
0392         notify = new KNotification(eventId, KNotification::CloseOnTimeout);
0393         connect(notify, &KNotification::closed, this, &Notification::notificationClosed);
0394         notify->setProperty("uni", connectionId);
0395         notify->setComponentName(QStringLiteral("networkmanagement"));
0396         m_notifications[connectionId] = notify;
0397     }
0398 
0399     if (!iconName.isEmpty()) {
0400         notify->setIconName(iconName);
0401     } else {
0402         if (state == NetworkManager::ActiveConnection::Activated) {
0403             notify->setIconName(QStringLiteral("dialog-information"));
0404         } else {
0405             notify->setIconName(QStringLiteral("dialog-warning"));
0406         }
0407     }
0408     notify->setTitle(acName);
0409     notify->setText(text.toHtmlEscaped());
0410     notify->sendEvent();
0411 }
0412 
0413 void Notification::onVpnConnectionStateChanged(NetworkManager::VpnConnection::State state, NetworkManager::VpnConnection::StateChangeReason reason)
0414 {
0415     auto vpn = qobject_cast<NetworkManager::VpnConnection *>(sender());
0416 
0417     QString eventId, text;
0418     const QString vpnName = vpn->connection()->name();
0419     const QString connectionId = vpn->path();
0420 
0421     if (state == NetworkManager::VpnConnection::Activated) {
0422         eventId = QStringLiteral("ConnectionActivated");
0423         text = i18n("VPN connection '%1' activated.", vpnName);
0424     } else if (state == NetworkManager::VpnConnection::Failed) {
0425         eventId = QStringLiteral("FailedToActivateConnection");
0426         text = i18n("VPN connection '%1' failed to activate.", vpnName);
0427     } else if (state == NetworkManager::VpnConnection::Disconnected) {
0428         eventId = QStringLiteral("ConnectionDeactivated");
0429         text = i18n("VPN connection '%1' deactivated.", vpnName);
0430     } else {
0431         qCWarning(PLASMA_NM_KDED_LOG) << "Unhandled VPN connection state change: " << state;
0432         return;
0433     }
0434 
0435     switch (reason) {
0436     case NetworkManager::VpnConnection::UserDisconnectedReason:
0437         text = i18n("VPN connection '%1' deactivated.", vpnName);
0438         break;
0439     case NetworkManager::VpnConnection::DeviceDisconnectedReason:
0440         text = i18n("VPN connection '%1' was deactivated because the device it was using was disconnected.", vpnName);
0441         break;
0442     case NetworkManager::VpnConnection::ServiceStoppedReason:
0443         text = i18n("The service providing the VPN connection '%1' was stopped.", vpnName);
0444         break;
0445     case NetworkManager::VpnConnection::IpConfigInvalidReason:
0446         text = i18n("The IP config of the VPN connection '%1', was invalid.", vpnName);
0447         break;
0448     case NetworkManager::VpnConnection::ConnectTimeoutReason:
0449         text = i18n("The connection attempt to the VPN service timed out.");
0450         break;
0451     case NetworkManager::VpnConnection::ServiceStartTimeoutReason:
0452         text = i18n("A timeout occurred while starting the service providing the VPN connection '%1'.", vpnName);
0453         break;
0454     case NetworkManager::VpnConnection::ServiceStartFailedReason:
0455         text = i18n("Starting the service providing the VPN connection '%1' failed.", vpnName);
0456         break;
0457     case NetworkManager::VpnConnection::NoSecretsReason:
0458         text = i18n("Necessary secrets for the VPN connection '%1' were not provided.", vpnName);
0459         break;
0460     case NetworkManager::VpnConnection::LoginFailedReason:
0461         text = i18n("Authentication to the VPN server failed.");
0462         break;
0463     case NetworkManager::VpnConnection::ConnectionRemovedReason:
0464         text = i18n("The connection was deleted.");
0465         break;
0466     default:
0467     case NetworkManager::VpnConnection::UnknownReason:
0468     case NetworkManager::VpnConnection::NoneReason:
0469         break;
0470     }
0471 
0472     auto notify = new KNotification(eventId, KNotification::CloseOnTimeout);
0473     connect(notify, &KNotification::closed, this, &Notification::notificationClosed);
0474     notify->setProperty("uni", connectionId);
0475     notify->setComponentName(QStringLiteral("networkmanagement"));
0476     if (state == NetworkManager::VpnConnection::Activated) {
0477         notify->setIconName(QStringLiteral("dialog-information"));
0478     } else if (state == NetworkManager::VpnConnection::Disconnected && reason == NetworkManager::VpnConnection::UserDisconnectedReason) {
0479         // Don't show warning icon if the user explicitly disconnected
0480         notify->setIconName(QStringLiteral("dialog-information"));
0481     } else {
0482         notify->setIconName(QStringLiteral("dialog-warning"));
0483     }
0484     notify->setTitle(vpnName);
0485     notify->setText(text.toHtmlEscaped());
0486     m_notifications[connectionId] = notify;
0487     notify->sendEvent();
0488 }
0489 
0490 void Notification::notificationClosed()
0491 {
0492     auto notify = qobject_cast<KNotification *>(sender());
0493     m_notifications.remove(notify->property("uni").toString());
0494 }
0495 
0496 void Notification::onPrepareForSleep(bool sleep)
0497 {
0498     m_preparingForSleep = sleep;
0499 
0500     if (m_checkActiveConnectionOnResumeTimer) {
0501         m_checkActiveConnectionOnResumeTimer->stop();
0502     }
0503 
0504     if (sleep) {
0505         // store all active notifications so we don't show a "is connected" notification
0506         // on resume if we were connected previously
0507         m_activeConnectionsBeforeSleep.clear();
0508         const auto &connections = NetworkManager::activeConnections();
0509         for (const auto &connection : connections) {
0510             if (!connection->vpn() && connection->state() == NetworkManager::ActiveConnection::State::Activated) {
0511                 m_activeConnectionsBeforeSleep << connection->uuid();
0512             }
0513         }
0514     } else {
0515         if (!m_checkActiveConnectionOnResumeTimer) {
0516             m_checkActiveConnectionOnResumeTimer = new QTimer(this);
0517             m_checkActiveConnectionOnResumeTimer->setInterval(10s);
0518             m_checkActiveConnectionOnResumeTimer->setSingleShot(true);
0519             connect(m_checkActiveConnectionOnResumeTimer, &QTimer::timeout, this, &Notification::onCheckActiveConnectionOnResume);
0520         }
0521 
0522         m_checkActiveConnectionOnResumeTimer->start();
0523     }
0524 }
0525 
0526 void Notification::onCheckActiveConnectionOnResume()
0527 {
0528     if (m_activeConnectionsBeforeSleep.isEmpty()) {
0529         // if we weren't connected before, don't bother telling us now :)
0530         return;
0531     }
0532 
0533     m_activeConnectionsBeforeSleep.clear();
0534 
0535     const auto ac = NetworkManager::activeConnections();
0536     if (std::any_of(ac.constBegin(), ac.constEnd(), [](const auto &connection) {
0537             return connection->state() == NetworkManager::ActiveConnection::State::Activated
0538                 || connection->state() == NetworkManager::ActiveConnection::State::Activating;
0539         })) {
0540         // we have an active or activating connection, don't tell the user we're no longer connected
0541         return;
0542     }
0543 
0544     auto notify = new KNotification(QStringLiteral("NoLongerConnected"), KNotification::CloseOnTimeout);
0545     connect(notify, &KNotification::closed, this, &Notification::notificationClosed);
0546     const QString uni = QStringLiteral("offlineNotification");
0547     notify->setProperty("uni", uni);
0548     notify->setComponentName(QStringLiteral("networkmanagement"));
0549     notify->setIconName(QStringLiteral("dialog-warning"));
0550     notify->setTitle(i18n("No Network Connection"));
0551     notify->setText(i18n("You are no longer connected to a network."));
0552     m_notifications[uni] = notify;
0553     notify->sendEvent();
0554 }