Commit a101533e0a76a0890a2ad3ea5fcbb578da75a6b4

Authored by Jay Berkenbilt
1 parent b26ce88e

Add command line option to copy encryption from other file

Add --copy-encryption and --encryption-file-password options to qpdf.
Also strengthen test suite for copying encryption.  The strengthened
test suite would have caught the failure to preserve AES and the
failure to update the file version, which was invalidating the
encrypted data.
ChangeLog
1 2012-07-15 Jay Berkenbilt <ejb@ql.org> 1 2012-07-15 Jay Berkenbilt <ejb@ql.org>
2 2
  3 + * add new QPDF::isEncrypted method that returns some additional
  4 + information beyond other versions.
  5 +
  6 + * libqpdf/QPDFWriter.cc: fix copyEncryptionParameters to fix the
  7 + minimum PDF version based on other file's encryption needs. This
  8 + is a fix to code added on 2012-07-14 and did not impact previously
  9 + released code.
  10 +
3 * libqpdf/QPDFWriter.cc (copyEncryptionParameters): Bug fix: qpdf 11 * libqpdf/QPDFWriter.cc (copyEncryptionParameters): Bug fix: qpdf
4 was not preserving whether or not AES encryption was being used 12 was not preserving whether or not AES encryption was being used
5 - when copying encryption parameters. 13 + when copying encryption parameters. The file would still have
  14 + been properly encrypted, but a file that started off encrypted
  15 + with AES could have become encrypted with RC4.
6 16
7 2012-07-14 Jay Berkenbilt <ejb@ql.org> 17 2012-07-14 Jay Berkenbilt <ejb@ql.org>
8 18
@@ -64,9 +64,11 @@ Next @@ -64,9 +64,11 @@ Next
64 - Tests through qpdf command line: copy pages from multiple PDFs 64 - Tests through qpdf command line: copy pages from multiple PDFs
65 starting with one PDF and also starting with empty. 65 starting with one PDF and also starting with empty.
66 66
67 - * qpdf commandline: provide an option to copy encryption parameters  
68 - from another file, specifying file and password. Search for "Copy  
69 - encryption parameters" in qpdf.test. 67 + * Document --copy-encryption and --encryption-file-password in
  68 + manual. Mention that the first half of /ID as well as all the
  69 + encryption parameters are copied. Maybe mention about StrF and
  70 + StrM with respect to AES here and also with encryption
  71 + preservation.
70 72
71 73
72 Soon 74 Soon
include/qpdf/QPDF.hh
@@ -248,6 +248,12 @@ class QPDF @@ -248,6 +248,12 @@ class QPDF
248 QPDF_DLL 248 QPDF_DLL
249 bool isEncrypted(int& R, int& P); 249 bool isEncrypted(int& R, int& P);
250 250
  251 + QPDF_DLL
  252 + bool isEncrypted(int& R, int& P, int& V,
  253 + encryption_method_e& stream_method,
  254 + encryption_method_e& string_method,
  255 + encryption_method_e& file_method);
  256 +
251 // Encryption permissions -- not enforced by QPDF 257 // Encryption permissions -- not enforced by QPDF
252 QPDF_DLL 258 QPDF_DLL
253 bool allowAccessibility(); 259 bool allowAccessibility();
libqpdf/QPDF_encryption.cc
@@ -744,14 +744,30 @@ QPDF::isEncrypted() const @@ -744,14 +744,30 @@ QPDF::isEncrypted() const
744 bool 744 bool
745 QPDF::isEncrypted(int& R, int& P) 745 QPDF::isEncrypted(int& R, int& P)
746 { 746 {
  747 + int V;
  748 + encryption_method_e stream, string, file;
  749 + return isEncrypted(R, P, V, stream, string, file);
  750 +}
  751 +
  752 +bool
  753 +QPDF::isEncrypted(int& R, int& P, int& V,
  754 + encryption_method_e& stream_method,
  755 + encryption_method_e& string_method,
  756 + encryption_method_e& file_method)
  757 +{
747 if (this->encrypted) 758 if (this->encrypted)
748 { 759 {
749 QPDFObjectHandle trailer = getTrailer(); 760 QPDFObjectHandle trailer = getTrailer();
750 QPDFObjectHandle encrypt = trailer.getKey("/Encrypt"); 761 QPDFObjectHandle encrypt = trailer.getKey("/Encrypt");
751 QPDFObjectHandle Pkey = encrypt.getKey("/P"); 762 QPDFObjectHandle Pkey = encrypt.getKey("/P");
752 QPDFObjectHandle Rkey = encrypt.getKey("/R"); 763 QPDFObjectHandle Rkey = encrypt.getKey("/R");
  764 + QPDFObjectHandle Vkey = encrypt.getKey("/V");
753 P = Pkey.getIntValue(); 765 P = Pkey.getIntValue();
754 R = Rkey.getIntValue(); 766 R = Rkey.getIntValue();
  767 + V = Vkey.getIntValue();
  768 + stream_method = this->cf_stream;
  769 + string_method = this->cf_stream;
  770 + file_method = this->cf_file;
755 return true; 771 return true;
756 } 772 }
757 else 773 else
qpdf/qpdf.cc
@@ -33,18 +33,29 @@ Usage: qpdf [ options ] infilename [ outfilename ]\n\ @@ -33,18 +33,29 @@ Usage: qpdf [ options ] infilename [ outfilename ]\n\
33 \n\ 33 \n\
34 An option summary appears below. Please see the documentation for details.\n\ 34 An option summary appears below. Please see the documentation for details.\n\
35 \n\ 35 \n\
  36 +Note that when contradictory options are provided, whichever options are\n\
  37 +provided last take precedence.\n\
  38 +\n\
36 \n\ 39 \n\
37 Basic Options\n\ 40 Basic Options\n\
38 -------------\n\ 41 -------------\n\
39 \n\ 42 \n\
40 --password=password specify a password for accessing encrypted files\n\ 43 --password=password specify a password for accessing encrypted files\n\
41 --linearize generated a linearized (web optimized) file\n\ 44 --linearize generated a linearized (web optimized) file\n\
  45 +--copy-encryption=file copy encryption parameters from specified file\n\
  46 +--encryption-file-password=password\n\
  47 + password used to open the file from which encryption\n\
  48 + parameters are being copied\n\
42 --encrypt options -- generate an encrypted file\n\ 49 --encrypt options -- generate an encrypted file\n\
43 --decrypt remove any encryption on the file\n\ 50 --decrypt remove any encryption on the file\n\
44 \n\ 51 \n\
45 -If neither --encrypt or --decrypt are given, qpdf will preserve any\n\  
46 -encryption data associated with a file.\n\ 52 +If none of --copy-encryption, --encrypt or --decrypt are given, qpdf will\n\
  53 +preserve any encryption data associated with a file.\n\
47 \n\ 54 \n\
  55 +Note that when copying encryption parameters from another file, all\n\
  56 +parameters will be copied, including both user and owner passwords, even\n\
  57 +if the user password is used to open the other file. This works even if\n\
  58 +the owner password is not known.\n\
48 \n\ 59 \n\
49 Encryption Options\n\ 60 Encryption Options\n\
50 ------------------\n\ 61 ------------------\n\
@@ -192,12 +203,39 @@ static std::string show_bool(bool v) @@ -192,12 +203,39 @@ static std::string show_bool(bool v)
192 return v ? "allowed" : "not allowed"; 203 return v ? "allowed" : "not allowed";
193 } 204 }
194 205
  206 +static std::string show_encryption_method(QPDF::encryption_method_e method)
  207 +{
  208 + std::string result = "unknown";
  209 + switch (method)
  210 + {
  211 + case QPDF::e_none:
  212 + result = "none";
  213 + break;
  214 + case QPDF::e_unknown:
  215 + result = "unknown";
  216 + break;
  217 + case QPDF::e_rc4:
  218 + result = "RC4";
  219 + break;
  220 + case QPDF::e_aes:
  221 + result = "AESv2";
  222 + break;
  223 + // no default so gcc will warn for missing case
  224 + }
  225 + return result;
  226 +}
  227 +
195 static void show_encryption(QPDF& pdf) 228 static void show_encryption(QPDF& pdf)
196 { 229 {
197 // Extract /P from /Encrypt 230 // Extract /P from /Encrypt
198 int R = 0; 231 int R = 0;
199 int P = 0; 232 int P = 0;
200 - if (! pdf.isEncrypted(R, P)) 233 + int V = 0;
  234 + QPDF::encryption_method_e stream_method = QPDF::e_unknown;
  235 + QPDF::encryption_method_e string_method = QPDF::e_unknown;
  236 + QPDF::encryption_method_e file_method = QPDF::e_unknown;
  237 + if (! pdf.isEncrypted(R, P, V,
  238 + stream_method, string_method, file_method))
201 { 239 {
202 std::cout << "File is not encrypted" << std::endl; 240 std::cout << "File is not encrypted" << std::endl;
203 } 241 }
@@ -206,25 +244,34 @@ static void show_encryption(QPDF&amp; pdf) @@ -206,25 +244,34 @@ static void show_encryption(QPDF&amp; pdf)
206 std::cout << "R = " << R << std::endl; 244 std::cout << "R = " << R << std::endl;
207 std::cout << "P = " << P << std::endl; 245 std::cout << "P = " << P << std::endl;
208 std::string user_password = pdf.getTrimmedUserPassword(); 246 std::string user_password = pdf.getTrimmedUserPassword();
209 - std::cout << "User password = " << user_password << std::endl;  
210 - std::cout << "extract for accessibility: "  
211 - << show_bool(pdf.allowAccessibility()) << std::endl;  
212 - std::cout << "extract for any purpose: "  
213 - << show_bool(pdf.allowExtractAll()) << std::endl;  
214 - std::cout << "print low resolution: "  
215 - << show_bool(pdf.allowPrintLowRes()) << std::endl;  
216 - std::cout << "print high resolution: "  
217 - << show_bool(pdf.allowPrintHighRes()) << std::endl;  
218 - std::cout << "modify document assembly: "  
219 - << show_bool(pdf.allowModifyAssembly()) << std::endl;  
220 - std::cout << "modify forms: "  
221 - << show_bool(pdf.allowModifyForm()) << std::endl;  
222 - std::cout << "modify annotations: "  
223 - << show_bool(pdf.allowModifyAnnotation()) << std::endl;  
224 - std::cout << "modify other: "  
225 - << show_bool(pdf.allowModifyOther()) << std::endl;  
226 - std::cout << "modify anything: " 247 + std::cout << "User password = " << user_password << std::endl
  248 + << "extract for accessibility: "
  249 + << show_bool(pdf.allowAccessibility()) << std::endl
  250 + << "extract for any purpose: "
  251 + << show_bool(pdf.allowExtractAll()) << std::endl
  252 + << "print low resolution: "
  253 + << show_bool(pdf.allowPrintLowRes()) << std::endl
  254 + << "print high resolution: "
  255 + << show_bool(pdf.allowPrintHighRes()) << std::endl
  256 + << "modify document assembly: "
  257 + << show_bool(pdf.allowModifyAssembly()) << std::endl
  258 + << "modify forms: "
  259 + << show_bool(pdf.allowModifyForm()) << std::endl
  260 + << "modify annotations: "
  261 + << show_bool(pdf.allowModifyAnnotation()) << std::endl
  262 + << "modify other: "
  263 + << show_bool(pdf.allowModifyOther()) << std::endl
  264 + << "modify anything: "
227 << show_bool(pdf.allowModifyAll()) << std::endl; 265 << show_bool(pdf.allowModifyAll()) << std::endl;
  266 + if (V >= 4)
  267 + {
  268 + std::cout << "stream encryption method: "
  269 + << show_encryption_method(stream_method) << std::endl
  270 + << "string encryption method: "
  271 + << show_encryption_method(string_method) << std::endl
  272 + << "file encryption method: "
  273 + << show_encryption_method(file_method) << std::endl;
  274 + }
228 } 275 }
229 } 276 }
230 277
@@ -579,6 +626,10 @@ int main(int argc, char* argv[]) @@ -579,6 +626,10 @@ int main(int argc, char* argv[])
579 bool linearize = false; 626 bool linearize = false;
580 bool decrypt = false; 627 bool decrypt = false;
581 628
  629 + bool copy_encryption = false;
  630 + char const* encryption_file = 0;
  631 + char const* encryption_file_password = "";
  632 +
582 bool encrypt = false; 633 bool encrypt = false;
583 std::string user_password; 634 std::string user_password;
584 std::string owner_password; 635 std::string owner_password;
@@ -664,11 +715,36 @@ int main(int argc, char* argv[]) @@ -664,11 +715,36 @@ int main(int argc, char* argv[])
664 r3_accessibility, r3_extract, r3_print, r3_modify, 715 r3_accessibility, r3_extract, r3_print, r3_modify,
665 force_V4, cleartext_metadata, use_aes); 716 force_V4, cleartext_metadata, use_aes);
666 encrypt = true; 717 encrypt = true;
  718 + decrypt = false;
  719 + copy_encryption = false;
667 } 720 }
668 else if (strcmp(arg, "decrypt") == 0) 721 else if (strcmp(arg, "decrypt") == 0)
669 { 722 {
670 decrypt = true; 723 decrypt = true;
  724 + encrypt = false;
  725 + copy_encryption = false;
671 } 726 }
  727 + else if (strcmp(arg, "copy-encryption") == 0)
  728 + {
  729 + if (parameter == 0)
  730 + {
  731 + usage("--copy-encryption must be given as"
  732 + "--copy_encryption=file");
  733 + }
  734 + encryption_file = parameter;
  735 + copy_encryption = true;
  736 + encrypt = false;
  737 + decrypt = false;
  738 + }
  739 + else if (strcmp(arg, "encryption-file-password") == 0)
  740 + {
  741 + if (parameter == 0)
  742 + {
  743 + usage("--encryption-file-password must be given as"
  744 + "--encryption-file-password=password");
  745 + }
  746 + encryption_file_password = parameter;
  747 + }
672 else if (strcmp(arg, "stream-data") == 0) 748 else if (strcmp(arg, "stream-data") == 0)
673 { 749 {
674 if (parameter == 0) 750 if (parameter == 0)
@@ -865,6 +941,7 @@ int main(int argc, char* argv[]) @@ -865,6 +941,7 @@ int main(int argc, char* argv[])
865 try 941 try
866 { 942 {
867 QPDF pdf; 943 QPDF pdf;
  944 + QPDF encryption_pdf;
868 if (ignore_xref_streams) 945 if (ignore_xref_streams)
869 { 946 {
870 pdf.setIgnoreXRefStreams(true); 947 pdf.setIgnoreXRefStreams(true);
@@ -1082,6 +1159,12 @@ int main(int argc, char* argv[]) @@ -1082,6 +1159,12 @@ int main(int argc, char* argv[])
1082 { 1159 {
1083 w.setSuppressOriginalObjectIDs(true); 1160 w.setSuppressOriginalObjectIDs(true);
1084 } 1161 }
  1162 + if (copy_encryption)
  1163 + {
  1164 + encryption_pdf.processFile(
  1165 + encryption_file, encryption_file_password);
  1166 + w.copyEncryptionParameters(encryption_pdf);
  1167 + }
1085 if (encrypt) 1168 if (encrypt)
1086 { 1169 {
1087 if (keylen == 40) 1170 if (keylen == 40)
qpdf/qtest/qpdf.test
@@ -1271,7 +1271,7 @@ $td-&gt;runtest(&quot;linearize and encrypt file&quot;, @@ -1271,7 +1271,7 @@ $td-&gt;runtest(&quot;linearize and encrypt file&quot;,
1271 $td->EXIT_STATUS => 0}); 1271 $td->EXIT_STATUS => 0});
1272 $td->runtest("check encryption", 1272 $td->runtest("check encryption",
1273 {$td->COMMAND => "qpdf --show-encryption --password=owner a.pdf", 1273 {$td->COMMAND => "qpdf --show-encryption --password=owner a.pdf",
1274 - $td->FILTER => "grep -v allowed"}, 1274 + $td->FILTER => "grep -v allowed | grep -v method"},
1275 {$td->STRING => "R = 4\nP = -4\nUser password = user\n", 1275 {$td->STRING => "R = 4\nP = -4\nUser password = user\n",
1276 $td->EXIT_STATUS => 0}, 1276 $td->EXIT_STATUS => 0},
1277 $td->NORMALIZE_NEWLINES); 1277 $td->NORMALIZE_NEWLINES);
@@ -1290,7 +1290,7 @@ $td-&gt;runtest(&quot;encrypt with AES&quot;, @@ -1290,7 +1290,7 @@ $td-&gt;runtest(&quot;encrypt with AES&quot;,
1290 {$td->STRING => "", $td->EXIT_STATUS => 0}); 1290 {$td->STRING => "", $td->EXIT_STATUS => 0});
1291 $td->runtest("check encryption", 1291 $td->runtest("check encryption",
1292 {$td->COMMAND => "qpdf --show-encryption a.pdf", 1292 {$td->COMMAND => "qpdf --show-encryption a.pdf",
1293 - $td->FILTER => "grep -v allowed"}, 1293 + $td->FILTER => "grep -v allowed | grep -v method"},
1294 {$td->STRING => "R = 4\nP = -4\nUser password = \n", 1294 {$td->STRING => "R = 4\nP = -4\nUser password = \n",
1295 $td->EXIT_STATUS => 0}, 1295 $td->EXIT_STATUS => 0},
1296 $td->NORMALIZE_NEWLINES); 1296 $td->NORMALIZE_NEWLINES);
@@ -1311,7 +1311,7 @@ $td-&gt;runtest(&quot;linearize with AES and object streams&quot;, @@ -1311,7 +1311,7 @@ $td-&gt;runtest(&quot;linearize with AES and object streams&quot;,
1311 {$td->STRING => "", $td->EXIT_STATUS => 0}); 1311 {$td->STRING => "", $td->EXIT_STATUS => 0});
1312 $td->runtest("check encryption", 1312 $td->runtest("check encryption",
1313 {$td->COMMAND => "qpdf --show-encryption a.pdf", 1313 {$td->COMMAND => "qpdf --show-encryption a.pdf",
1314 - $td->FILTER => "grep -v allowed"}, 1314 + $td->FILTER => "grep -v allowed | grep -v method"},
1315 {$td->STRING => "R = 4\nP = -4\nUser password = \n", 1315 {$td->STRING => "R = 4\nP = -4\nUser password = \n",
1316 $td->EXIT_STATUS => 0}, 1316 $td->EXIT_STATUS => 0},
1317 $td->NORMALIZE_NEWLINES); 1317 $td->NORMALIZE_NEWLINES);
@@ -1345,7 +1345,7 @@ $td-&gt;runtest(&quot;make sure there is no xref stream&quot;, @@ -1345,7 +1345,7 @@ $td-&gt;runtest(&quot;make sure there is no xref stream&quot;,
1345 $td->NORMALIZE_NEWLINES); 1345 $td->NORMALIZE_NEWLINES);
1346 1346
1347 # Look at some actual V4 files 1347 # Look at some actual V4 files
1348 -$n_tests += 10; 1348 +$n_tests += 14;
1349 foreach my $d (['--force-V4', 'V4'], 1349 foreach my $d (['--force-V4', 'V4'],
1350 ['--cleartext-metadata', 'V4-clearmeta'], 1350 ['--cleartext-metadata', 'V4-clearmeta'],
1351 ['--use-aes=y', 'V4-aes'], 1351 ['--use-aes=y', 'V4-aes'],
@@ -1359,6 +1359,10 @@ foreach my $d ([&#39;--force-V4&#39;, &#39;V4&#39;], @@ -1359,6 +1359,10 @@ foreach my $d ([&#39;--force-V4&#39;, &#39;V4&#39;],
1359 $td->runtest("check output", 1359 $td->runtest("check output",
1360 {$td->FILE => "a.pdf"}, 1360 {$td->FILE => "a.pdf"},
1361 {$td->FILE => "$out.pdf"}); 1361 {$td->FILE => "$out.pdf"});
  1362 + $td->runtest("show encryption",
  1363 + {$td->COMMAND => "qpdf --show-encryption a.pdf"},
  1364 + {$td->FILE => "$out-encryption.out", $td->EXIT_STATUS => 0},
  1365 + $td->NORMALIZE_NEWLINES);
1362 } 1366 }
1363 # Crypt Filter 1367 # Crypt Filter
1364 $td->runtest("decrypt with crypt filter", 1368 $td->runtest("decrypt with crypt filter",
@@ -1370,7 +1374,11 @@ $td-&gt;runtest(&quot;check output&quot;, @@ -1370,7 +1374,11 @@ $td-&gt;runtest(&quot;check output&quot;,
1370 {$td->FILE => 'decrypted-crypt-filter.pdf'}); 1374 {$td->FILE => 'decrypted-crypt-filter.pdf'});
1371 1375
1372 # Copy encryption parameters 1376 # Copy encryption parameters
1373 -$n_tests += 3; 1377 +$n_tests += 10;
  1378 +$td->runtest("create reference qdf",
  1379 + {$td->COMMAND =>
  1380 + "qpdf --qdf --no-original-object-ids minimal.pdf a.qdf"},
  1381 + {$td->STRING => "", $td->EXIT_STATUS => 0});
1374 $td->runtest("create encrypted file", 1382 $td->runtest("create encrypted file",
1375 {$td->COMMAND => 1383 {$td->COMMAND =>
1376 "qpdf --encrypt user owner 128 --use-aes=y --extract=n --" . 1384 "qpdf --encrypt user owner 128 --use-aes=y --extract=n --" .
@@ -1380,11 +1388,42 @@ $td-&gt;runtest(&quot;copy encryption parameters&quot;, @@ -1380,11 +1388,42 @@ $td-&gt;runtest(&quot;copy encryption parameters&quot;,
1380 {$td->COMMAND => "test_driver 30 minimal.pdf a.pdf"}, 1388 {$td->COMMAND => "test_driver 30 minimal.pdf a.pdf"},
1381 {$td->STRING => "test 30 done\n", $td->EXIT_STATUS => 0}, 1389 {$td->STRING => "test 30 done\n", $td->EXIT_STATUS => 0},
1382 $td->NORMALIZE_NEWLINES); 1390 $td->NORMALIZE_NEWLINES);
1383 -$td->runtest("checkout encryption", 1391 +$td->runtest("check output encryption",
1384 {$td->COMMAND => "qpdf --show-encryption b.pdf --password=owner"}, 1392 {$td->COMMAND => "qpdf --show-encryption b.pdf --password=owner"},
1385 {$td->FILE => "copied-encryption.out", 1393 {$td->FILE => "copied-encryption.out",
1386 $td->EXIT_STATUS => 0}, 1394 $td->EXIT_STATUS => 0},
1387 $td->NORMALIZE_NEWLINES); 1395 $td->NORMALIZE_NEWLINES);
  1396 +$td->runtest("convert to qdf",
  1397 + {$td->COMMAND =>
  1398 + "qpdf --qdf b.pdf b.qdf" .
  1399 + " --password=owner --no-original-object-ids"},
  1400 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  1401 +$td->runtest("compare qdf",
  1402 + {$td->COMMAND => "./diff-ignore-ID-version a.qdf b.qdf"},
  1403 + {$td->STRING => "okay\n", $td->EXIT_STATUS => 0},
  1404 + $td->NORMALIZE_NEWLINES);
  1405 +$td->runtest("copy encryption with qpdf",
  1406 + {$td->COMMAND =>
  1407 + "qpdf --copy-encryption=a.pdf".
  1408 + " --encryption-file-password=user" .
  1409 + " minimal.pdf c.pdf"},
  1410 + {$td->STRING => "", $td->EXIT_STATUS => 0},
  1411 + $td->NORMALIZE_NEWLINES);
  1412 +$td->runtest("check output encryption",
  1413 + {$td->COMMAND => "qpdf --show-encryption c.pdf --password=owner"},
  1414 + {$td->FILE => "copied-encryption.out",
  1415 + $td->EXIT_STATUS => 0},
  1416 + $td->NORMALIZE_NEWLINES);
  1417 +$td->runtest("convert to qdf",
  1418 + {$td->COMMAND =>
  1419 + "qpdf --qdf c.pdf c.qdf" .
  1420 + " --password=owner --no-original-object-ids"},
  1421 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  1422 +$td->runtest("compare qdf",
  1423 + {$td->COMMAND => "./diff-ignore-ID-version a.qdf c.qdf"},
  1424 + {$td->STRING => "okay\n", $td->EXIT_STATUS => 0},
  1425 + $td->NORMALIZE_NEWLINES);
  1426 +
1388 1427
1389 show_ntests(); 1428 show_ntests();
1390 # ---------- 1429 # ----------
@@ -1753,6 +1792,5 @@ sub get_md5_checksum @@ -1753,6 +1792,5 @@ sub get_md5_checksum
1753 1792
1754 sub cleanup 1793 sub cleanup
1755 { 1794 {
1756 - system("rm -rf *.ps *.pnm a.pdf a.qdf b.pdf b.qdf c.pdf" .  
1757 - " *.enc* tif1 tif2 tiff-cache"); 1795 + system("rm -rf *.ps *.pnm ?.pdf ?.qdf *.enc* tif1 tif2 tiff-cache");
1758 } 1796 }
qpdf/qtest/qpdf/V4-aes-clearmeta-encryption.out 0 → 100644
  1 +R = 4
  2 +P = -4
  3 +User password =
  4 +extract for accessibility: allowed
  5 +extract for any purpose: allowed
  6 +print low resolution: allowed
  7 +print high resolution: allowed
  8 +modify document assembly: allowed
  9 +modify forms: allowed
  10 +modify annotations: allowed
  11 +modify other: allowed
  12 +modify anything: allowed
  13 +stream encryption method: AESv2
  14 +string encryption method: AESv2
  15 +file encryption method: AESv2
qpdf/qtest/qpdf/V4-aes-encryption.out 0 → 100644
  1 +R = 4
  2 +P = -4
  3 +User password =
  4 +extract for accessibility: allowed
  5 +extract for any purpose: allowed
  6 +print low resolution: allowed
  7 +print high resolution: allowed
  8 +modify document assembly: allowed
  9 +modify forms: allowed
  10 +modify annotations: allowed
  11 +modify other: allowed
  12 +modify anything: allowed
  13 +stream encryption method: AESv2
  14 +string encryption method: AESv2
  15 +file encryption method: AESv2
qpdf/qtest/qpdf/V4-clearmeta-encryption.out 0 → 100644
  1 +R = 4
  2 +P = -4
  3 +User password =
  4 +extract for accessibility: allowed
  5 +extract for any purpose: allowed
  6 +print low resolution: allowed
  7 +print high resolution: allowed
  8 +modify document assembly: allowed
  9 +modify forms: allowed
  10 +modify annotations: allowed
  11 +modify other: allowed
  12 +modify anything: allowed
  13 +stream encryption method: RC4
  14 +string encryption method: RC4
  15 +file encryption method: RC4
qpdf/qtest/qpdf/V4-encryption.out 0 → 100644
  1 +R = 4
  2 +P = -4
  3 +User password =
  4 +extract for accessibility: allowed
  5 +extract for any purpose: allowed
  6 +print low resolution: allowed
  7 +print high resolution: allowed
  8 +modify document assembly: allowed
  9 +modify forms: allowed
  10 +modify annotations: allowed
  11 +modify other: allowed
  12 +modify anything: allowed
  13 +stream encryption method: RC4
  14 +string encryption method: RC4
  15 +file encryption method: RC4
qpdf/qtest/qpdf/copied-encryption.out
@@ -10,3 +10,6 @@ modify forms: allowed @@ -10,3 +10,6 @@ modify forms: allowed
10 modify annotations: allowed 10 modify annotations: allowed
11 modify other: allowed 11 modify other: allowed
12 modify anything: allowed 12 modify anything: allowed
  13 +stream encryption method: AESv2
  14 +string encryption method: AESv2
  15 +file encryption method: AESv2
qpdf/qtest/qpdf/diff-ignore-ID-version 0 → 100755
  1 +#!/bin/sh
  2 +lines=$(expr + $(diff $1 $2 | egrep '^[<>]' | \
  3 + egrep -v '/ID' | egrep -v '%PDF-' | wc -l))
  4 +if [ "$lines" = "0" ]; then
  5 + echo okay
  6 +else
  7 + diff -a -U 0 $1 $2
  8 +fi
qpdf/test_driver.cc
@@ -58,12 +58,17 @@ class Provider: public QPDFObjectHandle::StreamDataProvider @@ -58,12 +58,17 @@ class Provider: public QPDFObjectHandle::StreamDataProvider
58 bool bad_length; 58 bool bad_length;
59 }; 59 };
60 60
61 -static void checkPageContents(QPDFObjectHandle page,  
62 - std::string const& wanted_string) 61 +static std::string getPageContents(QPDFObjectHandle page)
63 { 62 {
64 PointerHolder<Buffer> b1 = 63 PointerHolder<Buffer> b1 =
65 page.getKey("/Contents").getStreamData(); 64 page.getKey("/Contents").getStreamData();
66 - std::string contents = std::string((char *)(b1->getBuffer())); 65 + return std::string((char *)(b1->getBuffer()), b1->getSize()) + "\0";
  66 +}
  67 +
  68 +static void checkPageContents(QPDFObjectHandle page,
  69 + std::string const& wanted_string)
  70 +{
  71 + std::string contents = getPageContents(page);
67 if (contents.find(wanted_string) == std::string::npos) 72 if (contents.find(wanted_string) == std::string::npos)
68 { 73 {
69 std::cout << "didn't find " << wanted_string << " in " 74 std::cout << "didn't find " << wanted_string << " in "
@@ -1030,10 +1035,24 @@ void runtest(int n, char const* filename1, char const* filename2) @@ -1030,10 +1035,24 @@ void runtest(int n, char const* filename1, char const* filename2)
1030 QPDF encrypted; 1035 QPDF encrypted;
1031 encrypted.processFile(filename2, "user"); 1036 encrypted.processFile(filename2, "user");
1032 QPDFWriter w(pdf, "b.pdf"); 1037 QPDFWriter w(pdf, "b.pdf");
1033 - w.setStaticID(true);  
1034 w.setStreamDataMode(qpdf_s_preserve); 1038 w.setStreamDataMode(qpdf_s_preserve);
1035 w.copyEncryptionParameters(encrypted); 1039 w.copyEncryptionParameters(encrypted);
1036 w.write(); 1040 w.write();
  1041 +
  1042 + // Make sure the contents are actually the same
  1043 + QPDF final;
  1044 + final.processFile("b.pdf", "user");
  1045 + std::vector<QPDFObjectHandle> pages = pdf.getAllPages();
  1046 + std::string orig_contents = getPageContents(pages[0]);
  1047 + pages = final.getAllPages();
  1048 + std::string new_contents = getPageContents(pages[0]);
  1049 + if (orig_contents != new_contents)
  1050 + {
  1051 + std::cout << "oops -- page contents don't match" << std::endl
  1052 + << "original:\n" << orig_contents
  1053 + << "new:\n" << new_contents
  1054 + << std::endl;
  1055 + }
1037 } 1056 }
1038 else 1057 else
1039 { 1058 {