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