diff --git a/src/constants.h b/src/constants.h new file mode 100644 index 0000000..1e58ea0 --- /dev/null +++ b/src/constants.h @@ -0,0 +1,11 @@ +#ifndef SAILPUSH_CONSTANTS_H +#define SAILPUSH_CONSTANTS_H + +// Shared path constants used across daemon components. +// Both NotificationManager (writer) and DbusInterface (reader) use this +// to construct the pending_open file path from their cachePath. +namespace SailPushPaths { + inline constexpr const char *PENDING_OPEN = "/pending_open"; +} + +#endif // SAILPUSH_CONSTANTS_H diff --git a/src/credentialstore.cpp b/src/credentialstore.cpp index 5272831..c90bbe2 100644 --- a/src/credentialstore.cpp +++ b/src/credentialstore.cpp @@ -93,7 +93,7 @@ bool CredentialStore::checkSecretExists() return gsr.result().errorCode() == Sailfish::Secrets::Result::NoError; } -bool CredentialStore::save(const QString &secret, const QString &deviceId, const QString &userKey, const QString &deviceName) +bool CredentialStore::save(const QString &secret, const QString &deviceId) { if (!m_manager.isInitialized()) { m_lastError = LoadError::BackendUnavailable; @@ -108,8 +108,6 @@ bool CredentialStore::save(const QString &secret, const QString &deviceId, const QJsonObject obj; obj.insert("secret", secret); obj.insert("device_id", deviceId); - obj.insert("user_key", userKey); - obj.insert("device_name", deviceName); QByteArray jsonData = QJsonDocument(obj).toJson(QJsonDocument::Compact); // Delete existing secret first (save() must succeed even if secret already exists) @@ -153,7 +151,7 @@ bool CredentialStore::save(const QString &secret, const QString &deviceId, const return true; } -bool CredentialStore::load(QString &secret, QString &deviceId, QString &userKey, QString &deviceName) +bool CredentialStore::load(QString &secret, QString &deviceId) { m_lastError = LoadError::None; @@ -206,8 +204,6 @@ bool CredentialStore::load(QString &secret, QString &deviceId, QString &userKey, QJsonObject obj = doc.object(); secret = obj.value("secret").toString(); deviceId = obj.value("device_id").toString(); - userKey = obj.value("user_key").toString(); - deviceName = obj.value("device_name").toString(); if (secret.isEmpty() || deviceId.isEmpty()) { qCWarning(lcCredentialStore) << "Stored credentials have empty required fields"; diff --git a/src/credentialstore.h b/src/credentialstore.h index 8dc1a1b..bced791 100644 --- a/src/credentialstore.h +++ b/src/credentialstore.h @@ -12,8 +12,8 @@ class CredentialStore : public ICredentialStore { explicit CredentialStore(const QString &dataPath, QObject *parent = nullptr); ~CredentialStore() override; - bool save(const QString &secret, const QString &deviceId, const QString &userKey, const QString &deviceName) override; - bool load(QString &secret, QString &deviceId, QString &userKey, QString &deviceName) override; + bool save(const QString &secret, const QString &deviceId) override; + bool load(QString &secret, QString &deviceId) override; bool clear() override; bool hasCredentials() const override; LoadError lastError() const override { return m_lastError; } diff --git a/src/daemon.cpp b/src/daemon.cpp index 28b0da5..e45763b 100644 --- a/src/daemon.cpp +++ b/src/daemon.cpp @@ -12,6 +12,17 @@ Q_LOGGING_CATEGORY(lcDaemon, "com.zackslash.sailpush.daemon") static const int WS_DISCONNECT_TIMEOUT_MS = 30000; static const int DEFAULT_POLLING_INTERVAL_MS = 5 * 60 * 1000; +static int pollingIntervalFromIndex(int index) +{ + switch (index) { + case 0: return 60 * 1000; + case 1: return 5 * 60 * 1000; + case 2: return 15 * 60 * 1000; + case 3: return 30 * 60 * 1000; + default: return DEFAULT_POLLING_INTERVAL_MS; + } +} + QString Daemon::dataPath() { return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) @@ -112,8 +123,8 @@ void Daemon::start() qCWarning(lcDaemon) << "Failed to register D-Bus service, continuing anyway"; } - QString secret, deviceId, userKey, deviceName; - if (m_credentials->load(secret, deviceId, userKey, deviceName)) { + QString secret, deviceId; + if (m_credentials->load(secret, deviceId)) { m_secret = secret; m_deviceId = deviceId; m_credentialsLoaded = true; @@ -141,14 +152,7 @@ void Daemon::loadSettings() m_pollingEnabled = settings.value("pollingFallback", true).toBool(); int intervalIndex = settings.value("pollingIntervalIndex", 1).toInt(); - - switch (intervalIndex) { - case 0: m_pollingIntervalMs = 60 * 1000; break; - case 1: m_pollingIntervalMs = 5 * 60 * 1000; break; - case 2: m_pollingIntervalMs = 15 * 60 * 1000; break; - case 3: m_pollingIntervalMs = 30 * 60 * 1000; break; - default: m_pollingIntervalMs = DEFAULT_POLLING_INTERVAL_MS; break; - } + m_pollingIntervalMs = pollingIntervalFromIndex(intervalIndex); qCInfo(lcDaemon) << "Settings loaded: polling=" << m_pollingEnabled << "interval=" << m_pollingIntervalMs; @@ -175,10 +179,6 @@ void Daemon::performSync() m_cpuKeepalive->start(); } m_client->downloadMessages(m_secret, m_deviceId); - - if (!m_startupSyncDone) { - m_startupSyncDone = true; - } } void Daemon::deleteMessagesUpTo(const QString &highestId) @@ -208,14 +208,6 @@ void Daemon::publishNotificationForMessage(const Message &msg, bool isNew) } } -void Daemon::handleEmergencyMessage(const Message &msg) -{ - if (msg.isEmergency() && !msg.acked) { - qCInfo(lcDaemon) << "Emergency message detected:" << msg.id; - publishNotificationForMessage(msg, true); - } -} - void Daemon::onDeviceRegistered(const QString &deviceId) { qCInfo(lcDaemon) << "Device registered:" << deviceId; @@ -240,7 +232,10 @@ void Daemon::onMessagesDownloaded(const QList &messages) if (!m_store->containsMessage(msg.id)) { m_store->addMessage(msg); newMessages.append(msg); - handleEmergencyMessage(msg); + if (msg.isEmergency() && !msg.acked) { + qCInfo(lcDaemon) << "Emergency message detected:" << msg.id; + publishNotificationForMessage(msg, true); + } } } @@ -254,7 +249,10 @@ void Daemon::onMessagesDownloaded(const QList &messages) if (!newMessages.isEmpty() && m_startupSyncDone) { for (const Message &msg : newMessages) { - if (m_systemNotifications) { + // Emergency messages already received a notification in the + // inline check above; skip the redundant notification here + // while still signalling the UI that a new message arrived. + if (m_systemNotifications && !msg.isEmergency()) { publishNotificationForMessage(msg, true); } m_dbus->onMessageReceived(msg); @@ -265,6 +263,7 @@ void Daemon::onMessagesDownloaded(const QList &messages) qCInfo(lcDaemon) << "Connecting WebSocket after sync"; m_wsManager->connectToServer(m_deviceId, m_secret); } + m_startupSyncDone = true; updateDiagnostics(); } @@ -300,14 +299,6 @@ void Daemon::onMessagesDownloadFailed(const QString &error) void Daemon::onMessagesDeleted() { qCInfo(lcDaemon) << "Messages deleted from server"; - if (!m_pendingDeleteIds.isEmpty()) { - for (const QString &id : m_pendingDeleteIds) { - m_store->removeMessage(id); - } - m_store->save(); - m_dbus->notifyUnreadCountChanged(); - m_pendingDeleteIds.clear(); - } } void Daemon::onMessageDeleteFailed(const QString &error) @@ -422,8 +413,8 @@ void Daemon::onDbusRequestSync() void Daemon::onDbusRequestReloadCredentials() { qCInfo(lcDaemon) << "D-Bus: reloading credentials"; - QString secret, deviceId, userKey, deviceName; - if (m_credentials->load(secret, deviceId, userKey, deviceName)) { + QString secret, deviceId; + if (m_credentials->load(secret, deviceId)) { m_secret = secret; m_deviceId = deviceId; m_credentialsLoaded = true; @@ -458,14 +449,7 @@ void Daemon::onDbusRequestUpdateSettings(const QVariantMap &settings) m_pollingEnabled = settings.value("pollingFallback").toBool(); } if (settings.contains("pollingIntervalIndex")) { - int intervalIndex = settings.value("pollingIntervalIndex").toInt(); - switch (intervalIndex) { - case 0: m_pollingIntervalMs = 60 * 1000; break; - case 1: m_pollingIntervalMs = 5 * 60 * 1000; break; - case 2: m_pollingIntervalMs = 15 * 60 * 1000; break; - case 3: m_pollingIntervalMs = 30 * 60 * 1000; break; - default: m_pollingIntervalMs = DEFAULT_POLLING_INTERVAL_MS; break; - } + m_pollingIntervalMs = pollingIntervalFromIndex(settings.value("pollingIntervalIndex").toInt()); } if (settings.contains("preventDeepSleep")) { m_preventDeepSleep = settings.value("preventDeepSleep").toBool(); @@ -497,9 +481,9 @@ void Daemon::onDbusRequestAcknowledge(const QString &receipt) void Daemon::onDbusRequestDeleteMessage(const QString &messageId) { qCInfo(lcDaemon) << "D-Bus: delete message:" << messageId; - // Track for deletion after server confirms - m_pendingDeleteIds.insert(messageId); - m_client->deleteMessages(m_secret, m_deviceId, messageId); + m_store->removeMessage(messageId); + m_store->save(); + m_dbus->notifyUnreadCountChanged(); } void Daemon::onDbusRequestQuit() @@ -508,49 +492,42 @@ void Daemon::onDbusRequestQuit() QCoreApplication::quit(); } -void Daemon::onDbusRequestOpenMessage(const QString &messageId) +bool Daemon::isUiRunning() { - qCInfo(lcDaemon) << "D-Bus: open message requested:" << messageId; - - // Write pending file so the app can read it on startup - m_notificationManager->writePendingOpenMessage(messageId); - - // Emit signal for warm-start case (UI already running and receives this) - m_dbus->notifyOpenMessageRequested(messageId); - - // Check if the UI is already running by scanning /proc for another - // sailpush process besides our own (the daemon). - // If it is, the D-Bus signal above is sufficient — don't spawn a duplicate. - // If it's not, launch via invoker for cold start. - bool uiRunning = false; - qint64 myPid = QCoreApplication::applicationPid(); + const qint64 myPid = QCoreApplication::applicationPid(); QDir procDir("/proc"); for (const QString &entry : procDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { bool ok; - qint64 pid = entry.toLongLong(&ok); - if (!ok || pid == myPid) continue; - + const qint64 pid = entry.toLongLong(&ok); + if (!ok || pid == myPid) + continue; QFile cmdlineFile(QStringLiteral("/proc/%1/cmdline").arg(pid)); - if (cmdlineFile.open(QIODevice::ReadOnly)) { - QByteArray cmdline = cmdlineFile.readAll(); - // Match UI process: starts with "sailpush" but is NOT a daemon instance - if ((cmdline.startsWith("sailpush") || cmdline.startsWith("/usr/bin/sailpush")) - && !cmdline.contains("--daemon")) { - uiRunning = true; - break; - } + if (!cmdlineFile.open(QIODevice::ReadOnly)) + continue; + const QByteArray cmdline = cmdlineFile.readAll(); + if ((cmdline.startsWith("sailpush") || cmdline.startsWith("/usr/bin/sailpush")) + && !cmdline.contains("--daemon")) { + return true; } } + return false; +} + +void Daemon::onDbusRequestOpenMessage(const QString &messageId) +{ + qCInfo(lcDaemon) << "D-Bus: open message requested:" << messageId; + + m_notificationManager->writePendingOpenMessage(messageId); + m_dbus->notifyOpenMessageRequested(messageId); - if (!uiRunning) { - // Cold start: launch the app via invoker + if (isUiRunning()) { + qCInfo(lcDaemon) << "UI already running, relying on D-Bus signal"; + } else { QTimer::singleShot(0, this, [this]() { qCInfo(lcDaemon) << "Activating app via invoker (cold start)"; QProcess::startDetached("/usr/bin/invoker", QStringList() << "--type=silica-qt5" << "/usr/bin/sailpush"); }); - } else { - qCInfo(lcDaemon) << "UI already running, relying on D-Bus signal"; } } @@ -579,7 +556,6 @@ void Daemon::updateDiagnostics() diag["wsDisconnectTimerActive"] = m_wsDisconnectTimer->isActive(); diag["secretPresent"] = !m_secret.isEmpty(); diag["deviceIdPresent"] = !m_deviceId.isEmpty(); - diag["pendingDeletes"] = m_pendingDeleteIds.size(); if (!m_credentialError.isEmpty()) { diag["credentialError"] = m_credentialError; } diff --git a/src/daemon.h b/src/daemon.h index f28e1d5..d06cb6e 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "sailpushclient.h" #include "websocketmanager.h" @@ -61,7 +60,7 @@ private slots: void performSync(); void deleteMessagesUpTo(const QString &highestId); void publishNotificationForMessage(const Message &msg, bool isNew); - void handleEmergencyMessage(const Message &msg); + bool isUiRunning(); void updateDiagnostics(); SailPushClient *m_client; @@ -79,7 +78,6 @@ private slots: QString m_secret; QString m_deviceId; - QSet m_pendingDeleteIds; bool m_startupSyncDone; bool m_credentialsLoaded; diff --git a/src/dbusinterface.cpp b/src/dbusinterface.cpp index 62dc826..353be6a 100644 --- a/src/dbusinterface.cpp +++ b/src/dbusinterface.cpp @@ -1,4 +1,5 @@ #include "dbusinterface.h" +#include "constants.h" #include #include #include @@ -148,7 +149,7 @@ void DbusInterface::OpenMessage(const QString &messageId) QString DbusInterface::GetPendingOpenMessage() { - QString path = m_cachePath + "/pending_open"; + QString path = m_cachePath + SailPushPaths::PENDING_OPEN; QFile file(path); if (file.open(QIODevice::ReadOnly)) { QString messageId = QString::fromUtf8(file.readAll()).trimmed(); @@ -217,21 +218,31 @@ void DbusInterface::notifyUnreadCountChanged() emit unreadCountChanged(); } -void DbusInterface::refreshAutoStartCache() +void DbusInterface::runSystemctl(const QStringList &args, + std::function callback) { QProcess *proc = new QProcess(this); connect(proc, static_cast(&QProcess::finished), - this, [this, proc](int exitCode, QProcess::ExitStatus) { - Q_UNUSED(exitCode); - m_autoStartEnabled = QString::fromUtf8(proc->readAllStandardOutput()).trimmed() == "enabled"; - qCInfo(lcDbus) << "AutoStart cache refreshed:" << m_autoStartEnabled; + this, [this, proc, callback](int exitCode, QProcess::ExitStatus) { + QString stdOut = QString::fromUtf8(proc->readAllStandardOutput()).trimmed(); + QString stdErr = QString::fromUtf8(proc->readAllStandardError()).trimmed(); + callback(exitCode, stdOut, stdErr); proc->deleteLater(); }); connect(proc, &QProcess::errorOccurred, this, [this, proc](QProcess::ProcessError) { - qCWarning(lcDbus) << "Failed to check autostart status:" << proc->errorString(); + qCWarning(lcDbus) << "systemctl failed:" << proc->errorString(); proc->deleteLater(); }); - proc->start("/usr/bin/systemctl", {"--user", "is-enabled", "sailpush"}); + proc->start("/usr/bin/systemctl", args); +} + +void DbusInterface::refreshAutoStartCache() +{ + runSystemctl({"--user", "is-enabled", "sailpush"}, + [this](int, const QString &stdOut, const QString &) { + m_autoStartEnabled = (stdOut == "enabled"); + qCInfo(lcDbus) << "AutoStart cache refreshed:" << m_autoStartEnabled; + }); } bool DbusInterface::GetAutoStartEnabled() @@ -242,26 +253,17 @@ bool DbusInterface::GetAutoStartEnabled() void DbusInterface::SetAutoStartEnabled(bool enabled) { qCInfo(lcDbus) << "Setting AutoStart to" << enabled; - QProcess *proc = new QProcess(this); - connect(proc, static_cast(&QProcess::finished), - this, [this, proc, enabled](int exitCode, QProcess::ExitStatus) { - QString stdOut = QString::fromUtf8(proc->readAllStandardOutput()).trimmed(); - QString stdErr = QString::fromUtf8(proc->readAllStandardError()).trimmed(); - if (!stdOut.isEmpty()) qCInfo(lcDbus) << "systemctl stdout:" << stdOut; - if (!stdErr.isEmpty()) qCWarning(lcDbus) << "systemctl stderr:" << stdErr; - if (exitCode != 0) { - qCWarning(lcDbus) << "systemctl exit code:" << exitCode; - } else { - m_autoStartEnabled = enabled; - qCInfo(lcDbus) << "AutoStart cache updated:" << m_autoStartEnabled; - } - proc->deleteLater(); - }); - connect(proc, &QProcess::errorOccurred, this, [this, proc](QProcess::ProcessError) { - qCWarning(lcDbus) << "Failed to set autostart:" << proc->errorString(); - proc->deleteLater(); - }); - proc->start("/usr/bin/systemctl", {"--user", enabled ? "enable" : "disable", "sailpush"}); + runSystemctl({"--user", enabled ? "enable" : "disable", "sailpush"}, + [this, enabled](int exitCode, const QString &stdOut, const QString &stdErr) { + if (!stdOut.isEmpty()) qCInfo(lcDbus) << "systemctl stdout:" << stdOut; + if (!stdErr.isEmpty()) qCWarning(lcDbus) << "systemctl stderr:" << stdErr; + if (exitCode != 0) { + qCWarning(lcDbus) << "systemctl exit code:" << exitCode; + } else { + m_autoStartEnabled = enabled; + qCInfo(lcDbus) << "AutoStart cache updated:" << m_autoStartEnabled; + } + }); } void DbusInterface::onMessageReceived(const Message &msg) diff --git a/src/dbusinterface.h b/src/dbusinterface.h index 7fbf167..ffbfd74 100644 --- a/src/dbusinterface.h +++ b/src/dbusinterface.h @@ -3,6 +3,7 @@ #include #include +#include #include "message.h" #include "messagestore.h" #include "websocketmanager.h" @@ -87,6 +88,8 @@ private slots: QString wsStateToString(WebSocketManager::ConnectionState state) const; void refreshAutoStartCache(); + void runSystemctl(const QStringList &args, + std::function callback); MessageStore *m_store; WebSocketManager *m_wsManager; diff --git a/src/filecredentialstore.cpp b/src/filecredentialstore.cpp index 6e638cd..5eb69e7 100644 --- a/src/filecredentialstore.cpp +++ b/src/filecredentialstore.cpp @@ -28,14 +28,12 @@ FileCredentialStore::~FileCredentialStore() m_cachedKey.fill(0); } -bool FileCredentialStore::save(const QString &secret, const QString &deviceId, const QString &userKey, const QString &deviceName) +bool FileCredentialStore::save(const QString &secret, const QString &deviceId) { QJsonObject obj; obj.insert("version", 2); obj.insert("secret", QString::fromUtf8(encrypt(secret.toUtf8()))); obj.insert("device_id", QString::fromUtf8(encrypt(deviceId.toUtf8()))); - obj.insert("user_key", QString::fromUtf8(encrypt(userKey.toUtf8()))); - obj.insert("device_name", QString::fromUtf8(encrypt(deviceName.toUtf8()))); QJsonDocument doc(obj); QFile file(storagePath()); @@ -63,7 +61,7 @@ bool FileCredentialStore::save(const QString &secret, const QString &deviceId, c return true; } -bool FileCredentialStore::load(QString &secret, QString &deviceId, QString &userKey, QString &deviceName) +bool FileCredentialStore::load(QString &secret, QString &deviceId) { m_lastError = LoadError::None; @@ -112,8 +110,6 @@ bool FileCredentialStore::load(QString &secret, QString &deviceId, QString &user secret = decryptField("secret"); deviceId = decryptField("device_id"); - userKey = decryptField("user_key"); - deviceName = decryptField("device_name"); if (!ok) { qCWarning(lcFileCredentialStore) << "Failed to decrypt some credential fields"; diff --git a/src/filecredentialstore.h b/src/filecredentialstore.h index 964997e..b5b68d5 100644 --- a/src/filecredentialstore.h +++ b/src/filecredentialstore.h @@ -10,8 +10,8 @@ class FileCredentialStore : public ICredentialStore { explicit FileCredentialStore(const QString &dataPath, QObject *parent = nullptr); ~FileCredentialStore() override; - bool save(const QString &secret, const QString &deviceId, const QString &userKey, const QString &deviceName) override; - bool load(QString &secret, QString &deviceId, QString &userKey, QString &deviceName) override; + bool save(const QString &secret, const QString &deviceId) override; + bool load(QString &secret, QString &deviceId) override; bool clear() override; bool hasCredentials() const override; LoadError lastError() const override { return m_lastError; } diff --git a/src/icredentialstore.h b/src/icredentialstore.h index 2059d8d..14ea0b9 100644 --- a/src/icredentialstore.h +++ b/src/icredentialstore.h @@ -18,8 +18,8 @@ class ICredentialStore : public QObject { explicit ICredentialStore(QObject *parent = nullptr) : QObject(parent) {} virtual ~ICredentialStore() = default; - virtual bool save(const QString &secret, const QString &deviceId, const QString &userKey, const QString &deviceName) = 0; - virtual bool load(QString &secret, QString &deviceId, QString &userKey, QString &deviceName) = 0; + virtual bool save(const QString &secret, const QString &deviceId) = 0; + virtual bool load(QString &secret, QString &deviceId) = 0; virtual bool clear() = 0; virtual bool hasCredentials() const = 0; virtual LoadError lastError() const = 0; diff --git a/src/loginhelper.cpp b/src/loginhelper.cpp index 136e573..0747eba 100644 --- a/src/loginhelper.cpp +++ b/src/loginhelper.cpp @@ -18,8 +18,8 @@ LoginHelper::LoginHelper(QObject *parent) , m_registering(false) , m_needsTwoFactor(false) { - QString secret, deviceId, userKey, deviceName; - if (!m_store->load(secret, deviceId, userKey, deviceName)) { + QString secret, deviceId; + if (!m_store->load(secret, deviceId)) { ICredentialStore::LoadError err = m_store->lastError(); if (err == ICredentialStore::LoadError::InvalidFormat) { m_migrationReason = tr("App was upgraded — saved credentials use an older format and cannot be migrated. Please log in again."); @@ -54,9 +54,7 @@ void LoginHelper::cancel() setNeedsTwoFactor(false); setErrorString(QString()); - m_pendingUserKey.clear(); m_pendingSecret.clear(); - m_pendingDeviceName.clear(); } void LoginHelper::logout() @@ -77,13 +75,12 @@ void LoginHelper::setMigrationReason(const QString &reason) void LoginHelper::onLoginSuccess(const QString &userKey, const QString &secret) { qCInfo(lcLoginHelper) << "Login successful, registering device"; - m_pendingUserKey = userKey; + Q_UNUSED(userKey) m_pendingSecret = secret; setLoggingIn(false); setRegistering(true); - m_pendingDeviceName = generateDeviceName(); - m_client->registerDevice(secret, m_pendingDeviceName); + m_client->registerDevice(secret, generateDeviceName()); } void LoginHelper::onLoginFailed(const QString &error) @@ -107,7 +104,7 @@ void LoginHelper::onDeviceRegistered(const QString &deviceId) qCInfo(lcLoginHelper) << "Device registered:" << deviceId; setRegistering(false); - bool saved = m_store->save(m_pendingSecret, deviceId, m_pendingUserKey, m_pendingDeviceName); + bool saved = m_store->save(m_pendingSecret, deviceId); if (saved) { qCInfo(lcLoginHelper) << "Credentials saved successfully"; if (!m_migrationReason.isEmpty()) { @@ -121,9 +118,7 @@ void LoginHelper::onDeviceRegistered(const QString &deviceId) emit loginFailed(tr("Failed to save credentials")); } - m_pendingUserKey.clear(); m_pendingSecret.clear(); - m_pendingDeviceName.clear(); } void LoginHelper::onDeviceRegistrationFailed(const QString &error) @@ -133,9 +128,7 @@ void LoginHelper::onDeviceRegistrationFailed(const QString &error) setErrorString(error); emit loginFailed(error); - m_pendingUserKey.clear(); m_pendingSecret.clear(); - m_pendingDeviceName.clear(); } void LoginHelper::setLoggingIn(bool value) diff --git a/src/loginhelper.h b/src/loginhelper.h index b97ed45..5823135 100644 --- a/src/loginhelper.h +++ b/src/loginhelper.h @@ -61,9 +61,7 @@ private slots: bool m_needsTwoFactor; QString m_errorString; QString m_migrationReason; - QString m_pendingUserKey; QString m_pendingSecret; - QString m_pendingDeviceName; }; #endif // LOGINHELPER_H diff --git a/src/main.cpp b/src/main.cpp index 3e9c584..91701ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,19 +27,20 @@ static const QString DBUS_SERVICE = "com.zackslash.sailpush"; static void loadTranslations(QCoreApplication *app) { QLocale locale = QLocale::system(); + const QString localeName = locale.name(); auto *translator = new QTranslator(app); QString transDir = SailfishApp::pathTo("translations").toLocalFile(); - if (translator->load(locale.name(), "sailpush", "_", transDir)) { + if (translator->load(localeName, "sailpush", "_", transDir)) { app->installTranslator(translator); - qCInfo(lcMain) << "Loaded translations for" << locale.name(); + qCInfo(lcMain) << "Loaded translations for" << localeName; } else { // Try language-only (e.g. "de" from "de_DE") - QString lang = locale.name().left(locale.name().indexOf('_')); + QString lang = localeName.left(localeName.indexOf('_')); if (!lang.isEmpty() && translator->load(lang, "sailpush", "_", transDir)) { app->installTranslator(translator); qCInfo(lcMain) << "Loaded translations for" << lang; } else { - qCInfo(lcMain) << "No translations found for" << locale.name() << "- using English"; + qCInfo(lcMain) << "No translations found for" << localeName << "- using English"; delete translator; } } diff --git a/src/messagestore.cpp b/src/messagestore.cpp index f6b4be8..21ea3e1 100644 --- a/src/messagestore.cpp +++ b/src/messagestore.cpp @@ -41,10 +41,14 @@ bool MessageStore::load() } m_messages.clear(); + m_idSet.clear(); QJsonArray array = doc.array(); for (const QJsonValue &v : array) { m_messages.append(Message::fromJson(v.toObject())); } + for (const Message &msg : m_messages) { + m_idSet.insert(msg.id); + } qCInfo(lcMessageStore) << "Loaded" << m_messages.size() << "messages from store"; return true; @@ -80,17 +84,6 @@ bool MessageStore::save() const return true; } -QList MessageStore::unreadMessages() const -{ - QList unread; - for (const Message &msg : m_messages) { - if (!msg.read) { - unread.append(msg); - } - } - return unread; -} - int MessageStore::unreadCount() const { int count = 0; @@ -104,20 +97,22 @@ int MessageStore::unreadCount() const void MessageStore::addMessage(const Message &msg) { - if (containsMessage(msg.id)) { + if (m_idSet.contains(msg.id)) { return; } m_messages.prepend(msg); + m_idSet.insert(msg.id); trimMessages(); } void MessageStore::markAsRead(const QString &id) { + if (!m_idSet.contains(id)) { + return; + } for (int i = 0; i < m_messages.size(); ++i) { if (m_messages[i].id == id) { - Message msg = m_messages[i]; - msg.read = true; - m_messages[i] = msg; + m_messages[i].read = true; break; } } @@ -126,43 +121,33 @@ void MessageStore::markAsRead(const QString &id) void MessageStore::markAllAsRead() { for (int i = 0; i < m_messages.size(); ++i) { - Message msg = m_messages[i]; - msg.read = true; - m_messages[i] = msg; + m_messages[i].read = true; } } void MessageStore::removeMessage(const QString &id) { + if (!m_idSet.contains(id)) { + return; + } for (int i = 0; i < m_messages.size(); ++i) { if (m_messages[i].id == id) { m_messages.removeAt(i); break; } } -} - -QString MessageStore::highestMessageId() const -{ - if (m_messages.isEmpty()) { - return QString(); - } - return m_messages.first().id; + m_idSet.remove(id); } bool MessageStore::containsMessage(const QString &id) const { - for (const Message &msg : m_messages) { - if (msg.id == id) { - return true; - } - } - return false; + return m_idSet.contains(id); } void MessageStore::trimMessages() { while (m_messages.size() > m_maxMessages) { + m_idSet.remove(m_messages.last().id); m_messages.removeLast(); } } diff --git a/src/messagestore.h b/src/messagestore.h index 8c2ca1b..b2d7787 100644 --- a/src/messagestore.h +++ b/src/messagestore.h @@ -3,6 +3,7 @@ #include #include +#include #include "message.h" class MessageStore : public QObject { @@ -17,7 +18,6 @@ class MessageStore : public QObject { bool save() const; QList messages() const { return m_messages; } - QList unreadMessages() const; int unreadCount() const; int totalCount() const { return m_messages.size(); } @@ -26,9 +26,6 @@ class MessageStore : public QObject { void markAllAsRead(); void removeMessage(const QString &id); - QString highestMessageId() const; - int maxMessages() const { return m_maxMessages; } - bool containsMessage(const QString &id) const; private: @@ -36,6 +33,7 @@ class MessageStore : public QObject { QString storagePath() const; QList m_messages; + QSet m_idSet; QString m_dataPath; int m_maxMessages; }; diff --git a/src/notificationmanager.cpp b/src/notificationmanager.cpp index 9c28ece..fe44030 100644 --- a/src/notificationmanager.cpp +++ b/src/notificationmanager.cpp @@ -1,4 +1,5 @@ #include "notificationmanager.h" +#include "constants.h" #include #include #include @@ -9,6 +10,7 @@ #include #include #include +#include Q_LOGGING_CATEGORY(lcNotification, "com.zackslash.sailpush.notification") @@ -23,8 +25,11 @@ NotificationManager::NotificationManager(const QString &cachePath, QObject *pare , m_cachePath(cachePath) { QDBusConnection::sessionBus().connect(DBUS_SERVICE, DBUS_PATH, DBUS_IFACE, - "ActionInvoked", this, - SLOT(onActionInvoked(uint, QString))); + "ActionInvoked", this, + SLOT(onActionInvoked(uint, QString))); + QDBusConnection::sessionBus().connect(DBUS_SERVICE, DBUS_PATH, DBUS_IFACE, + "NotificationClosed", this, + SLOT(onNotificationClosed(uint, uint))); } void NotificationManager::publishNotification(const Message &msg, int unreadCount, bool displayOn) @@ -116,6 +121,13 @@ void NotificationManager::onActionInvoked(uint id, const QString &actionKey) } } +void NotificationManager::onNotificationClosed(uint id, uint reason) +{ + Q_UNUSED(reason); + m_notificationMessageMap.remove(id); + m_notificationReceiptMap.remove(id); +} + QString NotificationManager::truncate(const QString &text, int maxLength) { if (text.length() <= maxLength) return text; @@ -146,13 +158,24 @@ QString NotificationManager::stripHtml(const QString &html) text.replace("—", "\u2014"); text.replace("–", "\u2013"); text.replace("…", "\u2026"); - // Numeric entities — safe to loop since replacements are single chars - QRegularExpressionMatch m; - while ((m = hexEntityRe.match(text)).hasMatch()) { + // Numeric entities — single pass using globalMatch, replacing in reverse + // order so earlier captured positions remain valid after each replacement. + QList hexMatches; + auto it = hexEntityRe.globalMatch(text); + while (it.hasNext()) + hexMatches.append(it.next()); + for (int i = hexMatches.size() - 1; i >= 0; --i) { + const auto &m = hexMatches[i]; text.replace(m.capturedStart(), m.capturedLength(), QChar(m.captured(1).toUInt(nullptr, 16))); } - while ((m = decEntityRe.match(text)).hasMatch()) { + + QList decMatches; + it = decEntityRe.globalMatch(text); + while (it.hasNext()) + decMatches.append(it.next()); + for (int i = decMatches.size() - 1; i >= 0; --i) { + const auto &m = decMatches[i]; text.replace(m.capturedStart(), m.capturedLength(), QChar(m.captured(1).toUInt())); } @@ -208,7 +231,7 @@ void NotificationManager::trackNotification(uint notifId, const Message &msg) void NotificationManager::writePendingOpenMessage(const QString &messageId) { - QString path = m_cachePath + "/pending_open"; + QString path = m_cachePath + SailPushPaths::PENDING_OPEN; QFile file(path); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { file.write(messageId.toUtf8()); diff --git a/src/notificationmanager.h b/src/notificationmanager.h index 5846635..052db8d 100644 --- a/src/notificationmanager.h +++ b/src/notificationmanager.h @@ -34,6 +34,7 @@ class NotificationManager : public QObject { private slots: void onActionInvoked(uint id, const QString &actionKey); + void onNotificationClosed(uint id, uint reason); private: void trackNotification(uint notifId, const Message &msg); diff --git a/src/sailpushclient.cpp b/src/sailpushclient.cpp index 5c8f500..4649184 100644 --- a/src/sailpushclient.cpp +++ b/src/sailpushclient.cpp @@ -21,123 +21,66 @@ static const int REQUEST_TIMEOUT_MS = 30000; SailPushClient::SailPushClient(QObject *parent) : QObject(parent) , m_networkManager(new QNetworkAccessManager(this)) - , m_userAgent(USER_AGENT) { } void SailPushClient::login(const QString &email, const QString &password, const QString &twofa) { - QUrl url(loginUrl()); - QNetworkRequest request(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); - QUrlQuery query; query.addQueryItem("email", email); query.addQueryItem("password", password); if (!twofa.isEmpty()) { query.addQueryItem("twofa", twofa); } - - QNetworkReply *reply = m_networkManager->post(request, query.toString(QUrl::FullyEncoded).toUtf8()); - QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); }); - reply->setProperty("action", "login"); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); }); + sendRequest(QUrl(loginUrl()), query.toString(QUrl::FullyEncoded).toUtf8(), Action::Login); } void SailPushClient::registerDevice(const QString &secret, const QString &deviceName) { - QUrl url(registerUrl()); - QNetworkRequest request(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); - QUrlQuery query; query.addQueryItem("secret", secret); query.addQueryItem("name", deviceName); query.addQueryItem("os", "O"); - - QNetworkReply *reply = m_networkManager->post(request, query.toString(QUrl::FullyEncoded).toUtf8()); - QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); }); - reply->setProperty("action", "register"); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); }); + sendRequest(QUrl(registerUrl()), query.toString(QUrl::FullyEncoded).toUtf8(), Action::Register); } void SailPushClient::downloadMessages(const QString &secret, const QString &deviceId) { - QUrl url(messagesUrl(secret, deviceId)); - QNetworkRequest request(url); - request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); - - QNetworkReply *reply = m_networkManager->get(request); - QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); }); - reply->setProperty("action", "download"); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); }); + sendRequest(QUrl(messagesUrl(secret, deviceId)), QByteArray(), Action::Download); } void SailPushClient::deleteMessages(const QString &secret, const QString &deviceId, const QString &highestMessageId) { - QUrl url(deleteUrl(deviceId)); - QNetworkRequest request(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); - QUrlQuery query; query.addQueryItem("secret", secret); query.addQueryItem("message", highestMessageId); - - QNetworkReply *reply = m_networkManager->post(request, query.toString(QUrl::FullyEncoded).toUtf8()); - QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); }); - reply->setProperty("action", "delete"); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); }); + sendRequest(QUrl(deleteUrl(deviceId)), query.toString(QUrl::FullyEncoded).toUtf8(), Action::Delete); } void SailPushClient::acknowledgeEmergency(const QString &secret, const QString &receipt) { - QUrl url(acknowledgeUrl(receipt)); - QNetworkRequest request(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); - QUrlQuery query; query.addQueryItem("secret", secret); - - QNetworkReply *reply = m_networkManager->post(request, query.toString(QUrl::FullyEncoded).toUtf8()); - QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); }); - reply->setProperty("action", "acknowledge"); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); }); + sendRequest(QUrl(acknowledgeUrl(receipt)), query.toString(QUrl::FullyEncoded).toUtf8(), Action::Acknowledge); } void SailPushClient::onReplyFinished(QNetworkReply *reply) { - QString action = reply->property("action").toString(); + Action action = static_cast(reply->property("action").toInt()); int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); QByteArray data = reply->readAll(); reply->deleteLater(); // Check for auth failures before generic error handling — Qt treats HTTP 4xx as NoError - if ((httpStatus == 401 || httpStatus == 403) && (action == "download" || action == "delete" || action == "acknowledge")) { - qCWarning(lcSailPushClient) << "HTTP" << httpStatus << "for action" << action << "— credentials rejected"; - QString errMsg = QStringLiteral("HTTP %1: credentials rejected").arg(httpStatus); - if (action == "download") emit messagesDownloadFailed(errMsg); - else if (action == "delete") emit messageDeleteFailed(errMsg); - else if (action == "acknowledge") emit emergencyAckFailed(errMsg); + if ((httpStatus == 401 || httpStatus == 403) && (action == Action::Download || action == Action::Delete || action == Action::Acknowledge)) { + qCWarning(lcSailPushClient) << "HTTP" << httpStatus << "for action" << static_cast(action) << "— credentials rejected"; + emitError(action, QStringLiteral("HTTP %1: credentials rejected").arg(httpStatus)); return; } if (reply->error() != QNetworkReply::NoError) { qCWarning(lcSailPushClient) << "Network error:" << reply->errorString(); - if (action == "login") { - emit loginFailed(reply->errorString()); - } else if (action == "register") { - emit deviceRegistrationFailed(reply->errorString()); - } else if (action == "download") { - emit messagesDownloadFailed(reply->errorString()); - } else if (action == "delete") { - emit messageDeleteFailed(reply->errorString()); - } else if (action == "acknowledge") { - emit emergencyAckFailed(reply->errorString()); - } + emitError(action, reply->errorString()); return; } @@ -145,18 +88,14 @@ void SailPushClient::onReplyFinished(QNetworkReply *reply) QJsonDocument doc = QJsonDocument::fromJson(data, &parseError); if (parseError.error != QJsonParseError::NoError) { qCWarning(lcSailPushClient) << "JSON parse error:" << parseError.errorString(); - if (action == "login") emit loginFailed(tr("Invalid server response")); - else if (action == "register") emit deviceRegistrationFailed(tr("Invalid server response")); - else if (action == "download") emit messagesDownloadFailed(tr("Invalid server response")); - else if (action == "delete") emit messageDeleteFailed(tr("Invalid server response")); - else if (action == "acknowledge") emit emergencyAckFailed(tr("Invalid server response")); + emitError(action, tr("Invalid server response")); return; } QJsonObject root = doc.object(); int status = root.value("status").toInt(0); - if (action == "login") { + if (action == Action::Login) { if (httpStatus == 412) { emit twoFactorRequired(); return; @@ -164,27 +103,17 @@ void SailPushClient::onReplyFinished(QNetworkReply *reply) if (status == 1) { emit loginSuccess(root.value("id").toString(), root.value("secret").toString()); } else { - QJsonArray errors = root.value("errors").toArray(); - QString errorStr; - for (const QJsonValue &e : errors) { - if (!errorStr.isEmpty()) errorStr += "; "; - errorStr += e.toString(); - } + QString errorStr = extractErrorMessage(root); emit loginFailed(errorStr.isEmpty() ? tr("Login failed") : errorStr); } - } else if (action == "register") { + } else if (action == Action::Register) { if (status == 1) { emit deviceRegistered(root.value("id").toString()); } else { - QJsonArray errors = root.value("errors").toArray(); - QString errorStr; - for (const QJsonValue &e : errors) { - if (!errorStr.isEmpty()) errorStr += "; "; - errorStr += e.toString(); - } + QString errorStr = extractErrorMessage(root); emit deviceRegistrationFailed(errorStr.isEmpty() ? tr("Registration failed") : errorStr); } - } else if (action == "download") { + } else if (action == Action::Download) { if (status == 1) { QList messages; QJsonArray msgs = root.value("messages").toArray(); @@ -193,41 +122,65 @@ void SailPushClient::onReplyFinished(QNetworkReply *reply) } emit messagesDownloaded(messages); } else { - QJsonArray errors = root.value("errors").toArray(); - QString errorStr; - for (const QJsonValue &e : errors) { - if (!errorStr.isEmpty()) errorStr += "; "; - errorStr += e.toString(); - } + QString errorStr = extractErrorMessage(root); emit messagesDownloadFailed(errorStr.isEmpty() ? tr("Download failed") : errorStr); } - } else if (action == "delete") { + } else if (action == Action::Delete) { if (status == 1) { emit messagesDeleted(); } else { - QJsonArray errors = root.value("errors").toArray(); - QString errorStr; - for (const QJsonValue &e : errors) { - if (!errorStr.isEmpty()) errorStr += "; "; - errorStr += e.toString(); - } + QString errorStr = extractErrorMessage(root); emit messageDeleteFailed(errorStr.isEmpty() ? tr("Delete failed") : errorStr); } - } else if (action == "acknowledge") { + } else if (action == Action::Acknowledge) { if (status == 1) { emit emergencyAcknowledged(); } else { - QJsonArray errors = root.value("errors").toArray(); - QString errorStr; - for (const QJsonValue &e : errors) { - if (!errorStr.isEmpty()) errorStr += "; "; - errorStr += e.toString(); - } + QString errorStr = extractErrorMessage(root); emit emergencyAckFailed(errorStr.isEmpty() ? tr("Acknowledge failed") : errorStr); } } } +QNetworkReply* SailPushClient::sendRequest(const QUrl &url, const QByteArray &postData, Action action) +{ + QNetworkRequest request(url); + if (!postData.isEmpty()) { + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + } + request.setHeader(QNetworkRequest::UserAgentHeader, USER_AGENT); + + QNetworkReply *reply = postData.isEmpty() + ? m_networkManager->get(request) + : m_networkManager->post(request, postData); + QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); }); + reply->setProperty("action", static_cast(action)); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); }); + return reply; +} + +void SailPushClient::emitError(Action action, const QString &message) +{ + switch (action) { + case Action::Login: emit loginFailed(message); break; + case Action::Register: emit deviceRegistrationFailed(message); break; + case Action::Download: emit messagesDownloadFailed(message); break; + case Action::Delete: emit messageDeleteFailed(message); break; + case Action::Acknowledge: emit emergencyAckFailed(message); break; + } +} + +QString SailPushClient::extractErrorMessage(const QJsonObject &root) +{ + QJsonArray errors = root.value("errors").toArray(); + QString errorStr; + for (const QJsonValue &e : errors) { + if (!errorStr.isEmpty()) errorStr += "; "; + errorStr += e.toString(); + } + return errorStr; +} + QString SailPushClient::loginUrl() { return QStringLiteral("%1/users/login.json").arg(API_BASE); } QString SailPushClient::registerUrl() { return QStringLiteral("%1/devices.json").arg(API_BASE); } // Note: secret is in query string — avoid logging this URL diff --git a/src/sailpushclient.h b/src/sailpushclient.h index 0d1b40e..c3efdc8 100644 --- a/src/sailpushclient.h +++ b/src/sailpushclient.h @@ -45,8 +45,19 @@ private slots: void onReplyFinished(QNetworkReply *reply); private: + enum class Action { + Login, + Register, + Download, + Delete, + Acknowledge + }; + + QNetworkReply* sendRequest(const QUrl &url, const QByteArray &postData, Action action); + void emitError(Action action, const QString &message); + static QString extractErrorMessage(const QJsonObject &root); + QNetworkAccessManager *m_networkManager; - QString m_userAgent; }; #endif // SAILPUSHCLIENT_H diff --git a/src/soundplayer.cpp b/src/soundplayer.cpp index bc43d3c..fa03ee5 100644 --- a/src/soundplayer.cpp +++ b/src/soundplayer.cpp @@ -56,7 +56,9 @@ bool SoundPlayer::soundExists(const QString &soundName, const QString &cacheDir) void SoundPlayer::downloadSound(const QString &soundName, const QString &cacheDir) { - m_downloadQueue.enqueue(qMakePair(soundName, cacheDir)); + QPair entry = qMakePair(soundName, cacheDir); + if (m_downloadQueue.contains(entry)) return; + m_downloadQueue.enqueue(entry); if (!m_downloading) { processNextDownload(); } diff --git a/src/websocketmanager.cpp b/src/websocketmanager.cpp index 8e6a716..503dfb9 100644 --- a/src/websocketmanager.cpp +++ b/src/websocketmanager.cpp @@ -17,8 +17,8 @@ WebSocketManager::WebSocketManager(QObject *parent) connect(m_webSocket, &QWebSocket::disconnected, this, &WebSocketManager::onDisconnected); connect(m_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketManager::onTextMessageReceived); connect(m_webSocket, &QWebSocket::binaryMessageReceived, this, &WebSocketManager::onBinaryMessageReceived); - connect(m_webSocket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(onError(QAbstractSocket::SocketError))); + connect(m_webSocket, QOverload::of(&QWebSocket::error), + this, &WebSocketManager::onError); connect(m_reconnectTimer, &QTimer::timeout, this, &WebSocketManager::onReconnectTimer); } @@ -46,6 +46,8 @@ void WebSocketManager::disconnectFromServer() m_autoReconnect = false; m_reconnectTimer->stop(); m_webSocket->close(); + m_secret.clear(); + m_deviceId.clear(); setState(ConnectionState::Disconnected); } @@ -75,9 +77,19 @@ void WebSocketManager::onDisconnected() void WebSocketManager::onTextMessageReceived(const QString &message) { - FrameType frame = static_cast(parseFrame(message.toUtf8()).toInt()); + FrameType frame = static_cast(parseFrame(message.toUtf8())); + handleFrame(frame); +} - switch (frame) { +void WebSocketManager::onBinaryMessageReceived(const QByteArray &message) +{ + FrameType frame = static_cast(parseFrame(message)); + handleFrame(frame); +} + +void WebSocketManager::handleFrame(FrameType type) +{ + switch (type) { case FrameType::NewMessage: qCInfo(lcWebSocket) << "New message available"; emit newMessageAvailable(); @@ -99,31 +111,7 @@ void WebSocketManager::onTextMessageReceived(const QString &message) case FrameType::KeepAlive: break; case FrameType::Unknown: - qCDebug(lcWebSocket) << "Unknown frame received:" << message; - break; - } -} - -void WebSocketManager::onBinaryMessageReceived(const QByteArray &message) -{ - FrameType frame = static_cast(parseFrame(message).toInt()); - - switch (frame) { - case FrameType::NewMessage: - emit newMessageAvailable(); - break; - case FrameType::Reload: - emit reloadRequested(); - break; - case FrameType::Error: - m_autoReconnect = false; - emit connectionError(QStringLiteral("Permanent server error.")); - break; - case FrameType::SessionClosedByServer: - m_autoReconnect = false; - emit sessionClosedByServer(); - break; - default: + qCDebug(lcWebSocket) << "Unknown frame received"; break; } } @@ -170,7 +158,7 @@ void WebSocketManager::setState(ConnectionState newState) } } -QVariant WebSocketManager::parseFrame(const QByteArray &data) +int WebSocketManager::parseFrame(const QByteArray &data) { if (data.isEmpty()) return static_cast(FrameType::Unknown); diff --git a/src/websocketmanager.h b/src/websocketmanager.h index 05771d1..419b9fb 100644 --- a/src/websocketmanager.h +++ b/src/websocketmanager.h @@ -2,7 +2,6 @@ #define WEBSOCKETMANAGER_H #include -#include #include #include #include @@ -65,7 +64,8 @@ private slots: void sendLoginFrame(); void scheduleReconnect(); void setState(ConnectionState newState); - Q_INVOKABLE QVariant parseFrame(const QByteArray &data); + Q_INVOKABLE int parseFrame(const QByteArray &data); + void handleFrame(FrameType type); QWebSocket *m_webSocket; QTimer *m_reconnectTimer; diff --git a/tests/tst_credentialstore.cpp b/tests/tst_credentialstore.cpp index a04b654..262dd80 100644 --- a/tests/tst_credentialstore.cpp +++ b/tests/tst_credentialstore.cpp @@ -47,28 +47,26 @@ void TestCredentialStore::testNoCredentialsInitially() void TestCredentialStore::testSaveAndLoad() { - QVERIFY(m_store->save("secret123", "device456", "user789", "MyDevice")); + QVERIFY(m_store->save("secret123", "device456")); - QString secret, deviceId, userKey, deviceName; - QVERIFY(m_store->load(secret, deviceId, userKey, deviceName)); + QString secret, deviceId; + QVERIFY(m_store->load(secret, deviceId)); QCOMPARE(secret, QString("secret123")); QCOMPARE(deviceId, QString("device456")); - QCOMPARE(userKey, QString("user789")); - QCOMPARE(deviceName, QString("MyDevice")); } void TestCredentialStore::testHasCredentials() { QVERIFY(!m_store->hasCredentials()); - m_store->save("secret", "device", "user", "name"); + m_store->save("secret", "device"); QVERIFY(m_store->hasCredentials()); } void TestCredentialStore::testClear() { - m_store->save("secret", "device", "user", "name"); + m_store->save("secret", "device"); QVERIFY(m_store->hasCredentials()); QVERIFY(m_store->clear()); @@ -77,48 +75,48 @@ void TestCredentialStore::testClear() void TestCredentialStore::testOverwrite() { - m_store->save("secret1", "device1", "user1", "name1"); + m_store->save("secret1", "device1"); - QString secret, deviceId, userKey, deviceName; - m_store->load(secret, deviceId, userKey, deviceName); + QString secret, deviceId; + m_store->load(secret, deviceId); QCOMPARE(secret, QString("secret1")); - m_store->save("secret2", "device2", "user2", "name2"); - m_store->load(secret, deviceId, userKey, deviceName); + m_store->save("secret2", "device2"); + m_store->load(secret, deviceId); QCOMPARE(secret, QString("secret2")); QCOMPARE(deviceId, QString("device2")); } void TestCredentialStore::testEmptyValues() { - m_store->save("", "", "", ""); + m_store->save("", ""); - QString secret, deviceId, userKey, deviceName; - QVERIFY(!m_store->load(secret, deviceId, userKey, deviceName)); + QString secret, deviceId; + QVERIFY(!m_store->load(secret, deviceId)); } void TestCredentialStore::testLoadNonexistent() { FileCredentialStore *emptyStore = new FileCredentialStore("/nonexistent/path/that/does/not/exist", this); - QString secret, deviceId, userKey, deviceName; - QVERIFY(!emptyStore->load(secret, deviceId, userKey, deviceName)); + QString secret, deviceId; + QVERIFY(!emptyStore->load(secret, deviceId)); QCOMPARE(emptyStore->lastError(), FileCredentialStore::LoadError::SecretNotFound); delete emptyStore; } void TestCredentialStore::testLastErrorNoneOnSuccess() { - m_store->save("secret", "device", "user", "name"); + m_store->save("secret", "device"); - QString secret, deviceId, userKey, deviceName; - QVERIFY(m_store->load(secret, deviceId, userKey, deviceName)); + QString secret, deviceId; + QVERIFY(m_store->load(secret, deviceId)); QCOMPARE(m_store->lastError(), FileCredentialStore::LoadError::None); } void TestCredentialStore::testLastErrorSecretNotFound() { - QString secret, deviceId, userKey, deviceName; - QVERIFY(!m_store->load(secret, deviceId, userKey, deviceName)); + QString secret, deviceId; + QVERIFY(!m_store->load(secret, deviceId)); QCOMPARE(m_store->lastError(), FileCredentialStore::LoadError::SecretNotFound); } @@ -128,8 +126,6 @@ void TestCredentialStore::testLastErrorInvalidFormat() obj.insert("version", 1); obj.insert("secret", "encrypted"); obj.insert("device_id", "encrypted"); - obj.insert("user_key", "encrypted"); - obj.insert("device_name", "encrypted"); QJsonDocument doc(obj); QString path = m_tempDir.path() + "/credentials.json"; @@ -138,14 +134,14 @@ void TestCredentialStore::testLastErrorInvalidFormat() file.write(doc.toJson(QJsonDocument::Compact)); file.close(); - QString secret, deviceId, userKey, deviceName; - QVERIFY(!m_store->load(secret, deviceId, userKey, deviceName)); + QString secret, deviceId; + QVERIFY(!m_store->load(secret, deviceId)); QCOMPARE(m_store->lastError(), FileCredentialStore::LoadError::InvalidFormat); } void TestCredentialStore::testFilePermissionsAfterSave() { - m_store->save("secret", "device", "user", "name"); + m_store->save("secret", "device"); QFile::Permissions perms = QFile::permissions(m_tempDir.path() + "/credentials.json"); QFile::Permissions expected = QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser; diff --git a/tests/tst_messagestore.cpp b/tests/tst_messagestore.cpp index f84b262..f88bbc8 100644 --- a/tests/tst_messagestore.cpp +++ b/tests/tst_messagestore.cpp @@ -17,8 +17,8 @@ private slots: void testMarkAsRead(); void testMarkAllAsRead(); void testRemoveMessage(); + void testContainsAfterRemove(); void testUnreadCount(); - void testHighestMessageId(); void testContainsMessage(); void testTrimMessages(); void testSaveAndLoad(); @@ -44,7 +44,6 @@ void TestMessageStore::testEmptyStore() QCOMPARE(m_store->messages().size(), 0); QCOMPARE(m_store->unreadCount(), 0); QCOMPARE(m_store->totalCount(), 0); - QCOMPARE(m_store->highestMessageId(), QString()); } void TestMessageStore::testAddMessage() @@ -129,6 +128,20 @@ void TestMessageStore::testRemoveMessage() QCOMPARE(m_store->messages().size(), 0); } +void TestMessageStore::testContainsAfterRemove() +{ + Message msg; + msg.id = "msg1"; + m_store->addMessage(msg); + + QVERIFY(m_store->containsMessage("msg1")); + + m_store->removeMessage("msg1"); + + QVERIFY(!m_store->containsMessage("msg1")); + QCOMPARE(m_store->messages().size(), 0); +} + void TestMessageStore::testUnreadCount() { Message m1, m2, m3; @@ -143,18 +156,6 @@ void TestMessageStore::testUnreadCount() QCOMPARE(m_store->unreadCount(), 2); } -void TestMessageStore::testHighestMessageId() -{ - Message m1, m2; - m1.id = "1"; - m2.id = "2"; - - m_store->addMessage(m1); - m_store->addMessage(m2); - - QCOMPARE(m_store->highestMessageId(), QString("2")); -} - void TestMessageStore::testContainsMessage() { Message msg; diff --git a/tests/tst_websocketmanager.cpp b/tests/tst_websocketmanager.cpp index a351c12..30b399c 100644 --- a/tests/tst_websocketmanager.cpp +++ b/tests/tst_websocketmanager.cpp @@ -24,12 +24,12 @@ void TestWebSocketManager::testParseKeepAlive() { m_manager = new WebSocketManager(this); - QVariant result; + int result; QMetaObject::invokeMethod(m_manager, "parseFrame", - Q_RETURN_ARG(QVariant, result), + Q_RETURN_ARG(int, result), Q_ARG(QByteArray, QByteArray("#"))); - QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::KeepAlive)); + QCOMPARE(result, static_cast(WebSocketManager::FrameType::KeepAlive)); delete m_manager; } @@ -37,12 +37,12 @@ void TestWebSocketManager::testParseNewMessage() { m_manager = new WebSocketManager(this); - QVariant result; + int result; QMetaObject::invokeMethod(m_manager, "parseFrame", - Q_RETURN_ARG(QVariant, result), + Q_RETURN_ARG(int, result), Q_ARG(QByteArray, QByteArray("!"))); - QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::NewMessage)); + QCOMPARE(result, static_cast(WebSocketManager::FrameType::NewMessage)); delete m_manager; } @@ -50,12 +50,12 @@ void TestWebSocketManager::testParseReload() { m_manager = new WebSocketManager(this); - QVariant result; + int result; QMetaObject::invokeMethod(m_manager, "parseFrame", - Q_RETURN_ARG(QVariant, result), + Q_RETURN_ARG(int, result), Q_ARG(QByteArray, QByteArray("R"))); - QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::Reload)); + QCOMPARE(result, static_cast(WebSocketManager::FrameType::Reload)); delete m_manager; } @@ -63,12 +63,12 @@ void TestWebSocketManager::testParseError() { m_manager = new WebSocketManager(this); - QVariant result; + int result; QMetaObject::invokeMethod(m_manager, "parseFrame", - Q_RETURN_ARG(QVariant, result), + Q_RETURN_ARG(int, result), Q_ARG(QByteArray, QByteArray("E"))); - QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::Error)); + QCOMPARE(result, static_cast(WebSocketManager::FrameType::Error)); delete m_manager; } @@ -76,12 +76,12 @@ void TestWebSocketManager::testParseSessionClosed() { m_manager = new WebSocketManager(this); - QVariant result; + int result; QMetaObject::invokeMethod(m_manager, "parseFrame", - Q_RETURN_ARG(QVariant, result), + Q_RETURN_ARG(int, result), Q_ARG(QByteArray, QByteArray("A"))); - QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::SessionClosedByServer)); + QCOMPARE(result, static_cast(WebSocketManager::FrameType::SessionClosedByServer)); delete m_manager; } @@ -89,12 +89,12 @@ void TestWebSocketManager::testParseUnknown() { m_manager = new WebSocketManager(this); - QVariant result; + int result; QMetaObject::invokeMethod(m_manager, "parseFrame", - Q_RETURN_ARG(QVariant, result), + Q_RETURN_ARG(int, result), Q_ARG(QByteArray, QByteArray("X"))); - QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::Unknown)); + QCOMPARE(result, static_cast(WebSocketManager::FrameType::Unknown)); delete m_manager; } @@ -102,12 +102,12 @@ void TestWebSocketManager::testParseEmpty() { m_manager = new WebSocketManager(this); - QVariant result; + int result; QMetaObject::invokeMethod(m_manager, "parseFrame", - Q_RETURN_ARG(QVariant, result), + Q_RETURN_ARG(int, result), Q_ARG(QByteArray, QByteArray())); - QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::Unknown)); + QCOMPARE(result, static_cast(WebSocketManager::FrameType::Unknown)); delete m_manager; } diff --git a/translations/sailpush_be.ts b/translations/sailpush_be.ts index a848abf..ef7bb4d 100644 --- a/translations/sailpush_be.ts +++ b/translations/sailpush_be.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Захаваныя ўліковыя даныя не ўдалося расшыфраваць. Гэта можа здарыцца пасля абнаўлення сістэмы. Калі ласка, увайдзіце зноў. + + Secrets service is not available. Please restart the device and try again. + Служба secrets недаступная. Перезагрузіце прыладу і паспрабуйце зноў. + LoginPage @@ -305,7 +309,7 @@ mcetool --set-suspend-policy=early Polling Fallback - Рэзервовы апытанне + Рэзервовае апытанне Polling Interval diff --git a/translations/sailpush_bg.ts b/translations/sailpush_bg.ts index 26c834e..e9dd24f 100644 --- a/translations/sailpush_bg.ts +++ b/translations/sailpush_bg.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Запазените данни за вход не могат да бъдат декриптирани. Това може да се случи след актуализация на системата. Моля, влезте отново. + + Secrets service is not available. Please restart the device and try again. + Услугата secrets не е налична. Рестартирайте устройството и опитайте отново. + LoginPage diff --git a/translations/sailpush_bs.ts b/translations/sailpush_bs.ts index f35b071..f67643d 100644 --- a/translations/sailpush_bs.ts +++ b/translations/sailpush_bs.ts @@ -52,7 +52,7 @@ LoginHelper App was upgraded — saved credentials use an older format and cannot be migrated. Please log in again. - Aplikacija je ažurirana — sačuvani akreditive koriste stariji format i ne mogu se migrirati. Molimo prijavite se ponovo. + Aplikacija je ažurirana — sačuvani akreditivi koriste stariji format i ne mogu se migrirati. Molimo prijavite se ponovo. Failed to save credentials @@ -60,7 +60,11 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. - Sačuvani akreditive nisu mogli biti dešifrirani. Ovo se može desiti nakon ažuriranja sistema. Molimo prijavite se ponovo. + Sačuvani akreditivi nisu mogli biti dešifrirani. Ovo se može desiti nakon ažuriranja sistema. Molimo prijavite se ponovo. + + + Secrets service is not available. Please restart the device and try again. + Secrets usluga nije dostupna. Ponovo pokrenite uređaj i pokušajte ponovo. diff --git a/translations/sailpush_ca.ts b/translations/sailpush_ca.ts index 4625545..7a5f234 100644 --- a/translations/sailpush_ca.ts +++ b/translations/sailpush_ca.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Les credencials desades no s'han pogut desxifrar. Això pot passar després d'una actualització del sistema. Torna a iniciar sessió. + + Secrets service is not available. Please restart the device and try again. + El servei de secrets no està disponible. Reinicieu el dispositiu i torneu-ho a provar. + LoginPage diff --git a/translations/sailpush_cs.ts b/translations/sailpush_cs.ts index 9cc06bd..da53294 100644 --- a/translations/sailpush_cs.ts +++ b/translations/sailpush_cs.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Uložené přihlašovací údaje nelze dešifrovat. K tomu může dojít po aktualizaci systému. Přihlaste se znovu. + + Secrets service is not available. Please restart the device and try again. + Služba secrets není k dispozici. Restartujte zařízení a zkuste to znovu. + LoginPage diff --git a/translations/sailpush_cy.ts b/translations/sailpush_cy.ts index 6bd53d0..1ed59d7 100644 --- a/translations/sailpush_cy.ts +++ b/translations/sailpush_cy.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Ni ellid datgryptio'r manylion a gadwyd. Gall hyn ddigwydd ar ôl diweddariad system. Mewngofnodwch eto os gwelwch yn dda. + + Secrets service is not available. Please restart the device and try again. + Nid yw gwasanaeth secrets ar gael. Ail-gychwynnwch y ddyfais a rhowch gynnig arall arni. + LoginPage diff --git a/translations/sailpush_da.ts b/translations/sailpush_da.ts index ec5610b..8755b2b 100644 --- a/translations/sailpush_da.ts +++ b/translations/sailpush_da.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Gemte loginoplysninger kunne ikke dekrypteres. Dette kan ske efter en systemopdatering. Log venligst ind igen. + + Secrets service is not available. Please restart the device and try again. + Secrets-tjenesten er ikke tilgængelig. Genstart enheden og prøv igen. + LoginPage diff --git a/translations/sailpush_de.ts b/translations/sailpush_de.ts index 4c128b2..761c46f 100644 --- a/translations/sailpush_de.ts +++ b/translations/sailpush_de.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Gespeicherte Anmeldedaten konnten nicht entschlüsselt werden. Dies kann nach einem Systemupdate passieren. Bitte erneut anmelden. + + Secrets service is not available. Please restart the device and try again. + Der Secrets-Dienst ist nicht verfügbar. Bitte starten Sie das Gerät neu und versuchen Sie es erneut. + LoginPage diff --git a/translations/sailpush_el.ts b/translations/sailpush_el.ts index 5cae5c6..d5c01ca 100644 --- a/translations/sailpush_el.ts +++ b/translations/sailpush_el.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Τα αποθηκευμένα διαπιστευτήρια δεν μπόρεσαν να αποκρυπτογραφηθούν. Αυτό μπορεί να συμβεί μετά από ενημέρωση συστήματος. Παρακαλώ συνδεθείτε ξανά. + + Secrets service is not available. Please restart the device and try again. + Η υπηρεσία secrets δεν είναι διαθέσιμη. Επανεκκινήστε τη συσκευή και δοκιμάστε ξανά. + LoginPage diff --git a/translations/sailpush_es.ts b/translations/sailpush_es.ts index d0b8382..a4afa5c 100644 --- a/translations/sailpush_es.ts +++ b/translations/sailpush_es.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Las credenciales guardadas no se pudieron descifrar. Esto puede ocurrir después de una actualización del sistema. Vuelve a iniciar sesión. + + Secrets service is not available. Please restart the device and try again. + El servicio de secretos no está disponible. Reinicie el dispositivo e inténtelo de nuevo. + LoginPage diff --git a/translations/sailpush_et.ts b/translations/sailpush_et.ts index abe462a..dd267cd 100644 --- a/translations/sailpush_et.ts +++ b/translations/sailpush_et.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Salvestatud mandaate ei saanud dekrüptida. See võib juhtuda pärast süsteemiuuendust. Palun logige uuesti sisse. + + Secrets service is not available. Please restart the device and try again. + Secrets-teenus pole saadaval. Taaskäivitage seade ja proovige uuesti. + LoginPage diff --git a/translations/sailpush_eu.ts b/translations/sailpush_eu.ts index dac85b6..7b55fc1 100644 --- a/translations/sailpush_eu.ts +++ b/translations/sailpush_eu.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Gordetako kredentzialak ezin izan dira deszifratu. Hau sistema eguneratu baten ondoren gerta daiteke. Hasi saioa berriro. + + Secrets service is not available. Please restart the device and try again. + Secrets zerbitzua ez dago erabilgarri. Berrabiarazi gailua eta saiatu berriro. + LoginPage diff --git a/translations/sailpush_fi.ts b/translations/sailpush_fi.ts index 08bd558..f928525 100644 --- a/translations/sailpush_fi.ts +++ b/translations/sailpush_fi.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Tallennettuja kirjautumistietoja ei voitu purkaa. Tämä voi tapahtua järjestelmäpäivityksen jälkeen. Kirjaudu sisään uudelleen. + + Secrets service is not available. Please restart the device and try again. + Secrets-palvelu ei ole käytettävissä. Käynnistä laite uudelleen ja yritä uudelleen. + LoginPage diff --git a/translations/sailpush_fr.ts b/translations/sailpush_fr.ts index 4db3e36..1068c6e 100644 --- a/translations/sailpush_fr.ts +++ b/translations/sailpush_fr.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Les identifiants enregistrés n'ont pas pu être déchiffrés. Cela peut survenir après une mise à jour système. Veuillez vous reconnecter. + + Secrets service is not available. Please restart the device and try again. + Le service de secrets n'est pas disponible. Veuillez redémarrer l'appareil et réessayer. + LoginPage diff --git a/translations/sailpush_ga.ts b/translations/sailpush_ga.ts index 006f77d..f2fd29c 100644 --- a/translations/sailpush_ga.ts +++ b/translations/sailpush_ga.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Níorbh fhéidir na creidiúintí sábháilte a dhíchriptiú. Is féidir é seo tarlú tar éis nuashonrú córais. Logáil isteach arís le do thoil. + + Secrets service is not available. Please restart the device and try again. + Níl seirbhís secrets ar fáil. Atosaigh an gléas agus bain triail as arís. + LoginPage diff --git a/translations/sailpush_gl.ts b/translations/sailpush_gl.ts index 99b9996..0bd7cd7 100644 --- a/translations/sailpush_gl.ts +++ b/translations/sailpush_gl.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. As credenciais gardadas non puideron ser descifradas. Isto pode acontecer tras unha actualización do sistema. Inicia sesión novamente. + + Secrets service is not available. Please restart the device and try again. + O servizo de secrets non está dispoñible. Reinicia o dispositivo e téntao de novo. + LoginPage diff --git a/translations/sailpush_hr.ts b/translations/sailpush_hr.ts index 53bc82c..e604d6a 100644 --- a/translations/sailpush_hr.ts +++ b/translations/sailpush_hr.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Spremljene vjerodajnice nije moguće dešifrirati. To se može dogoditi nakon ažuriranja sustava. Prijavite se ponovo. + + Secrets service is not available. Please restart the device and try again. + Secrets usluga nije dostupna. Ponovno pokrenite uređaj i pokušajte ponovno. + LoginPage @@ -143,11 +147,11 @@ Povucite dolje za sinkronizaciju MessageDelegate %1h ago - pri %1h + prije %1h %1m ago - pri %1m + prije %1m Delete diff --git a/translations/sailpush_hu.ts b/translations/sailpush_hu.ts index 0eaada1..844ce54 100644 --- a/translations/sailpush_hu.ts +++ b/translations/sailpush_hu.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. A mentett hitelesítő adatok nem fejthetők vissza. Ez rendszerfrissítés után fordulhat elő. Kérjük, jelentkezzen be újra. + + Secrets service is not available. Please restart the device and try again. + A secrets szolgáltatás nem érhető el. Kérjük, indítsa újra az eszközt és próbálja újra. + LoginPage diff --git a/translations/sailpush_hy.ts b/translations/sailpush_hy.ts index d033aaa..0aebd1b 100644 --- a/translations/sailpush_hy.ts +++ b/translations/sailpush_hy.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Պահպանված մուտքագրման տվյալները չհաջողվեց վերծանել: Սա կարող է տեղի ունենալ համակարգի թարմացումից հետո: Խնդրում ենք կրկին մուտք գործել: + + Secrets service is not available. Please restart the device and try again. + Secrets ծառայությունը հասանելի չէ: Վերագործարկեք սարքը և կրկին փորձեք: + LoginPage @@ -337,7 +341,7 @@ mcetool --set-suspend-policy=early This will clear your credentials and stop the background service. Are you sure? - Սա կջնջի Ձեր մուտքագրման տվյալները և կդադարեցնի ֆոնային ծառայությունը: Համոզված եք: + Սա կջնջի Ձեր մուտքագրման տվյալները և կդադարեցնի ֆոնային ծառայությունը: Համոզված ե՞ք Unofficial Pushover Open Client for SailfishOS. diff --git a/translations/sailpush_is.ts b/translations/sailpush_is.ts index ce6d988..7431527 100644 --- a/translations/sailpush_is.ts +++ b/translations/sailpush_is.ts @@ -60,7 +60,11 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. - Ekki var hægt að afrita vistuð skilríki. Þetta getur gerst eftir kerfisuppfærslu. Vinsamlegast skráðu þig inn aftur. + Ekki var hægt að dulráða vistuð skilríki. Þetta getur gerst eftir kerfisuppfærslu. Vinsamlegast skráðu þig inn aftur. + + + Secrets service is not available. Please restart the device and try again. + Secrets-þjónustan er ekki tiltæk. Endurræstu tækið og reyndu aftur. @@ -283,7 +287,7 @@ Dragðu niður til að samstilla If WebSocket disconnects for 30s, falls back to periodic polling for notifications. - Ef WebSocket aftengist í 30 sekúndur, fellur á aftur á tímabundið polling fyrir tilkynningar. + Ef WebSocket aftengist í 30 sekúndur, fellur aftur á tímabundið polling fyrir tilkynningar. Keeps CPU awake during message sync to ensure reliable delivery. Uses MCE keepalive API. diff --git a/translations/sailpush_it.ts b/translations/sailpush_it.ts index bd9a3f3..6591e8d 100644 --- a/translations/sailpush_it.ts +++ b/translations/sailpush_it.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Le credenziali salvate non possono essere decriptate. Questo può accadere dopo un aggiornamento di sistema. Effettua nuovamente l'accesso. + + Secrets service is not available. Please restart the device and try again. + Il servizio secrets non è disponibile. Riavvia il dispositivo e riprova. + LoginPage diff --git a/translations/sailpush_ka.ts b/translations/sailpush_ka.ts index cce211d..ae3d402 100644 --- a/translations/sailpush_ka.ts +++ b/translations/sailpush_ka.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. შენახული მონაცემების გაშიფვრა ვერ მოხერხდა. ეს შეიძლება მოხდეს სისტემის განახლების შემდეგ. გთხოვთ, ხელახლა შეხვიდეთ. + + Secrets service is not available. Please restart the device and try again. + Secrets სერვისი ხელმისაწვდომი არ არის. გადატვირთეთ მოწყობილობა და კიდევ სცადეთ. + LoginPage @@ -170,7 +174,7 @@ Pull down to sync Deleting - წაშლა + წაშლა მიმდინარეობს Message diff --git a/translations/sailpush_lt.ts b/translations/sailpush_lt.ts index 973e68d..f35f03d 100644 --- a/translations/sailpush_lt.ts +++ b/translations/sailpush_lt.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Išsaugoti prisijungimo duomenys negalėjo būti iššifruoti. Tai gali nutikti po sistemos atnaujinimo. Prašome prisijungti iš naujo. + + Secrets service is not available. Please restart the device and try again. + Secrets paslauga neprieinama. Paleiskite įrenginį iš naujo ir bandykite dar kartą. + LoginPage diff --git a/translations/sailpush_lv.ts b/translations/sailpush_lv.ts index 9cd97f5..4f1206b 100644 --- a/translations/sailpush_lv.ts +++ b/translations/sailpush_lv.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Saglabātās akreditācijas nevarēja atšifrēt. Tas var notikt pēc sistēmas atjauninājuma. Lūdzu, piesakieties vēlreiz. + + Secrets service is not available. Please restart the device and try again. + Secrets pakalpojums nav pieejams. Restartējiet ierīci un mēģiniet vēlreiz. + LoginPage @@ -275,7 +279,7 @@ Velciet lejup, lai sinhronizētu Deep Sleep - Dziļais miegs + Dziļais miegs Enable Polling Fallback diff --git a/translations/sailpush_mk.ts b/translations/sailpush_mk.ts index 8a0f830..2f68fa3 100644 --- a/translations/sailpush_mk.ts +++ b/translations/sailpush_mk.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Зачуваните акредитиви не можеа да се декриптираат. Ова може да се случи по ажурирање на системот. Ве молиме, најавете се повторно. + + Secrets service is not available. Please restart the device and try again. + Сервисот secrets не е достапен. Рестартирајте го уредот и обидете се повторно. + LoginPage diff --git a/translations/sailpush_mt.ts b/translations/sailpush_mt.ts index a0a62df..3e6dc1a 100644 --- a/translations/sailpush_mt.ts +++ b/translations/sailpush_mt.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Il-kredenzjali maħżuna ma setgħux jiġu deċifrati. Dan jista' jiġri wara aġġornament tas-sistema. Jekk jogħġbok erġa' idħol. + + Secrets service is not available. Please restart the device and try again. + Is-servizz tas-secrets mhuwiex disponibbli. Erġa' ibda l-apparat u erġa' pprova. + LoginPage diff --git a/translations/sailpush_nb.ts b/translations/sailpush_nb.ts index 0a7ce26..82ba47c 100644 --- a/translations/sailpush_nb.ts +++ b/translations/sailpush_nb.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Lagrede påloggingsopplysninger kunne ikke dekrypteres. Dette kan skje etter en systemoppdatering. Vennligst logg inn på nytt. + + Secrets service is not available. Please restart the device and try again. + Secrets-tjenesten er ikke tilgjengelig. Start enheten på nytt og prøv igjen. + LoginPage diff --git a/translations/sailpush_nl.ts b/translations/sailpush_nl.ts index 5aec927..3fd58c6 100644 --- a/translations/sailpush_nl.ts +++ b/translations/sailpush_nl.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Opgeslagen inloggegevens konden niet worden ontsleuteld. Dit kan gebeuren na een systeemupdate. Log opnieuw in. + + Secrets service is not available. Please restart the device and try again. + De secrets-service is niet beschikbaar. Start het apparaat opnieuw op en probeer het opnieuw. + LoginPage diff --git a/translations/sailpush_pl.ts b/translations/sailpush_pl.ts index 68afe30..649c92c 100644 --- a/translations/sailpush_pl.ts +++ b/translations/sailpush_pl.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Zapisane dane logowania nie mogły zostać odszyfrowane. Może to nastąpić po aktualizacji systemu. Zaloguj się ponownie. + + Secrets service is not available. Please restart the device and try again. + Usługa secrets jest niedostępna. Uruchom ponownie urządzenie i spróbuj ponownie. + LoginPage diff --git a/translations/sailpush_pt.ts b/translations/sailpush_pt.ts index e6ccc77..ca4f459 100644 --- a/translations/sailpush_pt.ts +++ b/translations/sailpush_pt.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. As credenciais guardadas não puderam ser desencriptadas. Isto pode acontecer após uma atualização do sistema. Inicie sessão novamente. + + Secrets service is not available. Please restart the device and try again. + O serviço de segredos não está disponível. Reinicie o dispositivo e tente novamente. + LoginPage diff --git a/translations/sailpush_ro.ts b/translations/sailpush_ro.ts index cfca53b..8c1e2a2 100644 --- a/translations/sailpush_ro.ts +++ b/translations/sailpush_ro.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Datele de conectare salvate nu au putut fi decriptate. Acest lucru se poate întâmpla după o actualizare de sistem. Vă rugăm să vă autentificați din nou. + + Secrets service is not available. Please restart the device and try again. + Serviciul secrets nu este disponibil. Reporniți dispozitivul și încercați din nou. + LoginPage diff --git a/translations/sailpush_ru.ts b/translations/sailpush_ru.ts index 66bd5eb..a52fbfa 100644 --- a/translations/sailpush_ru.ts +++ b/translations/sailpush_ru.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Сохранённые учётные данные не удалось расшифровать. Это может произойти после обновления системы. Пожалуйста, войдите снова. + + Secrets service is not available. Please restart the device and try again. + Служба secrets недоступна. Перезагрузите устройство и попробуйте снова. + LoginPage diff --git a/translations/sailpush_sk.ts b/translations/sailpush_sk.ts index f56a8e9..b60d18d 100644 --- a/translations/sailpush_sk.ts +++ b/translations/sailpush_sk.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Uložené prihlasovacie údaje nemožno dešifrovať. K tomu môže dôjsť po aktualizácii systému. Prihláste sa znova. + + Secrets service is not available. Please restart the device and try again. + Služba secrets nie je dostupná. Reštartujte zariadenie a skúste to znova. + LoginPage diff --git a/translations/sailpush_sl.ts b/translations/sailpush_sl.ts index 5caa00e..4ff45d3 100644 --- a/translations/sailpush_sl.ts +++ b/translations/sailpush_sl.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Shranjenih poverilnic ni mogoče dešifrirati. To se lahko zgodi po posodobitvi sistema. Ponovno se prijavite. + + Secrets service is not available. Please restart the device and try again. + Storitev secrets ni na voljo. Znova zaženite napravo in poskusite znova. + LoginPage diff --git a/translations/sailpush_sq.ts b/translations/sailpush_sq.ts index 01049e2..86a88dc 100644 --- a/translations/sailpush_sq.ts +++ b/translations/sailpush_sq.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Kredencialet e ruajtura nuk mund të deshifroheshin. Kjo mund të ndodhë pas një përditësimi të sistemit. Ju lutemi, hyni përsëri. + + Secrets service is not available. Please restart the device and try again. + Shërbimi i secrets nuk është i disponueshëm. Rinisni pajisjen dhe provoni përsëri. + LoginPage diff --git a/translations/sailpush_sr.ts b/translations/sailpush_sr.ts index a631125..94676fc 100644 --- a/translations/sailpush_sr.ts +++ b/translations/sailpush_sr.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Sačuvani akreditivi ne mogu se dešifrovati. To se može dogoditi nakon ažuriranja sistema. Prijavite se ponovo. + + Secrets service is not available. Please restart the device and try again. + Secrets сервис није доступан. Поново покрените уређај и покушајте поново. + LoginPage diff --git a/translations/sailpush_sv.ts b/translations/sailpush_sv.ts index 8fad0d6..3e4f180 100644 --- a/translations/sailpush_sv.ts +++ b/translations/sailpush_sv.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Sparade inloggningsuppgifter kunde inte dekrypteras. Detta kan hända efter en systemuppdatering. Vänligen logga in igen. + + Secrets service is not available. Please restart the device and try again. + Secrets-tjänsten är inte tillgänglig. Starta om enheten och försök igen. + LoginPage diff --git a/translations/sailpush_tr.ts b/translations/sailpush_tr.ts index a1ecaaa..ee6e6a8 100644 --- a/translations/sailpush_tr.ts +++ b/translations/sailpush_tr.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Kayıtlı kimlik bilgileri çözülemedi. Bu durum bir sistem güncellemesinden sonra olabilir. Lütfen tekrar giriş yapın. + + Secrets service is not available. Please restart the device and try again. + Secrets servisi kullanılamıyor. Lütfen cihazı yeniden başlatın ve tekrar deneyin. + LoginPage diff --git a/translations/sailpush_uk.ts b/translations/sailpush_uk.ts index 5cd6b1d..d4a3e5c 100644 --- a/translations/sailpush_uk.ts +++ b/translations/sailpush_uk.ts @@ -62,6 +62,10 @@ Saved credentials could not be decrypted. This can happen after a system update. Please log in again. Збережені облікові дані не вдалося розшифрувати. Це може статися після оновлення системи. Будь ласка, увійдіть знову. + + Secrets service is not available. Please restart the device and try again. + Служба secrets недоступна. Перезавантажте пристрій і спробуйте знову. + LoginPage