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 "fpdfsdk/javascript/JS_GlobalData.h" 8 9 #include <utility> 10 11 #include "core/fdrm/crypto/fx_crypt.h" 12 #include "third_party/base/stl_util.h" 13 14 #define JS_MAXGLOBALDATA (1024 * 4 - 8) 15 16 #define READER_JS_GLOBALDATA_FILENAME L"Reader_JsGlobal.Data" 17 #define PHANTOM_JS_GLOBALDATA_FILENAME L"Phantom_JsGlobal.Data" 18 #define SDK_JS_GLOBALDATA_FILENAME L"SDK_JsGlobal.Data" 19 20 namespace { 21 22 const uint8_t JS_RC4KEY[] = { 23 0x19, 0xa8, 0xe8, 0x01, 0xf6, 0xa8, 0xb6, 0x4d, 0x82, 0x04, 0x45, 0x6d, 24 0xb4, 0xcf, 0xd7, 0x77, 0x67, 0xf9, 0x75, 0x9f, 0xf0, 0xe0, 0x1e, 0x51, 25 0xee, 0x46, 0xfd, 0x0b, 0xc9, 0x93, 0x25, 0x55, 0x4a, 0xee, 0xe0, 0x16, 26 0xd0, 0xdf, 0x8c, 0xfa, 0x2a, 0xa9, 0x49, 0xfd, 0x97, 0x1c, 0x0e, 0x22, 27 0x13, 0x28, 0x7c, 0xaf, 0xc4, 0xfc, 0x9c, 0x12, 0x65, 0x8c, 0x4e, 0x5b, 28 0x04, 0x75, 0x89, 0xc9, 0xb1, 0xed, 0x50, 0xca, 0x96, 0x6f, 0x1a, 0x7a, 29 0xfe, 0x58, 0x5d, 0xec, 0x19, 0x4a, 0xf6, 0x35, 0x6a, 0x97, 0x14, 0x00, 30 0x0e, 0xd0, 0x6b, 0xbb, 0xd5, 0x75, 0x55, 0x8b, 0x6e, 0x6b, 0x19, 0xa0, 31 0xf8, 0x77, 0xd5, 0xa3}; 32 33 // Returns true if non-empty, setting sPropName 34 bool TrimPropName(CFX_ByteString* sPropName) { 35 sPropName->TrimLeft(); 36 sPropName->TrimRight(); 37 return sPropName->GetLength() != 0; 38 } 39 40 CJS_GlobalData* g_pInstance = nullptr; 41 42 } // namespace 43 44 // static 45 CJS_GlobalData* CJS_GlobalData::GetRetainedInstance( 46 CPDFSDK_FormFillEnvironment* pApp) { 47 if (!g_pInstance) { 48 g_pInstance = new CJS_GlobalData(); 49 } 50 ++g_pInstance->m_RefCount; 51 return g_pInstance; 52 } 53 54 void CJS_GlobalData::Release() { 55 if (!--m_RefCount) { 56 delete g_pInstance; 57 g_pInstance = nullptr; 58 } 59 } 60 61 CJS_GlobalData::CJS_GlobalData() 62 : m_RefCount(0), m_sFilePath(SDK_JS_GLOBALDATA_FILENAME) { 63 LoadGlobalPersistentVariables(); 64 } 65 66 CJS_GlobalData::~CJS_GlobalData() { 67 SaveGlobalPersisitentVariables(); 68 } 69 70 CJS_GlobalData::iterator CJS_GlobalData::FindGlobalVariable( 71 const CFX_ByteString& propname) { 72 for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end(); 73 ++it) { 74 if ((*it)->data.sKey == propname) 75 return it; 76 } 77 return m_arrayGlobalData.end(); 78 } 79 80 CJS_GlobalData::const_iterator CJS_GlobalData::FindGlobalVariable( 81 const CFX_ByteString& propname) const { 82 for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end(); 83 ++it) { 84 if ((*it)->data.sKey == propname) 85 return it; 86 } 87 return m_arrayGlobalData.end(); 88 } 89 90 CJS_GlobalData_Element* CJS_GlobalData::GetGlobalVariable( 91 const CFX_ByteString& propname) { 92 auto iter = FindGlobalVariable(propname); 93 return iter != m_arrayGlobalData.end() ? iter->get() : nullptr; 94 } 95 96 void CJS_GlobalData::SetGlobalVariableNumber(const CFX_ByteString& propname, 97 double dData) { 98 CFX_ByteString sPropName(propname); 99 if (!TrimPropName(&sPropName)) 100 return; 101 102 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) { 103 pData->data.nType = JS_GlobalDataType::NUMBER; 104 pData->data.dData = dData; 105 return; 106 } 107 std::unique_ptr<CJS_GlobalData_Element> pNewData(new CJS_GlobalData_Element); 108 pNewData->data.sKey = sPropName; 109 pNewData->data.nType = JS_GlobalDataType::NUMBER; 110 pNewData->data.dData = dData; 111 m_arrayGlobalData.push_back(std::move(pNewData)); 112 } 113 114 void CJS_GlobalData::SetGlobalVariableBoolean(const CFX_ByteString& propname, 115 bool bData) { 116 CFX_ByteString sPropName(propname); 117 if (!TrimPropName(&sPropName)) 118 return; 119 120 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) { 121 pData->data.nType = JS_GlobalDataType::BOOLEAN; 122 pData->data.bData = bData; 123 return; 124 } 125 std::unique_ptr<CJS_GlobalData_Element> pNewData(new CJS_GlobalData_Element); 126 pNewData->data.sKey = sPropName; 127 pNewData->data.nType = JS_GlobalDataType::BOOLEAN; 128 pNewData->data.bData = bData; 129 m_arrayGlobalData.push_back(std::move(pNewData)); 130 } 131 132 void CJS_GlobalData::SetGlobalVariableString(const CFX_ByteString& propname, 133 const CFX_ByteString& sData) { 134 CFX_ByteString sPropName(propname); 135 if (!TrimPropName(&sPropName)) 136 return; 137 138 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) { 139 pData->data.nType = JS_GlobalDataType::STRING; 140 pData->data.sData = sData; 141 return; 142 } 143 std::unique_ptr<CJS_GlobalData_Element> pNewData(new CJS_GlobalData_Element); 144 pNewData->data.sKey = sPropName; 145 pNewData->data.nType = JS_GlobalDataType::STRING; 146 pNewData->data.sData = sData; 147 m_arrayGlobalData.push_back(std::move(pNewData)); 148 } 149 150 void CJS_GlobalData::SetGlobalVariableObject( 151 const CFX_ByteString& propname, 152 const CJS_GlobalVariableArray& array) { 153 CFX_ByteString sPropName(propname); 154 if (!TrimPropName(&sPropName)) 155 return; 156 157 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) { 158 pData->data.nType = JS_GlobalDataType::OBJECT; 159 pData->data.objData.Copy(array); 160 return; 161 } 162 std::unique_ptr<CJS_GlobalData_Element> pNewData(new CJS_GlobalData_Element); 163 pNewData->data.sKey = sPropName; 164 pNewData->data.nType = JS_GlobalDataType::OBJECT; 165 pNewData->data.objData.Copy(array); 166 m_arrayGlobalData.push_back(std::move(pNewData)); 167 } 168 169 void CJS_GlobalData::SetGlobalVariableNull(const CFX_ByteString& propname) { 170 CFX_ByteString sPropName(propname); 171 if (!TrimPropName(&sPropName)) 172 return; 173 174 if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) { 175 pData->data.nType = JS_GlobalDataType::NULLOBJ; 176 return; 177 } 178 std::unique_ptr<CJS_GlobalData_Element> pNewData(new CJS_GlobalData_Element); 179 pNewData->data.sKey = sPropName; 180 pNewData->data.nType = JS_GlobalDataType::NULLOBJ; 181 m_arrayGlobalData.push_back(std::move(pNewData)); 182 } 183 184 bool CJS_GlobalData::SetGlobalVariablePersistent(const CFX_ByteString& propname, 185 bool bPersistent) { 186 CFX_ByteString sPropName(propname); 187 if (!TrimPropName(&sPropName)) 188 return false; 189 190 CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName); 191 if (!pData) 192 return false; 193 194 pData->bPersistent = bPersistent; 195 return true; 196 } 197 198 bool CJS_GlobalData::DeleteGlobalVariable(const CFX_ByteString& propname) { 199 CFX_ByteString sPropName(propname); 200 if (!TrimPropName(&sPropName)) 201 return false; 202 203 auto iter = FindGlobalVariable(sPropName); 204 if (iter == m_arrayGlobalData.end()) 205 return false; 206 207 m_arrayGlobalData.erase(iter); 208 return true; 209 } 210 211 int32_t CJS_GlobalData::GetSize() const { 212 return pdfium::CollectionSize<int32_t>(m_arrayGlobalData); 213 } 214 215 CJS_GlobalData_Element* CJS_GlobalData::GetAt(int index) const { 216 if (index < 0 || index >= GetSize()) 217 return nullptr; 218 return m_arrayGlobalData[index].get(); 219 } 220 221 void CJS_GlobalData::LoadGlobalPersistentVariables() { 222 uint8_t* pBuffer = nullptr; 223 int32_t nLength = 0; 224 225 LoadFileBuffer(m_sFilePath.c_str(), pBuffer, nLength); 226 CRYPT_ArcFourCryptBlock(pBuffer, nLength, JS_RC4KEY, sizeof(JS_RC4KEY)); 227 228 if (pBuffer) { 229 uint8_t* p = pBuffer; 230 uint16_t wType = *((uint16_t*)p); 231 p += sizeof(uint16_t); 232 233 if (wType == (uint16_t)(('X' << 8) | 'F')) { 234 uint16_t wVersion = *((uint16_t*)p); 235 p += sizeof(uint16_t); 236 237 ASSERT(wVersion <= 2); 238 239 uint32_t dwCount = *((uint32_t*)p); 240 p += sizeof(uint32_t); 241 242 uint32_t dwSize = *((uint32_t*)p); 243 p += sizeof(uint32_t); 244 245 if (dwSize == nLength - sizeof(uint16_t) * 2 - sizeof(uint32_t) * 2) { 246 for (int32_t i = 0, sz = dwCount; i < sz; i++) { 247 if (p > pBuffer + nLength) 248 break; 249 250 uint32_t dwNameLen = *((uint32_t*)p); 251 p += sizeof(uint32_t); 252 253 if (p + dwNameLen > pBuffer + nLength) 254 break; 255 256 CFX_ByteString sEntry = CFX_ByteString(p, dwNameLen); 257 p += sizeof(char) * dwNameLen; 258 259 JS_GlobalDataType wDataType = 260 static_cast<JS_GlobalDataType>(*((uint16_t*)p)); 261 p += sizeof(uint16_t); 262 263 switch (wDataType) { 264 case JS_GlobalDataType::NUMBER: { 265 double dData = 0; 266 switch (wVersion) { 267 case 1: { 268 uint32_t dwData = *((uint32_t*)p); 269 p += sizeof(uint32_t); 270 dData = dwData; 271 } break; 272 case 2: { 273 dData = *((double*)p); 274 p += sizeof(double); 275 } break; 276 } 277 SetGlobalVariableNumber(sEntry, dData); 278 SetGlobalVariablePersistent(sEntry, true); 279 } break; 280 case JS_GlobalDataType::BOOLEAN: { 281 uint16_t wData = *((uint16_t*)p); 282 p += sizeof(uint16_t); 283 SetGlobalVariableBoolean(sEntry, (bool)(wData == 1)); 284 SetGlobalVariablePersistent(sEntry, true); 285 } break; 286 case JS_GlobalDataType::STRING: { 287 uint32_t dwLength = *((uint32_t*)p); 288 p += sizeof(uint32_t); 289 290 if (p + dwLength > pBuffer + nLength) 291 break; 292 293 SetGlobalVariableString(sEntry, CFX_ByteString(p, dwLength)); 294 SetGlobalVariablePersistent(sEntry, true); 295 p += sizeof(char) * dwLength; 296 } break; 297 case JS_GlobalDataType::NULLOBJ: { 298 SetGlobalVariableNull(sEntry); 299 SetGlobalVariablePersistent(sEntry, true); 300 } 301 case JS_GlobalDataType::OBJECT: 302 break; 303 } 304 } 305 } 306 } 307 FX_Free(pBuffer); 308 } 309 } 310 311 void CJS_GlobalData::SaveGlobalPersisitentVariables() { 312 uint32_t nCount = 0; 313 CFX_BinaryBuf sData; 314 for (const auto& pElement : m_arrayGlobalData) { 315 if (pElement->bPersistent) { 316 CFX_BinaryBuf sElement; 317 MakeByteString(pElement->data.sKey, &pElement->data, sElement); 318 if (sData.GetSize() + sElement.GetSize() > JS_MAXGLOBALDATA) 319 break; 320 321 sData.AppendBlock(sElement.GetBuffer(), sElement.GetSize()); 322 nCount++; 323 } 324 } 325 326 CFX_BinaryBuf sFile; 327 uint16_t wType = (uint16_t)(('X' << 8) | 'F'); 328 sFile.AppendBlock(&wType, sizeof(uint16_t)); 329 uint16_t wVersion = 2; 330 sFile.AppendBlock(&wVersion, sizeof(uint16_t)); 331 sFile.AppendBlock(&nCount, sizeof(uint32_t)); 332 uint32_t dwSize = sData.GetSize(); 333 sFile.AppendBlock(&dwSize, sizeof(uint32_t)); 334 335 sFile.AppendBlock(sData.GetBuffer(), sData.GetSize()); 336 337 CRYPT_ArcFourCryptBlock(sFile.GetBuffer(), sFile.GetSize(), JS_RC4KEY, 338 sizeof(JS_RC4KEY)); 339 WriteFileBuffer(m_sFilePath.c_str(), (const FX_CHAR*)sFile.GetBuffer(), 340 sFile.GetSize()); 341 } 342 343 void CJS_GlobalData::LoadFileBuffer(const FX_WCHAR* sFilePath, 344 uint8_t*& pBuffer, 345 int32_t& nLength) { 346 // UnSupport. 347 } 348 349 void CJS_GlobalData::WriteFileBuffer(const FX_WCHAR* sFilePath, 350 const FX_CHAR* pBuffer, 351 int32_t nLength) { 352 // UnSupport. 353 } 354 355 void CJS_GlobalData::MakeByteString(const CFX_ByteString& name, 356 CJS_KeyValue* pData, 357 CFX_BinaryBuf& sData) { 358 switch (pData->nType) { 359 case JS_GlobalDataType::NUMBER: { 360 uint32_t dwNameLen = (uint32_t)name.GetLength(); 361 sData.AppendBlock(&dwNameLen, sizeof(uint32_t)); 362 sData.AppendString(name); 363 sData.AppendBlock(&pData->nType, sizeof(uint16_t)); 364 365 double dData = pData->dData; 366 sData.AppendBlock(&dData, sizeof(double)); 367 } break; 368 case JS_GlobalDataType::BOOLEAN: { 369 uint32_t dwNameLen = (uint32_t)name.GetLength(); 370 sData.AppendBlock(&dwNameLen, sizeof(uint32_t)); 371 sData.AppendString(name); 372 sData.AppendBlock(&pData->nType, sizeof(uint16_t)); 373 374 uint16_t wData = (uint16_t)pData->bData; 375 sData.AppendBlock(&wData, sizeof(uint16_t)); 376 } break; 377 case JS_GlobalDataType::STRING: { 378 uint32_t dwNameLen = (uint32_t)name.GetLength(); 379 sData.AppendBlock(&dwNameLen, sizeof(uint32_t)); 380 sData.AppendString(name); 381 sData.AppendBlock(&pData->nType, sizeof(uint16_t)); 382 383 uint32_t dwDataLen = (uint32_t)pData->sData.GetLength(); 384 sData.AppendBlock(&dwDataLen, sizeof(uint32_t)); 385 sData.AppendString(pData->sData); 386 } break; 387 case JS_GlobalDataType::NULLOBJ: { 388 uint32_t dwNameLen = (uint32_t)name.GetLength(); 389 sData.AppendBlock(&dwNameLen, sizeof(uint32_t)); 390 sData.AppendString(name); 391 sData.AppendBlock(&pData->nType, sizeof(uint32_t)); 392 } break; 393 default: 394 break; 395 } 396 } 397