Commit fbbb0ee0167a9013c3a712c790a9772075aed2ad

Authored by Jay Berkenbilt
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.
include/qpdf/QPDF.hh
@@ -692,6 +692,30 @@ class QPDF @@ -692,6 +692,30 @@ class QPDF
692 }; 692 };
693 friend class ResolveRecorder; 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 void parse(char const* password); 719 void parse(char const* password);
696 void warn(QPDFExc const& e); 720 void warn(QPDFExc const& e);
697 void setTrailer(QPDFObjectHandle obj); 721 void setTrailer(QPDFObjectHandle obj);
@@ -735,6 +759,16 @@ class QPDF @@ -735,6 +759,16 @@ class QPDF
735 Pipeline* pipeline, 759 Pipeline* pipeline,
736 bool suppress_warnings, 760 bool suppress_warnings,
737 bool will_retry); 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 // For QPDFWriter: 773 // For QPDFWriter:
740 774
@@ -776,9 +810,12 @@ class QPDF @@ -776,9 +810,12 @@ class QPDF
776 bool check_duplicate); 810 bool check_duplicate);
777 811
778 // methods to support encryption -- implemented in QPDF_encryption.cc 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 void initializeEncryption(); 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 void decryptString(std::string&, int objid, int generation); 819 void decryptString(std::string&, int objid, int generation);
783 static std::string compute_encryption_key_from_password( 820 static std::string compute_encryption_key_from_password(
784 std::string const& password, EncryptionData const& data); 821 std::string const& password, EncryptionData const& data);
@@ -787,9 +824,12 @@ class QPDF @@ -787,9 +824,12 @@ class QPDF
787 static std::string recover_encryption_key_with_password( 824 static std::string recover_encryption_key_with_password(
788 std::string const& password, EncryptionData const& data, 825 std::string const& password, EncryptionData const& data,
789 bool& perms_valid); 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 std::vector<PointerHolder<Pipeline> >& heap); 833 std::vector<PointerHolder<Pipeline> >& heap);
794 834
795 // Methods to support object copying 835 // Methods to support object copying
@@ -1160,30 +1200,6 @@ class QPDF @@ -1160,30 +1200,6 @@ class QPDF
1160 std::set<QPDFObjGen>& visited, bool top); 1200 std::set<QPDFObjGen>& visited, bool top);
1161 void filterCompressedObjects(std::map<int, int> const& object_stream_data); 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 class Members 1203 class Members
1188 { 1204 {
1189 friend class QPDF; 1205 friend class QPDF;
libqpdf/QPDF.cc
@@ -2512,34 +2512,40 @@ QPDF::getCompressibleObjGens() @@ -2512,34 +2512,40 @@ QPDF::getCompressibleObjGens()
2512 } 2512 }
2513 2513
2514 bool 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 qpdf_offset_t offset, size_t length, 2519 qpdf_offset_t offset, size_t length,
2517 QPDFObjectHandle stream_dict, 2520 QPDFObjectHandle stream_dict,
  2521 + bool is_attachment_stream,
2518 Pipeline* pipeline, 2522 Pipeline* pipeline,
2519 bool suppress_warnings, 2523 bool suppress_warnings,
2520 bool will_retry) 2524 bool will_retry)
2521 { 2525 {
2522 - bool success = false;  
2523 std::vector<PointerHolder<Pipeline> > to_delete; 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 try 2535 try
2530 { 2536 {
2531 - this->m->file->seek(offset, SEEK_SET); 2537 + file->seek(offset, SEEK_SET);
2532 char buf[10240]; 2538 char buf[10240];
2533 while (length > 0) 2539 while (length > 0)
2534 { 2540 {
2535 size_t to_read = (sizeof(buf) < length ? sizeof(buf) : length); 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 if (len == 0) 2543 if (len == 0)
2538 { 2544 {
2539 throw QPDFExc(qpdf_e_damaged_pdf, 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 "unexpected EOF reading stream data"); 2549 "unexpected EOF reading stream data");
2544 } 2550 }
2545 length -= len; 2551 length -= len;
@@ -2552,7 +2558,7 @@ QPDF::pipeStreamData(int objid, int generation, @@ -2552,7 +2558,7 @@ QPDF::pipeStreamData(int objid, int generation,
2552 { 2558 {
2553 if (! suppress_warnings) 2559 if (! suppress_warnings)
2554 { 2560 {
2555 - warn(e); 2561 + qpdf_for_warning.warn(e);
2556 } 2562 }
2557 } 2563 }
2558 catch (std::exception& e) 2564 catch (std::exception& e)
@@ -2560,17 +2566,19 @@ QPDF::pipeStreamData(int objid, int generation, @@ -2560,17 +2566,19 @@ QPDF::pipeStreamData(int objid, int generation,
2560 if (! suppress_warnings) 2566 if (! suppress_warnings)
2561 { 2567 {
2562 QTC::TC("qpdf", "QPDF decoding error warning"); 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 if (will_retry) 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,6 +2596,23 @@ QPDF::pipeStreamData(int objid, int generation,
2588 return success; 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 void 2616 void
2592 QPDF::findAttachmentStreams() 2617 QPDF::findAttachmentStreams()
2593 { 2618 {
libqpdf/QPDF_encryption.cc
@@ -764,14 +764,15 @@ QPDF::recover_encryption_key_with_password( @@ -764,14 +764,15 @@ QPDF::recover_encryption_key_with_password(
764 } 764 }
765 765
766 QPDF::encryption_method_e 766 QPDF::encryption_method_e
767 -QPDF::interpretCF(QPDFObjectHandle cf) 767 +QPDF::interpretCF(
  768 + PointerHolder<EncryptionParameters> encp, QPDFObjectHandle cf)
768 { 769 {
769 if (cf.isName()) 770 if (cf.isName())
770 { 771 {
771 std::string filter = cf.getName(); 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 else if (filter == "/Identity") 777 else if (filter == "/Identity")
777 { 778 {
@@ -1000,11 +1001,11 @@ QPDF::initializeEncryption() @@ -1000,11 +1001,11 @@ QPDF::initializeEncryption()
1000 QPDFObjectHandle StmF = encryption_dict.getKey("/StmF"); 1001 QPDFObjectHandle StmF = encryption_dict.getKey("/StmF");
1001 QPDFObjectHandle StrF = encryption_dict.getKey("/StrF"); 1002 QPDFObjectHandle StrF = encryption_dict.getKey("/StrF");
1002 QPDFObjectHandle EFF = encryption_dict.getKey("/EFF"); 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 if (EFF.isName()) 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 else 1010 else
1010 { 1011 {
@@ -1068,26 +1069,28 @@ QPDF::initializeEncryption() @@ -1068,26 +1069,28 @@ QPDF::initializeEncryption()
1068 } 1069 }
1069 1070
1070 std::string 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 throw std::logic_error( 1078 throw std::logic_error(
1076 "request for encryption key in non-encrypted PDF"); 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 void 1096 void
@@ -1131,7 +1134,8 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation) @@ -1131,7 +1134,8 @@ QPDF::decryptString(std::string&amp; 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 try 1139 try
1136 { 1140 {
1137 if (use_aes) 1141 if (use_aes)
@@ -1175,8 +1179,12 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation) @@ -1175,8 +1179,12 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation)
1175 } 1179 }
1176 1180
1177 void 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 QPDFObjectHandle& stream_dict, 1186 QPDFObjectHandle& stream_dict,
  1187 + bool is_attachment_stream,
1180 std::vector<PointerHolder<Pipeline> >& heap) 1188 std::vector<PointerHolder<Pipeline> >& heap)
1181 { 1189 {
1182 std::string type; 1190 std::string type;
@@ -1190,7 +1198,7 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation, @@ -1190,7 +1198,7 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
1190 return; 1198 return;
1191 } 1199 }
1192 bool use_aes = false; 1200 bool use_aes = false;
1193 - if (this->m->encp->encryption_V >= 4) 1201 + if (encp->encryption_V >= 4)
1194 { 1202 {
1195 encryption_method_e method = e_unknown; 1203 encryption_method_e method = e_unknown;
1196 std::string method_source = "/StmF from /Encrypt dictionary"; 1204 std::string method_source = "/StmF from /Encrypt dictionary";
@@ -1206,7 +1214,7 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation, @@ -1206,7 +1214,7 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
1206 "/CryptFilterDecodeParms")) 1214 "/CryptFilterDecodeParms"))
1207 { 1215 {
1208 QTC::TC("qpdf", "QPDF_encryption stream crypt filter"); 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 method_source = "stream's Crypt decode parameters"; 1218 method_source = "stream's Crypt decode parameters";
1211 } 1219 }
1212 } 1220 }
@@ -1229,7 +1237,7 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation, @@ -1229,7 +1237,7 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
1229 { 1237 {
1230 QTC::TC("qpdf", "QPDF_encrypt crypt array"); 1238 QTC::TC("qpdf", "QPDF_encrypt crypt array");
1231 method = interpretCF( 1239 method = interpretCF(
1232 - crypt_params.getKey("/Name")); 1240 + encp, crypt_params.getKey("/Name"));
1233 method_source = "stream's Crypt " 1241 method_source = "stream's Crypt "
1234 "decode parameters (array)"; 1242 "decode parameters (array)";
1235 } 1243 }
@@ -1241,21 +1249,21 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation, @@ -1241,21 +1249,21 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
1241 1249
1242 if (method == e_unknown) 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 QTC::TC("qpdf", "QPDF_encryption cleartext metadata"); 1254 QTC::TC("qpdf", "QPDF_encryption cleartext metadata");
1247 method = e_none; 1255 method = e_none;
1248 } 1256 }
1249 else 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 else 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*&amp; pipeline, int objid, int generation, @@ -1279,19 +1287,20 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
1279 1287
1280 default: 1288 default:
1281 // filter local to this stream. 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 // To avoid repeated warnings, reset cf_stream. Assume 1296 // To avoid repeated warnings, reset cf_stream. Assume
1289 // we'd want to use AES if V == 4. 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 break; 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 if (use_aes) 1304 if (use_aes)
1296 { 1305 {
1297 QTC::TC("qpdf", "QPDF_encryption aes decode stream"); 1306 QTC::TC("qpdf", "QPDF_encryption aes decode stream");
qpdf/qpdf.testcov
@@ -406,3 +406,4 @@ qpdf image optimize no pipeline 0 @@ -406,3 +406,4 @@ qpdf image optimize no pipeline 0
406 qpdf image optimize no shrink 0 406 qpdf image optimize no shrink 0
407 qpdf image optimize too small 0 407 qpdf image optimize too small 0
408 QPDFFormFieldObjectHelper WinAnsi 0 408 QPDFFormFieldObjectHelper WinAnsi 0
  409 +QPDF_encryption attachment stream 0