1 // Copyright 2017 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 <memory>
6 #include <string>
7 #include <vector>
8
9 #include "public/fpdf_attachment.h"
10 #include "public/fpdfview.h"
11 #include "testing/embedder_test.h"
12 #include "testing/fx_string_testhelpers.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 // Retrieve the first attachment.
26 FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
27 ASSERT_TRUE(attachment);
28
29 // Check that the name of the first attachment is correct.
30 unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
31 ASSERT_EQ(12u, length_bytes);
32 std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
33 EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
34 EXPECT_EQ(L"1.txt", GetPlatformWString(buf.data()));
35
36 // Check that the content of the first attachment is correct.
37 length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
38 std::vector<char> content_buf(length_bytes);
39 ASSERT_EQ(
40 4u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes));
41 EXPECT_EQ(std::string("test"), std::string(content_buf.data(), 4));
42
43 // Check that a non-existent key does not exist.
44 EXPECT_FALSE(FPDFAttachment_HasKey(attachment, "none"));
45
46 // Check that the string value of a non-string dictionary entry is empty.
47 static constexpr char kSizeKey[] = "Size";
48 EXPECT_EQ(FPDF_OBJECT_NUMBER,
49 FPDFAttachment_GetValueType(attachment, kSizeKey));
50 EXPECT_EQ(2u,
51 FPDFAttachment_GetStringValue(attachment, kSizeKey, nullptr, 0));
52
53 // Check that the creation date of the first attachment is correct.
54 length_bytes =
55 FPDFAttachment_GetStringValue(attachment, kDateKey, nullptr, 0);
56 ASSERT_EQ(48u, length_bytes);
57 buf = GetFPDFWideStringBuffer(length_bytes);
58 EXPECT_EQ(48u, FPDFAttachment_GetStringValue(attachment, kDateKey, buf.data(),
59 length_bytes));
60 EXPECT_EQ(L"D:20170712214438-07'00'", GetPlatformWString(buf.data()));
61
62 // Retrieve the second attachment.
63 attachment = FPDFDoc_GetAttachment(document(), 1);
64 ASSERT_TRUE(attachment);
65
66 // Retrieve the second attachment file.
67 length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
68 content_buf.clear();
69 content_buf.resize(length_bytes);
70 ASSERT_EQ(5869u, FPDFAttachment_GetFile(attachment, content_buf.data(),
71 length_bytes));
72
73 // Check that the calculated checksum of the file data matches expectation.
74 const char kCheckSum[] = "72afcddedf554dda63c0c88e06f1ce18";
75 const wchar_t kCheckSumW[] = L"<72AFCDDEDF554DDA63C0C88E06F1CE18>";
76 const std::string generated_checksum = GenerateMD5Base16(
77 reinterpret_cast<uint8_t*>(content_buf.data()), length_bytes);
78 EXPECT_EQ(kCheckSum, generated_checksum);
79
80 // Check that the stored checksum matches expectation.
81 length_bytes =
82 FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
83 ASSERT_EQ(70u, length_bytes);
84 buf = GetFPDFWideStringBuffer(length_bytes);
85 EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
86 buf.data(), length_bytes));
87 EXPECT_EQ(kCheckSumW, GetPlatformWString(buf.data()));
88 }
89
TEST_F(FPDFAttachmentEmbedderTest,AddAttachments)90 TEST_F(FPDFAttachmentEmbedderTest, AddAttachments) {
91 // Open a file with two attachments.
92 ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
93 EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
94
95 // Check that adding an attachment with an empty name would fail.
96 EXPECT_FALSE(FPDFDoc_AddAttachment(document(), nullptr));
97
98 // Add an attachment to the beginning of the embedded file list.
99 ScopedFPDFWideString file_name = GetFPDFWideString(L"0.txt");
100 FPDF_ATTACHMENT attachment =
101 FPDFDoc_AddAttachment(document(), file_name.get());
102
103 // Check that writing to a file with nullptr but non-zero bytes would fail.
104 EXPECT_FALSE(FPDFAttachment_SetFile(attachment, document(), nullptr, 10));
105
106 // Set the new attachment's file.
107 constexpr char kContents1[] = "Hello!";
108 EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents1,
109 strlen(kContents1)));
110
111 // Verify the name of the new attachment (i.e. the first attachment).
112 attachment = FPDFDoc_GetAttachment(document(), 0);
113 ASSERT_TRUE(attachment);
114 unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
115 ASSERT_EQ(12u, length_bytes);
116 std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
117 EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
118 EXPECT_EQ(L"0.txt", GetPlatformWString(buf.data()));
119
120 // Verify the content of the new attachment (i.e. the first attachment).
121 length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
122 std::vector<char> content_buf(length_bytes);
123 ASSERT_EQ(
124 6u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes));
125 EXPECT_EQ(std::string(kContents1), std::string(content_buf.data(), 6));
126
127 // Add an attachment to the end of the embedded file list and set its file.
128 file_name = GetFPDFWideString(L"z.txt");
129 attachment = FPDFDoc_AddAttachment(document(), file_name.get());
130 constexpr char kContents2[] = "World!";
131 EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents2,
132 strlen(kContents2)));
133 EXPECT_EQ(4, FPDFDoc_GetAttachmentCount(document()));
134
135 // Verify the name of the new attachment (i.e. the fourth attachment).
136 attachment = FPDFDoc_GetAttachment(document(), 3);
137 ASSERT_TRUE(attachment);
138 length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
139 ASSERT_EQ(12u, length_bytes);
140 buf = GetFPDFWideStringBuffer(length_bytes);
141 EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
142 EXPECT_EQ(L"z.txt", GetPlatformWString(buf.data()));
143
144 // Verify the content of the new attachment (i.e. the fourth attachment).
145 length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
146 content_buf.clear();
147 content_buf.resize(length_bytes);
148 ASSERT_EQ(
149 6u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes));
150 EXPECT_EQ(std::string(kContents2), std::string(content_buf.data(), 6));
151 }
152
TEST_F(FPDFAttachmentEmbedderTest,AddAttachmentsWithParams)153 TEST_F(FPDFAttachmentEmbedderTest, AddAttachmentsWithParams) {
154 // Open a file with two attachments.
155 ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
156 EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
157
158 // Add an attachment to the embedded file list.
159 ScopedFPDFWideString file_name = GetFPDFWideString(L"5.txt");
160 FPDF_ATTACHMENT attachment =
161 FPDFDoc_AddAttachment(document(), file_name.get());
162 constexpr char kContents[] = "Hello World!";
163 EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents,
164 strlen(kContents)));
165
166 // Set the date to be an arbitrary value.
167 constexpr wchar_t kDateW[] = L"D:20170720161527-04'00'";
168 ScopedFPDFWideString ws_date = GetFPDFWideString(kDateW);
169 EXPECT_TRUE(
170 FPDFAttachment_SetStringValue(attachment, kDateKey, ws_date.get()));
171
172 // Set the checksum to be an arbitrary value.
173 constexpr wchar_t kCheckSumW[] = L"<ABCDEF01234567899876543210FEDCBA>";
174 ScopedFPDFWideString ws_checksum = GetFPDFWideString(kCheckSumW);
175 EXPECT_TRUE(FPDFAttachment_SetStringValue(attachment, kChecksumKey,
176 ws_checksum.get()));
177
178 // Verify the name of the new attachment (i.e. the second attachment).
179 attachment = FPDFDoc_GetAttachment(document(), 1);
180 ASSERT_TRUE(attachment);
181 unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
182 ASSERT_EQ(12u, length_bytes);
183 std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
184 EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
185 EXPECT_EQ(L"5.txt", GetPlatformWString(buf.data()));
186
187 // Verify the content of the new attachment.
188 length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
189 std::vector<char> content_buf(length_bytes);
190 ASSERT_EQ(12u, FPDFAttachment_GetFile(attachment, content_buf.data(),
191 length_bytes));
192 EXPECT_EQ(std::string(kContents), std::string(content_buf.data(), 12));
193
194 // Verify the creation date of the new attachment.
195 length_bytes =
196 FPDFAttachment_GetStringValue(attachment, kDateKey, nullptr, 0);
197 ASSERT_EQ(48u, length_bytes);
198 buf = GetFPDFWideStringBuffer(length_bytes);
199 EXPECT_EQ(48u, FPDFAttachment_GetStringValue(attachment, kDateKey, buf.data(),
200 length_bytes));
201 EXPECT_EQ(kDateW, GetPlatformWString(buf.data()));
202
203 // Verify the checksum of the new attachment.
204 length_bytes =
205 FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
206 ASSERT_EQ(70u, length_bytes);
207 buf = GetFPDFWideStringBuffer(length_bytes);
208 EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
209 buf.data(), length_bytes));
210 EXPECT_EQ(kCheckSumW, GetPlatformWString(buf.data()));
211
212 // Overwrite the existing file with empty content, and check that the checksum
213 // gets updated to the correct value.
214 EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), nullptr, 0));
215 EXPECT_EQ(0u, FPDFAttachment_GetFile(attachment, nullptr, 0));
216 length_bytes =
217 FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
218 ASSERT_EQ(70u, length_bytes);
219 buf = GetFPDFWideStringBuffer(length_bytes);
220 EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
221 buf.data(), length_bytes));
222 EXPECT_EQ(L"<D41D8CD98F00B204E9800998ECF8427E>",
223 GetPlatformWString(buf.data()));
224 }
225
TEST_F(FPDFAttachmentEmbedderTest,DeleteAttachment)226 TEST_F(FPDFAttachmentEmbedderTest, DeleteAttachment) {
227 // Open a file with two attachments.
228 ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
229 EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
230
231 // Verify the name of the first attachment.
232 FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
233 unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
234 ASSERT_EQ(12u, length_bytes);
235 std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
236 EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
237 EXPECT_EQ(L"1.txt", GetPlatformWString(buf.data()));
238
239 // Delete the first attachment.
240 EXPECT_TRUE(FPDFDoc_DeleteAttachment(document(), 0));
241 EXPECT_EQ(1, FPDFDoc_GetAttachmentCount(document()));
242
243 // Verify the name of the new first attachment.
244 attachment = FPDFDoc_GetAttachment(document(), 0);
245 length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
246 ASSERT_EQ(26u, length_bytes);
247 buf = GetFPDFWideStringBuffer(length_bytes);
248 EXPECT_EQ(26u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
249 EXPECT_EQ(L"attached.pdf", GetPlatformWString(buf.data()));
250 }
251