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
CPDF_ObjectAvail(CPDF_ReadValidator * validator,CPDF_IndirectObjectHolder * holder,const CPDF_Object * root)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
CPDF_ObjectAvail(CPDF_ReadValidator * validator,CPDF_IndirectObjectHolder * holder,uint32_t obj_num)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
~CPDF_ObjectAvail()36 CPDF_ObjectAvail::~CPDF_ObjectAvail() {}
37
CheckAvail()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
LoadRootObject()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
CheckObjects()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
AppendObjectSubRefs(const CPDF_Object * object,std::stack<uint32_t> * refs) const104 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
CleanMemory()137 void CPDF_ObjectAvail::CleanMemory() {
138 root_.Reset();
139 parsed_objnums_.clear();
140 }
141
ExcludeObject(const CPDF_Object * object) const142 bool CPDF_ObjectAvail::ExcludeObject(const CPDF_Object* object) const {
143 return false;
144 }
145
HasObjectParsed(uint32_t obj_num) const146 bool CPDF_ObjectAvail::HasObjectParsed(uint32_t obj_num) const {
147 return parsed_objnums_.count(obj_num) > 0;
148 }
149