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