• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
TrimPropName(ByteString * 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
GetRetainedInstance(CPDFSDK_FormFillEnvironment * pApp)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 
Release()54 void CJS_GlobalData::Release() {
55   if (!--m_RefCount) {
56     delete g_pInstance;
57     g_pInstance = nullptr;
58   }
59 }
60 
CJS_GlobalData()61 CJS_GlobalData::CJS_GlobalData()
62     : m_RefCount(0), m_sFilePath(SDK_JS_GLOBALDATA_FILENAME) {
63   LoadGlobalPersistentVariables();
64 }
65 
~CJS_GlobalData()66 CJS_GlobalData::~CJS_GlobalData() {
67   SaveGlobalPersisitentVariables();
68 }
69 
FindGlobalVariable(const ByteString & propname)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 
FindGlobalVariable(const ByteString & propname) const80 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 
GetGlobalVariable(const ByteString & propname)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 
SetGlobalVariableNumber(const ByteString & propname,double dData)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 
SetGlobalVariableBoolean(const ByteString & propname,bool bData)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 
SetGlobalVariableString(const ByteString & propname,const ByteString & sData)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 
SetGlobalVariableObject(const ByteString & propname,const CJS_GlobalVariableArray & array)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 
SetGlobalVariableNull(const ByteString & propname)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 
SetGlobalVariablePersistent(const ByteString & propname,bool bPersistent)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 
DeleteGlobalVariable(const ByteString & propname)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 
GetSize() const211 int32_t CJS_GlobalData::GetSize() const {
212   return pdfium::CollectionSize<int32_t>(m_arrayGlobalData);
213 }
214 
GetAt(int index) const215 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 
LoadGlobalPersistentVariables()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 
SaveGlobalPersisitentVariables()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 
LoadFileBuffer(const wchar_t * sFilePath,uint8_t * & pBuffer,int32_t & nLength)343 void CJS_GlobalData::LoadFileBuffer(const wchar_t* sFilePath,
344                                     uint8_t*& pBuffer,
345                                     int32_t& nLength) {
346   // UnSupport.
347 }
348 
WriteFileBuffer(const wchar_t * sFilePath,const char * pBuffer,int32_t nLength)349 void CJS_GlobalData::WriteFileBuffer(const wchar_t* sFilePath,
350                                      const char* pBuffer,
351                                      int32_t nLength) {
352   // UnSupport.
353 }
354 
MakeByteString(const ByteString & name,CJS_KeyValue * pData,CFX_BinaryBuf & sData)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