You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
400 lines
12 KiB
400 lines
12 KiB
// Copyright 2014 PDFium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
|
|
|
|
#include "fxjs/cfx_globaldata.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "core/fdrm/fx_crypt.h"
|
|
#include "third_party/base/ptr_util.h"
|
|
#include "third_party/base/stl_util.h"
|
|
|
|
namespace {
|
|
|
|
constexpr size_t kMinGlobalDataBytes = 12;
|
|
constexpr size_t kMaxGlobalDataBytes = 4 * 1024 - 8;
|
|
constexpr uint16_t kMagic = ('X' << 8) | 'F';
|
|
constexpr uint16_t kMaxVersion = 2;
|
|
|
|
const uint8_t kRC4KEY[] = {
|
|
0x19, 0xa8, 0xe8, 0x01, 0xf6, 0xa8, 0xb6, 0x4d, 0x82, 0x04, 0x45, 0x6d,
|
|
0xb4, 0xcf, 0xd7, 0x77, 0x67, 0xf9, 0x75, 0x9f, 0xf0, 0xe0, 0x1e, 0x51,
|
|
0xee, 0x46, 0xfd, 0x0b, 0xc9, 0x93, 0x25, 0x55, 0x4a, 0xee, 0xe0, 0x16,
|
|
0xd0, 0xdf, 0x8c, 0xfa, 0x2a, 0xa9, 0x49, 0xfd, 0x97, 0x1c, 0x0e, 0x22,
|
|
0x13, 0x28, 0x7c, 0xaf, 0xc4, 0xfc, 0x9c, 0x12, 0x65, 0x8c, 0x4e, 0x5b,
|
|
0x04, 0x75, 0x89, 0xc9, 0xb1, 0xed, 0x50, 0xca, 0x96, 0x6f, 0x1a, 0x7a,
|
|
0xfe, 0x58, 0x5d, 0xec, 0x19, 0x4a, 0xf6, 0x35, 0x6a, 0x97, 0x14, 0x00,
|
|
0x0e, 0xd0, 0x6b, 0xbb, 0xd5, 0x75, 0x55, 0x8b, 0x6e, 0x6b, 0x19, 0xa0,
|
|
0xf8, 0x77, 0xd5, 0xa3};
|
|
|
|
CFX_GlobalData* g_pInstance = nullptr;
|
|
|
|
// Returns true if non-empty, setting sPropName
|
|
bool TrimPropName(ByteString* sPropName) {
|
|
sPropName->Trim();
|
|
return sPropName->GetLength() != 0;
|
|
}
|
|
|
|
void MakeNameTypeString(const ByteString& name,
|
|
CFX_Value::DataType eType,
|
|
CFX_BinaryBuf* result) {
|
|
uint32_t dwNameLen = (uint32_t)name.GetLength();
|
|
result->AppendBlock(&dwNameLen, sizeof(uint32_t));
|
|
result->AppendString(name);
|
|
|
|
uint16_t wType = static_cast<uint16_t>(eType);
|
|
result->AppendBlock(&wType, sizeof(uint16_t));
|
|
}
|
|
|
|
bool MakeByteString(const ByteString& name,
|
|
const CFX_KeyValue& pData,
|
|
CFX_BinaryBuf* result) {
|
|
switch (pData.nType) {
|
|
case CFX_Value::DataType::NUMBER: {
|
|
MakeNameTypeString(name, pData.nType, result);
|
|
double dData = pData.dData;
|
|
result->AppendBlock(&dData, sizeof(double));
|
|
return true;
|
|
}
|
|
case CFX_Value::DataType::BOOLEAN: {
|
|
MakeNameTypeString(name, pData.nType, result);
|
|
uint16_t wData = static_cast<uint16_t>(pData.bData);
|
|
result->AppendBlock(&wData, sizeof(uint16_t));
|
|
return true;
|
|
}
|
|
case CFX_Value::DataType::STRING: {
|
|
MakeNameTypeString(name, pData.nType, result);
|
|
uint32_t dwDataLen = (uint32_t)pData.sData.GetLength();
|
|
result->AppendBlock(&dwDataLen, sizeof(uint32_t));
|
|
result->AppendString(pData.sData);
|
|
return true;
|
|
}
|
|
case CFX_Value::DataType::NULLOBJ: {
|
|
MakeNameTypeString(name, pData.nType, result);
|
|
return true;
|
|
}
|
|
// Arrays don't get persisted per JS spec page 484.
|
|
case CFX_Value::DataType::OBJECT:
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
CFX_GlobalData* CFX_GlobalData::GetRetainedInstance(Delegate* pDelegate) {
|
|
if (!g_pInstance) {
|
|
g_pInstance = new CFX_GlobalData(pDelegate);
|
|
}
|
|
++g_pInstance->m_RefCount;
|
|
return g_pInstance;
|
|
}
|
|
|
|
bool CFX_GlobalData::Release() {
|
|
if (--m_RefCount)
|
|
return false;
|
|
|
|
delete g_pInstance;
|
|
g_pInstance = nullptr;
|
|
return true;
|
|
}
|
|
|
|
CFX_GlobalData::CFX_GlobalData(Delegate* pDelegate) : m_pDelegate(pDelegate) {
|
|
LoadGlobalPersistentVariables();
|
|
}
|
|
|
|
CFX_GlobalData::~CFX_GlobalData() {
|
|
SaveGlobalPersisitentVariables();
|
|
}
|
|
|
|
CFX_GlobalData::iterator CFX_GlobalData::FindGlobalVariable(
|
|
const ByteString& propname) {
|
|
for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end();
|
|
++it) {
|
|
if ((*it)->data.sKey == propname)
|
|
return it;
|
|
}
|
|
return m_arrayGlobalData.end();
|
|
}
|
|
|
|
CFX_GlobalData::Element* CFX_GlobalData::GetGlobalVariable(
|
|
const ByteString& propname) {
|
|
auto iter = FindGlobalVariable(propname);
|
|
return iter != m_arrayGlobalData.end() ? iter->get() : nullptr;
|
|
}
|
|
|
|
void CFX_GlobalData::SetGlobalVariableNumber(ByteString sPropName,
|
|
double dData) {
|
|
if (!TrimPropName(&sPropName))
|
|
return;
|
|
|
|
CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
|
|
if (pData) {
|
|
pData->data.nType = CFX_Value::DataType::NUMBER;
|
|
pData->data.dData = dData;
|
|
return;
|
|
}
|
|
auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
|
|
pNewData->data.sKey = std::move(sPropName);
|
|
pNewData->data.nType = CFX_Value::DataType::NUMBER;
|
|
pNewData->data.dData = dData;
|
|
m_arrayGlobalData.push_back(std::move(pNewData));
|
|
}
|
|
|
|
void CFX_GlobalData::SetGlobalVariableBoolean(ByteString sPropName,
|
|
bool bData) {
|
|
if (!TrimPropName(&sPropName))
|
|
return;
|
|
|
|
CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
|
|
if (pData) {
|
|
pData->data.nType = CFX_Value::DataType::BOOLEAN;
|
|
pData->data.bData = bData;
|
|
return;
|
|
}
|
|
auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
|
|
pNewData->data.sKey = std::move(sPropName);
|
|
pNewData->data.nType = CFX_Value::DataType::BOOLEAN;
|
|
pNewData->data.bData = bData;
|
|
m_arrayGlobalData.push_back(std::move(pNewData));
|
|
}
|
|
|
|
void CFX_GlobalData::SetGlobalVariableString(ByteString sPropName,
|
|
const ByteString& sData) {
|
|
if (!TrimPropName(&sPropName))
|
|
return;
|
|
|
|
CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
|
|
if (pData) {
|
|
pData->data.nType = CFX_Value::DataType::STRING;
|
|
pData->data.sData = sData;
|
|
return;
|
|
}
|
|
auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
|
|
pNewData->data.sKey = std::move(sPropName);
|
|
pNewData->data.nType = CFX_Value::DataType::STRING;
|
|
pNewData->data.sData = sData;
|
|
m_arrayGlobalData.push_back(std::move(pNewData));
|
|
}
|
|
|
|
void CFX_GlobalData::SetGlobalVariableObject(
|
|
ByteString sPropName,
|
|
std::vector<std::unique_ptr<CFX_KeyValue>> array) {
|
|
if (!TrimPropName(&sPropName))
|
|
return;
|
|
|
|
CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
|
|
if (pData) {
|
|
pData->data.nType = CFX_Value::DataType::OBJECT;
|
|
pData->data.objData = std::move(array);
|
|
return;
|
|
}
|
|
auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
|
|
pNewData->data.sKey = std::move(sPropName);
|
|
pNewData->data.nType = CFX_Value::DataType::OBJECT;
|
|
pNewData->data.objData = std::move(array);
|
|
m_arrayGlobalData.push_back(std::move(pNewData));
|
|
}
|
|
|
|
void CFX_GlobalData::SetGlobalVariableNull(ByteString sPropName) {
|
|
if (!TrimPropName(&sPropName))
|
|
return;
|
|
|
|
CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
|
|
if (pData) {
|
|
pData->data.nType = CFX_Value::DataType::NULLOBJ;
|
|
return;
|
|
}
|
|
auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
|
|
pNewData->data.sKey = std::move(sPropName);
|
|
pNewData->data.nType = CFX_Value::DataType::NULLOBJ;
|
|
m_arrayGlobalData.push_back(std::move(pNewData));
|
|
}
|
|
|
|
bool CFX_GlobalData::SetGlobalVariablePersistent(ByteString sPropName,
|
|
bool bPersistent) {
|
|
if (!TrimPropName(&sPropName))
|
|
return false;
|
|
|
|
CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
|
|
if (!pData)
|
|
return false;
|
|
|
|
pData->bPersistent = bPersistent;
|
|
return true;
|
|
}
|
|
|
|
bool CFX_GlobalData::DeleteGlobalVariable(ByteString sPropName) {
|
|
if (!TrimPropName(&sPropName))
|
|
return false;
|
|
|
|
auto iter = FindGlobalVariable(sPropName);
|
|
if (iter == m_arrayGlobalData.end())
|
|
return false;
|
|
|
|
m_arrayGlobalData.erase(iter);
|
|
return true;
|
|
}
|
|
|
|
int32_t CFX_GlobalData::GetSize() const {
|
|
return pdfium::CollectionSize<int32_t>(m_arrayGlobalData);
|
|
}
|
|
|
|
CFX_GlobalData::Element* CFX_GlobalData::GetAt(int index) {
|
|
if (index < 0 || index >= GetSize())
|
|
return nullptr;
|
|
return m_arrayGlobalData[index].get();
|
|
}
|
|
|
|
bool CFX_GlobalData::LoadGlobalPersistentVariables() {
|
|
if (!m_pDelegate)
|
|
return false;
|
|
|
|
bool ret;
|
|
{
|
|
// Span can't outlive call to BufferDone().
|
|
Optional<pdfium::span<uint8_t>> buffer = m_pDelegate->LoadBuffer();
|
|
if (!buffer.has_value() || buffer.value().empty())
|
|
return false;
|
|
|
|
ret = LoadGlobalPersistentVariablesFromBuffer(buffer.value());
|
|
}
|
|
m_pDelegate->BufferDone();
|
|
return ret;
|
|
}
|
|
|
|
bool CFX_GlobalData::LoadGlobalPersistentVariablesFromBuffer(
|
|
pdfium::span<uint8_t> buffer) {
|
|
if (buffer.size() < kMinGlobalDataBytes)
|
|
return false;
|
|
|
|
CRYPT_ArcFourCryptBlock(buffer, kRC4KEY);
|
|
|
|
uint8_t* p = buffer.data();
|
|
uint16_t wType = *((uint16_t*)p);
|
|
p += sizeof(uint16_t);
|
|
if (wType != kMagic)
|
|
return false;
|
|
|
|
uint16_t wVersion = *((uint16_t*)p);
|
|
p += sizeof(uint16_t);
|
|
if (wVersion > kMaxVersion)
|
|
return false;
|
|
|
|
uint32_t dwCount = *((uint32_t*)p);
|
|
p += sizeof(uint32_t);
|
|
|
|
uint32_t dwSize = *((uint32_t*)p);
|
|
p += sizeof(uint32_t);
|
|
|
|
if (dwSize != buffer.size() - sizeof(uint16_t) * 2 - sizeof(uint32_t) * 2)
|
|
return false;
|
|
|
|
for (int32_t i = 0, sz = dwCount; i < sz; i++) {
|
|
if (p > buffer.end())
|
|
break;
|
|
|
|
uint32_t dwNameLen = *((uint32_t*)p);
|
|
p += sizeof(uint32_t);
|
|
if (p + dwNameLen > buffer.end())
|
|
break;
|
|
|
|
ByteString sEntry = ByteString(p, dwNameLen);
|
|
p += sizeof(char) * dwNameLen;
|
|
|
|
CFX_Value::DataType wDataType =
|
|
static_cast<CFX_Value::DataType>(*((uint16_t*)p));
|
|
p += sizeof(uint16_t);
|
|
|
|
switch (wDataType) {
|
|
case CFX_Value::DataType::NUMBER: {
|
|
double dData = 0;
|
|
switch (wVersion) {
|
|
case 1: {
|
|
uint32_t dwData = *((uint32_t*)p);
|
|
p += sizeof(uint32_t);
|
|
dData = dwData;
|
|
} break;
|
|
case 2: {
|
|
dData = *((double*)p);
|
|
p += sizeof(double);
|
|
} break;
|
|
}
|
|
SetGlobalVariableNumber(sEntry, dData);
|
|
SetGlobalVariablePersistent(sEntry, true);
|
|
} break;
|
|
case CFX_Value::DataType::BOOLEAN: {
|
|
uint16_t wData = *((uint16_t*)p);
|
|
p += sizeof(uint16_t);
|
|
SetGlobalVariableBoolean(sEntry, (bool)(wData == 1));
|
|
SetGlobalVariablePersistent(sEntry, true);
|
|
} break;
|
|
case CFX_Value::DataType::STRING: {
|
|
uint32_t dwLength = *((uint32_t*)p);
|
|
p += sizeof(uint32_t);
|
|
if (p + dwLength > buffer.end())
|
|
break;
|
|
|
|
SetGlobalVariableString(sEntry, ByteString(p, dwLength));
|
|
SetGlobalVariablePersistent(sEntry, true);
|
|
p += sizeof(char) * dwLength;
|
|
} break;
|
|
case CFX_Value::DataType::NULLOBJ: {
|
|
SetGlobalVariableNull(sEntry);
|
|
SetGlobalVariablePersistent(sEntry, true);
|
|
} break;
|
|
case CFX_Value::DataType::OBJECT:
|
|
default:
|
|
// Arrays aren't allowed in these buffers, nor are unrecoginzed tags.
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CFX_GlobalData::SaveGlobalPersisitentVariables() {
|
|
if (!m_pDelegate)
|
|
return false;
|
|
|
|
uint32_t nCount = 0;
|
|
CFX_BinaryBuf sData;
|
|
for (const auto& pElement : m_arrayGlobalData) {
|
|
if (!pElement->bPersistent)
|
|
continue;
|
|
|
|
CFX_BinaryBuf sElement;
|
|
if (!MakeByteString(pElement->data.sKey, pElement->data, &sElement))
|
|
continue;
|
|
|
|
if (sData.GetSize() + sElement.GetSize() > kMaxGlobalDataBytes)
|
|
break;
|
|
|
|
sData.AppendSpan(sElement.GetSpan());
|
|
nCount++;
|
|
}
|
|
|
|
CFX_BinaryBuf sFile;
|
|
uint16_t wType = kMagic;
|
|
uint16_t wVersion = 2;
|
|
sFile.AppendBlock(&wType, sizeof(uint16_t));
|
|
sFile.AppendBlock(&wVersion, sizeof(uint16_t));
|
|
sFile.AppendBlock(&nCount, sizeof(uint32_t));
|
|
|
|
uint32_t dwSize = sData.GetSize();
|
|
sFile.AppendBlock(&dwSize, sizeof(uint32_t));
|
|
sFile.AppendSpan(sData.GetSpan());
|
|
|
|
CRYPT_ArcFourCryptBlock(sFile.GetSpan(), kRC4KEY);
|
|
|
|
return m_pDelegate->StoreBuffer({sFile.GetBuffer(), sFile.GetSize()});
|
|
}
|
|
|
|
CFX_GlobalData::Element::Element() = default;
|
|
|
|
CFX_GlobalData::Element::~Element() = default;
|