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