Home | History | Annotate | Download | only in fpdfsdk
      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 "public/fpdf_attachment.h"
      6 
      7 #include <memory>
      8 #include <utility>
      9 
     10 #include "core/fdrm/crypto/fx_crypt.h"
     11 #include "core/fpdfapi/parser/cpdf_array.h"
     12 #include "core/fpdfapi/parser/cpdf_document.h"
     13 #include "core/fpdfapi/parser/cpdf_name.h"
     14 #include "core/fpdfapi/parser/cpdf_number.h"
     15 #include "core/fpdfapi/parser/cpdf_reference.h"
     16 #include "core/fpdfapi/parser/cpdf_string.h"
     17 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
     18 #include "core/fpdfdoc/cpdf_filespec.h"
     19 #include "core/fpdfdoc/cpdf_nametree.h"
     20 #include "core/fxcrt/cfx_datetime.h"
     21 #include "core/fxcrt/fx_extension.h"
     22 #include "fpdfsdk/fsdk_define.h"
     23 
     24 namespace {
     25 
     26 constexpr char kChecksumKey[] = "CheckSum";
     27 
     28 ByteString CFXByteStringHexDecode(const ByteString& bsHex) {
     29   uint8_t* result = nullptr;
     30   uint32_t size = 0;
     31   HexDecode(bsHex.raw_str(), bsHex.GetLength(), &result, &size);
     32   ByteString bsDecoded(result, size);
     33   FX_Free(result);
     34   return bsDecoded;
     35 }
     36 
     37 ByteString GenerateMD5Base16(const void* contents, const unsigned long len) {
     38   uint8_t digest[16];
     39   CRYPT_MD5Generate(reinterpret_cast<const uint8_t*>(contents), len, digest);
     40   char buf[32];
     41   for (int i = 0; i < 16; ++i)
     42     FXSYS_IntToTwoHexChars(digest[i], &buf[i * 2]);
     43 
     44   return ByteString(buf, 32);
     45 }
     46 
     47 }  // namespace
     48 
     49 FPDF_EXPORT int FPDF_CALLCONV
     50 FPDFDoc_GetAttachmentCount(FPDF_DOCUMENT document) {
     51   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
     52   if (!pDoc)
     53     return 0;
     54 
     55   return CPDF_NameTree(pDoc, "EmbeddedFiles").GetCount();
     56 }
     57 
     58 FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
     59 FPDFDoc_AddAttachment(FPDF_DOCUMENT document, FPDF_WIDESTRING name) {
     60   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
     61   WideString wsName =
     62       WideString::FromUTF16LE(name, WideString::WStringLength(name));
     63   if (!pDoc || wsName.IsEmpty())
     64     return nullptr;
     65 
     66   CPDF_Dictionary* pRoot = pDoc->GetRoot();
     67   if (!pRoot)
     68     return nullptr;
     69 
     70   // Retrieve the document's Names dictionary; create it if missing.
     71   CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
     72   if (!pNames) {
     73     pNames = pDoc->NewIndirect<CPDF_Dictionary>();
     74     pRoot->SetNewFor<CPDF_Reference>("Names", pDoc, pNames->GetObjNum());
     75   }
     76 
     77   // Create the EmbeddedFiles dictionary if missing.
     78   if (!pNames->GetDictFor("EmbeddedFiles")) {
     79     CPDF_Dictionary* pFiles = pDoc->NewIndirect<CPDF_Dictionary>();
     80     pFiles->SetNewFor<CPDF_Array>("Names");
     81     pNames->SetNewFor<CPDF_Reference>("EmbeddedFiles", pDoc,
     82                                       pFiles->GetObjNum());
     83   }
     84 
     85   // Set up the basic entries in the filespec dictionary.
     86   CPDF_Dictionary* pFile = pDoc->NewIndirect<CPDF_Dictionary>();
     87   pFile->SetNewFor<CPDF_Name>("Type", "Filespec");
     88   pFile->SetNewFor<CPDF_String>("UF", wsName);
     89   pFile->SetNewFor<CPDF_String>("F", wsName);
     90 
     91   // Add the new attachment name and filespec into the document's EmbeddedFiles.
     92   CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
     93   if (!nameTree.AddValueAndName(
     94           pdfium::MakeUnique<CPDF_Reference>(pDoc, pFile->GetObjNum()),
     95           wsName)) {
     96     return nullptr;
     97   }
     98 
     99   return pFile;
    100 }
    101 
    102 FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
    103 FPDFDoc_GetAttachment(FPDF_DOCUMENT document, int index) {
    104   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
    105   if (!pDoc || index < 0)
    106     return nullptr;
    107 
    108   CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
    109   if (static_cast<size_t>(index) >= nameTree.GetCount())
    110     return nullptr;
    111 
    112   WideString csName;
    113   return nameTree.LookupValueAndName(index, &csName);
    114 }
    115 
    116 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    117 FPDFDoc_DeleteAttachment(FPDF_DOCUMENT document, int index) {
    118   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
    119   if (!pDoc || index < 0)
    120     return false;
    121 
    122   CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
    123   if (static_cast<size_t>(index) >= nameTree.GetCount())
    124     return false;
    125 
    126   return nameTree.DeleteValueAndName(index);
    127 }
    128 
    129 FPDF_EXPORT unsigned long FPDF_CALLCONV
    130 FPDFAttachment_GetName(FPDF_ATTACHMENT attachment,
    131                        void* buffer,
    132                        unsigned long buflen) {
    133   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
    134   if (!pFile)
    135     return 0;
    136 
    137   return Utf16EncodeMaybeCopyAndReturnLength(CPDF_FileSpec(pFile).GetFileName(),
    138                                              buffer, buflen);
    139 }
    140 
    141 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    142 FPDFAttachment_HasKey(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) {
    143   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
    144   if (!pFile)
    145     return 0;
    146 
    147   CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
    148   return pParamsDict ? pParamsDict->KeyExist(key) : 0;
    149 }
    150 
    151 FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
    152 FPDFAttachment_GetValueType(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) {
    153   if (!FPDFAttachment_HasKey(attachment, key))
    154     return FPDF_OBJECT_UNKNOWN;
    155 
    156   CPDF_FileSpec spec(CPDFObjectFromFPDFAttachment(attachment));
    157   CPDF_Object* pObj = spec.GetParamsDict()->GetObjectFor(key);
    158   return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
    159 }
    160 
    161 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    162 FPDFAttachment_SetStringValue(FPDF_ATTACHMENT attachment,
    163                               FPDF_BYTESTRING key,
    164                               FPDF_WIDESTRING value) {
    165   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
    166   if (!pFile)
    167     return false;
    168 
    169   CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
    170   if (!pParamsDict)
    171     return false;
    172 
    173   ByteString bsKey = key;
    174   ByteString bsValue = CFXByteStringFromFPDFWideString(value);
    175   bool bEncodedAsHex = bsKey == kChecksumKey;
    176   if (bEncodedAsHex)
    177     bsValue = CFXByteStringHexDecode(bsValue);
    178 
    179   pParamsDict->SetNewFor<CPDF_String>(bsKey, bsValue, bEncodedAsHex);
    180   return true;
    181 }
    182 
    183 FPDF_EXPORT unsigned long FPDF_CALLCONV
    184 FPDFAttachment_GetStringValue(FPDF_ATTACHMENT attachment,
    185                               FPDF_BYTESTRING key,
    186                               void* buffer,
    187                               unsigned long buflen) {
    188   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
    189   if (!pFile)
    190     return 0;
    191 
    192   CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
    193   if (!pParamsDict)
    194     return 0;
    195 
    196   ByteString bsKey = key;
    197   WideString value = pParamsDict->GetUnicodeTextFor(bsKey);
    198   if (bsKey == kChecksumKey && !value.IsEmpty()) {
    199     CPDF_String* stringValue = pParamsDict->GetObjectFor(bsKey)->AsString();
    200     if (stringValue->IsHex()) {
    201       ByteString encoded = PDF_EncodeString(stringValue->GetString(), true);
    202       value = CPDF_String(nullptr, encoded, false).GetUnicodeText();
    203     }
    204   }
    205 
    206   return Utf16EncodeMaybeCopyAndReturnLength(value, buffer, buflen);
    207 }
    208 
    209 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    210 FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment,
    211                        FPDF_DOCUMENT document,
    212                        const void* contents,
    213                        const unsigned long len) {
    214   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
    215   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
    216   if (!pFile || !pFile->IsDictionary() || !pDoc || len > INT_MAX)
    217     return false;
    218 
    219   // An empty content must have a zero length.
    220   if (!contents && len != 0)
    221     return false;
    222 
    223   // Create a dictionary for the new embedded file stream.
    224   auto pFileStreamDict = pdfium::MakeUnique<CPDF_Dictionary>();
    225   CPDF_Dictionary* pParamsDict =
    226       pFileStreamDict->SetNewFor<CPDF_Dictionary>("Params");
    227 
    228   // Set the size of the new file in the dictionary.
    229   pFileStreamDict->SetNewFor<CPDF_Number>("DL", static_cast<int>(len));
    230   pParamsDict->SetNewFor<CPDF_Number>("Size", static_cast<int>(len));
    231 
    232   // Set the creation date of the new attachment in the dictionary.
    233   CFX_DateTime dateTime = CFX_DateTime::Now();
    234   pParamsDict->SetNewFor<CPDF_String>(
    235       "CreationDate",
    236       ByteString::Format("D:%d%02d%02d%02d%02d%02d", dateTime.GetYear(),
    237                          dateTime.GetMonth(), dateTime.GetDay(),
    238                          dateTime.GetHour(), dateTime.GetMinute(),
    239                          dateTime.GetSecond()),
    240       false);
    241 
    242   // Set the checksum of the new attachment in the dictionary.
    243   pParamsDict->SetNewFor<CPDF_String>(
    244       kChecksumKey, CFXByteStringHexDecode(GenerateMD5Base16(contents, len)),
    245       true);
    246 
    247   // Create the file stream and have the filespec dictionary link to it.
    248   std::unique_ptr<uint8_t, FxFreeDeleter> stream(FX_Alloc(uint8_t, len));
    249   memcpy(stream.get(), contents, len);
    250   CPDF_Stream* pFileStream = pDoc->NewIndirect<CPDF_Stream>(
    251       std::move(stream), len, std::move(pFileStreamDict));
    252   CPDF_Dictionary* pEFDict =
    253       pFile->AsDictionary()->SetNewFor<CPDF_Dictionary>("EF");
    254   pEFDict->SetNewFor<CPDF_Reference>("F", pDoc, pFileStream->GetObjNum());
    255   return true;
    256 }
    257 
    258 FPDF_EXPORT unsigned long FPDF_CALLCONV
    259 FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment,
    260                        void* buffer,
    261                        unsigned long buflen) {
    262   CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
    263   if (!pFile)
    264     return 0;
    265 
    266   CPDF_Stream* pFileStream = CPDF_FileSpec(pFile).GetFileStream();
    267   if (!pFileStream)
    268     return 0;
    269 
    270   return DecodeStreamMaybeCopyAndReturnLength(pFileStream, buffer, buflen);
    271 }
    272