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