summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libqpdf/QPDFWriter.cc43
-rw-r--r--qpdf/qpdf.testcov1
-rw-r--r--qpdf/qtest/qpdf.test21
-rw-r--r--qpdf/qtest/qpdf/unfilterable-with-crypt-after.out4
-rw-r--r--qpdf/qtest/qpdf/unfilterable-with-crypt-before.out4
-rw-r--r--qpdf/qtest/qpdf/unfilterable-with-crypt.pdfbin0 -> 17868 bytes
-rw-r--r--qpdf/test_driver.cc33
7 files changed, 102 insertions, 4 deletions
diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc
index d7ab0c8b..ba37d423 100644
--- a/libqpdf/QPDFWriter.cc
+++ b/libqpdf/QPDFWriter.cc
@@ -1297,12 +1297,53 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
// Suppress /Length since we will write it manually
object.removeKey("/Length");
- // XXX BUG: /Crypt filters should always be removed.
if (flags & f_filtered)
{
+ // We will supply our own filter and decode
+ // parameters.
object.removeKey("/Filter");
object.removeKey("/DecodeParms");
}
+ else
+ {
+ // Make sure, no matter what else we have, that we
+ // don't have /Crypt in the output filters.
+ QPDFObjectHandle filter = object.getKey("/Filter");
+ QPDFObjectHandle decode_parms = object.getKey("/DecodeParms");
+ if (filter.isOrHasName("/Crypt"))
+ {
+ if (filter.isName())
+ {
+ object.removeKey("/Filter");
+ object.removeKey("/DecodeParms");
+ }
+ else
+ {
+ int idx = -1;
+ for (int i = 0; i < filter.getArrayNItems(); ++i)
+ {
+ QPDFObjectHandle item = filter.getArrayItem(i);
+ if (item.isName() && item.getName() == "/Crypt")
+ {
+ idx = i;
+ break;
+ }
+ }
+ if (idx >= 0)
+ {
+ // If filter is an array, then the code in
+ // QPDF_Stream has already verified that
+ // DecodeParms and Filters are arrays of
+ // the same length, but if they weren't
+ // for some reason, eraseItem does type
+ // and bounds checking.
+ QTC::TC("qpdf", "QPDFWriter remove Crypt");
+ filter.eraseItem(idx);
+ decode_parms.eraseItem(idx);
+ }
+ }
+ }
+ }
}
writeString("<<");
diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov
index 4ade4e7e..0263f312 100644
--- a/qpdf/qpdf.testcov
+++ b/qpdf/qpdf.testcov
@@ -255,3 +255,4 @@ QPDFWriter preserve ADBE 0
QPDF_encryption skip 0x28 0
QPDF_encrypt crypt array 0
QPDF_encryption CFM AESV3 0
+QPDFWriter remove Crypt 0
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 47afbcf2..0e164be7 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -1701,12 +1701,12 @@ my @attachments = (
'enc-XI-attachments-base.pdf',
'enc-XI-R6,V5,U=attachment,encrypted-attachments.pdf',
'enc-XI-R6,V5,U=view,attachments,cleartext-metadata.pdf');
-$n_tests += 4 * @attachments;
+$n_tests += 4 * @attachments + 3;
foreach my $f (@attachments)
{
my $pass = '';
my $tpass = '';
- if ($f =~ m/U=([^,]+)/)
+ if ($f =~ m/U=([^,\.]+)/)
{
$pass = "--password=$1";
$tpass = $1;
@@ -1726,6 +1726,23 @@ foreach my $f (@attachments)
{$td->FILE => "attachments.out", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
}
+$td->runtest("unfilterable with crypt",
+ {$td->COMMAND =>
+ "test_driver 36 unfilterable-with-crypt.pdf attachment"},
+ {$td->FILE => "unfilterable-with-crypt-before.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+unlink "a.pdf";
+$td->runtest("decrypt file",
+ {$td->COMMAND => "qpdf -decrypt --password=attachment" .
+ " unfilterable-with-crypt.pdf a.pdf"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0});
+$td->runtest("copy of unfilterable with crypt",
+ {$td->COMMAND =>
+ "test_driver 36 a.pdf attachment"},
+ {$td->FILE => "unfilterable-with-crypt-after.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
show_ntests();
# ----------
diff --git a/qpdf/qtest/qpdf/unfilterable-with-crypt-after.out b/qpdf/qtest/qpdf/unfilterable-with-crypt-after.out
new file mode 100644
index 00000000..ac523138
--- /dev/null
+++ b/qpdf/qtest/qpdf/unfilterable-with-crypt-after.out
@@ -0,0 +1,4 @@
+<< /DL 30 /DecodeParms [ null ] /Filter [ /ZlateDecode ] /Length 39 /Params << /CheckSum <c4f73a3ba2b5fef86a4085d6f006eacd> /CreationDate (D:20121229172641-05'00') /ModDate (D:20121229172600) /Size 30 >> /Subtype /text#2fplain >>attachment1.txt:
+This is the first attachment.
+--END--
+test 36 done
diff --git a/qpdf/qtest/qpdf/unfilterable-with-crypt-before.out b/qpdf/qtest/qpdf/unfilterable-with-crypt-before.out
new file mode 100644
index 00000000..36e2852d
--- /dev/null
+++ b/qpdf/qtest/qpdf/unfilterable-with-crypt-before.out
@@ -0,0 +1,4 @@
+<< /DL 30 /DecodeParms [ << /Name /StdCF >> null ] /Filter [ /Crypt /ZlateDecode ] /Length 64 /Params << /CheckSum <c4f73a3ba2b5fef86a4085d6f006eacd> /CreationDate (D:20121229172641-05'00') /ModDate (D:20121229172600) /Size 30 >> /Subtype /text#2fplain >>attachment1.txt:
+This is the first attachment.
+--END--
+test 36 done
diff --git a/qpdf/qtest/qpdf/unfilterable-with-crypt.pdf b/qpdf/qtest/qpdf/unfilterable-with-crypt.pdf
new file mode 100644
index 00000000..970ea536
--- /dev/null
+++ b/qpdf/qtest/qpdf/unfilterable-with-crypt.pdf
Binary files differ
diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc
index 45190042..239464ce 100644
--- a/qpdf/test_driver.cc
+++ b/qpdf/test_driver.cc
@@ -112,7 +112,7 @@ void runtest(int n, char const* filename1, char const* arg2)
{
pdf.setAttemptRecovery(false);
}
- if ((n == 35) && (arg2 != 0))
+ if (((n == 35) || (n == 36)) && (arg2 != 0))
{
// arg2 is password
pdf.processFile(filename1, arg2);
@@ -1214,6 +1214,37 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << filename << ":\n" << data << "--END--\n";
}
}
+ else if (n == 36)
+ {
+ // Extract raw unfilterable attachment
+
+ QPDFObjectHandle root = pdf.getRoot();
+ QPDFObjectHandle names = root.getKey("/Names");
+ QPDFObjectHandle embeddedFiles = names.getKey("/EmbeddedFiles");
+ names = embeddedFiles.getKey("/Names");
+ for (int i = 0; i < names.getArrayNItems(); ++i)
+ {
+ QPDFObjectHandle item = names.getArrayItem(i);
+ if (item.isDictionary() &&
+ item.getKey("/Type").isName() &&
+ (item.getKey("/Type").getName() == "/Filespec") &&
+ item.getKey("/EF").isDictionary() &&
+ item.getKey("/EF").getKey("/F").isStream() &&
+ (item.getKey("/F").getStringValue() == "attachment1.txt"))
+ {
+ std::string filename = item.getKey("/F").getStringValue();
+ QPDFObjectHandle stream = item.getKey("/EF").getKey("/F");
+ Pl_Buffer p1("buffer");
+ Pl_Flate p2("compress", &p1, Pl_Flate::a_inflate);
+ stream.pipeStreamData(&p2, false, false, false);
+ PointerHolder<Buffer> buf = p1.getBuffer();
+ std::string data = std::string(
+ (char const*)buf->getBuffer(), buf->getSize());
+ std::cout << stream.getDict().unparse()
+ << filename << ":\n" << data << "--END--\n";
+ }
+ }
+ }
else
{
throw std::runtime_error(std::string("invalid test ") +