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