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 "JS_GlobalData.h"
8
9 #include "core/include/fdrm/fx_crypt.h"
10 #include "fpdfsdk/include/javascript/IJavaScript.h"
11
12 #define JS_MAXGLOBALDATA (1024 * 4 - 8)
13
14 /* --------------------- CJS_GlobalVariableArray --------------------- */
15
CJS_GlobalVariableArray()16 CJS_GlobalVariableArray::CJS_GlobalVariableArray() {}
17
~CJS_GlobalVariableArray()18 CJS_GlobalVariableArray::~CJS_GlobalVariableArray() {
19 Empty();
20 }
21
Copy(const CJS_GlobalVariableArray & array)22 void CJS_GlobalVariableArray::Copy(const CJS_GlobalVariableArray& array) {
23 Empty();
24 for (int i = 0, sz = array.Count(); i < sz; i++) {
25 CJS_KeyValue* pOldObjData = array.GetAt(i);
26 switch (pOldObjData->nType) {
27 case JS_GLOBALDATA_TYPE_NUMBER: {
28 CJS_KeyValue* pNewObjData = new CJS_KeyValue;
29 pNewObjData->sKey = pOldObjData->sKey;
30 pNewObjData->nType = pOldObjData->nType;
31 pNewObjData->dData = pOldObjData->dData;
32 Add(pNewObjData);
33 } break;
34 case JS_GLOBALDATA_TYPE_BOOLEAN: {
35 CJS_KeyValue* pNewObjData = new CJS_KeyValue;
36 pNewObjData->sKey = pOldObjData->sKey;
37 pNewObjData->nType = pOldObjData->nType;
38 pNewObjData->bData = pOldObjData->bData;
39 Add(pNewObjData);
40 } break;
41 case JS_GLOBALDATA_TYPE_STRING: {
42 CJS_KeyValue* pNewObjData = new CJS_KeyValue;
43 pNewObjData->sKey = pOldObjData->sKey;
44 pNewObjData->nType = pOldObjData->nType;
45 pNewObjData->sData = pOldObjData->sData;
46 Add(pNewObjData);
47 } break;
48 case JS_GLOBALDATA_TYPE_OBJECT: {
49 CJS_KeyValue* pNewObjData = new CJS_KeyValue;
50 pNewObjData->sKey = pOldObjData->sKey;
51 pNewObjData->nType = pOldObjData->nType;
52 pNewObjData->objData.Copy(pOldObjData->objData);
53 Add(pNewObjData);
54 } break;
55 case JS_GLOBALDATA_TYPE_NULL: {
56 CJS_KeyValue* pNewObjData = new CJS_KeyValue;
57 pNewObjData->sKey = pOldObjData->sKey;
58 pNewObjData->nType = pOldObjData->nType;
59 Add(pNewObjData);
60 } break;
61 }
62 }
63 }
64
Add(CJS_KeyValue * p)65 void CJS_GlobalVariableArray::Add(CJS_KeyValue* p) {
66 array.Add(p);
67 }
68
Count() const69 int CJS_GlobalVariableArray::Count() const {
70 return array.GetSize();
71 }
72
GetAt(int index) const73 CJS_KeyValue* CJS_GlobalVariableArray::GetAt(int index) const {
74 return array.GetAt(index);
75 }
76
Empty()77 void CJS_GlobalVariableArray::Empty() {
78 for (int i = 0, sz = array.GetSize(); i < sz; i++)
79 delete array.GetAt(i);
80 array.RemoveAll();
81 }
82
83 /* -------------------------- CJS_GlobalData -------------------------- */
84
85 #define READER_JS_GLOBALDATA_FILENAME L"Reader_JsGlobal.Data"
86 #define PHANTOM_JS_GLOBALDATA_FILENAME L"Phantom_JsGlobal.Data"
87 #define SDK_JS_GLOBALDATA_FILENAME L"SDK_JsGlobal.Data"
88
89 static const uint8_t JS_RC4KEY[] = {
90 0x19, 0xa8, 0xe8, 0x01, 0xf6, 0xa8, 0xb6, 0x4d, 0x82, 0x04, 0x45, 0x6d,
91 0xb4, 0xcf, 0xd7, 0x77, 0x67, 0xf9, 0x75, 0x9f, 0xf0, 0xe0, 0x1e, 0x51,
92 0xee, 0x46, 0xfd, 0x0b, 0xc9, 0x93, 0x25, 0x55, 0x4a, 0xee, 0xe0, 0x16,
93 0xd0, 0xdf, 0x8c, 0xfa, 0x2a, 0xa9, 0x49, 0xfd, 0x97, 0x1c, 0x0e, 0x22,
94 0x13, 0x28, 0x7c, 0xaf, 0xc4, 0xfc, 0x9c, 0x12, 0x65, 0x8c, 0x4e, 0x5b,
95 0x04, 0x75, 0x89, 0xc9, 0xb1, 0xed, 0x50, 0xca, 0x96, 0x6f, 0x1a, 0x7a,
96 0xfe, 0x58, 0x5d, 0xec, 0x19, 0x4a, 0xf6, 0x35, 0x6a, 0x97, 0x14, 0x00,
97 0x0e, 0xd0, 0x6b, 0xbb, 0xd5, 0x75, 0x55, 0x8b, 0x6e, 0x6b, 0x19, 0xa0,
98 0xf8, 0x77, 0xd5, 0xa3};
99
100 CJS_GlobalData* CJS_GlobalData::g_Instance = nullptr;
101
102 // static
GetRetainedInstance(CPDFDoc_Environment * pApp)103 CJS_GlobalData* CJS_GlobalData::GetRetainedInstance(CPDFDoc_Environment* pApp) {
104 if (!g_Instance) {
105 g_Instance = new CJS_GlobalData();
106 }
107 ++g_Instance->m_RefCount;
108 return g_Instance;
109 }
110
Release()111 void CJS_GlobalData::Release() {
112 if (!--m_RefCount) {
113 delete g_Instance;
114 g_Instance = nullptr;
115 }
116 }
117
CJS_GlobalData()118 CJS_GlobalData::CJS_GlobalData() : m_RefCount(0) {
119 m_sFilePath += SDK_JS_GLOBALDATA_FILENAME;
120 LoadGlobalPersistentVariables();
121 }
122
~CJS_GlobalData()123 CJS_GlobalData::~CJS_GlobalData() {
124 SaveGlobalPersisitentVariables();
125 for (int i = 0, sz = m_arrayGlobalData.GetSize(); i < sz; i++)
126 delete m_arrayGlobalData.GetAt(i);
127
128 m_arrayGlobalData.RemoveAll();
129 }
130
FindGlobalVariable(const FX_CHAR * propname)131 int CJS_GlobalData::FindGlobalVariable(const FX_CHAR* propname) {
132 for (int i = 0, sz = m_arrayGlobalData.GetSize(); i < sz; i++) {
133 CJS_GlobalData_Element* pTemp = m_arrayGlobalData.GetAt(i);
134 if (pTemp->data.sKey[0] == *propname && pTemp->data.sKey == propname)
135 return i;
136 }
137 return -1;
138 }
139
GetGlobalVariable(const FX_CHAR * propname)140 CJS_GlobalData_Element* CJS_GlobalData::GetGlobalVariable(
141 const FX_CHAR* propname) {
142 ASSERT(propname);
143
144 int nFind = FindGlobalVariable(propname);
145 return nFind >= 0 ? m_arrayGlobalData.GetAt(nFind) : nullptr;
146 }
147
SetGlobalVariableNumber(const FX_CHAR * propname,double dData)148 void CJS_GlobalData::SetGlobalVariableNumber(const FX_CHAR* propname,
149 double dData) {
150 ASSERT(propname);
151 CFX_ByteString sPropName = propname;
152 sPropName.TrimLeft();
153 sPropName.TrimRight();
154 if (sPropName.GetLength() == 0)
155 return;
156
157 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
158 pData->data.nType = JS_GLOBALDATA_TYPE_NUMBER;
159 pData->data.dData = dData;
160 } else {
161 CJS_GlobalData_Element* pNewData = new CJS_GlobalData_Element;
162 pNewData->data.sKey = sPropName;
163 pNewData->data.nType = JS_GLOBALDATA_TYPE_NUMBER;
164 pNewData->data.dData = dData;
165 m_arrayGlobalData.Add(pNewData);
166 }
167 }
168
SetGlobalVariableBoolean(const FX_CHAR * propname,bool bData)169 void CJS_GlobalData::SetGlobalVariableBoolean(const FX_CHAR* propname,
170 bool bData) {
171 ASSERT(propname);
172 CFX_ByteString sPropName = propname;
173
174 sPropName.TrimLeft();
175 sPropName.TrimRight();
176
177 if (sPropName.GetLength() == 0)
178 return;
179
180 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
181 pData->data.nType = JS_GLOBALDATA_TYPE_BOOLEAN;
182 pData->data.bData = bData;
183 } else {
184 CJS_GlobalData_Element* pNewData = new CJS_GlobalData_Element;
185 pNewData->data.sKey = sPropName;
186 pNewData->data.nType = JS_GLOBALDATA_TYPE_BOOLEAN;
187 pNewData->data.bData = bData;
188
189 m_arrayGlobalData.Add(pNewData);
190 }
191 }
192
SetGlobalVariableString(const FX_CHAR * propname,const CFX_ByteString & sData)193 void CJS_GlobalData::SetGlobalVariableString(const FX_CHAR* propname,
194 const CFX_ByteString& sData) {
195 ASSERT(propname);
196 CFX_ByteString sPropName = propname;
197
198 sPropName.TrimLeft();
199 sPropName.TrimRight();
200
201 if (sPropName.GetLength() == 0)
202 return;
203
204 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
205 pData->data.nType = JS_GLOBALDATA_TYPE_STRING;
206 pData->data.sData = sData;
207 } else {
208 CJS_GlobalData_Element* pNewData = new CJS_GlobalData_Element;
209 pNewData->data.sKey = sPropName;
210 pNewData->data.nType = JS_GLOBALDATA_TYPE_STRING;
211 pNewData->data.sData = sData;
212
213 m_arrayGlobalData.Add(pNewData);
214 }
215 }
216
SetGlobalVariableObject(const FX_CHAR * propname,const CJS_GlobalVariableArray & array)217 void CJS_GlobalData::SetGlobalVariableObject(
218 const FX_CHAR* propname,
219 const CJS_GlobalVariableArray& array) {
220 ASSERT(propname);
221 CFX_ByteString sPropName = propname;
222
223 sPropName.TrimLeft();
224 sPropName.TrimRight();
225
226 if (sPropName.GetLength() == 0)
227 return;
228
229 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
230 pData->data.nType = JS_GLOBALDATA_TYPE_OBJECT;
231 pData->data.objData.Copy(array);
232 } else {
233 CJS_GlobalData_Element* pNewData = new CJS_GlobalData_Element;
234 pNewData->data.sKey = sPropName;
235 pNewData->data.nType = JS_GLOBALDATA_TYPE_OBJECT;
236 pNewData->data.objData.Copy(array);
237
238 m_arrayGlobalData.Add(pNewData);
239 }
240 }
241
SetGlobalVariableNull(const FX_CHAR * propname)242 void CJS_GlobalData::SetGlobalVariableNull(const FX_CHAR* propname) {
243 ASSERT(propname);
244 CFX_ByteString sPropName = propname;
245
246 sPropName.TrimLeft();
247 sPropName.TrimRight();
248
249 if (sPropName.GetLength() == 0)
250 return;
251
252 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
253 pData->data.nType = JS_GLOBALDATA_TYPE_NULL;
254 } else {
255 CJS_GlobalData_Element* pNewData = new CJS_GlobalData_Element;
256 pNewData->data.sKey = sPropName;
257 pNewData->data.nType = JS_GLOBALDATA_TYPE_NULL;
258
259 m_arrayGlobalData.Add(pNewData);
260 }
261 }
262
SetGlobalVariablePersistent(const FX_CHAR * propname,FX_BOOL bPersistent)263 FX_BOOL CJS_GlobalData::SetGlobalVariablePersistent(const FX_CHAR* propname,
264 FX_BOOL bPersistent) {
265 ASSERT(propname);
266 CFX_ByteString sPropName = propname;
267
268 sPropName.TrimLeft();
269 sPropName.TrimRight();
270
271 if (sPropName.GetLength() == 0)
272 return FALSE;
273
274 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
275 pData->bPersistent = bPersistent;
276 return TRUE;
277 }
278
279 return FALSE;
280 }
281
DeleteGlobalVariable(const FX_CHAR * propname)282 FX_BOOL CJS_GlobalData::DeleteGlobalVariable(const FX_CHAR* propname) {
283 ASSERT(propname);
284 CFX_ByteString sPropName = propname;
285
286 sPropName.TrimLeft();
287 sPropName.TrimRight();
288
289 if (sPropName.GetLength() == 0)
290 return FALSE;
291
292 int nFind = FindGlobalVariable(sPropName);
293
294 if (nFind >= 0) {
295 delete m_arrayGlobalData.GetAt(nFind);
296 m_arrayGlobalData.RemoveAt(nFind);
297 return TRUE;
298 }
299
300 return FALSE;
301 }
302
GetSize() const303 int32_t CJS_GlobalData::GetSize() const {
304 return m_arrayGlobalData.GetSize();
305 }
306
GetAt(int index) const307 CJS_GlobalData_Element* CJS_GlobalData::GetAt(int index) const {
308 return m_arrayGlobalData.GetAt(index);
309 }
310
LoadGlobalPersistentVariables()311 void CJS_GlobalData::LoadGlobalPersistentVariables() {
312 uint8_t* pBuffer = NULL;
313 int32_t nLength = 0;
314
315 LoadFileBuffer(m_sFilePath.c_str(), pBuffer, nLength);
316 CRYPT_ArcFourCryptBlock(pBuffer, nLength, JS_RC4KEY, sizeof(JS_RC4KEY));
317
318 if (pBuffer) {
319 uint8_t* p = pBuffer;
320 FX_WORD wType = *((FX_WORD*)p);
321 p += sizeof(FX_WORD);
322
323 // FX_WORD wTemp = (FX_WORD)(('X' << 8) | 'F');
324
325 if (wType == (FX_WORD)(('X' << 8) | 'F')) {
326 FX_WORD wVersion = *((FX_WORD*)p);
327 p += sizeof(FX_WORD);
328
329 ASSERT(wVersion <= 2);
330
331 FX_DWORD dwCount = *((FX_DWORD*)p);
332 p += sizeof(FX_DWORD);
333
334 FX_DWORD dwSize = *((FX_DWORD*)p);
335 p += sizeof(FX_DWORD);
336
337 if (dwSize == nLength - sizeof(FX_WORD) * 2 - sizeof(FX_DWORD) * 2) {
338 for (int32_t i = 0, sz = dwCount; i < sz; i++) {
339 if (p > pBuffer + nLength)
340 break;
341
342 FX_DWORD dwNameLen = *((FX_DWORD*)p);
343 p += sizeof(FX_DWORD);
344
345 if (p + dwNameLen > pBuffer + nLength)
346 break;
347
348 CFX_ByteString sEntry = CFX_ByteString(p, dwNameLen);
349 p += sizeof(char) * dwNameLen;
350
351 FX_WORD wDataType = *((FX_WORD*)p);
352 p += sizeof(FX_WORD);
353
354 switch (wDataType) {
355 case JS_GLOBALDATA_TYPE_NUMBER: {
356 double dData = 0;
357 switch (wVersion) {
358 case 1: {
359 FX_DWORD dwData = *((FX_DWORD*)p);
360 p += sizeof(FX_DWORD);
361 dData = dwData;
362 } break;
363 case 2: {
364 dData = *((double*)p);
365 p += sizeof(double);
366 } break;
367 }
368 SetGlobalVariableNumber(sEntry, dData);
369 SetGlobalVariablePersistent(sEntry, TRUE);
370 } break;
371 case JS_GLOBALDATA_TYPE_BOOLEAN: {
372 FX_WORD wData = *((FX_WORD*)p);
373 p += sizeof(FX_WORD);
374 SetGlobalVariableBoolean(sEntry, (bool)(wData == 1));
375 SetGlobalVariablePersistent(sEntry, TRUE);
376 } break;
377 case JS_GLOBALDATA_TYPE_STRING: {
378 FX_DWORD dwLength = *((FX_DWORD*)p);
379 p += sizeof(FX_DWORD);
380
381 if (p + dwLength > pBuffer + nLength)
382 break;
383
384 SetGlobalVariableString(sEntry, CFX_ByteString(p, dwLength));
385 SetGlobalVariablePersistent(sEntry, TRUE);
386 p += sizeof(char) * dwLength;
387 } break;
388 case JS_GLOBALDATA_TYPE_NULL: {
389 SetGlobalVariableNull(sEntry);
390 SetGlobalVariablePersistent(sEntry, TRUE);
391 }
392 }
393 }
394 }
395 }
396 FX_Free(pBuffer);
397 }
398 }
399
SaveGlobalPersisitentVariables()400 void CJS_GlobalData::SaveGlobalPersisitentVariables() {
401 FX_DWORD nCount = 0;
402 CFX_BinaryBuf sData;
403
404 for (int i = 0, sz = m_arrayGlobalData.GetSize(); i < sz; i++) {
405 CJS_GlobalData_Element* pElement = m_arrayGlobalData.GetAt(i);
406 if (pElement->bPersistent) {
407 CFX_BinaryBuf sElement;
408 MakeByteString(pElement->data.sKey, &pElement->data, sElement);
409
410 if (sData.GetSize() + sElement.GetSize() > JS_MAXGLOBALDATA)
411 break;
412
413 sData.AppendBlock(sElement.GetBuffer(), sElement.GetSize());
414 nCount++;
415 }
416 }
417
418 CFX_BinaryBuf sFile;
419
420 FX_WORD wType = (FX_WORD)(('X' << 8) | 'F');
421 sFile.AppendBlock(&wType, sizeof(FX_WORD));
422 FX_WORD wVersion = 2;
423 sFile.AppendBlock(&wVersion, sizeof(FX_WORD));
424 sFile.AppendBlock(&nCount, sizeof(FX_DWORD));
425 FX_DWORD dwSize = sData.GetSize();
426 sFile.AppendBlock(&dwSize, sizeof(FX_DWORD));
427
428 sFile.AppendBlock(sData.GetBuffer(), sData.GetSize());
429
430 CRYPT_ArcFourCryptBlock(sFile.GetBuffer(), sFile.GetSize(), JS_RC4KEY,
431 sizeof(JS_RC4KEY));
432 WriteFileBuffer(m_sFilePath.c_str(), (const FX_CHAR*)sFile.GetBuffer(),
433 sFile.GetSize());
434 }
435
LoadFileBuffer(const FX_WCHAR * sFilePath,uint8_t * & pBuffer,int32_t & nLength)436 void CJS_GlobalData::LoadFileBuffer(const FX_WCHAR* sFilePath,
437 uint8_t*& pBuffer,
438 int32_t& nLength) {
439 // UnSupport.
440 }
441
WriteFileBuffer(const FX_WCHAR * sFilePath,const FX_CHAR * pBuffer,int32_t nLength)442 void CJS_GlobalData::WriteFileBuffer(const FX_WCHAR* sFilePath,
443 const FX_CHAR* pBuffer,
444 int32_t nLength) {
445 // UnSupport.
446 }
447
MakeByteString(const CFX_ByteString & name,CJS_KeyValue * pData,CFX_BinaryBuf & sData)448 void CJS_GlobalData::MakeByteString(const CFX_ByteString& name,
449 CJS_KeyValue* pData,
450 CFX_BinaryBuf& sData) {
451 FX_WORD wType = (FX_WORD)pData->nType;
452 switch (wType) {
453 case JS_GLOBALDATA_TYPE_NUMBER: {
454 FX_DWORD dwNameLen = (FX_DWORD)name.GetLength();
455 sData.AppendBlock(&dwNameLen, sizeof(FX_DWORD));
456 sData.AppendString(name);
457 sData.AppendBlock(&wType, sizeof(FX_WORD));
458
459 double dData = pData->dData;
460 sData.AppendBlock(&dData, sizeof(double));
461 } break;
462 case JS_GLOBALDATA_TYPE_BOOLEAN: {
463 FX_DWORD dwNameLen = (FX_DWORD)name.GetLength();
464 sData.AppendBlock(&dwNameLen, sizeof(FX_DWORD));
465 sData.AppendString(name);
466 sData.AppendBlock(&wType, sizeof(FX_WORD));
467
468 FX_WORD wData = (FX_WORD)pData->bData;
469 sData.AppendBlock(&wData, sizeof(FX_WORD));
470 } break;
471 case JS_GLOBALDATA_TYPE_STRING: {
472 FX_DWORD dwNameLen = (FX_DWORD)name.GetLength();
473 sData.AppendBlock(&dwNameLen, sizeof(FX_DWORD));
474 sData.AppendString(name);
475 sData.AppendBlock(&wType, sizeof(FX_WORD));
476
477 FX_DWORD dwDataLen = (FX_DWORD)pData->sData.GetLength();
478 sData.AppendBlock(&dwDataLen, sizeof(FX_DWORD));
479 sData.AppendString(pData->sData);
480 } break;
481 case JS_GLOBALDATA_TYPE_NULL: {
482 FX_DWORD dwNameLen = (FX_DWORD)name.GetLength();
483 sData.AppendBlock(&dwNameLen, sizeof(FX_DWORD));
484 sData.AppendString(name);
485 sData.AppendBlock(&wType, sizeof(FX_DWORD));
486 } break;
487 default:
488 break;
489 }
490 }
491