qt: Allow appending a detached signature to an existing file
* lang/qt/src/qgpgmesignjob.cpp (sign_to_filename): Add argument "appendSignature". Append new detached signature to an existing file if requested. * lang/qt/src/signjob.cpp, lang/qt/src/signjob.h (class SignJob): Add member functions setAppendSignature, appendSignatureEnabled. * lang/qt/src/signjob_p.h (struct SignJobPrivate): Add member m_appendSignature. * lang/qt/tests/run-signjob.cpp (struct CommandLineOptions): Add members signingFlags, appendSignature. Initialize armor. (parseCommandLine): Add command line options --detach-sign and --append. (main): Do not exit if output file exists and append is enabled. Pass new options to the job. -- This change simplifies cross-signing a document by appending additional detached signatures to a file with already existing detached signatures. GnuPG-bug-id: 6867
This commit is contained in:
parent
09827ffc77
commit
7d5df0bf0d
4
NEWS
4
NEWS
@ -24,6 +24,8 @@ Noteworthy changes in version 1.24.0 (unrelease)
|
|||||||
|
|
||||||
* qt: Allow specifying import options when importing keys. [T7152]
|
* qt: Allow specifying import options when importing keys. [T7152]
|
||||||
|
|
||||||
|
* qt: Allow appending a detached signature to an existing file. [T6867]
|
||||||
|
|
||||||
* Interface changes relative to the 1.23.2 release:
|
* Interface changes relative to the 1.23.2 release:
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
GPGME_ENCRYPT_FILE NEW.
|
GPGME_ENCRYPT_FILE NEW.
|
||||||
@ -67,6 +69,8 @@ Noteworthy changes in version 1.24.0 (unrelease)
|
|||||||
qt: SignJob::outputFile NEW.
|
qt: SignJob::outputFile NEW.
|
||||||
qt: SignJob::setSigningFlags NEW.
|
qt: SignJob::setSigningFlags NEW.
|
||||||
qt: SignJob::signingFlags NEW.
|
qt: SignJob::signingFlags NEW.
|
||||||
|
qt: SignJob::setAppendSignature NEW.
|
||||||
|
qt: SignJob::appendSignatureEnabled NEW.
|
||||||
qt: VerifyDetachedJob::setSignatureFile NEW.
|
qt: VerifyDetachedJob::setSignatureFile NEW.
|
||||||
qt: VerifyDetachedJob::signatureFile NEW.
|
qt: VerifyDetachedJob::signatureFile NEW.
|
||||||
qt: VerifyDetachedJob::setSignedFile NEW.
|
qt: VerifyDetachedJob::setSignedFile NEW.
|
||||||
|
@ -170,7 +170,8 @@ static QGpgMESignJob::result_type sign_to_filename(Context *ctx,
|
|||||||
const std::vector<Key> &signers,
|
const std::vector<Key> &signers,
|
||||||
const QString &inputFilePath,
|
const QString &inputFilePath,
|
||||||
const QString &outputFilePath,
|
const QString &outputFilePath,
|
||||||
SignatureMode flags)
|
SignatureMode flags,
|
||||||
|
bool appendSignature)
|
||||||
{
|
{
|
||||||
Data indata;
|
Data indata;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
@ -206,13 +207,42 @@ static QGpgMESignJob::result_type sign_to_filename(Context *ctx,
|
|||||||
flags = static_cast<SignatureMode>(flags | SignFile);
|
flags = static_cast<SignatureMode>(flags | SignFile);
|
||||||
const auto signingResult = ctx->sign(indata, outdata, flags);
|
const auto signingResult = ctx->sign(indata, outdata, flags);
|
||||||
|
|
||||||
if (!signingResult.error().code()) {
|
|
||||||
// the operation succeeded -> save the result under the requested file name
|
|
||||||
partFileGuard.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
Error ae;
|
Error ae;
|
||||||
const QString log = _detail::audit_log_as_html(ctx, ae);
|
const QString log = _detail::audit_log_as_html(ctx, ae);
|
||||||
|
|
||||||
|
if (!signingResult.error().code()) {
|
||||||
|
// the operation succeeded
|
||||||
|
const bool appendSignatureToExistingFile = appendSignature && (flags & Detached) && QFile::exists(outputFilePath);
|
||||||
|
if (appendSignatureToExistingFile) {
|
||||||
|
// append the result to the existing file
|
||||||
|
QFile newSignatureFile{partFileGuard.tempFileName()};
|
||||||
|
if (!newSignatureFile.open(QIODevice::ReadOnly)) {
|
||||||
|
qCDebug(QGPGME_LOG) << "Failed to open detached signature file" << newSignatureFile.fileName() << "(" << newSignatureFile.errorString() << ")";
|
||||||
|
return std::make_tuple(SigningResult{Error::fromCode(GPG_ERR_GENERAL)}, QByteArray{}, log, ae);
|
||||||
|
}
|
||||||
|
const QByteArray newSigData = newSignatureFile.readAll();
|
||||||
|
if (newSigData.isEmpty()) {
|
||||||
|
qCDebug(QGPGME_LOG) << "Failed to read detached signature from file" << newSignatureFile.fileName() << "(" << newSignatureFile.errorString() << ")";
|
||||||
|
return std::make_tuple(SigningResult{Error::fromCode(GPG_ERR_GENERAL)}, QByteArray{}, log, ae);
|
||||||
|
}
|
||||||
|
newSignatureFile.close();
|
||||||
|
|
||||||
|
QFile existingSignatureFile{outputFilePath};
|
||||||
|
if (!existingSignatureFile.open(QIODevice::WriteOnly | QIODevice::Append)) {
|
||||||
|
qCDebug(QGPGME_LOG) << "Failed to open existing detached signature file for appending" << existingSignatureFile.fileName() << "(" << existingSignatureFile.errorString() << ")";
|
||||||
|
return std::make_tuple(SigningResult{Error::fromCode(GPG_ERR_GENERAL)}, QByteArray{}, log, ae);
|
||||||
|
}
|
||||||
|
const auto bytesWritten = existingSignatureFile.write(newSigData);
|
||||||
|
if (bytesWritten != newSigData.size()) {
|
||||||
|
qCDebug(QGPGME_LOG) << "Failed to write new signature to existing detached signature file" << existingSignatureFile.fileName() << "(" << existingSignatureFile.errorString() << ")";
|
||||||
|
return std::make_tuple(SigningResult{Error::fromCode(GPG_ERR_GENERAL)}, QByteArray{}, log, ae);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// save the result under the requested file name
|
||||||
|
partFileGuard.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return std::make_tuple(signingResult, QByteArray{}, log, ae);
|
return std::make_tuple(signingResult, QByteArray{}, log, ae);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +271,7 @@ GpgME::Error QGpgMESignJobPrivate::startIt()
|
|||||||
}
|
}
|
||||||
|
|
||||||
q->run([=](Context *ctx) {
|
q->run([=](Context *ctx) {
|
||||||
return sign_to_filename(ctx, m_signers, m_inputFilePath, m_outputFilePath, m_signingFlags);
|
return sign_to_filename(ctx, m_signers, m_inputFilePath, m_outputFilePath, m_signingFlags, m_appendSignature);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -95,4 +95,16 @@ GpgME::SignatureMode SignJob::signingFlags() const
|
|||||||
return d->m_signingFlags;
|
return d->m_signingFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SignJob::setAppendSignature(bool append)
|
||||||
|
{
|
||||||
|
auto d = jobPrivate<SignJobPrivate>(this);
|
||||||
|
d->m_appendSignature = append;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SignJob::appendSignatureEnabled() const
|
||||||
|
{
|
||||||
|
auto d = jobPrivate<SignJobPrivate>(this);
|
||||||
|
return d->m_appendSignature;
|
||||||
|
}
|
||||||
|
|
||||||
#include "signjob.moc"
|
#include "signjob.moc"
|
||||||
|
@ -113,7 +113,8 @@ public:
|
|||||||
*
|
*
|
||||||
* \note If a file with this path exists, then the job will fail, i.e. you
|
* \note If a file with this path exists, then the job will fail, i.e. you
|
||||||
* need to delete an existing file that shall be overwritten before you
|
* need to delete an existing file that shall be overwritten before you
|
||||||
* start the job.
|
* start the job. If you create a detached signature then you can tell
|
||||||
|
* the job to append the new detached signature to an existing file.
|
||||||
*/
|
*/
|
||||||
void setOutputFile(const QString &path);
|
void setOutputFile(const QString &path);
|
||||||
QString outputFile() const;
|
QString outputFile() const;
|
||||||
@ -129,6 +130,17 @@ public:
|
|||||||
void setSigningFlags(GpgME::SignatureMode flags);
|
void setSigningFlags(GpgME::SignatureMode flags);
|
||||||
GpgME::SignatureMode signingFlags() const;
|
GpgME::SignatureMode signingFlags() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If @c true then a new detached signature is appended to an already
|
||||||
|
* existing detached signature.
|
||||||
|
*
|
||||||
|
* Defaults to \c false.
|
||||||
|
*
|
||||||
|
* Used if the job is started with startIt().
|
||||||
|
*/
|
||||||
|
void setAppendSignature(bool append);
|
||||||
|
bool appendSignatureEnabled() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Starts the signing operation. \a signers is the list of keys to
|
Starts the signing operation. \a signers is the list of keys to
|
||||||
sign \a plainText with. Empty (null) keys are ignored.
|
sign \a plainText with. Empty (null) keys are ignored.
|
||||||
|
@ -48,6 +48,7 @@ struct SignJobPrivate : public JobPrivate
|
|||||||
QString m_inputFilePath;
|
QString m_inputFilePath;
|
||||||
QString m_outputFilePath;
|
QString m_outputFilePath;
|
||||||
GpgME::SignatureMode m_signingFlags = GpgME::SignFile;
|
GpgME::SignatureMode m_signingFlags = GpgME::SignFile;
|
||||||
|
bool m_appendSignature = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,9 @@ std::ostream &operator<<(std::ostream &os, const QString &s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct CommandLineOptions {
|
struct CommandLineOptions {
|
||||||
bool armor;
|
GpgME::SignatureMode signingFlags = GpgME::NormalSignatureMode;
|
||||||
|
bool armor = false;
|
||||||
|
bool appendSignature = false;
|
||||||
QString inputFile;
|
QString inputFile;
|
||||||
QString outputFile;
|
QString outputFile;
|
||||||
std::chrono::seconds cancelTimeout{0};
|
std::chrono::seconds cancelTimeout{0};
|
||||||
@ -72,6 +74,8 @@ CommandLineOptions parseCommandLine(const QStringList &arguments)
|
|||||||
parser.addOptions({
|
parser.addOptions({
|
||||||
{{"o", "output"}, "Write output to FILE.", "FILE"},
|
{{"o", "output"}, "Write output to FILE.", "FILE"},
|
||||||
{{"a", "armor"}, "Create ASCII armored output."},
|
{{"a", "armor"}, "Create ASCII armored output."},
|
||||||
|
{{"b", "detach-sign"}, "Create a detached signature."},
|
||||||
|
{"append", "Append new (detached) signature to existing file."},
|
||||||
{"cancel-after", "Cancel the running job after SECONDS seconds.", "SECONDS"},
|
{"cancel-after", "Cancel the running job after SECONDS seconds.", "SECONDS"},
|
||||||
});
|
});
|
||||||
parser.addPositionalArgument("file", "File to sign", "FILE");
|
parser.addPositionalArgument("file", "File to sign", "FILE");
|
||||||
@ -84,6 +88,10 @@ CommandLineOptions parseCommandLine(const QStringList &arguments)
|
|||||||
}
|
}
|
||||||
|
|
||||||
options.armor = parser.isSet("armor");
|
options.armor = parser.isSet("armor");
|
||||||
|
if (parser.isSet("detach-sign")) {
|
||||||
|
options.signingFlags = GpgME::Detached;
|
||||||
|
options.appendSignature = parser.isSet("append");
|
||||||
|
}
|
||||||
options.inputFile = args.front();
|
options.inputFile = args.front();
|
||||||
options.outputFile = parser.value("output");
|
options.outputFile = parser.value("output");
|
||||||
if (parser.isSet("cancel-after")) {
|
if (parser.isSet("cancel-after")) {
|
||||||
@ -114,7 +122,7 @@ int main(int argc, char **argv)
|
|||||||
output.reset(new QFile);
|
output.reset(new QFile);
|
||||||
output->open(stdout, QIODevice::WriteOnly);
|
output->open(stdout, QIODevice::WriteOnly);
|
||||||
} else {
|
} else {
|
||||||
if (QFile::exists(options.outputFile)) {
|
if (QFile::exists(options.outputFile) && !options.appendSignature) {
|
||||||
qCritical() << "File" << options.outputFile << "exists. Bailing out.";
|
qCritical() << "File" << options.outputFile << "exists. Bailing out.";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -146,6 +154,8 @@ int main(int argc, char **argv)
|
|||||||
} else {
|
} else {
|
||||||
job->setInputFile(options.inputFile);
|
job->setInputFile(options.inputFile);
|
||||||
job->setOutputFile(options.outputFile);
|
job->setOutputFile(options.outputFile);
|
||||||
|
job->setSigningFlags(options.signingFlags);
|
||||||
|
job->setAppendSignature(options.appendSignature);
|
||||||
err = job->startIt();
|
err = job->startIt();
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
|
Loading…
Reference in New Issue
Block a user