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