// Copyright 2017 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "core/fpdfapi/parser/cpdf_cross_ref_avail.h" #include #include #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_name.h" #include "core/fpdfapi/parser/cpdf_read_validator.h" #include "core/fpdfapi/parser/cpdf_reference.h" #include "core/fpdfapi/parser/cpdf_syntax_parser.h" #include "core/fpdfapi/parser/fpdf_parser_utility.h" namespace { constexpr char kCrossRefKeyword[] = "xref"; constexpr char kTrailerKeyword[] = "trailer"; constexpr char kPrevCrossRefFieldKey[] = "Prev"; constexpr char kTypeFieldKey[] = "Type"; constexpr char kPrevCrossRefStreamOffsetFieldKey[] = "XRefStm"; constexpr char kXRefKeyword[] = "XRef"; constexpr char kEncryptKey[] = "Encrypt"; } // namespace CPDF_CrossRefAvail::CPDF_CrossRefAvail(CPDF_SyntaxParser* parser, FX_FILESIZE last_crossref_offset) : parser_(parser), last_crossref_offset_(last_crossref_offset) { ASSERT(parser_); AddCrossRefForCheck(last_crossref_offset); } CPDF_CrossRefAvail::~CPDF_CrossRefAvail() {} CPDF_DataAvail::DocAvailStatus CPDF_CrossRefAvail::CheckAvail() { if (current_status_ == CPDF_DataAvail::DataAvailable) return CPDF_DataAvail::DataAvailable; const CPDF_ReadValidator::Session read_session(GetValidator().Get()); while (true) { bool check_result = false; switch (current_state_) { case State::kCrossRefCheck: check_result = CheckCrossRef(); break; case State::kCrossRefV4ItemCheck: check_result = CheckCrossRefV4Item(); break; case State::kCrossRefV4TrailerCheck: check_result = CheckCrossRefV4Trailer(); break; case State::kDone: break; default: { current_status_ = CPDF_DataAvail::DataError; NOTREACHED(); break; } } if (!check_result) break; ASSERT(!GetValidator()->has_read_problems()); } return current_status_; } bool CPDF_CrossRefAvail::CheckReadProblems() { if (GetValidator()->read_error()) { current_status_ = CPDF_DataAvail::DataError; return true; } return GetValidator()->has_unavailable_data(); } bool CPDF_CrossRefAvail::CheckCrossRef() { if (cross_refs_for_check_.empty()) { // All cross refs were checked. current_state_ = State::kDone; current_status_ = CPDF_DataAvail::DataAvailable; return true; } parser_->SetPos(cross_refs_for_check_.front()); const ByteString first_word = parser_->PeekNextWord(nullptr); if (CheckReadProblems()) return false; const bool result = (first_word == kCrossRefKeyword) ? CheckCrossRefV4() : CheckCrossRefStream(); if (result) cross_refs_for_check_.pop(); return result; } bool CPDF_CrossRefAvail::CheckCrossRefV4() { const ByteString keyword = parser_->GetKeyword(); if (CheckReadProblems()) return false; if (keyword != kCrossRefKeyword) { current_status_ = CPDF_DataAvail::DataError; return false; } current_state_ = State::kCrossRefV4ItemCheck; current_offset_ = parser_->GetPos(); return true; } bool CPDF_CrossRefAvail::CheckCrossRefV4Item() { parser_->SetPos(current_offset_); const ByteString keyword = parser_->GetKeyword(); if (CheckReadProblems()) return false; if (keyword.IsEmpty()) { current_status_ = CPDF_DataAvail::DataError; return false; } if (keyword == kTrailerKeyword) current_state_ = State::kCrossRefV4TrailerCheck; // Go to next item. current_offset_ = parser_->GetPos(); return true; } bool CPDF_CrossRefAvail::CheckCrossRefV4Trailer() { parser_->SetPos(current_offset_); std::unique_ptr trailer = ToDictionary(parser_->GetObjectBody(nullptr)); if (CheckReadProblems()) return false; if (!trailer) { current_status_ = CPDF_DataAvail::DataError; return false; } if (ToReference(trailer->GetObjectFor(kEncryptKey))) { current_status_ = CPDF_DataAvail::DataError; return false; } const int32_t xrefpos = GetDirectInteger(trailer.get(), kPrevCrossRefFieldKey); if (xrefpos && pdfium::base::IsValueInRangeForNumericType(xrefpos)) AddCrossRefForCheck(static_cast(xrefpos)); const int32_t stream_xref_offset = GetDirectInteger(trailer.get(), kPrevCrossRefStreamOffsetFieldKey); if (stream_xref_offset && pdfium::base::IsValueInRangeForNumericType( stream_xref_offset)) AddCrossRefForCheck(static_cast(stream_xref_offset)); // Goto check next crossref current_state_ = State::kCrossRefCheck; return true; } bool CPDF_CrossRefAvail::CheckCrossRefStream() { auto cross_ref = parser_->GetIndirectObject(nullptr, CPDF_SyntaxParser::ParseType::kLoose); if (CheckReadProblems()) return false; const CPDF_Dictionary* trailer = cross_ref && cross_ref->IsStream() ? cross_ref->GetDict() : nullptr; if (!trailer) { current_status_ = CPDF_DataAvail::DataError; return false; } if (ToReference(trailer->GetObjectFor(kEncryptKey))) { current_status_ = CPDF_DataAvail::DataError; return false; } const CPDF_Name* type_name = ToName(trailer->GetObjectFor(kTypeFieldKey)); if (type_name && type_name->GetString() == kXRefKeyword) { const int32_t xrefpos = trailer->GetIntegerFor(kPrevCrossRefFieldKey); if (xrefpos && pdfium::base::IsValueInRangeForNumericType(xrefpos)) AddCrossRefForCheck(static_cast(xrefpos)); } // Goto check next crossref current_state_ = State::kCrossRefCheck; return true; } void CPDF_CrossRefAvail::AddCrossRefForCheck(FX_FILESIZE crossref_offset) { if (registered_crossrefs_.count(crossref_offset)) return; cross_refs_for_check_.push(crossref_offset); registered_crossrefs_.insert(crossref_offset); } fxcrt::RetainPtr CPDF_CrossRefAvail::GetValidator() { return parser_->GetValidator(); }