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_cross_ref_avail.h"
      6 
      7 #include <algorithm>
      8 #include <vector>
      9 
     10 #include "core/fpdfapi/parser/cpdf_dictionary.h"
     11 #include "core/fpdfapi/parser/cpdf_name.h"
     12 #include "core/fpdfapi/parser/cpdf_read_validator.h"
     13 #include "core/fpdfapi/parser/cpdf_reference.h"
     14 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
     15 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
     16 
     17 namespace {
     18 
     19 constexpr char kCrossRefKeyword[] = "xref";
     20 constexpr char kTrailerKeyword[] = "trailer";
     21 constexpr char kPrevCrossRefFieldKey[] = "Prev";
     22 constexpr char kTypeFieldKey[] = "Type";
     23 constexpr char kPrevCrossRefStreamOffsetFieldKey[] = "XRefStm";
     24 constexpr char kXRefKeyword[] = "XRef";
     25 constexpr char kEncryptKey[] = "Encrypt";
     26 
     27 }  // namespace
     28 
     29 CPDF_CrossRefAvail::CPDF_CrossRefAvail(CPDF_SyntaxParser* parser,
     30                                        FX_FILESIZE last_crossref_offset)
     31     : parser_(parser), last_crossref_offset_(last_crossref_offset) {
     32   ASSERT(parser_);
     33   AddCrossRefForCheck(last_crossref_offset);
     34 }
     35 
     36 CPDF_CrossRefAvail::~CPDF_CrossRefAvail() {}
     37 
     38 CPDF_DataAvail::DocAvailStatus CPDF_CrossRefAvail::CheckAvail() {
     39   if (current_status_ == CPDF_DataAvail::DataAvailable)
     40     return CPDF_DataAvail::DataAvailable;
     41 
     42   const CPDF_ReadValidator::Session read_session(GetValidator().Get());
     43   while (true) {
     44     bool check_result = false;
     45     switch (current_state_) {
     46       case State::kCrossRefCheck:
     47         check_result = CheckCrossRef();
     48         break;
     49       case State::kCrossRefV4ItemCheck:
     50         check_result = CheckCrossRefV4Item();
     51         break;
     52       case State::kCrossRefV4TrailerCheck:
     53         check_result = CheckCrossRefV4Trailer();
     54         break;
     55       case State::kDone:
     56         break;
     57       default: {
     58         current_status_ = CPDF_DataAvail::DataError;
     59         NOTREACHED();
     60         break;
     61       }
     62     }
     63     if (!check_result)
     64       break;
     65 
     66     ASSERT(!GetValidator()->has_read_problems());
     67   }
     68   return current_status_;
     69 }
     70 
     71 bool CPDF_CrossRefAvail::CheckReadProblems() {
     72   if (GetValidator()->read_error()) {
     73     current_status_ = CPDF_DataAvail::DataError;
     74     return true;
     75   }
     76   return GetValidator()->has_unavailable_data();
     77 }
     78 
     79 bool CPDF_CrossRefAvail::CheckCrossRef() {
     80   if (cross_refs_for_check_.empty()) {
     81     // All cross refs were checked.
     82     current_state_ = State::kDone;
     83     current_status_ = CPDF_DataAvail::DataAvailable;
     84     return true;
     85   }
     86   parser_->SetPos(cross_refs_for_check_.front());
     87 
     88   const ByteString first_word = parser_->PeekNextWord(nullptr);
     89   if (CheckReadProblems())
     90     return false;
     91 
     92   const bool result = (first_word == kCrossRefKeyword) ? CheckCrossRefV4()
     93                                                        : CheckCrossRefStream();
     94 
     95   if (result)
     96     cross_refs_for_check_.pop();
     97 
     98   return result;
     99 }
    100 
    101 bool CPDF_CrossRefAvail::CheckCrossRefV4() {
    102   const ByteString keyword = parser_->GetKeyword();
    103   if (CheckReadProblems())
    104     return false;
    105 
    106   if (keyword != kCrossRefKeyword) {
    107     current_status_ = CPDF_DataAvail::DataError;
    108     return false;
    109   }
    110 
    111   current_state_ = State::kCrossRefV4ItemCheck;
    112   current_offset_ = parser_->GetPos();
    113   return true;
    114 }
    115 
    116 bool CPDF_CrossRefAvail::CheckCrossRefV4Item() {
    117   parser_->SetPos(current_offset_);
    118   const ByteString keyword = parser_->GetKeyword();
    119   if (CheckReadProblems())
    120     return false;
    121 
    122   if (keyword.IsEmpty()) {
    123     current_status_ = CPDF_DataAvail::DataError;
    124     return false;
    125   }
    126 
    127   if (keyword == kTrailerKeyword)
    128     current_state_ = State::kCrossRefV4TrailerCheck;
    129 
    130   // Go to next item.
    131   current_offset_ = parser_->GetPos();
    132   return true;
    133 }
    134 
    135 bool CPDF_CrossRefAvail::CheckCrossRefV4Trailer() {
    136   parser_->SetPos(current_offset_);
    137 
    138   std::unique_ptr<CPDF_Dictionary> trailer =
    139       ToDictionary(parser_->GetObjectBody(nullptr));
    140   if (CheckReadProblems())
    141     return false;
    142 
    143   if (!trailer) {
    144     current_status_ = CPDF_DataAvail::DataError;
    145     return false;
    146   }
    147 
    148   if (ToReference(trailer->GetObjectFor(kEncryptKey))) {
    149     current_status_ = CPDF_DataAvail::DataError;
    150     return false;
    151   }
    152 
    153   const int32_t xrefpos =
    154       GetDirectInteger(trailer.get(), kPrevCrossRefFieldKey);
    155   if (xrefpos &&
    156       pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(xrefpos))
    157     AddCrossRefForCheck(static_cast<FX_FILESIZE>(xrefpos));
    158 
    159   const int32_t stream_xref_offset =
    160       GetDirectInteger(trailer.get(), kPrevCrossRefStreamOffsetFieldKey);
    161   if (stream_xref_offset &&
    162       pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(
    163           stream_xref_offset))
    164     AddCrossRefForCheck(static_cast<FX_FILESIZE>(stream_xref_offset));
    165 
    166   // Goto check next crossref
    167   current_state_ = State::kCrossRefCheck;
    168   return true;
    169 }
    170 
    171 bool CPDF_CrossRefAvail::CheckCrossRefStream() {
    172   auto cross_ref =
    173       parser_->GetIndirectObject(nullptr, CPDF_SyntaxParser::ParseType::kLoose);
    174   if (CheckReadProblems())
    175     return false;
    176 
    177   const CPDF_Dictionary* trailer =
    178       cross_ref && cross_ref->IsStream() ? cross_ref->GetDict() : nullptr;
    179   if (!trailer) {
    180     current_status_ = CPDF_DataAvail::DataError;
    181     return false;
    182   }
    183 
    184   if (ToReference(trailer->GetObjectFor(kEncryptKey))) {
    185     current_status_ = CPDF_DataAvail::DataError;
    186     return false;
    187   }
    188 
    189   CPDF_Name* type_name = ToName(trailer->GetObjectFor(kTypeFieldKey));
    190   if (type_name && type_name->GetString() == kXRefKeyword) {
    191     const int32_t xrefpos = trailer->GetIntegerFor(kPrevCrossRefFieldKey);
    192     if (xrefpos &&
    193         pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(xrefpos))
    194       AddCrossRefForCheck(static_cast<FX_FILESIZE>(xrefpos));
    195   }
    196   // Goto check next crossref
    197   current_state_ = State::kCrossRefCheck;
    198   return true;
    199 }
    200 
    201 void CPDF_CrossRefAvail::AddCrossRefForCheck(FX_FILESIZE crossref_offset) {
    202   if (registered_crossrefs_.count(crossref_offset))
    203     return;
    204 
    205   cross_refs_for_check_.push(crossref_offset);
    206   registered_crossrefs_.insert(crossref_offset);
    207 }
    208 
    209 fxcrt::RetainPtr<CPDF_ReadValidator> CPDF_CrossRefAvail::GetValidator() {
    210   return parser_->GetValidator();
    211 }
    212