From 93ac1695a4b79f3d5b71e2d57ed876c28866d2c9 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 29 Dec 2012 19:00:05 -0500 Subject: Support files with only attachments encrypted Test cases added in a future commit since they depend on /R=6 support. --- libqpdf/QPDF_Stream.cc | 250 +++++++++++++++++++++++++++++-------------------- 1 file changed, 148 insertions(+), 102 deletions(-) (limited to 'libqpdf/QPDF_Stream.cc') diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 970ee58b..88b8e8ff 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -90,6 +90,80 @@ QPDF_Stream::getRawStreamData() return buf.getBuffer(); } +bool +QPDF_Stream::understandDecodeParams( + std::string const& filter, QPDFObjectHandle decode_obj, + int& predictor, int& columns, bool& early_code_change) +{ + bool filterable = true; + std::set keys = decode_obj.getKeys(); + for (std::set::iterator iter = keys.begin(); + iter != keys.end(); ++iter) + { + std::string const& key = *iter; + if ((filter == "/FlateDecode") && (key == "/Predictor")) + { + QPDFObjectHandle predictor_obj = decode_obj.getKey(key); + if (predictor_obj.isInteger()) + { + predictor = predictor_obj.getIntValue(); + if (! ((predictor == 1) || (predictor == 12))) + { + filterable = false; + } + } + else + { + filterable = false; + } + } + else if ((filter == "/LZWDecode") && (key == "/EarlyChange")) + { + QPDFObjectHandle earlychange_obj = decode_obj.getKey(key); + if (earlychange_obj.isInteger()) + { + int earlychange = earlychange_obj.getIntValue(); + early_code_change = (earlychange == 1); + if (! ((earlychange == 0) || (earlychange == 1))) + { + filterable = false; + } + } + else + { + filterable = false; + } + } + else if (key == "/Columns") + { + QPDFObjectHandle columns_obj = decode_obj.getKey(key); + if (columns_obj.isInteger()) + { + columns = columns_obj.getIntValue(); + } + else + { + filterable = false; + } + } + else if ((filter == "/Crypt") && + (((key == "/Type") || (key == "/Name")) && + (decode_obj.getKey("/Type").isNull() || + (decode_obj.getKey("/Type").isName() && + (decode_obj.getKey("/Type").getName() == + "/CryptFilterDecodeParms"))))) + { + // we handle this in decryptStream + } + else + { + filterable = false; + } + } + + return filterable; +} + bool QPDF_Stream::filterable(std::vector& filters, int& predictor, int& columns, @@ -110,106 +184,6 @@ QPDF_Stream::filterable(std::vector& filters, filter_abbreviations["/DCT"] = "/DCTDecode"; } - // Initialize values to their defaults as per the PDF spec - predictor = 1; - columns = 0; - early_code_change = true; - - bool filterable = true; - - // See if we can support any decode parameters that are specified. - - QPDFObjectHandle decode_obj = - this->stream_dict.getKey("/DecodeParms"); - if (decode_obj.isNull()) - { - // no problem - } - else if (decode_obj.isDictionary()) - { - std::set keys = decode_obj.getKeys(); - for (std::set::iterator iter = keys.begin(); - iter != keys.end(); ++iter) - { - std::string const& key = *iter; - if (key == "/Predictor") - { - QPDFObjectHandle predictor_obj = decode_obj.getKey(key); - if (predictor_obj.isInteger()) - { - predictor = predictor_obj.getIntValue(); - if (! ((predictor == 1) || (predictor == 12))) - { - filterable = false; - } - } - else - { - filterable = false; - } - } - else if (key == "/EarlyChange") - { - QPDFObjectHandle earlychange_obj = decode_obj.getKey(key); - if (earlychange_obj.isInteger()) - { - int earlychange = earlychange_obj.getIntValue(); - early_code_change = (earlychange == 1); - if (! ((earlychange == 0) || (earlychange == 1))) - { - filterable = false; - } - } - else - { - filterable = false; - } - } - else if (key == "/Columns") - { - QPDFObjectHandle columns_obj = decode_obj.getKey(key); - if (columns_obj.isInteger()) - { - columns = columns_obj.getIntValue(); - } - else - { - filterable = false; - } - } - else if (((key == "/Type") || (key == "/Name")) && - decode_obj.getKey("/Type").isName() && - (decode_obj.getKey("/Type").getName() == - "/CryptFilterDecodeParms")) - { - // we handle this in decryptStream - } - else - { - filterable = false; - } - } - } - else - { - // Ignore for now -- some filter types, like CCITTFaxDecode, - // use types other than dictionary for this. - QTC::TC("qpdf", "QPDF_Stream ignore non-dictionary DecodeParms"); - - filterable = false; - } - - if ((predictor > 1) && (columns == 0)) - { - // invalid - filterable = false; - } - - if (! filterable) - { - return false; - } - // Check filters QPDFObjectHandle filter_obj = this->stream_dict.getKey("/Filter"); @@ -254,8 +228,7 @@ QPDF_Stream::filterable(std::vector& filters, "stream filter type is not name or array"); } - // `filters' now contains a list of filters to be applied in - // order. See which ones we can support. + bool filterable = true; for (std::vector::iterator iter = filters.begin(); iter != filters.end(); ++iter) @@ -278,6 +251,79 @@ QPDF_Stream::filterable(std::vector& filters, } } + if (! filterable) + { + return false; + } + + // `filters' now contains a list of filters to be applied in + // order. See which ones we can support. + + // Initialize values to their defaults as per the PDF spec + predictor = 1; + columns = 0; + early_code_change = true; + + // See if we can support any decode parameters that are specified. + + QPDFObjectHandle decode_obj = this->stream_dict.getKey("/DecodeParms"); + std::vector decode_parms; + if (decode_obj.isArray()) + { + for (int i = 0; i < decode_obj.getArrayNItems(); ++i) + { + decode_parms.push_back(decode_obj.getArrayItem(i)); + } + } + else + { + for (unsigned int i = 0; i < filters.size(); ++i) + { + decode_parms.push_back(decode_obj); + } + } + + if (decode_parms.size() != filters.size()) + { + throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(), + "", this->offset, + "stream /DecodeParms length is" + " inconsistent with filters"); + } + + for (unsigned int i = 0; i < filters.size(); ++i) + { + QPDFObjectHandle decode_item = decode_parms[i]; + if (decode_item.isNull()) + { + // okay + } + else if (decode_item.isDictionary()) + { + if (! understandDecodeParams( + filters[i], decode_item, + predictor, columns, early_code_change)) + { + filterable = false; + } + } + else + { + filterable = false; + } + } + + if ((predictor > 1) && (columns == 0)) + { + // invalid + filterable = false; + } + + if (! filterable) + { + return false; + } + return filterable; } -- cgit v1.2.3-54-g00ecf