Fix confusion between length in bytes and length in characters in app::response().
[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 "../../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
15 CJS_GlobalVariableArray::CJS_GlobalVariableArray()
16 {
17 }
18
19 CJS_GlobalVariableArray::~CJS_GlobalVariableArray()
20 {
21         Empty();
22 }
23
24 void 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
80 void CJS_GlobalVariableArray::Add(CJS_KeyValue* p)
81 {
82         array.Add(p);
83 }
84
85 int CJS_GlobalVariableArray::Count() const
86 {
87         return array.GetSize();
88 }
89
90 CJS_KeyValue* CJS_GlobalVariableArray::GetAt(int index) const
91 {
92         return array.GetAt(index);
93 }
94
95 void 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
108 static 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
120 CJS_GlobalData::CJS_GlobalData(CPDFDoc_Environment* 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
131 CJS_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
141 int     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
160 CJS_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
172 void 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
198 void 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
224 void 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
250 void 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
276 void 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
300 FX_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
319 FX_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
341 FX_INT32 CJS_GlobalData::GetSize() const
342 {
343         return m_arrayGlobalData.GetSize();
344 }
345
346 CJS_GlobalData_Element* CJS_GlobalData::GetAt(int index) const
347 {
348         return m_arrayGlobalData.GetAt(index);
349 }
350
351 void 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 /*
460 struct js_global_datafile_header
461 {
462         FX_WORD type; //FX ('X' << 8) | 'F'
463         FX_WORD version; //1.0
464         FX_DWORD datacount;
465 };
466 struct 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
476 void 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
515 void CJS_GlobalData::LoadFileBuffer(FX_LPCWSTR sFilePath, FX_LPBYTE& pBuffer, FX_INT32& nLength)
516 {
517 //UnSupport.
518 }
519
520 void CJS_GlobalData::WriteFileBuffer(FX_LPCWSTR sFilePath, FX_LPCSTR pBuffer, FX_INT32 nLength)
521 {
522 //UnSupport.
523 }
524
525 void 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