2
1
mirror of https://github.com/qpdf/qpdf.git synced 2025-01-31 02:48:31 +00:00

Table-driven parsing of encrypt options

This commit is contained in:
Jay Berkenbilt 2018-12-20 20:52:26 -05:00
parent 245723c570
commit 3c075fc017

View File

@ -329,13 +329,25 @@ class ArgParser
void argWithImages();
void argShowJson();
void argCheck();
void arg40Print(char* parameter);
void arg40Modify(char* parameter);
void arg40Extract(char* parameter);
void arg40Annotate(char* parameter);
void arg128Accessibility(char* parameter);
void arg128Extract(char* parameter);
void arg128Print(char* parameter);
void arg128Modify(char* parameter);
void arg128ClearTextMetadata();
void arg128UseAes(char* parameter);
void arg128ForceV4();
void arg256ForceR5();
void argEndEncrypt();
void usage(std::string const& message);
void initOptionTable();
void handleHelpVersion();
void handleArgFileArguments();
void readArgsFromFile(char const* filename);
void parseEncryptOptions();
std::vector<PageSpec> parsePagesOptions();
void parseRotationParameter(std::string const&);
std::vector<int> parseNumrange(char const* range, int max,
@ -348,7 +360,11 @@ class ArgParser
Options& o;
int cur_arg;
std::map<std::string, OptionEntry> option_table;
std::map<std::string, OptionEntry>* option_table;
std::map<std::string, OptionEntry> main_option_table;
std::map<std::string, OptionEntry> encrypt40_option_table;
std::map<std::string, OptionEntry> encrypt128_option_table;
std::map<std::string, OptionEntry> encrypt256_option_table;
std::vector<PointerHolder<char> > new_argv;
PointerHolder<char*> argv_ph;
};
@ -359,6 +375,7 @@ ArgParser::ArgParser(int argc, char* argv[], Options& o) :
o(o),
cur_arg(0)
{
option_table = &main_option_table;
initOptionTable();
}
@ -414,75 +431,107 @@ ArgParser::oe_requiredChoices(param_arg_handler_t h, char const** choices)
void
ArgParser::initOptionTable()
{
std::map<std::string, OptionEntry>& t = this->option_table;
std::map<std::string, OptionEntry>* t = &this->main_option_table;
char const* yn[] = {"y", "n", 0};
t[""] = oe_positional(&ArgParser::argPositional);
t["password"] = oe_requiredParameter(&ArgParser::argPassword, "pass");
t["empty"] = oe_bare(&ArgParser::argEmpty);
t["linearize"] = oe_bare(&ArgParser::argLinearize);
t["encrypt"] = oe_bare(&ArgParser::argEncrypt);
t["decrypt"] = oe_bare(&ArgParser::argDecrypt);
t["password-is-hex-key"] = oe_bare(&ArgParser::argPasswordIsHexKey);
t["copy-encryption"] = oe_requiredParameter(
(*t)[""] = oe_positional(&ArgParser::argPositional);
(*t)["password"] = oe_requiredParameter(&ArgParser::argPassword, "pass");
(*t)["empty"] = oe_bare(&ArgParser::argEmpty);
(*t)["linearize"] = oe_bare(&ArgParser::argLinearize);
(*t)["encrypt"] = oe_bare(&ArgParser::argEncrypt);
(*t)["decrypt"] = oe_bare(&ArgParser::argDecrypt);
(*t)["password-is-hex-key"] = oe_bare(&ArgParser::argPasswordIsHexKey);
(*t)["copy-encryption"] = oe_requiredParameter(
&ArgParser::argCopyEncryption, "file");
t["encryption-file-password"] = oe_requiredParameter(
(*t)["encryption-file-password"] = oe_requiredParameter(
&ArgParser::argEncryptionFilePassword, "password");
t["pages"] = oe_bare(&ArgParser::argPages);
t["rotate"] = oe_requiredParameter(
(*t)["pages"] = oe_bare(&ArgParser::argPages);
(*t)["rotate"] = oe_requiredParameter(
&ArgParser::argRotate, "[+|-]angle:page-range");
char const* streamDataChoices[] =
{"compress", "preserve", "uncompress", 0};
t["stream-data"] = oe_requiredChoices(
(*t)["stream-data"] = oe_requiredChoices(
&ArgParser::argStreamData, streamDataChoices);
t["compress-streams"] = oe_requiredChoices(
(*t)["compress-streams"] = oe_requiredChoices(
&ArgParser::argCompressStreams, yn);
char const* decodeLevelChoices[] =
{"none", "generalized", "specialized", "all", 0};
t["decode-level"] = oe_requiredChoices(
(*t)["decode-level"] = oe_requiredChoices(
&ArgParser::argDecodeLevel, decodeLevelChoices);
t["normalize-content"] = oe_requiredChoices(
(*t)["normalize-content"] = oe_requiredChoices(
&ArgParser::argNormalizeContent, yn);
t["suppress-recovery"] = oe_bare(&ArgParser::argSuppressRecovery);
(*t)["suppress-recovery"] = oe_bare(&ArgParser::argSuppressRecovery);
char const* objectStreamsChoices[] = {"disable", "preserve", "generate", 0};
t["object-streams"] = oe_requiredChoices(
(*t)["object-streams"] = oe_requiredChoices(
&ArgParser::argObjectStreams, objectStreamsChoices);
t["ignore-xref-streams"] = oe_bare(&ArgParser::argIgnoreXrefStreams);
t["qdf"] = oe_bare(&ArgParser::argQdf);
t["preserve-unreferenced"] = oe_bare(&ArgParser::argPreserveUnreferenced);
t["preserve-unreferenced-resources"] = oe_bare(
(*t)["ignore-xref-streams"] = oe_bare(&ArgParser::argIgnoreXrefStreams);
(*t)["qdf"] = oe_bare(&ArgParser::argQdf);
(*t)["preserve-unreferenced"] = oe_bare(
&ArgParser::argPreserveUnreferenced);
(*t)["preserve-unreferenced-resources"] = oe_bare(
&ArgParser::argPreserveUnreferencedResources);
t["keep-files-open"] = oe_requiredChoices(&ArgParser::argKeepFilesOpen, yn);
t["newline-before-endstream"] = oe_bare(
(*t)["keep-files-open"] = oe_requiredChoices(
&ArgParser::argKeepFilesOpen, yn);
(*t)["newline-before-endstream"] = oe_bare(
&ArgParser::argNewlineBeforeEndstream);
t["linearize-pass1"] = oe_requiredParameter(
(*t)["linearize-pass1"] = oe_requiredParameter(
&ArgParser::argLinearizePass1, "filename");
t["coalesce-contents"] = oe_bare(&ArgParser::argCoalesceContents);
t["min-version"] = oe_requiredParameter(
(*t)["coalesce-contents"] = oe_bare(&ArgParser::argCoalesceContents);
(*t)["min-version"] = oe_requiredParameter(
&ArgParser::argMinVersion, "version");
t["force-version"] = oe_requiredParameter(
(*t)["force-version"] = oe_requiredParameter(
&ArgParser::argForceVersion, "version");
t["split-pages"] = oe_optionalParameter(&ArgParser::argSplitPages);
t["verbose"] = oe_bare(&ArgParser::argVerbose);
t["progress"] = oe_bare(&ArgParser::argProgress);
t["no-warn"] = oe_bare(&ArgParser::argNoWarn);
t["deterministic-id"] = oe_bare(&ArgParser::argDeterministicId);
t["static-id"] = oe_bare(&ArgParser::argStaticId);
t["static-aes-iv"] = oe_bare(&ArgParser::argStaticAesIv);
t["no-original-object-ids"] = oe_bare(&ArgParser::argNoOriginalObjectIds);
t["show-encryption"] = oe_bare(&ArgParser::argShowEncryption);
t["show-encryption-key"] = oe_bare(&ArgParser::argShowEncryptionKey);
t["check-linearization"] = oe_bare(&ArgParser::argCheckLinearization);
t["show-linearization"] = oe_bare(&ArgParser::argShowLinearization);
t["show-xref"] = oe_bare(&ArgParser::argShowXref);
t["show-object"] = oe_requiredParameter(
(*t)["split-pages"] = oe_optionalParameter(&ArgParser::argSplitPages);
(*t)["verbose"] = oe_bare(&ArgParser::argVerbose);
(*t)["progress"] = oe_bare(&ArgParser::argProgress);
(*t)["no-warn"] = oe_bare(&ArgParser::argNoWarn);
(*t)["deterministic-id"] = oe_bare(&ArgParser::argDeterministicId);
(*t)["static-id"] = oe_bare(&ArgParser::argStaticId);
(*t)["static-aes-iv"] = oe_bare(&ArgParser::argStaticAesIv);
(*t)["no-original-object-ids"] = oe_bare(
&ArgParser::argNoOriginalObjectIds);
(*t)["show-encryption"] = oe_bare(&ArgParser::argShowEncryption);
(*t)["show-encryption-key"] = oe_bare(&ArgParser::argShowEncryptionKey);
(*t)["check-linearization"] = oe_bare(&ArgParser::argCheckLinearization);
(*t)["show-linearization"] = oe_bare(&ArgParser::argShowLinearization);
(*t)["show-xref"] = oe_bare(&ArgParser::argShowXref);
(*t)["show-object"] = oe_requiredParameter(
&ArgParser::argShowObject, "obj[,gen]");
t["raw-stream-data"] = oe_bare(&ArgParser::argShowObject);
t["filtered-stream-data"] = oe_bare(&ArgParser::argFilteredStreamData);
t["show-npages"] = oe_bare(&ArgParser::argShowNpages);
t["show-pages"] = oe_bare(&ArgParser::argShowPages);
t["with-images"] = oe_bare(&ArgParser::argWithImages);
t["show-json"] = oe_bare(&ArgParser::argShowJson);
t["check"] = oe_bare(&ArgParser::argCheck);
(*t)["raw-stream-data"] = oe_bare(&ArgParser::argShowObject);
(*t)["filtered-stream-data"] = oe_bare(&ArgParser::argFilteredStreamData);
(*t)["show-npages"] = oe_bare(&ArgParser::argShowNpages);
(*t)["show-pages"] = oe_bare(&ArgParser::argShowPages);
(*t)["with-images"] = oe_bare(&ArgParser::argWithImages);
(*t)["show-json"] = oe_bare(&ArgParser::argShowJson);
(*t)["check"] = oe_bare(&ArgParser::argCheck);
t = &this->encrypt40_option_table;
(*t)["--"] = oe_bare(&ArgParser::argEndEncrypt);
(*t)["print"] = oe_requiredChoices(&ArgParser::arg40Print, yn);
(*t)["modify"] = oe_requiredChoices(&ArgParser::arg40Modify, yn);
(*t)["extract"] = oe_requiredChoices(&ArgParser::arg40Extract, yn);
(*t)["annotate"] = oe_requiredChoices(&ArgParser::arg40Annotate, yn);
t = &this->encrypt128_option_table;
(*t)["--"] = oe_bare(&ArgParser::argEndEncrypt);
(*t)["accessibility"] = oe_requiredChoices(
&ArgParser::arg128Accessibility, yn);
(*t)["extract"] = oe_requiredChoices(&ArgParser::arg128Extract, yn);
char const* print128Choices[] = {"full", "low", "none", 0};
(*t)["print"] = oe_requiredChoices(
&ArgParser::arg128Print, print128Choices);
char const* modify128Choices[] =
{"all", "annotate", "form", "assembly", "none", 0};
(*t)["modify"] = oe_requiredChoices(
&ArgParser::arg128Modify, modify128Choices);
(*t)["cleartext-metadata"] = oe_bare(&ArgParser::arg128ClearTextMetadata);
// The above 128-bit options are also 256-bit options, so copy
// what we have so far. Then continue separately with 128 and 256.
this->encrypt256_option_table = this->encrypt128_option_table;
(*t)["use-aes"] = oe_requiredChoices(&ArgParser::arg128UseAes, yn);
(*t)["force-V4"] = oe_bare(&ArgParser::arg128ForceV4);
t = &this->encrypt256_option_table;
(*t)["force-R5"] = oe_bare(&ArgParser::arg256ForceR5);
}
void
@ -524,10 +573,33 @@ void
ArgParser::argEncrypt()
{
++cur_arg;
parseEncryptOptions();
o.encrypt = true;
o.decrypt = false;
o.copy_encryption = false;
if (cur_arg + 3 >= argc)
{
usage("insufficient arguments to --encrypt");
}
o.user_password = argv[cur_arg++];
o.owner_password = argv[cur_arg++];
std::string len_str = argv[cur_arg];
if (len_str == "40")
{
o.keylen = 40;
this->option_table = &(this->encrypt40_option_table);
}
else if (len_str == "128")
{
o.keylen = 128;
this->option_table = &(this->encrypt128_option_table);
}
else if (len_str == "256")
{
o.keylen = 256;
o.use_aes = true;
this->option_table = &(this->encrypt256_option_table);
}
else
{
usage("encryption key length must be 40, 128, or 256");
}
}
void
@ -877,6 +949,125 @@ ArgParser::argCheck()
o.require_outfile = false;
}
void
ArgParser::arg40Print(char* parameter)
{
o.r2_print = (strcmp(parameter, "y") == 0);
}
void
ArgParser::arg40Modify(char* parameter)
{
o.r2_modify = (strcmp(parameter, "y") == 0);
}
void
ArgParser::arg40Extract(char* parameter)
{
o.r2_extract = (strcmp(parameter, "y") == 0);
}
void
ArgParser::arg40Annotate(char* parameter)
{
o.r2_annotate = (strcmp(parameter, "y") == 0);
}
void
ArgParser::arg128Accessibility(char* parameter)
{
o.r3_accessibility = (strcmp(parameter, "y") == 0);
}
void
ArgParser::arg128Extract(char* parameter)
{
o.r3_extract = (strcmp(parameter, "y") == 0);
}
void
ArgParser::arg128Print(char* parameter)
{
if (strcmp(parameter, "full") == 0)
{
o.r3_print = qpdf_r3p_full;
}
else if (strcmp(parameter, "low") == 0)
{
o.r3_print = qpdf_r3p_low;
}
else if (strcmp(parameter, "none") == 0)
{
o.r3_print = qpdf_r3p_none;
}
else
{
usage("invalid print option");
}
}
void
ArgParser::arg128Modify(char* parameter)
{
if (strcmp(parameter, "all") == 0)
{
o.r3_modify = qpdf_r3m_all;
}
else if (strcmp(parameter, "annotate") == 0)
{
o.r3_modify = qpdf_r3m_annotate;
}
else if (strcmp(parameter, "form") == 0)
{
o.r3_modify = qpdf_r3m_form;
}
else if (strcmp(parameter, "assembly") == 0)
{
o.r3_modify = qpdf_r3m_assembly;
}
else if (strcmp(parameter, "none") == 0)
{
o.r3_modify = qpdf_r3m_none;
}
else
{
usage("invalid modify option");
}
}
void
ArgParser::arg128ClearTextMetadata()
{
o.cleartext_metadata = true;
}
void
ArgParser::arg128UseAes(char* parameter)
{
o.use_aes = (strcmp(parameter, "y") == 0);
}
void
ArgParser::arg128ForceV4()
{
o.force_V4 = true;
}
void
ArgParser::arg256ForceR5()
{
o.force_R5 = true;
}
void
ArgParser::argEndEncrypt()
{
o.encrypt = true;
o.decrypt = false;
o.copy_encryption = false;
this->option_table = &(this->main_option_table);
}
void
ArgParser::handleArgFileArguments()
{
@ -1400,329 +1591,6 @@ ArgParser::parseNumrange(char const* range, int max, bool throw_error)
return std::vector<int>();
}
void
ArgParser::parseEncryptOptions()
{
if (cur_arg + 3 >= argc)
{
usage("insufficient arguments to --encrypt");
}
o.user_password = argv[cur_arg++];
o.owner_password = argv[cur_arg++];
std::string len_str = argv[cur_arg++];
if (len_str == "40")
{
o.keylen = 40;
}
else if (len_str == "128")
{
o.keylen = 128;
}
else if (len_str == "256")
{
o.keylen = 256;
o.use_aes = true;
}
else
{
usage("encryption key length must be 40, 128, or 256");
}
while (1)
{
char* arg = argv[cur_arg];
if (arg == 0)
{
usage("insufficient arguments to --encrypt");
}
else if (strcmp(arg, "--") == 0)
{
return;
}
if (arg[0] == '-')
{
++arg;
if (arg[0] == '-')
{
++arg;
}
}
else
{
usage(std::string("invalid encryption parameter ") + arg);
}
++cur_arg;
char* parameter = strchr(arg, '=');
if (parameter)
{
*parameter++ = 0;
}
if (strcmp(arg, "print") == 0)
{
if (parameter == 0)
{
usage("--print must be given as --print=option");
}
std::string val = parameter;
if (o.keylen == 40)
{
if (val == "y")
{
o.r2_print = true;
}
else if (val == "n")
{
o.r2_print = false;
}
else
{
usage("invalid 40-bit -print parameter");
}
}
else
{
if (val == "full")
{
o.r3_print = qpdf_r3p_full;
}
else if (val == "low")
{
o.r3_print = qpdf_r3p_low;
}
else if (val == "none")
{
o.r3_print = qpdf_r3p_none;
}
else
{
usage("invalid 128-bit -print parameter");
}
}
}
else if (strcmp(arg, "modify") == 0)
{
if (parameter == 0)
{
usage("--modify must be given as --modify=option");
}
std::string val = parameter;
if (o.keylen == 40)
{
if (val == "y")
{
o.r2_modify = true;
}
else if (val == "n")
{
o.r2_modify = false;
}
else
{
usage("invalid 40-bit -modify parameter");
}
}
else
{
if (val == "all")
{
o.r3_modify = qpdf_r3m_all;
}
else if (val == "annotate")
{
o.r3_modify = qpdf_r3m_annotate;
}
else if (val == "form")
{
o.r3_modify = qpdf_r3m_form;
}
else if (val == "assembly")
{
o.r3_modify = qpdf_r3m_assembly;
}
else if (val == "none")
{
o.r3_modify = qpdf_r3m_none;
}
else
{
usage("invalid 128-bit -modify parameter");
}
}
}
else if (strcmp(arg, "extract") == 0)
{
if (parameter == 0)
{
usage("--extract must be given as --extract=option");
}
std::string val = parameter;
bool result = false;
if (val == "y")
{
result = true;
}
else if (val == "n")
{
result = false;
}
else
{
usage("invalid -extract parameter");
}
if (o.keylen == 40)
{
o.r2_extract = result;
}
else
{
o.r3_extract = result;
}
}
else if (strcmp(arg, "annotate") == 0)
{
if (parameter == 0)
{
usage("--annotate must be given as --annotate=option");
}
std::string val = parameter;
bool result = false;
if (val == "y")
{
result = true;
}
else if (val == "n")
{
result = false;
}
else
{
usage("invalid -annotate parameter");
}
if (o.keylen == 40)
{
o.r2_annotate = result;
}
else
{
usage("-annotate invalid for 128-bit keys");
}
}
else if (strcmp(arg, "accessibility") == 0)
{
if (parameter == 0)
{
usage("--accessibility must be given as"
" --accessibility=option");
}
std::string val = parameter;
bool result = false;
if (val == "y")
{
result = true;
}
else if (val == "n")
{
result = false;
}
else
{
usage("invalid -accessibility parameter");
}
if (o.keylen == 40)
{
usage("-accessibility invalid for 40-bit keys");
}
else
{
o.r3_accessibility = result;
}
}
else if (strcmp(arg, "cleartext-metadata") == 0)
{
if (parameter)
{
usage("--cleartext-metadata does not take a parameter");
}
if (o.keylen == 40)
{
usage("--cleartext-metadata is invalid for 40-bit keys");
}
else
{
o.cleartext_metadata = true;
}
}
else if (strcmp(arg, "force-V4") == 0)
{
if (parameter)
{
usage("--force-V4 does not take a parameter");
}
if (o.keylen != 128)
{
usage("--force-V4 is invalid only for 128-bit keys");
}
else
{
o.force_V4 = true;
}
}
else if (strcmp(arg, "force-R5") == 0)
{
if (parameter)
{
usage("--force-R5 does not take a parameter");
}
if (o.keylen != 256)
{
usage("--force-R5 is invalid only for 256-bit keys");
}
else
{
o.force_R5 = true;
}
}
else if (strcmp(arg, "use-aes") == 0)
{
if (parameter == 0)
{
usage("--use-aes must be given as --extract=option");
}
std::string val = parameter;
bool result = false;
if (val == "y")
{
result = true;
}
else if (val == "n")
{
result = false;
}
else
{
usage("invalid -use-aes parameter");
}
if ((o.keylen == 40) && result)
{
usage("use-aes is invalid for 40-bit keys");
}
else if ((o.keylen == 256) && (! result))
{
// qpdf would happily create files encrypted with RC4
// using /V=5, but Adobe reader can't read them.
usage("use-aes can't be disabled with 256-bit keys");
}
else
{
o.use_aes = result;
}
}
else
{
usage(std::string("invalid encryption parameter --") + arg);
}
}
}
std::vector<PageSpec>
ArgParser::parsePagesOptions()
{
@ -1990,7 +1858,17 @@ ArgParser::parseOptions()
for (cur_arg = 1; cur_arg < argc; ++cur_arg)
{
char* arg = argv[cur_arg];
if ((arg[0] == '-') && (strcmp(arg, "-") != 0))
if (strcmp(arg, "--") == 0)
{
// Special case for -- option, which is used to break out
// of subparsers.
OptionEntry& oe = (*this->option_table)["--"];
if (oe.bare_arg_handler)
{
(this->*(oe.bare_arg_handler))();
}
}
else if ((arg[0] == '-') && (strcmp(arg, "-") != 0))
{
++arg;
if (arg[0] == '-')
@ -1998,19 +1876,28 @@ ArgParser::parseOptions()
// Be lax about -arg vs --arg
++arg;
}
char* parameter = const_cast<char*>(strchr(arg, '='));
char* parameter = 0;
if (strlen(arg) > 0)
{
// Prevent --=something from being treated as an empty
// arg since the empty string in the option table is
// for positional arguments.
parameter = const_cast<char*>(strchr(1 + arg, '='));
}
if (parameter)
{
*parameter++ = 0;
}
std::string arg_s(arg);
if (0 == this->option_table.count(arg_s))
if (arg_s.empty() ||
(arg_s.at(0) == '-') ||
(0 == this->option_table->count(arg_s)))
{
usage(std::string("unknown option --") + arg);
}
OptionEntry& oe = this->option_table[arg_s];
OptionEntry& oe = (*this->option_table)[arg_s];
if ((oe.parameter_needed && (0 == parameter)) ||
((! oe.choices.empty() &&
((0 == parameter) ||
@ -2055,9 +1942,11 @@ ArgParser::parseOptions()
(this->*(oe.param_arg_handler))(parameter);
}
}
else if (0 != this->option_table.count(""))
else if (0 != this->option_table->count(""))
{
OptionEntry& oe = this->option_table[""];
// The empty string maps to the positional argument
// handler.
OptionEntry& oe = (*this->option_table)[""];
if (oe.param_arg_handler)
{
(this->*(oe.param_arg_handler))(arg);
@ -2069,6 +1958,10 @@ ArgParser::parseOptions()
}
}
if (this->option_table != &(this->main_option_table))
{
usage("missing -- at end of options");
}
if (o.infilename == 0)
{
usage("an input file name is required");