|
|
@@ -19,7 +19,6 @@
|
|
|
#include <libsync/vfs/cfapi/shellext/configvfscfapishellext.h>
|
|
|
#include "folder.h"
|
|
|
#include "folderman.h"
|
|
|
-#include "ocssharejob.h"
|
|
|
#include <QDir>
|
|
|
#include <QJsonArray>
|
|
|
#include <QJsonDocument>
|
|
|
@@ -28,7 +27,6 @@
|
|
|
|
|
|
namespace {
|
|
|
constexpr auto isSharedInvalidationInterval = 2 * 60 * 1000; // 2 minutes, so we don't make fetch sharees requests too often
|
|
|
-constexpr auto folderAliasPropertyKey = "folderAlias";
|
|
|
}
|
|
|
|
|
|
namespace OCC {
|
|
|
@@ -102,6 +100,7 @@ void ShellExtensionsServer::processCustomStateRequest(QLocalSocket *socket, cons
|
|
|
sendEmptyDataAndCloseSession(socket);
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
const auto filePathRelative = QString(customStateRequestInfo.path).remove(folder->path());
|
|
|
|
|
|
SyncJournalFileRecord record;
|
|
|
@@ -123,43 +122,14 @@ void ShellExtensionsServer::processCustomStateRequest(QLocalSocket *socket, cons
|
|
|
QVariantMap{{VfsShellExtensions::Protocol::CustomStateStatesKey, states}}}};
|
|
|
};
|
|
|
|
|
|
- if (QDateTime::currentMSecsSinceEpoch() - record._lastShareStateFetchedTimestmap < _isSharedInvalidationInterval) {
|
|
|
- qCInfo(lcShellExtServer) << record.path() << " record._lastShareStateFetchedTimestmap has less than " << _isSharedInvalidationInterval << " ms difference with QDateTime::currentMSecsSinceEpoch(). Returning data from SyncJournal.";
|
|
|
+ if (QDateTime::currentMSecsSinceEpoch() - record._lastShareStateFetchedTimestamp < _isSharedInvalidationInterval) {
|
|
|
+ qCInfo(lcShellExtServer) << record.path() << " record._lastShareStateFetchedTimestamp has less than " << _isSharedInvalidationInterval << " ms difference with QDateTime::currentMSecsSinceEpoch(). Returning data from SyncJournal.";
|
|
|
sendJsonMessageWithVersion(socket, composeMessageReplyFromRecord(record));
|
|
|
closeSession(socket);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- const auto job = new OcsShareJob(folder->accountState()->account());
|
|
|
- job->setProperty(folderAliasPropertyKey, customStateRequestInfo.folderAlias);
|
|
|
- connect(job, &OcsShareJob::shareJobFinished, this, &ShellExtensionsServer::slotSharesFetched);
|
|
|
- connect(job, &OcsJob::ocsError, this, &ShellExtensionsServer::slotSharesFetchError);
|
|
|
-
|
|
|
- {
|
|
|
- _customStateSocketConnections.insert(socket->socketDescriptor(), QObject::connect(this, &ShellExtensionsServer::fetchSharesJobFinished, [this, socket, filePathRelative, composeMessageReplyFromRecord](const QString &folderAlias) {
|
|
|
- {
|
|
|
- const auto connection = _customStateSocketConnections[socket->socketDescriptor()];
|
|
|
- if (connection) {
|
|
|
- QObject::disconnect(connection);
|
|
|
- }
|
|
|
- _customStateSocketConnections.remove(socket->socketDescriptor());
|
|
|
- }
|
|
|
-
|
|
|
- const auto folder = FolderMan::instance()->folder(folderAlias);
|
|
|
- SyncJournalFileRecord record;
|
|
|
- if (!folder || !folder->journalDb()->getFileRecord(filePathRelative, &record) || !record.isValid()) {
|
|
|
- qCWarning(lcShellExtServer) << "Record not found in SyncJournal for: " << filePathRelative;
|
|
|
- sendEmptyDataAndCloseSession(socket);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- qCInfo(lcShellExtServer) << "Sending reply from OcsShareJob for socket: " << socket->socketDescriptor() << " and record: " << record.path();
|
|
|
- sendJsonMessageWithVersion(socket, composeMessageReplyFromRecord(record));
|
|
|
- closeSession(socket);
|
|
|
- }));
|
|
|
- }
|
|
|
-
|
|
|
- const auto sharesPath = [&record, folder, &filePathRelative]() {
|
|
|
+ const auto lsColJobPath = [folder, &filePathRelative]() {
|
|
|
const auto filePathRelativeRemote = QDir(folder->remotePath()).filePath(filePathRelative);
|
|
|
// either get parent's path, or, return '/' if we are in the root folder
|
|
|
auto recordPathSplit = filePathRelativeRemote.split(QLatin1Char('/'), Qt::SkipEmptyParts);
|
|
|
@@ -170,13 +140,88 @@ void ShellExtensionsServer::processCustomStateRequest(QLocalSocket *socket, cons
|
|
|
return QStringLiteral("/");
|
|
|
}();
|
|
|
|
|
|
- if (!_runningFetchShareJobsForPaths.contains(sharesPath)) {
|
|
|
- _runningFetchShareJobsForPaths.push_back(sharesPath);
|
|
|
- qCInfo(lcShellExtServer) << "Started OcsShareJob for path: " << sharesPath;
|
|
|
- job->getShares(sharesPath, {{QStringLiteral("subfiles"), QStringLiteral("true")}});
|
|
|
- } else {
|
|
|
- qCInfo(lcShellExtServer) << "OcsShareJob is already running for path: " << sharesPath;
|
|
|
+ if (_runningLsColJobsForPaths.contains(lsColJobPath)) {
|
|
|
+ qCInfo(lcShellExtServer) << "LsColJob is already running for path: " << lsColJobPath;
|
|
|
+ sendJsonMessageWithVersion(socket, composeMessageReplyFromRecord(record));
|
|
|
+ closeSession(socket);
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ _customStateSocketConnections.insert(socket->socketDescriptor(), QObject::connect(this, &ShellExtensionsServer::directoryListingIterationFinished, [this, socket, filePathRelative, composeMessageReplyFromRecord](const QString &folderAlias) {
|
|
|
+ {
|
|
|
+ const auto connection = _customStateSocketConnections[socket->socketDescriptor()];
|
|
|
+ if (connection) {
|
|
|
+ QObject::disconnect(connection);
|
|
|
+ }
|
|
|
+ _customStateSocketConnections.remove(socket->socketDescriptor());
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto folder = FolderMan::instance()->folder(folderAlias);
|
|
|
+ SyncJournalFileRecord record;
|
|
|
+ if (!folder || !folder->journalDb()->getFileRecord(filePathRelative, &record) || !record.isValid()) {
|
|
|
+ qCWarning(lcShellExtServer) << "Record not found in SyncJournal for: " << filePathRelative;
|
|
|
+ sendEmptyDataAndCloseSession(socket);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ qCInfo(lcShellExtServer) << "Sending reply from LsColJob for socket: " << socket->socketDescriptor() << " and record: " << record.path();
|
|
|
+ sendJsonMessageWithVersion(socket, composeMessageReplyFromRecord(record));
|
|
|
+ closeSession(socket);
|
|
|
+ }));
|
|
|
+
|
|
|
+ auto *const lsColJob = new LsColJob(folder->accountState()->account(), QDir::cleanPath(folder->remotePath() + lsColJobPath), this);
|
|
|
+ lsColJob->setProperties({QByteArrayLiteral("http://owncloud.org/ns:share-types"), QByteArrayLiteral("http://owncloud.org/ns:permissions")});
|
|
|
+
|
|
|
+ const auto folderAlias = customStateRequestInfo.folderAlias;
|
|
|
+
|
|
|
+ QObject::connect(lsColJob, &LsColJob::directoryListingIterated, this, [this, folderAlias, lsColJobPath](const QString &name, const QMap<QString, QString> &properties) {
|
|
|
+ const auto folder = FolderMan::instance()->folder(folderAlias);
|
|
|
+
|
|
|
+ if (!folder) {
|
|
|
+ qCWarning(lcShellExtServer) << "No folder found for folderAlias: " << folderAlias;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ SyncJournalFileRecord record;
|
|
|
+ const auto filePathWithoutDavPath = QString(name).remove(folder->accountState()->account()->davPathRoot());
|
|
|
+ const auto filePathAdjusted = (filePathWithoutDavPath.size() > 1 && filePathWithoutDavPath.startsWith(QLatin1Char('/'))) ? filePathWithoutDavPath.mid(1) : filePathWithoutDavPath;
|
|
|
+ if (filePathAdjusted.isEmpty() || filePathAdjusted == lsColJobPath) {
|
|
|
+ // we are skipping the first item as it is the current path, but we are interested in nested items
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!folder || !folder->journalDb()->getFileRecord(filePathAdjusted, &record) || !record.isValid()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto isIncomingShare = properties.contains(QStringLiteral("permissions")) && RemotePermissions::fromServerString(properties.value(QStringLiteral("permissions"))).hasPermission(OCC::RemotePermissions::IsShared);
|
|
|
+
|
|
|
+ const auto sharedByMe = !properties.value(QStringLiteral("share-types")).isEmpty();
|
|
|
+
|
|
|
+ record._sharedByMe = sharedByMe;
|
|
|
+
|
|
|
+ record._isShared = isIncomingShare || sharedByMe;
|
|
|
+ record._lastShareStateFetchedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
|
|
+
|
|
|
+ if (!folder->journalDb()->setFileRecord(record)) {
|
|
|
+ qCWarning(lcShellExtServer) << "Could not set file record for path: " << record._path;
|
|
|
+ emit directoryListingIterationFinished(folderAlias);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ QObject::connect(lsColJob, &LsColJob::finishedWithError, this, [this, folderAlias, lsColJobPath](QNetworkReply *reply) {
|
|
|
+ _runningLsColJobsForPaths.removeOne(lsColJobPath);
|
|
|
+ const auto httpCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
|
+ qCWarning(lcShellExtServer) << "LSCOL job error" << reply->errorString() << httpCode << reply->error();
|
|
|
+ emit directoryListingIterationFinished(folderAlias);
|
|
|
+ });
|
|
|
+
|
|
|
+ QObject::connect(lsColJob, &LsColJob::finishedWithoutError, this, [this, folderAlias, lsColJobPath]() {
|
|
|
+ _runningLsColJobsForPaths.removeOne(lsColJobPath);
|
|
|
+ emit directoryListingIterationFinished(folderAlias);
|
|
|
+ });
|
|
|
+
|
|
|
+ _runningLsColJobsForPaths.push_back(lsColJobPath);
|
|
|
+ lsColJob->start();
|
|
|
}
|
|
|
|
|
|
void ShellExtensionsServer::processThumbnailRequest(QLocalSocket *socket, const ThumbnailRequestInfo &thumbnailRequestInfo)
|
|
|
@@ -252,108 +297,6 @@ void ShellExtensionsServer::slotNewConnection()
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
-void ShellExtensionsServer::slotSharesFetched(const QJsonDocument &reply)
|
|
|
-{
|
|
|
- const auto job = qobject_cast<OcsShareJob *>(sender());
|
|
|
-
|
|
|
- Q_ASSERT(job);
|
|
|
- if (!job) {
|
|
|
- qCWarning(lcShellExtServer) << "ShellExtensionsServer::slotSharesFetched is not called by OcsShareJob's signal!";
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const auto sharesPath = job->getParamValue(QStringLiteral("path"));
|
|
|
-
|
|
|
- _runningFetchShareJobsForPaths.removeAll(sharesPath);
|
|
|
-
|
|
|
- const auto folderAlias = job->property(folderAliasPropertyKey).toString();
|
|
|
-
|
|
|
- Q_ASSERT(!folderAlias.isEmpty());
|
|
|
- if (folderAlias.isEmpty()) {
|
|
|
- qCWarning(lcShellExtServer) << "No 'folderAlias' set for OcsShareJob's instance!";
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const auto folder = FolderMan::instance()->folder(folderAlias);
|
|
|
-
|
|
|
- Q_ASSERT(folder);
|
|
|
- if (!folder) {
|
|
|
- qCWarning(lcShellExtServer) << "folder not found for folderAlias: " << folderAlias;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const auto timeStamp = QDateTime::currentMSecsSinceEpoch();
|
|
|
- QStringList recortPathsToResetIsSharedFlag;
|
|
|
- const QByteArray pathOfSharesToResetIsSharedFlag = sharesPath == QStringLiteral("/") ? QByteArrayLiteral("") : sharesPath.toUtf8();
|
|
|
- if (folder->journalDb()->listFilesInPath(pathOfSharesToResetIsSharedFlag, [&](const SyncJournalFileRecord &rec) {
|
|
|
- recortPathsToResetIsSharedFlag.push_back(rec.path());
|
|
|
- })) {
|
|
|
- for (const auto &recordPath : recortPathsToResetIsSharedFlag) {
|
|
|
- SyncJournalFileRecord record;
|
|
|
- if (!folder->journalDb()->getFileRecord(recordPath, &record) || !record.isValid()) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- record._isShared = false;
|
|
|
- record._lastShareStateFetchedTimestmap = timeStamp;
|
|
|
- if (!folder->journalDb()->setFileRecord(record)) {
|
|
|
- qCWarning(lcShellExtServer) << "Could not set file record for path: " << record._path;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const auto sharesFetched = reply.object().value(QStringLiteral("ocs")).toObject().value(QStringLiteral("data")).toArray();
|
|
|
-
|
|
|
- for (const auto &share : sharesFetched) {
|
|
|
- const auto shareData = share.toObject();
|
|
|
-
|
|
|
- const auto sharePath = [&shareData, folder]() {
|
|
|
- const auto sharePathRemote = shareData.value(QStringLiteral("path")).toString();
|
|
|
-
|
|
|
- const auto folderPath = folder->remotePath();
|
|
|
- if (folderPath != QLatin1Char('/') && sharePathRemote.startsWith(folderPath)) {
|
|
|
- // shares are ruturned with absolute remote path, so, if we have our remote root set to subfolder, we need to adjust share's remote path to relative local path
|
|
|
- const auto sharePathLocalRelative = sharePathRemote.midRef(folder->remotePathTrailingSlash().length());
|
|
|
- return sharePathLocalRelative.toString();
|
|
|
- }
|
|
|
- return sharePathRemote.size() > 1 && sharePathRemote.startsWith(QLatin1Char('/'))
|
|
|
- ? QString(sharePathRemote).remove(0, 1)
|
|
|
- : sharePathRemote;
|
|
|
- }();
|
|
|
-
|
|
|
- SyncJournalFileRecord record;
|
|
|
- if (!folder || !folder->journalDb()->getFileRecord(sharePath, &record) || !record.isValid()) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- record._isShared = true;
|
|
|
- record._lastShareStateFetchedTimestmap = timeStamp;
|
|
|
-
|
|
|
- if (!folder->journalDb()->setFileRecord(record)) {
|
|
|
- qCWarning(lcShellExtServer) << "Could not set file record for path: " << record._path;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- qCInfo(lcShellExtServer) << "Succeeded OcsShareJob for path: " << sharesPath;
|
|
|
- emit fetchSharesJobFinished(folderAlias);
|
|
|
-}
|
|
|
-
|
|
|
-void ShellExtensionsServer::slotSharesFetchError(int statusCode, const QString &message)
|
|
|
-{
|
|
|
- const auto job = qobject_cast<OcsShareJob *>(sender());
|
|
|
-
|
|
|
- Q_ASSERT(job);
|
|
|
- if (!job) {
|
|
|
- qCWarning(lcShellExtServer) << "ShellExtensionsServer::slotSharesFetched is not called by OcsShareJob's signal!";
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const auto sharesPath = job->getParamValue(QStringLiteral("path"));
|
|
|
-
|
|
|
- _runningFetchShareJobsForPaths.removeAll(sharesPath);
|
|
|
-
|
|
|
- emit fetchSharesJobFinished(sharesPath);
|
|
|
- qCWarning(lcShellExtServer) << "Failed OcsShareJob for path: " << sharesPath;
|
|
|
-}
|
|
|
-
|
|
|
void ShellExtensionsServer::parseCustomStateRequest(QLocalSocket *socket, const QVariantMap &message)
|
|
|
{
|
|
|
const auto customStateRequestMessage = message.value(VfsShellExtensions::Protocol::CustomStateProviderRequestKey).toMap();
|