blob: 96434bf9e33fc23208d06d904891d02b104c8b1f [file] [log] [blame]
K. Moon832a6942022-10-31 20:11:31 +00001// Copyright 2017 The PDFium Authors
Jane Liu53aafa92017-07-12 19:55:02 -04002// 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
Lei Zhangbfd8a9b2020-04-23 16:49:08 +00007#include <limits.h>
8
Lei Zhang295454b2024-05-22 02:10:33 +00009#include <array>
Jane Liu605fb602017-07-25 14:44:11 -040010#include <memory>
11#include <utility>
12
Lei Zhang26170562018-04-17 17:01:52 +000013#include "constants/stream_dict_common.h"
Lei Zhangd145e4b2018-10-12 18:54:31 +000014#include "core/fdrm/fx_crypt.h"
Jane Liu54a42142017-07-24 16:40:54 -040015#include "core/fpdfapi/parser/cpdf_array.h"
Lei Zhang81535612018-10-09 21:15:17 +000016#include "core/fpdfapi/parser/cpdf_dictionary.h"
Jane Liu53aafa92017-07-12 19:55:02 -040017#include "core/fpdfapi/parser/cpdf_document.h"
Jane Liu54a42142017-07-24 16:40:54 -040018#include "core/fpdfapi/parser/cpdf_name.h"
19#include "core/fpdfapi/parser/cpdf_number.h"
20#include "core/fpdfapi/parser/cpdf_reference.h"
Lei Zhanged975f92019-02-20 18:49:01 +000021#include "core/fpdfapi/parser/cpdf_stream.h"
Jane Liu18ae06d2017-07-18 10:15:16 -040022#include "core/fpdfapi/parser/cpdf_string.h"
23#include "core/fpdfapi/parser/fpdf_parser_decode.h"
Jane Liu53aafa92017-07-12 19:55:02 -040024#include "core/fpdfdoc/cpdf_filespec.h"
25#include "core/fpdfdoc/cpdf_nametree.h"
Lei Zhangfc0397e2024-05-16 21:07:59 +000026#include "core/fxcodec/data_and_bytes_consumed.h"
Jane Liu54a42142017-07-24 16:40:54 -040027#include "core/fxcrt/cfx_datetime.h"
Lei Zhang6dc8a142022-10-20 23:08:15 +000028#include "core/fxcrt/data_vector.h"
Jane Liu54a42142017-07-24 16:40:54 -040029#include "core/fxcrt/fx_extension.h"
Lei Zhangf36006c2024-02-17 00:56:24 +000030#include "core/fxcrt/numerics/safe_conversions.h"
Dan Sinclair00d47a62018-03-28 18:39:04 +000031#include "fpdfsdk/cpdfsdk_helpers.h"
Jane Liu53aafa92017-07-12 19:55:02 -040032
Jane Liu54a42142017-07-24 16:40:54 -040033namespace {
34
Lei Zhangdf064df2017-08-31 02:33:27 -070035constexpr char kChecksumKey[] = "CheckSum";
36
Jane Liu54a42142017-07-24 16:40:54 -040037} // namespace
38
Dan Sinclair00d2ad12017-08-10 14:13:02 -040039FPDF_EXPORT int FPDF_CALLCONV
40FPDFDoc_GetAttachmentCount(FPDF_DOCUMENT document) {
Jane Liu53aafa92017-07-12 19:55:02 -040041 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
42 if (!pDoc)
43 return 0;
44
Lei Zhang1575b472020-04-02 23:09:15 +000045 auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles");
Lei Zhangf36006c2024-02-17 00:56:24 +000046 return name_tree ? pdfium::checked_cast<int>(name_tree->GetCount()) : 0;
Jane Liu53aafa92017-07-12 19:55:02 -040047}
48
Dan Sinclair00d2ad12017-08-10 14:13:02 -040049FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
50FPDFDoc_AddAttachment(FPDF_DOCUMENT document, FPDF_WIDESTRING name) {
Jane Liu54a42142017-07-24 16:40:54 -040051 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
Lei Zhangb46a7632019-01-09 02:56:16 +000052 if (!pDoc)
Jane Liu54a42142017-07-24 16:40:54 -040053 return nullptr;
54
Tom Sepez6967ba92024-05-10 00:40:04 +000055 // SAFETY: required from caller.
56 WideString wsName = UNSAFE_BUFFERS(WideStringFromFPDFWideString(name));
Lei Zhangb46a7632019-01-09 02:56:16 +000057 if (wsName.IsEmpty())
58 return nullptr;
59
Lei Zhang74694c12020-04-02 21:25:13 +000060 auto name_tree =
61 CPDF_NameTree::CreateWithRootNameArray(pDoc, "EmbeddedFiles");
62 if (!name_tree)
63 return nullptr;
Jane Liu54a42142017-07-24 16:40:54 -040064
65 // Set up the basic entries in the filespec dictionary.
Tom Sepez2c1b81c2022-09-20 23:57:45 +000066 auto pFile = pDoc->NewIndirect<CPDF_Dictionary>();
Jane Liu54a42142017-07-24 16:40:54 -040067 pFile->SetNewFor<CPDF_Name>("Type", "Filespec");
Tom Sepez9e802622022-03-18 20:44:15 +000068 pFile->SetNewFor<CPDF_String>("UF", wsName.AsStringView());
69 pFile->SetNewFor<CPDF_String>(pdfium::stream::kF, wsName.AsStringView());
Jane Liu54a42142017-07-24 16:40:54 -040070
71 // Add the new attachment name and filespec into the document's EmbeddedFiles.
Lei Zhang74694c12020-04-02 21:25:13 +000072 if (!name_tree->AddValueAndName(pFile->MakeReference(pDoc), wsName))
Jane Liu54a42142017-07-24 16:40:54 -040073 return nullptr;
Jane Liu54a42142017-07-24 16:40:54 -040074
Tom Sepez490d6812022-11-04 23:25:37 +000075 // Unretained reference in public API. NOLINTNEXTLINE
76 return FPDFAttachmentFromCPDFObject(pFile);
Jane Liu54a42142017-07-24 16:40:54 -040077}
78
Dan Sinclair00d2ad12017-08-10 14:13:02 -040079FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
80FPDFDoc_GetAttachment(FPDF_DOCUMENT document, int index) {
Jane Liu53aafa92017-07-12 19:55:02 -040081 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
82 if (!pDoc || index < 0)
Jane Liu18ae06d2017-07-18 10:15:16 -040083 return nullptr;
Jane Liu53aafa92017-07-12 19:55:02 -040084
Lei Zhang1575b472020-04-02 23:09:15 +000085 auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles");
86 if (!name_tree || static_cast<size_t>(index) >= name_tree->GetCount())
Jane Liu18ae06d2017-07-18 10:15:16 -040087 return nullptr;
Jane Liu53aafa92017-07-12 19:55:02 -040088
Ryan Harrison275e2602017-09-18 14:23:18 -040089 WideString csName;
Tom Sepez490d6812022-11-04 23:25:37 +000090
91 // Unretained reference in public API. NOLINTNEXTLINE
Tom Sepez525147a2018-05-03 17:19:53 +000092 return FPDFAttachmentFromCPDFObject(
Tom Sepez490d6812022-11-04 23:25:37 +000093 name_tree->LookupValueAndName(index, &csName));
Jane Liu18ae06d2017-07-18 10:15:16 -040094}
95
Dan Sinclair00d2ad12017-08-10 14:13:02 -040096FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
97FPDFDoc_DeleteAttachment(FPDF_DOCUMENT document, int index) {
Jane Liuf63e8132017-07-25 18:11:27 -040098 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
99 if (!pDoc || index < 0)
100 return false;
101
Lei Zhang1575b472020-04-02 23:09:15 +0000102 auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles");
103 if (!name_tree || static_cast<size_t>(index) >= name_tree->GetCount())
Jane Liuf63e8132017-07-25 18:11:27 -0400104 return false;
105
Lei Zhang1575b472020-04-02 23:09:15 +0000106 return name_tree->DeleteValueAndName(index);
Jane Liuf63e8132017-07-25 18:11:27 -0400107}
108
Dan Sinclair00d2ad12017-08-10 14:13:02 -0400109FPDF_EXPORT unsigned long FPDF_CALLCONV
Jane Liu18ae06d2017-07-18 10:15:16 -0400110FPDFAttachment_GetName(FPDF_ATTACHMENT attachment,
Lei Zhang960e2952019-05-15 20:58:36 +0000111 FPDF_WCHAR* buffer,
Jane Liu18ae06d2017-07-18 10:15:16 -0400112 unsigned long buflen) {
113 CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
Jane Liu53aafa92017-07-12 19:55:02 -0400114 if (!pFile)
115 return 0;
116
Tom Sepez3df809c2022-09-27 23:03:54 +0000117 CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
Tom Sepez946c0d32024-05-09 20:47:23 +0000118 // SAFETY: required from caller.
Tom Sepez6967ba92024-05-10 00:40:04 +0000119 return Utf16EncodeMaybeCopyAndReturnLength(
120 spec.GetFileName(), UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
Jane Liu18ae06d2017-07-18 10:15:16 -0400121}
Jane Liu53aafa92017-07-12 19:55:02 -0400122
Dan Sinclair00d2ad12017-08-10 14:13:02 -0400123FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
Lei Zhangdf064df2017-08-31 02:33:27 -0700124FPDFAttachment_HasKey(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) {
Jane Liu18ae06d2017-07-18 10:15:16 -0400125 CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
126 if (!pFile)
127 return 0;
128
Tom Sepez3df809c2022-09-27 23:03:54 +0000129 CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
130 RetainPtr<const CPDF_Dictionary> pParamsDict = spec.GetParamsDict();
Lei Zhangdf064df2017-08-31 02:33:27 -0700131 return pParamsDict ? pParamsDict->KeyExist(key) : 0;
Jane Liu18ae06d2017-07-18 10:15:16 -0400132}
133
Dan Sinclair00d2ad12017-08-10 14:13:02 -0400134FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
Lei Zhangdf064df2017-08-31 02:33:27 -0700135FPDFAttachment_GetValueType(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) {
Jane Liu18ae06d2017-07-18 10:15:16 -0400136 if (!FPDFAttachment_HasKey(attachment, key))
137 return FPDF_OBJECT_UNKNOWN;
138
Tom Sepez3df809c2022-09-27 23:03:54 +0000139 CPDF_FileSpec spec(
140 pdfium::WrapRetain(CPDFObjectFromFPDFAttachment(attachment)));
Tom Sepez3f90d432022-09-19 20:53:13 +0000141 RetainPtr<const CPDF_Object> pObj = spec.GetParamsDict()->GetObjectFor(key);
Lei Zhangdf064df2017-08-31 02:33:27 -0700142 return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
Jane Liu18ae06d2017-07-18 10:15:16 -0400143}
144
Dan Sinclair00d2ad12017-08-10 14:13:02 -0400145FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
Jane Liu54a42142017-07-24 16:40:54 -0400146FPDFAttachment_SetStringValue(FPDF_ATTACHMENT attachment,
Lei Zhangdf064df2017-08-31 02:33:27 -0700147 FPDF_BYTESTRING key,
Jane Liu54a42142017-07-24 16:40:54 -0400148 FPDF_WIDESTRING value) {
149 CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
150 if (!pFile)
151 return false;
152
Tom Sepez3df809c2022-09-27 23:03:54 +0000153 CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
154 RetainPtr<CPDF_Dictionary> pParamsDict = spec.GetMutableParamsDict();
Jane Liu54a42142017-07-24 16:40:54 -0400155 if (!pParamsDict)
156 return false;
157
Tom Sepez6967ba92024-05-10 00:40:04 +0000158 // SAFETY: required from caller.
159 ByteString bsValue = UNSAFE_BUFFERS(ByteStringFromFPDFWideString(value));
Ryan Harrison275e2602017-09-18 14:23:18 -0400160 ByteString bsKey = key;
Lei Zhang295454b2024-05-22 02:10:33 +0000161 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);
Tom Sepez6967ba92024-05-10 00:40:04 +0000167 }
Jane Liu54a42142017-07-24 16:40:54 -0400168 return true;
169}
170
Dan Sinclair00d2ad12017-08-10 14:13:02 -0400171FPDF_EXPORT unsigned long FPDF_CALLCONV
Jane Liu18ae06d2017-07-18 10:15:16 -0400172FPDFAttachment_GetStringValue(FPDF_ATTACHMENT attachment,
Lei Zhangdf064df2017-08-31 02:33:27 -0700173 FPDF_BYTESTRING key,
Lei Zhang960e2952019-05-15 20:58:36 +0000174 FPDF_WCHAR* buffer,
Jane Liu18ae06d2017-07-18 10:15:16 -0400175 unsigned long buflen) {
Lei Zhang4ce737f2024-05-22 02:10:39 +0000176 CPDF_Object* file = CPDFObjectFromFPDFAttachment(attachment);
177 if (!file) {
Jane Liu18ae06d2017-07-18 10:15:16 -0400178 return 0;
Lei Zhang4ce737f2024-05-22 02:10:39 +0000179 }
Jane Liu18ae06d2017-07-18 10:15:16 -0400180
Lei Zhang4ce737f2024-05-22 02:10:39 +0000181 CPDF_FileSpec spec(pdfium::WrapRetain(file));
182 RetainPtr<const CPDF_Dictionary> params = spec.GetParamsDict();
183 if (!params) {
Jane Liu18ae06d2017-07-18 10:15:16 -0400184 return 0;
Lei Zhang4ce737f2024-05-22 02:10:39 +0000185 }
Jane Liu18ae06d2017-07-18 10:15:16 -0400186
Lei Zhang4ce737f2024-05-22 02:10:39 +0000187 // 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()) {
Tom Sepez07819c42022-01-06 23:17:00 +0000200 ByteString encoded =
Lei Zhang4ce737f2024-05-22 02:10:39 +0000201 PDF_HexEncodeString(string_object->GetString().AsStringView());
202 return Utf16EncodeMaybeCopyAndReturnLength(
Lei Zhangd4b014a2024-05-22 02:10:46 +0000203 PDF_DecodeText(encoded.unsigned_span()), buffer_span);
Jane Liu18ae06d2017-07-18 10:15:16 -0400204 }
205 }
Lei Zhang4ce737f2024-05-22 02:10:39 +0000206
207 return Utf16EncodeMaybeCopyAndReturnLength(object->GetUnicodeText(),
208 buffer_span);
Jane Liu18ae06d2017-07-18 10:15:16 -0400209}
210
Dan Sinclair00d2ad12017-08-10 14:13:02 -0400211FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
212FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment,
213 FPDF_DOCUMENT document,
214 const void* contents,
Lei Zhangca86e902020-06-09 22:12:34 +0000215 unsigned long len) {
Lei Zhang15e66ce2024-05-21 00:52:25 +0000216 // An empty content must have a zero length.
217 if (!contents && len != 0) {
218 return false;
219 }
220
Jane Liu54a42142017-07-24 16:40:54 -0400221 CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
222 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
Lei Zhang15e66ce2024-05-21 00:52:25 +0000223 if (!pFile || !pFile->IsDictionary() || !pDoc || len > INT_MAX) {
Jane Liu54a42142017-07-24 16:40:54 -0400224 return false;
Lei Zhang15e66ce2024-05-21 00:52:25 +0000225 }
Jane Liu54a42142017-07-24 16:40:54 -0400226
227 // Create a dictionary for the new embedded file stream.
Tom Sepeza3097da2019-05-01 16:42:36 +0000228 auto pFileStreamDict = pdfium::MakeRetain<CPDF_Dictionary>();
Tom Sepez8aad22a2022-09-22 18:56:16 +0000229 auto pParamsDict = pFileStreamDict->SetNewFor<CPDF_Dictionary>("Params");
Jane Liu54a42142017-07-24 16:40:54 -0400230
231 // Set the size of the new file in the dictionary.
Lei Zhang26170562018-04-17 17:01:52 +0000232 pFileStreamDict->SetNewFor<CPDF_Number>(pdfium::stream::kDL,
233 static_cast<int>(len));
Jane Liu54a42142017-07-24 16:40:54 -0400234 pParamsDict->SetNewFor<CPDF_Number>("Size", static_cast<int>(len));
235
236 // Set the creation date of the new attachment in the dictionary.
Dan Sinclairec2209d2017-11-16 22:08:27 +0000237 CFX_DateTime dateTime = CFX_DateTime::Now();
Dan Sinclair1c4735a2017-11-16 22:08:07 +0000238 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(),
Lei Zhang7d870c72024-05-21 16:44:46 +0000243 dateTime.GetSecond()));
Jane Liu54a42142017-07-24 16:40:54 -0400244
Lei Zhang15e66ce2024-05-21 00:52:25 +0000245 // 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
Lei Zhang295454b2024-05-22 02:10:33 +0000249 std::array<uint8_t, 16> digest;
250 CRYPT_MD5Generate(contents_span, digest.data());
Lei Zhang15e66ce2024-05-21 00:52:25 +0000251
Jane Liu54a42142017-07-24 16:40:54 -0400252 // Set the checksum of the new attachment in the dictionary.
Lei Zhang295454b2024-05-22 02:10:33 +0000253 pParamsDict->SetNewFor<CPDF_String>(kChecksumKey, digest,
254 CPDF_String::DataType::kIsHex);
Jane Liu54a42142017-07-24 16:40:54 -0400255
256 // Create the file stream and have the filespec dictionary link to it.
Lei Zhang6dc8a142022-10-20 23:08:15 +0000257 auto pFileStream = pDoc->NewIndirect<CPDF_Stream>(
Lei Zhang15e66ce2024-05-21 00:52:25 +0000258 DataVector<uint8_t>(contents_span.begin(), contents_span.end()),
Lei Zhang6dc8a142022-10-20 23:08:15 +0000259 std::move(pFileStreamDict));
Tom Sepez946c0d32024-05-09 20:47:23 +0000260
Tom Sepez8aad22a2022-09-22 18:56:16 +0000261 auto pEFDict = pFile->AsMutableDictionary()->SetNewFor<CPDF_Dictionary>("EF");
Tom Sepezf8c67a22019-05-06 17:09:15 +0000262 pEFDict->SetNewFor<CPDF_Reference>("F", pDoc, pFileStream->GetObjNum());
Jane Liu54a42142017-07-24 16:40:54 -0400263 return true;
264}
265
Hui Yingst4414ee22020-06-10 20:58:19 +0000266FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
Jane Liu18ae06d2017-07-18 10:15:16 -0400267FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment,
268 void* buffer,
Hui Yingst4414ee22020-06-10 20:58:19 +0000269 unsigned long buflen,
270 unsigned long* out_buflen) {
271 if (!out_buflen)
272 return false;
273
Jane Liu18ae06d2017-07-18 10:15:16 -0400274 CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
275 if (!pFile)
Hui Yingst4414ee22020-06-10 20:58:19 +0000276 return false;
Jane Liu18ae06d2017-07-18 10:15:16 -0400277
Tom Sepez3df809c2022-09-27 23:03:54 +0000278 CPDF_FileSpec spec(pdfium::WrapRetain(pFile));
279 RetainPtr<const CPDF_Stream> pFileStream = spec.GetFileStream();
Jane Liu18ae06d2017-07-18 10:15:16 -0400280 if (!pFileStream)
Hui Yingst4414ee22020-06-10 20:58:19 +0000281 return false;
Jane Liu18ae06d2017-07-18 10:15:16 -0400282
Tom Sepez3b679fe2024-04-09 18:49:32 +0000283 // SAFETY: required from caller.
Tom Sepez06943a92022-11-10 20:29:34 +0000284 *out_buflen = DecodeStreamMaybeCopyAndReturnLength(
285 std::move(pFileStream),
Tom Sepez3b679fe2024-04-09 18:49:32 +0000286 UNSAFE_BUFFERS(pdfium::make_span(static_cast<uint8_t*>(buffer),
287 static_cast<size_t>(buflen))));
Hui Yingst4414ee22020-06-10 20:58:19 +0000288 return true;
Jane Liu53aafa92017-07-12 19:55:02 -0400289}