• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The PDFium Authors
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 "public/fpdf_attachment.h"
6 
7 #include <limits.h>
8 
9 #include <array>
10 #include <memory>
11 #include <utility>
12 
13 #include "constants/stream_dict_common.h"
14 #include "core/fdrm/fx_crypt.h"
15 #include "core/fpdfapi/parser/cpdf_array.h"
16 #include "core/fpdfapi/parser/cpdf_dictionary.h"
17 #include "core/fpdfapi/parser/cpdf_document.h"
18 #include "core/fpdfapi/parser/cpdf_name.h"
19 #include "core/fpdfapi/parser/cpdf_number.h"
20 #include "core/fpdfapi/parser/cpdf_reference.h"
21 #include "core/fpdfapi/parser/cpdf_stream.h"
22 #include "core/fpdfapi/parser/cpdf_string.h"
23 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
24 #include "core/fpdfdoc/cpdf_filespec.h"
25 #include "core/fpdfdoc/cpdf_nametree.h"
26 #include "core/fxcodec/data_and_bytes_consumed.h"
27 #include "core/fxcrt/cfx_datetime.h"
28 #include "core/fxcrt/data_vector.h"
29 #include "core/fxcrt/fx_extension.h"
30 #include "core/fxcrt/numerics/safe_conversions.h"
31 #include "fpdfsdk/cpdfsdk_helpers.h"
32 
33 namespace {
34 
35 constexpr char kChecksumKey[] = "CheckSum";
36 
37 }  // namespace
38 
39 FPDF_EXPORT int FPDF_CALLCONV
FPDFDoc_GetAttachmentCount(FPDF_DOCUMENT document)40 FPDFDoc_GetAttachmentCount(FPDF_DOCUMENT document) {
41   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
42   if (!pDoc)
43     return 0;
44 
45   auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles");
46   return name_tree ? pdfium::checked_cast<int>(name_tree->GetCount()) : 0;
47 }
48 
49 FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
FPDFDoc_AddAttachment(FPDF_DOCUMENT document,FPDF_WIDESTRING name)50 FPDFDoc_AddAttachment(FPDF_DOCUMENT document, FPDF_WIDESTRING name) {
51   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
52   if (!pDoc)
53     return nullptr;
54 
55   // SAFETY: required from caller.
56   WideString wsName = UNSAFE_BUFFERS(WideStringFromFPDFWideString(name));
57   if (wsName.IsEmpty())
58     return nullptr;
59 
60   auto name_tree =
61       CPDF_NameTree::CreateWithRootNameArray(pDoc, "EmbeddedFiles");
62   if (!name_tree)
63     return nullptr;
64 
65   // Set up the basic entries in the filespec dictionary.
66   auto pFile = pDoc->NewIndirect<CPDF_Dictionary>();
67   pFile->SetNewFor<CPDF_Name>("Type", "Filespec");
68   pFile->SetNewFor<CPDF_String>("UF", wsName.AsStringView());
69   pFile->SetNewFor<CPDF_String>(pdfium::stream::kF, wsName.AsStringView());
70 
71   // Add the new attachment name and filespec into the document's EmbeddedFiles.
72   if (!name_tree->AddValueAndName(pFile->MakeReference(pDoc), wsName))
73     return nullptr;
74 
75   // Unretained reference in public API. NOLINTNEXTLINE
76   return FPDFAttachmentFromCPDFObject(pFile);
77 }
78 
79 FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
FPDFDoc_GetAttachment(FPDF_DOCUMENT document,int index)80 FPDFDoc_GetAttachment(FPDF_DOCUMENT document, int index) {
81   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
82   if (!pDoc || index < 0)
83     return nullptr;
84 
85   auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles");
86   if (!name_tree || static_cast<size_t>(index) >= name_tree->GetCount())
87     return nullptr;
88 
89   WideString csName;
90 
91   // Unretained reference in public API. NOLINTNEXTLINE
92   return FPDFAttachmentFromCPDFObject(
93       name_tree->LookupValueAndName(index, &csName));
94 }
95 
96 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFDoc_DeleteAttachment(FPDF_DOCUMENT document,int index)97 FPDFDoc_DeleteAttachment(FPDF_DOCUMENT document, int index) {
98   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
99   if (!pDoc || index < 0)
100     return false;
101 
102   auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles");
103   if (!name_tree || static_cast<size_t>(index) >= name_tree->GetCount())
104     return false;
105 
106   return name_tree->DeleteValueAndName(index);
107 }
108 
109 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAttachment_GetName(FPDF_ATTACHMENT attachment,FPDF_WCHAR * buffer,unsigned long buflen)110 FPDFAttachment_GetName(FPDF_ATTACHMENT attachment,
111                        FPDF_WCHAR* buffer,
112                        unsigned long buflen) {
113   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
114   if (!pFile)
115     return 0;
116 
117   CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
118   // SAFETY: required from caller.
119   return Utf16EncodeMaybeCopyAndReturnLength(
120       spec.GetFileName(), UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
121 }
122 
123 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAttachment_HasKey(FPDF_ATTACHMENT attachment,FPDF_BYTESTRING key)124 FPDFAttachment_HasKey(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) {
125   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
126   if (!pFile)
127     return 0;
128 
129   CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
130   RetainPtr<const CPDF_Dictionary> pParamsDict = spec.GetParamsDict();
131   return pParamsDict ? pParamsDict->KeyExist(key) : 0;
132 }
133 
134 FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
FPDFAttachment_GetValueType(FPDF_ATTACHMENT attachment,FPDF_BYTESTRING key)135 FPDFAttachment_GetValueType(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) {
136   if (!FPDFAttachment_HasKey(attachment, key))
137     return FPDF_OBJECT_UNKNOWN;
138 
139   CPDF_FileSpec spec(
140       pdfium::WrapRetain(CPDFObjectFromFPDFAttachment(attachment)));
141   RetainPtr<const CPDF_Object> pObj = spec.GetParamsDict()->GetObjectFor(key);
142   return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
143 }
144 
145 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAttachment_SetStringValue(FPDF_ATTACHMENT attachment,FPDF_BYTESTRING key,FPDF_WIDESTRING value)146 FPDFAttachment_SetStringValue(FPDF_ATTACHMENT attachment,
147                               FPDF_BYTESTRING key,
148                               FPDF_WIDESTRING value) {
149   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
150   if (!pFile)
151     return false;
152 
153   CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
154   RetainPtr<CPDF_Dictionary> pParamsDict = spec.GetMutableParamsDict();
155   if (!pParamsDict)
156     return false;
157 
158   // SAFETY: required from caller.
159   ByteString bsValue = UNSAFE_BUFFERS(ByteStringFromFPDFWideString(value));
160   ByteString bsKey = key;
161   if (bsKey == kChecksumKey) {
162     pParamsDict->SetNewFor<CPDF_String>(bsKey,
163                                         HexDecode(bsValue.unsigned_span()).data,
164                                         CPDF_String::DataType::kIsHex);
165   } else {
166     pParamsDict->SetNewFor<CPDF_String>(bsKey, bsValue);
167   }
168   return true;
169 }
170 
171 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAttachment_GetStringValue(FPDF_ATTACHMENT attachment,FPDF_BYTESTRING key,FPDF_WCHAR * buffer,unsigned long buflen)172 FPDFAttachment_GetStringValue(FPDF_ATTACHMENT attachment,
173                               FPDF_BYTESTRING key,
174                               FPDF_WCHAR* buffer,
175                               unsigned long buflen) {
176   CPDF_Object* file = CPDFObjectFromFPDFAttachment(attachment);
177   if (!file) {
178     return 0;
179   }
180 
181   CPDF_FileSpec spec(pdfium::WrapRetain(file));
182   RetainPtr<const CPDF_Dictionary> params = spec.GetParamsDict();
183   if (!params) {
184     return 0;
185   }
186 
187   // SAFETY: required from caller.
188   auto buffer_span = UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen));
189 
190   ByteString key_str = key;
191   RetainPtr<const CPDF_Object> object = params->GetObjectFor(key_str);
192   if (!object || (!object->IsString() && !object->IsName())) {
193     // Per API description, return an empty string in these cases.
194     return Utf16EncodeMaybeCopyAndReturnLength(WideString(), buffer_span);
195   }
196 
197   if (key_str == kChecksumKey) {
198     RetainPtr<const CPDF_String> string_object = ToString(object);
199     if (string_object && string_object->IsHex()) {
200       ByteString encoded =
201           PDF_HexEncodeString(string_object->GetString().AsStringView());
202       return Utf16EncodeMaybeCopyAndReturnLength(
203           PDF_DecodeText(encoded.unsigned_span()), buffer_span);
204     }
205   }
206 
207   return Utf16EncodeMaybeCopyAndReturnLength(object->GetUnicodeText(),
208                                              buffer_span);
209 }
210 
211 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment,FPDF_DOCUMENT document,const void * contents,unsigned long len)212 FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment,
213                        FPDF_DOCUMENT document,
214                        const void* contents,
215                        unsigned long len) {
216   // An empty content must have a zero length.
217   if (!contents && len != 0) {
218     return false;
219   }
220 
221   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
222   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
223   if (!pFile || !pFile->IsDictionary() || !pDoc || len > INT_MAX) {
224     return false;
225   }
226 
227   // Create a dictionary for the new embedded file stream.
228   auto pFileStreamDict = pdfium::MakeRetain<CPDF_Dictionary>();
229   auto pParamsDict = pFileStreamDict->SetNewFor<CPDF_Dictionary>("Params");
230 
231   // Set the size of the new file in the dictionary.
232   pFileStreamDict->SetNewFor<CPDF_Number>(pdfium::stream::kDL,
233                                           static_cast<int>(len));
234   pParamsDict->SetNewFor<CPDF_Number>("Size", static_cast<int>(len));
235 
236   // Set the creation date of the new attachment in the dictionary.
237   CFX_DateTime dateTime = CFX_DateTime::Now();
238   pParamsDict->SetNewFor<CPDF_String>(
239       "CreationDate",
240       ByteString::Format("D:%d%02d%02d%02d%02d%02d", dateTime.GetYear(),
241                          dateTime.GetMonth(), dateTime.GetDay(),
242                          dateTime.GetHour(), dateTime.GetMinute(),
243                          dateTime.GetSecond()));
244 
245   // SAFETY: required from caller.
246   pdfium::span<const uint8_t> contents_span = UNSAFE_BUFFERS(
247       pdfium::make_span(static_cast<const uint8_t*>(contents), len));
248 
249   std::array<uint8_t, 16> digest;
250   CRYPT_MD5Generate(contents_span, digest);
251 
252   // Set the checksum of the new attachment in the dictionary.
253   pParamsDict->SetNewFor<CPDF_String>(kChecksumKey, digest,
254                                       CPDF_String::DataType::kIsHex);
255 
256   // Create the file stream and have the filespec dictionary link to it.
257   auto pFileStream = pDoc->NewIndirect<CPDF_Stream>(
258       DataVector<uint8_t>(contents_span.begin(), contents_span.end()),
259       std::move(pFileStreamDict));
260 
261   auto pEFDict = pFile->AsMutableDictionary()->SetNewFor<CPDF_Dictionary>("EF");
262   pEFDict->SetNewFor<CPDF_Reference>("F", pDoc, pFileStream->GetObjNum());
263   return true;
264 }
265 
266 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment,void * buffer,unsigned long buflen,unsigned long * out_buflen)267 FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment,
268                        void* buffer,
269                        unsigned long buflen,
270                        unsigned long* out_buflen) {
271   if (!out_buflen)
272     return false;
273 
274   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
275   if (!pFile)
276     return false;
277 
278   CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
279   RetainPtr<const CPDF_Stream> pFileStream = spec.GetFileStream();
280   if (!pFileStream)
281     return false;
282 
283   // SAFETY: required from caller.
284   *out_buflen = DecodeStreamMaybeCopyAndReturnLength(
285       std::move(pFileStream),
286       UNSAFE_BUFFERS(pdfium::make_span(static_cast<uint8_t*>(buffer),
287                                        static_cast<size_t>(buflen))));
288   return true;
289 }
290