Commit 3a055d5b6af5e3eab1eac33c77ccb1c8a386867f

Authored by m-holger
Committed by GitHub
2 parents a5ec9dc0 9faffb74

Merge pull request #1495 from m-holger/writer

Simplify QPDFWriter pipeline management
libqpdf/Pl_AES_PDF.cc
... ... @@ -9,25 +9,18 @@
9 9  
10 10 bool Pl_AES_PDF::use_static_iv = false;
11 11  
12   -Pl_AES_PDF::Pl_AES_PDF(
13   - char const* identifier,
14   - Pipeline* next,
15   - bool encrypt,
16   - unsigned char const* key,
17   - size_t key_bytes) :
  12 +Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, bool encrypt, std::string key) :
18 13 Pipeline(identifier, next),
  14 + key(key),
19 15 crypto(QPDFCryptoProvider::getImpl()),
20   - encrypt(encrypt),
21   - key_bytes(key_bytes)
  16 + encrypt(encrypt)
22 17 {
23 18 if (!next) {
24 19 throw std::logic_error("Attempt to create Pl_AES_PDF with nullptr as next");
25 20 }
26   - if (!(key_bytes == 32 || key_bytes == 16)) {
  21 + if (!(key.size() == 32 || key.size() == 16)) {
27 22 throw std::runtime_error("unsupported key length");
28 23 }
29   - this->key = std::make_unique<unsigned char[]>(key_bytes);
30   - std::memcpy(this->key.get(), key, key_bytes);
31 24 std::memset(this->inbuf, 0, this->buf_size);
32 25 std::memset(this->outbuf, 0, this->buf_size);
33 26 std::memset(this->cbc_block, 0, this->buf_size);
... ... @@ -170,7 +163,12 @@ Pl_AES_PDF::flush(bool strip_padding)
170 163 return_after_init = true;
171 164 }
172 165 }
173   - crypto->rijndael_init(encrypt, key.get(), key_bytes, cbc_mode, cbc_block);
  166 + crypto->rijndael_init(
  167 + encrypt,
  168 + reinterpret_cast<const unsigned char*>(key.data()),
  169 + key.size(),
  170 + cbc_mode,
  171 + cbc_block);
174 172 if (return_after_init) {
175 173 return;
176 174 }
... ...
libqpdf/Pl_RC4.cc
... ... @@ -2,15 +2,10 @@
2 2  
3 3 #include <qpdf/QUtil.hh>
4 4  
5   -Pl_RC4::Pl_RC4(
6   - char const* identifier,
7   - Pipeline* next,
8   - unsigned char const* key_data,
9   - int key_len,
10   - size_t out_bufsize) :
  5 +Pl_RC4::Pl_RC4(char const* identifier, Pipeline* next, std::string key, size_t out_bufsize) :
11 6 Pipeline(identifier, next),
12 7 out_bufsize(out_bufsize),
13   - rc4(key_data, key_len)
  8 + rc4(reinterpret_cast<unsigned char const*>(key.data()), static_cast<int>(key.size()))
14 9 {
15 10 if (!next) {
16 11 throw std::logic_error("Attempt to create Pl_RC4 with nullptr as next");
... ...
libqpdf/QPDFWriter.cc
... ... @@ -141,20 +141,20 @@ namespace
141 141 }
142 142  
143 143 void
144   - activate(Popper& pp, std::unique_ptr<pl::Link> link)
  144 + activate(Popper& pp, std::unique_ptr<Pipeline> next)
145 145 {
146 146 count_buffer.clear();
147   - activate(pp, false, &count_buffer, std::move(link));
  147 + activate(pp, false, &count_buffer, std::move(next));
148 148 }
149 149  
150 150 Popper
151 151 activate(
152 152 bool discard = false,
153 153 std::string* str = nullptr,
154   - std::unique_ptr<pl::Link> link = nullptr)
  154 + std::unique_ptr<Pipeline> next = nullptr)
155 155 {
156 156 Popper pp{*this};
157   - activate(pp, discard, str, std::move(link));
  157 + activate(pp, discard, str, std::move(next));
158 158 return pp;
159 159 }
160 160  
... ... @@ -163,11 +163,11 @@ namespace
163 163 Popper& pp,
164 164 bool discard = false,
165 165 std::string* str = nullptr,
166   - std::unique_ptr<pl::Link> link = nullptr)
  166 + std::unique_ptr<Pipeline> next = nullptr)
167 167 {
168 168 std::unique_ptr<pl::Count> c;
169   - if (link) {
170   - c = std::make_unique<pl::Count>(++last_id, count_buffer, std::move(link));
  169 + if (next) {
  170 + c = std::make_unique<pl::Count>(++last_id, count_buffer, std::move(next));
171 171 } else if (discard) {
172 172 c = std::make_unique<pl::Count>(++last_id, nullptr);
173 173 } else if (!str) {
... ... @@ -1109,18 +1109,9 @@ QPDFWriter::write_encrypted(std::string_view str)
1109 1109 if (!(m->encryption && !m->cur_data_key.empty())) {
1110 1110 write(str);
1111 1111 } else if (m->encrypt_use_aes) {
1112   - write(
1113   - pl::pipe<Pl_AES_PDF>(
1114   - str,
1115   - true,
1116   - QUtil::unsigned_char_pointer(m->cur_data_key),
1117   - m->cur_data_key.length()));
  1112 + write(pl::pipe<Pl_AES_PDF>(str, true, m->cur_data_key));
1118 1113 } else {
1119   - write(
1120   - pl::pipe<Pl_RC4>(
1121   - str,
1122   - QUtil::unsigned_char_pointer(m->cur_data_key),
1123   - QIntC::to_int(m->cur_data_key.length())));
  1114 + write(pl::pipe<Pl_RC4>(str, m->cur_data_key));
1124 1115 }
1125 1116  
1126 1117 return *this;
... ... @@ -1654,12 +1645,7 @@ QPDFWriter::unparseObject(
1654 1645 val = object.getStringValue();
1655 1646 if (m->encrypt_use_aes) {
1656 1647 Pl_Buffer bufpl("encrypted string");
1657   - Pl_AES_PDF pl(
1658   - "aes encrypt string",
1659   - &bufpl,
1660   - true,
1661   - QUtil::unsigned_char_pointer(m->cur_data_key),
1662   - m->cur_data_key.length());
  1648 + Pl_AES_PDF pl("aes encrypt string", &bufpl, true, m->cur_data_key);
1663 1649 pl.writeString(val);
1664 1650 pl.finish();
1665 1651 val = QPDF_String(bufpl.getString()).unparse(true);
... ... @@ -1768,7 +1754,6 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1768 1754 }
1769 1755 }
1770 1756 {
1771   - auto pp_ostream = m->pipeline_stack.popper();
1772 1757 // Adjust offsets to skip over comment before first object
1773 1758 first = offsets.at(0);
1774 1759 for (auto& iter: offsets) {
... ... @@ -1783,18 +1768,15 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1783 1768 }
1784 1769  
1785 1770 // Set up a stream to write the stream data into a buffer.
1786   - if (compressed) {
1787   - m->pipeline_stack.activate(
1788   - pp_ostream,
1789   - pl::create<Pl_Flate>(
1790   - pl::create<pl::String>(stream_buffer_pass2), Pl_Flate::a_deflate));
1791   - } else {
1792   - m->pipeline_stack.activate(pp_ostream, stream_buffer_pass2);
1793   - }
  1771 + auto pp_ostream = m->pipeline_stack.activate(stream_buffer_pass2);
  1772 +
1794 1773 writeObjectStreamOffsets(offsets, first_obj);
1795 1774 write(stream_buffer_pass1);
1796 1775 stream_buffer_pass1.clear();
1797 1776 stream_buffer_pass1.shrink_to_fit();
  1777 + if (compressed) {
  1778 + stream_buffer_pass2 = pl::pipe<Pl_Flate>(stream_buffer_pass2, Pl_Flate::a_deflate);
  1779 + }
1798 1780 }
1799 1781  
1800 1782 // Write the object
... ... @@ -2528,20 +2510,7 @@ QPDFWriter::writeXRefStream(
2528 2510 std::string xref_data;
2529 2511 const bool compressed = m->compress_streams && !m->qdf_mode;
2530 2512 {
2531   - auto pp_xref = m->pipeline_stack.popper();
2532   - if (compressed) {
2533   - m->pipeline_stack.clear_buffer();
2534   - auto link = pl::create<pl::String>(xref_data);
2535   - if (!skip_compression) {
2536   - // Write the stream dictionary for compression but don't actually compress. This
2537   - // helps us with computation of padding for pass 1 of linearization.
2538   - link = pl::create<Pl_Flate>(std::move(link), Pl_Flate::a_deflate);
2539   - }
2540   - m->pipeline_stack.activate(
2541   - pp_xref, pl::create<Pl_PNGFilter>(std::move(link), Pl_PNGFilter::a_encode, esize));
2542   - } else {
2543   - m->pipeline_stack.activate(pp_xref, xref_data);
2544   - }
  2513 + auto pp_xref = m->pipeline_stack.activate(xref_data);
2545 2514  
2546 2515 for (int i = first; i <= last; ++i) {
2547 2516 QPDFXRefEntry& e = m->new_obj[i].xref;
... ... @@ -2577,6 +2546,15 @@ QPDFWriter::writeXRefStream(
2577 2546 }
2578 2547 }
2579 2548  
  2549 + if (compressed) {
  2550 + xref_data = pl::pipe<Pl_PNGFilter>(xref_data, Pl_PNGFilter::a_encode, esize);
  2551 + if (!skip_compression) {
  2552 + // Write the stream dictionary for compression but don't actually compress. This
  2553 + // helps us with computation of padding for pass 1 of linearization.
  2554 + xref_data = pl::pipe<Pl_Flate>(xref_data, Pl_Flate::a_deflate);
  2555 + }
  2556 + }
  2557 +
2580 2558 openObject(xref_id);
2581 2559 write("<<").write_qdf("\n ").write(" /Type /XRef").write_qdf("\n ");
2582 2560 write(" /Length ").write(xref_data.size());
... ... @@ -2743,9 +2721,7 @@ QPDFWriter::writeLinearized()
2743 2721 lin_pass1_file = QUtil::safe_fopen(m->lin_pass1_filename.c_str(), "wb");
2744 2722 m->pipeline_stack.activate(
2745 2723 pp_pass1,
2746   - std::make_unique<pl::Link>(
2747   - nullptr,
2748   - std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file)));
  2724 + std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file));
2749 2725 } else {
2750 2726 m->pipeline_stack.activate(pp_pass1, true);
2751 2727 }
... ...
libqpdf/QPDF_encryption.cc
... ... @@ -230,8 +230,7 @@ process_with_aes(
230 230 size_t iv_length = 0)
231 231 {
232 232 Pl_Buffer buffer("buffer");
233   - Pl_AES_PDF aes(
234   - "aes", &buffer, encrypt, QUtil::unsigned_char_pointer(key), QIntC::to_uint(key.length()));
  233 + Pl_AES_PDF aes("aes", &buffer, encrypt, key);
235 234 if (iv) {
236 235 aes.setIV(iv, iv_length);
237 236 } else {
... ... @@ -902,12 +901,7 @@ QPDF::decryptString(std::string&amp; str, QPDFObjGen og)
902 901 if (use_aes) {
903 902 QTC::TC("qpdf", "QPDF_encryption aes decode string");
904 903 Pl_Buffer bufpl("decrypted string");
905   - Pl_AES_PDF pl(
906   - "aes decrypt string",
907   - &bufpl,
908   - false,
909   - QUtil::unsigned_char_pointer(key),
910   - key.length());
  904 + Pl_AES_PDF pl("aes decrypt string", &bufpl, false, key);
911 905 pl.writeString(str);
912 906 pl.finish();
913 907 str = bufpl.getString();
... ... @@ -1028,19 +1022,11 @@ QPDF::decryptStream(
1028 1022 std::string key = getKeyForObject(encp, og, use_aes);
1029 1023 if (use_aes) {
1030 1024 QTC::TC("qpdf", "QPDF_encryption aes decode stream");
1031   - decrypt_pipeline = std::make_unique<Pl_AES_PDF>(
1032   - "AES stream decryption",
1033   - pipeline,
1034   - false,
1035   - QUtil::unsigned_char_pointer(key),
1036   - key.length());
  1025 + decrypt_pipeline =
  1026 + std::make_unique<Pl_AES_PDF>("AES stream decryption", pipeline, false, key);
1037 1027 } else {
1038 1028 QTC::TC("qpdf", "QPDF_encryption rc4 decode stream");
1039   - decrypt_pipeline = std::make_unique<Pl_RC4>(
1040   - "RC4 stream decryption",
1041   - pipeline,
1042   - QUtil::unsigned_char_pointer(key),
1043   - toI(key.length()));
  1029 + decrypt_pipeline = std::make_unique<Pl_RC4>("RC4 stream decryption", pipeline, key);
1044 1030 }
1045 1031 pipeline = decrypt_pipeline.get();
1046 1032 }
... ...
libqpdf/QPDF_linearization.cc
... ... @@ -1753,21 +1753,18 @@ QPDF::generateHintStream(
1753 1753  
1754 1754 // Write the hint stream itself into a compressed memory buffer. Write through a counter so we
1755 1755 // can get offsets.
1756   - std::string b;
1757   - auto c = compressed
1758   - ? std::make_unique<pl::Count>(
1759   - 0, b, pl::create<Pl_Flate>(pl::create<pl::String>(hint_buffer), Pl_Flate::a_deflate))
1760   - : std::make_unique<pl::Count>(0, hint_buffer);
1761   -
1762   - BitWriter w(c.get());
  1756 + pl::Count c(0, hint_buffer);
  1757 + BitWriter w(&c);
1763 1758  
1764 1759 writeHPageOffset(w);
1765   - S = toI(c->getCount());
  1760 + S = toI(c.getCount());
1766 1761 writeHSharedObject(w);
1767 1762 O = 0;
1768 1763 if (m->outline_hints.nobjects > 0) {
1769   - O = toI(c->getCount());
  1764 + O = toI(c.getCount());
1770 1765 writeHGeneric(w, m->outline_hints);
1771 1766 }
1772   - c->finish();
  1767 + if (compressed) {
  1768 + hint_buffer = pl::pipe<Pl_Flate>(hint_buffer, Pl_Flate::a_deflate);
  1769 + }
1773 1770 }
... ...
libqpdf/qpdf/Pipeline_private.hh
... ... @@ -8,35 +8,6 @@
8 8  
9 9 namespace qpdf::pl
10 10 {
11   - struct Link
12   - {
13   - Link(std::unique_ptr<Link> next_link, std::unique_ptr<Pipeline> next_pl) :
14   - next_link(std::move(next_link)),
15   - next_pl(std::move(next_pl))
16   - {
17   - }
18   -
19   - std::unique_ptr<Link> next_link{nullptr};
20   - std::unique_ptr<Pipeline> next_pl{nullptr};
21   - };
22   -
23   - template <typename P, typename... Args>
24   - std::unique_ptr<Link>
25   - create(Args&&... args)
26   - {
27   - return std::make_unique<Link>(
28   - nullptr, std::make_unique<P>("", nullptr, std::forward<Args>(args)...));
29   - }
30   -
31   - template <typename P, typename... Args>
32   - std::unique_ptr<Link>
33   - create(std::unique_ptr<Link> link, Args&&... args)
34   - {
35   - auto* next = link->next_pl.get();
36   - return std::make_unique<Link>(
37   - std::move(link), std::make_unique<P>("", next, std::forward<Args>(args)...));
38   - }
39   -
40 11 class String final: public Pipeline
41 12 {
42 13 public:
... ... @@ -83,20 +54,10 @@ namespace qpdf::pl
83 54 {
84 55 }
85 56  
86   - // Count the number of characters written. If 'next' is not set, the content written will be
87   - // discarded.
88   - Count(unsigned long id, std::unique_ptr<Link> link) :
89   - Pipeline("", link ? link->next_pl.get() : nullptr),
90   - link(std::move(link)),
91   - id_(id),
92   - pass_immediately_to_next(link)
93   - {
94   - }
95   -
96 57 // Write to 'str'. If 'next' is set, 'str' will be written to 'next' when 'finish' is
97 58 // called.
98   - Count(unsigned long id, std::string& str, std::unique_ptr<Link> link = nullptr) :
99   - Pipeline("", link ? link->next_pl.get() : nullptr),
  59 + Count(unsigned long id, std::string& str, std::unique_ptr<Pipeline> link = nullptr) :
  60 + Pipeline("", link.get()),
100 61 str(&str),
101 62 link(std::move(link)),
102 63 id_(id)
... ... @@ -169,7 +130,7 @@ namespace qpdf::pl
169 130 private:
170 131 qpdf_offset_t count{0};
171 132 std::string* str{nullptr};
172   - std::unique_ptr<Link> link{nullptr};
  133 + std::unique_ptr<Pipeline> link{nullptr};
173 134 unsigned long id_{0};
174 135 bool pass_immediately_to_next{false};
175 136 };
... ...
libqpdf/qpdf/Pl_AES_PDF.hh
... ... @@ -11,12 +11,7 @@ class Pl_AES_PDF final: public Pipeline
11 11 {
12 12 public:
13 13 // key should be a pointer to key_bytes bytes of data
14   - Pl_AES_PDF(
15   - char const* identifier,
16   - Pipeline* next,
17   - bool encrypt,
18   - unsigned char const* key,
19   - size_t key_bytes);
  14 + Pl_AES_PDF(char const* identifier, Pipeline* next, bool encrypt, std::string key);
20 15 ~Pl_AES_PDF() final = default;
21 16  
22 17 void write(unsigned char const* data, size_t len) final;
... ... @@ -42,13 +37,12 @@ class Pl_AES_PDF final: public Pipeline
42 37 static unsigned int const buf_size = QPDFCryptoImpl::rijndael_buf_size;
43 38 static bool use_static_iv;
44 39  
  40 + std::string key;
45 41 std::shared_ptr<QPDFCryptoImpl> crypto;
46 42 bool encrypt;
47 43 bool cbc_mode{true};
48 44 bool first{true};
49 45 size_t offset{0}; // offset into memory buffer
50   - std::unique_ptr<unsigned char[]> key;
51   - size_t key_bytes{0};
52 46 unsigned char inbuf[buf_size];
53 47 unsigned char outbuf[buf_size];
54 48 unsigned char cbc_block[buf_size];
... ...
libqpdf/qpdf/Pl_RC4.hh
... ... @@ -12,11 +12,7 @@ class Pl_RC4 final: public Pipeline
12 12  
13 13 // key_len of -1 means treat key_data as a null-terminated string
14 14 Pl_RC4(
15   - char const* identifier,
16   - Pipeline* next,
17   - unsigned char const* key_data,
18   - int key_len = -1,
19   - size_t out_bufsize = def_bufsize);
  15 + char const* identifier, Pipeline* next, std::string key, size_t out_bufsize = def_bufsize);
20 16 ~Pl_RC4() final = default;
21 17  
22 18 void write(unsigned char const* data, size_t len) final;
... ...
libtests/aes.cc
... ... @@ -84,8 +84,9 @@ main(int argc, char* argv[])
84 84 key[i / 2] = static_cast<unsigned char>(val);
85 85 }
86 86  
  87 + std::string keystr(reinterpret_cast<char const*>(key), keylen);
87 88 auto* out = new Pl_StdioFile("stdout", outfile);
88   - auto* aes = new Pl_AES_PDF("aes_128_cbc", out, encrypt, key, keylen);
  89 + auto* aes = new Pl_AES_PDF("aes_128_cbc", out, encrypt, keystr);
89 90 delete[] key;
90 91 key = nullptr;
91 92 if (!cbc_mode) {
... ...
libtests/rc4.cc
... ... @@ -58,7 +58,8 @@ main(int argc, char* argv[])
58 58 FILE* outfile = QUtil::safe_fopen(outfilename, "wb");
59 59 auto* out = new Pl_StdioFile("stdout", outfile);
60 60 // Use a small buffer size (64) for testing
61   - auto* rc4 = new Pl_RC4("rc4", out, key, QIntC::to_int(keylen), 64U);
  61 + std::string keystr(reinterpret_cast<char const*>(key), keylen);
  62 + auto* rc4 = new Pl_RC4("rc4", out, keystr, 64U);
62 63 delete[] key;
63 64  
64 65 // 64 < buffer size < 512, buffer_size is not a power of 2 for testing
... ...