From 76c55a87cb2dc5608b00d49f0ec62d32b773e071 Mon Sep 17 00:00:00 2001 From: Kulikova Svetlana Date: Wed, 5 May 2021 16:27:58 +0300 Subject: [PATCH] create COOXMLSignerBuffer_private --- .../xmlsec/src/include/OOXMLSignerBuffer.h | 23 + .../xmlsec/src/src/OOXMLSignerBuffer.cpp | 734 ++++++++++++++++++ OfficeUtils/js/wasm/src/engine.h | 8 + 3 files changed, 765 insertions(+) create mode 100644 DesktopEditor/xmlsec/src/include/OOXMLSignerBuffer.h create mode 100644 DesktopEditor/xmlsec/src/src/OOXMLSignerBuffer.cpp diff --git a/DesktopEditor/xmlsec/src/include/OOXMLSignerBuffer.h b/DesktopEditor/xmlsec/src/include/OOXMLSignerBuffer.h new file mode 100644 index 0000000000..426ad46c2d --- /dev/null +++ b/DesktopEditor/xmlsec/src/include/OOXMLSignerBuffer.h @@ -0,0 +1,23 @@ +#ifndef _XML_OOXMLSIGNERBUFFER_H_ +#define _XML_OOXMLSIGNERBUFFER_H_ + +#include "./XmlCertificate.h" + +class COOXMLSignerBuffer_private; +class Q_DECL_EXPORT COOXMLSignerBuffer +{ +public: + COOXMLSignerBuffer(unsigned char* file, ICertificate* pContext); + ~COOXMLSignerBuffer(); + + void SetGuid(const std::wstring& guid); + void SetImageValid(unsigned char* file); + void SetImageInvalid(unsigned char* file); + + int Sign(); + +private: + COOXMLSignerBuffer_private* m_internal; +}; + +#endif //_XML_OOXMLSIGNERBUFFER_H_ diff --git a/DesktopEditor/xmlsec/src/src/OOXMLSignerBuffer.cpp b/DesktopEditor/xmlsec/src/src/OOXMLSignerBuffer.cpp new file mode 100644 index 0000000000..9980879f29 --- /dev/null +++ b/DesktopEditor/xmlsec/src/src/OOXMLSignerBuffer.cpp @@ -0,0 +1,734 @@ +#include "./../include/OOXMLSignerBuffer.h" +#include "./../src/XmlTransform.h" +#include "./../../../../OfficeUtils/js/wasm/src/base.h" +#include "./../../../../OfficeUtils/js/wasm/src/engine.h" +#include +#include +#include +#include + +class CFile +{ + std::wstring sPath; + BYTE* pData; + DWORD nLength; +public: + CFile(const std::wstring& _sPath, BYTE* _pData, DWORD _nLength) : sPath(_sPath), pData(_pData), nLength(_nLength) {} +}; + +class CFileManagerBuffer +{ + vector arrFiles; + +public: + void addFile(const std::wstring& sPath, BYTE* pData, DWORD nLength) + { + arrFiles.push_back(CFile(sPath, pData, nLength)); + } +}; + +class COOXMLSignerBuffer_private +{ +public: + ICertificate* m_certificate; + CData* m_file; + CFileManagerBuffer m_file_manager; + + std::wstring m_date; + + std::map m_content_types; + std::vector m_rels; + std::vector m_files; + + NSStringUtils::CStringBuilderA m_signed_info; + + std::wstring m_image_valid; + std::wstring m_image_invalid; + + std::wstring m_guid; + +private: + unsigned int GetLength(BYTE* x) + { + return x[0] | x[1] << 8 | x[2] << 16 | x[3] << 24; + } + +public: + COOXMLSignerBuffer_private(BYTE* file, ICertificate* pContext) + { + m_file = new CData(file, GetLength(file)); + + Zlib* zlib = Zlib_Load(file + 4, GetLength(file)); + BYTE* sPaths = Zlib_GetPathsInArchive(zlib); + unsigned int nLength = GetLength(sPaths); + unsigned int i = 4; + nLength -= 4; + + while (i < nLength) + { + unsigned int nPathLength = GetLength(sPaths + i); + i += 4; + + BYTE* sFile = Zlib_GetFileFromArchive(zlib, std::string((char*)(sPaths + i), nPathLength).c_str()); + m_file_manager.addFile(NSFile::CUtf8Converter::GetUnicodeStringFromUTF8(sPaths + i, nPathLength), sFile + 4, GetLength(sFile)); + i += nPathLength; + } + RELEASEARRAYOBJECTS(sPaths); + + m_certificate = pContext; + + m_date = L"2017-04-21T08:30:21Z"; + + std::time_t rawtime; + std::tm* timeinfo; + char buffer1[100]; + char buffer2[100]; + + std::time(&rawtime); + timeinfo = std::gmtime(&rawtime); + + std::strftime(buffer1, 100, "%Y-%m-%d", timeinfo); + std::strftime(buffer2, 100, "%H:%M:%S", timeinfo); + + std::string date = (std::string(buffer1) + "T" + std::string(buffer2) + "Z"); + m_date = NSFile::CUtf8Converter::GetUnicodeFromCharPtr(date); + + m_signed_info.WriteString(""); + m_signed_info.WriteString("GetHashAlg())); + m_signed_info.WriteString("\"/>"); + } + ~COOXMLSignerBuffer_private() {} + + std::wstring GetReference(const std::wstring& file, const std::wstring& content_type) + { + std::wstring sXml = L""; + sXml += (L"GetHashAlg()) + L"\"/>"); + sXml += L""; + std::string sTmp = m_certificate->GetHash(m_sFolder + file, m_certificate->GetHashAlg()); + sXml += UTF8_TO_U(sTmp); + sXml += L""; + sXml += L""; + return sXml; + } + + std::string GetHashXml(const std::wstring& xml) + { + std::string sXmlSigned = U_TO_UTF8(xml); + sXmlSigned = XmlUtils::NSXmlCanonicalizator::Execute(sXmlSigned, XmlUtils::XML_C14N_1_0); + return m_certificate->GetHash(sXmlSigned, m_certificate->GetHashAlg()); + } + + std::string GetReferenceMain(const std::wstring& xml, const std::wstring& id, const bool& isCannon = true) + { + std::wstring sXml1 = L""; + else + sXml1 += (L" Id=\"" + id + L"\">"); + sXml1 += xml; + sXml1 += L""; + + std::string sHash = GetHashXml(sXml1); + + std::string sRet; + if (isCannon) + sRet = ""; + + sRet += ("GetHashAlg()) + "\"/>" + sHash + ""); + + return sRet; + } + + std::wstring GetImageBase64(BYTE* file) + { + /* + if (0 == file.find(L"data:image/")) + { + return file.substr(file.find(L",") + 1); + } + */ + + DWORD dwLen = GetLength(file); + BYTE* pData = file + 4; + + char* pDataC = NULL; + int nLen = 0; + NSFile::CBase64Converter::Encode(pData, (int)dwLen, pDataC, nLen, NSBase64::B64_BASE64_FLAG_NOCRLF); + + std::wstring sReturn = NSFile::CUtf8Converter::GetUnicodeFromCharPtr(pDataC, (LONG)nLen, FALSE); + + RELEASEARRAYOBJECTS(pData); + RELEASEARRAYOBJECTS(pDataC); + + return sReturn; + } + + std::wstring GetRelsReference(const std::wstring& file) + { + COOXMLRelationships oRels(m_sFolder + file, true); + if (oRels.rels.size() == 0) + return L""; + + if (L"/_rels/.rels" == file) + { + oRels.CheckOriginSigs(m_sFolder + file); + + // удалим все лишнее + std::vector::iterator i = oRels.rels.begin(); + while (i != oRels.rels.end()) + { + if (0 == i->target.find(L"docProps/")) + i = oRels.rels.erase(i); + else if (0 == i->target.find(L"_xmlsignatures/")) + i = oRels.rels.erase(i); + else + i++; + } + } + + NSStringUtils::CStringBuilder builder; + builder.WriteString(L""); + builder.WriteString(oRels.GetTransforms()); + builder.WriteString(L"GetHashAlg()) + L"\"/>"); + + std::wstring sXml = oRels.GetXml(); + std::string sHash = GetHashXml(sXml); + + std::wstring sHashW = UTF8_TO_U(sHash); + builder.WriteString(sHashW); + + builder.WriteString(L""); + + return builder.GetData(); + } + + int GetCountSigns(const std::wstring& folder) + { + std::wstring sRelsFolder = folder + L"/_rels"; + + std::vector arFiles = NSDirectory::GetFiles(sRelsFolder, false); + std::map arSigFiles; + + for (std::vector::iterator iter = arFiles.begin(); iter != arFiles.end(); iter++) + { + XmlUtils::CXmlNode oNodeRels; + if (!oNodeRels.FromXmlFile(*iter)) + continue; + XmlUtils::CXmlNodes oNodesRels = oNodeRels.GetNodes(L"Relationship"); + int nCount = oNodesRels.GetCount(); + for (int nIndex = 0; nIndex < nCount; nIndex++) + { + XmlUtils::CXmlNode oNodeRel; + oNodesRels.GetAt(nIndex, oNodeRel); + + std::wstring sTarget = oNodeRel.GetAttribute(L"Target"); + if (!sTarget.empty() && arSigFiles.find(sTarget) == arSigFiles.end() && NSFile::CFileBinary::Exists(folder + L"/" + sTarget)) + arSigFiles.insert(std::pair(sTarget, true)); + } + NSFile::CFileBinary::Remove(*iter); + } + + int nCountSigs = (int)arSigFiles.size(); + std::wstring sFile = sRelsFolder + L"/origin.sigs.rels"; + + NSStringUtils::CStringBuilder oBuilder; + oBuilder.WriteString(L""); + + for (int nIndex = 0; nIndex <= nCountSigs/*old + one new*/; nIndex++) + { + oBuilder.WriteString(L""); + } + + oBuilder.WriteString(L""); + + NSFile::CFileBinary::Remove(sFile); + NSFile::CFileBinary oFile; + oFile.CreateFileW(sFile); + oFile.WriteStringUTF8(oBuilder.GetData()); + oFile.CloseFile(); + + // теперь перебьем все имена файлов + + std::vector arSigs; + + std::vector arFilesXml = NSDirectory::GetFiles(folder, false); + for (std::vector::iterator iter = arFilesXml.begin(); iter != arFilesXml.end(); iter++) + { + std::wstring sXmlFileName = NSFile::GetFileName(*iter); + if (NSFile::GetFileExtention(sXmlFileName) != L"xml") + continue; + + std::map::iterator find = arSigFiles.find(sXmlFileName); + if (find == arSigFiles.end()) + { + // ненужная xml + NSFile::CFileBinary::Remove(*iter); + continue; + } + + arSigs.push_back(sXmlFileName); + } + + std::sort(arSigs.begin(), arSigs.end()); + for (std::vector::iterator iter = arSigs.begin(); iter != arSigs.end(); iter++) + { + NSFile::CFileBinary::Move(folder + L"/" + *iter, folder + L"/onlyoffice_" + *iter); + } + int nSigNumber = 1; + for (std::vector::iterator iter = arSigs.begin(); iter != arSigs.end(); iter++) + { + NSFile::CFileBinary::Move(folder + L"/onlyoffice_" + *iter, folder + L"/sig" + std::to_wstring(nSigNumber++) + L".xml"); + } + + return (int)arSigs.size(); + } + + void ParseContentTypes() + { + std::wstring file = m_sFolder + L"/[Content_Types].xml"; + XmlUtils::CXmlNode oNode; + oNode.FromXmlFile(file); + + XmlUtils::CXmlNodes nodesDefaults; + oNode.GetNodes(L"Default", nodesDefaults); + + XmlUtils::CXmlNodes nodesOverrides; + oNode.GetNodes(L"Override", nodesOverrides); + + int nCount = nodesDefaults.GetCount(); + for (int i = 0; i < nCount; ++i) + { + XmlUtils::CXmlNode node; + nodesDefaults.GetAt(i, node); + + m_content_types.insert(std::pair(node.GetAttribute("Extension"), node.GetAttribute("ContentType"))); + } + + nCount = nodesOverrides.GetCount(); + for (int i = 0; i < nCount; ++i) + { + XmlUtils::CXmlNode node; + nodesOverrides.GetAt(i, node); + + m_content_types.insert(std::pair(node.GetAttribute("PartName"), node.GetAttribute("ContentType"))); + } + } + + void Parse() + { + // 1) Parse Content_Types.xml + ParseContentTypes(); + + // 2) Parse files in directory + std::vector files = NSDirectory::GetFiles(m_sFolder, true); + + // 3) Check each file + std::wstring sFolder = m_sFolder; + NSStringUtils::string_replace(sFolder, L"\\", L"/"); + for (std::vector::iterator i = files.begin(); i != files.end(); i++) + { + std::wstring sCheckFile = *i; + NSStringUtils::string_replace(sCheckFile, L"\\", L"/"); + + if (0 != sCheckFile.find(sFolder)) + continue; + + // make cool filename + sCheckFile = sCheckFile.substr(sFolder.length()); + + // check needed file + if (0 == sCheckFile.find(L"/_xmlsignatures") || + 0 == sCheckFile.find(L"/docProps") || + 0 == sCheckFile.find(L"/[Content_Types].xml") || + 0 == sCheckFile.find(L"/[trash]")) + continue; + + // check rels and add to needed array + std::wstring::size_type posExt = sCheckFile.rfind(L"."); + if (std::wstring::npos == posExt) + continue; + + std::wstring sExt = sCheckFile.substr(posExt + 1); + if (sExt == L"rels") + m_rels.push_back(sCheckFile); + else + m_files.push_back(sCheckFile); + } + + std::sort(m_rels.begin(), m_rels.end()); + std::sort(m_files.begin(), m_files.end()); + } + + void WriteRelsReferences(NSStringUtils::CStringBuilder& builder) + { + for (std::vector::iterator i = m_rels.begin(); i != m_rels.end(); i++) + { + builder.WriteString(GetRelsReference(*i)); + } + } + + void WriteFilesReferences(NSStringUtils::CStringBuilder& builder) + { + for (std::vector::iterator i = m_files.begin(); i != m_files.end(); i++) + { + std::wstring sFile = *i; + std::wstring sContentType = L"application/xml"; + + std::map::iterator _find = m_content_types.find(sFile); + if (_find != m_content_types.end()) + { + sContentType = _find->second; + } + else + { + std::wstring::size_type posExt = sFile.rfind(L"."); + if (std::wstring::npos != posExt) + { + std::wstring sExt = sFile.substr(posExt + 1); + + _find = m_content_types.find(sExt); + if (_find != m_content_types.end()) + sContentType = _find->second; + } + } + + builder.WriteString(GetReference(sFile, sContentType)); + } + } + + void WriteManifest(NSStringUtils::CStringBuilder& builder) + { + builder.WriteString(L""); + WriteRelsReferences(builder); + WriteFilesReferences(builder); + builder.WriteString(L""); + } + + void CorrectContentTypes(int nCountSignatures) + { + std::wstring file = m_sFolder + L"/[Content_Types].xml"; + XmlUtils::CXmlNode oNode; + oNode.FromXmlFile(file); + + NSStringUtils::CStringBuilder oBuilder; + oBuilder.WriteString(L""); + + int nAttributesCount = oNode.GetAttributesCount(); + oBuilder.WriteNodeBegin(oNode.GetName(), nAttributesCount != 0); + + std::vector attrNames; + std::vector attrValues; + oNode.GetAllAttributes(attrNames, attrValues); + + for (int nAttrIndex = 0; nAttrIndex < nAttributesCount; ++nAttrIndex) + { + oBuilder.WriteAttributeEncodeXml(attrNames[nAttrIndex], attrValues[nAttrIndex]); + } + + oBuilder.WriteNodeEnd(oNode.GetName(), true, false); + + XmlUtils::CXmlNodes oNodes = oNode.GetNodes(L"*"); + + int nCountNodes = oNodes.GetCount(); + for (int nIndex = 0; nIndex < nCountNodes; ++nIndex) + { + XmlUtils::CXmlNode oCurrentRecord; + oNodes.GetAt(nIndex, oCurrentRecord); + + if (L"Default" == oCurrentRecord.GetName() && oCurrentRecord.GetAttributeA("Extension") == "sigs") + continue; + if (L"Override" == oCurrentRecord.GetName() && oCurrentRecord.GetAttributeA("ContentType") == "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml") + continue; + + oBuilder.WriteString(oCurrentRecord.GetXml()); + } + + if (0 != nCountSignatures) + oBuilder.WriteString(L""); + + for (int nIndex = 0; nIndex < nCountSignatures; ++nIndex) + { + oBuilder.WriteString(L""); + } + + oBuilder.WriteNodeEnd(oNode.GetName()); + + NSFile::CFileBinary::Remove(file); + NSFile::CFileBinary oFile; + oFile.CreateFileW(file); + oFile.WriteStringUTF8(oBuilder.GetData()); + oFile.CloseFile(); + } + + void SetGuid(const std::wstring& guid) + { + m_guid = guid; + } + void SetImageValid(BYTE* file) + { + m_image_valid = GetImageBase64(file); + } + void SetImageInvalid(BYTE* file) + { + m_image_invalid = GetImageBase64(file); + } + + std::wstring GeneratePackageObject() + { + NSStringUtils::CStringBuilder builder; + WriteManifest(builder); + + builder.WriteString(L""); + builder.WriteString(L""); + builder.WriteString(L"YYYY-MM-DDThh:mm:ssTZD"); + builder.WriteString(L""); + builder.WriteString(m_date); + builder.WriteString(L""); + builder.WriteString(L""); + + std::wstring sXml = builder.GetData(); + + m_signed_info.WriteString(""); + m_signed_info.WriteString(GetReferenceMain(sXml, L"idPackageObject", false)); + m_signed_info.WriteString(""); + + return (L"" + sXml + L""); + } + std::wstring GenerateOfficeObject() + { + NSStringUtils::CStringBuilder builder; + + builder.WriteString(L""); + builder.WriteString(L""); + builder.WriteString(L""); + + std::wstring sGUID = m_guid; + if (0 == sGUID.find(L"/_xmlsignatures")) + sGUID = L""; + + builder.WriteString(sGUID); + builder.WriteString(L""); + builder.WriteString(L""); + builder.WriteString(L""); + builder.WriteString(m_image_valid); + builder.WriteString(L""); + + if (!m_certificate->IsGOST()) + { + builder.WriteString(L"\ +10.0\ +16.0\ +16.0\ +2\ +1680\ +1050\ +32\ +{00000000-0000-0000-0000-000000000000}\ +\ +9\ +2\ +\ +\ +"); + } + else + { + builder.WriteString(L"\ +10.0\ +16.0\ +16.0\ +2\ +1680\ +1050\ +32\ +{F5AC7D23-DA04-45F5-ABCB-38CE7A982553}\ +http://www.cryptopro.ru/products/office/signature\ +8\ +1\ +\ +\ +"); + } + + m_signed_info.WriteString(""); + m_signed_info.WriteString(GetReferenceMain(builder.GetData(), L"idOfficeObject", false)); + m_signed_info.WriteString(""); + + return (L"" + builder.GetData() + L""); + } + + std::wstring GenerateImageObject() + { + if (m_image_valid.empty()) + return L""; + + m_signed_info.WriteString(""); + m_signed_info.WriteString(GetReferenceMain(m_image_valid, L"idValidSigLnImg", false)); + m_signed_info.WriteString(""); + + m_signed_info.WriteString(""); + m_signed_info.WriteString(GetReferenceMain(m_image_invalid, L"idInvalidSigLnImg", false)); + m_signed_info.WriteString(""); + + return (L"" + m_image_valid + L"" + m_image_invalid + L""); + } + + std::wstring GenerateSignPropertiesObject() + { + std::wstring sName = m_certificate->GetIssuerName(); + + std::string sKeyA = m_certificate->GetNumber(); + std::wstring sKey = UTF8_TO_U(sKeyA); + + std::string sCertHA = m_certificate->GetCertificateHash(); + std::wstring sCertHW = UTF8_TO_U(sCertHA); + + std::wstring sXml = (L"\ +" + m_date + L"\ +\ +\ +\ +GetHashAlg()) + L"\"/>\ +" + sCertHW + L"\ +\ +\ +CN=" + sName + L"\ +" + sKey + L"\ +\ +\ +\ +\ +\ +\ +"); + + std::wstring sSignedXml = L""; + sSignedXml += sXml; + sSignedXml += L""; + + std::string sXmlTmp = XmlUtils::NSXmlCanonicalizator::Execute(U_TO_UTF8(sSignedXml), XmlUtils::XML_C14N_1_0); + + m_signed_info.WriteString(""); + m_signed_info.WriteString(""); + m_signed_info.WriteString("GetHashAlg()) + "\"/>"); + m_signed_info.WriteString(m_certificate->GetHash(sXmlTmp, m_certificate->GetHashAlg())); + m_signed_info.WriteString(""); + + return (L"\ +" + sXml + L""); + } + + int AddSignatureReference() + { + std::wstring sDirectory = m_sFolder + L"/_xmlsignatures"; + + if (!NSDirectory::Exists(sDirectory)) + NSDirectory::CreateDirectory(sDirectory); + + // remove old .sig file + std::vector arFiles = NSDirectory::GetFiles(sDirectory, false); + for (std::vector::iterator i = arFiles.begin(); i != arFiles.end(); i++) + { + if (NSFile::GetFileExtention(*i) == L"sigs") + { + NSFile::CFileBinary::Remove(*i); + } + } + + std::wstring sOriginName = L"origin.sigs"; + if (!NSFile::CFileBinary::Exists(sDirectory + L"/" + sOriginName)) + { + NSFile::CFileBinary oFile; + oFile.CreateFileW(sDirectory + L"/" + sOriginName); + oFile.CloseFile(); + } + + if (!NSDirectory::Exists(sDirectory + L"/_rels")) + NSDirectory::CreateDirectory(sDirectory + L"/_rels"); + + int nSignNum = GetCountSigns(sDirectory); + + CorrectContentTypes(nSignNum + 1); + return nSignNum; + } + + int Sign() + { + Parse(); + + std::string sSignedData; + + NSStringUtils::CStringBuilder builderMain; + + builderMain.WriteString(GeneratePackageObject()); + builderMain.WriteString(GenerateOfficeObject()); + builderMain.WriteString(GenerateSignPropertiesObject()); + builderMain.WriteString(GenerateImageObject()); + + std::string sSignedInfoData = m_signed_info.GetData(); + std::string sSignedXml = "" + sSignedInfoData + ""; + sSignedXml = XmlUtils::NSXmlCanonicalizator::Execute(sSignedXml, XmlUtils::XML_C14N_1_0); + sSignedXml = m_certificate->Sign(sSignedXml); + + NSStringUtils::CStringBuilder builderResult; + builderResult.WriteString(L""); + builderResult.WriteString(UTF8_TO_U(sSignedInfoData)); + builderResult.WriteString(L""); + builderResult.WriteString(L""); + builderResult.WriteString(UTF8_TO_U(sSignedXml)); + builderResult.WriteString(L""); + builderResult.WriteString(L""); + builderResult.WriteString(UTF8_TO_U(m_certificate->GetCertificateBase64())); + builderResult.WriteString(L""); + + builderResult.Write(builderMain); + builderResult.WriteString(L""); + + int nSignNum = AddSignatureReference(); + + BYTE* pData; LONG nLength = 0; + NSFile::CUtf8Converter::GetUtf8StringFromUnicode(builderResult.GetData().c_str(), builderResult.GetSize(), pData, nLength); + m_file_manager.addFile(L"_xmlsignatures/sig" + std::to_wstring(nSignNum + 1) + L".xml", pData, nLength); + + return (sSignedXml.empty()) ? 1 : 0; + } +}; + +COOXMLSignerBuffer::COOXMLSignerBuffer(unsigned char* arrFileTree, ICertificate* pContext) +{ + m_internal = new COOXMLSignerBuffer_private(arrFileTree, pContext); +} + +COOXMLSignerBuffer::~COOXMLSignerBuffer() +{ + RELEASEOBJECT(m_internal); +} + +void COOXMLSignerBuffer::SetGuid(const std::wstring& guid) +{ + m_internal->SetGuid(guid); +} + +void COOXMLSignerBuffer::SetImageValid(unsigned char* file) +{ + m_internal->SetImageValid(file); +} + +void COOXMLSignerBuffer::SetImageInvalid(unsigned char* file) +{ + m_internal->SetImageInvalid(file); +} + +int COOXMLSignerBuffer::Sign() +{ + return m_internal->Sign(); +} diff --git a/OfficeUtils/js/wasm/src/engine.h b/OfficeUtils/js/wasm/src/engine.h index 8da917ca83..106873e981 100644 --- a/OfficeUtils/js/wasm/src/engine.h +++ b/OfficeUtils/js/wasm/src/engine.h @@ -27,6 +27,14 @@ public: m_lSizeCur = 0; m_pDataCur = m_pData; } + CData(unsigned char* value, unsigned int len) + { + m_lSize = len; + m_pData = value; + + m_lSizeCur = m_lSize; + m_pDataCur = m_pData + m_lSizeCur; + } virtual ~CData() { Clear();