mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
Add --is-encrypted and --requires-password (fixes #390)
Allow exit status-based checking of whether a file is encrypted or requires a password without necessarily supplying the correct password. Useful for scripting.
This commit is contained in:
parent
8b1c4828b7
commit
731c4f711b
10
ChangeLog
10
ChangeLog
@ -1,3 +1,13 @@
|
|||||||
|
2020-01-26 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* Add options --is-encrypted and --requires-password. These can be
|
||||||
|
used with files, including encrypted files with unknown passwords,
|
||||||
|
to determine whether or not a file is encrypted and whether a
|
||||||
|
password is required to open the file. The --requires-password
|
||||||
|
option can also be used to determine whether a supplied password
|
||||||
|
is correct. Information is supplied through exit codes, making
|
||||||
|
these options particularly useful for shell scripts. Fixes #390.
|
||||||
|
|
||||||
2020-01-14 Jay Berkenbilt <ejb@ql.org>
|
2020-01-14 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
* Fix for Windows being unable to acquire crypt context with a new
|
* Fix for Windows being unable to acquire crypt context with a new
|
||||||
|
@ -665,6 +665,40 @@ make
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--is-encrypted</option></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Silently exit with status 0 if the file is encrypted or status
|
||||||
|
2 if the file is not encrypted. This is useful for shell
|
||||||
|
scripts. Other options are ignored if this is given. This
|
||||||
|
option is mutually exclusive with
|
||||||
|
<option>--requires-password</option>. Both this option and
|
||||||
|
<option>--requires-password</option> exit with status 2 for
|
||||||
|
non-encrypted files.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--requires-password</option></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Silently exit with status 0 if a password (other than as
|
||||||
|
supplied) is required. Exit with status 2 if the file is not
|
||||||
|
encrypted. Exit with status 3 if the file is encrypted but
|
||||||
|
requires no password or the correct password has been
|
||||||
|
supplied. This is useful for shell scripts. Note that any
|
||||||
|
supplied password is used when opening the file. When used
|
||||||
|
with a <option>--password</option> option, this option can be
|
||||||
|
used to check the correctness of the password. In that case,
|
||||||
|
an exit status of 3 means the file works with the supplied
|
||||||
|
password. This option is mutually exclusive with
|
||||||
|
<option>--is-encrypted</option>. Both this option and
|
||||||
|
<option>--is-encrypted</option> exit with status 2 for
|
||||||
|
non-encrypted files.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--verbose</option></term>
|
<term><option>--verbose</option></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
@ -4675,6 +4709,23 @@ print "\n";
|
|||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
CLI Enhancements
|
||||||
|
</para>
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Added options <option>--is-encrypted</option> and
|
||||||
|
<option>--requires-password</option> for testing whether a
|
||||||
|
file is encrypted or requires a password other than the
|
||||||
|
supplied (or empty) password. These communicate via exit
|
||||||
|
status, making them useful for shell scripts. They also work
|
||||||
|
on encrypted files with unknown passwords.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
82
qpdf/qpdf.cc
82
qpdf/qpdf.cc
@ -29,8 +29,12 @@
|
|||||||
#include <qpdf/QPDFWriter.hh>
|
#include <qpdf/QPDFWriter.hh>
|
||||||
#include <qpdf/QIntC.hh>
|
#include <qpdf/QIntC.hh>
|
||||||
|
|
||||||
static int const EXIT_ERROR = 2;
|
static int constexpr EXIT_ERROR = 2;
|
||||||
static int const EXIT_WARNING = 3;
|
static int constexpr EXIT_WARNING = 3;
|
||||||
|
|
||||||
|
// For is-encrypted and requires-password
|
||||||
|
static int constexpr EXIT_IS_NOT_ENCRYPTED = 2;
|
||||||
|
static int constexpr EXIT_CORRECT_PASSWORD = 3;
|
||||||
|
|
||||||
static char const* whoami = 0;
|
static char const* whoami = 0;
|
||||||
|
|
||||||
@ -183,6 +187,8 @@ struct Options
|
|||||||
under_overlay(0),
|
under_overlay(0),
|
||||||
require_outfile(true),
|
require_outfile(true),
|
||||||
replace_input(false),
|
replace_input(false),
|
||||||
|
check_is_encrypted(false),
|
||||||
|
check_requires_password(false),
|
||||||
infilename(0),
|
infilename(0),
|
||||||
outfilename(0)
|
outfilename(0)
|
||||||
{
|
{
|
||||||
@ -287,6 +293,8 @@ struct Options
|
|||||||
std::map<std::string, RotationSpec> rotations;
|
std::map<std::string, RotationSpec> rotations;
|
||||||
bool require_outfile;
|
bool require_outfile;
|
||||||
bool replace_input;
|
bool replace_input;
|
||||||
|
bool check_is_encrypted;
|
||||||
|
bool check_requires_password;
|
||||||
char const* infilename;
|
char const* infilename;
|
||||||
char const* outfilename;
|
char const* outfilename;
|
||||||
};
|
};
|
||||||
@ -718,6 +726,8 @@ class ArgParser
|
|||||||
void argUOpassword(char* parameter);
|
void argUOpassword(char* parameter);
|
||||||
void argEndUnderOverlay();
|
void argEndUnderOverlay();
|
||||||
void argReplaceInput();
|
void argReplaceInput();
|
||||||
|
void argIsEncrypted();
|
||||||
|
void argRequiresPassword();
|
||||||
|
|
||||||
void usage(std::string const& message);
|
void usage(std::string const& message);
|
||||||
void checkCompletion();
|
void checkCompletion();
|
||||||
@ -948,6 +958,8 @@ ArgParser::initOptionTable()
|
|||||||
(*t)["overlay"] = oe_bare(&ArgParser::argOverlay);
|
(*t)["overlay"] = oe_bare(&ArgParser::argOverlay);
|
||||||
(*t)["underlay"] = oe_bare(&ArgParser::argUnderlay);
|
(*t)["underlay"] = oe_bare(&ArgParser::argUnderlay);
|
||||||
(*t)["replace-input"] = oe_bare(&ArgParser::argReplaceInput);
|
(*t)["replace-input"] = oe_bare(&ArgParser::argReplaceInput);
|
||||||
|
(*t)["is-encrypted"] = oe_bare(&ArgParser::argIsEncrypted);
|
||||||
|
(*t)["requires-password"] = oe_bare(&ArgParser::argRequiresPassword);
|
||||||
|
|
||||||
t = &this->encrypt40_option_table;
|
t = &this->encrypt40_option_table;
|
||||||
(*t)["--"] = oe_bare(&ArgParser::argEndEncrypt);
|
(*t)["--"] = oe_bare(&ArgParser::argEndEncrypt);
|
||||||
@ -1105,6 +1117,13 @@ ArgParser::argHelp()
|
|||||||
<< "--completion-bash output a bash complete command you can eval\n"
|
<< "--completion-bash output a bash complete command you can eval\n"
|
||||||
<< "--completion-zsh output a zsh complete command you can eval\n"
|
<< "--completion-zsh output a zsh complete command you can eval\n"
|
||||||
<< "--password=password specify a password for accessing encrypted files\n"
|
<< "--password=password specify a password for accessing encrypted files\n"
|
||||||
|
<< "--is-encrypted silently exit 0 if the file is encrypted or 2\n"
|
||||||
|
<< " if not; useful for shell scripts\n"
|
||||||
|
<< "--requires-password silently exit 0 if a password (other than as\n"
|
||||||
|
<< " supplied) is required, 2 if the file is not\n"
|
||||||
|
<< " encrypted, or 3 if the file is encrypted\n"
|
||||||
|
<< " but requires no password or the supplied password\n"
|
||||||
|
<< " is correct; useful for shell scripts\n"
|
||||||
<< "--verbose provide additional informational output\n"
|
<< "--verbose provide additional informational output\n"
|
||||||
<< "--progress give progress indicators while writing output\n"
|
<< "--progress give progress indicators while writing output\n"
|
||||||
<< "--no-warn suppress warnings\n"
|
<< "--no-warn suppress warnings\n"
|
||||||
@ -2352,6 +2371,20 @@ ArgParser::argReplaceInput()
|
|||||||
o.replace_input = true;
|
o.replace_input = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ArgParser::argIsEncrypted()
|
||||||
|
{
|
||||||
|
o.check_is_encrypted = true;
|
||||||
|
o.require_outfile = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ArgParser::argRequiresPassword()
|
||||||
|
{
|
||||||
|
o.check_requires_password = true;
|
||||||
|
o.require_outfile = false;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ArgParser::handleArgFileArguments()
|
ArgParser::handleArgFileArguments()
|
||||||
{
|
{
|
||||||
@ -3113,6 +3146,11 @@ ArgParser::doFinalChecks()
|
|||||||
{
|
{
|
||||||
o.externalize_inline_images = true;
|
o.externalize_inline_images = true;
|
||||||
}
|
}
|
||||||
|
if (o.check_requires_password && o.check_is_encrypted)
|
||||||
|
{
|
||||||
|
usage("--requires-password and --is-encrypted may not be given"
|
||||||
|
" together");
|
||||||
|
}
|
||||||
|
|
||||||
if (o.require_outfile && o.outfilename &&
|
if (o.require_outfile && o.outfilename &&
|
||||||
(strcmp(o.outfilename, "-") == 0))
|
(strcmp(o.outfilename, "-") == 0))
|
||||||
@ -5252,9 +5290,45 @@ int realmain(int argc, char* argv[])
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
ap.parseOptions();
|
ap.parseOptions();
|
||||||
PointerHolder<QPDF> pdf_ph =
|
PointerHolder<QPDF> pdf_ph;
|
||||||
process_file(o.infilename, o.password, o);
|
try
|
||||||
|
{
|
||||||
|
pdf_ph = process_file(o.infilename, o.password, o);
|
||||||
|
}
|
||||||
|
catch (QPDFExc& e)
|
||||||
|
{
|
||||||
|
if ((e.getErrorCode() == qpdf_e_password) &&
|
||||||
|
(o.check_is_encrypted || o.check_requires_password))
|
||||||
|
{
|
||||||
|
// Allow --is-encrypted and --requires-password to
|
||||||
|
// work when an incorrect password is supplied.
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
QPDF& pdf = *pdf_ph;
|
QPDF& pdf = *pdf_ph;
|
||||||
|
if (o.check_is_encrypted)
|
||||||
|
{
|
||||||
|
if (pdf.isEncrypted())
|
||||||
|
{
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
exit(EXIT_IS_NOT_ENCRYPTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (o.check_requires_password)
|
||||||
|
{
|
||||||
|
if (pdf.isEncrypted())
|
||||||
|
{
|
||||||
|
exit(EXIT_CORRECT_PASSWORD);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
exit(EXIT_IS_NOT_ENCRYPTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (! o.page_specs.empty())
|
if (! o.page_specs.empty())
|
||||||
{
|
{
|
||||||
handle_page_specs(pdf, o);
|
handle_page_specs(pdf, o);
|
||||||
|
@ -250,6 +250,28 @@ $td->runtest("check exception handling",
|
|||||||
{$td->FILE => "exceptions.out", $td->EXIT_STATUS => 0},
|
{$td->FILE => "exceptions.out", $td->EXIT_STATUS => 0},
|
||||||
$td->NORMALIZE_NEWLINES);
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
|
show_ntests();
|
||||||
|
# ----------
|
||||||
|
$td->notify("--- Check encryption/password ---");
|
||||||
|
my @check_encryption_password = (
|
||||||
|
# file, password, is-encrypted, requires-password
|
||||||
|
["minimal.pdf", "", 2, 2],
|
||||||
|
["20-pages.pdf", "", 0, 0],
|
||||||
|
["20-pages.pdf", "user", 0, 3],
|
||||||
|
);
|
||||||
|
$n_tests += 2 * scalar(@check_encryption_password);
|
||||||
|
foreach my $d (@check_encryption_password)
|
||||||
|
{
|
||||||
|
my ($file, $pass, $is_encrypted, $requires_password) = @$d;
|
||||||
|
$td->runtest("is encrypted ($file, pass=$pass)",
|
||||||
|
{$td->COMMAND => "qpdf --is-encrypted --password=$pass $file"},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => $is_encrypted});
|
||||||
|
$td->runtest("requires password ($file, pass=$pass)",
|
||||||
|
{$td->COMMAND => "qpdf --requires-password" .
|
||||||
|
" --password=$pass $file"},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => $requires_password});
|
||||||
|
}
|
||||||
|
|
||||||
show_ntests();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
$td->notify("--- Dangling Refs ---");
|
$td->notify("--- Dangling Refs ---");
|
||||||
|
Loading…
Reference in New Issue
Block a user