Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/desktop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHannah von Reth <hannah.vonreth@owncloud.com>2021-02-19 18:00:32 +0300
committerMatthieu Gallien <matthieu.gallien@nextcloud.com>2021-08-13 00:17:13 +0300
commit8adf24112370fef10b49c274e59d89cf2ffbb7d6 (patch)
treee00d04663f07125810a80c5902a2dc21f85cf0d6
parent899010b37c4c40be90e567c965dbc7afdf439a68 (diff)
Ensure the socket listener still existsfeature/commandUploadListOfFiles
-rw-r--r--src/gui/socketapi/socketapi.cpp158
-rw-r--r--src/gui/socketapi/socketapi.h7
-rw-r--r--src/gui/socketapi/socketapi_p.h53
-rw-r--r--src/gui/socketapi/socketuploadjob.cpp49
-rw-r--r--src/gui/socketapi/socketuploadjob.h9
5 files changed, 173 insertions, 103 deletions
diff --git a/src/gui/socketapi/socketapi.cpp b/src/gui/socketapi/socketapi.cpp
index 4335c4e10..0669ebc64 100644
--- a/src/gui/socketapi/socketapi.cpp
+++ b/src/gui/socketapi/socketapi.cpp
@@ -70,8 +70,6 @@
#include <QProcess>
#include <QStandardPaths>
-#include <QTemporaryFile>
-#include <networkjobs.h>
#ifdef Q_OS_MAC
#include <CoreFoundation/CoreFoundation.h>
@@ -197,11 +195,6 @@ Q_LOGGING_CATEGORY(lcSocketApi, "nextcloud.gui.socketapi", QtInfoMsg)
Q_LOGGING_CATEGORY(lcPublicLink, "nextcloud.gui.socketapi.publiclink", QtInfoMsg)
-void SocketListener::sendMessage(const QString &function, const QJsonObject &obj, bool doWait) const
-{
- sendMessage(function + QLatin1Char(':') + QJsonDocument(obj).toJson(QJsonDocument::Compact), doWait);
-}
-
void SocketListener::sendMessage(const QString &message, bool doWait) const
{
if (!socket) {
@@ -225,16 +218,6 @@ void SocketListener::sendMessage(const QString &message, bool doWait) const
}
}
-struct ListenerHasSocketPred
-{
- QIODevice *socket;
- ListenerHasSocketPred(QIODevice *socket)
- : socket(socket)
- {
- }
- bool operator()(const SocketListener &listener) const { return listener.socket == socket; }
-};
-
SocketApi::SocketApi(QObject *parent)
: QObject(parent)
{
@@ -242,6 +225,7 @@ SocketApi::SocketApi(QObject *parent)
qRegisterMetaType<SocketListener *>("SocketListener*");
qRegisterMetaType<QSharedPointer<SocketApiJob>>("QSharedPointer<SocketApiJob>");
+ qRegisterMetaType<QSharedPointer<SocketApiJobV2>>("QSharedPointer<SocketApiJobV2>");
if (Utility::isWindows()) {
socketPath = QLatin1String(R"(\\.\pipe\)")
@@ -312,7 +296,7 @@ SocketApi::~SocketApi()
qCDebug(lcSocketApi) << "dtor";
_localServer.close();
// All remaining sockets will be destroyed with _localServer, their parent
- ASSERT(_listeners.isEmpty() || _listeners.first().socket->parent() == &_localServer);
+ ASSERT(_listeners.isEmpty() || _listeners.first()->socket->parent() == &_localServer)
_listeners.clear();
}
@@ -331,14 +315,13 @@ void SocketApi::slotNewConnection()
connect(socket, &QObject::destroyed, this, &SocketApi::slotSocketDestroyed);
ASSERT(socket->readAll().isEmpty());
- _listeners.append(SocketListener(socket));
- SocketListener &listener = _listeners.last();
-
- foreach (Folder *f, FolderMan::instance()->map()) {
+ auto listener = QSharedPointer<SocketListener>::create(socket);
+ _listeners.insert(socket, listener);
+ for (Folder *f : FolderMan::instance()->map()) {
if (f->canSync()) {
QString message = buildRegisterPathMessage(removeTrailingSlash(f->path()));
- qCInfo(lcSocketApi) << "Trying to send SocketAPI Register Path Message -->" << message << "to" << listener.socket;
- listener.sendMessage(message);
+ qCInfo(lcSocketApi) << "Trying to send SocketAPI Register Path Message -->" << message << "to" << listener->socket;
+ listener->sendMessage(message);
}
}
}
@@ -350,13 +333,13 @@ void SocketApi::onLostConnection()
auto socket = qobject_cast<QIODevice *>(sender());
ASSERT(socket);
- _listeners.erase(std::remove_if(_listeners.begin(), _listeners.end(), ListenerHasSocketPred(socket)), _listeners.end());
+ _listeners.remove(socket);
}
void SocketApi::slotSocketDestroyed(QObject *obj)
{
auto *socket = static_cast<QIODevice *>(obj);
- _listeners.erase(std::remove_if(_listeners.begin(), _listeners.end(), ListenerHasSocketPred(socket)), _listeners.end());
+ _listeners.remove(socket);
}
void SocketApi::slotReadSocket()
@@ -370,27 +353,38 @@ void SocketApi::slotReadSocket()
// the readyRead() signals are received - in that case there won't be a
// valid listener. We execute the handler anyway, but it will work with
// a SocketListener that doesn't send any messages.
- static auto noListener = SocketListener(nullptr);
- SocketListener *listener = &noListener;
- auto listenerIt = std::find_if(_listeners.begin(), _listeners.end(), ListenerHasSocketPred(socket));
- if (listenerIt != _listeners.end()) {
- listener = &*listenerIt;
- }
-
+ static auto invalidListener = QSharedPointer<SocketListener>::create(nullptr);
+ const auto listener = _listeners.value(socket, invalidListener);
while (socket->canReadLine()) {
// Make sure to normalize the input from the socket to
// make sure that the path will match, especially on OS X.
const QString line = QString::fromUtf8(socket->readLine().trimmed()).normalized(QString::NormalizationForm_C);
qCInfo(lcSocketApi) << "Received SocketAPI message <--" << line << "from" << socket;
- const QByteArray command = line.midRef(0, line.indexOf(QLatin1Char(':'))).toUtf8().toUpper().replace("/", "_");
- const QByteArray functionWithArguments = "command_" + command + (command.startsWith("ASYNC_") ? "(QSharedPointer<SocketApiJob>)" : "(QString,SocketListener*)");
- const int indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments);
+ const int argPos = line.indexOf(QLatin1Char(':'));
+ const QByteArray command = line.midRef(0, argPos).toUtf8().toUpper();
+ const int indexOfMethod = [&] {
+ QByteArray functionWithArguments = QByteArrayLiteral("command_");
+ if (command.startsWith("ASYNC_")) {
+ functionWithArguments += command + QByteArrayLiteral("(QSharedPointer<SocketApiJob>)");
+ } else if (command.startsWith("V2/")) {
+ functionWithArguments += QByteArrayLiteral("V2_") + command.mid(3) + QByteArrayLiteral("(QSharedPointer<SocketApiJobV2>)");
+ } else {
+ functionWithArguments += command + QByteArrayLiteral("(QString,SocketListener*)");
+ }
+ Q_ASSERT(staticQtMetaObject.normalizedSignature(functionWithArguments) == functionWithArguments);
+ const auto out = staticMetaObject.indexOfMethod(functionWithArguments);
+ if (out == -1) {
+ listener->sendError(QStringLiteral("Function %1 not found").arg(QString::fromUtf8(functionWithArguments)));
+ }
+ ASSERT(out != -1)
+ return out;
+ }();
- const auto argument = line.midRef(command.length() + 1);
+ const auto argument = argPos != -1 ? line.midRef(argPos + 1) : QStringRef();
if (command.startsWith("ASYNC_")) {
auto arguments = argument.split('|');
if (arguments.size() != 2) {
- listener->sendMessage(QStringLiteral("argument count is wrong"));
+ listener->sendError(QStringLiteral("argument count is wrong"));
return;
}
@@ -409,15 +403,31 @@ void SocketApi::slotReadSocket()
<< "with argument:" << argument;
socketApiJob->reject(QStringLiteral("command not found"));
}
+ } else if (command.startsWith("V2/")) {
+ QJsonParseError error;
+ const auto json = QJsonDocument::fromJson(argument.toUtf8(), &error).object();
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(lcSocketApi()) << "Invalid json" << argument.toString() << error.errorString();
+ listener->sendError(error.errorString());
+ return;
+ }
+ auto socketApiJob = QSharedPointer<SocketApiJobV2>::create(listener, command, json);
+ if (indexOfMethod != -1) {
+ staticMetaObject.method(indexOfMethod)
+ .invoke(this, Qt::QueuedConnection,
+ Q_ARG(QSharedPointer<SocketApiJobV2>, socketApiJob));
+ } else {
+ qCWarning(lcSocketApi) << "The command is not supported by this version of the client:" << command
+ << "with argument:" << argument;
+ socketApiJob->failure(QStringLiteral("command not found"));
+ }
} else {
if (indexOfMethod != -1) {
// to ensure that listener is still valid we need to call it with Qt::DirectConnection
ASSERT(thread() == QThread::currentThread())
staticMetaObject.method(indexOfMethod)
.invoke(this, Qt::DirectConnection, Q_ARG(QString, argument.toString()),
- Q_ARG(SocketListener *, listener));
- } else {
- qCWarning(lcSocketApi) << "The command is not supported by this version of the client:" << command << "with argument:" << argument;
+ Q_ARG(SocketListener *, listener.data()));
}
}
}
@@ -431,10 +441,10 @@ void SocketApi::slotRegisterPath(const QString &alias)
Folder *f = FolderMan::instance()->folder(alias);
if (f) {
- QString message = buildRegisterPathMessage(removeTrailingSlash(f->path()));
- foreach (auto &listener, _listeners) {
- qCInfo(lcSocketApi) << "Trying to send SocketAPI Register Path Message -->" << message << "to" << listener.socket;
- listener.sendMessage(message);
+ const QString message = buildRegisterPathMessage(removeTrailingSlash(f->path()));
+ for (const auto &listener : qAsConst(_listeners)) {
+ qCInfo(lcSocketApi) << "Trying to send SocketAPI Register Path Message -->" << message << "to" << listener->socket;
+ listener->sendMessage(message);
}
}
@@ -479,8 +489,8 @@ void SocketApi::slotUpdateFolderView(Folder *f)
void SocketApi::broadcastMessage(const QString &msg, bool doWait)
{
- foreach (auto &listener, _listeners) {
- listener.sendMessage(msg, doWait);
+ for (const auto &listener : qAsConst(_listeners)) {
+ listener->sendMessage(msg, doWait);
}
}
@@ -530,8 +540,8 @@ void SocketApi::broadcastStatusPushMessage(const QString &systemPath, SyncFileSt
QString msg = buildMessage(QLatin1String("STATUS"), systemPath, fileStatus.toSocketAPIString());
Q_ASSERT(!systemPath.endsWith('/'));
uint directoryHash = qHash(systemPath.left(systemPath.lastIndexOf('/')));
- foreach (auto &listener, _listeners) {
- listener.sendMessageIfDirectoryMonitored(msg, directoryHash);
+ for (const auto &listener : qAsConst(_listeners)) {
+ listener->sendMessageIfDirectoryMonitored(msg, directoryHash);
}
}
@@ -935,20 +945,20 @@ void SocketApi::command_MOVE_ITEM(const QString &localFile, SocketListener *)
solver.setRemoteVersionFilename(target);
}
-void SocketApi::command_V2_LIST_ACCOUNTS(const QString &, SocketListener *listener) const
+void SocketApi::command_V2_LIST_ACCOUNTS(const QSharedPointer<SocketApiJobV2> &job) const
{
QJsonArray out;
for (auto acc : AccountManager::instance()->accounts()) {
// TODO: Use uuid once https://github.com/owncloud/client/pull/8397 is merged
out << QJsonObject({ { "name", acc->account()->displayName() }, { "id", acc->account()->id() } });
}
- listener->sendMessage(QStringLiteral("V2/ACCOUNTS"), { { "accounts", out } });
+ job->success({ { "accounts", out } });
}
-void SocketApi::command_V2_UPLOAD_FILES_FROM(const QString &argument, SocketListener *listener) const
+void SocketApi::command_V2_UPLOAD_FILES_FROM(const QSharedPointer<SocketApiJobV2> &job) const
{
- auto job = new SocketUploadJob(listener, argument);
- job->start();
+ auto uploadJob = new SocketUploadJob(job);
+ uploadJob->start();
}
void SocketApi::emailPrivateLink(const QString &link)
@@ -1429,6 +1439,46 @@ QString SocketApi::buildRegisterPathMessage(const QString &path)
return message;
}
+void SocketApiJob::resolve(const QString &response)
+{
+ _socketListener->sendMessage(QStringLiteral("RESOLVE|") + _jobId + QLatin1Char('|') + response);
+}
+
+void SocketApiJob::resolve(const QJsonObject &response)
+{
+ resolve(QJsonDocument { response }.toJson());
+}
+
+void SocketApiJob::reject(const QString &response)
+{
+ _socketListener->sendMessage(QStringLiteral("REJECT|") + _jobId + QLatin1Char('|') + response);
+}
+
+SocketApiJobV2::SocketApiJobV2(const QSharedPointer<SocketListener> &socketListener, const QByteArray &command, const QJsonObject &arguments)
+ : _socketListener(socketListener)
+ , _command(command)
+ , _jobId(arguments[QStringLiteral("id")].toString())
+ , _arguments(arguments[QStringLiteral("arguments")].toObject())
+{
+ ASSERT(!_jobId.isEmpty())
+}
+
+void SocketApiJobV2::success(const QJsonObject &response) const
+{
+ doFinish(response);
+}
+
+void SocketApiJobV2::failure(const QString &error) const
+{
+ doFinish({ { QStringLiteral("error"), error } });
+}
+
+void SocketApiJobV2::doFinish(const QJsonObject &obj) const
+{
+ _socketListener->sendMessage(_command + QStringLiteral("_RESULT:") + QJsonDocument({ { QStringLiteral("id"), _jobId }, { QStringLiteral("arguments"), obj } }).toJson(QJsonDocument::Compact));
+ Q_EMIT finished();
+}
+
} // namespace OCC
#include "socketapi.moc"
diff --git a/src/gui/socketapi/socketapi.h b/src/gui/socketapi/socketapi.h
index cd7d9ff01..1350607e4 100644
--- a/src/gui/socketapi/socketapi.h
+++ b/src/gui/socketapi/socketapi.h
@@ -40,6 +40,7 @@ class Folder;
class SocketListener;
class DirectEditor;
class SocketApiJob;
+class SocketApiJobV2;
Q_DECLARE_LOGGING_CATEGORY(lcSocketApi)
@@ -130,8 +131,8 @@ private:
#endif
// External sync
- Q_INVOKABLE void command_V2_LIST_ACCOUNTS(const QString &argument, SocketListener *listener) const;
- Q_INVOKABLE void command_V2_UPLOAD_FILES_FROM(const QString &argument, SocketListener *listener) const;
+ Q_INVOKABLE void command_V2_LIST_ACCOUNTS(const QSharedPointer<SocketApiJobV2> &job) const;
+ Q_INVOKABLE void command_V2_UPLOAD_FILES_FROM(const QSharedPointer<SocketApiJobV2> &job) const;
// Fetch the private link and call targetFun
void fetchPrivateLinkUrlHelper(const QString &localFile, const std::function<void(const QString &url)> &targetFun);
@@ -168,7 +169,7 @@ private:
QString buildRegisterPathMessage(const QString &path);
QSet<QString> _registeredAliases;
- QList<SocketListener> _listeners;
+ QMap<QIODevice *, QSharedPointer<SocketListener>> _listeners;
SocketApiServer _localServer;
};
}
diff --git a/src/gui/socketapi/socketapi_p.h b/src/gui/socketapi/socketapi_p.h
index 38738c84b..91724d6da 100644
--- a/src/gui/socketapi/socketapi_p.h
+++ b/src/gui/socketapi/socketapi_p.h
@@ -62,13 +62,20 @@ class SocketListener
public:
QPointer<QIODevice> socket;
- explicit SocketListener(QIODevice *socket)
- : socket(socket)
+ explicit SocketListener(QIODevice *_socket)
+ : socket(_socket)
{
}
void sendMessage(const QString &message, bool doWait = false) const;
- void sendMessage(const QString &function, const QJsonObject &obj, bool doWait = false) const;
+ void sendWarning(const QString &message, bool doWait = false) const
+ {
+ sendMessage(QStringLiteral("WARNING:") + message, doWait);
+ }
+ void sendError(const QString &message, bool doWait = false) const
+ {
+ sendMessage(QStringLiteral("ERROR:") + message, doWait);
+ }
void sendMessageIfDirectoryMonitored(const QString &message, uint systemDirectoryHash) const
{
@@ -110,30 +117,48 @@ class SocketApiJob : public QObject
{
Q_OBJECT
public:
- SocketApiJob(const QString &jobId, SocketListener *socketListener, const QJsonObject &arguments)
+ explicit SocketApiJob(const QString &jobId, const QSharedPointer<SocketListener> &socketListener, const QJsonObject &arguments)
: _jobId(jobId)
, _socketListener(socketListener)
, _arguments(arguments)
{
}
- void resolve(const QString &response = QString())
- {
- _socketListener->sendMessage(QLatin1String("RESOLVE|") + _jobId + '|' + response);
- }
+ void resolve(const QString &response = QString());
- void resolve(const QJsonObject &response) { resolve(QJsonDocument { response }.toJson()); }
+ void resolve(const QJsonObject &response);
const QJsonObject &arguments() { return _arguments; }
- void reject(const QString &response)
- {
- _socketListener->sendMessage(QLatin1String("REJECT|") + _jobId + '|' + response);
- }
+ void reject(const QString &response);
+
+protected:
+ QString _jobId;
+ QSharedPointer<SocketListener> _socketListener;
+ QJsonObject _arguments;
+};
+
+class SocketApiJobV2 : public QObject
+{
+ Q_OBJECT
+public:
+ explicit SocketApiJobV2(const QSharedPointer<SocketListener> &socketListener, const QByteArray &command, const QJsonObject &arguments);
+
+ void success(const QJsonObject &response) const;
+ void failure(const QString &error) const;
+
+ const QJsonObject &arguments() const { return _arguments; }
+ QByteArray command() const { return _command; }
+
+Q_SIGNALS:
+ void finished() const;
private:
+ void doFinish(const QJsonObject &obj) const;
+
+ QSharedPointer<SocketListener> _socketListener;
+ const QByteArray _command;
QString _jobId;
- SocketListener *_socketListener;
QJsonObject _arguments;
};
}
diff --git a/src/gui/socketapi/socketuploadjob.cpp b/src/gui/socketapi/socketuploadjob.cpp
index 8ade844b0..467a46973 100644
--- a/src/gui/socketapi/socketuploadjob.cpp
+++ b/src/gui/socketapi/socketuploadjob.cpp
@@ -25,24 +25,30 @@
using namespace OCC;
-SocketUploadJob::SocketUploadJob(OCC::SocketListener *listener, const QString &argument, QObject *parent)
- : QObject(parent)
- , _listener(listener)
+SocketUploadJob::SocketUploadJob(const QSharedPointer<SocketApiJobV2> &job)
+ : _apiJob(job)
{
- const auto args = QJsonDocument::fromJson(argument.toUtf8()).object();
- _localPath = args[QLatin1String("localPath")].toString();
- _remotePath = args[QLatin1String("remotePath")].toString();
- if (!_remotePath.startsWith("/")) {
+ connect(job.data(), &SocketApiJobV2::finished, this, &SocketUploadJob::deleteLater);
+
+ _localPath = _apiJob->arguments()[QLatin1String("localPath")].toString();
+ _remotePath = _apiJob->arguments()[QLatin1String("remotePath")].toString();
+ if (!_remotePath.startsWith(QLatin1Char('/'))) {
_remotePath = QLatin1Char('/') + _remotePath;
}
- _pattern = args[QLatin1String("pattern")].toString();
+ _pattern = job->arguments()[QLatin1String("pattern")].toString();
// TODO: use uuid
- const auto accname = args[QLatin1String("account")][QLatin1String("name")].toString();
+ const auto accname = job->arguments()[QLatin1String("account")][QLatin1String("name")].toString();
auto account = AccountManager::instance()->account(accname);
- ENFORCE(QFileInfo(_localPath).isAbsolute())
- ENFORCE(_tmp.open())
+ if (!QFileInfo(_localPath).isAbsolute()) {
+ job->failure(QStringLiteral("Local path must be a an absolute path"));
+ return;
+ }
+ if (!_tmp.open()) {
+ job->failure(QStringLiteral("Failed to create temporary database"));
+ return;
+ }
_db = new SyncJournalDb(_tmp.fileName(), this);
_engine = new SyncEngine(account->account(), _localPath.endsWith(QLatin1Char('/')) ? _localPath : _localPath + QLatin1Char('/'), _remotePath, _db);
@@ -54,10 +60,12 @@ SocketUploadJob::SocketUploadJob(OCC::SocketListener *listener, const QString &a
connect(_engine, &OCC::SyncEngine::finished, this, [this](bool ok) {
if (ok) {
- finish({});
+ _apiJob->success({ { "localPath", _localPath }, { "syncedFiles", QJsonArray::fromStringList(_syncedFiles) } });
}
});
- connect(_engine, &OCC::SyncEngine::syncError, this, &SocketUploadJob::finish);
+ connect(_engine, &OCC::SyncEngine::syncError, this, [this](const QString &error, ErrorCategory) {
+ _apiJob->failure(error);
+ });
}
void SocketUploadJob::start()
@@ -65,7 +73,7 @@ void SocketUploadJob::start()
auto opt = _engine->syncOptions();
opt.setFilePattern(_pattern);
if (!opt.fileRegex().isValid()) {
- finish(opt.fileRegex().errorString());
+ _apiJob->failure(opt.fileRegex().errorString());
return;
}
_engine->setSyncOptions(opt);
@@ -75,19 +83,10 @@ void SocketUploadJob::start()
connect(mkdir, &OCC::MkColJob::finishedWithoutError, _engine, &OCC::SyncEngine::startSync);
connect(mkdir, &OCC::MkColJob::finishedWithError, this, [this](QNetworkReply *reply) {
if (reply->error() == 202) {
- finish(QStringLiteral("Destination %1 already exists").arg(_remotePath));
+ _apiJob->failure(QStringLiteral("Destination %1 already exists").arg(_remotePath));
} else {
- finish(reply->errorString());
+ _apiJob->failure(reply->errorString());
}
});
mkdir->start();
}
-
-void SocketUploadJob::finish(const QString &error)
-{
- if (!_finished) {
- _finished = true;
- _listener->sendMessage(QStringLiteral("V2/UPLOAD_FILES_RESULT"), { { "localPath", _localPath }, { "error", error }, { "syncedFiles", QJsonArray::fromStringList(_syncedFiles) } });
- deleteLater();
- }
-}
diff --git a/src/gui/socketapi/socketuploadjob.h b/src/gui/socketapi/socketuploadjob.h
index 477b8bf0c..3b04061d3 100644
--- a/src/gui/socketapi/socketuploadjob.h
+++ b/src/gui/socketapi/socketuploadjob.h
@@ -28,14 +28,11 @@ class SocketUploadJob : public QObject
{
Q_OBJECT
public:
- SocketUploadJob(OCC::SocketListener *listener, const QString &argument, QObject *parent = nullptr);
-
+ SocketUploadJob(const QSharedPointer<SocketApiJobV2> &job);
void start();
- void finish(const QString &error);
-
private:
- SocketListener *_listener;
+ QSharedPointer<SocketApiJobV2> _apiJob;
QString _localPath;
QString _remotePath;
QString _pattern;
@@ -43,7 +40,5 @@ private:
SyncJournalDb *_db;
SyncEngine *_engine;
QStringList _syncedFiles;
-
- bool _finished = false;
};
}