Home | History | Annotate | Download | only in fxjs
      1 // Copyright 2014 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "fxjs/cjs_globaldata.h"
      8 
      9 #include <utility>
     10 
     11 #include "core/fdrm/crypto/fx_crypt.h"
     12 #include "third_party/base/ptr_util.h"
     13 #include "third_party/base/stl_util.h"
     14 
     15 #define JS_MAXGLOBALDATA (1024 * 4 - 8)
     16 
     17 #define READER_JS_GLOBALDATA_FILENAME L"Reader_JsGlobal.Data"
     18 #define PHANTOM_JS_GLOBALDATA_FILENAME L"Phantom_JsGlobal.Data"
     19 #define SDK_JS_GLOBALDATA_FILENAME L"SDK_JsGlobal.Data"
     20 
     21 namespace {
     22 
     23 const uint8_t JS_RC4KEY[] = {
     24     0x19, 0xa8, 0xe8, 0x01, 0xf6, 0xa8, 0xb6, 0x4d, 0x82, 0x04, 0x45, 0x6d,
     25     0xb4, 0xcf, 0xd7, 0x77, 0x67, 0xf9, 0x75, 0x9f, 0xf0, 0xe0, 0x1e, 0x51,
     26     0xee, 0x46, 0xfd, 0x0b, 0xc9, 0x93, 0x25, 0x55, 0x4a, 0xee, 0xe0, 0x16,
     27     0xd0, 0xdf, 0x8c, 0xfa, 0x2a, 0xa9, 0x49, 0xfd, 0x97, 0x1c, 0x0e, 0x22,
     28     0x13, 0x28, 0x7c, 0xaf, 0xc4, 0xfc, 0x9c, 0x12, 0x65, 0x8c, 0x4e, 0x5b,
     29     0x04, 0x75, 0x89, 0xc9, 0xb1, 0xed, 0x50, 0xca, 0x96, 0x6f, 0x1a, 0x7a,
     30     0xfe, 0x58, 0x5d, 0xec, 0x19, 0x4a, 0xf6, 0x35, 0x6a, 0x97, 0x14, 0x00,
     31     0x0e, 0xd0, 0x6b, 0xbb, 0xd5, 0x75, 0x55, 0x8b, 0x6e, 0x6b, 0x19, 0xa0,
     32     0xf8, 0x77, 0xd5, 0xa3};
     33 
     34 // Returns true if non-empty, setting sPropName
     35 bool TrimPropName(ByteString* sPropName) {
     36   sPropName->Trim();
     37   return sPropName->GetLength() != 0;
     38 }
     39 
     40 CJS_GlobalData* g_pInstance = nullptr;
     41 
     42 }  // namespace
     43 
     44 // static
     45 CJS_GlobalData* CJS_GlobalData::GetRetainedInstance(
     46     CPDFSDK_FormFillEnvironment* pApp) {
     47   if (!g_pInstance) {
     48     g_pInstance = new CJS_GlobalData();
     49   }
     50   ++g_pInstance->m_RefCount;
     51   return g_pInstance;
     52 }
     53 
     54 void CJS_GlobalData::Release() {
     55   if (!--m_RefCount) {
     56     delete g_pInstance;
     57     g_pInstance = nullptr;
     58   }
     59 }
     60 
     61 CJS_GlobalData::CJS_GlobalData()
     62     : m_RefCount(0), m_sFilePath(SDK_JS_GLOBALDATA_FILENAME) {
     63   LoadGlobalPersistentVariables();
     64 }
     65 
     66 CJS_GlobalData::~CJS_GlobalData() {
     67   SaveGlobalPersisitentVariables();
     68 }
     69 
     70 CJS_GlobalData::iterator CJS_GlobalData::FindGlobalVariable(
     71     const ByteString& propname) {
     72   for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end();
     73        ++it) {
     74     if ((*it)->data.sKey == propname)
     75       return it;
     76   }
     77   return m_arrayGlobalData.end();
     78 }
     79 
     80 CJS_GlobalData::const_iterator CJS_GlobalData::FindGlobalVariable(
     81     const ByteString& propname) const {
     82   for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end();
     83        ++it) {
     84     if ((*it)->data.sKey == propname)
     85       return it;
     86   }
     87   return m_arrayGlobalData.end();
     88 }
     89 
     90 CJS_GlobalData_Element* CJS_GlobalData::GetGlobalVariable(
     91     const ByteString& propname) {
     92   auto iter = FindGlobalVariable(propname);
     93   return iter != m_arrayGlobalData.end() ? iter->get() : nullptr;
     94 }
     95 
     96 void CJS_GlobalData::SetGlobalVariableNumber(const ByteString& propname,
     97                                              double dData) {
     98   ByteString sPropName(propname);
     99   if (!TrimPropName(&sPropName))
    100     return;
    101 
    102   if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
    103     pData->data.nType = JS_GlobalDataType::NUMBER;
    104     pData->data.dData = dData;
    105     return;
    106   }
    107   auto pNewData = pdfium::MakeUnique<CJS_GlobalData_Element>();
    108   pNewData->data.sKey = sPropName;
    109   pNewData->data.nType = JS_GlobalDataType::NUMBER;
    110   pNewData->data.dData = dData;
    111   m_arrayGlobalData.push_back(std::move(pNewData));
    112 }
    113 
    114 void CJS_GlobalData::SetGlobalVariableBoolean(const ByteString& propname,
    115                                               bool bData) {
    116   ByteString sPropName(propname);
    117   if (!TrimPropName(&sPropName))
    118     return;
    119 
    120   if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
    121     pData->data.nType = JS_GlobalDataType::BOOLEAN;
    122     pData->data.bData = bData;
    123     return;
    124   }
    125   auto pNewData = pdfium::MakeUnique<CJS_GlobalData_Element>();
    126   pNewData->data.sKey = sPropName;
    127   pNewData->data.nType = JS_GlobalDataType::BOOLEAN;
    128   pNewData->data.bData = bData;
    129   m_arrayGlobalData.push_back(std::move(pNewData));
    130 }
    131 
    132 void CJS_GlobalData::SetGlobalVariableString(const ByteString& propname,
    133                                              const ByteString& sData) {
    134   ByteString sPropName(propname);
    135   if (!TrimPropName(&sPropName))
    136     return;
    137 
    138   if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
    139     pData->data.nType = JS_GlobalDataType::STRING;
    140     pData->data.sData = sData;
    141     return;
    142   }
    143   auto pNewData = pdfium::MakeUnique<CJS_GlobalData_Element>();
    144   pNewData->data.sKey = sPropName;
    145   pNewData->data.nType = JS_GlobalDataType::STRING;
    146   pNewData->data.sData = sData;
    147   m_arrayGlobalData.push_back(std::move(pNewData));
    148 }
    149 
    150 void CJS_GlobalData::SetGlobalVariableObject(
    151     const ByteString& propname,
    152     const CJS_GlobalVariableArray& array) {
    153   ByteString sPropName(propname);
    154   if (!TrimPropName(&sPropName))
    155     return;
    156 
    157   if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
    158     pData->data.nType = JS_GlobalDataType::OBJECT;
    159     pData->data.objData.Copy(array);
    160     return;
    161   }
    162   auto pNewData = pdfium::MakeUnique<CJS_GlobalData_Element>();
    163   pNewData->data.sKey = sPropName;
    164   pNewData->data.nType = JS_GlobalDataType::OBJECT;
    165   pNewData->data.objData.Copy(array);
    166   m_arrayGlobalData.push_back(std::move(pNewData));
    167 }
    168 
    169 void CJS_GlobalData::SetGlobalVariableNull(const ByteString& propname) {
    170   ByteString sPropName(propname);
    171   if (!TrimPropName(&sPropName))
    172     return;
    173 
    174   if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
    175     pData->data.nType = JS_GlobalDataType::NULLOBJ;
    176     return;
    177   }
    178   auto pNewData = pdfium::MakeUnique<CJS_GlobalData_Element>();
    179   pNewData->data.sKey = sPropName;
    180   pNewData->data.nType = JS_GlobalDataType::NULLOBJ;
    181   m_arrayGlobalData.push_back(std::move(pNewData));
    182 }
    183 
    184 bool CJS_GlobalData::SetGlobalVariablePersistent(const ByteString& propname,
    185                                                  bool bPersistent) {
    186   ByteString sPropName(propname);
    187   if (!TrimPropName(&sPropName))
    188     return false;
    189 
    190   CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName);
    191   if (!pData)
    192     return false;
    193 
    194   pData->bPersistent = bPersistent;
    195   return true;
    196 }
    197 
    198 bool CJS_GlobalData::DeleteGlobalVariable(const ByteString& propname) {
    199   ByteString sPropName(propname);
    200   if (!TrimPropName(&sPropName))
    201     return false;
    202 
    203   auto iter = FindGlobalVariable(sPropName);
    204   if (iter == m_arrayGlobalData.end())
    205     return false;
    206 
    207   m_arrayGlobalData.erase(iter);
    208   return true;
    209 }
    210 
    211 int32_t CJS_GlobalData::GetSize() const {
    212   return pdfium::CollectionSize<int32_t>(m_arrayGlobalData);
    213 }
    214 
    215 CJS_GlobalData_Element* CJS_GlobalData::GetAt(int index) const {
    216   if (index < 0 || index >= GetSize())
    217     return nullptr;
    218   return m_arrayGlobalData[index].get();
    219 }
    220 
    221 void CJS_GlobalData::LoadGlobalPersistentVariables() {
    222   uint8_t* pBuffer = nullptr;
    223   int32_t nLength = 0;
    224 
    225   LoadFileBuffer(m_sFilePath.c_str(), pBuffer, nLength);
    226   CRYPT_ArcFourCryptBlock(pBuffer, nLength, JS_RC4KEY, sizeof(JS_RC4KEY));
    227 
    228   if (pBuffer) {
    229     uint8_t* p = pBuffer;
    230     uint16_t wType = *((uint16_t*)p);
    231     p += sizeof(uint16_t);
    232 
    233     if (wType == (uint16_t)(('X' << 8) | 'F')) {
    234       uint16_t wVersion = *((uint16_t*)p);
    235       p += sizeof(uint16_t);
    236 
    237       ASSERT(wVersion <= 2);
    238 
    239       uint32_t dwCount = *((uint32_t*)p);
    240       p += sizeof(uint32_t);
    241 
    242       uint32_t dwSize = *((uint32_t*)p);
    243       p += sizeof(uint32_t);
    244 
    245       if (dwSize == nLength - sizeof(uint16_t) * 2 - sizeof(uint32_t) * 2) {
    246         for (int32_t i = 0, sz = dwCount; i < sz; i++) {
    247           if (p > pBuffer + nLength)
    248             break;
    249 
    250           uint32_t dwNameLen = *((uint32_t*)p);
    251           p += sizeof(uint32_t);
    252 
    253           if (p + dwNameLen > pBuffer + nLength)
    254             break;
    255 
    256           ByteString sEntry = ByteString(p, dwNameLen);
    257           p += sizeof(char) * dwNameLen;
    258 
    259           JS_GlobalDataType wDataType =
    260               static_cast<JS_GlobalDataType>(*((uint16_t*)p));
    261           p += sizeof(uint16_t);
    262 
    263           switch (wDataType) {
    264             case JS_GlobalDataType::NUMBER: {
    265               double dData = 0;
    266               switch (wVersion) {
    267                 case 1: {
    268                   uint32_t dwData = *((uint32_t*)p);
    269                   p += sizeof(uint32_t);
    270                   dData = dwData;
    271                 } break;
    272                 case 2: {
    273                   dData = *((double*)p);
    274                   p += sizeof(double);
    275                 } break;
    276               }
    277               SetGlobalVariableNumber(sEntry, dData);
    278               SetGlobalVariablePersistent(sEntry, true);
    279             } break;
    280             case JS_GlobalDataType::BOOLEAN: {
    281               uint16_t wData = *((uint16_t*)p);
    282               p += sizeof(uint16_t);
    283               SetGlobalVariableBoolean(sEntry, (bool)(wData == 1));
    284               SetGlobalVariablePersistent(sEntry, true);
    285             } break;
    286             case JS_GlobalDataType::STRING: {
    287               uint32_t dwLength = *((uint32_t*)p);
    288               p += sizeof(uint32_t);
    289 
    290               if (p + dwLength > pBuffer + nLength)
    291                 break;
    292 
    293               SetGlobalVariableString(sEntry, ByteString(p, dwLength));
    294               SetGlobalVariablePersistent(sEntry, true);
    295               p += sizeof(char) * dwLength;
    296             } break;
    297             case JS_GlobalDataType::NULLOBJ: {
    298               SetGlobalVariableNull(sEntry);
    299               SetGlobalVariablePersistent(sEntry, true);
    300             }
    301             case JS_GlobalDataType::OBJECT:
    302               break;
    303           }
    304         }
    305       }
    306     }
    307     FX_Free(pBuffer);
    308   }
    309 }
    310 
    311 void CJS_GlobalData::SaveGlobalPersisitentVariables() {
    312   uint32_t nCount = 0;
    313   CFX_BinaryBuf sData;
    314   for (const auto& pElement : m_arrayGlobalData) {
    315     if (pElement->bPersistent) {
    316       CFX_BinaryBuf sElement;
    317       MakeByteString(pElement->data.sKey, &pElement->data, sElement);
    318       if (sData.GetSize() + sElement.GetSize() > JS_MAXGLOBALDATA)
    319         break;
    320 
    321       sData.AppendBlock(sElement.GetBuffer(), sElement.GetSize());
    322       nCount++;
    323     }
    324   }
    325 
    326   CFX_BinaryBuf sFile;
    327   uint16_t wType = (uint16_t)(('X' << 8) | 'F');
    328   sFile.AppendBlock(&wType, sizeof(uint16_t));
    329   uint16_t wVersion = 2;
    330   sFile.AppendBlock(&wVersion, sizeof(uint16_t));
    331   sFile.AppendBlock(&nCount, sizeof(uint32_t));
    332   uint32_t dwSize = sData.GetSize();
    333   sFile.AppendBlock(&dwSize, sizeof(uint32_t));
    334 
    335   sFile.AppendBlock(sData.GetBuffer(), sData.GetSize());
    336 
    337   CRYPT_ArcFourCryptBlock(sFile.GetBuffer(), sFile.GetSize(), JS_RC4KEY,
    338                           sizeof(JS_RC4KEY));
    339   WriteFileBuffer(m_sFilePath.c_str(),
    340                   reinterpret_cast<char*>(sFile.GetBuffer()), sFile.GetSize());
    341 }
    342 
    343 void CJS_GlobalData::LoadFileBuffer(const wchar_t* sFilePath,
    344                                     uint8_t*& pBuffer,
    345                                     int32_t& nLength) {
    346   // UnSupport.
    347 }
    348 
    349 void CJS_GlobalData::WriteFileBuffer(const wchar_t* sFilePath,
    350                                      const char* pBuffer,
    351                                      int32_t nLength) {
    352   // UnSupport.
    353 }
    354 
    355 void CJS_GlobalData::MakeByteString(const ByteString& name,
    356                                     CJS_KeyValue* pData,
    357                                     CFX_BinaryBuf& sData) {
    358   switch (pData->nType) {
    359     case JS_GlobalDataType::NUMBER: {
    360       uint32_t dwNameLen = (uint32_t)name.GetLength();
    361       sData.AppendBlock(&dwNameLen, sizeof(uint32_t));
    362       sData.AppendString(name);
    363       sData.AppendBlock(&pData->nType, sizeof(uint16_t));
    364 
    365       double dData = pData->dData;
    366       sData.AppendBlock(&dData, sizeof(double));
    367     } break;
    368     case JS_GlobalDataType::BOOLEAN: {
    369       uint32_t dwNameLen = (uint32_t)name.GetLength();
    370       sData.AppendBlock(&dwNameLen, sizeof(uint32_t));
    371       sData.AppendString(name);
    372       sData.AppendBlock(&pData->nType, sizeof(uint16_t));
    373 
    374       uint16_t wData = (uint16_t)pData->bData;
    375       sData.AppendBlock(&wData, sizeof(uint16_t));
    376     } break;
    377     case JS_GlobalDataType::STRING: {
    378       uint32_t dwNameLen = (uint32_t)name.GetLength();
    379       sData.AppendBlock(&dwNameLen, sizeof(uint32_t));
    380       sData.AppendString(name);
    381       sData.AppendBlock(&pData->nType, sizeof(uint16_t));
    382 
    383       uint32_t dwDataLen = (uint32_t)pData->sData.GetLength();
    384       sData.AppendBlock(&dwDataLen, sizeof(uint32_t));
    385       sData.AppendString(pData->sData);
    386     } break;
    387     case JS_GlobalDataType::NULLOBJ: {
    388       uint32_t dwNameLen = (uint32_t)name.GetLength();
    389       sData.AppendBlock(&dwNameLen, sizeof(uint32_t));
    390       sData.AppendString(name);
    391       sData.AppendBlock(&pData->nType, sizeof(uint32_t));
    392     } break;
    393     default:
    394       break;
    395   }
    396 }
    397