Commit fbbb0ee0167a9013c3a712c790a9772075aed2ad
1 parent
7588cac2
Make a static version of QPDF::pipeStreamData
This is in preparation of being able to pipe a stream's data without keeping a copy of its containing qpdf object.
Showing
4 changed files
with
134 additions
and
83 deletions
include/qpdf/QPDF.hh
| ... | ... | @@ -692,6 +692,30 @@ class QPDF |
| 692 | 692 | }; |
| 693 | 693 | friend class ResolveRecorder; |
| 694 | 694 | |
| 695 | + class EncryptionParameters | |
| 696 | + { | |
| 697 | + friend class QPDF; | |
| 698 | + public: | |
| 699 | + EncryptionParameters(); | |
| 700 | + | |
| 701 | + private: | |
| 702 | + bool encrypted; | |
| 703 | + bool encryption_initialized; | |
| 704 | + int encryption_V; | |
| 705 | + int encryption_R; | |
| 706 | + bool encrypt_metadata; | |
| 707 | + std::map<std::string, encryption_method_e> crypt_filters; | |
| 708 | + encryption_method_e cf_stream; | |
| 709 | + encryption_method_e cf_string; | |
| 710 | + encryption_method_e cf_file; | |
| 711 | + std::string provided_password; | |
| 712 | + std::string user_password; | |
| 713 | + std::string encryption_key; | |
| 714 | + std::string cached_object_encryption_key; | |
| 715 | + int cached_key_objid; | |
| 716 | + int cached_key_generation; | |
| 717 | + }; | |
| 718 | + | |
| 695 | 719 | void parse(char const* password); |
| 696 | 720 | void warn(QPDFExc const& e); |
| 697 | 721 | void setTrailer(QPDFObjectHandle obj); |
| ... | ... | @@ -735,6 +759,16 @@ class QPDF |
| 735 | 759 | Pipeline* pipeline, |
| 736 | 760 | bool suppress_warnings, |
| 737 | 761 | bool will_retry); |
| 762 | + static bool pipeStreamData(PointerHolder<QPDF::EncryptionParameters> encp, | |
| 763 | + PointerHolder<InputSource> file, | |
| 764 | + QPDF& qpdf_for_warning, | |
| 765 | + int objid, int generation, | |
| 766 | + qpdf_offset_t offset, size_t length, | |
| 767 | + QPDFObjectHandle dict, | |
| 768 | + bool is_attachment_stream, | |
| 769 | + Pipeline* pipeline, | |
| 770 | + bool suppress_warnings, | |
| 771 | + bool will_retry); | |
| 738 | 772 | |
| 739 | 773 | // For QPDFWriter: |
| 740 | 774 | |
| ... | ... | @@ -776,9 +810,12 @@ class QPDF |
| 776 | 810 | bool check_duplicate); |
| 777 | 811 | |
| 778 | 812 | // methods to support encryption -- implemented in QPDF_encryption.cc |
| 779 | - encryption_method_e interpretCF(QPDFObjectHandle); | |
| 813 | + static encryption_method_e interpretCF( | |
| 814 | + PointerHolder<EncryptionParameters> encp, QPDFObjectHandle); | |
| 780 | 815 | void initializeEncryption(); |
| 781 | - std::string getKeyForObject(int objid, int generation, bool use_aes); | |
| 816 | + static std::string getKeyForObject( | |
| 817 | + PointerHolder<EncryptionParameters> encp, | |
| 818 | + int objid, int generation, bool use_aes); | |
| 782 | 819 | void decryptString(std::string&, int objid, int generation); |
| 783 | 820 | static std::string compute_encryption_key_from_password( |
| 784 | 821 | std::string const& password, EncryptionData const& data); |
| ... | ... | @@ -787,9 +824,12 @@ class QPDF |
| 787 | 824 | static std::string recover_encryption_key_with_password( |
| 788 | 825 | std::string const& password, EncryptionData const& data, |
| 789 | 826 | bool& perms_valid); |
| 790 | - void decryptStream( | |
| 791 | - Pipeline*& pipeline, int objid, int generation, | |
| 792 | - QPDFObjectHandle& stream_dict, | |
| 827 | + static void decryptStream( | |
| 828 | + PointerHolder<EncryptionParameters> encp, | |
| 829 | + PointerHolder<InputSource> file, | |
| 830 | + QPDF& qpdf_for_warning, Pipeline*& pipeline, | |
| 831 | + int objid, int generation, | |
| 832 | + QPDFObjectHandle& stream_dict, bool is_attachment_stream, | |
| 793 | 833 | std::vector<PointerHolder<Pipeline> >& heap); |
| 794 | 834 | |
| 795 | 835 | // Methods to support object copying |
| ... | ... | @@ -1160,30 +1200,6 @@ class QPDF |
| 1160 | 1200 | std::set<QPDFObjGen>& visited, bool top); |
| 1161 | 1201 | void filterCompressedObjects(std::map<int, int> const& object_stream_data); |
| 1162 | 1202 | |
| 1163 | - class EncryptionParameters | |
| 1164 | - { | |
| 1165 | - friend class QPDF; | |
| 1166 | - public: | |
| 1167 | - EncryptionParameters(); | |
| 1168 | - | |
| 1169 | - private: | |
| 1170 | - bool encrypted; | |
| 1171 | - bool encryption_initialized; | |
| 1172 | - int encryption_V; | |
| 1173 | - int encryption_R; | |
| 1174 | - bool encrypt_metadata; | |
| 1175 | - std::map<std::string, encryption_method_e> crypt_filters; | |
| 1176 | - encryption_method_e cf_stream; | |
| 1177 | - encryption_method_e cf_string; | |
| 1178 | - encryption_method_e cf_file; | |
| 1179 | - std::string provided_password; | |
| 1180 | - std::string user_password; | |
| 1181 | - std::string encryption_key; | |
| 1182 | - std::string cached_object_encryption_key; | |
| 1183 | - int cached_key_objid; | |
| 1184 | - int cached_key_generation; | |
| 1185 | - }; | |
| 1186 | - | |
| 1187 | 1203 | class Members |
| 1188 | 1204 | { |
| 1189 | 1205 | friend class QPDF; | ... | ... |
libqpdf/QPDF.cc
| ... | ... | @@ -2512,34 +2512,40 @@ QPDF::getCompressibleObjGens() |
| 2512 | 2512 | } |
| 2513 | 2513 | |
| 2514 | 2514 | bool |
| 2515 | -QPDF::pipeStreamData(int objid, int generation, | |
| 2515 | +QPDF::pipeStreamData(PointerHolder<EncryptionParameters> encp, | |
| 2516 | + PointerHolder<InputSource> file, | |
| 2517 | + QPDF& qpdf_for_warning, | |
| 2518 | + int objid, int generation, | |
| 2516 | 2519 | qpdf_offset_t offset, size_t length, |
| 2517 | 2520 | QPDFObjectHandle stream_dict, |
| 2521 | + bool is_attachment_stream, | |
| 2518 | 2522 | Pipeline* pipeline, |
| 2519 | 2523 | bool suppress_warnings, |
| 2520 | 2524 | bool will_retry) |
| 2521 | 2525 | { |
| 2522 | - bool success = false; | |
| 2523 | 2526 | std::vector<PointerHolder<Pipeline> > to_delete; |
| 2524 | - if (this->m->encp->encrypted) | |
| 2527 | + if (encp->encrypted) | |
| 2525 | 2528 | { |
| 2526 | - decryptStream(pipeline, objid, generation, stream_dict, to_delete); | |
| 2529 | + decryptStream(encp, file, qpdf_for_warning, | |
| 2530 | + pipeline, objid, generation, | |
| 2531 | + stream_dict, is_attachment_stream, to_delete); | |
| 2527 | 2532 | } |
| 2528 | 2533 | |
| 2534 | + bool success = false; | |
| 2529 | 2535 | try |
| 2530 | 2536 | { |
| 2531 | - this->m->file->seek(offset, SEEK_SET); | |
| 2537 | + file->seek(offset, SEEK_SET); | |
| 2532 | 2538 | char buf[10240]; |
| 2533 | 2539 | while (length > 0) |
| 2534 | 2540 | { |
| 2535 | 2541 | size_t to_read = (sizeof(buf) < length ? sizeof(buf) : length); |
| 2536 | - size_t len = this->m->file->read(buf, to_read); | |
| 2542 | + size_t len = file->read(buf, to_read); | |
| 2537 | 2543 | if (len == 0) |
| 2538 | 2544 | { |
| 2539 | 2545 | throw QPDFExc(qpdf_e_damaged_pdf, |
| 2540 | - this->m->file->getName(), | |
| 2541 | - this->m->last_object_description, | |
| 2542 | - this->m->file->getLastOffset(), | |
| 2546 | + file->getName(), | |
| 2547 | + "", | |
| 2548 | + file->getLastOffset(), | |
| 2543 | 2549 | "unexpected EOF reading stream data"); |
| 2544 | 2550 | } |
| 2545 | 2551 | length -= len; |
| ... | ... | @@ -2552,7 +2558,7 @@ QPDF::pipeStreamData(int objid, int generation, |
| 2552 | 2558 | { |
| 2553 | 2559 | if (! suppress_warnings) |
| 2554 | 2560 | { |
| 2555 | - warn(e); | |
| 2561 | + qpdf_for_warning.warn(e); | |
| 2556 | 2562 | } |
| 2557 | 2563 | } |
| 2558 | 2564 | catch (std::exception& e) |
| ... | ... | @@ -2560,17 +2566,19 @@ QPDF::pipeStreamData(int objid, int generation, |
| 2560 | 2566 | if (! suppress_warnings) |
| 2561 | 2567 | { |
| 2562 | 2568 | QTC::TC("qpdf", "QPDF decoding error warning"); |
| 2563 | - warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), | |
| 2564 | - "", this->m->file->getLastOffset(), | |
| 2565 | - "error decoding stream data for object " + | |
| 2566 | - QUtil::int_to_string(objid) + " " + | |
| 2567 | - QUtil::int_to_string(generation) + ": " + e.what())); | |
| 2569 | + qpdf_for_warning.warn( | |
| 2570 | + QPDFExc(qpdf_e_damaged_pdf, file->getName(), | |
| 2571 | + "", file->getLastOffset(), | |
| 2572 | + "error decoding stream data for object " + | |
| 2573 | + QUtil::int_to_string(objid) + " " + | |
| 2574 | + QUtil::int_to_string(generation) + ": " + e.what())); | |
| 2568 | 2575 | if (will_retry) |
| 2569 | 2576 | { |
| 2570 | - warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), | |
| 2571 | - "", this->m->file->getLastOffset(), | |
| 2572 | - "stream will be re-processed without" | |
| 2573 | - " filtering to avoid data loss")); | |
| 2577 | + qpdf_for_warning.warn( | |
| 2578 | + QPDFExc(qpdf_e_damaged_pdf, file->getName(), | |
| 2579 | + "", file->getLastOffset(), | |
| 2580 | + "stream will be re-processed without" | |
| 2581 | + " filtering to avoid data loss")); | |
| 2574 | 2582 | } |
| 2575 | 2583 | } |
| 2576 | 2584 | } |
| ... | ... | @@ -2588,6 +2596,23 @@ QPDF::pipeStreamData(int objid, int generation, |
| 2588 | 2596 | return success; |
| 2589 | 2597 | } |
| 2590 | 2598 | |
| 2599 | +bool | |
| 2600 | +QPDF::pipeStreamData(int objid, int generation, | |
| 2601 | + qpdf_offset_t offset, size_t length, | |
| 2602 | + QPDFObjectHandle stream_dict, | |
| 2603 | + Pipeline* pipeline, | |
| 2604 | + bool suppress_warnings, | |
| 2605 | + bool will_retry) | |
| 2606 | +{ | |
| 2607 | + bool is_attachment_stream = this->m->attachment_streams.count( | |
| 2608 | + QPDFObjGen(objid, generation)); | |
| 2609 | + return pipeStreamData( | |
| 2610 | + this->m->encp, this->m->file, *this, | |
| 2611 | + objid, generation, offset, length, | |
| 2612 | + stream_dict, is_attachment_stream, | |
| 2613 | + pipeline, suppress_warnings, will_retry); | |
| 2614 | +} | |
| 2615 | + | |
| 2591 | 2616 | void |
| 2592 | 2617 | QPDF::findAttachmentStreams() |
| 2593 | 2618 | { | ... | ... |
libqpdf/QPDF_encryption.cc
| ... | ... | @@ -764,14 +764,15 @@ QPDF::recover_encryption_key_with_password( |
| 764 | 764 | } |
| 765 | 765 | |
| 766 | 766 | QPDF::encryption_method_e |
| 767 | -QPDF::interpretCF(QPDFObjectHandle cf) | |
| 767 | +QPDF::interpretCF( | |
| 768 | + PointerHolder<EncryptionParameters> encp, QPDFObjectHandle cf) | |
| 768 | 769 | { |
| 769 | 770 | if (cf.isName()) |
| 770 | 771 | { |
| 771 | 772 | std::string filter = cf.getName(); |
| 772 | - if (this->m->encp->crypt_filters.count(filter) != 0) | |
| 773 | + if (encp->crypt_filters.count(filter) != 0) | |
| 773 | 774 | { |
| 774 | - return this->m->encp->crypt_filters[filter]; | |
| 775 | + return encp->crypt_filters[filter]; | |
| 775 | 776 | } |
| 776 | 777 | else if (filter == "/Identity") |
| 777 | 778 | { |
| ... | ... | @@ -1000,11 +1001,11 @@ QPDF::initializeEncryption() |
| 1000 | 1001 | QPDFObjectHandle StmF = encryption_dict.getKey("/StmF"); |
| 1001 | 1002 | QPDFObjectHandle StrF = encryption_dict.getKey("/StrF"); |
| 1002 | 1003 | QPDFObjectHandle EFF = encryption_dict.getKey("/EFF"); |
| 1003 | - this->m->encp->cf_stream = interpretCF(StmF); | |
| 1004 | - this->m->encp->cf_string = interpretCF(StrF); | |
| 1004 | + this->m->encp->cf_stream = interpretCF(this->m->encp, StmF); | |
| 1005 | + this->m->encp->cf_string = interpretCF(this->m->encp, StrF); | |
| 1005 | 1006 | if (EFF.isName()) |
| 1006 | 1007 | { |
| 1007 | - this->m->encp->cf_file = interpretCF(EFF); | |
| 1008 | + this->m->encp->cf_file = interpretCF(this->m->encp, EFF); | |
| 1008 | 1009 | } |
| 1009 | 1010 | else |
| 1010 | 1011 | { |
| ... | ... | @@ -1068,26 +1069,28 @@ QPDF::initializeEncryption() |
| 1068 | 1069 | } |
| 1069 | 1070 | |
| 1070 | 1071 | std::string |
| 1071 | -QPDF::getKeyForObject(int objid, int generation, bool use_aes) | |
| 1072 | +QPDF::getKeyForObject( | |
| 1073 | + PointerHolder<EncryptionParameters> encp, | |
| 1074 | + int objid, int generation, bool use_aes) | |
| 1072 | 1075 | { |
| 1073 | - if (! this->m->encp->encrypted) | |
| 1076 | + if (! encp->encrypted) | |
| 1074 | 1077 | { |
| 1075 | 1078 | throw std::logic_error( |
| 1076 | 1079 | "request for encryption key in non-encrypted PDF"); |
| 1077 | 1080 | } |
| 1078 | 1081 | |
| 1079 | - if (! ((objid == this->m->encp->cached_key_objid) && | |
| 1080 | - (generation == this->m->encp->cached_key_generation))) | |
| 1082 | + if (! ((objid == encp->cached_key_objid) && | |
| 1083 | + (generation == encp->cached_key_generation))) | |
| 1081 | 1084 | { |
| 1082 | - this->m->encp->cached_object_encryption_key = | |
| 1083 | - compute_data_key(this->m->encp->encryption_key, objid, generation, | |
| 1084 | - use_aes, this->m->encp->encryption_V, | |
| 1085 | - this->m->encp->encryption_R); | |
| 1086 | - this->m->encp->cached_key_objid = objid; | |
| 1087 | - this->m->encp->cached_key_generation = generation; | |
| 1085 | + encp->cached_object_encryption_key = | |
| 1086 | + compute_data_key(encp->encryption_key, objid, generation, | |
| 1087 | + use_aes, encp->encryption_V, | |
| 1088 | + encp->encryption_R); | |
| 1089 | + encp->cached_key_objid = objid; | |
| 1090 | + encp->cached_key_generation = generation; | |
| 1088 | 1091 | } |
| 1089 | 1092 | |
| 1090 | - return this->m->encp->cached_object_encryption_key; | |
| 1093 | + return encp->cached_object_encryption_key; | |
| 1091 | 1094 | } |
| 1092 | 1095 | |
| 1093 | 1096 | void |
| ... | ... | @@ -1131,7 +1134,8 @@ QPDF::decryptString(std::string& str, int objid, int generation) |
| 1131 | 1134 | } |
| 1132 | 1135 | } |
| 1133 | 1136 | |
| 1134 | - std::string key = getKeyForObject(objid, generation, use_aes); | |
| 1137 | + std::string key = getKeyForObject( | |
| 1138 | + this->m->encp, objid, generation, use_aes); | |
| 1135 | 1139 | try |
| 1136 | 1140 | { |
| 1137 | 1141 | if (use_aes) |
| ... | ... | @@ -1175,8 +1179,12 @@ QPDF::decryptString(std::string& str, int objid, int generation) |
| 1175 | 1179 | } |
| 1176 | 1180 | |
| 1177 | 1181 | void |
| 1178 | -QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, | |
| 1182 | +QPDF::decryptStream(PointerHolder<EncryptionParameters> encp, | |
| 1183 | + PointerHolder<InputSource> file, | |
| 1184 | + QPDF& qpdf_for_warning, Pipeline*& pipeline, | |
| 1185 | + int objid, int generation, | |
| 1179 | 1186 | QPDFObjectHandle& stream_dict, |
| 1187 | + bool is_attachment_stream, | |
| 1180 | 1188 | std::vector<PointerHolder<Pipeline> >& heap) |
| 1181 | 1189 | { |
| 1182 | 1190 | std::string type; |
| ... | ... | @@ -1190,7 +1198,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, |
| 1190 | 1198 | return; |
| 1191 | 1199 | } |
| 1192 | 1200 | bool use_aes = false; |
| 1193 | - if (this->m->encp->encryption_V >= 4) | |
| 1201 | + if (encp->encryption_V >= 4) | |
| 1194 | 1202 | { |
| 1195 | 1203 | encryption_method_e method = e_unknown; |
| 1196 | 1204 | std::string method_source = "/StmF from /Encrypt dictionary"; |
| ... | ... | @@ -1206,7 +1214,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, |
| 1206 | 1214 | "/CryptFilterDecodeParms")) |
| 1207 | 1215 | { |
| 1208 | 1216 | QTC::TC("qpdf", "QPDF_encryption stream crypt filter"); |
| 1209 | - method = interpretCF(decode_parms.getKey("/Name")); | |
| 1217 | + method = interpretCF(encp, decode_parms.getKey("/Name")); | |
| 1210 | 1218 | method_source = "stream's Crypt decode parameters"; |
| 1211 | 1219 | } |
| 1212 | 1220 | } |
| ... | ... | @@ -1229,7 +1237,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, |
| 1229 | 1237 | { |
| 1230 | 1238 | QTC::TC("qpdf", "QPDF_encrypt crypt array"); |
| 1231 | 1239 | method = interpretCF( |
| 1232 | - crypt_params.getKey("/Name")); | |
| 1240 | + encp, crypt_params.getKey("/Name")); | |
| 1233 | 1241 | method_source = "stream's Crypt " |
| 1234 | 1242 | "decode parameters (array)"; |
| 1235 | 1243 | } |
| ... | ... | @@ -1241,21 +1249,21 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, |
| 1241 | 1249 | |
| 1242 | 1250 | if (method == e_unknown) |
| 1243 | 1251 | { |
| 1244 | - if ((! this->m->encp->encrypt_metadata) && (type == "/Metadata")) | |
| 1252 | + if ((! encp->encrypt_metadata) && (type == "/Metadata")) | |
| 1245 | 1253 | { |
| 1246 | 1254 | QTC::TC("qpdf", "QPDF_encryption cleartext metadata"); |
| 1247 | 1255 | method = e_none; |
| 1248 | 1256 | } |
| 1249 | 1257 | else |
| 1250 | 1258 | { |
| 1251 | - if (this->m->attachment_streams.count( | |
| 1252 | - QPDFObjGen(objid, generation)) > 0) | |
| 1259 | + if (is_attachment_stream) | |
| 1253 | 1260 | { |
| 1254 | - method = this->m->encp->cf_file; | |
| 1261 | + QTC::TC("qpdf", "QPDF_encryption attachment stream"); | |
| 1262 | + method = encp->cf_file; | |
| 1255 | 1263 | } |
| 1256 | 1264 | else |
| 1257 | 1265 | { |
| 1258 | - method = this->m->encp->cf_stream; | |
| 1266 | + method = encp->cf_stream; | |
| 1259 | 1267 | } |
| 1260 | 1268 | } |
| 1261 | 1269 | } |
| ... | ... | @@ -1279,19 +1287,20 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, |
| 1279 | 1287 | |
| 1280 | 1288 | default: |
| 1281 | 1289 | // filter local to this stream. |
| 1282 | - warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), | |
| 1283 | - this->m->last_object_description, | |
| 1284 | - this->m->file->getLastOffset(), | |
| 1285 | - "unknown encryption filter for streams" | |
| 1286 | - " (check " + method_source + ");" | |
| 1287 | - " streams may be decrypted improperly")); | |
| 1290 | + qpdf_for_warning.warn( | |
| 1291 | + QPDFExc(qpdf_e_damaged_pdf, file->getName(), | |
| 1292 | + "", file->getLastOffset(), | |
| 1293 | + "unknown encryption filter for streams" | |
| 1294 | + " (check " + method_source + ");" | |
| 1295 | + " streams may be decrypted improperly")); | |
| 1288 | 1296 | // To avoid repeated warnings, reset cf_stream. Assume |
| 1289 | 1297 | // we'd want to use AES if V == 4. |
| 1290 | - this->m->encp->cf_stream = e_aes; | |
| 1298 | + encp->cf_stream = e_aes; | |
| 1299 | + use_aes = true; | |
| 1291 | 1300 | break; |
| 1292 | 1301 | } |
| 1293 | 1302 | } |
| 1294 | - std::string key = getKeyForObject(objid, generation, use_aes); | |
| 1303 | + std::string key = getKeyForObject(encp, objid, generation, use_aes); | |
| 1295 | 1304 | if (use_aes) |
| 1296 | 1305 | { |
| 1297 | 1306 | QTC::TC("qpdf", "QPDF_encryption aes decode stream"); | ... | ... |