Home | History | Annotate | Download | only in parser
      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