• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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