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