Basic APIs and tests for adding attachments

1. Added APIs for adding attachments, setting attachment files, and
modifying attachment dictionary entries.
    * Added two embedder tests covering all new APIs.

Bug=pdfium:174

Change-Id: I65f43cd6ca4887b71f9f7bcee64a87ba6b7e2706
Reviewed-on: https://pdfium-review.googlesource.com/8671
Commit-Queue: Jane Liu <janeliulwq@google.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: dsinclair <dsinclair@chromium.org>
diff --git a/fpdfsdk/fpdfattachment_embeddertest.cpp b/fpdfsdk/fpdfattachment_embeddertest.cpp
index d873d9b..f4d0bfc 100644
--- a/fpdfsdk/fpdfattachment_embeddertest.cpp
+++ b/fpdfsdk/fpdfattachment_embeddertest.cpp
@@ -85,3 +85,153 @@
   EXPECT_EQ(kCheckSumW,
             GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data())));
 }
+
+TEST_F(FPDFAttachmentEmbeddertest, AddAttachments) {
+  // Open a file with two attachments.
+  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
+  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
+
+  // Check that adding an attachment with an empty name would fail.
+  EXPECT_FALSE(FPDFDoc_AddAttachment(document(), nullptr));
+
+  // Add an attachment to the beginning of the embedded file list.
+  std::unique_ptr<unsigned short, pdfium::FreeDeleter> file_name =
+      GetFPDFWideString(L"0.txt");
+  FPDF_ATTACHMENT attachment =
+      FPDFDoc_AddAttachment(document(), file_name.get());
+
+  // Check that writing to a file with nullptr but non-zero bytes would fail.
+  EXPECT_FALSE(FPDFAttachment_SetFile(attachment, document(), nullptr, 10));
+
+  // Set the new attachment's file.
+  constexpr char kContents1[] = "Hello!";
+  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents1,
+                                     strlen(kContents1)));
+
+  // Verify the name of the new attachment (i.e. the first attachment).
+  attachment = FPDFDoc_GetAttachment(document(), 0);
+  ASSERT_TRUE(attachment);
+  unsigned long len = FPDFAttachment_GetName(attachment, nullptr, 0);
+  std::vector<char> buf(len);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), len));
+  EXPECT_STREQ(L"0.txt",
+               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
+                   .c_str());
+
+  // Verify the content of the new attachment (i.e. the first attachment).
+  len = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  ASSERT_EQ(6u, FPDFAttachment_GetFile(attachment, buf.data(), len));
+  EXPECT_EQ(std::string(kContents1), std::string(buf.data(), 6));
+
+  // Add an attachment to the end of the embedded file list and set its file.
+  file_name = GetFPDFWideString(L"z.txt");
+  attachment = FPDFDoc_AddAttachment(document(), file_name.get());
+  constexpr char kContents2[] = "World!";
+  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents2,
+                                     strlen(kContents2)));
+  EXPECT_EQ(4, FPDFDoc_GetAttachmentCount(document()));
+
+  // Verify the name of the new attachment (i.e. the fourth attachment).
+  attachment = FPDFDoc_GetAttachment(document(), 3);
+  ASSERT_TRUE(attachment);
+  len = FPDFAttachment_GetName(attachment, nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), len));
+  EXPECT_STREQ(L"z.txt",
+               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
+                   .c_str());
+
+  // Verify the content of the new attachment (i.e. the fourth attachment).
+  len = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  ASSERT_EQ(6u, FPDFAttachment_GetFile(attachment, buf.data(), len));
+  EXPECT_EQ(std::string(kContents2), std::string(buf.data(), 6));
+}
+
+TEST_F(FPDFAttachmentEmbeddertest, AddAttachmentsWithParams) {
+  // Open a file with two attachments.
+  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
+  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
+
+  // Add an attachment to the embedded file list.
+  std::unique_ptr<unsigned short, pdfium::FreeDeleter> file_name =
+      GetFPDFWideString(L"5.txt");
+  FPDF_ATTACHMENT attachment =
+      FPDFDoc_AddAttachment(document(), file_name.get());
+  constexpr char kContents[] = "Hello World!";
+  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents,
+                                     strlen(kContents)));
+
+  // Set the date to be an arbitrary value.
+  std::unique_ptr<unsigned short, pdfium::FreeDeleter> date_key =
+      GetFPDFWideString(L"CreationDate");
+  constexpr wchar_t kDateW[] = L"D:20170720161527-04'00'";
+  std::unique_ptr<unsigned short, pdfium::FreeDeleter> ws_date =
+      GetFPDFWideString(kDateW);
+  EXPECT_TRUE(
+      FPDFAttachment_SetStringValue(attachment, date_key.get(), ws_date.get()));
+
+  // Set the checksum to be an arbitrary value.
+  std::unique_ptr<unsigned short, pdfium::FreeDeleter> checksum_key =
+      GetFPDFWideString(L"CheckSum");
+  constexpr wchar_t kCheckSumW[] = L"<ABCDEF01234567899876543210FEDCBA>";
+  std::unique_ptr<unsigned short, pdfium::FreeDeleter> ws_checksum =
+      GetFPDFWideString(kCheckSumW);
+  EXPECT_TRUE(FPDFAttachment_SetStringValue(attachment, checksum_key.get(),
+                                            ws_checksum.get()));
+
+  // Verify the name of the new attachment (i.e. the second attachment).
+  attachment = FPDFDoc_GetAttachment(document(), 1);
+  ASSERT_TRUE(attachment);
+  unsigned long len = FPDFAttachment_GetName(attachment, nullptr, 0);
+  std::vector<char> buf(len);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), len));
+  EXPECT_STREQ(L"5.txt",
+               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
+                   .c_str());
+
+  // Verify the content of the new attachment.
+  len = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  ASSERT_EQ(12u, FPDFAttachment_GetFile(attachment, buf.data(), len));
+  EXPECT_EQ(std::string(kContents), std::string(buf.data(), 12));
+
+  // Verify the creation date of the new attachment.
+  len = FPDFAttachment_GetStringValue(attachment, date_key.get(), nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  EXPECT_EQ(48u, FPDFAttachment_GetStringValue(attachment, date_key.get(),
+                                               buf.data(), len));
+  EXPECT_STREQ(kDateW,
+               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
+                   .c_str());
+
+  // Verify the checksum of the new attachment.
+  len =
+      FPDFAttachment_GetStringValue(attachment, checksum_key.get(), nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, checksum_key.get(),
+                                               buf.data(), len));
+  EXPECT_STREQ(kCheckSumW,
+               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
+                   .c_str());
+
+  // Overwrite the existing file with empty content, and check that the checksum
+  // gets updated to the correct value.
+  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), nullptr, 0));
+  EXPECT_EQ(0u, FPDFAttachment_GetFile(attachment, nullptr, 0));
+  len =
+      FPDFAttachment_GetStringValue(attachment, checksum_key.get(), nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, checksum_key.get(),
+                                               buf.data(), len));
+  EXPECT_EQ(L"<D41D8CD98F00B204E9800998ECF8427E>",
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data())));
+}
\ No newline at end of file