Page MenuHomePhorge

No OneTemporary

Size
20 KB
Referenced Files
None
Subscribers
None
diff --git a/src/contents/ui/MediaFileMenu.qml b/src/contents/ui/MediaFileMenu.qml
index f85897e..fb3767a 100644
--- a/src/contents/ui/MediaFileMenu.qml
+++ b/src/contents/ui/MediaFileMenu.qml
@@ -1,51 +1,57 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2022-2023 nannanko <nannanko@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import Qt.labs.platform 1.1 as Platform
import org.kde.kirigami 2.13 as Kirigami
import '.' as Types
QtObject {
id: mediaFileMenu
required property var serverUrl
required property var jobId
required property var fileName
+ /**
+ * key and iv for encryption. If encryption is not enabled, the value is ""
+ **/
+ required property var key
+ required property var iv
signal startDownload
property var viewAction: Kirigami.Action {
text: l10n.get('media-file-menu-option-view')
// shortcut: StandardKey.Open
// TODO: open with default program
}
property var saveAction: Kirigami.Action {
text: l10n.get('media-file-menu-option-save-as')
// Can't think of a suitable shortcut key
// shortcut: StandardKey.Save
onTriggered: {
fileDialog.open()
}
}
property var optionMenu: [viewAction, saveAction]
property var fileDialog: Platform.FileDialog {
fileMode: Platform.FileDialog.SaveFile
folder: Platform.StandardPaths.writableLocation(Platform.StandardPaths.DownloadLocation)
// This is not work
// currentFile: fileName
onAccepted: {
- kazvIOManager.startNewDownloadJob(mediaFileMenu.serverUrl, file, mediaFileMenu.jobId)
+ kazvIOManager.startNewDownloadJob(mediaFileMenu.serverUrl, file, mediaFileMenu.jobId,
+ mediaFileMenu.key, mediaFileMenu.iv)
startDownload()
}
}
}
diff --git a/src/contents/ui/event-types/Image.qml b/src/contents/ui/event-types/Image.qml
index e11cebe..932dfac 100644
--- a/src/contents/ui/event-types/Image.qml
+++ b/src/contents/ui/event-types/Image.qml
@@ -1,69 +1,73 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import moe.kazv.mxc.kazv 0.0 as MK
import org.kde.kirigami 2.13 as Kirigami
import '.' as Types
import '..' as Kazv
Simple {
id: upper
property var gender: 'neutral'
property var body: event.content.body
+ property var key: event.content.file.key.k || ""
+ property var iv: event.content.file.key.iv || ""
property var thumbnailMxcUri: event.encrypted ? event.content.info.thumbnail_file.url : event.content.info.thumbnail_url
property var imageMxcUri: event.encrypted ? event.content.file.url : event.content.url
property var kazvIOJob: thumbnailUri ? kazvIOManager.getDownloadJob(event.eventId) : (imageUri ? kazvIOManager.getDownloadJob(event.eventId) : null)
property var thumbnailUri: thumbnailMxcUri ? kazvIOManager.cacheFile(matrixSdk.mxcUriToHttp(thumbnailMxcUri), event.eventId) : undefined
property var imageUri: thumbnailMxcUri ? matrixSdk.mxcUriToHttp(imageMxcUri) : kazvIOManager.cacheFile(matrixSdk.mxcUriToHttp(imageMxcUri), event.eventId)
property var imageInfo: thumbnailUri ? (event.content.info.thumbnail_info || {}) : (event.content.info || {})
property var imageWidth: image.implicitWidth || imageInfo.w || 1 // lest we divide by 0
property var imageHeight: image.implicitHeight || imageInfo.h || 1
property var innerContentWidth: upper.contentMaxWidth - bubble.bubbleSpacing
Types.MediaBubble {
id: bubble
eventId: event.eventId
serverUrl: upper.imageUri
fileName: body
+ key: upper.key
+ iv: upper.iv
property var label: Label {
// Layout.fillWidth: false
// Layout.maximumWidth: innerContentWidth
width: contentWidth > innerContentWidth ? innerContentWidth : contentWidth
wrapMode: Text.Wrap
text: l10n.get('event-message-image-sent', { gender, body })
}
property var image: Image {
id: image
source: kazvIOJob ? null : (thumbnailMxcUri ? thumbnailUri : imageUri)
// Layout.maximumWidth: innerContentWidth
// Layout.preferredWidth: imageWidth
// Layout.preferredHeight: imageHeight / imageWidth * width
width: sourceSize.width > innerContentWidth ? innerContentWidth : source.width
fillMode: Image.PreserveAspectFit
}
Column {
id: layout
width: childrenRect.width > innerContentWidth ? innerContentWidth : childrenRect.width
data: [
bubble.mediaFileMenu,
bubble.label,
bubble.image,
bubble.progressBar
]
}
}
}
diff --git a/src/contents/ui/event-types/MediaBubble.qml b/src/contents/ui/event-types/MediaBubble.qml
index c2bd31f..9ac45f9 100644
--- a/src/contents/ui/event-types/MediaBubble.qml
+++ b/src/contents/ui/event-types/MediaBubble.qml
@@ -1,36 +1,40 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2022-2023 nannanko <nannanko@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import moe.kazv.mxc.kazv 0.0 as MK
import '..' as Kazv
Kazv.Bubble {
id: bubble
required property var eventId
required property var serverUrl
required property var fileName
+ required property var key
+ required property var iv
property var mediaFileMenu: Kazv.MediaFileMenu {
serverUrl: bubble.serverUrl
jobId: bubble.eventId
fileName: bubble.fileName
+ key: bubble.key
+ iv: bubble.iv
onStartDownload: {
progressBar.kazvIOJob = kazvIOManager.getDownloadJob(eventId)
}
}
menuContent: mediaFileMenu.optionMenu
property var progressBar: Kazv.KazvIOMenu {
id: progressBar
jobId: bubble.eventId
isUpload: false
kazvIOJob: kazvIOManager.getDownloadJob(jobId)
width: parent.width
}
}
diff --git a/src/kazv-io-job.cpp b/src/kazv-io-job.cpp
index 0e5d7b8..8269133 100644
--- a/src/kazv-io-job.cpp
+++ b/src/kazv-io-job.cpp
@@ -1,306 +1,307 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2022-2023 nannanko <nannanko@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include "kazv-io-job.hpp"
#include "matrix-room.hpp"
#include <QObject>
#include <QPointer>
#include <QSharedPointer>
#include <QSaveFile>
#include <QFile>
#include <QString>
#include <QMimeDatabase>
#include <KJob>
#include <KIO/TransferJob>
#include <nlohmann/json.hpp>
#include <optional>
#include <string>
struct KazvIOBaseJobPrivate
{
QPointer<KIO::TransferJob> job;
std::optional<KazvIOBaseJob::ErrorCode> result = std::nullopt;
std::optional<Kazv::AES256CTRDesc> aes = std::nullopt;
};
KazvIOBaseJob::KazvIOBaseJob(std::optional<Kazv::AES256CTRDesc> aes, QObject *parent)
: QObject(parent)
, m_d(new KazvIOBaseJobPrivate)
{
m_d->aes = aes;
connect(this, &KazvIOBaseJob::jobChanged, this, &KazvIOBaseJob::connectJob);
}
KazvIOBaseJob::~KazvIOBaseJob() = default;
float KazvIOBaseJob::progress() {
if (m_d->job.isNull()) {
return 0;
}
return static_cast<float>(m_d->job->percent()) / 100;
}
void KazvIOBaseJob::suspend()
{
if (m_d->job.isNull()) {
emitResult(KazvError);
return;
}
m_d->job->suspend();
}
void KazvIOBaseJob::resume()
{
if (m_d->job.isNull()) {
emitResult(KazvError);
return;
}
m_d->job->resume();
}
void KazvIOBaseJob::cancel()
{
if (m_d->job.isNull()) {
emitResult(KazvError);
return;
}
emitResult(UserCancel);
m_d->job->kill();
}
bool KazvIOBaseJob::isSuspended()
{
if (m_d->job.isNull()) {
return false;
}
return m_d->job->isSuspended();
}
bool KazvIOBaseJob::isResulted()
{
return m_d->result.has_value();
}
KazvIOBaseJob::ErrorCode KazvIOBaseJob::error()
{
if (m_d->result.has_value()) {
return m_d->result.value();
}
// Shouldn't call this function before result are emited
return NoError;
}
void KazvIOBaseJob::connectJob()
{
connect(m_d->job, &KJob::result, this, [this](KJob *job) {
if (job->error()) {
emitResult(KIOError);
}
});
connect(m_d->job, &KJob::percentChanged, this,
[this](KJob * /* job */, unsigned long /* percent */) { Q_EMIT progressChanged(); });
}
QPointer<KIO::TransferJob> KazvIOBaseJob::job()
{
return m_d->job;
}
void KazvIOBaseJob::setJob(QPointer<KIO::TransferJob> job)
{
m_d->job = job;
Q_EMIT jobChanged();
}
void KazvIOBaseJob::emitResult(ErrorCode ec, QString data)
{
if (m_d->result.has_value()) {
return;
}
m_d->result = ec;
Q_EMIT result(m_d->result.value(), data);
}
std::optional<Kazv::AES256CTRDesc> KazvIOBaseJob::aes()
{
return m_d->aes;
}
void KazvIOBaseJob::setAes(Kazv::AES256CTRDesc aes)
{
m_d->aes = aes;
}
struct KazvIODownloadJobPrivate
{
QSharedPointer<QSaveFile> file;
};
KazvIODownloadJob::KazvIODownloadJob(std::optional<Kazv::AES256CTRDesc> aes, QObject *parent)
: KazvIOBaseJob(aes, parent)
, m_d(new KazvIODownloadJobPrivate)
{
}
KazvIODownloadJob::KazvIODownloadJob(QString fileName, QUrl serverUrl,
std::optional<Kazv::AES256CTRDesc> aes, QObject *parent)
: KazvIOBaseJob(aes, parent)
, m_d(new KazvIODownloadJobPrivate)
{
if (setFile(fileName)) {
setJob(KIO::get(serverUrl));
}
}
KazvIODownloadJob::~KazvIODownloadJob() = default;
QString KazvIODownloadJob::fileName() {
if (m_d->file.isNull()) {
return QStringLiteral("");
}
return m_d->file->fileName();
}
void KazvIODownloadJob::connectJob()
{
KazvIOBaseJob::connectJob();
connect(this->job(), &KIO::TransferJob::data, this, &KazvIODownloadJob::writeFile);
connect(this->job(), &KJob::result, this, &KazvIODownloadJob::closeFile);
}
bool KazvIODownloadJob::setFile(QString fileName)
{
m_d->file.reset(new QSaveFile(fileName));
if (m_d->file->open(QIODevice::WriteOnly)) {
return true;
}
emitResult(KazvIOBaseJob::OpenFileError);
return false;
}
void KazvIODownloadJob::writeFile(KJob *job, const QByteArray &data)
{
if (m_d->file.isNull() || !m_d->file->isOpen()) {
emitResult(KazvError);
job->kill();
return;
}
QByteArray plainData = data;
if (this->aes() != std::nullopt) {
- auto [aes, plainData] = this->aes().value().process(data.toStdString());
- this->setAes(aes);
+ // auto result = this->aes().value().process(data.toStdString());
+ // plainData = result.second
+ // this->setAes(result.first);
}
auto len = m_d->file->write(plainData);
if (len == -1) {
KazvIOBaseJob::emitResult(WriteFileError);
job->kill();
}
}
void KazvIODownloadJob::closeFile(KJob *job)
{
if (m_d->file.isNull()) {
emitResult(KazvError);
return;
}
if (job->error()) {
m_d->file->cancelWriting();
return;
}
m_d->file->commit();
emitResult(NoError);
}
struct KazvIOUploadJobPrivate
{
QSharedPointer<QFile> file;
QString response;
QSharedPointer<MatrixRoom> room;
QString mimeType;
QString mxcUri;
};
KazvIOUploadJob::KazvIOUploadJob(std::optional<Kazv::AES256CTRDesc> aes, QObject *parent)
: KazvIOBaseJob(aes, parent)
, m_d(new KazvIOUploadJobPrivate)
{
}
KazvIOUploadJob::KazvIOUploadJob(const QString fileName, const QUrl serverUrl,
MatrixRoomList *roomList, const QString &roomId, const QString token,
std::optional<Kazv::AES256CTRDesc> aes, QObject *parent)
: KazvIOBaseJob(aes, parent)
, m_d(new KazvIOUploadJobPrivate)
{
if (setFile(fileName)) {
auto kazvUploadJob = Kazv::Api::UploadContentJob(serverUrl.toString().toStdString(),
token.toStdString(), Kazv::FileDesc(std::string()));
auto job = KIO::http_post(QString(kazvUploadJob.url().data()), m_d->file.data());
job->addMetaData("customHTTPHeader", QStringLiteral("Authorization: ")
.append(kazvUploadJob.requestHeader()->at("Authorization").data()));
job->addMetaData("PropagateHttpHeader", "true");
setJob(job);
if (roomList) {
m_d->room.reset(roomList->room(roomId));
}
}
}
KazvIOUploadJob::~KazvIOUploadJob() = default;
QString KazvIOUploadJob::fileName()
{
if (m_d->file.isNull()) {
return QStringLiteral("");
}
return m_d->file->fileName();
}
void KazvIOUploadJob::connectJob()
{
KazvIOBaseJob::connectJob();
connect(this->job(), &KIO::TransferJob::data, this,
[this](KJob * /* job */, const QByteArray &data) { m_d->response.append(data); });
connect(this->job(), &KJob::result, this, &KazvIOUploadJob::handleResult);
connect(this->job(), &KIO::TransferJob::mimeTypeFound, this,
[this](KJob * /* job */, const QString &mimeType) { m_d->mimeType = mimeType; });
}
void KazvIOUploadJob::handleResult(KJob *job)
{
if (m_d->file.isNull()) {
emitResult(KazvError);
return;
}
m_d->file->close();
if (job->error()) {
return;
}
auto j = nlohmann::json::parse(m_d->response.toStdString());
auto mxcUri = QString::fromStdString(j["content_uri"].get<std::string>());
auto shouldSendMessage = !!m_d->room;
if (shouldSendMessage) {
m_d->room->sendMediaFileMessage(QUrl::fromLocalFile(m_d->file->fileName()).fileName(),
QMimeDatabase().mimeTypeForData(m_d->file.data()).name(), m_d->file->size(), mxcUri);
}
KazvIOBaseJob::emitResult(NoError, mxcUri);
}
bool KazvIOUploadJob::setFile(const QString fileName)
{
m_d->file.reset(new QFile(fileName));
if (m_d->file->open(QIODevice::ReadOnly)) {
return true;
}
emitResult(KazvIOBaseJob::OpenFileError);
return false;
}
diff --git a/src/kazv-io-manager.cpp b/src/kazv-io-manager.cpp
index 788fd59..a857851 100644
--- a/src/kazv-io-manager.cpp
+++ b/src/kazv-io-manager.cpp
@@ -1,125 +1,132 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2022-2023 nannanko <nannanko@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include "kazv-io-manager.hpp"
#include "kazv-io-job.hpp"
#include "matrix-room.hpp"
#include "upload-job-model.hpp"
#include <api/csapi/content-repo.hpp>
#include <file-desc.hpp>
#include <QObject>
#include <QIODevice>
#include <QFile>
#include <QSaveFile>
#include <QPointer>
#include <QSharedPointer>
#include <QUrl>
#include <QMap>
#include <QMultiMap>
#include <QTemporaryDir>
#include <string>
static const QString ROOMLESS = QStringLiteral("not-a-room");
struct KazvIOManagerPrivate
{
QTemporaryDir cacheDir;
QMap<QString, QSharedPointer<KazvIODownloadJob>> downloadJobs;
QMap<QString, QSharedPointer<UploadJobModel>> uploadJobs;
};
KazvIOManager::KazvIOManager(QObject *parent)
: QObject(parent)
, m_d(new KazvIOManagerPrivate)
{
}
KazvIOManager::~KazvIOManager() = default;
QUrl KazvIOManager::cacheFile(const QUrl &fileUrl, const QString &id)
{
const QString filePath = m_d->cacheDir.filePath(id);
QDir dir(m_d->cacheDir.path());
if (!dir.exists(id)) {
auto job = this->startNewDownloadJob(fileUrl, QUrl::fromLocalFile(filePath), id);
connect(job, &KazvIOBaseJob::result, this,
[this, id](auto ec) {
if (ec == KazvIOBaseJob::NoError) {
m_d->downloadJobs.remove(id);
}
});
}
return QUrl::fromLocalFile(filePath);
}
-KazvIOBaseJob *KazvIOManager::startNewDownloadJob(const QUrl &serverUrl, const QUrl &localFileUrl, const QString &jobId)
+KazvIOBaseJob *KazvIOManager::startNewDownloadJob(const QUrl &serverUrl, const QUrl &localFileUrl,
+ const QString &jobId, const QString key, const QString iv)
{
- auto downloadJob = QSharedPointer<KazvIODownloadJob>(
- new KazvIODownloadJob(localFileUrl.toLocalFile(), serverUrl, std::nullopt, this));
+ auto downloadJob = QSharedPointer<KazvIODownloadJob>{};
+ if (key == "") {
+ downloadJob.reset(new KazvIODownloadJob{localFileUrl.toLocalFile(), serverUrl, std::nullopt, this});
+ } else {
+ auto aes = Kazv::AES256CTRDesc{key.toStdString(), iv.toStdString()};
+ downloadJob.reset(new KazvIODownloadJob{localFileUrl.toLocalFile(), serverUrl, aes, this});
+ }
+
m_d->downloadJobs[jobId] = downloadJob;
return downloadJob.data();
}
KazvIOBaseJob *KazvIOManager::startNewUploadJob(const QUrl &serverUrl, const QUrl &localFileUrl, const QString &token, const QString &roomId, MatrixRoomList *roomList)
{
auto uploadJob = QPointer<KazvIOUploadJob>(
new KazvIOUploadJob(localFileUrl.toLocalFile(), serverUrl, roomList, roomId, token, std::nullopt,this));
if (!m_d->uploadJobs.contains(roomId) || m_d->uploadJobs[roomId].isNull()) {
m_d->uploadJobs[roomId].reset(new UploadJobModel(this));
}
m_d->uploadJobs[roomId]->addJob(uploadJob);
return uploadJob.data();
}
KazvIOBaseJob *KazvIOManager::startNewRoomlessUploadJob(const QUrl &serverUrl, const QUrl &localFileUrl, const QString &token)
{
return startNewUploadJob(serverUrl, localFileUrl, token, ROOMLESS, 0);
}
KazvIOBaseJob *KazvIOManager::getDownloadJob(const QString &jobId)
{
auto targetJob = m_d->downloadJobs.find(jobId);
if (targetJob == m_d->downloadJobs.end()) {
return nullptr;
}
return targetJob.value().data();
}
void KazvIOManager::deleteDownloadJob(const QString &jobId)
{
m_d->downloadJobs.remove(jobId);
}
UploadJobModel *KazvIOManager::getUploadJobs(const QString &roomId)
{
if (!m_d->uploadJobs.contains(roomId) || m_d->uploadJobs[roomId].isNull()) {
m_d->uploadJobs[roomId].reset(new UploadJobModel(this));
}
return m_d->uploadJobs[roomId].data();
}
void KazvIOManager::deleteUploadJob(const QString &roomId, KazvIOBaseJob *job)
{
m_d->uploadJobs[roomId]->removeJob(QPointer<KazvIOUploadJob>(qobject_cast<KazvIOUploadJob*>(job)));
}
void KazvIOManager::deleteRoomlessUploadJob(KazvIOBaseJob *job)
{
deleteUploadJob(ROOMLESS, job);
}
void KazvIOManager::deleteModelIfEmpty(const QString &roomId)
{
if (!m_d->uploadJobs.contains(roomId)) {
return;
}
if (m_d->uploadJobs[roomId]->rowCount() == 0) {
m_d->uploadJobs.remove(roomId);
}
}
diff --git a/src/kazv-io-manager.hpp b/src/kazv-io-manager.hpp
index 52ec387..3ebe07e 100644
--- a/src/kazv-io-manager.hpp
+++ b/src/kazv-io-manager.hpp
@@ -1,49 +1,50 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2022-2023 nannanko <nannanko@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include "kazv-io-job.hpp"
#include "matrix-room.hpp"
#include "upload-job-model.hpp"
#include "matrix-room-list.hpp"
#include <QObject>
#include <QtQml>
#include <QUrl>
#include <QString>
#include <KIO/TransferJob>
#include <QPointer>
#include <memory>
struct KazvIOManagerPrivate;
class KazvIOManager : public QObject
{
Q_OBJECT
QML_ELEMENT
std::unique_ptr<KazvIOManagerPrivate> m_d;
public:
explicit KazvIOManager(QObject *parent = 0);
~KazvIOManager() override;
/**
* Cache the file specified by fileUrl into a temporary folder
* Returns the local url of the cached file.
*/
Q_INVOKABLE QUrl cacheFile(const QUrl &fileUrl, const QString &id);
- Q_INVOKABLE KazvIOBaseJob *startNewDownloadJob(const QUrl &serverUrl, const QUrl &localFileName, const QString &jobId);
+ Q_INVOKABLE KazvIOBaseJob *startNewDownloadJob(const QUrl &serverUrl, const QUrl &localFileName,
+ const QString &jobId, const QString key = QStringLiteral(""), const QString iv = QStringLiteral(""));
Q_INVOKABLE KazvIOBaseJob *startNewUploadJob(const QUrl &serverUrl, const QUrl &localFileName, const QString &token, const QString &roomId, MatrixRoomList *roomList);
Q_INVOKABLE KazvIOBaseJob *startNewRoomlessUploadJob(const QUrl &serverUrl, const QUrl &localFileName, const QString &token);
Q_INVOKABLE KazvIOBaseJob *getDownloadJob(const QString &jobId);
Q_INVOKABLE void deleteDownloadJob(const QString &jobId);
Q_INVOKABLE UploadJobModel *getUploadJobs(const QString &roomId);
Q_INVOKABLE void deleteUploadJob(const QString &roomId, KazvIOBaseJob *job);
Q_INVOKABLE void deleteRoomlessUploadJob(KazvIOBaseJob *job);
Q_INVOKABLE void deleteModelIfEmpty(const QString &roomId);
};

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 1:25 PM (21 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55198
Default Alt Text
(20 KB)

Event Timeline