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
16CJS_GlobalVariableArray::CJS_GlobalVariableArray() {}
17
18CJS_GlobalVariableArray::~CJS_GlobalVariableArray() {
19  Empty();
20}
21
22void 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
65void CJS_GlobalVariableArray::Add(CJS_KeyValue* p) {
66  array.Add(p);
67}
68
69int CJS_GlobalVariableArray::Count() const {
70  return array.GetSize();
71}
72
73CJS_KeyValue* CJS_GlobalVariableArray::GetAt(int index) const {
74  return array.GetAt(index);
75}
76
77void 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
89static 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
100CJS_GlobalData* CJS_GlobalData::g_Instance = nullptr;
101
102// static
103CJS_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
111void CJS_GlobalData::Release() {
112  if (!--m_RefCount) {
113    delete g_Instance;
114    g_Instance = nullptr;
115  }
116}
117
118CJS_GlobalData::CJS_GlobalData() : m_RefCount(0) {
119  m_sFilePath += SDK_JS_GLOBALDATA_FILENAME;
120  LoadGlobalPersistentVariables();
121}
122
123CJS_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
131int 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
140CJS_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
148void 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
169void 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
193void 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
217void 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
242void 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
263FX_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
282FX_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
303int32_t CJS_GlobalData::GetSize() const {
304  return m_arrayGlobalData.GetSize();
305}
306
307CJS_GlobalData_Element* CJS_GlobalData::GetAt(int index) const {
308  return m_arrayGlobalData.GetAt(index);
309}
310
311void 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
400void 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
436void CJS_GlobalData::LoadFileBuffer(const FX_WCHAR* sFilePath,
437                                    uint8_t*& pBuffer,
438                                    int32_t& nLength) {
439  // UnSupport.
440}
441
442void CJS_GlobalData::WriteFileBuffer(const FX_WCHAR* sFilePath,
443                                     const FX_CHAR* pBuffer,
444                                     int32_t nLength) {
445  // UnSupport.
446}
447
448void 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