1 // Copyright 2018 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_stream.h"
6 
7 #include <utility>
8 
9 #include "core/fpdfapi/parser/cpdf_dictionary.h"
10 #include "core/fpdfapi/parser/cpdf_number.h"
11 #include "core/fpdfapi/parser/cpdf_parser.h"
12 #include "core/fpdfapi/parser/cpdf_reference.h"
13 #include "core/fpdfapi/parser/cpdf_stream.h"
14 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
15 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
16 #include "core/fxcrt/cfx_readonlymemorystream.h"
17 #include "core/fxcrt/fx_safe_types.h"
18 #include "third_party/base/ptr_util.h"
19 #include "third_party/base/stl_util.h"
20 
21 // static
IsObjectsStreamObject(const CPDF_Object * object)22 bool CPDF_ObjectStream::IsObjectsStreamObject(const CPDF_Object* object) {
23   const CPDF_Stream* stream = ToStream(object);
24   if (!stream)
25     return false;
26 
27   const CPDF_Dictionary* stream_dict = stream->GetDict();
28   if (!stream_dict)
29     return false;
30 
31   if (stream_dict->GetStringFor("Type") != "ObjStm")
32     return false;
33 
34   const CPDF_Number* number_of_objects =
35       ToNumber(stream_dict->GetObjectFor("N"));
36   if (!number_of_objects || !number_of_objects->IsInteger() ||
37       number_of_objects->GetInteger() < 0 ||
38       number_of_objects->GetInteger() >=
39           static_cast<int>(CPDF_Parser::kMaxObjectNumber)) {
40     return false;
41   }
42 
43   const CPDF_Number* first_object_offset =
44       ToNumber(stream_dict->GetObjectFor("First"));
45   if (!first_object_offset || !first_object_offset->IsInteger() ||
46       first_object_offset->GetInteger() < 0) {
47     return false;
48   }
49 
50   return true;
51 }
52 
53 //  static
Create(const CPDF_Stream * stream)54 std::unique_ptr<CPDF_ObjectStream> CPDF_ObjectStream::Create(
55     const CPDF_Stream* stream) {
56   if (!IsObjectsStreamObject(stream))
57     return nullptr;
58   // The ctor of CPDF_ObjectStream is protected. Use WrapUnique instead
59   // MakeUnique.
60   return pdfium::WrapUnique(new CPDF_ObjectStream(stream));
61 }
62 
CPDF_ObjectStream(const CPDF_Stream * obj_stream)63 CPDF_ObjectStream::CPDF_ObjectStream(const CPDF_Stream* obj_stream)
64     : obj_num_(obj_stream->GetObjNum()),
65       first_object_offset_(obj_stream->GetDict()->GetIntegerFor("First")) {
66   ASSERT(IsObjectsStreamObject(obj_stream));
67   if (const auto* extends_ref =
68           ToReference(obj_stream->GetDict()->GetObjectFor("Extends"))) {
69     extends_obj_num_ = extends_ref->GetRefObjNum();
70   }
71   Init(obj_stream);
72 }
73 
74 CPDF_ObjectStream::~CPDF_ObjectStream() = default;
75 
HasObject(uint32_t obj_number) const76 bool CPDF_ObjectStream::HasObject(uint32_t obj_number) const {
77   return pdfium::ContainsKey(objects_offsets_, obj_number);
78 }
79 
ParseObject(CPDF_IndirectObjectHolder * pObjList,uint32_t obj_number) const80 RetainPtr<CPDF_Object> CPDF_ObjectStream::ParseObject(
81     CPDF_IndirectObjectHolder* pObjList,
82     uint32_t obj_number) const {
83   const auto it = objects_offsets_.find(obj_number);
84   if (it == objects_offsets_.end())
85     return nullptr;
86 
87   RetainPtr<CPDF_Object> result = ParseObjectAtOffset(pObjList, it->second);
88   if (!result)
89     return nullptr;
90 
91   result->SetObjNum(obj_number);
92   return result;
93 }
94 
Init(const CPDF_Stream * stream)95 void CPDF_ObjectStream::Init(const CPDF_Stream* stream) {
96   {
97     auto stream_acc = pdfium::MakeRetain<CPDF_StreamAcc>(stream);
98     stream_acc->LoadAllDataFiltered();
99     const uint32_t data_size = stream_acc->GetSize();
100     data_stream_ = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
101         stream_acc->DetachData(), data_size);
102   }
103 
104   CPDF_SyntaxParser syntax(data_stream_);
105   const int object_count = stream->GetDict()->GetIntegerFor("N");
106   for (int32_t i = object_count; i > 0; --i) {
107     if (syntax.GetPos() >= data_stream_->GetSize())
108       break;
109 
110     const uint32_t obj_num = syntax.GetDirectNum();
111     const uint32_t obj_offset = syntax.GetDirectNum();
112     if (!obj_num)
113       continue;
114 
115     objects_offsets_[obj_num] = obj_offset;
116   }
117 }
118 
ParseObjectAtOffset(CPDF_IndirectObjectHolder * pObjList,uint32_t object_offset) const119 RetainPtr<CPDF_Object> CPDF_ObjectStream::ParseObjectAtOffset(
120     CPDF_IndirectObjectHolder* pObjList,
121     uint32_t object_offset) const {
122   FX_SAFE_FILESIZE offset_in_stream = first_object_offset_;
123   offset_in_stream += object_offset;
124 
125   if (!offset_in_stream.IsValid())
126     return nullptr;
127 
128   if (offset_in_stream.ValueOrDie() >= data_stream_->GetSize())
129     return nullptr;
130 
131   CPDF_SyntaxParser syntax(data_stream_);
132   syntax.SetPos(offset_in_stream.ValueOrDie());
133   return syntax.GetObjectBody(pObjList);
134 }
135