Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- name: Install dependencies
run: |
brew install flatbuffers
curl -O -L -s https://installer.id.ee/media/github/opensc_0.26.1.pkg
curl -O -L -s https://installer.id.ee/media/github/opensc_0.27.1-3.pkg
sudo installer -verboseR -pkg libdigidocpp-pkg/build/macos/libdigidocpp*.pkg -target /
sudo installer -verboseR -pkg opensc_*.pkg -target /
rm -rf libdigidocpp-pkg
Expand All @@ -41,12 +41,12 @@ jobs:
key: vcpkg-macOS-${{ hashFiles('prepare_osx_build_environment.sh') }}
- name: Build libraries
run: |
mkdir -p ${LIBS_PATH}/Qt-6.10.3-OpenSSL
mkdir -p ${LIBS_PATH}/Qt-6.11.2-OpenSSL
./prepare_osx_build_environment.sh -p ${LIBS_PATH}
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: 6.10.3
version: 6.11.2
arch: clang_64
cache: true
- name: Build
Expand Down Expand Up @@ -169,22 +169,27 @@ jobs:
Rename-Item "libdigidocpp*.msi" libdigidocpp.msi
$r = Start-Process msiexec -ArgumentList '/a', 'libdigidocpp.msi', '/qn', "TARGETDIR=$env:GITHUB_WORKSPACE\libs", '/l*v', 'install.log' -Wait -PassThru
if ($r.ExitCode -ne 0) { Get-Content install.log; exit $r.ExitCode }
- name: Cache vcpkg
- name: Cache vcpkg and WiX
uses: actions/cache@v5
with:
path: ${{ github.workspace }}/vcpkg_cache
key: vcpkg-${{ matrix.vcver }}-${{ matrix.platform }}-${{ hashFiles('client/libcdoc/vcpkg.json') }}
path: |
${{ github.workspace }}/vcpkg_cache
~/.dotnet/tools
~/.wix
key: vcpkg-${{ matrix.vcver }}-${{ matrix.platform }}-${{ hashFiles('client/libcdoc/vcpkg.json') }}-wix-7.0.0
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: 6.11.1
version: 6.11.2
arch: ${{ matrix.platform == 'arm64' && 'win64_msvc2022_arm64_cross_compiled' || 'win64_msvc2022_64' }}
cache: true
aqtsource: git+https://github.com/miurahr/aqtinstall.git
- name: Install WiX
run: |
dotnet tool install -g wix --version 6.0.2
wix extension -g add WixToolset.UI.wixext/6.0.2
dotnet tool install -g wix --version 7.0.0
wix eula accept wix7
wix extension -g add WixToolset.UI.wixext/7.0.0
wix extension -g add WixToolset.Util.wixext/7.0.0
- name: Build
shell: cmd
env:
Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.22)
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/common/CMakeLists.txt)
message(FATAL_ERROR "common submodule directory empty, did you 'git clone --recursive'?")
endif()
project(qdigidoc4 VERSION 4.11.1
project(qdigidoc4 VERSION 4.12.0
DESCRIPTION "DigiDoc4 application for digital signing and encryption"
HOMEPAGE_URL https://github.com/open-eid/DigiDoc4-Client
)
Expand Down Expand Up @@ -46,7 +46,7 @@ set(CPACK_RPM_PACKAGE_RELEASE_DIST ON)
include(CPack)
include(GNUInstallDirs)

find_package(libdigidocpp 4.5.0 REQUIRED HINTS /Library)
find_package(libdigidocpp 4.6.0 REQUIRED HINTS /Library)
message("-- Found libdigidocpp: ${libdigidocpp_DIR} (found version \"${libdigidocpp_VERSION}\")")
find_package(LDAP REQUIRED)
find_package(Qt6 6.2.0 REQUIRED COMPONENTS Core Widgets Network PrintSupport SvgWidgets LinguistTools)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
3. Configure

cmake -B build -S . \
-DCMAKE_PREFIX_PATH=~/cmake_builds/Qt-6.11.1-OpenSSL \
-DCMAKE_PREFIX_PATH=~/cmake_builds/Qt-6.11.2-OpenSSL \
-DOPENSSL_ROOT_DIR=~/cmake_build/OpenSSL \
-DLDAP_ROOT=~/cmake_build/OpenLDAP \
-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
Expand Down Expand Up @@ -95,7 +95,7 @@
3. Configure

cmake -G"NMAKE Makefiles" -B build -S . `
-DCMAKE_PREFIX_PATH=C:\Qt\6.11.1\msvc2022_64 `
-DCMAKE_PREFIX_PATH=C:\Qt\6.11.2\msvc2022_64 `
-DLibDigiDocpp_ROOT="C:\Program Files (x86)\libdigidocpp" `
-DVCPKG_MANIFEST_DIR=client/libcdoc

Expand Down
7 changes: 2 additions & 5 deletions client/CryptoDoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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>(libcdoc::CDocWriter::createWriter(d->version, d->fileName.toStdString(), &d->conf, &d->crypto, &d->network));
Expand Down Expand Up @@ -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);
Expand Down
1 change: 0 additions & 1 deletion client/CryptoDoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
61 changes: 19 additions & 42 deletions client/DigiDoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {};
Expand Down Expand Up @@ -431,30 +424,29 @@ bool DigiDoc::extend()
Common::applicationOs(),
Common::drivers().join(',')).toUtf8().constData());
qApp->waitForTSL(fileName());
ServiceConfirmation cb(parent);
QString current = m_fileName;
size_t extendCount = 0;
bool wrapped = false;
const auto signatures = b->signatures();
const bool timestampedContainer = std::any_of(signatures.cbegin(), signatures.cend(), [](const Signature *signature) {
return signature->profile() == "TimeStampToken";
});
if(std::unique_ptr<Container> extended = waitFor([&] {
return Container::extendContainerValidity(*b, signer, extendCount);
}))
{
wrapped = true;
const QString asics = QCoreApplication::translate("MainWindow", "Documents (%1)").arg(QLatin1String("*.asics *.scs"));
QFileInfo f(current);
QString name = f.absolutePath() + '/' + f.completeBaseName() + QStringLiteral(".asics");
QString filename = FileDialog::getSaveFileName(Application::mainWindow(), QCoreApplication::translate("MainWindow", "Save file"), name, asics);
if(filename.isEmpty())
return false;
auto original = std::exchange(b, std::move(extended));
auto originalParent = std::exchange(parentContainer, {});
if(!saveAs(filename))
{
b = std::move(original);
parentContainer = std::move(originalParent);
return false;
}
load(std::move(b), cb);
load(std::move(b));
m_fileName = filename;
Application::addRecent(m_fileName);
modified = false;
Expand All @@ -466,8 +458,8 @@ bool DigiDoc::extend()
}
if(!save())
return false;
load(std::move(b), cb);
if(extendCount < b->signatures().size())
load(std::move(b));
if(!timestampedContainer && extendCount < b->signatures().size())
{
WarningDialog::create(parent)
->withTitle(tr("Container validity extended"))
Expand Down Expand Up @@ -546,35 +538,19 @@ bool DigiDoc::isSupported() const
return b && b->mediaType() == "application/vnd.etsi.asic-e+zip" && !isCades();
}

void DigiDoc::load(std::unique_ptr<Container> &&doc, ServiceConfirmation &cb)
void DigiDoc::load(std::unique_ptr<Container> &&doc)
{
parentContainer.reset();
m_signatures.clear();
m_timestamps.clear();
documentModel()->clearTempFolder();
b = std::move(doc);
if(b && b->mediaType() == "application/vnd.etsi.asic-s+zip" &&
b->dataFiles().size() == 1 &&
b->signatures().size() == 1)
{
if(from(b->dataFiles().at(0)->fileName()).endsWith(QStringLiteral(".ddoc"), Qt::CaseInsensitive))
{
if(QString tmppath = documentModel()->saveTemp(0); !tmppath.isEmpty())
{
try {
parentContainer = std::exchange(b, Container::openPtr(to(tmppath), &cb));
} catch(const Exception &) {}
}
}
}
bool isTimeStamped = parentContainer && parentContainer->signatures().at(0)->trustedSigningTime().compare("2018-07-01T00:00:00Z") < 0;
for(const Signature *signature: b->signatures())
m_signatures.emplace_back(signature, this, isTimeStamped);
if(parentContainer)
{
for(const Signature *signature: parentContainer->signatures())
m_timestamps.emplace_back(signature, this);
}
const auto containerSignatures = b->signatures();
const bool isTimeStamped = std::any_of(containerSignatures.cbegin(), containerSignatures.cend(), [](const Signature *signature) {
return signature->profile() == "TimeStampToken" &&
signature->trustedSigningTime().compare("2018-07-01T00:00:00Z") < 0;
});
for(const Signature *signature: containerSignatures)
m_signatures.emplace_back(signature, this, signature->profile() != "TimeStampToken" && isTimeStamped);
setState(signatures().isEmpty() ? ContainerState::UnsignedSavedContainer : ContainerState::SignedContainer);
}

Expand Down Expand Up @@ -615,7 +591,7 @@ std::unique_ptr<DigiDoc> DigiDoc::open(const QString &file, QWidget *parent)
std::unique_ptr<DigiDoc> doc(new DigiDoc(parent));
WaitDialogHolder waitDialog(parent, tr("Opening"), false);
if(waitFor([&] {
doc->load(Container::openPtr(to(file), &cb), cb);
doc->load(Container::openPtr(to(file), &cb));
Application::addRecent(file);
doc->m_fileName = file;
return true;
Expand Down Expand Up @@ -710,8 +686,9 @@ bool DigiDoc::saveAs(const QString &filename)
{
try
{
auto keepAlive = FileDialog::keepAccessAlive(filename);
return waitFor([&] {
parentContainer ? parentContainer->save(to(filename)) : b->save(to(filename));
b->save(to(filename));
return true;
});
}
Expand Down
4 changes: 1 addition & 3 deletions client/DigiDoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -158,12 +157,11 @@ class DigiDoc: public QObject
QWidget *parentWidget() const;
explicit DigiDoc(QWidget *parent = {});
bool isError(bool failure, const QString &title, const QString &text) const;
void load(std::unique_ptr<digidoc::Container> &&doc, ServiceConfirmation &cb);
void load(std::unique_ptr<digidoc::Container> &&doc);
static void setLastError(const QString &title, const digidoc::Exception &e);
void setState(ContainerState s);

std::unique_ptr<digidoc::Container> b;
std::unique_ptr<digidoc::Container> parentContainer;
std::unique_ptr<DocumentModel> m_documentModel;

ContainerState containerState = ria::qdigidoc4::UnsignedContainer;
Expand Down
1 change: 0 additions & 1 deletion client/DocumentModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
19 changes: 19 additions & 0 deletions client/dialogs/FileDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,25 @@ bool FileDialog::fileIsWritable( const QString &filename )
return result;
}

std::unique_ptr<QFile> 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<QFile>(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))
Expand Down
4 changes: 4 additions & 0 deletions client/dialogs/FileDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@

#pragma once

#include <QtCore/QFile>
#include <QtWidgets/QFileDialog>

#include <memory>

class FileDialog : public QFileDialog
{
Q_OBJECT
Expand All @@ -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<QFile> 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);
Expand Down
4 changes: 2 additions & 2 deletions prepare_osx_build_environment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
set -e

######### Versions of libraries/frameworks to be compiled
QT_VER="6.10.3"
QT_VER="6.11.2"
OPENSSL_VER="3.5.7"
OPENLDAP_VER="2.6.13"
OPENLDAP_VER="2.6.14"
REBUILD=false
BUILD_PATH=~/cmake_builds
: ${MACOSX_DEPLOYMENT_TARGET:="14.0"}
Expand Down
3 changes: 1 addition & 2 deletions qdigidoc4.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
<?endif?>

<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui"
xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<Package Name="DigiDoc4 Client $(sys.BUILDARCH)" UpgradeCode="67932072-aa9e-45e3-b697-d825349f273d"
Language="1033" Version="!(bind.FileVersion.qdigidoc4.exe)" Manufacturer="RIA" Scope="perMachine">
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
Expand Down
Loading