Page MenuHomePhorge

D217.1751406466.diff
No OneTemporary

Size
20 KB
Referenced Files
None
Subscribers
None

D217.1751406466.diff

diff --git a/src/contents/ui/FileHandler.qml b/src/contents/ui/FileHandler.qml
--- a/src/contents/ui/FileHandler.qml
+++ b/src/contents/ui/FileHandler.qml
@@ -30,7 +30,9 @@
* according to whether the file is encrypted or not.
*/
property var mxcUri: ""
- property var url: mxcUri ? fileHandler.matrixSdk.mxcUriToHttp(fileHandler.mxcUri) : ""
+ property var httpVer: matrixSdk.supportVersion("v1.11") ? MK.MatrixSdk.EndpointV1 : MK.MatrixSdk.EndpointV3
+ property var url: fileHandler.mxcToHttp(httpVer, mxcUri)
+ property var saveFileUrl: ""
property var mediaId: mxcUri && fileHandler.getMediaId(fileHandler.mxcUri)
property var sha256: ""
property var key: ""
@@ -79,9 +81,16 @@
}
function downloadFile(saveFileUrl) {
- fileHandler.kazvIOManager.startNewDownloadJob(fileHandler.url,saveFileUrl,
+ fileHandler.saveFileUrl = saveFileUrl
+ let token = ""
+ if (httpVer === MK.MatrixSdk.EndpointV1) {
+ token = matrixSdk.token
+ }
+ fileHandler.kazvIOManager.startNewDownloadJob(fileHandler.url,
+ fileHandler.saveFileUrl,
fileHandler.mediaId,
fileHandler.sha256,
+ token,
fileHandler.key,
fileHandler.iv)
fileHandler.updateKazvIOJob(fileHandler.mediaId)
@@ -90,9 +99,14 @@
}
function cacheFile() {
+ let token = ""
+ if (fileHandler.httpVer === MK.MatrixSdk.EndpointV1) {
+ token = matrixSdk.token
+ }
fileHandler.cachedFile = fileHandler.kazvIOManager.cacheFile(fileHandler.url,
fileHandler.mediaId,
fileHandler.sha256,
+ token,
fileHandler.key,
fileHandler.iv)
fileHandler.updateKazvIOJob(fileHandler.mediaId)
@@ -112,6 +126,19 @@
}
}
+ function mxcToHttp(httpVer, mxcUri) {
+ if (!mxcUri) {
+ return "";
+ }
+ if (httpVer === MK.MatrixSdk.EndpointV3) {
+ return matrixSdk.mxcUriToHttp(mxcUri);
+ } else if (httpVer === MK.MatrixSdk.EndpointV1) {
+ return matrixSdk.mxcUriToHttpV1(mxcUri);
+ } else {
+ return "";
+ }
+ }
+
Component.onCompleted: {
// Check if there is an encryted file or unencrypted file
if (encryptedFileMxcUri &&
diff --git a/src/kazv-io-job.hpp b/src/kazv-io-job.hpp
--- a/src/kazv-io-job.hpp
+++ b/src/kazv-io-job.hpp
@@ -22,6 +22,8 @@
#include <memory>
#include <optional>
+using namespace Qt::Literals::StringLiterals;
+
struct KazvIOBaseJobPrivate;
class KazvIOBaseJob : public QObject
@@ -119,8 +121,11 @@
public:
KazvIODownloadJob(const QString &hash, std::optional<Kazv::AES256CTRDesc> aes = std::nullopt, QObject *parent = 0);
- KazvIODownloadJob(const QString &fileName, const QUrl &serverUrl, const bool showProgressBar, const QString &hash,
- std::optional<Kazv::AES256CTRDesc> aes = std::nullopt, QObject *parent = 0);
+ KazvIODownloadJob(const QString &fileName, const QUrl &serverUrl,
+ const bool showProgressBar, const QString &hash,
+ const QString &token = u""_s,
+ std::optional<Kazv::AES256CTRDesc> aes = std::nullopt,
+ QObject *parent = 0);
~KazvIODownloadJob();
Q_INVOKABLE QString fileName();
diff --git a/src/kazv-io-job.cpp b/src/kazv-io-job.cpp
--- a/src/kazv-io-job.cpp
+++ b/src/kazv-io-job.cpp
@@ -4,10 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <client.hpp>
#include <kazv-defs.hpp>
#include "kazv-io-job.hpp"
#include "matrix-room.hpp"
#include "kazv-log.hpp"
+#include "client/actions/content.hpp"
#include <QObject>
#include <QPointer>
@@ -164,15 +166,34 @@
m_d->hash = hash;
}
-KazvIODownloadJob::KazvIODownloadJob(const QString &fileName, const QUrl &serverUrl, bool showProgressBar,
- const QString &hash, std::optional<Kazv::AES256CTRDesc> aes, QObject *parent)
+KazvIODownloadJob::KazvIODownloadJob(const QString &fileName,
+ const QUrl &serverUrl, bool showProgressBar, const QString &hash,
+ const QString &token, std::optional<Kazv::AES256CTRDesc> aes,
+ QObject *parent)
: KazvIOBaseJob(aes, parent)
, m_d(new KazvIODownloadJobPrivate)
{
- if (setFile(fileName)) {
- auto flag = showProgressBar ? KIO::DefaultFlags : KIO::HideProgressInfo;
- setJob(KIO::get(serverUrl, KIO::NoReload, flag));
+ if (!setFile(fileName)) {
+ return;
}
+ auto flag = showProgressBar ? KIO::DefaultFlags : KIO::HideProgressInfo;
+ auto kioJob = KIO::get(serverUrl, KIO::NoReload, flag);
+ if (!token.isEmpty()) {
+ auto [serverName, mediaId] =
+ [&serverUrl]() -> std::pair<std::string, std::string>{
+ auto splited = serverUrl.path().split(u"/"_s);
+ return {splited.last(2).first().toStdString(),
+ splited.last(2).last().toStdString()};
+ }();
+ auto getContentJobV1 = Kazv::GetContentJobV1{
+ serverUrl.toString().toStdString(),
+ token.toStdString(), serverName, mediaId};
+ kioJob->addMetaData(u"customHTTPHeader"_s,
+ QStringLiteral("Authorization: ") .append(QString::fromStdString(
+ getContentJobV1.requestHeader()->at("Authorization"))));
+ }
+
+ setJob(kioJob);
m_d->hash = hash;
}
diff --git a/src/kazv-io-manager.hpp b/src/kazv-io-manager.hpp
--- a/src/kazv-io-manager.hpp
+++ b/src/kazv-io-manager.hpp
@@ -21,6 +21,8 @@
#include <memory>
+using namespace Qt::Literals::StringLiterals;
+
struct KazvIOManagerPrivate;
class KazvIOManager : public QObject
@@ -35,8 +37,8 @@
QPointer<KazvIODownloadJob> makeDownloadJob(
const QUrl &serverUrl, const QUrl &localFileUrl,
const bool showPrograssBar, const QString &hash,
- const QString &key = QStringLiteral(""),
- const QString &iv = QStringLiteral("")) const;
+ const QString &token = u""_s, const QString &key = u""_s,
+ const QString &iv = u""_s) const;
public:
explicit KazvIOManager(QObject *parent = 0);
@@ -47,11 +49,13 @@
* Multiple calls do not repeat downloads
* Returns the local url of the cached file.
*/
- Q_INVOKABLE QUrl cacheFile(const QUrl &fileUrl, const QString &id, const QString &hash = QStringLiteral(""),
- const QString &key = QStringLiteral(""), const QString &iv = QStringLiteral(""));
- Q_INVOKABLE KazvIOBaseJob *startNewDownloadJob(const QUrl &serverUrl, const QUrl &localFileName,
- const QString &jobId, const QString &hash,
- const QString &key = QStringLiteral(""), const QString &iv = QStringLiteral(""));
+ Q_INVOKABLE QUrl cacheFile(const QUrl &fileUrl, const QString &id,
+ const QString &token = u""_s, const QString &hash = u""_s,
+ const QString &key = u""_s, const QString &iv = u""_s);
+ Q_INVOKABLE KazvIOBaseJob *startNewDownloadJob(const QUrl &serverUrl,
+ const QUrl &localFileName, const QString &jobId, const QString &hash,
+ const QString &token = u""_s, const QString &key = u""_s,
+ const QString &iv = u""_s);
Q_INVOKABLE KazvIOBaseJob *startNewUploadJob(
const QUrl &serverUrl, const QUrl &localFileName, const QString &token,
const QString &roomId, MatrixRoomList *roomList, const bool encrypted,
diff --git a/src/kazv-io-manager.cpp b/src/kazv-io-manager.cpp
--- a/src/kazv-io-manager.cpp
+++ b/src/kazv-io-manager.cpp
@@ -61,30 +61,31 @@
QPointer<KazvIODownloadJob> KazvIOManager::makeDownloadJob(
const QUrl &serverUrl, const QUrl &localFileUrl,
- const bool showPrograssBar, const QString &hash,
+ const bool showPrograssBar, const QString &hash, const QString &token,
const QString &key, const QString &iv) const
{
QPointer<KazvIODownloadJob> job{};
if (key.isEmpty()) {
- job = new KazvIODownloadJob{localFileUrl.toLocalFile(),
- serverUrl, showPrograssBar, hash, std::nullopt};
+ job = new KazvIODownloadJob{localFileUrl.toLocalFile(), serverUrl,
+ showPrograssBar, hash, token, std::nullopt};
} else {
auto aes = Kazv::AES256CTRDesc{key.toStdString(), iv.toStdString()};
- job = new KazvIODownloadJob{localFileUrl.toLocalFile(),
- serverUrl, showPrograssBar, hash, aes};
+ job = new KazvIODownloadJob{localFileUrl.toLocalFile(), serverUrl,
+ showPrograssBar, hash, token, aes};
}
return job;
}
-QUrl KazvIOManager::cacheFile(const QUrl &fileUrl, const QString &id, const QString &hash,
- const QString &key, const QString &iv)
+QUrl KazvIOManager::cacheFile(const QUrl &fileUrl, const QString &id,
+ const QString &hash, const QString &token, const QString &key,
+ const QString &iv)
{
const QString filePath = m_d->cacheDir.filePath(id);
QDir dir(m_d->cacheDir.path());
if (!dir.exists(id) && this->getCacheJob(id) == nullptr) {
auto job = this->makeDownloadJob(fileUrl,
QUrl::fromLocalFile(filePath),
- /* showProgressBar = */ false, hash, key, iv);
+ /* showProgressBar = */ false, hash, token, key, iv);
m_d->cacheJobs[id] = job;
connect(job, &KazvIOBaseJob::result, this, [this, id](auto /* result */) {
m_d->cacheJobs[id]->deleteLater();
@@ -94,11 +95,12 @@
return QUrl::fromLocalFile(filePath);
}
-KazvIOBaseJob *KazvIOManager::startNewDownloadJob(const QUrl &serverUrl, const QUrl &localFileUrl,
- const QString &jobId, const QString &hash, const QString &key, const QString &iv)
+KazvIOBaseJob *KazvIOManager::startNewDownloadJob(const QUrl &serverUrl,
+ const QUrl &localFileUrl, const QString &jobId, const QString &hash,
+ const QString &token, const QString &key, const QString &iv)
{
auto downloadJob = this->makeDownloadJob(serverUrl, localFileUrl,
- /* showProgressBar = */ true, hash, key, iv);
+ /* showProgressBar = */ true, hash, token, key, iv);
m_d->downloadJobs[jobId].reset(downloadJob);
QQmlEngine::setObjectOwnership(downloadJob.data(), QQmlEngine::CppOwnership);
diff --git a/src/matrix-sdk.hpp b/src/matrix-sdk.hpp
--- a/src/matrix-sdk.hpp
+++ b/src/matrix-sdk.hpp
@@ -83,6 +83,13 @@
Q_ENUM(LoadSessionResult);
+ enum EndpointVer {
+ EndpointV3, // Classical endpoint series
+ EndpointV1, // Endpoints that needs authorization
+ };
+
+ Q_ENUM(EndpointVer);
+
explicit MatrixSdk(QObject *parent = 0);
~MatrixSdk() override;
@@ -90,13 +97,18 @@
LAGER_QT_READER(QString, userId);
LAGER_QT_READER(QString, token);
LAGER_QT_READER(QString, deviceId);
+ LAGER_QT_READER(immer::array<std::string>, versions); // The versions of the Matrix specification supported by the server.
Q_INVOKABLE MatrixRoomList *roomList() const;
Q_INVOKABLE QString mxcUriToHttp(QString mxcUri) const;
+ Q_INVOKABLE QString mxcUriToHttpV1(QString mxcUri) const;
+
Q_INVOKABLE MatrixDeviceList *devicesOfUser(QString userId) const;
+ Q_INVOKABLE bool supportVersion(QString version) const;
+
Kazv::RandomInterface &randomGenerator() const;
private:
diff --git a/src/matrix-sdk.cpp b/src/matrix-sdk.cpp
--- a/src/matrix-sdk.cpp
+++ b/src/matrix-sdk.cpp
@@ -305,6 +305,7 @@
LAGER_QT(userId) = m_d->clientOnSecondaryRoot.userId().xform(strToQt); Q_EMIT userIdChanged(userId());
LAGER_QT(token) = m_d->clientOnSecondaryRoot.token().xform(strToQt); Q_EMIT tokenChanged(token());
LAGER_QT(deviceId) = m_d->clientOnSecondaryRoot.deviceId().xform(strToQt); Q_EMIT deviceIdChanged(deviceId());
+ LAGER_QT(versions) = m_d->clientOnSecondaryRoot.supportVersions(); Q_EMIT versionsChanged(versions());
m_d->watchable.afterAll(
[this](KazvEvent e) {
@@ -371,11 +372,21 @@
return QString::fromStdString(m_d->clientOnSecondaryRoot.mxcUriToHttp(mxcUri.toStdString()));
}
+QString MatrixSdk::mxcUriToHttpV1(QString mxcUri) const
+{
+ return QString::fromStdString(m_d->clientOnSecondaryRoot.mxcUriToHttpV1(mxcUri.toStdString()));
+}
+
MatrixDeviceList *MatrixSdk::devicesOfUser(QString userId) const
{
return new MatrixDeviceList(m_d->clientOnSecondaryRoot.devicesOfUser(userId.toStdString()));
}
+bool MatrixSdk::supportVersion(QString version) const
+{
+ return std::find(versions().begin(), versions().end(), version.toStdString()) != versions().end();
+}
+
std::string MatrixSdk::validateHomeserverUrl(const QString &url)
{
if (url.isEmpty()) {
@@ -575,6 +586,7 @@
return SessionDeserializeFailed;
}
emplace(std::move(model), std::move(lockGuard));
+ m_d->clientOnSecondaryRoot.getVersions(LAGER_QT(serverUrl).get().toStdString());
return SessionLoadSuccess;
};
diff --git a/src/tests/kazv-io-job-test.cpp b/src/tests/kazv-io-job-test.cpp
--- a/src/tests/kazv-io-job-test.cpp
+++ b/src/tests/kazv-io-job-test.cpp
@@ -40,20 +40,25 @@
const QString downloadEndpoint =
u"/_matrix/client/v1/media/download/serverName/download"_s;
+ const QString downloadAuthEndpoint =
+ u"/_matrix/client/v1/media/download/serverName/auth"_s;
const QString downloadPauseEndpoint =
u"/_matrix/client/v1/media/download/serverName/pause"_s;
const QString downloadCancelEndpoint =
u"/_matrix/client/v1/media/download/serverName/cancel"_s;
const QString uploadEndpoint = u"/_matrix/media/v3/upload"_s;
+ const QString token = u"token"_s;
const char *downloadFileContent = "download";
const char *uploadFileContent = "upload";
const char *responseErrorContent = "ResponseError";
QString serverUrl;
+ bool hasAuth{false};
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void testDownload();
+ void testDownloadAuth();
void testUpload();
void testDownloadPause();
void testUploadPause();
@@ -96,6 +101,10 @@
res.write(downloadFile.readAll(), "application/octet-stream"_ba);
downloadFile.close();
});
+ httpServer.route(downloadAuthEndpoint, [this](const QHttpServerRequest &req) {
+ hasAuth = !req.headers().value(u"Authorization"_s).isNull();
+ return QHttpServerResponse(QHttpServerResponder::StatusCode::Ok);
+ });
httpServer.route(downloadPauseEndpoint, [this](QHttpServerResponderRef res) {
downloadFile.open();
QSignalSpy qs{this, &KazvIOJobTest::readyResume};
@@ -160,6 +169,18 @@
savedFile.close();
}
+void KazvIOJobTest::testDownloadAuth()
+{
+ // QTemporaryFile cannot be written by QSaveFile, use QTemporaryDir instead.
+ QTemporaryDir dir{};
+ auto fileName = dir.filePath(u"savedFile"_s);
+ auto url = QUrl{serverUrl + downloadAuthEndpoint};
+
+ KazvIODownloadJob job{fileName, url, false, hashStr, token};
+ QTRY_VERIFY(job.isResulted());
+ QVERIFY(hasAuth);
+}
+
void KazvIOJobTest::testUpload()
{
QTemporaryFile file;
diff --git a/src/tests/quick-tests/test-helpers/KazvIOManagerMock.qml b/src/tests/quick-tests/test-helpers/KazvIOManagerMock.qml
--- a/src/tests/quick-tests/test-helpers/KazvIOManagerMock.qml
+++ b/src/tests/quick-tests/test-helpers/KazvIOManagerMock.qml
@@ -21,6 +21,8 @@
property var deleteDownloadJob: mockHelper.noop()
property var deleteUploadJob: mockHelper.noop()
property var cacheFile: mockHelper.noop()
+ property var getCacheJob: mockHelper.noop()
+ property var startNewDownloadJob: mockHelper.noop()
function getDownloadJob (jobId) {
const component = Qt.createComponent("KazvIOJobMock.qml");
diff --git a/src/tests/quick-tests/test-helpers/MatrixSdkMock.qml b/src/tests/quick-tests/test-helpers/MatrixSdkMock.qml
--- a/src/tests/quick-tests/test-helpers/MatrixSdkMock.qml
+++ b/src/tests/quick-tests/test-helpers/MatrixSdkMock.qml
@@ -29,6 +29,8 @@
property var sessions: []
+ property var versions: ["v1.11"]
+
function allSessions() {
return sessions;
}
@@ -38,7 +40,16 @@
return uri || '';
}
+ function mxcUriToHttpV1 (uri) {
+ console.log('mxcUriToHttpV1');
+ return uri || '';
+ }
+
function devicesOfUser (userId) {
return [];
}
+
+ function supportVersion (ver) {
+ return !!versions.includes(ver);
+ }
}
diff --git a/src/tests/quick-tests/tst_FileHandler.qml b/src/tests/quick-tests/tst_FileHandler.qml
new file mode 100644
--- /dev/null
+++ b/src/tests/quick-tests/tst_FileHandler.qml
@@ -0,0 +1,129 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2025 nannanko <nannanko@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import QtQuick
+import QtTest
+
+import '../../contents/ui' as Kazv
+import 'test-helpers' as QmlHelpers
+import 'test-helpers.js' as JsHelpers
+
+QmlHelpers.TestItem {
+ id: item
+
+ property var unencryptedEventContent: ({
+ body: "fileName",
+ msgtype: "m.image",
+ url: "mxc://some",
+ })
+
+ property var encryptedEventContent: ({
+ body: "fileName",
+ msgtype: "m.image",
+ file: {
+ url: "mxc://some",
+ key: {
+ kty: "oct",
+ key_ops: ["encrypt","decrypt"],
+ alg: "A256CTR",
+ k: "somekey",
+ ext: true,
+ },
+ iv: "someiv",
+ hashes: {
+ "sha256": "somesha256hash"
+ },
+ v: "v2",
+ }
+ })
+
+ property var oldFileHandler: Kazv.FileHandler {
+ eventContent: unencryptedEventContent
+ autoCache: true
+ kazvIOManager: QmlHelpers.KazvIOManagerMock {}
+ matrixSdk: QmlHelpers.MatrixSdkMock {
+ versions: []
+ property var mxcUriToHttp: mockHelper.noop()
+ }
+ }
+
+ property var newFileHandler: Kazv.FileHandler {
+ eventContent: unencryptedEventContent
+ autoCache: true
+ kazvIOManager: QmlHelpers.KazvIOManagerMock {}
+ matrixSdk: QmlHelpers.MatrixSdkMock {
+ property var mxcUriToHttpV1: mockHelper.noop()
+ }
+ }
+
+ property var unencryptedFileHandler: Kazv.FileHandler {
+ eventContent: unencryptedEventContent
+ autoCache: true
+ kazvIOManager: QmlHelpers.KazvIOManagerMock {}
+ matrixSdk: QmlHelpers.MatrixSdkMock {}
+ }
+
+ property var encryptedFileHandler: Kazv.FileHandler {
+ eventContent: encryptedEventContent
+ autoCache: true
+ kazvIOManager: QmlHelpers.KazvIOManagerMock {}
+ matrixSdk: QmlHelpers.MatrixSdkMock {}
+ }
+
+ TestCase {
+ id: fileHandlerTest
+ name: 'fileHandlerTest'
+ when: windowShown
+
+ function test_specVersion() {
+ compare(oldFileHandler.matrixSdk.mxcUriToHttp.calledTimes(), 1);
+ compare(newFileHandler.matrixSdk.mxcUriToHttpV1.calledTimes(), 1);
+
+ compare(oldFileHandler.kazvIOManager.cacheFile.calledTimes(), 1);
+ compare(oldFileHandler.kazvIOManager.cacheFile.lastArgs()[3], "");
+ compare(newFileHandler.kazvIOManager.cacheFile.calledTimes(), 1);
+ compare(newFileHandler.kazvIOManager.cacheFile.lastArgs()[3], newFileHandler.matrixSdk.token);
+
+ oldFileHandler.downloadFile("fileUrl");
+ compare(oldFileHandler.kazvIOManager.startNewDownloadJob.calledTimes(), 1);
+ compare(oldFileHandler.kazvIOManager.startNewDownloadJob.lastArgs()[4], "");
+ newFileHandler.downloadFile("fileUrl");
+ compare(newFileHandler.kazvIOManager.startNewDownloadJob.calledTimes(), 1);
+ compare(newFileHandler.kazvIOManager.startNewDownloadJob.lastArgs()[4], newFileHandler.matrixSdk.token);
+ }
+
+ function test_autoCache() {
+ compare(unencryptedFileHandler.kazvIOManager.cacheFile.calledTimes(), 1);
+ compare(encryptedFileHandler.kazvIOManager.cacheFile.calledTimes(), 1);
+ }
+
+ function test_encrypted() {
+ let downloadFunc = encryptedFileHandler.kazvIOManager.startNewDownloadJob;
+ encryptedFileHandler.downloadFile("fileUrl");
+ compare(downloadFunc.calledTimes(), 1);
+ compare(downloadFunc.lastArgs()[0], encryptedFileHandler.matrixSdk.mxcUriToHttpV1("mxc://some"));
+ compare(downloadFunc.lastArgs()[1], "fileUrl");
+ compare(downloadFunc.lastArgs()[3], "somesha256hash");
+ compare(downloadFunc.lastArgs()[4], encryptedFileHandler.matrixSdk.token);
+ compare(downloadFunc.lastArgs()[5], "somekey");
+ compare(downloadFunc.lastArgs()[6], "someiv");
+ }
+
+ function test_unencrypted() {
+ let downloadFunc = unencryptedFileHandler.kazvIOManager.startNewDownloadJob;
+ unencryptedFileHandler.downloadFile("fileUrl");
+ compare(downloadFunc.calledTimes(), 1);
+
+ let args = downloadFunc.lastArgs();
+ compare(args[0], unencryptedFileHandler.matrixSdk.mxcUriToHttpV1("mxc://some"));
+ compare(args[1], "fileUrl");
+ compare(args[3], "");
+ compare(args[4], unencryptedFileHandler.matrixSdk.token);
+ compare(args[5], "");
+ compare(args[6], "");
+ }
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Tue, Jul 1, 2:47 PM (7 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
241503
Default Alt Text
D217.1751406466.diff (20 KB)

Event Timeline