1 // Copyright 2017 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "core/fpdfapi/parser/cpdf_object_avail.h" 6 7 #include <utility> 8 9 #include "core/fpdfapi/parser/cpdf_dictionary.h" 10 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" 11 #include "core/fpdfapi/parser/cpdf_object_walker.h" 12 #include "core/fpdfapi/parser/cpdf_read_validator.h" 13 #include "core/fpdfapi/parser/cpdf_reference.h" 14 15 CPDF_ObjectAvail::CPDF_ObjectAvail(CPDF_ReadValidator* validator, 16 CPDF_IndirectObjectHolder* holder, 17 const CPDF_Object* root) 18 : validator_(validator), holder_(holder), root_(root) { 19 ASSERT(validator_); 20 ASSERT(holder); 21 ASSERT(root_); 22 if (!root_->IsInline()) 23 parsed_objnums_.insert(root_->GetObjNum()); 24 } 25 26 CPDF_ObjectAvail::CPDF_ObjectAvail(CPDF_ReadValidator* validator, 27 CPDF_IndirectObjectHolder* holder, 28 uint32_t obj_num) 29 : validator_(validator), 30 holder_(holder), 31 root_(pdfium::MakeUnique<CPDF_Reference>(holder, obj_num)) { 32 ASSERT(validator_); 33 ASSERT(holder); 34 } 35 36 CPDF_ObjectAvail::~CPDF_ObjectAvail() {} 37 38 CPDF_DataAvail::DocAvailStatus CPDF_ObjectAvail::CheckAvail() { 39 if (!LoadRootObject()) 40 return CPDF_DataAvail::DocAvailStatus::DataNotAvailable; 41 42 if (CheckObjects()) { 43 CleanMemory(); 44 return CPDF_DataAvail::DocAvailStatus::DataAvailable; 45 } 46 return CPDF_DataAvail::DocAvailStatus::DataNotAvailable; 47 } 48 49 bool CPDF_ObjectAvail::LoadRootObject() { 50 if (!non_parsed_objects_.empty()) 51 return true; 52 53 while (root_ && root_->IsReference()) { 54 const uint32_t ref_obj_num = root_->AsReference()->GetRefObjNum(); 55 if (HasObjectParsed(ref_obj_num)) { 56 root_ = nullptr; 57 return true; 58 } 59 60 const CPDF_ReadValidator::Session parse_session(validator_.Get()); 61 const CPDF_Object* direct = holder_->GetOrParseIndirectObject(ref_obj_num); 62 if (validator_->has_read_problems()) 63 return false; 64 65 parsed_objnums_.insert(ref_obj_num); 66 root_ = direct; 67 } 68 std::stack<uint32_t> non_parsed_objects_in_root; 69 if (AppendObjectSubRefs(root_.Get(), &non_parsed_objects_in_root)) { 70 non_parsed_objects_ = std::move(non_parsed_objects_in_root); 71 return true; 72 } 73 return false; 74 } 75 76 bool CPDF_ObjectAvail::CheckObjects() { 77 std::stack<uint32_t> objects_to_check = std::move(non_parsed_objects_); 78 std::set<uint32_t> checked_objects; 79 while (!objects_to_check.empty()) { 80 const uint32_t obj_num = objects_to_check.top(); 81 objects_to_check.pop(); 82 83 if (HasObjectParsed(obj_num)) 84 continue; 85 86 if (!checked_objects.insert(obj_num).second) 87 continue; 88 89 const CPDF_ReadValidator::Session parse_session(validator_.Get()); 90 const CPDF_Object* direct = holder_->GetOrParseIndirectObject(obj_num); 91 if (direct == root_.Get()) 92 continue; 93 94 if (validator_->has_read_problems() || 95 !AppendObjectSubRefs(direct, &objects_to_check)) { 96 non_parsed_objects_.push(obj_num); 97 continue; 98 } 99 parsed_objnums_.insert(obj_num); 100 } 101 return non_parsed_objects_.empty(); 102 } 103 104 bool CPDF_ObjectAvail::AppendObjectSubRefs(const CPDF_Object* object, 105 std::stack<uint32_t>* refs) const { 106 ASSERT(refs); 107 if (!object) 108 return true; 109 110 CPDF_ObjectWalker walker(object); 111 while (const CPDF_Object* obj = walker.GetNext()) { 112 const CPDF_ReadValidator::Session parse_session(validator_.Get()); 113 114 // Skip if this object if it's an inlined root, the parent object or 115 // explicitily excluded. 116 const bool skip = (walker.GetParent() && obj == root_.Get()) || 117 walker.dictionary_key() == "Parent" || 118 (obj != root_.Get() && ExcludeObject(obj)); 119 120 // We need to parse the object before we can do the exclusion check. 121 // This is because the exclusion check may check against a referenced 122 // field of the object which we need to make sure is loaded. 123 if (validator_->has_read_problems()) 124 return false; 125 126 if (skip) { 127 walker.SkipWalkIntoCurrentObject(); 128 continue; 129 } 130 131 if (obj->IsReference()) 132 refs->push(obj->AsReference()->GetRefObjNum()); 133 } 134 return true; 135 } 136 137 void CPDF_ObjectAvail::CleanMemory() { 138 root_.Reset(); 139 parsed_objnums_.clear(); 140 } 141 142 bool CPDF_ObjectAvail::ExcludeObject(const CPDF_Object* object) const { 143 return false; 144 } 145 146 bool CPDF_ObjectAvail::HasObjectParsed(uint32_t obj_num) const { 147 return parsed_objnums_.count(obj_num) > 0; 148 } 149