summaryrefslogtreecommitdiff
path: root/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/fpdfapi/parser/cpdf_cross_ref_avail.cpp')
-rw-r--r--core/fpdfapi/parser/cpdf_cross_ref_avail.cpp211
1 files changed, 211 insertions, 0 deletions
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
new file mode 100644
index 0000000000..be9818ae21
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
@@ -0,0 +1,211 @@
+// 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 <algorithm>
+#include <vector>
+
+#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<CPDF_Dictionary> 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<FX_FILESIZE>(xrefpos))
+ AddCrossRefForCheck(static_cast<FX_FILESIZE>(xrefpos));
+
+ const int32_t stream_xref_offset =
+ GetDirectInteger(trailer.get(), kPrevCrossRefStreamOffsetFieldKey);
+ if (stream_xref_offset &&
+ pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(
+ stream_xref_offset))
+ AddCrossRefForCheck(static_cast<FX_FILESIZE>(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;
+ }
+
+ 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<FX_FILESIZE>(xrefpos))
+ AddCrossRefForCheck(static_cast<FX_FILESIZE>(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_ReadValidator> CPDF_CrossRefAvail::GetValidator() {
+ return parser_->GetValidator();
+}