diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index a83b10d91..2ec479718 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -171,11 +171,6 @@ quint64 CDocumentModel::fileSize(int row) const return d->files.at(row).size; } -QString CDocumentModel::mime(int row) const -{ - return FileDialog::normalized(QString::fromStdString(d->files.at(row).mime)); -} - void CDocumentModel::open(int row) { if(!d->isEncrypted()) @@ -414,6 +409,7 @@ bool CryptoDoc::encrypt(const QString &filename, const QString& label, const QBy return false; } QString writer_last_error; + auto keepAlive = FileDialog::keepAccessAlive(d->fileName); libcdoc::result_t result = waitFor([&] -> libcdoc::result_t { qCDebug(CRYPTO) << "Encrypt" << d->fileName; auto writer = std::unique_ptr(libcdoc::CDocWriter::createWriter(d->version, d->fileName.toStdString(), &d->conf, &d->crypto, &d->network)); @@ -524,6 +520,7 @@ bool CryptoDoc::saveCopy(const QString &filename) QFileInfo dst(filename); if(src == dst) return true; + auto keepAlive = FileDialog::keepAccessAlive(filename); std::error_code ec; std::filesystem::copy_file(src.filesystemFilePath(), dst.filesystemFilePath(), std::filesystem::copy_options::overwrite_existing, ec); diff --git a/client/CryptoDoc.h b/client/CryptoDoc.h index 3a0c2da71..be9ca121a 100644 --- a/client/CryptoDoc.h +++ b/client/CryptoDoc.h @@ -89,7 +89,6 @@ class CDocumentModel final: public DocumentModel bool addFile(const QString &file, const QString &mime = QStringLiteral("application/octet-stream")) final; QString data(int row) const final; quint64 fileSize(int row) const final; - QString mime(int row) const final; void open(int row) final; bool removeRow(int row) final; int rowCount() const final; diff --git a/client/DigiDoc.cpp b/client/DigiDoc.cpp index 8409b55b4..329894dc8 100644 --- a/client/DigiDoc.cpp +++ b/client/DigiDoc.cpp @@ -333,14 +333,6 @@ quint64 SDocumentModel::fileSize(int row) const return doc->b->dataFiles().at(size_t(row))->fileSize(); } -QString SDocumentModel::mime(int row) const -{ - if(row >= rowCount()) - return {}; - - return from(doc->b->dataFiles().at(size_t(row))->mediaType()); -} - bool SDocumentModel::removeRow(int row) { if(!doc->b) @@ -368,6 +360,7 @@ QString SDocumentModel::save(int row, const QString &path) const return {}; if(QFileInfo::exists(path)) return path; + auto keepAlive = FileDialog::keepAccessAlive(path); doc->b->dataFiles().at(size_t(row))->saveAs(path.toStdString()); if(!QFileInfo::exists(path)) return {}; @@ -710,6 +703,7 @@ bool DigiDoc::saveAs(const QString &filename) { try { + auto keepAlive = FileDialog::keepAccessAlive(filename); return waitFor([&] { parentContainer ? parentContainer->save(to(filename)) : b->save(to(filename)); return true; diff --git a/client/DigiDoc.h b/client/DigiDoc.h index 739deede2..0988e01da 100644 --- a/client/DigiDoc.h +++ b/client/DigiDoc.h @@ -95,7 +95,6 @@ class SDocumentModel final: public DocumentModel bool addFile(const QString &file, const QString &mime = QStringLiteral("application/octet-stream")) final; QString data(int row) const final; quint64 fileSize(int row) const final; - QString mime(int row) const final; bool removeRow(int row) final; int rowCount() const final; QString save(int row, const QString &path) const final; diff --git a/client/DocumentModel.h b/client/DocumentModel.h index 3dc2ae2ce..f17fef7c4 100644 --- a/client/DocumentModel.h +++ b/client/DocumentModel.h @@ -35,7 +35,6 @@ class DocumentModel: public QObject virtual void copyModel(DocumentModel *model); virtual QString data(int row) const = 0; virtual quint64 fileSize(int row) const = 0; - virtual QString mime(int row) const = 0; virtual void open(int row); virtual bool removeRow(int row) = 0; virtual int rowCount() const = 0; diff --git a/client/dialogs/FileDialog.cpp b/client/dialogs/FileDialog.cpp index f47151c36..5f4d11a3c 100644 --- a/client/dialogs/FileDialog.cpp +++ b/client/dialogs/FileDialog.cpp @@ -104,6 +104,25 @@ bool FileDialog::fileIsWritable( const QString &filename ) return result; } +std::unique_ptr FileDialog::keepAccessAlive(const QString &path) +{ +#ifdef Q_OS_MACOS + // Qt 6.11's macOS sandbox file engine only keeps a user-selected path's + // security-scoped access active while some QFile instance for that exact + // path exists (not merely open). Code that writes to disk without going + // through QFile (libdigidocpp, libcdoc, std::filesystem) bypasses that + // entirely, so callers doing raw I/O to a save-panel-selected path must + // keep the object this returns alive for the duration of that write. + auto file = std::make_unique(path); + bool opened = file->open(QFile::WriteOnly|QFile::Append); + Q_UNUSED(opened) + return file; +#else + Q_UNUSED(path) + return {}; +#endif +} + bool FileDialog::isSignedPDF(const QString &path) { if(!path.endsWith(QLatin1String("pdf"), Qt::CaseInsensitive)) diff --git a/client/dialogs/FileDialog.h b/client/dialogs/FileDialog.h index cb87355ff..28e0dcf25 100644 --- a/client/dialogs/FileDialog.h +++ b/client/dialogs/FileDialog.h @@ -19,8 +19,11 @@ #pragma once +#include #include +#include + class FileDialog : public QFileDialog { Q_OBJECT @@ -36,6 +39,7 @@ class FileDialog : public QFileDialog static QString createNewFileName(const QString &file, bool signature, QWidget *parent); static FileType detect(const QString &filename); static bool fileIsWritable( const QString &filename ); + static std::unique_ptr keepAccessAlive(const QString &path); static bool isSignedPDF(const QString &path); static void setFileZone(const QString &target, const QString &source); static void setReadOnly(const QString &path, bool readonly = true);