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 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 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 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 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