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