Skip to content

Commit a44cead

Browse files
committed
feat: support notify_file_id push notifications
Signed-off-by: Jyrki Gadinger <[email protected]>
1 parent 50385f1 commit a44cead

File tree

6 files changed

+106
-5
lines changed

6 files changed

+106
-5
lines changed

src/common/syncjournaldb.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,40 @@ bool SyncJournalDb::updateLocalMetadata(const QString &filename,
16811681
return true;
16821682
}
16831683

1684+
bool SyncJournalDb::hasFileIds(const QList<qint64> &fileIds)
1685+
{
1686+
QMutexLocker locker(&_mutex);
1687+
1688+
if (!checkConnect()) {
1689+
return false;
1690+
}
1691+
1692+
QStringList fileIdStrings = {};
1693+
for (const auto &fileId : fileIds) {
1694+
fileIdStrings.append(QString::number(fileId));
1695+
}
1696+
1697+
// quick workaround for looking up pure numeric file IDs: simply `round()` that field!
1698+
// this will return the file ID as e.g. 12345.0, but that's still good enough to use it
1699+
// with the IN operator -- e.g. (12345, 1337, 29001)
1700+
SqlQuery query(
1701+
QLatin1String{"SELECT 1 FROM metadata WHERE ROUND(fileid) IN (%1) LIMIT 1;"}
1702+
.arg(fileIdStrings.join(QLatin1String{", "})).toLocal8Bit(),
1703+
_db
1704+
);
1705+
1706+
if (!query.exec()) {
1707+
qCWarning(lcDb) << "file id query failed:" << query.error();
1708+
return false;
1709+
}
1710+
1711+
if (query.next().hasData && query.intValue(0) == 1) {
1712+
return true;
1713+
}
1714+
1715+
return false;
1716+
}
1717+
16841718
Optional<SyncJournalDb::HasHydratedDehydrated> SyncJournalDb::hasHydratedOrDehydratedFiles(const QByteArray &filename)
16851719
{
16861720
QMutexLocker locker(&_mutex);

src/common/syncjournaldb.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ class OCSYNC_EXPORT SyncJournalDb : public QObject
7474
[[nodiscard]] bool updateLocalMetadata(const QString &filename,
7575
qint64 modtime, qint64 size, quint64 inode, const SyncJournalFileLockInfo &lockInfo);
7676

77+
[[nodiscard]] bool hasFileIds(const QList<qint64> &fileIds);
78+
7779
/// Return value for hasHydratedOrDehydratedFiles()
7880
struct HasHydratedDehydrated
7981
{

src/gui/folderman.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2079,7 +2079,7 @@ void FolderMan::slotSetupPushNotifications(const Folder::Map &folderMap)
20792079

20802080
void FolderMan::slotProcessFilesPushNotification(Account *account)
20812081
{
2082-
qCInfo(lcFolderMan) << "Got files push notification for account" << account;
2082+
qCInfo(lcFolderMan) << "Got files push notification for account" << account->displayName();
20832083

20842084
for (auto folder : std::as_const(_folderMap)) {
20852085
// Just run on the folders that belong to this account
@@ -2092,13 +2092,34 @@ void FolderMan::slotProcessFilesPushNotification(Account *account)
20922092
}
20932093
}
20942094

2095+
void FolderMan::slotProcessFileIdsPushNotification(Account *account, const QList<qint64> &fileIds)
2096+
{
2097+
qCInfo(lcFolderMan) << "Got file id push notification for account" << account->displayName() << "and fileIds" << fileIds;
2098+
2099+
for (auto folder : std::as_const(_folderMap)) {
2100+
// Just run on the folders that belong to this account
2101+
if (folder->accountState()->account() != account) {
2102+
continue;
2103+
}
2104+
2105+
if (!folder->journalDb()->hasFileIds(fileIds)) {
2106+
qCDebug(lcFolderMan) << "Folder" << folder->path() << "does not contain any of these file ids, ignoring";
2107+
continue;
2108+
}
2109+
2110+
qCInfo(lcFolderMan) << "Schedule folder" << folder->path() << "for sync";
2111+
scheduleFolder(folder);
2112+
}
2113+
}
2114+
20952115
void FolderMan::slotConnectToPushNotifications(const AccountPtr &account)
20962116
{
20972117
const auto pushNotifications = account->pushNotifications();
20982118

20992119
if (pushNotificationsFilesReady(account)) {
21002120
qCInfo(lcFolderMan) << "Push notifications ready";
21012121
connect(pushNotifications, &PushNotifications::filesChanged, this, &FolderMan::slotProcessFilesPushNotification, Qt::UniqueConnection);
2122+
connect(pushNotifications, &PushNotifications::fileIdsChanged, this, &FolderMan::slotProcessFileIdsPushNotification, Qt::UniqueConnection);
21022123
}
21032124
}
21042125

src/gui/folderman.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ private slots:
329329

330330
void slotSetupPushNotifications(const OCC::Folder::Map &);
331331
void slotProcessFilesPushNotification(OCC::Account *account);
332+
void slotProcessFileIdsPushNotification(OCC::Account *account, const QList<qint64> &fileIds);
332333
void slotConnectToPushNotifications(const OCC::AccountPtr &account);
333334

334335
void slotLeaveShare(const QString &localFile, const QByteArray &folderToken = {});

src/libsync/pushnotifications.cpp

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77
#include "creds/abstractcredentials.h"
88
#include "account.h"
99

10+
#include <QJsonArray>
11+
1012
namespace {
1113
static constexpr int MAX_ALLOWED_FAILED_AUTHENTICATION_ATTEMPTS = 3;
1214
static constexpr int PING_INTERVAL = 30 * 1000;
15+
16+
static constexpr QLatin1String NOTIFY_FILE_ID_PREFIX = QLatin1String{"notify_file_id "};
1317
}
1418

1519
namespace OCC {
@@ -102,7 +106,9 @@ void PushNotifications::onWebSocketTextMessageReceived(const QString &message)
102106
{
103107
qCInfo(lcPushNotifications) << "Received push notification:" << message;
104108

105-
if (message == "notify_file") {
109+
if (message.startsWith(NOTIFY_FILE_ID_PREFIX)) {
110+
handleNotifyFileId(message);
111+
} else if (message == "notify_file") {
106112
handleNotifyFile();
107113
} else if (message == "notify_activity") {
108114
handleNotifyActivity();
@@ -124,7 +130,7 @@ void PushNotifications::onWebSocketError(QAbstractSocket::SocketError error)
124130
return;
125131
}
126132

127-
qCWarning(lcPushNotifications) << "Websocket error on with account" << _account->url() << error;
133+
qCWarning(lcPushNotifications) << "Websocket error on with account" << _account->displayName() << _account->url() << error;
128134
closeWebSocket();
129135
emit connectionLost();
130136
}
@@ -153,7 +159,7 @@ bool PushNotifications::tryReconnectToWebSocket()
153159

154160
void PushNotifications::onWebSocketSslErrors(const QList<QSslError> &errors)
155161
{
156-
qCWarning(lcPushNotifications) << "Websocket ssl errors on with account" << _account->url() << errors;
162+
qCWarning(lcPushNotifications) << "Websocket ssl errors on with account" << _account->displayName() << _account->url() << errors;
157163
closeWebSocket();
158164
emit authenticationFailed();
159165
}
@@ -164,7 +170,7 @@ void PushNotifications::openWebSocket()
164170
const auto capabilities = _account->capabilities();
165171
const auto webSocketUrl = capabilities.pushNotificationsWebSocketUrl();
166172

167-
qCInfo(lcPushNotifications) << "Open connection to websocket on" << webSocketUrl << "for account" << _account->url();
173+
qCInfo(lcPushNotifications) << "Open connection to websocket on" << webSocketUrl << "for account" << _account->displayName() << _account->url();
168174
connect(_webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::errorOccurred), this, &PushNotifications::onWebSocketError);
169175
connect(_webSocket, &QWebSocket::sslErrors, this, &PushNotifications::onWebSocketSslErrors);
170176
_webSocket->open(webSocketUrl);
@@ -184,6 +190,8 @@ void PushNotifications::handleAuthenticated()
184190
{
185191
qCInfo(lcPushNotifications) << "Authenticated successful on websocket";
186192
_failedAuthenticationAttemptsCount = 0;
193+
qCDebug(lcPushNotifications) << "Requesting opt-in to 'notify_file_id' notifications";
194+
_webSocket->sendTextMessage("listen notify_file_id");
187195
_isReady = true;
188196
startPingTimer();
189197
emit ready();
@@ -202,6 +210,35 @@ void PushNotifications::handleNotifyFile()
202210
emitFilesChanged();
203211
}
204212

213+
void PushNotifications::handleNotifyFileId(const QString &message)
214+
{
215+
qCInfo(lcPushNotifications) << "File-ID push notification arrived";
216+
217+
QList<qint64> fileIds{};
218+
QJsonParseError parseError;
219+
const auto fileIdsJson = message.mid(NOTIFY_FILE_ID_PREFIX.length());
220+
const auto jsonDoc = QJsonDocument::fromJson(fileIdsJson.toUtf8(), &parseError);
221+
222+
if (parseError.error != QJsonParseError::NoError) {
223+
qCWarning(lcPushNotifications).nospace() << "could not parse received list of file IDs error=" << parseError.error << " errorString=" << parseError.errorString() << " fileIdsJson=" << fileIdsJson;
224+
return;
225+
}
226+
227+
if (const auto jsonArray = jsonDoc.array(); jsonDoc.isArray()) {
228+
for (const auto& fileid : jsonArray) {
229+
if (const auto fid = fileid.toInteger(); fileid.isDouble()) {
230+
fileIds.push_back(fid);
231+
}
232+
}
233+
}
234+
235+
if (fileIds.empty()) {
236+
return;
237+
}
238+
239+
emit fileIdsChanged(_account, fileIds);
240+
}
241+
205242
void PushNotifications::handleInvalidCredentials()
206243
{
207244
qCInfo(lcPushNotifications) << "Invalid credentials submitted to websocket";

src/libsync/pushnotifications.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ class OWNCLOUDSYNC_EXPORT PushNotifications : public QObject
6565
*/
6666
void filesChanged(OCC::Account *account);
6767

68+
/**
69+
* Will be emitted if specific files on the server changed
70+
*/
71+
void fileIdsChanged(OCC::Account *account, const QList<qint64> &fileIds);
72+
6873
/**
6974
* Will be emitted if activities have been changed on the server
7075
*/
@@ -111,6 +116,7 @@ private slots:
111116

112117
void handleAuthenticated();
113118
void handleNotifyFile();
119+
void handleNotifyFileId(const QString &message);
114120
void handleInvalidCredentials();
115121
void handleNotifyNotification();
116122
void handleNotifyActivity();

0 commit comments

Comments
 (0)