xref: /aosp_15_r20/external/pdfium/fpdfsdk/fpdf_attachment_embeddertest.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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 <string>
6 #include <vector>
7 
8 #include "public/fpdf_attachment.h"
9 #include "public/fpdfview.h"
10 #include "testing/embedder_test.h"
11 #include "testing/fx_string_testhelpers.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/utils/hash.h"
14 
15 static constexpr char kDateKey[] = "CreationDate";
16 static constexpr char kChecksumKey[] = "CheckSum";
17 
18 class FPDFAttachmentEmbedderTest : public EmbedderTest {};
19 
TEST_F(FPDFAttachmentEmbedderTest,ExtractAttachments)20 TEST_F(FPDFAttachmentEmbedderTest, ExtractAttachments) {
21   // Open a file with two attachments.
22   ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
23   EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
24 
25   // Try to retrieve attachments at bad indices.
26   EXPECT_FALSE(FPDFDoc_GetAttachment(document(), -1));
27   EXPECT_FALSE(FPDFDoc_GetAttachment(document(), 2));
28 
29   // Retrieve the first attachment.
30   FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
31   ASSERT_TRUE(attachment);
32 
33   // Check that the name of the first attachment is correct.
34   unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
35   ASSERT_EQ(12u, length_bytes);
36   std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
37   EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
38   EXPECT_EQ(L"1.txt", GetPlatformWString(buf.data()));
39 
40   // Check some unsuccessful cases of FPDFAttachment_GetFile.
41   EXPECT_FALSE(FPDFAttachment_GetFile(attachment, nullptr, 0, nullptr));
42   EXPECT_FALSE(FPDFAttachment_GetFile(nullptr, nullptr, 0, &length_bytes));
43 
44   // Check that the content of the first attachment is correct.
45   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
46   std::vector<uint8_t> content_buf(length_bytes);
47   unsigned long actual_length_bytes;
48   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
49                                      length_bytes, &actual_length_bytes));
50   ASSERT_THAT(content_buf, testing::ElementsAre('t', 'e', 's', 't'));
51 
52   // Check that a non-existent key does not exist.
53   EXPECT_FALSE(FPDFAttachment_HasKey(attachment, "none"));
54 
55   // Check that the string value of a non-string dictionary entry is empty.
56   static constexpr char kSizeKey[] = "Size";
57   EXPECT_EQ(FPDF_OBJECT_NUMBER,
58             FPDFAttachment_GetValueType(attachment, kSizeKey));
59   EXPECT_EQ(2u,
60             FPDFAttachment_GetStringValue(attachment, kSizeKey, nullptr, 0));
61 
62   // Check that the creation date of the first attachment is correct.
63   length_bytes =
64       FPDFAttachment_GetStringValue(attachment, kDateKey, nullptr, 0);
65   ASSERT_EQ(48u, length_bytes);
66   buf = GetFPDFWideStringBuffer(length_bytes);
67   EXPECT_EQ(48u, FPDFAttachment_GetStringValue(attachment, kDateKey, buf.data(),
68                                                length_bytes));
69   EXPECT_EQ(L"D:20170712214438-07'00'", GetPlatformWString(buf.data()));
70 
71   // Retrieve the second attachment.
72   attachment = FPDFDoc_GetAttachment(document(), 1);
73   ASSERT_TRUE(attachment);
74 
75   // Retrieve the second attachment file.
76   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
77   content_buf.clear();
78   content_buf.resize(length_bytes);
79   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
80                                      length_bytes, &actual_length_bytes));
81   ASSERT_EQ(5869u, actual_length_bytes);
82 
83   // Check that the calculated checksum of the file data matches expectation.
84   const char kCheckSum[] = "72afcddedf554dda63c0c88e06f1ce18";
85   const wchar_t kCheckSumW[] = L"<72AFCDDEDF554DDA63C0C88E06F1CE18>";
86   const std::string generated_checksum = GenerateMD5Base16(content_buf);
87   EXPECT_EQ(kCheckSum, generated_checksum);
88 
89   // Check that the stored checksum matches expectation.
90   length_bytes =
91       FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
92   ASSERT_EQ(70u, length_bytes);
93   buf = GetFPDFWideStringBuffer(length_bytes);
94   EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
95                                                buf.data(), length_bytes));
96   EXPECT_EQ(kCheckSumW, GetPlatformWString(buf.data()));
97 }
98 
TEST_F(FPDFAttachmentEmbedderTest,NoAttachmentToExtract)99 TEST_F(FPDFAttachmentEmbedderTest, NoAttachmentToExtract) {
100   // Open a file with no attachments.
101   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
102   EXPECT_EQ(0, FPDFDoc_GetAttachmentCount(document()));
103 
104   // Try to retrieve attachments at bad indices.
105   EXPECT_FALSE(FPDFDoc_GetAttachment(document(), -1));
106   EXPECT_FALSE(FPDFDoc_GetAttachment(document(), 0));
107 }
108 
TEST_F(FPDFAttachmentEmbedderTest,InvalidAttachmentData)109 TEST_F(FPDFAttachmentEmbedderTest, InvalidAttachmentData) {
110   // Open a file with an attachment that is missing the embedded file (/EF).
111   ASSERT_TRUE(OpenDocument("embedded_attachments_invalid_data.pdf"));
112   ASSERT_EQ(1, FPDFDoc_GetAttachmentCount(document()));
113 
114   // Retrieve the first attachment.
115   FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
116   ASSERT_TRUE(attachment);
117 
118   // Check that the name of the attachment is correct.
119   unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
120   ASSERT_EQ(12u, length_bytes);
121   std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
122   EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
123   EXPECT_EQ("1.txt", GetPlatformString(buf.data()));
124 
125   // Check that is is not possible to retrieve the file data.
126   EXPECT_FALSE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
127 
128   // Check that the attachment can be deleted.
129   EXPECT_TRUE(FPDFDoc_DeleteAttachment(document(), 0));
130   EXPECT_EQ(0, FPDFDoc_GetAttachmentCount(document()));
131 }
132 
TEST_F(FPDFAttachmentEmbedderTest,AddAttachments)133 TEST_F(FPDFAttachmentEmbedderTest, AddAttachments) {
134   // Open a file with two attachments.
135   ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
136   EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
137 
138   // Check that adding an attachment with an empty name would fail.
139   EXPECT_FALSE(FPDFDoc_AddAttachment(document(), nullptr));
140 
141   // Add an attachment to the beginning of the embedded file list.
142   ScopedFPDFWideString file_name = GetFPDFWideString(L"0.txt");
143   FPDF_ATTACHMENT attachment =
144       FPDFDoc_AddAttachment(document(), file_name.get());
145   ASSERT_TRUE(attachment);
146 
147   // Check that writing to a file with nullptr but non-zero bytes would fail.
148   EXPECT_FALSE(FPDFAttachment_SetFile(attachment, document(), nullptr, 10));
149 
150   // Set the new attachment's file.
151   constexpr char kContents1[] = "Hello!";
152   EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents1,
153                                      strlen(kContents1)));
154   EXPECT_EQ(3, FPDFDoc_GetAttachmentCount(document()));
155 
156   // Verify the name of the new attachment (i.e. the first attachment).
157   attachment = FPDFDoc_GetAttachment(document(), 0);
158   ASSERT_TRUE(attachment);
159   unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
160   ASSERT_EQ(12u, length_bytes);
161   std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
162   EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
163   EXPECT_EQ(L"0.txt", GetPlatformWString(buf.data()));
164 
165   // Verify the content of the new attachment (i.e. the first attachment).
166   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
167   std::vector<char> content_buf(length_bytes);
168   unsigned long actual_length_bytes;
169   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
170                                      length_bytes, &actual_length_bytes));
171   ASSERT_EQ(6u, actual_length_bytes);
172   EXPECT_EQ(std::string(kContents1), std::string(content_buf.data(), 6));
173 
174   // Add an attachment to the end of the embedded file list and set its file.
175   file_name = GetFPDFWideString(L"z.txt");
176   attachment = FPDFDoc_AddAttachment(document(), file_name.get());
177   ASSERT_TRUE(attachment);
178   constexpr char kContents2[] = "World!";
179   EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents2,
180                                      strlen(kContents2)));
181   EXPECT_EQ(4, FPDFDoc_GetAttachmentCount(document()));
182 
183   // Verify the name of the new attachment (i.e. the fourth attachment).
184   attachment = FPDFDoc_GetAttachment(document(), 3);
185   ASSERT_TRUE(attachment);
186   length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
187   ASSERT_EQ(12u, length_bytes);
188   buf = GetFPDFWideStringBuffer(length_bytes);
189   EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
190   EXPECT_EQ(L"z.txt", GetPlatformWString(buf.data()));
191 
192   // Verify the content of the new attachment (i.e. the fourth attachment).
193   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
194   content_buf.clear();
195   content_buf.resize(length_bytes);
196   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
197                                      length_bytes, &actual_length_bytes));
198   ASSERT_EQ(6u, actual_length_bytes);
199   EXPECT_EQ(std::string(kContents2), std::string(content_buf.data(), 6));
200 }
201 
TEST_F(FPDFAttachmentEmbedderTest,AddAttachmentsWithParams)202 TEST_F(FPDFAttachmentEmbedderTest, AddAttachmentsWithParams) {
203   // Open a file with two attachments.
204   ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
205   EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
206 
207   // Add an attachment to the embedded file list.
208   ScopedFPDFWideString file_name = GetFPDFWideString(L"5.txt");
209   FPDF_ATTACHMENT attachment =
210       FPDFDoc_AddAttachment(document(), file_name.get());
211   ASSERT_TRUE(attachment);
212   constexpr char kContents[] = "Hello World!";
213   EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents,
214                                      strlen(kContents)));
215 
216   // Set the date to be an arbitrary value.
217   constexpr wchar_t kDateW[] = L"D:20170720161527-04'00'";
218   ScopedFPDFWideString ws_date = GetFPDFWideString(kDateW);
219   EXPECT_TRUE(
220       FPDFAttachment_SetStringValue(attachment, kDateKey, ws_date.get()));
221 
222   // Set the checksum to be an arbitrary value.
223   constexpr wchar_t kCheckSumW[] = L"<ABCDEF01234567899876543210FEDCBA>";
224   ScopedFPDFWideString ws_checksum = GetFPDFWideString(kCheckSumW);
225   EXPECT_TRUE(FPDFAttachment_SetStringValue(attachment, kChecksumKey,
226                                             ws_checksum.get()));
227 
228   // Verify the name of the new attachment (i.e. the second attachment).
229   attachment = FPDFDoc_GetAttachment(document(), 1);
230   ASSERT_TRUE(attachment);
231   unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
232   ASSERT_EQ(12u, length_bytes);
233   std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
234   EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
235   EXPECT_EQ(L"5.txt", GetPlatformWString(buf.data()));
236 
237   // Verify the content of the new attachment.
238   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
239   std::vector<char> content_buf(length_bytes);
240   unsigned long actual_length_bytes;
241   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
242                                      length_bytes, &actual_length_bytes));
243   ASSERT_EQ(12u, actual_length_bytes);
244   EXPECT_EQ(std::string(kContents), std::string(content_buf.data(), 12));
245 
246   // Verify the creation date of the new attachment.
247   length_bytes =
248       FPDFAttachment_GetStringValue(attachment, kDateKey, nullptr, 0);
249   ASSERT_EQ(48u, length_bytes);
250   buf = GetFPDFWideStringBuffer(length_bytes);
251   EXPECT_EQ(48u, FPDFAttachment_GetStringValue(attachment, kDateKey, buf.data(),
252                                                length_bytes));
253   EXPECT_EQ(kDateW, GetPlatformWString(buf.data()));
254 
255   // Verify the checksum of the new attachment.
256   length_bytes =
257       FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
258   ASSERT_EQ(70u, length_bytes);
259   buf = GetFPDFWideStringBuffer(length_bytes);
260   EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
261                                                buf.data(), length_bytes));
262   EXPECT_EQ(kCheckSumW, GetPlatformWString(buf.data()));
263 
264   // Overwrite the existing file with empty content, and check that the checksum
265   // gets updated to the correct value.
266   EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), nullptr, 0));
267   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
268   EXPECT_EQ(0u, length_bytes);
269   length_bytes =
270       FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
271   ASSERT_EQ(70u, length_bytes);
272   buf = GetFPDFWideStringBuffer(length_bytes);
273   EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
274                                                buf.data(), length_bytes));
275   EXPECT_EQ(L"<D41D8CD98F00B204E9800998ECF8427E>",
276             GetPlatformWString(buf.data()));
277 }
278 
TEST_F(FPDFAttachmentEmbedderTest,AddAttachmentsToFileWithNoAttachments)279 TEST_F(FPDFAttachmentEmbedderTest, AddAttachmentsToFileWithNoAttachments) {
280   // Open a file with no attachments.
281   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
282   EXPECT_EQ(0, FPDFDoc_GetAttachmentCount(document()));
283 
284   // Add an attachment to the beginning of the embedded file list.
285   ScopedFPDFWideString file_name = GetFPDFWideString(L"0.txt");
286   FPDF_ATTACHMENT attachment =
287       FPDFDoc_AddAttachment(document(), file_name.get());
288   ASSERT_TRUE(attachment);
289 
290   // Set the new attachment's file.
291   constexpr char kContents1[] = "Hello!";
292   EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents1,
293                                      strlen(kContents1)));
294   EXPECT_EQ(1, FPDFDoc_GetAttachmentCount(document()));
295 
296   // Verify the name of the new attachment (i.e. the first attachment).
297   attachment = FPDFDoc_GetAttachment(document(), 0);
298   ASSERT_TRUE(attachment);
299   unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
300   ASSERT_EQ(12u, length_bytes);
301   std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
302   EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
303   EXPECT_EQ(L"0.txt", GetPlatformWString(buf.data()));
304 
305   // Verify the content of the new attachment (i.e. the first attachment).
306   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
307   std::vector<char> content_buf(length_bytes);
308   unsigned long actual_length_bytes;
309   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
310                                      length_bytes, &actual_length_bytes));
311   ASSERT_EQ(6u, actual_length_bytes);
312   EXPECT_EQ(std::string(kContents1), std::string(content_buf.data(), 6));
313 
314   // Add an attachment to the end of the embedded file list and set its file.
315   file_name = GetFPDFWideString(L"z.txt");
316   attachment = FPDFDoc_AddAttachment(document(), file_name.get());
317   ASSERT_TRUE(attachment);
318   constexpr char kContents2[] = "World!";
319   EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents2,
320                                      strlen(kContents2)));
321   EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
322 
323   // Verify the name of the new attachment (i.e. the second attachment).
324   attachment = FPDFDoc_GetAttachment(document(), 1);
325   ASSERT_TRUE(attachment);
326   length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
327   ASSERT_EQ(12u, length_bytes);
328   buf = GetFPDFWideStringBuffer(length_bytes);
329   EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
330   EXPECT_EQ(L"z.txt", GetPlatformWString(buf.data()));
331 
332   // Verify the content of the new attachment (i.e. the second attachment).
333   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes));
334   content_buf.clear();
335   content_buf.resize(length_bytes);
336   ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(),
337                                      length_bytes, &actual_length_bytes));
338   ASSERT_EQ(6u, actual_length_bytes);
339   EXPECT_EQ(std::string(kContents2), std::string(content_buf.data(), 6));
340 }
341 
TEST_F(FPDFAttachmentEmbedderTest,DeleteAttachment)342 TEST_F(FPDFAttachmentEmbedderTest, DeleteAttachment) {
343   // Open a file with two attachments.
344   ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
345   EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
346 
347   // Verify the name of the first attachment.
348   FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
349   ASSERT_TRUE(attachment);
350   unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
351   ASSERT_EQ(12u, length_bytes);
352   std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
353   EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
354   EXPECT_EQ(L"1.txt", GetPlatformWString(buf.data()));
355 
356   // Delete the first attachment.
357   EXPECT_TRUE(FPDFDoc_DeleteAttachment(document(), 0));
358   EXPECT_EQ(1, FPDFDoc_GetAttachmentCount(document()));
359 
360   // Verify the name of the new first attachment.
361   attachment = FPDFDoc_GetAttachment(document(), 0);
362   ASSERT_TRUE(attachment);
363   length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
364   ASSERT_EQ(26u, length_bytes);
365   buf = GetFPDFWideStringBuffer(length_bytes);
366   EXPECT_EQ(26u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
367   EXPECT_EQ(L"attached.pdf", GetPlatformWString(buf.data()));
368 }
369