diff --git a/.docker/core.bake.Dockerfile b/.docker/core.bake.Dockerfile index e61c84a69f..003faa4de6 100644 --- a/.docker/core.bake.Dockerfile +++ b/.docker/core.bake.Dockerfile @@ -106,6 +106,7 @@ FROM vcpkg-${NUGET_CACHE} AS core-base python3-httplib2 \ lsb-release autoconf automake libtool findutils \ gn \ + mdbtools-dev \ #&& curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc \ # | gpg --dearmor -o /etc/apt/keyrings/kitware.gpg \ #&& echo "deb [signed-by=/etc/apt/keyrings/kitware.gpg] https://apt.kitware.com/ubuntu/ noble main" \ diff --git a/Common/OfficeFileFormatChecker2.cpp b/Common/OfficeFileFormatChecker2.cpp index 140ab4fd8f..6d24d051c8 100644 --- a/Common/OfficeFileFormatChecker2.cpp +++ b/Common/OfficeFileFormatChecker2.cpp @@ -1,2181 +1,2219 @@ -/* - * (c) Copyright Ascensio System SIA 2010-2023 - * - * This program is a free software product. You can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License (AGPL) - * version 3 as published by the Free Software Foundation. In accordance with - * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect - * that Ascensio System SIA expressly excludes the warranty of non-infringement - * of any third-party rights. - * - * This program is distributed WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For - * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html - * - * The interactive user interfaces in modified source and object code versions - * of the Program must display Appropriate Legal Notices, as required under - * Section 5 of the GNU AGPL version 3. - * - * All the Product's GUI elements, including illustrations and icon sets, as - * well as technical writing content are licensed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International. See the License - * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode - * - */ -#include "OfficeFileFormatChecker.h" - -#include "../DesktopEditor/common/Directory.h" -#include "../DesktopEditor/xml/include/xmlutils.h" -#include "../OOXML/Base/Base.h" -#include "../OfficeUtils/src/OfficeUtils.h" - -//#define FILE_FORMAT_CHECKER_WITH_MACRO - -#if defined FILE_FORMAT_CHECKER_WITH_MACRO - #include "../MsBinaryFile/PptFile/Main/PPTFormatLib.h" - #endif - -#include "3dParty/pole/pole.h" -#include -#include - -#include "OfficeFileFormatDefines.h" - -#define MIN_SIZE_BUFFER 4096 -#define MAX_SIZE_BUFFER 102400 - -std::string ReadStringFromOle(POLE::Stream *stream, unsigned int max_size) -{ - std::string result; - - if (!stream) - return result; - - _UINT32 cch = 0; - if (4 != stream->read((BYTE *)&cch, 4)) - return result; - - unsigned char *stringBytes = new unsigned char[max_size]; - if (!stringBytes) - return result; - - if (cch > max_size) - { - // error ... skip to 0 - unsigned int pos_orinal = (unsigned int)stream->tell(); - unsigned int pos = 0; - - stream->read(stringBytes, max_size); - - while (pos < max_size) - { - if (stringBytes[pos] == 0) - break; - pos++; - } - stream->seek(pos_orinal + pos - 1); - } - else - { - if (cch > 0) - { - // dont read the terminating zero - cch = (_UINT32)stream->read(stringBytes, cch); - result = std::string((char *)stringBytes, cch); - } - } - RELEASEARRAYOBJECTS(stringBytes); - // skip the terminating zero of the Unicode string - stream->seek(stream->tell() + 2); - - return result; -} -bool COfficeFileFormatChecker::isRtfFormatFile(unsigned char *pBuffer, int dwBytes) -{ - if (pBuffer == NULL) - return false; - - if ((5 <= dwBytes) && ('{' == pBuffer[0] && '\\' == pBuffer[1] && 'r' == pBuffer[2] && 't' == pBuffer[3] && 'f' == pBuffer[4])) - return true; - - return false; -} -bool COfficeFileFormatChecker::isMultiPartsHtmlFormatFile(unsigned char *pBuffer, int dwBytes) -{ - if (pBuffer == NULL) - return false; - - const char *contentTypeFormatLine1 = "Content-Type: multipart/related"; +/* + * (c) Copyright Ascensio System SIA 2010-2023 + * + * This program is a free software product. You can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License (AGPL) + * version 3 as published by the Free Software Foundation. In accordance with + * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect + * that Ascensio System SIA expressly excludes the warranty of non-infringement + * of any third-party rights. + * + * This program is distributed WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For + * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html + * + * The interactive user interfaces in modified source and object code versions + * of the Program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU AGPL version 3. + * + * All the Product's GUI elements, including illustrations and icon sets, as + * well as technical writing content are licensed under the terms of the + * Creative Commons Attribution-ShareAlike 4.0 International. See the License + * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + * + */ +#include "OfficeFileFormatChecker.h" + +#include "../DesktopEditor/common/Directory.h" +#include "../DesktopEditor/xml/include/xmlutils.h" +#include "../OOXML/Base/Base.h" +#include "../OfficeUtils/src/OfficeUtils.h" + +//#define FILE_FORMAT_CHECKER_WITH_MACRO + +#if defined FILE_FORMAT_CHECKER_WITH_MACRO + #include "../MsBinaryFile/PptFile/Main/PPTFormatLib.h" + #endif + +#include "3dParty/pole/pole.h" +#include +#include + +#include "OfficeFileFormatDefines.h" + +#define MIN_SIZE_BUFFER 4096 +#define MAX_SIZE_BUFFER 102400 + +std::string ReadStringFromOle(POLE::Stream *stream, unsigned int max_size) +{ + std::string result; + + if (!stream) + return result; + + _UINT32 cch = 0; + if (4 != stream->read((BYTE *)&cch, 4)) + return result; + + unsigned char *stringBytes = new unsigned char[max_size]; + if (!stringBytes) + return result; + + if (cch > max_size) + { + // error ... skip to 0 + unsigned int pos_orinal = (unsigned int)stream->tell(); + unsigned int pos = 0; + + stream->read(stringBytes, max_size); + + while (pos < max_size) + { + if (stringBytes[pos] == 0) + break; + pos++; + } + stream->seek(pos_orinal + pos - 1); + } + else + { + if (cch > 0) + { + // dont read the terminating zero + cch = (_UINT32)stream->read(stringBytes, cch); + result = std::string((char *)stringBytes, cch); + } + } + RELEASEARRAYOBJECTS(stringBytes); + // skip the terminating zero of the Unicode string + stream->seek(stream->tell() + 2); + + return result; +} +bool COfficeFileFormatChecker::isRtfFormatFile(unsigned char *pBuffer, int dwBytes) +{ + if (pBuffer == NULL) + return false; + + if ((5 <= dwBytes) && ('{' == pBuffer[0] && '\\' == pBuffer[1] && 'r' == pBuffer[2] && 't' == pBuffer[3] && 'f' == pBuffer[4])) + return true; + + return false; +} +bool COfficeFileFormatChecker::isMultiPartsHtmlFormatFile(unsigned char *pBuffer, int dwBytes) +{ + if (pBuffer == NULL) + return false; + + const char *contentTypeFormatLine1 = "Content-Type: multipart/related"; const char *contentTypeFormatLine2 = "Content-Type: text/html"; // may be outside the specified buffer (TODO) - - std::string xml_string((char *)pBuffer, dwBytes); - - if ((std::string::npos != xml_string.find(contentTypeFormatLine1)) && (std::string::npos != xml_string.find(contentTypeFormatLine2))) - { - return true; - } - return false; -} - -bool COfficeFileFormatChecker::iXmlFile(const std::wstring &fileName) -{ - XmlUtils::CXmlLiteReader oReader; - if (!oReader.FromFile(fileName)) - return false; - if (!oReader.ReadNextNode()) - return false; - - std::wstring name = oReader.GetNameNoNS(); - - if (name == L"svg") - { - nFileType = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_SVG; - } - else if (name == L"html") - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML; - } - else - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_XML; - } - - return true; -} - -bool COfficeFileFormatChecker::isHtmlFormatFile(unsigned char *pBuffer, int dwBytes, bool testCloseTag) -{ - if (pBuffer == NULL || dwBytes < 4) - return false; - - bool tagOpen = false; - - if (testCloseTag && dwBytes > 5) - { - for (int i = 0; i < dwBytes - 6; i++) - { - if ((0x3C == pBuffer[i]) && (0x2F == pBuffer[i + 1]) && (0x48 == pBuffer[i + 2] || 0x68 == pBuffer[i + 2]) && (0x54 == pBuffer[i + 3] || 0x74 == pBuffer[i + 3]) && - (0x4d == pBuffer[i + 4] || 0x6d == pBuffer[i + 4]) && (0x4c == pBuffer[i + 5] || 0x6c == pBuffer[i + 5])) - { - return true; - } - else if ( - (0x3C == pBuffer[i]) && (0x2F == pBuffer[i + 1]) && (0x62 == pBuffer[i + 2]) && (0x6f == pBuffer[i + 3]) && (0x64 == pBuffer[i + 4]) && (0x79 == pBuffer[i + 5]) && - (0x3e == pBuffer[i + 6])) - { // - return true; - } - } - } - else if (dwBytes > 3) - { - for (int i = 0; i < dwBytes - 4 && i < 100; i++) - { - if (0x3C == pBuffer[i]) - tagOpen = true; - else if (0x3E == pBuffer[i]) - tagOpen = false; - else if ( - tagOpen && (0x48 == pBuffer[i] || 0x68 == pBuffer[i]) && (0x54 == pBuffer[i + 1] || 0x74 == pBuffer[i + 1]) && (0x4d == pBuffer[i + 2] || 0x6d == pBuffer[i + 2]) && - (0x4c == pBuffer[i + 3] || 0x6c == pBuffer[i + 3])) - { - return true; - } - } - } - return false; -} - -bool COfficeFileFormatChecker::isBinaryDoctFormatFile(unsigned char *pBuffer, int dwBytes) -{ - if (pBuffer == NULL) - return false; - - if ((4 <= dwBytes) && ('D' == pBuffer[0] && 'O' == pBuffer[1] && 'C' == pBuffer[2] && 'Y' == pBuffer[3])) - return true; - - return false; -} -bool COfficeFileFormatChecker::isBinaryXlstFormatFile(unsigned char *pBuffer, int dwBytes) -{ - if (pBuffer == NULL) - return false; - - if ((4 <= dwBytes) && ('X' == pBuffer[0] && 'L' == pBuffer[1] && 'S' == pBuffer[2] && 'Y' == pBuffer[3])) - return true; - - return false; -} -bool COfficeFileFormatChecker::isBinaryPpttFormatFile(unsigned char *pBuffer, int dwBytes) -{ - if (pBuffer == NULL) - return false; - - if ((4 <= dwBytes) && ('P' == pBuffer[0] && 'P' == pBuffer[1] && 'T' == pBuffer[2] && 'Y' == pBuffer[3])) - return true; - - return false; -} -bool COfficeFileFormatChecker::isBinaryVsdtFormatFile(unsigned char* pBuffer, int dwBytes) -{ - if (pBuffer == NULL) - return false; - - if ((4 <= dwBytes) && ('V' == pBuffer[0] && 'S' == pBuffer[1] && 'D' == pBuffer[2] && 'Y' == pBuffer[3])) - return true; - - return false; -} -bool COfficeFileFormatChecker::isPdfFormatFile(unsigned char *pBuffer, int dwBytes, std::wstring &documentID) -{ - if (pBuffer == NULL) - return false; - - documentID.clear(); - - if (dwBytes < 5 || (pBuffer[0] == 'P' && pBuffer[1] == 'K')) - return false; - - pBuffer[dwBytes - 1] = '\0'; - - char *pFirst = strstr((char *)pBuffer, "%PDF-"); - - if (NULL == pFirst) - { - char* pData = (char*)pBuffer; - for (int i = 0; i < dwBytes - 5; ++i) - { - int nPDF = strncmp(&pData[i], "%PDF-", 5); - if (!nPDF) - { - pFirst = (char*)pBuffer + i; - break; - } - } - if (NULL == pFirst) - { - //skip special - _UINT16 sz = pBuffer[0] + (pBuffer[1] << 8); - if (sz < dwBytes - 8) - { - pFirst = strstr((char*)(pBuffer + sz), "%PDF-"); - } - } - } - if (NULL != pFirst) - { - pFirst = strstr((char *)pBuffer, "%DocumentID "); - if (NULL != pFirst) - { - pFirst += 12; - char *pLast = strstr(pFirst, " "); - if (NULL != pLast) - { - std::string s(pFirst, pLast - pFirst); - documentID = NSFile::CUtf8Converter::GetUnicodeStringFromUTF8((BYTE*)pFirst, (LONG)(pLast - pFirst)); - } - } - return true; - } - - return false; -} -bool COfficeFileFormatChecker::isPdfOformFormatFile(unsigned char *pBuffer, int dwBytes) -{ - pBuffer[dwBytes - 1] = 0; - char* pFirst = strstr((char*)pBuffer, "%\315\312\322\251\015"); - - if (!pFirst || pFirst - (char*)pBuffer + 6 >= dwBytes) - return false; - - pFirst += 6; - - if (strncmp(pFirst, "1 0 obj\012<<\012", 11) != 0 || pFirst - (char*)pBuffer + 11 >= dwBytes) - return false; - - pFirst += 11; - - char* pStream = strstr(pFirst, "stream\015\012"); - char* pMeta = strstr(pFirst, g_format_oform_pdf_meta_tag); - if (!pStream || !pMeta || pStream < pMeta) - return false; - - pMeta += strlen(g_format_oform_pdf_meta_tag) + 3; - - char* pMetaLast = strstr(pMeta, " "); - if (!pMetaLast) - return false; - - pMeta = pMetaLast + 1; - pMetaLast = strstr(pMeta, " "); - if (!pMetaLast) - return false; - - return true; -} -bool COfficeFileFormatChecker::isOleObjectFile(POLE::Storage *storage) -{ - if (storage == NULL) - return false; - - POLE::Stream streamOle(storage, L"Ole"); - - if (false == streamOle.fail()) - { - std::string UserType, ClipboardFormat, Program; - - POLE::Stream streamCompObject(storage, L"CompObj"); - if (false == streamCompObject.fail() && streamCompObject.size() >= 28) - { - streamCompObject.seek(28); // skip Header - - unsigned int sz_obj = (unsigned int)(streamCompObject.size() - streamCompObject.tell()); - - if (sz_obj > 4) - { - UserType = ReadStringFromOle(&streamCompObject, sz_obj); - - sz_obj = (unsigned int)(streamCompObject.size() - streamCompObject.tell()); - if (sz_obj > 4) - ClipboardFormat = ReadStringFromOle(&streamCompObject, sz_obj); - - sz_obj = (unsigned int)(streamCompObject.size() - streamCompObject.tell()); - if (sz_obj > 4) - Program = ReadStringFromOle(&streamCompObject, sz_obj); - } - POLE::Stream streamPackage(storage, L"Package"); - if (false == streamPackage.fail()) - { - nFileType = AVS_OFFICESTUDIO_FILE_OTHER_PACKAGE_IN_OLE; - } - else if (std::string::npos != Program.find("Excel") || std::string::npos != UserType.find("Excel")) - { - if (isXlsFormatFile(storage)) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLS; - } - } - else if (std::string::npos != Program.find("Word") || std::string::npos != UserType.find("Word")) - { - if (isDocFormatFile(storage)) - { - //nFileType inside - } - } - else if (std::string::npos != Program.find("PowerPoint") || std::string::npos != UserType.find("PowerPoint")) - { - if (isPptFormatFile(storage)) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPT; - } - } - - return true; - } - else - { - POLE::Stream streamLinkInfo(storage, L"LinkInfo"); - if (false == streamLinkInfo.fail()) - { - short cch = 0; - if (2 == streamLinkInfo.read((BYTE *)&cch, 2)) - { - unsigned char *str = new unsigned char[cch]; - cch = (short)streamLinkInfo.read(str, cch); - - ClipboardFormat = std::string((char *)str, cch); - RELEASEARRAYOBJECTS(str); - - streamLinkInfo.seek(streamLinkInfo.tell() + 6); - // skip ... - } - return true; - } - } - } - return false; -} -bool COfficeFileFormatChecker::isDocFormatFile(POLE::Storage *storage) -{ - if (storage == NULL) - return false; - - POLE::Stream stream(storage, L"WordDocument"); - - unsigned char buffer[64]; - memset(buffer, 0, 64); - - if (stream.read(buffer, 64) > 0) - { - // ms office 2007 encrypted contains stream WordDocument !! - std::list entries = storage->entries(L"DataSpaces"); - if (entries.size() > 0) - return false; - - if ((buffer[0] == 0xEC && buffer[1] == 0xA5) || // word 1997-2003 - (buffer[0] == 0xDC && buffer[1] == 0xA5) || // word 1995 - (buffer[0] == 0xDB && buffer[1] == 0xA5)) // word 2.0 - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOC; - } - else if (isHtmlFormatFile(buffer, 64, false)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML_IN_CONTAINER; - return true; - } - if (storage->isDirectory(L"Macros")) - { - bMacroEnabled = true; - } - return true; - } - - return false; -} -bool COfficeFileFormatChecker::isVbaProjectFile(POLE::Storage *storage) -{ - if (storage == NULL) - return false; - - unsigned char buffer[10]; - - POLE::Stream stream(storage, L"PROJECT"); - if (stream.read(buffer, 10) < 1) - { - return false; - } - if (false == storage->isDirectory(L"VBA")) - { - return false; - } - POLE::Stream stream2(storage, L"VBA/dir"); - if (stream2.read(buffer, 10) < 1) - { - return false; - } - return true; -} -bool COfficeFileFormatChecker::isHwpFile(POLE::Storage* storage) -{ - if (storage == NULL) - return false; - - unsigned char buffer[17]; - - POLE::Stream stream(storage, L"FileHeader"); - - static constexpr const char* hwpFormatLine = "HWP Document File"; - - if (17 == stream.read(buffer, 17) && NULL != strstr((char*)buffer, hwpFormatLine)) - return true; - - return false; -} - -bool COfficeFileFormatChecker::isXlsFormatFile(POLE::Storage *storage) -{ - if (storage == NULL) - return false; - - unsigned char buffer[10]; - - POLE::Stream stream(storage, L"Workbook"); - - if (stream.read(buffer, 10) < 1) - { - POLE::Stream stream2(storage, L"Book"); - - if (stream2.read(buffer, 10) < 1) - { - POLE::Stream stream3(storage, L"WORKBOOK"); - - if (stream3.read(buffer, 10) < 1) - { - POLE::Stream stream4(storage, L"BOOK"); - - if (stream4.read(buffer, 10) < 1) - { - POLE::Stream stream5(storage, L"book"); - - if (stream5.read(buffer, 10) < 1) - return false; - } - } - } - } - if (storage->isDirectory(L"_VBA_PROJECT_CUR")) - { - bMacroEnabled = true; - } - return true; -} -bool COfficeFileFormatChecker::isDocFlatFormatFile(unsigned char *pBuffer, int dwBytes) -{ - if (pBuffer == NULL || dwBytes < 2) - return false; - - if ((pBuffer[0] == 0xEC && pBuffer[1] == 0xA5) || (pBuffer[0] == 0xDC && pBuffer[1] == 0xA5) || (pBuffer[0] == 0xDB && pBuffer[1] == 0xA5)) - return true; - - return false; -} -bool COfficeFileFormatChecker::isXlsFlatFormatFile(unsigned char *pBuffer, int dwBytes) -{ - if (pBuffer == NULL || dwBytes < 2) - return false; - - // BOF started - if ((pBuffer[1] == 0x08 && pBuffer[0] == 0x09) || (pBuffer[1] == 0x04 && pBuffer[0] == 0x09) || (pBuffer[1] == 0x02 && pBuffer[0] == 0x09) || - (pBuffer[2] == 0x04 && pBuffer[0] == 0x09 && pBuffer[1] == 0x00 && pBuffer[3] == 0x00)) - return true; - - return false; -} -bool COfficeFileFormatChecker::isPptFormatFile(POLE::Storage *storage) -{ - if (storage == NULL) - return false; - - POLE::Stream stream(storage, L"PowerPoint Document"); - - unsigned char buffer[10]; - if (stream.read(buffer, 10) < 1) - return false; - - return true; -} - -bool COfficeFileFormatChecker::isCompoundFile(POLE::Storage* storage) -{ - if (storage == NULL) return false; - - if (storage->GetAllStreams(L"/").size() == 1) return true; - - return false; -} - -std::wstring COfficeFileFormatChecker::getDocumentID(const std::wstring &_fileName) -{ -#if defined(_WIN32) || defined(_WIN32_WCE) || defined(_WIN64) - std::wstring fileName = CorrectPathW(_fileName); -#else - std::wstring fileName = _fileName; -#endif - std::wstring documentID; - - POLE::Storage storage(fileName.c_str()); - if (storage.open()) - { - if (isMS_OFFICECRYPTOFormatFile(&storage, documentID)) - { - nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_OFFCRYPTO; - } - else if (isMS_MITCRYPTOFormatFile(&storage, documentID)) - { - nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_MITCRYPTO; - } - } - else - { - if (false == isOpenOfficeFormatFile(fileName, documentID)) - { - NSFile::CFileBinary file; - if (!file.OpenFile(fileName)) - return documentID; - - unsigned char *buffer = new unsigned char[4096]; // enaf !! - if (!buffer) - { - file.CloseFile(); - return documentID; - } - - DWORD dwReadBytes = 0; - file.ReadFile(buffer, MIN_SIZE_BUFFER, dwReadBytes); - file.CloseFile(); - - if (isPdfFormatFile(buffer, (int)dwReadBytes, documentID)) - { - nFileType = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_PDF; - } - } - } - sDocumentID = documentID; - - return documentID; -} - -bool COfficeFileFormatChecker::isMS_OFFCRYPTOFormatFile(const std::wstring &_fileName, std::wstring &documentID) -{ -#if defined(_WIN32) || defined(_WIN32_WCE) || defined(_WIN64) - std::wstring fileName = CorrectPathW(_fileName); -#else - std::wstring fileName = _fileName; -#endif - - POLE::Storage storage(fileName.c_str()); - if (storage.open()) - { - if (isMS_OFFICECRYPTOFormatFile(&storage, documentID)) - { - nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_OFFCRYPTO; - return true; - } - if (isMS_MITCRYPTOFormatFile(&storage, documentID)) - { - nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_MITCRYPTO; - return true; - } - } - return false; -} -bool COfficeFileFormatChecker::isMS_OFFICECRYPTOFormatFile(POLE::Storage *storage, std::wstring &documentID) -{ - if (storage == NULL) - return false; - - documentID.clear(); - - bool result = false; - std::list entries = storage->entries(L"DataSpaces"); - if (/*false == entries.empty() && */ storage->exists(L"EncryptionInfo")) - { - result = true; - } - - if (result) - { - POLE::Stream stream(storage, L"DocumentID"); - - std::string sData; - sData.resize(stream.size()); - if (stream.read((BYTE *)sData.c_str(), stream.size()) > 0) - { - documentID = UTF8_TO_U(sData); - } - } - return result; -} - -bool COfficeFileFormatChecker::isMS_MITCRYPTOFormatFile(POLE::Storage *storage, std::wstring &documentID) -{ - if (storage == NULL) - return false; - - documentID.clear(); - - bool result = false; - std::list entries = storage->entries(L"DataSpaces"); - if (false == entries.empty() && false == storage->exists(L"EncryptionInfo") && storage->exists(L"EncryptedPackage")) - { - result = true; - } - - return result; -} -bool COfficeFileFormatChecker::isVbaProjectFile(const std::wstring &_fileName) -{ -#if defined(_WIN32) || defined(_WIN32_WCE) || defined(_WIN64) - std::wstring fileName = CorrectPathW(_fileName); -#else - std::wstring fileName = _fileName; -#endif - POLE::Storage storage(fileName.c_str()); - if (storage.open()) - { - if (isVbaProjectFile(&storage)) - { - nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_VBAPROJECT; - return true; - } - } - return false; -} -bool COfficeFileFormatChecker::isOfficeFile(const std::wstring &_fileName) -{ -#if defined(_WIN32) || defined(_WIN32_WCE) || defined(_WIN64) - std::wstring fileName = CorrectPathW(_fileName); -#else - std::wstring fileName = _fileName; -#endif - + + std::string xml_string((char *)pBuffer, dwBytes); + + if ((std::string::npos != xml_string.find(contentTypeFormatLine1)) && (std::string::npos != xml_string.find(contentTypeFormatLine2))) + { + return true; + } + return false; +} + +bool COfficeFileFormatChecker::iXmlFile(const std::wstring &fileName) +{ + XmlUtils::CXmlLiteReader oReader; + if (!oReader.FromFile(fileName)) + return false; + if (!oReader.ReadNextNode()) + return false; + + std::wstring name = oReader.GetNameNoNS(); + + if (name == L"svg") + { + nFileType = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_SVG; + } + else if (name == L"html") + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML; + } + else + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_XML; + } + + return true; +} + +bool COfficeFileFormatChecker::isHtmlFormatFile(unsigned char *pBuffer, int dwBytes, bool testCloseTag) +{ + if (pBuffer == NULL || dwBytes < 4) + return false; + + bool tagOpen = false; + + if (testCloseTag && dwBytes > 5) + { + for (int i = 0; i < dwBytes - 6; i++) + { + if ((0x3C == pBuffer[i]) && (0x2F == pBuffer[i + 1]) && (0x48 == pBuffer[i + 2] || 0x68 == pBuffer[i + 2]) && (0x54 == pBuffer[i + 3] || 0x74 == pBuffer[i + 3]) && + (0x4d == pBuffer[i + 4] || 0x6d == pBuffer[i + 4]) && (0x4c == pBuffer[i + 5] || 0x6c == pBuffer[i + 5])) + { + return true; + } + else if ( + (0x3C == pBuffer[i]) && (0x2F == pBuffer[i + 1]) && (0x62 == pBuffer[i + 2]) && (0x6f == pBuffer[i + 3]) && (0x64 == pBuffer[i + 4]) && (0x79 == pBuffer[i + 5]) && + (0x3e == pBuffer[i + 6])) + { // + return true; + } + } + } + else if (dwBytes > 3) + { + for (int i = 0; i < dwBytes - 4 && i < 100; i++) + { + if (0x3C == pBuffer[i]) + tagOpen = true; + else if (0x3E == pBuffer[i]) + tagOpen = false; + else if ( + tagOpen && (0x48 == pBuffer[i] || 0x68 == pBuffer[i]) && (0x54 == pBuffer[i + 1] || 0x74 == pBuffer[i + 1]) && (0x4d == pBuffer[i + 2] || 0x6d == pBuffer[i + 2]) && + (0x4c == pBuffer[i + 3] || 0x6c == pBuffer[i + 3])) + { + return true; + } + } + } + return false; +} + +bool COfficeFileFormatChecker::isBinaryDoctFormatFile(unsigned char *pBuffer, int dwBytes) +{ + if (pBuffer == NULL) + return false; + + if ((4 <= dwBytes) && ('D' == pBuffer[0] && 'O' == pBuffer[1] && 'C' == pBuffer[2] && 'Y' == pBuffer[3])) + return true; + + return false; +} +bool COfficeFileFormatChecker::isBinaryXlstFormatFile(unsigned char *pBuffer, int dwBytes) +{ + if (pBuffer == NULL) + return false; + + if ((4 <= dwBytes) && ('X' == pBuffer[0] && 'L' == pBuffer[1] && 'S' == pBuffer[2] && 'Y' == pBuffer[3])) + return true; + + return false; +} +bool COfficeFileFormatChecker::isBinaryPpttFormatFile(unsigned char *pBuffer, int dwBytes) +{ + if (pBuffer == NULL) + return false; + + if ((4 <= dwBytes) && ('P' == pBuffer[0] && 'P' == pBuffer[1] && 'T' == pBuffer[2] && 'Y' == pBuffer[3])) + return true; + + return false; +} +bool COfficeFileFormatChecker::isBinaryVsdtFormatFile(unsigned char* pBuffer, int dwBytes) +{ + if (pBuffer == NULL) + return false; + + if ((4 <= dwBytes) && ('V' == pBuffer[0] && 'S' == pBuffer[1] && 'D' == pBuffer[2] && 'Y' == pBuffer[3])) + return true; + + return false; +} +bool COfficeFileFormatChecker::isPdfFormatFile(unsigned char *pBuffer, int dwBytes, std::wstring &documentID) +{ + if (pBuffer == NULL) + return false; + + documentID.clear(); + + if (dwBytes < 5 || (pBuffer[0] == 'P' && pBuffer[1] == 'K')) + return false; + + pBuffer[dwBytes - 1] = '\0'; + + char *pFirst = strstr((char *)pBuffer, "%PDF-"); + + if (NULL == pFirst) + { + char* pData = (char*)pBuffer; + for (int i = 0; i < dwBytes - 5; ++i) + { + int nPDF = strncmp(&pData[i], "%PDF-", 5); + if (!nPDF) + { + pFirst = (char*)pBuffer + i; + break; + } + } + if (NULL == pFirst) + { + //skip special + _UINT16 sz = pBuffer[0] + (pBuffer[1] << 8); + if (sz < dwBytes - 8) + { + pFirst = strstr((char*)(pBuffer + sz), "%PDF-"); + } + } + } + if (NULL != pFirst) + { + pFirst = strstr((char *)pBuffer, "%DocumentID "); + if (NULL != pFirst) + { + pFirst += 12; + char *pLast = strstr(pFirst, " "); + if (NULL != pLast) + { + std::string s(pFirst, pLast - pFirst); + documentID = NSFile::CUtf8Converter::GetUnicodeStringFromUTF8((BYTE*)pFirst, (LONG)(pLast - pFirst)); + } + } + return true; + } + + return false; +} +bool COfficeFileFormatChecker::isPdfOformFormatFile(unsigned char *pBuffer, int dwBytes) +{ + pBuffer[dwBytes - 1] = 0; + char* pFirst = strstr((char*)pBuffer, "%\315\312\322\251\015"); + + if (!pFirst || pFirst - (char*)pBuffer + 6 >= dwBytes) + return false; + + pFirst += 6; + + if (strncmp(pFirst, "1 0 obj\012<<\012", 11) != 0 || pFirst - (char*)pBuffer + 11 >= dwBytes) + return false; + + pFirst += 11; + + char* pStream = strstr(pFirst, "stream\015\012"); + char* pMeta = strstr(pFirst, g_format_oform_pdf_meta_tag); + if (!pStream || !pMeta || pStream < pMeta) + return false; + + pMeta += strlen(g_format_oform_pdf_meta_tag) + 3; + + char* pMetaLast = strstr(pMeta, " "); + if (!pMetaLast) + return false; + + pMeta = pMetaLast + 1; + pMetaLast = strstr(pMeta, " "); + if (!pMetaLast) + return false; + + return true; +} +bool COfficeFileFormatChecker::isOleObjectFile(POLE::Storage *storage) +{ + if (storage == NULL) + return false; + + POLE::Stream streamOle(storage, L"Ole"); + + if (false == streamOle.fail()) + { + std::string UserType, ClipboardFormat, Program; + + POLE::Stream streamCompObject(storage, L"CompObj"); + if (false == streamCompObject.fail() && streamCompObject.size() >= 28) + { + streamCompObject.seek(28); // skip Header + + unsigned int sz_obj = (unsigned int)(streamCompObject.size() - streamCompObject.tell()); + + if (sz_obj > 4) + { + UserType = ReadStringFromOle(&streamCompObject, sz_obj); + + sz_obj = (unsigned int)(streamCompObject.size() - streamCompObject.tell()); + if (sz_obj > 4) + ClipboardFormat = ReadStringFromOle(&streamCompObject, sz_obj); + + sz_obj = (unsigned int)(streamCompObject.size() - streamCompObject.tell()); + if (sz_obj > 4) + Program = ReadStringFromOle(&streamCompObject, sz_obj); + } + POLE::Stream streamPackage(storage, L"Package"); + if (false == streamPackage.fail()) + { + nFileType = AVS_OFFICESTUDIO_FILE_OTHER_PACKAGE_IN_OLE; + } + else if (std::string::npos != Program.find("Excel") || std::string::npos != UserType.find("Excel")) + { + if (isXlsFormatFile(storage)) + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLS; + } + } + else if (std::string::npos != Program.find("Word") || std::string::npos != UserType.find("Word")) + { + if (isDocFormatFile(storage)) + { + //nFileType inside + } + } + else if (std::string::npos != Program.find("PowerPoint") || std::string::npos != UserType.find("PowerPoint")) + { + if (isPptFormatFile(storage)) + { + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPT; + } + } + + return true; + } + else + { + POLE::Stream streamLinkInfo(storage, L"LinkInfo"); + if (false == streamLinkInfo.fail()) + { + short cch = 0; + if (2 == streamLinkInfo.read((BYTE *)&cch, 2)) + { + unsigned char *str = new unsigned char[cch]; + cch = (short)streamLinkInfo.read(str, cch); + + ClipboardFormat = std::string((char *)str, cch); + RELEASEARRAYOBJECTS(str); + + streamLinkInfo.seek(streamLinkInfo.tell() + 6); + // skip ... + } + return true; + } + } + } + return false; +} +bool COfficeFileFormatChecker::isDocFormatFile(POLE::Storage *storage) +{ + if (storage == NULL) + return false; + + POLE::Stream stream(storage, L"WordDocument"); + + unsigned char buffer[64]; + memset(buffer, 0, 64); + + if (stream.read(buffer, 64) > 0) + { + // ms office 2007 encrypted contains stream WordDocument !! + std::list entries = storage->entries(L"DataSpaces"); + if (entries.size() > 0) + return false; + + if ((buffer[0] == 0xEC && buffer[1] == 0xA5) || // word 1997-2003 + (buffer[0] == 0xDC && buffer[1] == 0xA5) || // word 1995 + (buffer[0] == 0xDB && buffer[1] == 0xA5)) // word 2.0 + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOC; + } + else if (isHtmlFormatFile(buffer, 64, false)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML_IN_CONTAINER; + return true; + } + if (storage->isDirectory(L"Macros")) + { + bMacroEnabled = true; + } + return true; + } + + return false; +} +bool COfficeFileFormatChecker::isVbaProjectFile(POLE::Storage *storage) +{ + if (storage == NULL) + return false; + + unsigned char buffer[10]; + + POLE::Stream stream(storage, L"PROJECT"); + if (stream.read(buffer, 10) < 1) + { + return false; + } + if (false == storage->isDirectory(L"VBA")) + { + return false; + } + POLE::Stream stream2(storage, L"VBA/dir"); + if (stream2.read(buffer, 10) < 1) + { + return false; + } + return true; +} +bool COfficeFileFormatChecker::isHwpFile(POLE::Storage* storage) +{ + if (storage == NULL) + return false; + + unsigned char buffer[17]; + + POLE::Stream stream(storage, L"FileHeader"); + + static constexpr const char* hwpFormatLine = "HWP Document File"; + + if (17 == stream.read(buffer, 17) && NULL != strstr((char*)buffer, hwpFormatLine)) + return true; + + return false; +} + +bool COfficeFileFormatChecker::isXlsFormatFile(POLE::Storage *storage) +{ + if (storage == NULL) + return false; + + unsigned char buffer[10]; + + POLE::Stream stream(storage, L"Workbook"); + + if (stream.read(buffer, 10) < 1) + { + POLE::Stream stream2(storage, L"Book"); + + if (stream2.read(buffer, 10) < 1) + { + POLE::Stream stream3(storage, L"WORKBOOK"); + + if (stream3.read(buffer, 10) < 1) + { + POLE::Stream stream4(storage, L"BOOK"); + + if (stream4.read(buffer, 10) < 1) + { + POLE::Stream stream5(storage, L"book"); + + if (stream5.read(buffer, 10) < 1) + return false; + } + } + } + } + if (storage->isDirectory(L"_VBA_PROJECT_CUR")) + { + bMacroEnabled = true; + } + return true; +} +bool COfficeFileFormatChecker::isDocFlatFormatFile(unsigned char *pBuffer, int dwBytes) +{ + if (pBuffer == NULL || dwBytes < 2) + return false; + + if ((pBuffer[0] == 0xEC && pBuffer[1] == 0xA5) || (pBuffer[0] == 0xDC && pBuffer[1] == 0xA5) || (pBuffer[0] == 0xDB && pBuffer[1] == 0xA5)) + return true; + + return false; +} +bool COfficeFileFormatChecker::isXlsFlatFormatFile(unsigned char *pBuffer, int dwBytes) +{ + if (pBuffer == NULL || dwBytes < 2) + return false; + + // BOF started + if ((pBuffer[1] == 0x08 && pBuffer[0] == 0x09) || (pBuffer[1] == 0x04 && pBuffer[0] == 0x09) || (pBuffer[1] == 0x02 && pBuffer[0] == 0x09) || + (pBuffer[2] == 0x04 && pBuffer[0] == 0x09 && pBuffer[1] == 0x00 && pBuffer[3] == 0x00)) + return true; + + return false; +} +bool COfficeFileFormatChecker::isPptFormatFile(POLE::Storage *storage) +{ + if (storage == NULL) + return false; + + POLE::Stream stream(storage, L"PowerPoint Document"); + + unsigned char buffer[10]; + if (stream.read(buffer, 10) < 1) + return false; + + return true; +} + +bool COfficeFileFormatChecker::isCompoundFile(POLE::Storage* storage) +{ + if (storage == NULL) return false; + + if (storage->GetAllStreams(L"/").size() == 1) return true; + + return false; +} + +std::wstring COfficeFileFormatChecker::getDocumentID(const std::wstring &_fileName) +{ +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(_WIN64) + std::wstring fileName = CorrectPathW(_fileName); +#else + std::wstring fileName = _fileName; +#endif + std::wstring documentID; + + POLE::Storage storage(fileName.c_str()); + if (storage.open()) + { + if (isMS_OFFICECRYPTOFormatFile(&storage, documentID)) + { + nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_OFFCRYPTO; + } + else if (isMS_MITCRYPTOFormatFile(&storage, documentID)) + { + nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_MITCRYPTO; + } + } + else + { + if (false == isOpenOfficeFormatFile(fileName, documentID)) + { + NSFile::CFileBinary file; + if (!file.OpenFile(fileName)) + return documentID; + + unsigned char *buffer = new unsigned char[4096]; // enaf !! + if (!buffer) + { + file.CloseFile(); + return documentID; + } + + DWORD dwReadBytes = 0; + file.ReadFile(buffer, MIN_SIZE_BUFFER, dwReadBytes); + file.CloseFile(); + + if (isPdfFormatFile(buffer, (int)dwReadBytes, documentID)) + { + nFileType = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_PDF; + } + } + } + sDocumentID = documentID; + + return documentID; +} + +bool COfficeFileFormatChecker::isMS_OFFCRYPTOFormatFile(const std::wstring &_fileName, std::wstring &documentID) +{ +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(_WIN64) + std::wstring fileName = CorrectPathW(_fileName); +#else + std::wstring fileName = _fileName; +#endif + + POLE::Storage storage(fileName.c_str()); + if (storage.open()) + { + if (isMS_OFFICECRYPTOFormatFile(&storage, documentID)) + { + nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_OFFCRYPTO; + return true; + } + if (isMS_MITCRYPTOFormatFile(&storage, documentID)) + { + nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_MITCRYPTO; + return true; + } + } + return false; +} +bool COfficeFileFormatChecker::isMS_OFFICECRYPTOFormatFile(POLE::Storage *storage, std::wstring &documentID) +{ + if (storage == NULL) + return false; + + documentID.clear(); + + bool result = false; + std::list entries = storage->entries(L"DataSpaces"); + if (/*false == entries.empty() && */ storage->exists(L"EncryptionInfo")) + { + result = true; + } + + if (result) + { + POLE::Stream stream(storage, L"DocumentID"); + + std::string sData; + sData.resize(stream.size()); + if (stream.read((BYTE *)sData.c_str(), stream.size()) > 0) + { + documentID = UTF8_TO_U(sData); + } + } + return result; +} + +bool COfficeFileFormatChecker::isMS_MITCRYPTOFormatFile(POLE::Storage *storage, std::wstring &documentID) +{ + if (storage == NULL) + return false; + + documentID.clear(); + + bool result = false; + std::list entries = storage->entries(L"DataSpaces"); + if (false == entries.empty() && false == storage->exists(L"EncryptionInfo") && storage->exists(L"EncryptedPackage")) + { + result = true; + } + + return result; +} +bool COfficeFileFormatChecker::isVbaProjectFile(const std::wstring &_fileName) +{ +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(_WIN64) + std::wstring fileName = CorrectPathW(_fileName); +#else + std::wstring fileName = _fileName; +#endif + POLE::Storage storage(fileName.c_str()); + if (storage.open()) + { + if (isVbaProjectFile(&storage)) + { + nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_VBAPROJECT; + return true; + } + } + return false; +} +bool COfficeFileFormatChecker::isOfficeFile(const std::wstring &_fileName) +{ +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(_WIN64) + std::wstring fileName = CorrectPathW(_fileName); +#else + std::wstring fileName = _fileName; +#endif + // priority turns out to be important - // Metamorphic Manual for windows 28415.doc - POLE::Storage storage(fileName.c_str()); - if (storage.open()) - { - if (isOleObjectFile(&storage)) - { - if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) - return true; - return false; - } - else if (isDocFormatFile(&storage)) - { + // Metamorphic Manual for windows 28415.doc + POLE::Storage storage(fileName.c_str()); + if (storage.open()) + { + if (isOleObjectFile(&storage)) + { + if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) + return true; + return false; + } + else if (isDocFormatFile(&storage)) + { // nFileType inside - return true; - } - else if (isXlsFormatFile(&storage)) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLS; - return true; - } - else if (isPptFormatFile(&storage)) - { - // #if defined FILE_FORMAT_CHECKER_WITH_MACRO - // COfficePPTFile pptFile; - // - // bMacroEnabled = true; - // long nResult = pptFile.OpenFile(fileName, L"", bMacroEnabled); - // if (nResult != S_OK) - // { - // return false; - // } - // pptFile.CloseFile(); - // #endif - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPT; - return true; - } - else if (isMS_OFFICECRYPTOFormatFile(&storage, sDocumentID)) - { - nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_OFFCRYPTO; - return true; - } - else if (isMS_MITCRYPTOFormatFile(&storage, sDocumentID)) - { - nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_MITCRYPTO; - return true; - } - else if (isVbaProjectFile(&storage)) - { - nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_VBAPROJECT; - return true; - } - else if (isCompoundFile(&storage)) - { - nFileType = AVS_OFFICESTUDIO_FILE_OTHER_COMPOUND; - return true; - } - else if (isHwpFile(&storage)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWP; - return true; - } - } - NSFile::CFileBinary file; - if (!file.OpenFile(fileName)) - return false; - - unsigned char* bufferDetect = new unsigned char[4096]; // enaf !! - if (!bufferDetect) - { - file.CloseFile(); - return false; - } - - DWORD dwDetectdBytes = 0; - file.ReadFile(bufferDetect, MIN_SIZE_BUFFER, dwDetectdBytes); - - COfficeUtils OfficeUtils(NULL); - if (OfficeUtils.IsArchive(fileName) == S_OK && (false == isPdfFormatFile(bufferDetect, dwDetectdBytes, sDocumentID))) - { - if (isOOXFormatFile(fileName)) - { - if (bufferDetect) - delete[] bufferDetect; - bufferDetect = NULL; - return true; - } - else if (isOpenOfficeFormatFile(fileName, sDocumentID)) - { - if (bufferDetect) - delete[] bufferDetect; - bufferDetect = NULL; - return true; - } - else if (isOnlyOfficeFormatFile(fileName)) - { - if (bufferDetect) - delete[] bufferDetect; - bufferDetect = NULL; - return true; - } - else if (isXpsFile(fileName)) - { - if (bufferDetect) - delete[] bufferDetect; - bufferDetect = NULL; - return true; - } - else if (isOFDFile(fileName)) - { - if (bufferDetect) - delete[] bufferDetect; - bufferDetect = NULL; - return true; - } - else if (isMacFormatFile(fileName)) - { - if (bufferDetect) - delete[] bufferDetect; - bufferDetect = NULL; - return true; - } - else if (isHwpxFile(fileName)) - { - if (bufferDetect) - delete[] bufferDetect; - bufferDetect = NULL; - return true; - } - } - - //----------------------------------------------------------------------------------------------- - // others - - bool bEmptyFile = false; - { - int sizeRead = (int)dwDetectdBytes; - - bEmptyFile = (dwDetectdBytes < 1); - - if (isBinaryDoctFormatFile(bufferDetect, sizeRead)) // min size - 4 - { - nFileType = AVS_OFFICESTUDIO_FILE_CANVAS_WORD; - } - else if (isBinaryXlstFormatFile(bufferDetect, sizeRead)) // min size - 4 - { - nFileType = AVS_OFFICESTUDIO_FILE_CANVAS_SPREADSHEET; - } - else if (isBinaryPpttFormatFile(bufferDetect, sizeRead)) // min size - 4 - { - nFileType = AVS_OFFICESTUDIO_FILE_CANVAS_PRESENTATION; - } - else if (isBinaryVsdtFormatFile(bufferDetect, sizeRead)) // min size - 4 - { - nFileType = AVS_OFFICESTUDIO_FILE_CANVAS_DRAW; - } - else if (isOOXFlatFormatFile(bufferDetect, sizeRead)) - { - // nFileType; - } - else if (isRtfFormatFile(bufferDetect, sizeRead)) // min size - 5 - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_RTF; - } - else if (isPdfFormatFile(bufferDetect, sizeRead, sDocumentID)) // min size - 5 - { - nFileType = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_PDF; - - if (isPdfOformFormatFile(bufferDetect, sizeRead)) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_OFORM_PDF; - } - else if (isDjvuFormatFile(bufferDetect, sizeRead)) // min size - 8 - { - nFileType = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_DJVU; - } - else if (isHtmlFormatFile(bufferDetect, sizeRead, false)) // min size - 4 - { - long fileSize = file.GetFileSize(); - if (fileSize > MIN_SIZE_BUFFER) - { - file.SeekFile(fileSize - MIN_SIZE_BUFFER); - file.ReadFile(bufferDetect, MIN_SIZE_BUFFER, dwDetectdBytes); - } - if (isHtmlFormatFile(bufferDetect, sizeRead, true)) // min size - 6 - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML; - } - } - else if (isFB2FormatFile(bufferDetect, sizeRead)) // min size - 11 - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_FB2; - } - else if (isOpenOfficeFlatFormatFile(bufferDetect, sizeRead)) // min size - 78 - { - // nFileType - } - else if (isDocFlatFormatFile(bufferDetect, sizeRead)) // min size - 2 - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOC_FLAT; // without compaund container - } - else if (isXlsFlatFormatFile(bufferDetect, sizeRead)) // min size - 2 - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLS; // without compaund container - } - else if (isMultiPartsHtmlFormatFile(bufferDetect, sizeRead)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_MHT; - } - else if (isHwpmlFile(bufferDetect, sizeRead)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWPML; - } - //------------------------------------------------------------------------------------------------ - file.CloseFile(); - } - if (bufferDetect) - delete[] bufferDetect; - bufferDetect = NULL; - - if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) - return true; - //------------------------------------------------------------------------------------------------ - if (iXmlFile(fileName)) - { + return true; + } + else if (isXlsFormatFile(&storage)) + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLS; + return true; + } + else if (isPptFormatFile(&storage)) + { + // #if defined FILE_FORMAT_CHECKER_WITH_MACRO + // COfficePPTFile pptFile; + // + // bMacroEnabled = true; + // long nResult = pptFile.OpenFile(fileName, L"", bMacroEnabled); + // if (nResult != S_OK) + // { + // return false; + // } + // pptFile.CloseFile(); + // #endif + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPT; + return true; + } + else if (isMS_OFFICECRYPTOFormatFile(&storage, sDocumentID)) + { + nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_OFFCRYPTO; + return true; + } + else if (isMS_MITCRYPTOFormatFile(&storage, sDocumentID)) + { + nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_MITCRYPTO; + return true; + } + else if (isVbaProjectFile(&storage)) + { + nFileType = AVS_OFFICESTUDIO_FILE_OTHER_MS_VBAPROJECT; + return true; + } + else if (isCompoundFile(&storage)) + { + nFileType = AVS_OFFICESTUDIO_FILE_OTHER_COMPOUND; + return true; + } + else if (isHwpFile(&storage)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWP; + return true; + } + } + NSFile::CFileBinary file; + if (!file.OpenFile(fileName)) + return false; + + unsigned char* bufferDetect = new unsigned char[4096]; // enaf !! + if (!bufferDetect) + { + file.CloseFile(); + return false; + } + + DWORD dwDetectdBytes = 0; + file.ReadFile(bufferDetect, MIN_SIZE_BUFFER, dwDetectdBytes); + + COfficeUtils OfficeUtils(NULL); + if (OfficeUtils.IsArchive(fileName) == S_OK && (false == isPdfFormatFile(bufferDetect, dwDetectdBytes, sDocumentID))) + { + if (isOOXFormatFile(fileName)) + { + if (bufferDetect) + delete[] bufferDetect; + bufferDetect = NULL; + return true; + } + else if (isOpenOfficeFormatFile(fileName, sDocumentID)) + { + if (bufferDetect) + delete[] bufferDetect; + bufferDetect = NULL; + return true; + } + else if (isOnlyOfficeFormatFile(fileName)) + { + if (bufferDetect) + delete[] bufferDetect; + bufferDetect = NULL; + return true; + } + else if (isXpsFile(fileName)) + { + if (bufferDetect) + delete[] bufferDetect; + bufferDetect = NULL; + return true; + } + else if (isOFDFile(fileName)) + { + if (bufferDetect) + delete[] bufferDetect; + bufferDetect = NULL; + return true; + } + else if (isMacFormatFile(fileName)) + { + if (bufferDetect) + delete[] bufferDetect; + bufferDetect = NULL; + return true; + } + else if (isHwpxFile(fileName)) + { + if (bufferDetect) + delete[] bufferDetect; + bufferDetect = NULL; + return true; + } + } + + //----------------------------------------------------------------------------------------------- + // others + + bool bEmptyFile = false; + { + int sizeRead = (int)dwDetectdBytes; + + bEmptyFile = (dwDetectdBytes < 1); + + if (isBinaryDoctFormatFile(bufferDetect, sizeRead)) // min size - 4 + { + nFileType = AVS_OFFICESTUDIO_FILE_CANVAS_WORD; + } + else if (isBinaryXlstFormatFile(bufferDetect, sizeRead)) // min size - 4 + { + nFileType = AVS_OFFICESTUDIO_FILE_CANVAS_SPREADSHEET; + } + else if (isBinaryPpttFormatFile(bufferDetect, sizeRead)) // min size - 4 + { + nFileType = AVS_OFFICESTUDIO_FILE_CANVAS_PRESENTATION; + } + else if (isBinaryVsdtFormatFile(bufferDetect, sizeRead)) // min size - 4 + { + nFileType = AVS_OFFICESTUDIO_FILE_CANVAS_DRAW; + } + else if (isOOXFlatFormatFile(bufferDetect, sizeRead)) + { + // nFileType; + } + else if (isRtfFormatFile(bufferDetect, sizeRead)) // min size - 5 + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_RTF; + } + else if (isPdfFormatFile(bufferDetect, sizeRead, sDocumentID)) // min size - 5 + { + nFileType = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_PDF; + + if (isPdfOformFormatFile(bufferDetect, sizeRead)) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_OFORM_PDF; + } + else if (isDjvuFormatFile(bufferDetect, sizeRead)) // min size - 8 + { + nFileType = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_DJVU; + } + else if (isHtmlFormatFile(bufferDetect, sizeRead, false)) // min size - 4 + { + long fileSize = file.GetFileSize(); + if (fileSize > MIN_SIZE_BUFFER) + { + file.SeekFile(fileSize - MIN_SIZE_BUFFER); + file.ReadFile(bufferDetect, MIN_SIZE_BUFFER, dwDetectdBytes); + } + if (isHtmlFormatFile(bufferDetect, sizeRead, true)) // min size - 6 + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML; + } + } + else if (isFB2FormatFile(bufferDetect, sizeRead)) // min size - 11 + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_FB2; + } + else if (isOpenOfficeFlatFormatFile(bufferDetect, sizeRead)) // min size - 78 + { + // nFileType + } + else if (isDocFlatFormatFile(bufferDetect, sizeRead)) // min size - 2 + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOC_FLAT; // without compaund container + } + else if (isXlsFlatFormatFile(bufferDetect, sizeRead)) // min size - 2 + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLS; // without compaund container + } + else if (isMultiPartsHtmlFormatFile(bufferDetect, sizeRead)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_MHT; + } + else if (isHwpmlFile(bufferDetect, sizeRead)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWPML; + } + //------------------------------------------------------------------------------------------------ + file.CloseFile(); + } + if (bufferDetect) + delete[] bufferDetect; + bufferDetect = NULL; + + if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) + return true; + //------------------------------------------------------------------------------------------------ + if (iXmlFile(fileName)) + { // nFileType inside - return true; - } - //// by Extension - - std::wstring::size_type nExtPos = fileName.rfind(L'.'); - std::wstring sExt = L"unknown"; - - if (nExtPos != std::wstring::npos) - sExt = fileName.substr(nExtPos); - - std::transform(sExt.begin(), sExt.end(), sExt.begin(), tolower); - - if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) - return true; - - if (bEmptyFile) - { - if (0 == sExt.compare(L".xlsx")) - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSX; - else if (0 == sExt.compare(L".docx")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCX; - else if (0 == sExt.compare(L".pptx")) - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPTX; - else if (0 == sExt.compare(L".vsxd")) - nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSDX; - else if (0 == sExt.compare(L".ods")) - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_ODS; - else if (0 == sExt.compare(L".odt")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_ODT; - else if (0 == sExt.compare(L".odp")) - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_ODP; - } - else if (0 == sExt.compare(L".mht") || 0 == sExt.compare(L".mhtml")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_MHT; - else if (0 == sExt.compare(L".md")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_MD; - else if (0 == sExt.compare(L".tsv")) - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_TSV; - else if (0 == sExt.compare(L".scsv")) - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_SCSV; - else if (0 == sExt.compare(L".csv") || 0 == sExt.compare(L".tsv") || 0 == sExt.compare(L".dsv") || 0 == sExt.compare(L".cssv") - || 0 == sExt.compare(L".xls") || 0 == sExt.compare(L".xlsx") || 0 == sExt.compare(L".xlsb")) - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_CSV; - else if (0 == sExt.compare(L".html") || 0 == sExt.compare(L".htm")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML; - else if (0 == sExt.compare(L".bin")) // base64 string - nFileType = AVS_OFFICESTUDIO_FILE_CANVAS_PDF; + return true; + } + //// by Extension + + std::wstring::size_type nExtPos = fileName.rfind(L'.'); + std::wstring sExt = L"unknown"; + + if (nExtPos != std::wstring::npos) + sExt = fileName.substr(nExtPos); + + std::transform(sExt.begin(), sExt.end(), sExt.begin(), tolower); + + if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) + return true; + + if (bEmptyFile) + { + if (0 == sExt.compare(L".xlsx")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSX; + else if (0 == sExt.compare(L".docx")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCX; + else if (0 == sExt.compare(L".pptx")) + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPTX; + else if (0 == sExt.compare(L".vsxd")) + nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSDX; + else if (0 == sExt.compare(L".ods")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_ODS; + else if (0 == sExt.compare(L".odt")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_ODT; + else if (0 == sExt.compare(L".odp")) + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_ODP; + } + else if (0 == sExt.compare(L".mht") || 0 == sExt.compare(L".mhtml")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_MHT; + else if (0 == sExt.compare(L".md")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_MD; + else if (0 == sExt.compare(L".tsv")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_TSV; + else if (0 == sExt.compare(L".scsv")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_SCSV; + else if (0 == sExt.compare(L".csv") || 0 == sExt.compare(L".tsv") || 0 == sExt.compare(L".dsv") || 0 == sExt.compare(L".cssv") + || 0 == sExt.compare(L".xls") || 0 == sExt.compare(L".xlsx") || 0 == sExt.compare(L".xlsb")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_CSV; + else if (0 == sExt.compare(L".html") || 0 == sExt.compare(L".htm")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML; + else if (0 == sExt.compare(L".bin")) // base64 string + nFileType = AVS_OFFICESTUDIO_FILE_CANVAS_PDF; else if (0 == sExt.compare(L".doct")) // case of archive with html viewer - nFileType = AVS_OFFICESTUDIO_FILE_TEAMLAB_DOCY; - else if (0 == sExt.compare(L".txt") || 0 == sExt.compare(L".xml") || 0 == sExt.compare(L".rtf") || 0 == sExt.compare(L".doc") || 0 == sExt.compare(L".docx")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_TXT; - else if (0 == sExt.compare(L".pages")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_PAGES; - else if (0 == sExt.compare(L".numbers")) - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_NUMBERS; - else if (0 == sExt.compare(L".key")) - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_KEY; - else if (0 == sExt.compare(L".hwp")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWP; - else if (0 == sExt.compare(L".hwpx")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWPX; - else if (0 == sExt.compare(L".hml")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWPML; - - if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) - return true; - - return false; -} -bool COfficeFileFormatChecker::isOOXFormatFile(const std::wstring &fileName, bool unpacked) -{ - COfficeUtils OfficeUtils(NULL); - - ULONG nBufferSize = 0; - BYTE *pBuffer = NULL; - - HRESULT hresult = S_FALSE; - - if (unpacked) - { - if (NSFile::CFileBinary::ReadAllBytes(fileName + FILE_SEPARATOR_STR + L"[Content_Types].xml", &pBuffer, nBufferSize)) - hresult = S_OK; - } - else - { - hresult = OfficeUtils.LoadFileFromArchive(fileName, L"[Content_Types].xml", &pBuffer, nBufferSize); - } - if (hresult == S_OK && pBuffer != NULL) - { - - const char *docxFormatLine = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"; - const char *dotxFormatLine = "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml"; - const char *docmFormatLine = "application/vnd.ms-word.document.macroEnabled.main+xml"; - const char *dotmFormatLine = "application/vnd.ms-word.template.macroEnabledTemplate.main+xml"; - const char *oformFormatLine = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.oform"; - const char *docxfFormatLine = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxf"; - - const char *xlsxFormatLine = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"; - const char *xltxFormatLine = "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml"; - const char *xlsmFormatLine = "application/vnd.ms-excel.sheet.macroEnabled.main+xml"; - const char *xltmFormatLine = "application/vnd.ms-excel.template.macroEnabled.main+xml"; - const char *xlsbFormatLine = "application/vnd.ms-excel.sheet.binary.macroEnabled.main"; - - const char *pptxFormatLine = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"; - const char *ppsxFormatLine = "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml"; - const char *potxFormatLine = "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml"; - const char *pptmFormatLine = "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml"; - const char *ppsmFormatLine = "application/vnd.ms-powerpoint.slideshow.macroEnabled.main+xml"; - const char *potmFormatLine = "application/vnd.ms-powerpoint.template.macroEnabled.main+xml"; - - const char *vsdxFormatLine = "application/vnd.ms-visio.drawing.main+xml"; - const char *vssxFormatLine = "application/vnd.ms-visio.stencil.main+xml"; - const char *vstxFormatLine = "application/vnd.ms-visio.template.main+xml"; - const char *vsdmFormatLine = "application/vnd.ms-visio.drawing.macroEnabled.main+xml"; - const char *vssmFormatLine = "application/vnd.ms-visio.stencil.macroEnabled.main+xml"; - const char *vstmFormatLine = "application/vnd.ms-visio.template.macroEnabled.main+xml"; - - std::string strContentTypes((char*)pBuffer, nBufferSize); - - if (std::string::npos != strContentTypes.find(oformFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_OFORM; - } - else if (std::string::npos != strContentTypes.find(docxfFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCXF; - } - else if (std::string::npos != strContentTypes.find(docxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCX; - } - else if (std::string::npos != strContentTypes.find(docmFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCM; - bMacroEnabled = true; - } - else if (std::string::npos != strContentTypes.find(dotxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOTX; - } - else if (std::string::npos != strContentTypes.find(dotmFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOTM; - bMacroEnabled = true; - } - else if (std::string::npos != strContentTypes.find(xlsxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSX; - } - else if (std::string::npos != strContentTypes.find(xlsmFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSM; - bMacroEnabled = true; - } - else if (std::string::npos != strContentTypes.find(xltxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLTX; - } - else if (std::string::npos != strContentTypes.find(xltmFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLTM; - bMacroEnabled = true; - } - else if (std::string::npos != strContentTypes.find(xlsbFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSB; - bMacroEnabled = true; - } - else if (std::string::npos != strContentTypes.find(pptxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPTX; - } - else if (std::string::npos != strContentTypes.find(pptmFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPTM; - bMacroEnabled = true; - } - else if (std::string::npos != strContentTypes.find(ppsmFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPSM; - bMacroEnabled = true; - } - else if (std::string::npos != strContentTypes.find(ppsxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPSX; - } - else if (std::string::npos != strContentTypes.find(potxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_POTX; - } - else if (std::string::npos != strContentTypes.find(potmFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_POTM; - bMacroEnabled = true; - } - else if (std::string::npos != strContentTypes.find(vsdxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSDX; - } - else if (std::string::npos != strContentTypes.find(vssxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSSX; - } - else if (std::string::npos != strContentTypes.find(vstxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSTX; - } - else if (std::string::npos != strContentTypes.find(vsdmFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSDM; - bMacroEnabled = true; - } - else if (std::string::npos != strContentTypes.find(vssmFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSSM; - bMacroEnabled = true; - } - else if (std::string::npos != strContentTypes.find(vstmFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSTM; - bMacroEnabled = true; - } - delete []pBuffer; - pBuffer = NULL; - - if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) - return true; - } - return false; -} -bool COfficeFileFormatChecker::isDocFormatFile(const std::wstring &fileName) -{ - POLE::Storage storage(fileName.c_str()); - if (storage.open()) - { - if (isDocFormatFile(&storage)) - { + nFileType = AVS_OFFICESTUDIO_FILE_TEAMLAB_DOCY; + else if (0 == sExt.compare(L".txt") || 0 == sExt.compare(L".xml") || 0 == sExt.compare(L".rtf") || 0 == sExt.compare(L".doc") || 0 == sExt.compare(L".docx")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_TXT; + else if (0 == sExt.compare(L".pages")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_PAGES; + else if (0 == sExt.compare(L".numbers")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_NUMBERS; + else if (0 == sExt.compare(L".key")) + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_KEY; + else if (0 == sExt.compare(L".hwp")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWP; + else if (0 == sExt.compare(L".hwpx")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWPX; + else if (0 == sExt.compare(L".hml")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWPML; + else if (0 == sExt.compare(L".sqlite") || 0 == sExt.compare(L".sqlite3") || 0 == sExt.compare(L".db") || 0 == sExt.compare(L".db3")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_SQLITE; + else if (0 == sExt.compare(L".duckdb")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_DUCKDB; + else if (0 == sExt.compare(L".parquet") || 0 == sExt.compare(L".pq")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_PARQUET; + else if (0 == sExt.compare(L".mdb") || 0 == sExt.compare(L".accdb")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_MDB; + else if (0 == sExt.compare(L".fdb")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_FDB; + + if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) + return true; + + return false; +} +bool COfficeFileFormatChecker::isOOXFormatFile(const std::wstring &fileName, bool unpacked) +{ + COfficeUtils OfficeUtils(NULL); + + ULONG nBufferSize = 0; + BYTE *pBuffer = NULL; + + HRESULT hresult = S_FALSE; + + if (unpacked) + { + if (NSFile::CFileBinary::ReadAllBytes(fileName + FILE_SEPARATOR_STR + L"[Content_Types].xml", &pBuffer, nBufferSize)) + hresult = S_OK; + } + else + { + hresult = OfficeUtils.LoadFileFromArchive(fileName, L"[Content_Types].xml", &pBuffer, nBufferSize); + } + if (hresult == S_OK && pBuffer != NULL) + { + + const char *docxFormatLine = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"; + const char *dotxFormatLine = "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml"; + const char *docmFormatLine = "application/vnd.ms-word.document.macroEnabled.main+xml"; + const char *dotmFormatLine = "application/vnd.ms-word.template.macroEnabledTemplate.main+xml"; + const char *oformFormatLine = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.oform"; + const char *docxfFormatLine = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxf"; + + const char *xlsxFormatLine = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"; + const char *xltxFormatLine = "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml"; + const char *xlsmFormatLine = "application/vnd.ms-excel.sheet.macroEnabled.main+xml"; + const char *xltmFormatLine = "application/vnd.ms-excel.template.macroEnabled.main+xml"; + const char *xlsbFormatLine = "application/vnd.ms-excel.sheet.binary.macroEnabled.main"; + + const char *pptxFormatLine = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"; + const char *ppsxFormatLine = "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml"; + const char *potxFormatLine = "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml"; + const char *pptmFormatLine = "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml"; + const char *ppsmFormatLine = "application/vnd.ms-powerpoint.slideshow.macroEnabled.main+xml"; + const char *potmFormatLine = "application/vnd.ms-powerpoint.template.macroEnabled.main+xml"; + + const char *vsdxFormatLine = "application/vnd.ms-visio.drawing.main+xml"; + const char *vssxFormatLine = "application/vnd.ms-visio.stencil.main+xml"; + const char *vstxFormatLine = "application/vnd.ms-visio.template.main+xml"; + const char *vsdmFormatLine = "application/vnd.ms-visio.drawing.macroEnabled.main+xml"; + const char *vssmFormatLine = "application/vnd.ms-visio.stencil.macroEnabled.main+xml"; + const char *vstmFormatLine = "application/vnd.ms-visio.template.macroEnabled.main+xml"; + + std::string strContentTypes((char*)pBuffer, nBufferSize); + + if (std::string::npos != strContentTypes.find(oformFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_OFORM; + } + else if (std::string::npos != strContentTypes.find(docxfFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCXF; + } + else if (std::string::npos != strContentTypes.find(docxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCX; + } + else if (std::string::npos != strContentTypes.find(docmFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCM; + bMacroEnabled = true; + } + else if (std::string::npos != strContentTypes.find(dotxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOTX; + } + else if (std::string::npos != strContentTypes.find(dotmFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_DOTM; + bMacroEnabled = true; + } + else if (std::string::npos != strContentTypes.find(xlsxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSX; + } + else if (std::string::npos != strContentTypes.find(xlsmFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSM; + bMacroEnabled = true; + } + else if (std::string::npos != strContentTypes.find(xltxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLTX; + } + else if (std::string::npos != strContentTypes.find(xltmFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLTM; + bMacroEnabled = true; + } + else if (std::string::npos != strContentTypes.find(xlsbFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSB; + bMacroEnabled = true; + } + else if (std::string::npos != strContentTypes.find(pptxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPTX; + } + else if (std::string::npos != strContentTypes.find(pptmFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPTM; + bMacroEnabled = true; + } + else if (std::string::npos != strContentTypes.find(ppsmFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPSM; + bMacroEnabled = true; + } + else if (std::string::npos != strContentTypes.find(ppsxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_PPSX; + } + else if (std::string::npos != strContentTypes.find(potxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_POTX; + } + else if (std::string::npos != strContentTypes.find(potmFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_POTM; + bMacroEnabled = true; + } + else if (std::string::npos != strContentTypes.find(vsdxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSDX; + } + else if (std::string::npos != strContentTypes.find(vssxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSSX; + } + else if (std::string::npos != strContentTypes.find(vstxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSTX; + } + else if (std::string::npos != strContentTypes.find(vsdmFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSDM; + bMacroEnabled = true; + } + else if (std::string::npos != strContentTypes.find(vssmFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSSM; + bMacroEnabled = true; + } + else if (std::string::npos != strContentTypes.find(vstmFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DRAW_VSTM; + bMacroEnabled = true; + } + delete []pBuffer; + pBuffer = NULL; + + if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) + return true; + } + return false; +} +bool COfficeFileFormatChecker::isDocFormatFile(const std::wstring &fileName) +{ + POLE::Storage storage(fileName.c_str()); + if (storage.open()) + { + if (isDocFormatFile(&storage)) + { // nFileType inside; - return true; - } - } - return false; -} -bool COfficeFileFormatChecker::isXlsFormatFile(const std::wstring &fileName) -{ - POLE::Storage storage(fileName.c_str()); - if (storage.open()) - { - if (isXlsFormatFile(&storage)) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLS; - return true; - } - } - return false; -} -bool COfficeFileFormatChecker::isOnlyOfficeFormatFile(const std::wstring &fileName) -{ - COfficeUtils OfficeUtils(NULL); - + return true; + } + } + return false; +} +bool COfficeFileFormatChecker::isXlsFormatFile(const std::wstring &fileName) +{ + POLE::Storage storage(fileName.c_str()); + if (storage.open()) + { + if (isXlsFormatFile(&storage)) + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLS; + return true; + } + } + return false; +} +bool COfficeFileFormatChecker::isOnlyOfficeFormatFile(const std::wstring &fileName) +{ + COfficeUtils OfficeUtils(NULL); + ULONG nBufferSize = 128; // limit reading from the binary - BYTE *pBuffer = NULL; - - HRESULT hresult = OfficeUtils.LoadFileFromArchive(fileName, L"Editor.bin", &pBuffer, nBufferSize); - if (hresult == S_OK && pBuffer != NULL) - { - if (isBinaryDoctFormatFile(pBuffer, nBufferSize)) - { - nFileType = AVS_OFFICESTUDIO_FILE_TEAMLAB_DOCY; - } - else if (isBinaryXlstFormatFile(pBuffer, nBufferSize)) - { - nFileType = AVS_OFFICESTUDIO_FILE_TEAMLAB_XLSY; - } - else if (isBinaryPpttFormatFile(pBuffer, nBufferSize)) - { - nFileType = AVS_OFFICESTUDIO_FILE_TEAMLAB_PPTY; - } - else if (isBinaryVsdtFormatFile(pBuffer, nBufferSize)) - { - nFileType = AVS_OFFICESTUDIO_FILE_TEAMLAB_VSDY; - } - - delete[] pBuffer; - pBuffer = NULL; - - if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) - return true; - } - return false; -} - -struct TIWAField -{ - size_t m_unStart; - size_t m_unEnd; - unsigned m_uIndex; - unsigned m_unWireType; - uint64_t m_oValue; -}; - -bool ReadUVar(BYTE* pBuffer, size_t unEndPos, size_t& unPos, uint64_t& unValue) -{ - std::vector arBytes; - arBytes.reserve(8); - - unValue = 0; - - bool bNext = true; - while (unPos < unEndPos && bNext) - { - const unsigned char c = pBuffer[unPos++]; - arBytes.push_back((unsigned char)(c & ~0x80)); - bNext = c & 0x80; - } - - if (bNext && unPos == unEndPos) - return false; - - for (std::vector::const_reverse_iterator it = arBytes.rbegin(); it != arBytes.rend(); ++it) - { - if (std::numeric_limits::max() >> 7 < unValue || - std::numeric_limits::max() - (unValue << 7) < *it) // overflow - return false; - - unValue = (unValue << 7) + *it; - } - - return true; -} - -bool ReadIWAField(BYTE* pBuffer, size_t unEndPos, size_t& unPos, TIWAField& oIWAField) -{ - if (NULL == pBuffer || unPos + 2 > unEndPos) - return false; - - unsigned uSpec; - - uSpec = (unsigned)pBuffer[unPos++]; - oIWAField.m_unWireType = uSpec & 0x7; - - oIWAField.m_unStart = unPos; - - switch (oIWAField.m_unWireType) - { - case 0: - { - if (!ReadUVar(pBuffer, unEndPos, unPos, oIWAField.m_oValue)) - return false; - - break; - } - case 1: - { - unPos += 4; - break; - } - case 2: - { - uint64_t unLen; - if (!ReadUVar(pBuffer, unEndPos, unPos, unLen) || unPos + unLen > unEndPos) - return false; - - oIWAField.m_unStart = unPos; - unPos += unLen; - break; - } - case 5: - { - unPos += 2; - break; - } - default: - return false; - } - - oIWAField.m_unEnd = unPos; - oIWAField.m_uIndex = uSpec >> 3; - - return true; -} - -bool DetectIWorkFormat(const std::wstring& fileName, int &nType) -{ - COfficeUtils OfficeUtils(NULL); - - ULONG unSize = 0; - BYTE* pBuffer = NULL; - - HRESULT hresult = OfficeUtils.LoadFileFromArchive(fileName, L"Index/Document.iwa", &pBuffer, unSize); - - if (hresult != S_OK || NULL == pBuffer) - return false; - - #define CLEAR_BUFFER_AND_RETURN(return_value)\ - do{\ - delete[] pBuffer;\ - return return_value;\ - }while(false) - - if (unSize < 13) - CLEAR_BUFFER_AND_RETURN(false); - - size_t uPos = 6; - - for (; uPos < 12; ++uPos) - { - if (0x08 == pBuffer[uPos] && 0x01 == pBuffer[uPos + 1]) - { - --uPos; - break; - } - } - - if (12 == uPos) - CLEAR_BUFFER_AND_RETURN(false); - - uint64_t unHeaderLen; - if (!ReadUVar(pBuffer, unSize, uPos, unHeaderLen)) - CLEAR_BUFFER_AND_RETURN(false); - - const size_t uStartPos = uPos; - - if (unHeaderLen < 8 || unSize < unHeaderLen + uStartPos) - CLEAR_BUFFER_AND_RETURN(false); - - uPos += 2; - - TIWAField oMessageField; - - if (!ReadIWAField(pBuffer, uStartPos + unHeaderLen, uPos, oMessageField) || 2 != oMessageField.m_unWireType || - 2 != oMessageField.m_uIndex) - CLEAR_BUFFER_AND_RETURN(false); - - size_t uSubPos = oMessageField.m_unStart; - TIWAField oField; - - if (!ReadIWAField(pBuffer, oMessageField.m_unEnd, uSubPos, oField) || 0 != oField.m_unWireType || - 1 != oField.m_uIndex) - CLEAR_BUFFER_AND_RETURN(false); - - switch (oField.m_oValue) - { - case 1: - { - uint32_t unDataLen = 0; - - TIWAField oTempField; - if (ReadIWAField(pBuffer, oMessageField.m_unEnd, uSubPos, oTempField) && - ReadIWAField(pBuffer, oMessageField.m_unEnd, uSubPos, oTempField) && 0 == oTempField.m_unWireType && - 3 == oTempField.m_uIndex) - unDataLen += oTempField.m_oValue; - - size_t unTempPos = uStartPos + unHeaderLen; - - // keynote: presentation ref in 2 - // number: sheet ref in 1 - if (ReadIWAField(pBuffer, uStartPos + unDataLen, unTempPos, oTempField) && - (2 != oTempField.m_unWireType || 1 != oTempField.m_uIndex || oTempField.m_unEnd - oTempField.m_unStart < 2)) - { - nType = AVS_OFFICESTUDIO_FILE_PRESENTATION_KEY; - CLEAR_BUFFER_AND_RETURN(true); - } - else if (ReadIWAField(pBuffer, uStartPos + unDataLen, unTempPos, oTempField) && - (2 != oTempField.m_unWireType || 2 != oTempField.m_uIndex || oTempField.m_unEnd - oTempField.m_unStart < 2)) - { - nType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_NUMBERS; - CLEAR_BUFFER_AND_RETURN(true); - } - - break; - } - case 10000: - { - nType = AVS_OFFICESTUDIO_FILE_DOCUMENT_PAGES; - CLEAR_BUFFER_AND_RETURN(true); - } - } - - CLEAR_BUFFER_AND_RETURN(false); -} - -bool COfficeFileFormatChecker::isMacFormatFile(const std::wstring& fileName) -{ - if (DetectIWorkFormat(fileName, nFileType)) - return true; - - std::wstring::size_type nExtPos = fileName.rfind(L'.'); - std::wstring sExt = L"unknown"; - - if (nExtPos != std::wstring::npos) - sExt = fileName.substr(nExtPos); - - std::transform(sExt.begin(), sExt.end(), sExt.begin(), tolower); - - if (0 == sExt.compare(L".pages")) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_PAGES; - else if (0 == sExt.compare(L".numbers")) - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_NUMBERS; - else if (0 == sExt.compare(L".key")) - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_KEY; - else - return false; - - return true; -} - -bool COfficeFileFormatChecker::isHwpxFile(const std::wstring &fileName) -{ - COfficeUtils oOfficeUtils; - - ULONG unSize = 0; - BYTE* pBuffer = NULL; - - HRESULT hresult = oOfficeUtils.LoadFileFromArchive(fileName, L"mimetype", &pBuffer, unSize); - - if (hresult != S_OK || NULL == pBuffer) - return false; - - static constexpr const char* hwpxFormatLine = "application/hwp+zip"; - bool bResult = false; - - if (19 <= unSize && NULL != strstr((char *)pBuffer, hwpxFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWPX; - bResult = true; - } - - delete[] pBuffer; - return bResult; -} - -bool COfficeFileFormatChecker::isOpenOfficeFormatFile(const std::wstring &fileName, std::wstring &documentID) -{ - documentID.clear(); - - const char *odtFormatLine = "application/vnd.oasis.opendocument.text"; - const char *odsFormatLine = "application/vnd.oasis.opendocument.spreadsheet"; - const char *odpFormatLine = "application/vnd.oasis.opendocument.presentation"; - const char* odgFormatLine = "application/vnd.oasis.opendocument.graphics"; - const char *ottFormatLine = "application/vnd.oasis.opendocument.text-template"; - const char *otsFormatLine = "application/vnd.oasis.opendocument.spreadsheet-template"; - const char *otpFormatLine = "application/vnd.oasis.opendocument.presentation-template"; - const char *epubFormatLine = "application/epub+zip"; - const char *sxwFormatLine = "application/vnd.sun.xml.writer"; - const char *sxcFormatLine = "application/vnd.sun.xml.calc"; - const char *sxiFormatLine = "application/vnd.sun.xml.impress"; - - COfficeUtils OfficeUtils(NULL); - - ULONG nBufferSize = 0; - BYTE *pBuffer = NULL; - - HRESULT hresult = OfficeUtils.LoadFileFromArchive(fileName, L"documentID", &pBuffer, nBufferSize); - if (hresult == S_OK && pBuffer != NULL) - { - documentID = NSFile::CUtf8Converter::GetUnicodeStringFromUTF8(pBuffer, nBufferSize); - - delete[] pBuffer; - pBuffer = NULL; - } - - nBufferSize = 0; - hresult = OfficeUtils.LoadFileFromArchive(fileName, L"mimetype", &pBuffer, nBufferSize); - if (hresult == S_OK && pBuffer != NULL) - { - if (48 <= nBufferSize && NULL != strstr((char *)pBuffer, ottFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_OTT; - } - else if (55 <= nBufferSize && NULL != strstr((char *)pBuffer, otsFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_OTS; - } - else if (56 <= nBufferSize && NULL != strstr((char *)pBuffer, otpFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_OTP; - } - else if ((39 <= nBufferSize && NULL != strstr((char *)pBuffer, odtFormatLine)) || - (30 <= nBufferSize && NULL != strstr((char *)pBuffer, sxwFormatLine))) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_ODT; - } - else if ((46 <= nBufferSize && NULL != strstr((char *)pBuffer, odsFormatLine)) || - (28 <= nBufferSize && NULL != strstr((char *)pBuffer, sxcFormatLine))) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_ODS; - } - else if ((47 <= nBufferSize && NULL != strstr((char *)pBuffer, odpFormatLine)) || - (31 <= nBufferSize && NULL != strstr((char *)pBuffer, sxiFormatLine))) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_ODP; - } - else if (43 <= nBufferSize && NULL != strstr((char*)pBuffer, odgFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_ODG; - } - else if (20 <= nBufferSize && NULL != strstr((char *)pBuffer, epubFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_EPUB; - } - - delete[] pBuffer; - pBuffer = NULL; - - if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) - return true; - } - else - { + BYTE *pBuffer = NULL; + + HRESULT hresult = OfficeUtils.LoadFileFromArchive(fileName, L"Editor.bin", &pBuffer, nBufferSize); + if (hresult == S_OK && pBuffer != NULL) + { + if (isBinaryDoctFormatFile(pBuffer, nBufferSize)) + { + nFileType = AVS_OFFICESTUDIO_FILE_TEAMLAB_DOCY; + } + else if (isBinaryXlstFormatFile(pBuffer, nBufferSize)) + { + nFileType = AVS_OFFICESTUDIO_FILE_TEAMLAB_XLSY; + } + else if (isBinaryPpttFormatFile(pBuffer, nBufferSize)) + { + nFileType = AVS_OFFICESTUDIO_FILE_TEAMLAB_PPTY; + } + else if (isBinaryVsdtFormatFile(pBuffer, nBufferSize)) + { + nFileType = AVS_OFFICESTUDIO_FILE_TEAMLAB_VSDY; + } + + delete[] pBuffer; + pBuffer = NULL; + + if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) + return true; + } + return false; +} + +struct TIWAField +{ + size_t m_unStart; + size_t m_unEnd; + unsigned m_uIndex; + unsigned m_unWireType; + uint64_t m_oValue; +}; + +bool ReadUVar(BYTE* pBuffer, size_t unEndPos, size_t& unPos, uint64_t& unValue) +{ + std::vector arBytes; + arBytes.reserve(8); + + unValue = 0; + + bool bNext = true; + while (unPos < unEndPos && bNext) + { + const unsigned char c = pBuffer[unPos++]; + arBytes.push_back((unsigned char)(c & ~0x80)); + bNext = c & 0x80; + } + + if (bNext && unPos == unEndPos) + return false; + + for (std::vector::const_reverse_iterator it = arBytes.rbegin(); it != arBytes.rend(); ++it) + { + if (std::numeric_limits::max() >> 7 < unValue || + std::numeric_limits::max() - (unValue << 7) < *it) // overflow + return false; + + unValue = (unValue << 7) + *it; + } + + return true; +} + +bool ReadIWAField(BYTE* pBuffer, size_t unEndPos, size_t& unPos, TIWAField& oIWAField) +{ + if (NULL == pBuffer || unPos + 2 > unEndPos) + return false; + + unsigned uSpec; + + uSpec = (unsigned)pBuffer[unPos++]; + oIWAField.m_unWireType = uSpec & 0x7; + + oIWAField.m_unStart = unPos; + + switch (oIWAField.m_unWireType) + { + case 0: + { + if (!ReadUVar(pBuffer, unEndPos, unPos, oIWAField.m_oValue)) + return false; + + break; + } + case 1: + { + unPos += 4; + break; + } + case 2: + { + uint64_t unLen; + if (!ReadUVar(pBuffer, unEndPos, unPos, unLen) || unPos + unLen > unEndPos) + return false; + + oIWAField.m_unStart = unPos; + unPos += unLen; + break; + } + case 5: + { + unPos += 2; + break; + } + default: + return false; + } + + oIWAField.m_unEnd = unPos; + oIWAField.m_uIndex = uSpec >> 3; + + return true; +} + +bool DetectIWorkFormat(const std::wstring& fileName, int &nType) +{ + COfficeUtils OfficeUtils(NULL); + + ULONG unSize = 0; + BYTE* pBuffer = NULL; + + HRESULT hresult = OfficeUtils.LoadFileFromArchive(fileName, L"Index/Document.iwa", &pBuffer, unSize); + + if (hresult != S_OK || NULL == pBuffer) + return false; + + #define CLEAR_BUFFER_AND_RETURN(return_value)\ + do{\ + delete[] pBuffer;\ + return return_value;\ + }while(false) + + if (unSize < 13) + CLEAR_BUFFER_AND_RETURN(false); + + size_t uPos = 6; + + for (; uPos < 12; ++uPos) + { + if (0x08 == pBuffer[uPos] && 0x01 == pBuffer[uPos + 1]) + { + --uPos; + break; + } + } + + if (12 == uPos) + CLEAR_BUFFER_AND_RETURN(false); + + uint64_t unHeaderLen; + if (!ReadUVar(pBuffer, unSize, uPos, unHeaderLen)) + CLEAR_BUFFER_AND_RETURN(false); + + const size_t uStartPos = uPos; + + if (unHeaderLen < 8 || unSize < unHeaderLen + uStartPos) + CLEAR_BUFFER_AND_RETURN(false); + + uPos += 2; + + TIWAField oMessageField; + + if (!ReadIWAField(pBuffer, uStartPos + unHeaderLen, uPos, oMessageField) || 2 != oMessageField.m_unWireType || + 2 != oMessageField.m_uIndex) + CLEAR_BUFFER_AND_RETURN(false); + + size_t uSubPos = oMessageField.m_unStart; + TIWAField oField; + + if (!ReadIWAField(pBuffer, oMessageField.m_unEnd, uSubPos, oField) || 0 != oField.m_unWireType || + 1 != oField.m_uIndex) + CLEAR_BUFFER_AND_RETURN(false); + + switch (oField.m_oValue) + { + case 1: + { + uint32_t unDataLen = 0; + + TIWAField oTempField; + if (ReadIWAField(pBuffer, oMessageField.m_unEnd, uSubPos, oTempField) && + ReadIWAField(pBuffer, oMessageField.m_unEnd, uSubPos, oTempField) && 0 == oTempField.m_unWireType && + 3 == oTempField.m_uIndex) + unDataLen += oTempField.m_oValue; + + size_t unTempPos = uStartPos + unHeaderLen; + + // keynote: presentation ref in 2 + // number: sheet ref in 1 + if (ReadIWAField(pBuffer, uStartPos + unDataLen, unTempPos, oTempField) && + (2 != oTempField.m_unWireType || 1 != oTempField.m_uIndex || oTempField.m_unEnd - oTempField.m_unStart < 2)) + { + nType = AVS_OFFICESTUDIO_FILE_PRESENTATION_KEY; + CLEAR_BUFFER_AND_RETURN(true); + } + else if (ReadIWAField(pBuffer, uStartPos + unDataLen, unTempPos, oTempField) && + (2 != oTempField.m_unWireType || 2 != oTempField.m_uIndex || oTempField.m_unEnd - oTempField.m_unStart < 2)) + { + nType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_NUMBERS; + CLEAR_BUFFER_AND_RETURN(true); + } + + break; + } + case 10000: + { + nType = AVS_OFFICESTUDIO_FILE_DOCUMENT_PAGES; + CLEAR_BUFFER_AND_RETURN(true); + } + } + + CLEAR_BUFFER_AND_RETURN(false); +} + +bool COfficeFileFormatChecker::isMacFormatFile(const std::wstring& fileName) +{ + if (DetectIWorkFormat(fileName, nFileType)) + return true; + + std::wstring::size_type nExtPos = fileName.rfind(L'.'); + std::wstring sExt = L"unknown"; + + if (nExtPos != std::wstring::npos) + sExt = fileName.substr(nExtPos); + + std::transform(sExt.begin(), sExt.end(), sExt.begin(), tolower); + + if (0 == sExt.compare(L".pages")) + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_PAGES; + else if (0 == sExt.compare(L".numbers")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_NUMBERS; + else if (0 == sExt.compare(L".key")) + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_KEY; + else if (0 == sExt.compare(L".sqlite") || 0 == sExt.compare(L".sqlite3") || 0 == sExt.compare(L".db") || 0 == sExt.compare(L".db3")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_SQLITE; + else if (0 == sExt.compare(L".duckdb")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_DUCKDB; + else if (0 == sExt.compare(L".parquet") || 0 == sExt.compare(L".pq")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_PARQUET; + else if (0 == sExt.compare(L".mdb") || 0 == sExt.compare(L".accdb")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_MDB; + else if (0 == sExt.compare(L".fdb")) + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_FDB; + else + return false; + + return true; +} + +bool COfficeFileFormatChecker::isHwpxFile(const std::wstring &fileName) +{ + COfficeUtils oOfficeUtils; + + ULONG unSize = 0; + BYTE* pBuffer = NULL; + + HRESULT hresult = oOfficeUtils.LoadFileFromArchive(fileName, L"mimetype", &pBuffer, unSize); + + if (hresult != S_OK || NULL == pBuffer) + return false; + + static constexpr const char* hwpxFormatLine = "application/hwp+zip"; + bool bResult = false; + + if (19 <= unSize && NULL != strstr((char *)pBuffer, hwpxFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_HWPX; + bResult = true; + } + + delete[] pBuffer; + return bResult; +} + +bool COfficeFileFormatChecker::isOpenOfficeFormatFile(const std::wstring &fileName, std::wstring &documentID) +{ + documentID.clear(); + + const char *odtFormatLine = "application/vnd.oasis.opendocument.text"; + const char *odsFormatLine = "application/vnd.oasis.opendocument.spreadsheet"; + const char *odpFormatLine = "application/vnd.oasis.opendocument.presentation"; + const char* odgFormatLine = "application/vnd.oasis.opendocument.graphics"; + const char *ottFormatLine = "application/vnd.oasis.opendocument.text-template"; + const char *otsFormatLine = "application/vnd.oasis.opendocument.spreadsheet-template"; + const char *otpFormatLine = "application/vnd.oasis.opendocument.presentation-template"; + const char *epubFormatLine = "application/epub+zip"; + const char *sxwFormatLine = "application/vnd.sun.xml.writer"; + const char *sxcFormatLine = "application/vnd.sun.xml.calc"; + const char *sxiFormatLine = "application/vnd.sun.xml.impress"; + + COfficeUtils OfficeUtils(NULL); + + ULONG nBufferSize = 0; + BYTE *pBuffer = NULL; + + HRESULT hresult = OfficeUtils.LoadFileFromArchive(fileName, L"documentID", &pBuffer, nBufferSize); + if (hresult == S_OK && pBuffer != NULL) + { + documentID = NSFile::CUtf8Converter::GetUnicodeStringFromUTF8(pBuffer, nBufferSize); + + delete[] pBuffer; + pBuffer = NULL; + } + + nBufferSize = 0; + hresult = OfficeUtils.LoadFileFromArchive(fileName, L"mimetype", &pBuffer, nBufferSize); + if (hresult == S_OK && pBuffer != NULL) + { + if (48 <= nBufferSize && NULL != strstr((char *)pBuffer, ottFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_OTT; + } + else if (55 <= nBufferSize && NULL != strstr((char *)pBuffer, otsFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_OTS; + } + else if (56 <= nBufferSize && NULL != strstr((char *)pBuffer, otpFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_OTP; + } + else if ((39 <= nBufferSize && NULL != strstr((char *)pBuffer, odtFormatLine)) || + (30 <= nBufferSize && NULL != strstr((char *)pBuffer, sxwFormatLine))) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_ODT; + } + else if ((46 <= nBufferSize && NULL != strstr((char *)pBuffer, odsFormatLine)) || + (28 <= nBufferSize && NULL != strstr((char *)pBuffer, sxcFormatLine))) + { + nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_ODS; + } + else if ((47 <= nBufferSize && NULL != strstr((char *)pBuffer, odpFormatLine)) || + (31 <= nBufferSize && NULL != strstr((char *)pBuffer, sxiFormatLine))) + { + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_ODP; + } + else if (43 <= nBufferSize && NULL != strstr((char*)pBuffer, odgFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_ODG; + } + else if (20 <= nBufferSize && NULL != strstr((char *)pBuffer, epubFormatLine)) + { + nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_EPUB; + } + + delete[] pBuffer; + pBuffer = NULL; + + if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) + return true; + } + else + { // if the type isn't recorded, look at the manifest - nBufferSize = 0; - HRESULT hresult = OfficeUtils.LoadFileFromArchive(fileName, L"META-INF/manifest.xml", &pBuffer, nBufferSize); - if (hresult == S_OK && pBuffer != NULL) - { - std::string xml_string((char *)pBuffer, nBufferSize); - - std::string::size_type nFindMem1 = xml_string.find(odtFormatLine); - std::string::size_type nFindMem2 = xml_string.find(odsFormatLine); - std::string::size_type nFindMem3 = xml_string.find(odpFormatLine); - - if (nFindMem1 != std::string::npos && nFindMem2 != std::string::npos) - { - if (nFindMem1 < nFindMem2) - nFindMem2 = std::string::npos; - else - nFindMem1 = std::string::npos; - } - if (nFindMem1 != std::string::npos && nFindMem3 != std::string::npos) - { - if (nFindMem1 < nFindMem3) - nFindMem3 = std::string::npos; - else - nFindMem1 = std::string::npos; - } - if (nFindMem2 != std::string::npos && nFindMem3 != std::string::npos) - { - if (nFindMem2 < nFindMem3) - nFindMem3 = std::string::npos; - else - nFindMem2 = std::string::npos; - } - - if (nFindMem1 != std::string::npos) - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_ODT; - else if (nFindMem2 != std::string::npos) - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_ODS; - else if (nFindMem3 != std::string::npos) - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_ODP; - - delete[] pBuffer; - pBuffer = NULL; - - if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) - return true; - } - } - return false; -} - -bool COfficeFileFormatChecker::isOpenOfficeFlatFormatFile(unsigned char *pBuffer, int dwBytes) -{ - if (dwBytes < 78) - return false; - - const char *odfFormatLine1 = "office:document"; - const char *odfFormatLine2 = "xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\""; - - std::string xml_string((char *)pBuffer, dwBytes); - - if ((std::string::npos == xml_string.find(odfFormatLine1)) || (std::string::npos == xml_string.find(odfFormatLine2))) - { - return false; - } - - const char *odtFormatLine = "application/vnd.oasis.opendocument.text"; - const char *odsFormatLine = "application/vnd.oasis.opendocument.spreadsheet"; - const char *odpFormatLine = "application/vnd.oasis.opendocument.presentation"; - - if (std::string::npos != xml_string.find(odtFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_DOCUMENT_ODT_FLAT; - } - else if (std::string::npos != xml_string.find(odsFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_SPREADSHEET_ODS_FLAT; - } - else if (std::string::npos != xml_string.find(odpFormatLine)) - { - nFileType = AVS_OFFICESTUDIO_FILE_PRESENTATION_ODP_FLAT; - } - - if (nFileType != AVS_OFFICESTUDIO_FILE_UNKNOWN) - return true; - - return false; -} - -bool COfficeFileFormatChecker::isHwpmlFile(unsigned char *pBuffer, int dwBytes) -{ - if (NULL == pBuffer || dwBytes < 8) - return false; - - for (unsigned int unPos = 0; unPos < dwBytes - 8; ++unPos) - { - if ('<' != pBuffer[unPos]) - continue; - - if (dwBytes - unPos >= 15 && '!' == pBuffer[unPos + 1] && - 0 == memcmp(&pBuffer[unPos], "= 6 && 0 == memcmp(&pBuffer[unPos], "= 15 && '!' == pBuffer[unPos + 1] && + 0 == memcmp(&pBuffer[unPos], "= 6 && 0 == memcmp(&pBuffer[unPos], " + +namespace NExtractTools +{ + DuckDbResultSet::DuckDbResultSet(duckdb_result result) : m_result(result), m_currentRow(0) + { + m_rowCount = duckdb_row_count(&m_result); + } + + DuckDbResultSet::~DuckDbResultSet() + { + duckdb_destroy_result(&m_result); + } + + bool DuckDbResultSet::Next() + { + if (m_currentRow < m_rowCount) + { + m_currentRow++; + return true; + } + return false; + } + + std::wstring DuckDbResultSet::GetString(int columnIdx) + { + if (m_currentRow == 0 || m_currentRow > m_rowCount) return L""; + + char* val = duckdb_value_varchar(&m_result, columnIdx, m_currentRow - 1); + if (!val) return L""; + + std::string sVal(val); + duckdb_free(val); + return UTF8_TO_U(sVal); + } + + DuckDbEngine::DuckDbEngine() : m_db(nullptr), m_conn(nullptr) + { + } + + DuckDbEngine::~DuckDbEngine() + { + if (m_conn) + { + duckdb_disconnect(&m_conn); + m_conn = nullptr; + } + if (m_db) + { + duckdb_close(&m_db); + m_db = nullptr; + } + } + + bool DuckDbEngine::Open(const std::wstring& path) + { + std::string sPath = U_TO_UTF8(path); + + std::string sExt; + std::string::size_type nExtPos = sPath.rfind('.'); + if (nExtPos != std::string::npos) { + sExt = sPath.substr(nExtPos); + std::transform(sExt.begin(), sExt.end(), sExt.begin(), ::tolower); + } + + if (sExt == ".parquet" || sExt == ".pq") { + if (duckdb_open(NULL, &m_db) == DuckDBError) + { + return false; + } + if (duckdb_connect(m_db, &m_conn) == DuckDBError) + { + duckdb_close(&m_db); + m_db = nullptr; + return false; + } + std::string sql = "CREATE VIEW \"ParquetData\" AS SELECT * FROM '" + sPath + "'"; + duckdb_result result; + if (duckdb_query(m_conn, sql.c_str(), &result) != DuckDBSuccess) { + const char* error = duckdb_result_error(&result); + duckdb_destroy_result(&result); + return false; + } + duckdb_destroy_result(&result); + return true; + } + + if (duckdb_open(sPath.c_str(), &m_db) == DuckDBError) + { + return false; + } + + if (duckdb_connect(m_db, &m_conn) == DuckDBError) + { + duckdb_close(&m_db); + m_db = nullptr; + return false; + } + + return true; + } + + std::vector DuckDbEngine::GetTableNames() + { + std::vector tables; + if (!m_conn) return tables; + + duckdb_result result; + if (duckdb_query(m_conn, "SELECT table_name FROM information_schema.tables WHERE table_schema='main'", &result) == DuckDBSuccess) + { + idx_t rowCount = duckdb_row_count(&result); + for (idx_t i = 0; i < rowCount; i++) + { + char* val = duckdb_value_varchar(&result, 0, i); + if (val) + { + std::string sVal(val); + tables.push_back(UTF8_TO_U(sVal)); + duckdb_free(val); + } + } + duckdb_destroy_result(&result); + } + + return tables; + } + + TableSchema DuckDbEngine::GetTableSchema(const std::wstring& tableName) + { + TableSchema schema; + if (!m_conn) return schema; + + std::string sTableName = U_TO_UTF8(tableName); + std::string sql = "PRAGMA table_info('" + sTableName + "')"; + + duckdb_result result; + if (duckdb_query(m_conn, sql.c_str(), &result) == DuckDBSuccess) + { + idx_t rowCount = duckdb_row_count(&result); + for (idx_t i = 0; i < rowCount; i++) + { + // PRAGMA table_info returns: cid, name, type, notnull, dflt_value, pk + char* val = duckdb_value_varchar(&result, 1, i); + char* pk = duckdb_value_varchar(&result, 5, i); + if (val) + { + std::string sVal(val); + std::wstring wColName = UTF8_TO_U(sVal); + schema.columns.push_back(wColName); + if (pk && std::string(pk) == "true") { // DuckDB PRAGMA table_info returns "true" or "false" for pk usually, or "1" / "0" + schema.primaryKeys.push_back(wColName); + } else if (pk && std::string(pk) == "1") { + schema.primaryKeys.push_back(wColName); + } + duckdb_free(val); + } + if (pk) duckdb_free(pk); + } + duckdb_destroy_result(&result); + } + + // DuckDB's support for PRAGMA foreign_key_list is limited or missing compared to SQLite. + // For DuckDB, we'll extract columns but might skip foreign keys if not explicitly supported + // by PRAGMA foreign_key_list in standard way. But let's try it just in case: + std::string fkSql = "PRAGMA foreign_key_list('" + sTableName + "')"; + duckdb_result fkResult; + if (duckdb_query(m_conn, fkSql.c_str(), &fkResult) == DuckDBSuccess) + { + idx_t rowCount = duckdb_row_count(&fkResult); + for (idx_t i = 0; i < rowCount; i++) + { + // id, seq, table, from, to + char* table = duckdb_value_varchar(&fkResult, 2, i); + char* from = duckdb_value_varchar(&fkResult, 3, i); + char* to = duckdb_value_varchar(&fkResult, 4, i); + + if (table && from && to) + { + ForeignKeyDef fk; + std::string sTable(table); + std::string sFrom(from); + std::string sTo(to); + + fk.referencedTable = UTF8_TO_U(sTable); + fk.columnName = UTF8_TO_U(sFrom); + fk.referencedColumn = UTF8_TO_U(sTo); + schema.foreignKeys.push_back(fk); + } + + if (table) duckdb_free(table); + if (from) duckdb_free(from); + if (to) duckdb_free(to); + } + duckdb_destroy_result(&fkResult); + } + + return schema; + } + + std::unique_ptr DuckDbEngine::QueryTable(const std::wstring& tableName) + { + if (!m_conn) return nullptr; + + std::string sTableName = U_TO_UTF8(tableName); + std::string sql = "SELECT * FROM '" + sTableName + "'"; + + duckdb_result result; + if (duckdb_query(m_conn, sql.c_str(), &result) == DuckDBSuccess) + { + return std::unique_ptr(new DuckDbResultSet(result)); + } + + return nullptr; + } +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/DuckDbEngine.h b/OOXML/Binary/Sheets/Reader/DatabaseEngines/DuckDbEngine.h new file mode 100644 index 0000000000..38e551eb2c --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/DuckDbEngine.h @@ -0,0 +1,38 @@ +#pragma once + +#include "IDatabaseEngine.h" +#include + +namespace NExtractTools +{ + class DuckDbResultSet : public IDBResultSet + { + public: + DuckDbResultSet(duckdb_result result); + virtual ~DuckDbResultSet(); + + virtual bool Next() override; + virtual std::wstring GetString(int columnIdx) override; + + private: + duckdb_result m_result; + idx_t m_currentRow; + idx_t m_rowCount; + }; + + class DuckDbEngine : public IDatabaseEngine + { + public: + DuckDbEngine(); + virtual ~DuckDbEngine(); + + virtual bool Open(const std::wstring& path) override; + virtual std::vector GetTableNames() override; + virtual TableSchema GetTableSchema(const std::wstring& tableName) override; + virtual std::unique_ptr QueryTable(const std::wstring& tableName) override; + + private: + duckdb_database m_db; + duckdb_connection m_conn; + }; +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/IDatabaseEngine.h b/OOXML/Binary/Sheets/Reader/DatabaseEngines/IDatabaseEngine.h new file mode 100644 index 0000000000..f5a77fdc17 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/IDatabaseEngine.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +class IDBResultSet +{ +public: + virtual ~IDBResultSet() {} + virtual bool Next() = 0; + virtual std::wstring GetString(int columnIdx) = 0; +}; + +struct ForeignKeyDef { + std::wstring columnName; + std::wstring referencedTable; + std::wstring referencedColumn; +}; + +struct TableSchema { + std::vector columns; + std::vector foreignKeys; + std::vector primaryKeys; +}; + +class IDatabaseEngine +{ +public: + virtual ~IDatabaseEngine() {} + virtual bool Open(const std::wstring& path) = 0; + virtual std::vector GetTableNames() = 0; + virtual TableSchema GetTableSchema(const std::wstring& tableName) = 0; + virtual std::unique_ptr QueryTable(const std::wstring& tableName) = 0; +}; diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/MdbEngine.cpp b/OOXML/Binary/Sheets/Reader/DatabaseEngines/MdbEngine.cpp new file mode 100644 index 0000000000..ca54926a82 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/MdbEngine.cpp @@ -0,0 +1,131 @@ +#include "MdbEngine.h" +#include "../../../../../DesktopEditor/common/File.h" +#include +#include + +static std::wstring utf8_to_wstring(const char* str) +{ + if (!str) return L""; + std::string s(str); + return UTF8_TO_U(s); +} + +MdbResultSet::MdbResultSet(MdbTableDef* table) : m_table(table) +{ + mdb_read_columns(m_table); + for (unsigned int i = 0; i < m_table->num_cols; ++i) { + MdbColumn *col = (MdbColumn *)g_ptr_array_index(m_table->columns, i); + col->bind_ptr = malloc(MDB_BIND_SIZE); + col->len_ptr = (int *)malloc(sizeof(int)); + memset(col->bind_ptr, 0, MDB_BIND_SIZE); + *col->len_ptr = 0; + } + mdb_rewind_table(m_table); +} + +MdbResultSet::~MdbResultSet() +{ + if (m_table) { + for (unsigned int i = 0; i < m_table->num_cols; ++i) { + MdbColumn *col = (MdbColumn *)g_ptr_array_index(m_table->columns, i); + free(col->bind_ptr); + free(col->len_ptr); + col->bind_ptr = nullptr; + col->len_ptr = nullptr; + } + mdb_free_tabledef(m_table); + } +} + +bool MdbResultSet::Next() +{ + return mdb_fetch_row(m_table) != 0; +} + +std::wstring MdbResultSet::GetString(int columnIdx) +{ + if (columnIdx < 0 || columnIdx >= (int)m_table->num_cols) return L""; + MdbColumn *col = (MdbColumn *)g_ptr_array_index(m_table->columns, columnIdx); + + if (col->col_type == MDB_OLE || col->col_type == MDB_BINARY) { + return L"[Binary Data]"; + } + + return utf8_to_wstring((const char*)col->bind_ptr); +} + +MdbEngine::MdbEngine() : m_mdb(nullptr) {} + +MdbEngine::~MdbEngine() +{ + if (m_mdb) { + mdb_close(m_mdb); + } +} + +bool MdbEngine::Open(const std::wstring& path) +{ + std::string narrowPath = U_TO_UTF8(path); + + m_mdb = mdb_open(narrowPath.c_str(), MDB_NOFLAGS); + return m_mdb != nullptr; +} + +std::vector MdbEngine::GetTableNames() +{ + std::vector result; + if (!m_mdb) return result; + + mdb_read_catalog(m_mdb, MDB_TABLE); + for (unsigned int i = 0; i < m_mdb->num_catalog; ++i) { + MdbCatalogEntry *entry = (MdbCatalogEntry *)g_ptr_array_index(m_mdb->catalog, i); + if (entry->object_type == MDB_TABLE) { + if (mdb_is_user_table(entry)) { + result.push_back(utf8_to_wstring(entry->object_name)); + } + } + } + return result; +} + +TableSchema MdbEngine::GetTableSchema(const std::wstring& tableName) +{ + TableSchema schema; + if (!m_mdb) return schema; + + std::string narrowName = U_TO_UTF8(tableName); + + MdbCatalogEntry *entry = mdb_get_catalogentry_by_name(m_mdb, (char*)narrowName.c_str()); + if (!entry) return schema; + + MdbTableDef *table = mdb_read_table(entry); + if (!table) return schema; + + mdb_read_columns(table); + for (unsigned int i = 0; i < table->num_cols; ++i) { + MdbColumn *col = (MdbColumn *)g_ptr_array_index(table->columns, i); + schema.columns.push_back(utf8_to_wstring(col->name)); + } + + // Try to get foreign keys if possible in mdbtools (simplified: none for now, wait: is there an API?) + // mdbtools doesn't expose FKs nicely, but maybe we can parse indexes. + // We will skip FK extraction for MDB for now, or mock it if needed. + + mdb_free_tabledef(table); + return schema; +} + +std::unique_ptr MdbEngine::QueryTable(const std::wstring& tableName) +{ + if (!m_mdb) return nullptr; + + std::string narrowName = U_TO_UTF8(tableName); + + MdbCatalogEntry *entry = mdb_get_catalogentry_by_name(m_mdb, (char*)narrowName.c_str()); + if (!entry) return nullptr; + + MdbTableDef *table = mdb_read_table(entry); + if (!table) return nullptr; + + return std::unique_ptr(new MdbResultSet(table)); +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/MdbEngine.h b/OOXML/Binary/Sheets/Reader/DatabaseEngines/MdbEngine.h new file mode 100644 index 0000000000..7f330cb6fb --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/MdbEngine.h @@ -0,0 +1,34 @@ +#pragma once + +#include "IDatabaseEngine.h" +#include "mdbtools/mdbtools.h" +#include +#include + +class MdbResultSet : public IDBResultSet +{ +public: + MdbResultSet(MdbTableDef* table); + virtual ~MdbResultSet(); + + bool Next() override; + std::wstring GetString(int columnIdx) override; + +private: + MdbTableDef* m_table; +}; + +class MdbEngine : public IDatabaseEngine +{ +public: + MdbEngine(); + virtual ~MdbEngine(); + + bool Open(const std::wstring& path) override; + std::vector GetTableNames() override; + TableSchema GetTableSchema(const std::wstring& tableName) override; + std::unique_ptr QueryTable(const std::wstring& tableName) override; + +private: + MdbHandle* m_mdb; +}; diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/SqliteEngine.cpp b/OOXML/Binary/Sheets/Reader/DatabaseEngines/SqliteEngine.cpp new file mode 100644 index 0000000000..198abc940b --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/SqliteEngine.cpp @@ -0,0 +1,160 @@ +#include "SqliteEngine.h" +#include "../../../../../DesktopEditor/common/File.h" +#include + +namespace NExtractTools +{ + SqliteResultSet::SqliteResultSet(sqlite3_stmt* stmt) : m_stmt(stmt) + { + } + + SqliteResultSet::~SqliteResultSet() + { + if (m_stmt) + { + sqlite3_finalize(m_stmt); + m_stmt = nullptr; + } + } + + bool SqliteResultSet::Next() + { + if (!m_stmt) return false; + return sqlite3_step(m_stmt) == SQLITE_ROW; + } + + std::wstring SqliteResultSet::GetString(int columnIdx) + { + if (!m_stmt) return L""; + + const unsigned char* val = sqlite3_column_text(m_stmt, columnIdx); + if (!val) return L""; + + std::string sVal((const char*)val); + return UTF8_TO_U(sVal); + } + + SqliteEngine::SqliteEngine() : m_db(nullptr) + { + } + + SqliteEngine::~SqliteEngine() + { + if (m_db) + { + sqlite3_close(m_db); + m_db = nullptr; + } + } + + bool SqliteEngine::Open(const std::wstring& path) + { + std::string sPath = U_TO_UTF8(path); + int rc = sqlite3_open(sPath.c_str(), &m_db); + if (rc != SQLITE_OK) + { + if (m_db) + { + sqlite3_close(m_db); + m_db = nullptr; + } + return false; + } + return true; + } + + std::vector SqliteEngine::GetTableNames() + { + std::vector tables; + if (!m_db) return tables; + + sqlite3_stmt* stmt = nullptr; + const char* sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"; + if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, nullptr) == SQLITE_OK) + { + while (sqlite3_step(stmt) == SQLITE_ROW) + { + const unsigned char* name = sqlite3_column_text(stmt, 0); + if (name) + { + std::string sName((const char*)name); + tables.push_back(UTF8_TO_U(sName)); + } + } + sqlite3_finalize(stmt); + } + return tables; + } + + TableSchema SqliteEngine::GetTableSchema(const std::wstring& tableName) + { + TableSchema schema; + if (!m_db) return schema; + + std::string sTableName = U_TO_UTF8(tableName); + std::string sql = "PRAGMA table_info('" + sTableName + "')"; + + sqlite3_stmt* stmt = nullptr; + if (sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr) == SQLITE_OK) + { + while (sqlite3_step(stmt) == SQLITE_ROW) + { + const unsigned char* colName = sqlite3_column_text(stmt, 1); + const unsigned char* pk = sqlite3_column_text(stmt, 5); + if (colName) + { + std::string sColName((const char*)colName); + std::wstring wColName = UTF8_TO_U(sColName); + schema.columns.push_back(wColName); + if (pk && std::string((const char*)pk) != "0") { + schema.primaryKeys.push_back(wColName); + } + } + } + sqlite3_finalize(stmt); + } + + std::string fkSql = "PRAGMA foreign_key_list('" + sTableName + "')"; + if (sqlite3_prepare_v2(m_db, fkSql.c_str(), -1, &stmt, nullptr) == SQLITE_OK) + { + while (sqlite3_step(stmt) == SQLITE_ROW) + { + const unsigned char* table = sqlite3_column_text(stmt, 2); + const unsigned char* from = sqlite3_column_text(stmt, 3); + const unsigned char* to = sqlite3_column_text(stmt, 4); + + if (table && from && to) + { + ForeignKeyDef fk; + std::string sTable((const char*)table); + std::string sFrom((const char*)from); + std::string sTo((const char*)to); + + fk.referencedTable = UTF8_TO_U(sTable); + fk.columnName = UTF8_TO_U(sFrom); + fk.referencedColumn = UTF8_TO_U(sTo); + schema.foreignKeys.push_back(fk); + } + } + sqlite3_finalize(stmt); + } + + return schema; + } + + std::unique_ptr SqliteEngine::QueryTable(const std::wstring& tableName) + { + if (!m_db) return nullptr; + + std::string sTableName = U_TO_UTF8(tableName); + std::string sql = "SELECT * FROM '" + sTableName + "'"; + + sqlite3_stmt* stmt = nullptr; + if (sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr) == SQLITE_OK) + { + return std::unique_ptr(new SqliteResultSet(stmt)); + } + + return nullptr; + } +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/SqliteEngine.h b/OOXML/Binary/Sheets/Reader/DatabaseEngines/SqliteEngine.h new file mode 100644 index 0000000000..36e189cf56 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/SqliteEngine.h @@ -0,0 +1,35 @@ +#pragma once + +#include "IDatabaseEngine.h" +#include + +namespace NExtractTools +{ + class SqliteResultSet : public IDBResultSet + { + public: + SqliteResultSet(sqlite3_stmt* stmt); + virtual ~SqliteResultSet(); + + virtual bool Next() override; + virtual std::wstring GetString(int columnIdx) override; + + private: + sqlite3_stmt* m_stmt; + }; + + class SqliteEngine : public IDatabaseEngine + { + public: + SqliteEngine(); + virtual ~SqliteEngine(); + + virtual bool Open(const std::wstring& path) override; + virtual std::vector GetTableNames() override; + virtual TableSchema GetTableSchema(const std::wstring& tableName) override; + virtual std::unique_ptr QueryTable(const std::wstring& tableName) override; + + private: + sqlite3* m_db; + }; +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/COPYING.LIB b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/COPYING.LIB new file mode 100644 index 0000000000..5bc8fb2c8f --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/backend.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/backend.c new file mode 100644 index 0000000000..0f2432bcaf --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/backend.c @@ -0,0 +1,1219 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000-2011 Brian Bruns and others + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* +** functions to deal with different backend database engines +*/ + +#include "mdbtools.h" +#include "mdbprivate.h" + +/* Access data types */ +static const MdbBackendType mdb_access_types[] = { + [MDB_BOOL] = { .name = "Boolean" }, + [MDB_BYTE] = { .name = "Byte" }, + [MDB_INT] = { .name = "Integer" }, + [MDB_LONGINT] = { .name = "Long Integer" }, + [MDB_MONEY] = { .name = "Currency" }, + [MDB_FLOAT] = { .name = "Single" }, + [MDB_DOUBLE] = { .name = "Double" }, + [MDB_DATETIME] = { .name = "DateTime" }, + [MDB_BINARY] = { .name = "Binary" }, + [MDB_TEXT] = { .name = "Text", .needs_char_length = 1 }, + [MDB_OLE] = { .name = "OLE", .needs_byte_length = 1 }, + [MDB_MEMO] = { .name = "Memo/Hyperlink", .needs_char_length = 1 }, + [MDB_REPID] = { .name = "Replication ID" }, + [MDB_NUMERIC] = { .name = "Numeric", .needs_precision = 1, .needs_scale = 1 }, +}; + +/* Oracle data types */ +static const MdbBackendType mdb_oracle_types[] = { + [MDB_BOOL] = { .name = "NUMBER(1)" }, + [MDB_BYTE] = { .name = "NUMBER(3)" }, + [MDB_INT] = { .name = "NUMBER(5)" }, + [MDB_LONGINT] = { .name = "NUMBER(11)" }, + [MDB_MONEY] = { .name = "NUMBER(15,2)" }, + [MDB_FLOAT] = { .name = "FLOAT" }, + [MDB_DOUBLE] = { .name = "FLOAT" }, + [MDB_DATETIME] = { .name = "TIMESTAMP" }, + [MDB_BINARY] = { .name = "BINARY" }, + [MDB_TEXT] = { .name = "VARCHAR2", .needs_char_length= 1 }, + [MDB_OLE] = { .name = "BLOB" }, + [MDB_MEMO] = { .name = "CLOB" }, + [MDB_REPID] = { .name = "NUMBER", .needs_precision = 1 }, + [MDB_NUMERIC] = { .name = "NUMBER", .needs_precision = 1 }, +}; +static const MdbBackendType mdb_oracle_shortdate_type = + { .name = "DATE" }; + +/* Sybase/MSSQL data types */ +static const MdbBackendType mdb_sybase_types[] = { + [MDB_BOOL] = { .name = "bit" }, + [MDB_BYTE] = { .name = "char", .needs_byte_length = 1 }, + [MDB_INT] = { .name = "smallint" }, + [MDB_LONGINT] = { .name = "int" }, + [MDB_MONEY] = { .name = "money" }, + [MDB_FLOAT] = { .name = "real" }, + [MDB_DOUBLE] = { .name = "float" }, + [MDB_DATETIME] = { .name = "smalldatetime" }, + [MDB_BINARY] = { .name = "varbinary", .needs_byte_length = 1 }, + [MDB_TEXT] = { .name = "nvarchar", .needs_char_length = 1 }, + [MDB_OLE] = { .name = "varbinary(max)" }, + [MDB_MEMO] = { .name = "nvarchar(max)" }, + [MDB_REPID] = { .name = "Sybase_Replication ID" }, + [MDB_NUMERIC] = { .name = "numeric", .needs_precision = 1, .needs_scale = 1 }, +}; +static const MdbBackendType mdb_sybase_shortdate_type = + { .name = "DATE" }; + +/* Postgres data types */ +static const MdbBackendType mdb_postgres_types[] = { + [MDB_BOOL] = { .name = "BOOLEAN" }, + [MDB_BYTE] = { .name = "SMALLINT" }, + [MDB_INT] = { .name = "INTEGER" }, + [MDB_LONGINT] = { .name = "INTEGER" }, /* bigint */ + [MDB_MONEY] = { .name = "NUMERIC(15,2)" }, /* money deprecated ? */ + [MDB_FLOAT] = { .name = "REAL" }, + [MDB_DOUBLE] = { .name = "DOUBLE PRECISION" }, + [MDB_DATETIME] = { .name = "TIMESTAMP WITHOUT TIME ZONE" }, + [MDB_BINARY] = { .name = "BYTEA" }, + [MDB_TEXT] = { .name = "VARCHAR", .needs_char_length = 1 }, + [MDB_OLE] = { .name = "BYTEA" }, + [MDB_MEMO] = { .name = "TEXT" }, + [MDB_REPID] = { .name = "UUID" }, + [MDB_NUMERIC] = { .name = "NUMERIC", .needs_precision = 1, .needs_scale = 1 }, +}; +static const MdbBackendType mdb_postgres_shortdate_type = + { .name = "DATE" }; +static const MdbBackendType mdb_postgres_serial_type = + { .name = "SERIAL" }; + +/* MySQL data types */ +static const MdbBackendType mdb_mysql_types[] = { + [MDB_BOOL] = { .name = "boolean" }, + [MDB_BYTE] = { .name = "tinyint" }, + [MDB_INT] = { .name = "smallint" }, + [MDB_LONGINT] = { .name = "int" }, + [MDB_MONEY] = { .name = "float" }, + [MDB_FLOAT] = { .name = "float" }, + [MDB_DOUBLE] = { .name = "double" }, + [MDB_DATETIME] = { .name = "datetime" }, + [MDB_BINARY] = { .name = "blob" }, + [MDB_TEXT] = { .name = "varchar", .needs_char_length = 1 }, + [MDB_OLE] = { .name = "blob" }, + [MDB_MEMO] = { .name = "text" }, + [MDB_REPID] = { .name = "char(38)" }, + [MDB_NUMERIC] = { .name = "numeric", .needs_precision = 1, .needs_scale = 1 }, +}; +static const MdbBackendType mdb_mysql_shortdate_type = + { .name = "date" }; +/* We can't use the MySQL SERIAL type because that uses a bigint which + * is 64 bits wide, whereas MDB long ints are 32 bits */ +static const MdbBackendType mdb_mysql_serial_type = + { .name = "int not null auto_increment unique" }; + +/* sqlite data types */ +static const MdbBackendType mdb_sqlite_types[] = { + [MDB_BOOL] = { .name = "INTEGER" }, + [MDB_BYTE] = { .name = "INTEGER" }, + [MDB_INT] = { .name = "INTEGER" }, + [MDB_LONGINT] = { .name = "INTEGER" }, + [MDB_MONEY] = { .name = "REAL" }, + [MDB_FLOAT] = { .name = "REAL" }, + [MDB_DOUBLE] = { .name = "REAL" }, + [MDB_DATETIME] = { .name = "DateTime" }, + [MDB_BINARY] = { .name = "BLOB" }, + [MDB_TEXT] = { .name = "varchar" }, + [MDB_OLE] = { .name = "BLOB" }, + [MDB_MEMO] = { .name = "TEXT" }, + [MDB_REPID] = { .name = "INTEGER" }, + [MDB_NUMERIC] = { .name = "INTEGER" }, +}; + +enum { + MDB_BACKEND_ACCESS = 1, + MDB_BACKEND_ORACLE, + MDB_BACKEND_SYBASE, + MDB_BACKEND_POSTGRES, + MDB_BACKEND_MYSQL, + MDB_BACKEND_SQLITE, +}; + +static void mdb_drop_backend(gpointer key, gpointer value, gpointer data); + + +static gchar *passthrough_unchanged(const gchar *str) { + return (gchar *)str; +} + +static gchar *to_lower_case(const gchar *str) { + return g_utf8_strdown(str, -1); +} + +/** + * Convenience function to replace an input string with its database specific normalised version. + * + * This function throws away the input string after normalisation, freeing its memory, and replaces it with a new + * normalised version allocated on the stack. + * + * @param mdb Database specific MDB handle containing pointers to utility methods + * @param str string to normalise + * @return a pointer to the normalised version of the input string + */ +gchar *mdb_normalise_and_replace(MdbHandle *mdb, gchar **str) { + gchar *normalised_str = mdb->default_backend->normalise_case(*str); + if (normalised_str != *str) { + /* Free and replace the old string only and only if a new string was created at a different memory location + * so that we can account for the case where strings a just passed through unchanged. + */ + free(*str); + *str = normalised_str; + } + return *str; +} + +static gchar* +quote_generic(const gchar *value, gchar quote_char, gchar escape_char) { + gchar *result, *pr; + unsigned char c; + + pr = result = g_malloc(1+4*strlen(value)+2); // worst case scenario + + *pr++ = quote_char; + while ((c=*(unsigned char*)value++)) { + if (c<32) { + sprintf(pr, "\\%03o", c); + pr+=4; + continue; + } + else if (c == quote_char) { + *pr++ = escape_char; + } + *pr++ = c; + } + *pr++ = quote_char; + *pr++ = '\0'; + return result; +} +static gchar* +quote_schema_name_bracket_merge(const gchar* schema, const gchar *name) { + if (schema) + return g_strconcat("[", schema, "_", name, "]", NULL); + else + return g_strconcat("[", name, "]", NULL); +} + +/* + * For backends that really does support schema + * returns "name" or "schema"."name" + */ +static gchar* +quote_schema_name_dquote(const gchar* schema, const gchar *name) +{ + if (schema) { + gchar *frag1 = quote_generic(schema, '"', '"'); + gchar *frag2 = quote_generic(name, '"', '"'); + gchar *result = g_strconcat(frag1, ".", frag2, NULL); + g_free(frag1); + g_free(frag2); + return result; + } + return quote_generic(name, '"', '"'); +} + +/* + * For backends that really do NOT support schema + * returns "name" or "schema_name" + */ +/* +static gchar* +quote_schema_name_dquote_merge(const gchar* schema, const gchar *name) +{ + if (schema) { + gchar *combined = g_strconcat(schema, "_", name, NULL); + gchar *result = quote_generic(combined, '"', '"'); + g_free(combined); + return result; + } + return quote_generic(name, '"', '"'); +}*/ + +static gchar* +quote_schema_name_rquotes_merge(const gchar* schema, const gchar *name) +{ + if (schema) { + gchar *combined = g_strconcat(schema, "_", name, NULL); + gchar *result = quote_generic(combined, '`', '`'); + g_free(combined); + return result; + } + return quote_generic(name, '`', '`'); +} + +static gchar* +quote_with_squotes(const gchar* value) +{ + return quote_generic(value, '\'', '\''); +} + +const MdbBackendType* +mdb_get_colbacktype(const MdbColumn *col) { + MdbBackend *backend = col->table->entry->mdb->default_backend; + int col_type = col->col_type; + if (col_type > MDB_NUMERIC) + return NULL; + if (col_type == MDB_LONGINT && col->is_long_auto && backend->type_autonum) + return backend->type_autonum; + if (col_type == MDB_DATETIME && backend->type_shortdate) { + if (mdb_col_is_shortdate(col)) + return backend->type_shortdate; + } + if (!backend->types_table[col_type].name[0]) + return NULL; + return &backend->types_table[col_type]; +} + +const char * +mdb_get_colbacktype_string(const MdbColumn *col) +{ + const MdbBackendType *type = mdb_get_colbacktype(col); + if (!type) { + // return NULL; + static TLS char buf[16]; + snprintf(buf, sizeof(buf), "Unknown_%04x", col->col_type); + return buf; + } + return type->name; +} +int +mdb_colbacktype_takes_length(const MdbColumn *col) +{ + const MdbBackendType *type = mdb_get_colbacktype(col); + if (!type) return 0; + return type->needs_precision || type->needs_char_length || type->needs_byte_length; +} +static int +mdb_colbacktype_takes_length_in_characters(const MdbColumn *col) +{ + const MdbBackendType *type = mdb_get_colbacktype(col); + if (!type) return 0; + return type->needs_char_length; +} + +/** + * mdb_init_backends + * + * Initializes the mdb_backends hash and loads the builtin backends. + * Use mdb_remove_backends() to destroy this hash when done. + */ +void mdb_init_backends(MdbHandle *mdb) +{ + if (mdb->backends) { + mdb_remove_backends(mdb); + } + mdb->backends = g_hash_table_new(g_str_hash, g_str_equal); + + mdb_register_backend(mdb, "access", + MDB_SHEXP_DROPTABLE|MDB_SHEXP_CST_NOTNULL|MDB_SHEXP_DEFVALUES, + mdb_access_types, NULL, NULL, + "Date()", "Date()", + NULL, + NULL, + "-- That file uses encoding %s\n", + "DROP TABLE %s;\n", + NULL, + NULL, + NULL, + NULL, + NULL, + quote_schema_name_bracket_merge); + mdb_register_backend(mdb, "sybase", + MDB_SHEXP_DROPTABLE|MDB_SHEXP_CST_NOTNULL|MDB_SHEXP_CST_NOTEMPTY|MDB_SHEXP_COMMENTS|MDB_SHEXP_DEFVALUES, + mdb_sybase_types, &mdb_sybase_shortdate_type, NULL, + "getdate()", "getdate()", + NULL, + NULL, + "-- That file uses encoding %s\n", + "DROP TABLE %s;\n", + "ALTER TABLE %s ADD CHECK (%s <>'');\n", + "COMMENT ON COLUMN %s.%s IS %s;\n", + NULL, + "COMMENT ON TABLE %s IS %s;\n", + NULL, + quote_schema_name_dquote); + mdb_register_backend(mdb, "oracle", + MDB_SHEXP_DROPTABLE|MDB_SHEXP_CST_NOTNULL|MDB_SHEXP_COMMENTS|MDB_SHEXP_INDEXES|MDB_SHEXP_RELATIONS|MDB_SHEXP_DEFVALUES, + mdb_oracle_types, &mdb_oracle_shortdate_type, NULL, + "current_date", "sysdate", + NULL, + NULL, + "-- That file uses encoding %s\n", + "DROP TABLE %s;\n", + NULL, + "COMMENT ON COLUMN %s.%s IS %s;\n", + NULL, + "COMMENT ON TABLE %s IS %s;\n", + NULL, + quote_schema_name_dquote); + mdbi_register_backend2(mdb, "postgres", + MDB_SHEXP_DROPTABLE|MDB_SHEXP_CST_NOTNULL|MDB_SHEXP_CST_NOTEMPTY|MDB_SHEXP_COMMENTS|MDB_SHEXP_INDEXES|MDB_SHEXP_RELATIONS|MDB_SHEXP_DEFVALUES|MDB_SHEXP_BULK_INSERT, + mdb_postgres_types, &mdb_postgres_shortdate_type, &mdb_postgres_serial_type, + "current_date", "now()", + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d", + "SET client_encoding = '%s';\n", + "CREATE TABLE IF NOT EXISTS %s\n", + "DROP TABLE IF EXISTS %s;\n", + "ALTER TABLE %s ADD CHECK (%s <>'');\n", + "COMMENT ON COLUMN %s.%s IS %s;\n", + NULL, + "COMMENT ON TABLE %s IS %s;\n", + NULL, + quote_schema_name_dquote, + to_lower_case); + mdb_register_backend(mdb, "mysql", + MDB_SHEXP_DROPTABLE|MDB_SHEXP_CST_NOTNULL|MDB_SHEXP_CST_NOTEMPTY|MDB_SHEXP_INDEXES|MDB_SHEXP_RELATIONS|MDB_SHEXP_DEFVALUES|MDB_SHEXP_BULK_INSERT, + mdb_mysql_types, &mdb_mysql_shortdate_type, &mdb_mysql_serial_type, + "current_date", "now()", + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d", + "-- That file uses encoding %s\n", + "DROP TABLE IF EXISTS %s;\n", + "ALTER TABLE %s ADD CHECK (%s <>'');\n", + NULL, + "COMMENT %s", + NULL, + "COMMENT %s", + quote_schema_name_rquotes_merge); + mdb_register_backend(mdb, "sqlite", + MDB_SHEXP_DROPTABLE|MDB_SHEXP_DEFVALUES|MDB_SHEXP_BULK_INSERT|MDB_SHEXP_INDEXES|MDB_SHEXP_CST_NOTNULL, + mdb_sqlite_types, NULL, NULL, + "date('now')", "date('now')", + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d", + "-- That file uses encoding %s\n", + "DROP TABLE IF EXISTS %s;\n", + NULL, + NULL, + NULL, + NULL, + NULL, + quote_schema_name_rquotes_merge); +} + +MdbBackend *mdbi_register_backend2(MdbHandle *mdb, char *backend_name, guint32 capabilities, + const MdbBackendType *backend_type, const MdbBackendType *type_shortdate, const MdbBackendType *type_autonum, + const char *short_now, const char *long_now, + const char *date_fmt, const char *shortdate_fmt, + const char *charset_statement, + const char *create_table_statement, + const char *drop_statement, + const char *constraint_not_empty_statement, + const char *column_comment_statement, + const char *per_column_comment_statement, + const char *table_comment_statement, + const char *per_table_comment_statement, + gchar* (*quote_schema_name)(const gchar*, const gchar*), + gchar* (*normalise_case)(const gchar*)) { + MdbBackend *backend = g_malloc0(sizeof(MdbBackend)); + backend->capabilities = capabilities; + backend->types_table = backend_type; + backend->type_shortdate = type_shortdate; + backend->type_autonum = type_autonum; + backend->short_now = short_now; + backend->long_now = long_now; + backend->date_fmt = date_fmt; + backend->shortdate_fmt = shortdate_fmt; + backend->charset_statement = charset_statement; + backend->create_table_statement = create_table_statement; + backend->drop_statement = drop_statement; + backend->constaint_not_empty_statement = constraint_not_empty_statement; + backend->column_comment_statement = column_comment_statement; + backend->per_column_comment_statement = per_column_comment_statement; + backend->table_comment_statement = table_comment_statement; + backend->per_table_comment_statement = per_table_comment_statement; + backend->quote_schema_name = quote_schema_name; + backend->normalise_case = normalise_case; + g_hash_table_insert(mdb->backends, backend_name, backend); + return backend; +} + +void mdb_register_backend(MdbHandle *mdb, char *backend_name, guint32 capabilities, + const MdbBackendType *backend_type, const MdbBackendType *type_shortdate, const MdbBackendType *type_autonum, + const char *short_now, const char *long_now, + const char *date_fmt, const char *shortdate_fmt, + const char *charset_statement, + const char *drop_statement, + const char *constraint_not_empty_statement, + const char *column_comment_statement, + const char *per_column_comment_statement, + const char *table_comment_statement, + const char *per_table_comment_statement, + gchar* (*quote_schema_name)(const gchar*, const gchar*)) +{ + mdbi_register_backend2(mdb, backend_name, capabilities, + backend_type, type_shortdate, type_autonum, + short_now, long_now, + date_fmt, shortdate_fmt, + charset_statement, + "CREATE TABLE %s\n", + drop_statement, + constraint_not_empty_statement, + column_comment_statement, + per_column_comment_statement, + table_comment_statement, + per_table_comment_statement, + quote_schema_name, + passthrough_unchanged); +} + +/** + * mdb_remove_backends + * + * Removes all entries from and destroys the mdb_backends hash. + */ +void +mdb_remove_backends(MdbHandle *mdb) +{ + g_hash_table_foreach(mdb->backends, mdb_drop_backend, NULL); + g_hash_table_destroy(mdb->backends); +} +static void mdb_drop_backend(gpointer key, gpointer value, gpointer data) +{ + MdbBackend *backend = (MdbBackend *)value; + g_free (backend); +} + +/** + * mdb_set_default_backend + * @mdb: Handle to open MDB database file + * @backend_name: Name of the backend to set as default + * + * Sets the default backend of the handle @mdb to @backend_name. + * + * Returns: 1 if successful, 0 if unsuccessful. + */ +int mdb_set_default_backend(MdbHandle *mdb, const char *backend_name) +{ + MdbBackend *backend; + + if (!mdb->backends) { + mdb_init_backends(mdb); + } + backend = (MdbBackend *) g_hash_table_lookup(mdb->backends, backend_name); + if (backend) { + mdb->default_backend = backend; + g_free(mdb->backend_name); // NULL is ok + mdb->backend_name = (char *) g_strdup(backend_name); + mdb->relationships_table = NULL; + if (backend->date_fmt) { + mdb_set_date_fmt(mdb, backend->date_fmt); + } else { + mdb_set_date_fmt(mdb, "%x %X"); + } + if (backend->shortdate_fmt) { + mdb_set_shortdate_fmt(mdb, backend->shortdate_fmt); + } else { + mdb_set_shortdate_fmt(mdb, "%x"); + } + } + return (backend != NULL); +} + + +/** + * Generates index name based on backend. + * + * You should free() the returned value once you are done with it. + * + * @param backend backend we are generating indexes for + * @param table table being processed + * @param idx index being processed + * @return the index name + */ +static char * +mdb_get_index_name(int backend, MdbTableDef *table, MdbIndex *idx) +{ + char *index_name; + + switch(backend){ + case MDB_BACKEND_MYSQL: + // appending table name to index often makes it too long for mysql + if (idx->index_type==1) + // for mysql name of primary key is not used + index_name = g_strdup("_pkey"); + else { + index_name = g_strdup(idx->name); + } + break; + default: + if (idx->index_type==1) + index_name = g_strconcat(table->name, "_pkey", NULL); + else { + index_name = g_strconcat(table->name, "_", idx->name, "_idx", NULL); + } + } + + return index_name; +} +/** + * mdb_print_pk - print primary key constraint + * @output: Where to print the sql + * @table: Table to process + */ +static void +mdb_print_pk_if_sqlite(FILE *outfile, MdbTableDef *table) +{ + unsigned int i, j; + MdbHandle *mdb = table->entry->mdb; + MdbIndex *idx; + MdbColumn *col; + char *quoted_name; + // this is only necessary for sqlite + if (strcmp(mdb->backend_name, "sqlite") != 0) + return; + + if (table->indices==NULL) + mdb_read_indices(table); + + for (i = 0; i < table->num_idxs; i++) { + idx = g_ptr_array_index(table->indices, i); + if (idx->index_type == 1) { + fprintf(outfile, "\t, PRIMARY KEY ("); + for (j = 0; j < idx->num_keys; j++) { + if (j) + fprintf(outfile, ", "); + col = g_ptr_array_index(table->columns, idx->key_col_num[j] - 1); + quoted_name = mdb->default_backend->quote_schema_name(NULL, col->name); + quoted_name = mdb_normalise_and_replace(mdb, "ed_name); + fprintf(outfile, "%s", quoted_name); + if (idx->index_type != 1 && idx->key_col_order[j]) + /* no DESC for primary keys */ + fprintf(outfile, " DESC"); + + g_free(quoted_name); + } + fprintf(outfile, ")\n"); + } + } +} +/** + * mdb_print_indexes + * @output: Where to print the sql + * @table: Table to process + * @dbnamespace: Target namespace/schema name + */ +static void +mdb_print_indexes(FILE* outfile, MdbTableDef *table, char *dbnamespace) +{ + unsigned int i, j; + char* quoted_table_name; + char* index_name; + char* quoted_name; + int backend; + MdbHandle* mdb = table->entry->mdb; + MdbIndex *idx; + MdbColumn *col; + + if (!strcmp(mdb->backend_name, "postgres")) { + backend = MDB_BACKEND_POSTGRES; + } else if (!strcmp(mdb->backend_name, "mysql")) { + backend = MDB_BACKEND_MYSQL; + } else if (!strcmp(mdb->backend_name, "oracle")) { + backend = MDB_BACKEND_ORACLE; + } else if (!strcmp(mdb->backend_name, "sqlite")) { + backend = MDB_BACKEND_SQLITE; + } else { + fprintf(outfile, "-- Indexes are not implemented for %s\n\n", mdb->backend_name); + return; + } + + /* read indexes */ + if (table->indices==NULL) + mdb_read_indices(table); + + fprintf (outfile, "-- CREATE INDEXES ...\n"); + + quoted_table_name = mdb->default_backend->quote_schema_name(dbnamespace, table->name); + quoted_table_name = mdb->default_backend->normalise_case(quoted_table_name); + + for (i=0;inum_idxs;i++) { + idx = g_ptr_array_index (table->indices, i); + if (idx->index_type==2) + continue; + /* Sqlite3 primary keys have to be issued as a table constraint */ + if (idx->index_type == 1 && backend == MDB_BACKEND_SQLITE) + continue; + + index_name = mdb_get_index_name(backend, table, idx); + switch (backend) { + case MDB_BACKEND_POSTGRES: + /* PostgreSQL index and constraint names are + * never namespaced in DDL (they are always + * created in same namespace as table), so + * omit namespace. + */ + quoted_name = mdb->default_backend->quote_schema_name(NULL, index_name); + break; + + default: + quoted_name = mdb->default_backend->quote_schema_name(dbnamespace, index_name); + } + + quoted_name = mdb_normalise_and_replace(mdb, "ed_name); + if (idx->num_keys == 0) { + fprintf(outfile, "-- WARNING: found no keys for index %s - ignored\n", quoted_name); + continue; + } + if (idx->index_type==1) { + switch (backend) { + case MDB_BACKEND_ORACLE: + case MDB_BACKEND_POSTGRES: + fprintf (outfile, "ALTER TABLE %s ADD CONSTRAINT %s PRIMARY KEY (", quoted_table_name, quoted_name); + break; + case MDB_BACKEND_MYSQL: + fprintf (outfile, "ALTER TABLE %s ADD PRIMARY KEY (", quoted_table_name); + break; + } + } else { + switch (backend) { + case MDB_BACKEND_ORACLE: + case MDB_BACKEND_POSTGRES: + case MDB_BACKEND_SQLITE: + fprintf(outfile, "CREATE"); + if (idx->flags & MDB_IDX_UNIQUE) + fprintf (outfile, " UNIQUE"); + fprintf(outfile, " INDEX %s ON %s (", quoted_name, quoted_table_name); + break; + case MDB_BACKEND_MYSQL: + fprintf(outfile, "ALTER TABLE %s ADD", quoted_table_name); + if (idx->flags & MDB_IDX_UNIQUE) + fprintf (outfile, " UNIQUE"); + fprintf(outfile, " INDEX %s (", quoted_name); + break; + } + } + g_free(quoted_name); + free(index_name); + + for (j=0;jnum_keys;j++) { + if (j) + fprintf(outfile, ", "); + col=g_ptr_array_index(table->columns,idx->key_col_num[j]-1); + quoted_name = mdb->default_backend->quote_schema_name(NULL, col->name); + quoted_name = mdb_normalise_and_replace(mdb, "ed_name); + fprintf (outfile, "%s", quoted_name); + if (idx->index_type!=1 && idx->key_col_order[j]) + /* no DESC for primary keys */ + fprintf(outfile, " DESC"); + + g_free(quoted_name); + + } + fprintf (outfile, ");\n"); + } + fputc ('\n', outfile); + + g_free(quoted_table_name); +} + +/** + * mdb_get_relationships + * @mdb: Handle to open MDB database file + * @tablename: Name of the table to process. Process all tables if NULL. + * + * Generates relationships by reading the MSysRelationships table. + * 'szColumn' contains the column name of the child table. + * 'szObject' contains the table name of the child table. + * 'szReferencedColumn' contains the column name of the parent table. + * 'szReferencedObject' contains the table name of the parent table. + * 'grbit' contains integrity constraints. + * + * Returns: a string stating that relationships are not supported for the + * selected backend, or a string containing SQL commands for setting up + * the relationship, tailored for the selected backend. + * Returns NULL on last iteration. + * The caller is responsible for freeing this string. + */ +static char * +mdb_get_relationships(MdbHandle *mdb, const gchar *dbnamespace, const char* tablename) +{ + unsigned int i; + gchar *text = NULL; /* String to be returned */ + char **bound = mdb->relationships_values; /* Bound values */ + int backend = 0; + char *quoted_table_1, *quoted_column_1, + *quoted_table_2, *quoted_column_2, + *constraint_name, *quoted_constraint_name; + long grbit; + + if (!strcmp(mdb->backend_name, "oracle")) { + backend = MDB_BACKEND_ORACLE; + } else if (!strcmp(mdb->backend_name, "postgres")) { + backend = MDB_BACKEND_POSTGRES; + } else if (!strcmp(mdb->backend_name, "mysql")) { + backend = MDB_BACKEND_MYSQL; + } else if (!mdb->relationships_table) { + return NULL; + } + + if (!mdb->relationships_table) { + mdb->relationships_table = mdb_read_table_by_name(mdb, "MSysRelationships", MDB_TABLE); + if (!mdb->relationships_table || !mdb->relationships_table->num_rows) { + fprintf(stderr, "No MSysRelationships\n"); + return NULL; + } + if (!mdb_read_columns(mdb->relationships_table)) { + fprintf(stderr, "Unable to read columns of MSysRelationships\n"); + return NULL; + } + for (i=0;i<5;i++) { + bound[i] = g_malloc0(mdb->bind_size); + } + mdb_bind_column_by_name(mdb->relationships_table, "szColumn", bound[0], NULL); + mdb_bind_column_by_name(mdb->relationships_table, "szObject", bound[1], NULL); + mdb_bind_column_by_name(mdb->relationships_table, "szReferencedColumn", bound[2], NULL); + mdb_bind_column_by_name(mdb->relationships_table, "szReferencedObject", bound[3], NULL); + mdb_bind_column_by_name(mdb->relationships_table, "grbit", bound[4], NULL); + mdb_rewind_table(mdb->relationships_table); + } + if (mdb->relationships_table->cur_row >= mdb->relationships_table->num_rows) { /* past the last row */ + for (i=0;i<5;i++) + g_free(bound[i]); + mdb->relationships_table = NULL; + return NULL; + } + + while (1) { + if (!mdb_fetch_row(mdb->relationships_table)) { + for (i=0;i<5;i++) + g_free(bound[i]); + mdb->relationships_table = NULL; + return NULL; + } + if (!tablename || !strcmp(bound[1], tablename)) + break; + } + + quoted_table_1 = mdb->default_backend->quote_schema_name(dbnamespace, bound[1]); + quoted_table_2 = mdb->default_backend->quote_schema_name(dbnamespace, bound[3]); + grbit = atoi(bound[4]); + constraint_name = g_strconcat(bound[1], "_", bound[0], "_fk", NULL); + + switch (backend) { + case MDB_BACKEND_POSTGRES: + /* PostgreSQL index and constraint names are + * never namespaced in DDL (they are always + * created in same namespace as table), so + * omit namespace. Nor should column names + * be namespaced. + */ + quoted_constraint_name = mdb->default_backend->quote_schema_name(NULL, constraint_name); + quoted_constraint_name = mdb_normalise_and_replace(mdb, "ed_constraint_name); + quoted_column_1 = mdb->default_backend->quote_schema_name(NULL, bound[0]); + quoted_column_1 = mdb_normalise_and_replace(mdb, "ed_column_1); + quoted_column_2 = mdb->default_backend->quote_schema_name(NULL, bound[2]); + quoted_column_2 = mdb_normalise_and_replace(mdb, "ed_column_2); + break; + + default: + /* Other databases, namespace constraint and + * column names. + */ + quoted_constraint_name = mdb->default_backend->quote_schema_name(dbnamespace, constraint_name); + quoted_column_1 = mdb->default_backend->quote_schema_name(dbnamespace, bound[0]); + quoted_column_2 = mdb->default_backend->quote_schema_name(dbnamespace, bound[2]); + break; + } + g_free(constraint_name); + + if (grbit & 0x00000002) { + text = g_strconcat( + "-- Relationship from ", quoted_table_1, + " (", quoted_column_1, ")" + " to ", quoted_table_2, "(", quoted_column_2, ")", + " does not enforce integrity.\n", NULL); + } else { + switch (backend) { + case MDB_BACKEND_ORACLE: + text = g_strconcat( + "ALTER TABLE ", quoted_table_1, + " ADD CONSTRAINT ", quoted_constraint_name, + " FOREIGN KEY (", quoted_column_1, ")" + " REFERENCES ", quoted_table_2, "(", quoted_column_2, ")", + (grbit & 0x00001000) ? " ON DELETE CASCADE" : "", + ";\n", NULL); + + break; + case MDB_BACKEND_MYSQL: + text = g_strconcat( + "ALTER TABLE ", quoted_table_1, + " ADD CONSTRAINT ", quoted_constraint_name, + " FOREIGN KEY (", quoted_column_1, ")" + " REFERENCES ", quoted_table_2, "(", quoted_column_2, ")", + (grbit & 0x00000100) ? " ON UPDATE CASCADE" : "", + (grbit & 0x00001000) ? " ON DELETE CASCADE" : "", + ";\n", NULL); + break; + case MDB_BACKEND_POSTGRES: + text = g_strconcat( + "ALTER TABLE ", quoted_table_1, + " ADD CONSTRAINT ", quoted_constraint_name, + " FOREIGN KEY (", quoted_column_1, ")" + " REFERENCES ", quoted_table_2, "(", quoted_column_2, ")", + (grbit & 0x00000100) ? " ON UPDATE CASCADE" : "", + (grbit & 0x00001000) ? " ON DELETE CASCADE" : "", + /* On some databases (eg PostgreSQL) we also want to set + * the constraints to be optionally deferrable, to + * facilitate out of order bulk loading. + */ + " DEFERRABLE", + " INITIALLY IMMEDIATE", + ";\n", NULL); + + break; + } + } + g_free(quoted_table_1); + g_free(quoted_column_1); + g_free(quoted_table_2); + g_free(quoted_column_2); + g_free(quoted_constraint_name); + + return (char *)text; +} + +static void +generate_table_schema(FILE *outfile, MdbCatalogEntry *entry, char *dbnamespace, guint32 export_options) +{ + MdbTableDef *table; + MdbHandle *mdb = entry->mdb; + MdbColumn *col; + unsigned int i; + char* quoted_table_name; + char* quoted_name; + MdbProperties *props; + const char *prop_value; + + quoted_table_name = mdb->default_backend->quote_schema_name(dbnamespace, entry->object_name); + quoted_table_name = mdb_normalise_and_replace(mdb, "ed_table_name); + + /* drop the table if it exists */ + if (export_options & MDB_SHEXP_DROPTABLE) + fprintf (outfile, mdb->default_backend->drop_statement, quoted_table_name); + + /* create the table */ + fprintf (outfile, mdb->default_backend->create_table_statement, quoted_table_name); + fprintf (outfile, " (\n"); + + table = mdb_read_table (entry); + if (!table) { + fprintf(stderr, "Error: Table %s does not exist\n", entry->object_name); + return; + } + + /* get the columns */ + mdb_read_columns(table); + + /* loop over the columns, dumping the names and types */ + for (i = 0; i < table->num_cols; i++) { + col = g_ptr_array_index (table->columns, i); + + quoted_name = mdb->default_backend->quote_schema_name(NULL, col->name); + quoted_name = mdb_normalise_and_replace(mdb, "ed_name); + fprintf (outfile, "\t%s\t\t\t%s", quoted_name, + mdb_get_colbacktype_string (col)); + g_free(quoted_name); + + if (mdb_colbacktype_takes_length(col)) { + /* more portable version from DW patch */ + if (col->col_size == 0) + fputs(" (255)", outfile); + else if (col->col_scale != 0) + fprintf(outfile, " (%d, %d)", col->col_scale, col->col_prec); + else if (!IS_JET3(mdb) && mdb_colbacktype_takes_length_in_characters(col)) + fprintf(outfile, " (%d)", col->col_size/2); + else + fprintf(outfile, " (%d)", col->col_size); + } + + if (mdb->default_backend->per_column_comment_statement && export_options & MDB_SHEXP_COMMENTS) { + prop_value = mdb_col_get_prop(col, "Description"); + if (prop_value) { + char *comment = quote_with_squotes(prop_value); + fputs(" ", outfile); + fprintf(outfile, + mdb->default_backend->per_column_comment_statement, + comment); + free(comment); + } + } + + if (export_options & MDB_SHEXP_CST_NOTNULL) { + if (col->col_type == MDB_BOOL) { + /* access booleans are never null */ + fputs(" NOT NULL", outfile); + } else { + const gchar *not_null = mdb_col_get_prop(col, "Required"); + if (not_null && not_null[0]=='y') + fputs(" NOT NULL", outfile); + } + } + + if (export_options & MDB_SHEXP_DEFVALUES) { + int done = 0; + if (col->props) { + gchar *defval = g_hash_table_lookup(col->props->hash, "DefaultValue"); + if (defval) { + size_t def_len = strlen(defval); + fputs(" DEFAULT ", outfile); + /* ugly hack to detect the type */ + if (defval[0]=='"' && defval[def_len-1]=='"') { + /* this is a string */ + gchar *output_default = malloc(def_len-1); + gchar *output_default_escaped; + memcpy(output_default, defval+1, def_len-2); + output_default[def_len-2] = 0; + output_default_escaped = quote_with_squotes(output_default); + fputs(output_default_escaped, outfile); + g_free(output_default_escaped); + free(output_default); + } else if (!strcmp(defval, "Yes")) + fputs("TRUE", outfile); + else if (!strcmp(defval, "No")) + fputs("FALSE", outfile); + else if (!g_ascii_strcasecmp(defval, "date()")) { + if (mdb_col_is_shortdate(col)) + fputs(mdb->default_backend->short_now, outfile); + else + fputs(mdb->default_backend->long_now, outfile); + } + else + fputs(defval, outfile); + done = 1; + } + } + if (!done && col->col_type == MDB_BOOL) + /* access booleans are false by default */ + fputs(" DEFAULT FALSE", outfile); + } + if (i < table->num_cols - 1) + fputs(", \n", outfile); + else + fputs("\n", outfile); + } /* for */ + + if (export_options & MDB_SHEXP_INDEXES) { + // sqlite does not support ALTER TABLE PRIMARY KEY, so we need to place it directly into CREATE TABLE + mdb_print_pk_if_sqlite(outfile, table); + } + + fputs(")", outfile); + if (mdb->default_backend->per_table_comment_statement && export_options & MDB_SHEXP_COMMENTS) { + prop_value = mdb_table_get_prop(table, "Description"); + if (prop_value) { + char *comment = quote_with_squotes(prop_value); + fputs(" ", outfile); + fprintf(outfile, mdb->default_backend->per_table_comment_statement, comment); + free(comment); + } + } + fputs(";\n", outfile); + + /* Add the constraints on columns */ + for (i = 0; i < table->num_cols; i++) { + col = g_ptr_array_index (table->columns, i); + props = col->props; + if (!props) + continue; + + quoted_name = mdb->default_backend->quote_schema_name(NULL, col->name); + quoted_name = mdb_normalise_and_replace(mdb, "ed_name); + + if (export_options & MDB_SHEXP_CST_NOTEMPTY) { + prop_value = mdb_col_get_prop(col, "AllowZeroLength"); + if (prop_value && prop_value[0]=='n') + fprintf(outfile, + mdb->default_backend->constaint_not_empty_statement, + quoted_table_name, quoted_name); + } + + if (mdb->default_backend->column_comment_statement && export_options & MDB_SHEXP_COMMENTS) { + prop_value = mdb_col_get_prop(col, "Description"); + if (prop_value) { + char *comment = quote_with_squotes(prop_value); + fprintf(outfile, + mdb->default_backend->column_comment_statement, + quoted_table_name, quoted_name, comment); + g_free(comment); + } + } + + g_free(quoted_name); + } + + /* Add the constraints on table */ + if (mdb->default_backend->table_comment_statement && export_options & MDB_SHEXP_COMMENTS) { + prop_value = mdb_table_get_prop(table, "Description"); + if (prop_value) { + char *comment = quote_with_squotes(prop_value); + fprintf(outfile, + mdb->default_backend->table_comment_statement, + quoted_table_name, comment); + g_free(comment); + } + } + fputc('\n', outfile); + + + if (export_options & MDB_SHEXP_INDEXES) + // prints all the indexes of that table + mdb_print_indexes(outfile, table, dbnamespace); + + g_free(quoted_table_name); + + mdb_free_tabledef (table); +} + + +int +mdb_print_schema(MdbHandle *mdb, FILE *outfile, char *tabname, char *dbnamespace, guint32 export_options) +{ + unsigned int i; + char *the_relation; + MdbCatalogEntry *entry; + const char *charset; + int success = (tabname == NULL); + + /* clear unsupported options */ + export_options &= mdb->default_backend->capabilities; + + /* Print out a little message to show that this came from mdb-tools. + I like to know how something is generated. DW */ + fputs("-- ----------------------------------------------------------\n" + "-- MDB Tools - A library for reading MS Access database files\n" + "-- Copyright (C) 2000-2011 Brian Bruns and others.\n" + "-- Files in libmdb are licensed under LGPL and the utilities under\n" + "-- the GPL, see COPYING.LIB and COPYING files respectively.\n" + "-- Check out http://mdbtools.sourceforge.net\n" + "-- ----------------------------------------------------------\n\n", + outfile); + + charset = mdb_target_charset(mdb); + if (charset) { + fprintf(outfile, mdb->default_backend->charset_statement, charset); + fputc('\n', outfile); + } + + for (i=0; i < mdb->num_catalog; i++) { + entry = g_ptr_array_index (mdb->catalog, i); + if (entry->object_type == MDB_TABLE) { + if ((tabname && !strcmp(entry->object_name, tabname)) + || (!tabname && mdb_is_user_table(entry))) { + generate_table_schema(outfile, entry, dbnamespace, export_options); + success = 1; + } + } + } + fprintf (outfile, "\n"); + + if (export_options & MDB_SHEXP_RELATIONS) { + fputs ("-- CREATE Relationships ...\n", outfile); + the_relation=mdb_get_relationships(mdb, dbnamespace, tabname); + if (!the_relation) { + fputs("-- relationships are not implemented for ", outfile); + fputs(mdb->backend_name, outfile); + fputs("\n", outfile); + } else { + do { + fputs(the_relation, outfile); + g_free(the_relation); + } while ((the_relation=mdb_get_relationships(mdb, dbnamespace, tabname)) != NULL); + } + } + return success; +} + +#define MDB_BINEXPORT_MASK 0x0F +#define is_binary_type(x) (x==MDB_OLE || x==MDB_BINARY || x==MDB_REPID) +#define is_quote_type(x) (is_binary_type(x) || x==MDB_TEXT || x==MDB_MEMO || x==MDB_DATETIME) +//#define DONT_ESCAPE_ESCAPE +void +mdb_print_col(FILE *outfile, gchar *col_val, int quote_text, int col_type, int bin_len, + char *quote_char, char *escape_char, int flags) +/* quote_text: Don't quote if 0. + */ +{ + size_t quote_len = strlen(quote_char); /* multibyte */ + + size_t orig_escape_len = escape_char ? strlen(escape_char) : 0; + int quoting = quote_text && is_quote_type(col_type); + int bin_mode = (flags & MDB_BINEXPORT_MASK); + int escape_cr_lf = !!(flags & MDB_EXPORT_ESCAPE_CONTROL_CHARS); + + /* double the quote char if no escape char passed */ + if (!escape_char) + escape_char = quote_char; + + if (quoting) + fputs(quote_char, outfile); + + while (1) { + if (is_binary_type(col_type)) { + if (bin_mode == MDB_BINEXPORT_STRIP) + break; + if (!bin_len--) + break; + } else /* use \0 sentry */ + if (!*col_val) + break; + + if (is_binary_type(col_type) && bin_mode == MDB_BINEXPORT_OCTAL) { + fprintf(outfile, "\\%03o", *(unsigned char*)col_val++); + } else if (is_binary_type(col_type) && bin_mode == MDB_BINEXPORT_HEXADECIMAL) { + fprintf(outfile, "%02X", *(unsigned char*)col_val++); + } else if (quoting && quote_len && !strncmp(col_val, quote_char, quote_len)) { + fprintf(outfile, "%s%s", escape_char, quote_char); + col_val += quote_len; +#ifndef DONT_ESCAPE_ESCAPE + } else if (quoting && orig_escape_len && !strncmp(col_val, escape_char, orig_escape_len)) { + fprintf(outfile, "%s%s", escape_char, escape_char); + col_val += orig_escape_len; +#endif + } else if (escape_cr_lf && is_quote_type(col_type) && *col_val=='\r') { + col_val++; + putc('\\', outfile); + putc('r', outfile); + } else if (escape_cr_lf && is_quote_type(col_type) && *col_val=='\n') { + col_val++; + putc('\\', outfile); + putc('n', outfile); + } else if (escape_cr_lf && is_quote_type(col_type) && *col_val=='\t') { + col_val++; + putc('\\', outfile); + putc('t', outfile); + } else if (escape_cr_lf && is_quote_type(col_type) && *col_val=='\\') { + col_val++; + putc('\\', outfile); + putc('\\', outfile); + } else + putc(*col_val++, outfile); + } + if (quoting) + fputs(quote_char, outfile); +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/catalog.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/catalog.c new file mode 100644 index 0000000000..9239be2df2 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/catalog.c @@ -0,0 +1,203 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mdbtools.h" + +const char * +mdb_get_objtype_string(int obj_type) +{ + static const char *type_name[] = {"Form", + "Table", + "Macro", + "System Table", + "Report", + "Query", + "Linked Table", + "Module", + "Relationship", + "Unknown 0x09", + "User Info", + "Database" + }; + + if (obj_type >= (int)(sizeof(type_name)/sizeof(type_name[0]))) { + return NULL; + } else { + return type_name[obj_type]; + } +} + +void mdb_free_catalog(MdbHandle *mdb) +{ + guint i, j; + MdbCatalogEntry *entry; + + if ((!mdb) || (!mdb->catalog)) return; + for (i=0; icatalog->len; i++) { + entry = (MdbCatalogEntry *)g_ptr_array_index(mdb->catalog, i); + if (entry) { + if (entry->props) { + for (j=0; jprops->len; j++) + mdb_free_props(g_ptr_array_index(entry->props, j)); + g_ptr_array_free(entry->props, TRUE); + } + g_free(entry); + } + } + g_ptr_array_free(mdb->catalog, TRUE); + mdb->catalog = NULL; +} + +GPtrArray *mdb_read_catalog (MdbHandle *mdb, int objtype) +{ + MdbCatalogEntry *entry, msysobj; + MdbTableDef *table; + char *obj_id = NULL; + char *obj_name = NULL; + char *obj_type = NULL; + char *obj_flags = NULL; + char *obj_props = NULL; + int type; + int i; + MdbColumn *col_props; + int kkd_size_ole; + + if (!mdb) return NULL; + if (mdb->catalog) mdb_free_catalog(mdb); + mdb->catalog = g_ptr_array_new(); + mdb->num_catalog = 0; + + obj_id = malloc(mdb->bind_size); + obj_name = malloc(mdb->bind_size); + obj_type = malloc(mdb->bind_size); + obj_flags = malloc(mdb->bind_size); + obj_props = malloc(mdb->bind_size); + + /* dummy up a catalog entry so we may read the table def */ + memset(&msysobj, 0, sizeof(MdbCatalogEntry)); + msysobj.mdb = mdb; + msysobj.object_type = MDB_TABLE; + msysobj.table_pg = 2; + snprintf(msysobj.object_name, sizeof(msysobj.object_name), "%s", "MSysObjects"); + + /* mdb_table_dump(&msysobj); */ + + table = mdb_read_table(&msysobj); + if (!table) { + fprintf(stderr, "Unable to read table %s\n", msysobj.object_name); + mdb_free_catalog(mdb); + goto cleanup; + } + + if (!mdb_read_columns(table)) { + fprintf(stderr, "Unable to read columns of table %s\n", msysobj.object_name); + mdb_free_catalog(mdb); + goto cleanup; + } + + if (mdb_bind_column_by_name(table, "Id", obj_id, NULL) == -1 || + mdb_bind_column_by_name(table, "Name", obj_name, NULL) == -1 || + mdb_bind_column_by_name(table, "Type", obj_type, NULL) == -1 || + mdb_bind_column_by_name(table, "Flags", obj_flags, NULL) == -1) { + fprintf(stderr, "Unable to bind columns from table %s (%d columns found)\n", + msysobj.object_name, table->num_cols); + mdb_free_catalog(mdb); + goto cleanup; + } + if ((i = mdb_bind_column_by_name(table, "LvProp", obj_props, &kkd_size_ole)) == -1) { + fprintf(stderr, "Unable to bind column %s from table %s\n", "LvProp", msysobj.object_name); + mdb_free_catalog(mdb); + goto cleanup; + } + col_props = g_ptr_array_index(table->columns, i-1); + + mdb_rewind_table(table); + + while (mdb_fetch_row(table)) { + type = atoi(obj_type); + if (objtype==MDB_ANY || type == objtype) { + //fprintf(stderr, "obj_id: %10ld objtype: %-3d (0x%04x) obj_name: %s\n", + // (atol(obj_id) & 0x00FFFFFF), type, type, obj_name); + entry = g_malloc0(sizeof(MdbCatalogEntry)); + entry->mdb = mdb; + snprintf(entry->object_name, sizeof(entry->object_name), "%s", obj_name); + entry->object_type = (type & 0x7F); + entry->table_pg = atol(obj_id) & 0x00FFFFFF; + entry->flags = atol(obj_flags); + mdb->num_catalog++; + g_ptr_array_add(mdb->catalog, entry); + if (kkd_size_ole) { + size_t kkd_len; + void *kkd = mdb_ole_read_full(mdb, col_props, &kkd_len); + //mdb_buffer_dump(kkd, 0, kkd_len); + if (kkd) { + entry->props = mdb_kkd_to_props(mdb, kkd, kkd_len); + free(kkd); + } + } + } + } + //mdb_dump_catalog(mdb, MDB_TABLE); + +cleanup: + if (table) + mdb_free_tabledef(table); + + free(obj_id); + free(obj_name); + free(obj_type); + free(obj_flags); + free(obj_props); + + return mdb->catalog; +} + + +MdbCatalogEntry * +mdb_get_catalogentry_by_name(MdbHandle *mdb, const gchar* name) +{ + unsigned int i; + MdbCatalogEntry *entry; + + for (i=0; inum_catalog; i++) { + entry = g_ptr_array_index(mdb->catalog, i); + if (!g_ascii_strcasecmp(entry->object_name, name)) + return entry; + } + return NULL; +} + +void +mdb_dump_catalog(MdbHandle *mdb, int obj_type) +{ + unsigned int i; + MdbCatalogEntry *entry; + + mdb_read_catalog(mdb, obj_type); + for (i=0;inum_catalog;i++) { + entry = g_ptr_array_index(mdb->catalog,i); + if (obj_type==MDB_ANY || entry->object_type==obj_type) { + printf("Type: %-12s Name: %-48s Page: %06lx\n", + mdb_get_objtype_string(entry->object_type) ?: "Unknown", + entry->object_name, + entry->table_pg); + } + } + return; +} + diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/data.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/data.c new file mode 100644 index 0000000000..5b5668bea9 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/data.c @@ -0,0 +1,1146 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mdbtools.h" + +#include + +#define OFFSET_MASK 0x1fff +#define OLE_BUFFER_SIZE (MDB_BIND_SIZE*64) + +static int _mdb_attempt_bind(MdbHandle *mdb, + MdbColumn *col, unsigned char isnull, int offset, int len); +static char *mdb_date_to_string(MdbHandle *mdb, const char *fmt, void *buf, int start); +#ifdef MDB_COPY_OLE +static size_t mdb_copy_ole(MdbHandle *mdb, void *dest, int start, int size); +#endif + +#ifndef HAVE_REALLOCF +static void *reallocf(void *ptr, size_t len) { + void *ptr2 = realloc(ptr, len); + if (!ptr2) { + free(ptr); + return NULL; + } + return ptr2; +} +#endif + +static const int noleap_cal[] = {0,31,59,90,120,151,181,212,243,273,304,334,365}; +static const int leap_cal[] = {0,31,60,91,121,152,182,213,244,274,305,335,366}; + +/* Some databases (eg PostgreSQL) do not understand integer 0/1 values + * as TRUE/FALSE, so provide a means to override the values used to be + * the SQL Standard TRUE/FALSE values. + */ +static const char boolean_false_number[] = "0"; +static const char boolean_true_number[] = "1"; + +static const char boolean_false_word[] = "FALSE"; +static const char boolean_true_word[] = "TRUE"; + +void mdb_set_bind_size(MdbHandle *mdb, size_t bind_size) { + mdb->bind_size = bind_size; +} + +void mdb_set_date_fmt(MdbHandle *mdb, const char *fmt) +{ + snprintf(mdb->date_fmt, sizeof(mdb->date_fmt), "%s", fmt); +} + +void mdb_set_shortdate_fmt(MdbHandle *mdb, const char *fmt) +{ + snprintf(mdb->shortdate_fmt, sizeof(mdb->shortdate_fmt), "%s", fmt); +} + +void mdb_set_repid_fmt(MdbHandle *mdb, MdbUuidFormat format) +{ + mdb->repid_fmt = format; +} + +void mdb_set_boolean_fmt_numbers(MdbHandle *mdb) +{ + mdb->boolean_false_value = boolean_false_number; + mdb->boolean_true_value = boolean_true_number; +} + +void mdb_set_boolean_fmt_words(MdbHandle *mdb) +{ + mdb->boolean_false_value = boolean_false_word; + mdb->boolean_true_value = boolean_true_word; +} + +int mdb_bind_column(MdbTableDef *table, int col_num, void *bind_ptr, int *len_ptr) +{ + MdbColumn *col = NULL; + + if (!table->columns) + return -1; + /* + ** the column arrary is 0 based, so decrement to get 1 based parameter + */ + col_num--; + + if (col_num >= 0 && col_num < (int)table->num_cols) { + col=g_ptr_array_index(table->columns, col_num); + + if (col) { + if (bind_ptr) + col->bind_ptr = bind_ptr; + if (len_ptr) + col->len_ptr = len_ptr; + + return col_num + 1; + } + } + + return -1; +} + +int +mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr, int *len_ptr) +{ + unsigned int i; + int col_num = -1; + MdbColumn *col; + + if (!table->columns) + return -1; + + for (i=0;inum_cols;i++) { + col=g_ptr_array_index(table->columns,i); + if (!g_ascii_strcasecmp(col->name,col_name)) { + col_num = i + 1; + if (bind_ptr) + col->bind_ptr = bind_ptr; + if (len_ptr) + col->len_ptr = len_ptr; + break; + } + } + + return col_num; +} + +/** + * mdb_find_pg_row + * @mdb: Database file handle + * @pg_row: Lower byte contains the row number, the upper three contain page + * @buf: Pointer for returning a pointer to the page + * @off: Pointer for returning an offset to the row + * @len: Pointer for returning the length of the row + * + * Returns: 0 on success. -1 on failure. + */ +int mdb_find_pg_row(MdbHandle *mdb, int pg_row, void **buf, int *off, size_t *len) +{ + unsigned int pg = pg_row >> 8; + unsigned int row = pg_row & 0xff; + int result = 0; + + if (mdb_read_alt_pg(mdb, pg) != mdb->fmt->pg_size) + return -1; + mdb_swap_pgbuf(mdb); + result = mdb_find_row(mdb, row, off, len); + mdb_swap_pgbuf(mdb); + *off &= OFFSET_MASK; + *buf = mdb->alt_pg_buf; + return result; +} + +int mdb_find_row(MdbHandle *mdb, int row, int *start, size_t *len) +{ + int rco = mdb->fmt->row_count_offset; + int next_start; + + if (row > 1000) return -1; + + *start = mdb_get_int16(mdb->pg_buf, rco + 2 + row*2); + next_start = (row == 0) ? mdb->fmt->pg_size : + mdb_get_int16(mdb->pg_buf, rco + row*2) & OFFSET_MASK; + *len = next_start - (*start & OFFSET_MASK); + + if ((*start & OFFSET_MASK) >= mdb->fmt->pg_size || + (*start & OFFSET_MASK) > next_start || + next_start > mdb->fmt->pg_size) + return -1; + + return 0; +} + +int +mdb_find_end_of_row(MdbHandle *mdb, int row) +{ + int rco = mdb->fmt->row_count_offset; + int row_end; + +#if 1 + if (row > 1000) return -1; + + row_end = (row == 0) ? mdb->fmt->pg_size : + mdb_get_int16(mdb->pg_buf, rco + row*2) & OFFSET_MASK; +#else + /* Search the previous "row start" values for the first non-'lookupflag' + * one. If we don't find one, then the end of the page is the correct + * value. + */ + int i, row_start; + + if (row > 1000) return -1; + + /* if lookupflag is not set, it's good (deleteflag is ok) */ + for (i = row; i > 0; i--) { + row_start = mdb_get_int16(mdb->pg_buf, (rco + i*2)); + if (!(row_start & 0x8000)) { + break; + } + } + + row_end = (i == 0) ? mdb->fmt->pg_size : row_start & OFFSET_MASK; +#endif + return row_end - 1; +} +int mdb_is_null(unsigned char *null_mask, int col_num) +{ +int byte_num = (col_num - 1) / 8; +int bit_num = (col_num - 1) % 8; + + if ((1 << bit_num) & null_mask[byte_num]) { + return 0; + } else { + return 1; + } +} +/* bool has to be handled specially because it uses the null bit to store its +** value*/ +static size_t +mdb_xfer_bound_bool(MdbHandle *mdb, MdbColumn *col, int value) +{ + col->cur_value_len = value; + if (col->bind_ptr) { + strcpy(col->bind_ptr, + value ? mdb->boolean_false_value : mdb->boolean_true_value); + } + if (col->len_ptr) { + *col->len_ptr = strlen(col->bind_ptr); + } + + return 1; +} +static size_t +mdb_xfer_bound_ole(MdbHandle *mdb, int start, MdbColumn *col, int len) +{ + size_t ret = 0; + if (len) { + col->cur_value_start = start; + col->cur_value_len = len; + } else { + col->cur_value_start = 0; + col->cur_value_len = 0; + } +#ifdef MDB_COPY_OLE + if (col->bind_ptr || col->len_ptr) { + ret = mdb_copy_ole(mdb, col->bind_ptr, start, len); + } +#else + if (col->bind_ptr) { + memcpy(col->bind_ptr, mdb->pg_buf + start, MDB_MEMO_OVERHEAD); + } + ret = MDB_MEMO_OVERHEAD; +#endif + if (col->len_ptr) { + *col->len_ptr = ret; + } + return ret; +} +static size_t +mdb_xfer_bound_data(MdbHandle *mdb, int start, MdbColumn *col, int len) +{ +int ret; + //if (!strcmp("Name",col->name)) { + //printf("start %d %d\n",start, len); + //} + if (len) { + col->cur_value_start = start; + col->cur_value_len = len; + } else { + col->cur_value_start = 0; + col->cur_value_len = 0; + } + if (col->bind_ptr) { + if (!len) { + strcpy(col->bind_ptr, ""); + } else { + //fprintf(stdout,"len %d size %d\n",len, col->col_size); + char *str; + if (col->col_type == MDB_NUMERIC) { + str = mdb_numeric_to_string(mdb, start, col->col_scale, col->col_prec); + } else if (col->col_type == MDB_DATETIME) { + if (mdb_col_is_shortdate(col)) { + str = mdb_date_to_string(mdb, mdb->shortdate_fmt, mdb->pg_buf, start); + } else { + str = mdb_date_to_string(mdb, mdb->date_fmt, mdb->pg_buf, start); + } + } else { + str = mdb_col_to_string(mdb, mdb->pg_buf, start, col->col_type, len); + } + snprintf(col->bind_ptr, mdb->bind_size, "%s", str); + g_free(str); + } + ret = strlen(col->bind_ptr); + if (col->len_ptr) { + *col->len_ptr = ret; + } + return ret; + } + return 0; +} +int mdb_read_row(MdbTableDef *table, unsigned int row) +{ + MdbHandle *mdb = table->entry->mdb; + MdbColumn *col; + unsigned int i; + int row_start; + size_t row_size = 0; + int delflag, lookupflag; + MdbField *fields; + int num_fields; + + if (table->num_cols == 0 || !table->columns) + return 0; + + if (mdb_find_row(mdb, row, &row_start, &row_size) == -1 || row_size == 0) { + /* Emitting a warning here isn't especially helpful. The row metadata + * could be bogus for a number of reasons, so just skip to the next one + * without comment. */ + // fprintf(stderr, "warning: mdb_find_row failed.\n"); + // fprintf(stderr, "warning: row_size = 0.\n"); + return 0; + } + + delflag = lookupflag = 0; + if (row_start & 0x8000) lookupflag++; + if (row_start & 0x4000) delflag++; + row_start &= OFFSET_MASK; /* remove flags */ +#if MDB_DEBUG + fprintf(stdout,"Row %d bytes %d to %d %s %s\n", + row, row_start, row_start + row_size - 1, + lookupflag ? "[lookup]" : "", + delflag ? "[delflag]" : ""); +#endif + + if (!table->noskip_del && delflag) { + return 0; + } + + fields = malloc(sizeof(MdbField) * table->num_cols); + + num_fields = mdb_crack_row(table, row_start, row_size, fields); + if (num_fields < 0 || !mdb_test_sargs(table, fields, num_fields)) { + free(fields); + return 0; + } + +#if MDB_DEBUG + fprintf(stdout,"sarg test passed row %d \n", row); +#endif + +#if MDB_DEBUG + mdb_buffer_dump(mdb->pg_buf, row_start, row_size); +#endif + + /* take advantage of mdb_crack_row() to clean up binding */ + /* use num_cols instead of num_fields -- bsb 03/04/02 */ + for (i = 0; i < table->num_cols; i++) { + col = g_ptr_array_index(table->columns,fields[i].colnum); + _mdb_attempt_bind(mdb, col, fields[i].is_null, + fields[i].start, fields[i].siz); + } + + free(fields); + + return 1; +} +static int _mdb_attempt_bind(MdbHandle *mdb, + MdbColumn *col, + unsigned char isnull, + int offset, + int len) +{ + if (col->col_type == MDB_BOOL) { + mdb_xfer_bound_bool(mdb, col, isnull); + } else if (isnull) { + mdb_xfer_bound_data(mdb, 0, col, 0); + } else if (col->col_type == MDB_OLE) { + mdb_xfer_bound_ole(mdb, offset, col, len); + } else { + //if (!mdb_test_sargs(mdb, col, offset, len)) { + //return 0; + //} + mdb_xfer_bound_data(mdb, offset, col, len); + } + return 1; +} + +/* Read next data page into mdb->pg_buf */ +int mdb_read_next_dpg(MdbTableDef *table) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + int next_pg; + +#ifndef SLOW_READ + while (1) { + next_pg = mdb_map_find_next(mdb, table->usage_map, + table->map_sz, table->cur_phys_pg); + if (next_pg < 0) + break; /* unknow map type: goto fallback */ + if (!next_pg) + return 0; + if ((guint32)next_pg == table->cur_phys_pg) + return 0; /* Infinite loop */ + + if (!mdb_read_pg(mdb, next_pg)) { + fprintf(stderr, "error: reading page %d failed.\n", next_pg); + return 0; + } + + table->cur_phys_pg = next_pg; + if (mdb->pg_buf[0]==MDB_PAGE_DATA && mdb_get_int32(mdb->pg_buf, 4)==(long)entry->table_pg) + return table->cur_phys_pg; + + /* On rare occasion, mdb_map_find_next will return a wrong page */ + /* Found in a big file, over 4,000,000 records */ + fprintf(stderr, + "warning: page %d from map doesn't match: Type=%d, buf[4..7]=%ld Expected table_pg=%ld\n", + next_pg, mdb->pg_buf[0], mdb_get_int32(mdb->pg_buf, 4), entry->table_pg); + } + fprintf(stderr, "Warning: defaulting to brute force read\n"); +#endif + /* can't do a fast read, go back to the old way */ + do { + if (!mdb_read_pg(mdb, table->cur_phys_pg++)) + return 0; + } while (mdb->pg_buf[0]!=MDB_PAGE_DATA || mdb_get_int32(mdb->pg_buf, 4)!=(long)entry->table_pg); + /* fprintf(stderr,"returning new page %ld\n", table->cur_phys_pg); */ + return table->cur_phys_pg; +} +int mdb_rewind_table(MdbTableDef *table) +{ + table->cur_pg_num=0; + table->cur_phys_pg=0; + table->cur_row=0; + + return 0; +} +int +mdb_fetch_row(MdbTableDef *table) +{ + MdbHandle *mdb = table->entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + unsigned int rows; + int rc; + guint32 pg; + + /* initialize */ + if (!table->cur_pg_num) { + table->cur_pg_num=1; + table->cur_row=0; + if ((!table->is_temp_table)&&(table->strategy!=MDB_INDEX_SCAN)) + if (!mdb_read_next_dpg(table)) return 0; + } + + do { + if (table->is_temp_table) { + GPtrArray *pages = table->temp_table_pages; + if (pages->len == 0) + return 0; + rows = mdb_get_int16( + g_ptr_array_index(pages, table->cur_pg_num-1), + fmt->row_count_offset); + if (table->cur_row >= rows) { + table->cur_row = 0; + if (++table->cur_pg_num > (unsigned int)pages->len) + return 0; + } + memcpy(mdb->pg_buf, + g_ptr_array_index(pages, table->cur_pg_num-1), + fmt->pg_size); + } else if (table->strategy==MDB_INDEX_SCAN) { + + if (!mdb_index_find_next(table->mdbidx, table->scan_idx, table->chain, &pg, (guint16 *) &(table->cur_row))) { + mdb_index_scan_free(table); + return 0; + } + mdb_read_pg(mdb, pg); + } else { + rows = mdb_get_int16(mdb->pg_buf,fmt->row_count_offset); + + /* if at end of page, find a new data page */ + if (table->cur_row >= rows) { + table->cur_row=0; + + if (!mdb_read_next_dpg(table)) { + return 0; + } + } + } + + /* printf("page %d row %d\n",table->cur_phys_pg, table->cur_row); */ + rc = mdb_read_row(table, table->cur_row); + table->cur_row++; + } while (!rc); + + return 1; +} +void mdb_data_dump(MdbTableDef *table) +{ + unsigned int i; + int ret; + char **bound_values = calloc(table->num_cols, sizeof(char *)); + + for (i=0;inum_cols;i++) { + bound_values[i] = g_malloc(MDB_BIND_SIZE); + ret = mdb_bind_column(table, i+1, bound_values[i], NULL); + if (ret == -1) { + fprintf(stderr, "error binding column %d\n", i+1); + g_free(bound_values[i]); + bound_values[i] = NULL; + } + } + mdb_rewind_table(table); + while (mdb_fetch_row(table)) { + for (i=0;inum_cols;i++) { + if (bound_values[i]) { + fprintf(stdout, "column %d is %s\n", i+1, bound_values[i]); + } + } + } + for (i=0;inum_cols;i++) { + g_free(bound_values[i]); + } + free(bound_values); +} + +int mdb_is_fixed_col(MdbColumn *col) +{ + return col->is_fixed; +} +#if 0 +static char *mdb_data_to_hex(MdbHandle *mdb, char *text, int start, int size) +{ +int i; + + for (i=start; ipg_buf[i]); + } + text[(i-start)*2]='\0'; + + return text; +} +#endif +/* + * ole_ptr should point to the original blob value of the field. + * If omited, there will be no multi-page check to that the caller is + * responsible for not calling this function. Then, it doesn't have to + * preserve the original value. + */ +size_t +mdb_ole_read_next(MdbHandle *mdb, MdbColumn *col, void *ole_ptr) +{ + guint32 ole_len; + void *buf; + int row_start; + size_t len; + + if (ole_ptr) { + ole_len = mdb_get_int32(ole_ptr, 0); + mdb_debug(MDB_DEBUG_OLE,"ole len = %d ole flags = %02x", + ole_len & 0x00ffffff, ole_len >> 24); + + if ((ole_len & 0x80000000) + || (ole_len & 0x40000000)) + /* inline or single-page fields don't have a next */ + return 0; + } + mdb_debug(MDB_DEBUG_OLE, "pg_row %d", col->cur_blob_pg_row); + if (!col->cur_blob_pg_row) + return 0; /* we are done */ + if (mdb_find_pg_row(mdb, col->cur_blob_pg_row, + &buf, &row_start, &len)) { + return 0; + } + if (len < 4) + return 0; + mdb_debug(MDB_DEBUG_OLE,"start %d len %d", row_start, len); + + if (col->bind_ptr) + memcpy(col->bind_ptr, (char*)buf + row_start + 4, len - 4); + col->cur_blob_pg_row = mdb_get_int32(buf, row_start); + + return len - 4; +} +size_t +mdb_ole_read(MdbHandle *mdb, MdbColumn *col, void *ole_ptr, size_t chunk_size) +{ + guint32 ole_len; + void *buf; + int row_start; + size_t len; + + ole_len = mdb_get_int32(ole_ptr, 0); + mdb_debug(MDB_DEBUG_OLE,"ole len = %d ole flags = %02x", + ole_len & 0x00ffffff, ole_len >> 24); + + col->chunk_size = chunk_size; + + if (ole_len & 0x80000000) { + /* inline ole field, if we can satisfy it, then do it */ + len = col->cur_value_len - MDB_MEMO_OVERHEAD; + if (chunk_size < len) + return 0; + if (col->bind_ptr) + memcpy(col->bind_ptr, &mdb->pg_buf[col->cur_value_start + + MDB_MEMO_OVERHEAD], len); + return len; + } else if (ole_len & 0x40000000) { + col->cur_blob_pg_row = mdb_get_int32(ole_ptr, 4); + mdb_debug(MDB_DEBUG_OLE,"ole row = %d ole pg = %ld", + col->cur_blob_pg_row & 0xff, + col->cur_blob_pg_row >> 8); + + if (mdb_find_pg_row(mdb, col->cur_blob_pg_row, + &buf, &row_start, &len)) { + return 0; + } + mdb_debug(MDB_DEBUG_OLE,"start %d len %d", row_start, len); + + if (col->bind_ptr) { + memcpy(col->bind_ptr, (char*)buf + row_start, len); + if (mdb_get_option(MDB_DEBUG_OLE)) + mdb_buffer_dump(col->bind_ptr, 0, 16); + } + return len; + } else if ((ole_len & 0xf0000000) == 0) { + col->cur_blob_pg_row = mdb_get_int32(ole_ptr, 4); + mdb_debug(MDB_DEBUG_OLE,"ole row = %d ole pg = %ld", + col->cur_blob_pg_row & 0xff, + col->cur_blob_pg_row >> 8); + + if (mdb_find_pg_row(mdb, col->cur_blob_pg_row, + &buf, &row_start, &len) || len < 4) { + return 0; + } + mdb_debug(MDB_DEBUG_OLE,"start %d len %d", row_start, len); + + if (col->bind_ptr) + memcpy(col->bind_ptr, (char*)buf + row_start + 4, len - 4); + col->cur_blob_pg_row = mdb_get_int32(buf, row_start); + mdb_debug(MDB_DEBUG_OLE, "next pg_row %d", col->cur_blob_pg_row); + + return len - 4; + } else { + fprintf(stderr,"Unhandled ole field flags = %02x\n", ole_len >> 24); + return 0; + } +} +/* + * mdb_ole_read_full calls mdb_ole_read then loop over mdb_ole_read_next as much as necessary. + * returns the result in a big buffer. + * The call must free it. + * Note that this function is not idempotent: It may be called only once per column after each bind. + */ +void* +mdb_ole_read_full(MdbHandle *mdb, MdbColumn *col, size_t *size) +{ + char ole_ptr[MDB_MEMO_OVERHEAD]; + char *result = malloc(OLE_BUFFER_SIZE); + size_t result_buffer_size = OLE_BUFFER_SIZE; + size_t len, pos; + + memcpy(ole_ptr, col->bind_ptr, MDB_MEMO_OVERHEAD); + + len = mdb_ole_read(mdb, col, ole_ptr, OLE_BUFFER_SIZE); + memcpy(result, col->bind_ptr, len); + pos = len; + while ((len = mdb_ole_read_next(mdb, col, ole_ptr))) { + if (pos+len >= result_buffer_size) { + result_buffer_size += OLE_BUFFER_SIZE; + if ((result = reallocf(result, result_buffer_size)) == NULL) { + fprintf(stderr, "Out of memory while reading OLE object\n"); + return NULL; + } + } + memcpy(result + pos, col->bind_ptr, len); + pos += len; + } + if (size) + *size = pos; + return result; +} + +#ifdef MDB_COPY_OLE +static size_t mdb_copy_ole(MdbHandle *mdb, void *dest, int start, int size) +{ + guint32 ole_len; + gint32 row_start, pg_row; + size_t len; + void *buf, *pg_buf = mdb->pg_buf; + + if (size> 8); + + if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) { + return 0; + } + mdb_debug(MDB_DEBUG_OLE,"row num %d start %d len %d", + pg_row & 0xff, row_start, len); + + if (dest) + memcpy(dest, buf + row_start, len); + return len; + } else if ((ole_len & 0xff000000) == 0) { // assume all flags in MSB + /* multi-page */ + int cur = 0; + pg_row = mdb_get_int32(pg_buf, start+4); + do { + mdb_debug(MDB_DEBUG_OLE,"Reading LVAL page %06x", + pg_row >> 8); + + if (mdb_find_pg_row(mdb,pg_row,&buf,&row_start,&len) || len < 4) { + return 0; + } + + mdb_debug(MDB_DEBUG_OLE,"row num %d start %d len %d", + pg_row & 0xff, row_start, len); + + if (dest) + memcpy(dest+cur, buf + row_start + 4, len - 4); + cur += len - 4; + + /* find next lval page */ + pg_row = mdb_get_int32(buf, row_start); + } while ((pg_row >> 8)); + return cur; + } else { + fprintf(stderr, "Unhandled ole field flags = %02x\n", ole_len >> 24); + return 0; + } +} +#endif +static char *mdb_memo_to_string(MdbHandle *mdb, int start, int size) +{ + guint32 memo_len; + gint32 row_start, pg_row; + size_t len; + void *buf, *pg_buf = mdb->pg_buf; + char *text = g_malloc(mdb->bind_size); + + if (sizebind_size); + return text; + } else if (memo_len & 0x40000000) { + /* single-page memo field */ + pg_row = mdb_get_int32(pg_buf, start+4); +#if MDB_DEBUG + printf("Reading LVAL page %06x\n", pg_row >> 8); +#endif + if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) { + strcpy(text, ""); + return text; + } +#if MDB_DEBUG + printf("row num %d start %d len %d\n", + pg_row & 0xff, row_start, len); + mdb_buffer_dump(buf, row_start, len); +#endif + mdb_unicode2ascii(mdb, (char*)buf + row_start, len, text, mdb->bind_size); + return text; + } else if ((memo_len & 0xff000000) == 0) { // assume all flags in MSB + /* multi-page memo field */ + guint32 tmpoff = 0; + char *tmp; + + tmp = g_malloc(memo_len); + pg_row = mdb_get_int32(pg_buf, start+4); + do { +#if MDB_DEBUG + printf("Reading LVAL page %06x\n", pg_row >> 8); +#endif + if (mdb_find_pg_row(mdb,pg_row,&buf,&row_start,&len)) { + g_free(tmp); + strcpy(text, ""); + return text; + } +#if MDB_DEBUG + printf("row num %d start %d len %d\n", + pg_row & 0xff, row_start, len); +#endif + if (tmpoff + len - 4 > memo_len) + break; + + /* Stop processing on zero length multiple page memo fields */ + if (len < 4) + break; + + memcpy(tmp + tmpoff, (char*)buf + row_start + 4, len - 4); + tmpoff += len - 4; + } while (( pg_row = mdb_get_int32(buf, row_start) )); + if (tmpoff < memo_len) { + fprintf(stderr, "Warning: incorrect memo length\n"); + } + mdb_unicode2ascii(mdb, tmp, tmpoff, text, mdb->bind_size); + g_free(tmp); + return text; + } else { + fprintf(stderr, "Unhandled memo field flags = %02x\n", memo_len >> 24); + strcpy(text, ""); + return text; + } +} + +#if 0 +static int trim_trailing_zeros(char * buff) +{ + char *p; + int n = strlen(buff); + + /* Don't need to trim strings with no decimal portion */ + if(!strchr(buff,'.')) + return 0; + + /* Trim the zeros */ + p = buff + n - 1; + while (p >= buff && *p == '0') + *p-- = '\0'; + + /* If a decimal sign is left at the end, remove it too */ + if (*p == '.') + *p = '\0'; + + return 0; +} +#endif + + +/* Date/Time is stored as a double, where the whole + part is the days from 12/30/1899 and the fractional + part is the fractional part of one day. */ + +void +mdb_tm_to_date(struct tm *t, double *td) +{ + short yr = t->tm_year + 1900; + char leap = ((yr & 3) == 0) && ((yr % 100) != 0 || (yr % 400) == 0); + const int *cal = leap ? leap_cal : noleap_cal; + long int time = (yr*365+(yr/4)-(yr/100)+(yr/400)+cal[t->tm_mon]+t->tm_mday)-693959; + + *td = (((long)t->tm_hour * 3600)+((long)t->tm_min * 60)+((long)t->tm_sec)) / 86400.0; + if (time>=0) *td+=time; else *td=time-*td; +} + +void +mdb_date_to_tm(double td, struct tm *t) +{ + long day, time; + long yr, q; + const int *cal; + + if (td < 0.0 || td > 1e6) // About 2700 AD + return; + + yr = 1; + day = (long)(td); + time = (long)((td - day) * 86400.0 + 0.5); + t->tm_hour = time / 3600; + t->tm_min = (time / 60) % 60; + t->tm_sec = time % 60; + + day += 693593; /* Days from 1/1/1 to 12/31/1899 */ + t->tm_wday = (day+1) % 7; + + q = day / 146097; /* 146097 days in 400 years */ + yr += 400 * q; + day -= q * 146097; + + q = day / 36524; /* 36524 days in 100 years */ + if (q > 3) q = 3; + yr += 100 * q; + day -= q * 36524; + + q = day / 1461; /* 1461 days in 4 years */ + yr += 4 * q; + day -= q * 1461; + + q = day / 365; /* 365 days in 1 year */ + if (q > 3) q = 3; + yr += q; + day -= q * 365; + + cal = ((yr)%4==0 && ((yr)%100!=0 || (yr)%400==0)) ? + leap_cal : noleap_cal; + for (t->tm_mon=0; t->tm_mon<12; t->tm_mon++) { + if (day < cal[t->tm_mon+1]) break; + } + t->tm_year = yr - 1900; + t->tm_mday = day - cal[t->tm_mon] + 1; + t->tm_yday = day; + t->tm_isdst = -1; +} + +static char * +mdb_date_to_string(MdbHandle *mdb, const char *fmt, void *buf, int start) +{ + struct tm t = { 0 }; + char *text = g_malloc(mdb->bind_size); + double td = mdb_get_double(buf, start); + + mdb_date_to_tm(td, &t); + + strftime(text, mdb->bind_size, mdb->date_fmt, &t); + + return text; +} + +char *mdb_uuid_to_string(const void *buf, int pos) +{ + return mdb_uuid_to_string_fmt(buf,pos,MDB_BRACES_4_2_2_8); +} + +char *mdb_uuid_to_string_fmt(const void *buf, int pos, MdbUuidFormat format) +{ + const unsigned char *kkd = (const unsigned char *)buf; + return g_strdup_printf(format == MDB_BRACES_4_2_2_8 + ? "{%02X%02X%02X%02X" "-" "%02X%02X" "-" "%02X%02X" "-" "%02X%02X%02X%02X%02X%02X%02X%02X}" + : "%02X%02X%02X%02X" "-" "%02X%02X" "-" "%02X%02X" "-" "%02X%02X" "-" "%02X%02X%02X%02X%02X%02X", + kkd[pos+3], kkd[pos+2], kkd[pos+1], kkd[pos], // little-endian + kkd[pos+5], kkd[pos+4], // little-endian + kkd[pos+7], kkd[pos+6], // little-endian + kkd[pos+8], kkd[pos+9], // big-endian + kkd[pos+10], kkd[pos+11], + kkd[pos+12], kkd[pos+13], + kkd[pos+14], kkd[pos+15]); // big-endian +} + +#if 0 +int floor_log10(double f, int is_single) +{ + unsigned int i; + double y = 10.0; + + if (f < 0.0) + f = -f; + + if ((f == 0.0) || (f == 1.0) || isinf(f)) { + return 0; + } else if (f < 1.0) { + if (is_single) { + /* The intermediate value p is necessary to prevent + * promotion of the comparison to type double */ + float p; + for (i=1; (p = f * y) < 1.0; i++) + y *= 10.0; + } else { + for (i=1; f * y < 1.0; i++) + y *= 10.0; + } + return -(int)i; + } else { /* (x > 1.0) */ + for (i=0; f >= y; i++) + y *= 10.0; + return (int)i; + } +} +#endif + +char *mdb_col_to_string(MdbHandle *mdb, void *buf, int start, int datatype, int size) +{ + char *text = NULL; + float tf; + double td; + + switch (datatype) { + case MDB_BYTE: + text = g_strdup_printf("%hhu", mdb_get_byte(buf, start)); + break; + case MDB_INT: + text = g_strdup_printf("%hd", + (short)mdb_get_int16(buf, start)); + break; + case MDB_LONGINT: + case MDB_COMPLEX: + text = g_strdup_printf("%d", + (int)mdb_get_int32(buf, start)); + break; + case MDB_FLOAT: + tf = mdb_get_single(buf, start); + text = g_strdup_printf("%.8g", tf); + break; + case MDB_DOUBLE: + td = mdb_get_double(buf, start); + text = g_strdup_printf("%.16lg", td); + break; + case MDB_BINARY: + if (size<0) { + text = g_strdup(""); + } else { + text = g_malloc(size+1); + memcpy(text, (char*)buf+start, size); + text[size] = '\0'; + } + break; + case MDB_TEXT: + if (size<0) { + text = g_strdup(""); + } else { + text = g_malloc(mdb->bind_size); + mdb_unicode2ascii(mdb, (char*)buf + start, + size, text, mdb->bind_size); + } + break; + case MDB_DATETIME: + text = mdb_date_to_string(mdb, mdb->date_fmt, buf, start); + break; + case MDB_MEMO: + text = mdb_memo_to_string(mdb, start, size); + break; + case MDB_MONEY: + text = mdb_money_to_string(mdb, start); + break; + case MDB_REPID: + text = mdb_uuid_to_string_fmt(buf, start, mdb->repid_fmt); + break; + default: + /* shouldn't happen. bools are handled specially + ** by mdb_xfer_bound_bool() */ + fprintf(stderr, "Warning: mdb_col_to_string called on unsupported data type %d.\n", datatype); + text = g_strdup(""); + break; + } + return text; +} +int mdb_col_disp_size(MdbColumn *col) +{ + switch (col->col_type) { + case MDB_BOOL: + return 1; + break; + case MDB_BYTE: + return 4; + break; + case MDB_INT: + return 6; + break; + case MDB_LONGINT: + case MDB_COMPLEX: + return 11; + break; + case MDB_FLOAT: + return 10; + break; + case MDB_DOUBLE: + return 10; + break; + case MDB_TEXT: + return col->col_size; + break; + case MDB_DATETIME: + return 20; + break; + case MDB_MEMO: + return 64000; + break; + case MDB_MONEY: + return 21; + break; + } + return 0; +} +int mdb_col_fixed_size(MdbColumn *col) +{ + switch (col->col_type) { + case MDB_BOOL: + return 1; + break; + case MDB_BYTE: + return -1; + break; + case MDB_INT: + return 2; + break; + case MDB_LONGINT: + case MDB_COMPLEX: + return 4; + break; + case MDB_FLOAT: + return 4; + break; + case MDB_DOUBLE: + return 8; + break; + case MDB_TEXT: + return -1; + break; + case MDB_DATETIME: + return 4; + break; + case MDB_BINARY: + return -1; + break; + case MDB_MEMO: + return -1; + break; + case MDB_MONEY: + return 8; + break; + } + return 0; +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/dump.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/dump.c new file mode 100644 index 0000000000..121dc1d940 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/dump.c @@ -0,0 +1,57 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000-2011 Brian Bruns and others + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +void mdb_buffer_dump(const void* buf, off_t start, size_t len) +{ + char asc[20]; + size_t j; + int k = 0; + + memset(asc, 0, sizeof(asc)); + k = 0; + for (j=0; j +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_ICONV +#include +#endif + +/* Linked from libmdb */ +const char *mdb_iconv_name_from_code_page(int code_page); + +/* string functions */ + +void *g_memdup(const void *src, size_t len) { + void *dst = malloc(len); + memcpy(dst, src, len); + return dst; +} + +int g_str_equal(const void *str1, const void *str2) { + return strcmp(str1, str2) == 0; +} + +// max_tokens not yet implemented +char **g_strsplit(const char *haystack, const char *needle, int max_tokens) { + char **ret = NULL; + char *found = NULL; + size_t components = 2; // last component + terminating NULL + + while ((found = strstr(haystack, needle))) { + components++; + haystack = found + strlen(needle); + } + + ret = calloc(components, sizeof(char *)); + + int i = 0; + while ((found = strstr(haystack, needle))) { + ret[i++] = g_strndup(haystack, found - haystack); + haystack = found + strlen(needle); + } + ret[i] = strdup(haystack); + + return ret; +} + +void g_strfreev(char **dir) { + int i=0; + while (dir[i]) { + free(dir[i]); + i++; + } + free(dir); +} + +char *g_strconcat(const char *first, ...) { + char *ret = NULL; + size_t len = strlen(first); + char *arg = NULL; + va_list argp; + + va_start(argp, first); + while ((arg = va_arg(argp, char *))) { + len += strlen(arg); + } + va_end(argp); + + ret = malloc(len+1); + + char *pos = strcpy(ret, first) + strlen(first); + + va_start(argp, first); + while ((arg = va_arg(argp, char *))) { + pos = strcpy(pos, arg) + strlen(arg); + } + va_end(argp); + + ret[len] = '\0'; + + return ret; +} + +#if defined _WIN32 && !defined(HAVE_VASPRINTF) && !defined(HAVE_VASNPRINTF) +int vasprintf(char **ret, const char *format, va_list ap) { + int len; + int retval; + char *result; + if ((len = _vscprintf(format, ap)) < 0) + return -1; + if ((result = malloc(len+1)) == NULL) + return -1; + if ((retval = vsprintf_s(result, len+1, format, ap)) == -1) { + free(result); + return -1; + } + *ret = result; + return retval; +} +#endif + +char *g_strdup(const char *input) { + size_t len = strlen(input); + return g_memdup(input, len+1); +} + +char *g_strndup(const char *src, size_t len) { + if (!src) + return NULL; + char *result = malloc(len+1); + size_t i=0; + while (*src && istr = strdup(init ? init : ""); + str->len = strlen(str->str); + str->allocated_len = str->len+1; + return str; +} + +GString *g_string_assign(GString *string, const gchar *rval) { + size_t len = strlen(rval); + string->str = realloc(string->str, len+1); + strncpy(string->str, rval, len+1); + string->len = len; + string->allocated_len = len+1; + return string; +} + +GString * g_string_append (GString *string, const gchar *val) { + size_t len = strlen(val); + string->str = realloc(string->str, string->len + len + 1); + strncpy(&string->str[string->len], val, len+1); + string->len += len; + string->allocated_len = string->len + len + 1; + return string; +} + +gchar *g_string_free (GString *string, gboolean free_segment) { + char *data = string->str; + free(string); + if (free_segment) { + free(data); + return NULL; + } + return data; +} + +/* conversion */ +gint g_unichar_to_utf8(gunichar u, gchar *dst) { + if (u >= 0x0800) { + *dst++ = 0xE0 | ((u & 0xF000) >> 12); + *dst++ = 0x80 | ((u & 0x0FC0) >> 6); + *dst++ = 0x80 | ((u & 0x003F) >> 0); + return 3; + } + if (u >= 0x0080) { + *dst++ = 0xC0 | ((u & 0x07C0) >> 6); + *dst++ = 0x80 | ((u & 0x003F) >> 0); + return 2; + } + *dst++ = (u & 0x7F); + return 1; +} + +gchar *g_locale_to_utf8(const gchar *opsysstring, size_t len, + size_t *bytes_read, size_t *bytes_written, GError **error) { + if (len == (size_t)-1) + len = strlen(opsysstring); + size_t wlen = mbstowcs(NULL, opsysstring, 0); + if (wlen == (size_t)-1) { + if (error) { + *error = malloc(sizeof(GError)); + (*error)->message = g_strdup_printf("Invalid multibyte string: %s\n", opsysstring); + } + return NULL; + } + wchar_t *utf16 = malloc(sizeof(wchar_t)*(wlen+1)); + mbstowcs(utf16, opsysstring, wlen+1); + gchar *utf8 = malloc(3*len+1); + gchar *dst = utf8; + size_t i; + for (i=0; i= 0x10000 requires surrogate pairs, ignore + dst += g_unichar_to_utf8(utf16[i], dst); + } + *dst++ = '\0'; + free(utf16); + return utf8; +} + +gchar *g_utf8_casefold(const gchar *str, gssize len) { + return g_utf8_strdown(str, len); +} + +gchar *g_utf8_strdown(const gchar *str, gssize len) { + gssize i = 0; + if (len == -1) + len = strlen(str); + gchar *lower = malloc(len+1); + while (iarray->len; i++) { + MyNode *node = g_ptr_array_index(table->array, i); + if (table->compare(key, node->key)) + return node->value; + } + return NULL; +} + +gboolean g_hash_table_lookup_extended (GHashTable *table, const void *lookup_key, + void **orig_key, void **value) { + guint i; + for (i=0; iarray->len; i++) { + MyNode *node = g_ptr_array_index(table->array, i); + if (table->compare(lookup_key, node->key)) { + *orig_key = node->key; + *value = node->value; + return TRUE; + } + } + return FALSE; +} + +void g_hash_table_insert(GHashTable *table, void *key, void *value) { + MyNode *node = calloc(1, sizeof(MyNode)); + node->value = value; + node->key = key; + g_ptr_array_add(table->array, node); +} + +gboolean g_hash_table_remove(GHashTable *table, gconstpointer key) { + int found = 0; + guint i; + for (i=0; iarray->len; i++) { + MyNode *node = g_ptr_array_index(table->array, i); + if (found) { + table->array->pdata[i-1] = table->array->pdata[i]; + } else if (!found && table->compare(key, node->key)) { + found = 1; + } + } + if (found) { + table->array->len--; + } + return found; +} + +GHashTable *g_hash_table_new(GHashFunc hashes, GEqualFunc equals) { + GHashTable *table = calloc(1, sizeof(GHashTable)); + table->array = g_ptr_array_new(); + table->compare = equals; + return table; +} + +void g_hash_table_foreach(GHashTable *table, GHFunc function, void *data) { + guint i; + for (i=0; iarray->len; i++) { + MyNode *node = g_ptr_array_index(table->array, i); + function(node->key, node->value, data); + } +} + +void g_hash_table_destroy(GHashTable *table) { + guint i; + for (i=0; iarray->len; i++) { + MyNode *node = g_ptr_array_index(table->array, i); + free(node); + } + g_ptr_array_free(table->array, TRUE); + free(table); +} + +/* GPtrArray */ + +void g_ptr_array_sort(GPtrArray *array, GCompareFunc func) { + qsort(array->pdata, array->len, sizeof(void *), func); +} + +void g_ptr_array_foreach(GPtrArray *array, GFunc function, gpointer user_data) { + guint i; + for (i=0; ilen; i++) { + function(g_ptr_array_index(array, i), user_data); + } +} + +GPtrArray *g_ptr_array_new() { + GPtrArray *array = malloc(sizeof(GPtrArray)); + array->len = 0; + array->pdata = NULL; + return array; +} + +void g_ptr_array_add(GPtrArray *array, void *entry) { + array->pdata = realloc(array->pdata, (array->len+1) * sizeof(void *)); + array->pdata[array->len++] = entry; +} + +gboolean g_ptr_array_remove(GPtrArray *array, gpointer data) { + int found = 0; + guint i; + for (i=0; ilen; i++) { + if (found) { + array->pdata[i-1] = array->pdata[i]; + } else if (!found && array->pdata[i] == data) { + found = 1; + } + } + if (found) { + array->len--; + } + return found; +} + +void g_ptr_array_free(GPtrArray *array, gboolean something) { + free(array->pdata); + free(array); +} + +/* GList */ + +GList *g_list_append(GList *list, void *data) { + GList *new_list = calloc(1, sizeof(GList)); + new_list->data = data; + new_list->next = list; + if (list) + list->prev = new_list; + return new_list; +} + +GList *g_list_last(GList *list) { + while (list && list->next) { + list = list->next; + } + return list; +} + +GList *g_list_remove(GList *list, void *data) { + GList *link = list; + while (link) { + if (link->data == data) { + GList *return_list = list; + if (link->prev) + link->prev->next = link->next; + if (link->next) + link->next->prev = link->prev; + if (link == list) + return_list = link->next; + free(link); + return return_list; + } + link = link->next; + } + return list; +} + +void g_list_free(GList *list) { + GList *next = NULL; + while (list) { + next = list->next; + free(list); + list = next; + } +} + +/* GOption */ + +void g_option_context_add_main_entries (GOptionContext *context, + const GOptionEntry *entries, + const gchar *translation_domain) { + context->entries = entries; +} + +gchar *g_option_context_get_help (GOptionContext *context, + gboolean main_help, void *group) { +#if defined(__APPLE__) || defined(__FreeBSD__) + const char * appname = getprogname(); +#elif HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME + const char * appname = program_invocation_short_name; +#else + const char * appname = "mdb-util"; +#endif + + char *help = malloc(4096); + char *end = help + 4096; + char *p = help; + p += snprintf(p, end - p, + "Usage:\n %s [OPTION\xE2\x80\xA6] %s\n\n", appname, context->desc); + p += snprintf(p, end - p, + "Help Options:\n -h, --%-20s%s\n\n", "help", "Show help options"); + p += snprintf(p, end - p, + "Application Options:\n"); + int i=0; + for (i=0; context->entries[i].long_name; i++) { + p += snprintf(p, end - p, " "); + if (context->entries[i].short_name) { + p += snprintf(p, end - p, "-%c, ", context->entries[i].short_name); + } + p += snprintf(p, end - p, "--"); + if (context->entries[i].arg_description) { + char *long_name = g_strconcat( + context->entries[i].long_name, "=", + context->entries[i].arg_description, NULL); + p += snprintf(p, end - p, "%-20s", long_name); + free(long_name); + } else { + p += snprintf(p, end - p, "%-20s", context->entries[i].long_name); + } + if (!context->entries[i].short_name) { + p += snprintf(p, end - p, " "); + } + p += snprintf(p, end - p, "%s\n", context->entries[i].description); + } + p += snprintf(p, end - p, "\n"); + return help; +} + +GOptionContext *g_option_context_new(const char *description) { + GOptionContext *ctx = calloc(1, sizeof(GOptionContext)); + ctx->desc = description; + return ctx; +} + +gboolean g_option_context_parse(GOptionContext *context, + gint *argc, gchar ***argv, GError **error) { + int i; + int count = 0; + int len = 0; + if (*argc == 2 && + (strcmp((*argv)[1], "-h") == 0 || strcmp((*argv)[1], "--help") == 0)) { + fprintf(stderr, "%s", g_option_context_get_help(context, TRUE, NULL)); + exit(0); + } + for (i=0; context->entries[i].long_name; i++) { + GOptionArg arg = context->entries[i].arg; + count++; + len++; + if (arg != G_OPTION_ARG_NONE) + len++; + } + struct option *long_opts = calloc(count+1, sizeof(struct option)); + char *short_opts = calloc(1, len+1); + int j=0; + for (i=0; ientries[i]; + GOptionArg arg = entry->arg; + short_opts[j++] = entry->short_name; + if (arg != G_OPTION_ARG_NONE) + short_opts[j++] = ':'; + long_opts[i].name = entry->long_name; + long_opts[i].has_arg = entry->arg == G_OPTION_ARG_NONE ? no_argument : required_argument; + } + int c; + int longindex = 0; + opterr = 0; + while ((c = getopt_long(*argc, *argv, short_opts, long_opts, &longindex)) != -1) { + if (c == '?') { + *error = malloc(sizeof(GError)); + if (optopt) { + (*error)->message = g_strdup_printf("Unrecognized option: -%c", optopt); + } else { + (*error)->message = g_strdup_printf("Unrecognized option: %s", (*argv)[optind-1]); + } + free(short_opts); + free(long_opts); + return FALSE; + } + const GOptionEntry *entry = NULL; + if (c == 0) { + entry = &context->entries[longindex]; + } else { + for (i=0; ientries[i].short_name == c) { + entry = &context->entries[i]; + break; + } + } + } + if (entry->arg == G_OPTION_ARG_NONE) { + *(int *)entry->arg_data = !(entry->flags & G_OPTION_FLAG_REVERSE); + } else if (entry->arg == G_OPTION_ARG_INT) { + char *endptr = NULL; + *(int *)entry->arg_data = strtol(optarg, &endptr, 10); + if (*endptr) { + *error = malloc(sizeof(GError)); + (*error)->message = malloc(100); + snprintf((*error)->message, 100, "Argument to --%s must be an integer", entry->long_name); + free(short_opts); + free(long_opts); + return FALSE; + } + } else if (entry->arg == G_OPTION_ARG_FILENAME) { + *(char **)entry->arg_data = strdup(optarg); + } else if (entry->arg == G_OPTION_ARG_STRING) { + char *result = g_locale_to_utf8(optarg, -1, NULL, NULL, error); + if (result == NULL) { + free(short_opts); + free(long_opts); + return FALSE; + } + *(char **)entry->arg_data = result; + } + } + *argc -= (optind - 1); + *argv += (optind - 1); + free(short_opts); + free(long_opts); + + return TRUE; +} + +void g_option_context_free(GOptionContext *context) { + free(context); +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/file.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/file.c new file mode 100644 index 0000000000..c6d0d832f8 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/file.c @@ -0,0 +1,501 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "mdbtools.h" +#include "mdbprivate.h" + +MdbFormatConstants MdbJet4Constants = { + .pg_size = 4096, + .row_count_offset = 0x0c, + .tab_num_rows_offset = 16, + .tab_num_cols_offset = 45, + .tab_num_idxs_offset = 47, + .tab_num_ridxs_offset = 51, + .tab_usage_map_offset = 55, + .tab_first_dpg_offset = 56, + .tab_cols_start_offset = 63, + .tab_ridx_entry_size = 12, + .col_scale_offset = 11, + .col_prec_offset = 12, + .col_flags_offset = 15, + .col_size_offset = 23, + .col_num_offset = 5, + .tab_col_entry_size = 25, + .tab_free_map_offset = 59, + .tab_col_offset_var = 7, + .tab_col_offset_fixed = 21, + .tab_row_col_num_offset = 9 +}; +MdbFormatConstants MdbJet3Constants = { + .pg_size = 2048, + .row_count_offset = 0x08, + .tab_num_rows_offset = 12, + .tab_num_cols_offset = 25, + .tab_num_idxs_offset = 27, + .tab_num_ridxs_offset = 31, + .tab_usage_map_offset = 35, + .tab_first_dpg_offset = 36, + .tab_cols_start_offset = 43, + .tab_ridx_entry_size = 8, + .col_scale_offset = 9, + .col_prec_offset = 10, + .col_flags_offset = 13, + .col_size_offset = 16, + .col_num_offset = 1, + .tab_col_entry_size = 18, + .tab_free_map_offset = 39, + .tab_col_offset_var = 3, + .tab_col_offset_fixed = 14, + .tab_row_col_num_offset = 5 +}; + +static ssize_t _mdb_read_pg(MdbHandle *mdb, void *pg_buf, unsigned long pg); + +/** + * mdb_find_file: + * @filename: path to MDB (database) file + * + * Finds and returns the absolute path to an MDB file. Function will first try + * to fstat file as passed, then search through the $MDBPATH if not found. + * + * Return value: gchar pointer to absolute path. Caller is responsible for + * freeing. + **/ + +static char *mdb_find_file(const char *file_name) +{ + struct stat status; + gchar *mdbpath, **dir, *tmpfname; + unsigned int i = 0; + + /* try the provided file name first */ + if (!stat(file_name, &status)) { + char *result; + result = g_strdup(file_name); + if (!result) + fprintf(stderr, "Can't alloc filename\n"); + return result; + } + + /* Now pull apart $MDBPATH and try those */ + mdbpath = (gchar *) getenv("MDBPATH"); + /* no path, can't find file */ + if (!mdbpath || !strlen(mdbpath)) return NULL; + + dir = g_strsplit(mdbpath, ":", 0); + while (dir[i]) { + if (!strlen(dir[i])) continue; + tmpfname = g_strconcat(dir[i++], "/", file_name, NULL); + if (!stat(tmpfname, &status)) { + g_strfreev(dir); + return tmpfname; + } + g_free(tmpfname); + } + g_strfreev(dir); + return NULL; +} + +/** + * mdb_handle_from_stream: + * @stream An open file stream + * @flags MDB_NOFLAGS for read-only, MDB_WRITABLE for read/write + * + * Allocates, initializes, and return an MDB handle from a file stream pointing + * to an MDB file. + * + * Return value: The handle on success, NULL on failure + */ +static MdbHandle *mdb_handle_from_stream(FILE *stream, MdbFileFlags flags) { + MdbHandle *mdb = g_malloc0(sizeof(MdbHandle)); + mdb_set_default_backend(mdb, "access"); + mdb_set_date_fmt(mdb, "%x %X"); + mdb_set_shortdate_fmt(mdb, "%x"); + mdb_set_bind_size(mdb, MDB_BIND_SIZE); + mdb_set_boolean_fmt_numbers(mdb); + mdb_set_repid_fmt(mdb, MDB_BRACES_4_2_2_8); +#ifdef HAVE_ICONV + mdb->iconv_in = (iconv_t)-1; + mdb->iconv_out = (iconv_t)-1; +#endif + /* need something to bootstrap with, reassign after page 0 is read */ + mdb->fmt = &MdbJet3Constants; + mdb->f = g_malloc0(sizeof(MdbFile)); + mdb->f->refs = 1; + mdb->f->stream = stream; + if (flags & MDB_WRITABLE) { + mdb->f->writable = TRUE; + } + + if (!mdb_read_pg(mdb, 0)) { + // fprintf(stderr,"Couldn't read first page.\n"); + mdb_close(mdb); + return NULL; + } + if (mdb->pg_buf[0] != 0) { + mdb_close(mdb); + return NULL; + } + mdb->f->jet_version = mdb_get_byte(mdb->pg_buf, 0x14); + switch(mdb->f->jet_version) { + case MDB_VER_JET3: + mdb->fmt = &MdbJet3Constants; + break; + case MDB_VER_JET4: + case MDB_VER_ACCDB_2007: + case MDB_VER_ACCDB_2010: + case MDB_VER_ACCDB_2013: + case MDB_VER_ACCDB_2016: + case MDB_VER_ACCDB_2019: + mdb->fmt = &MdbJet4Constants; + break; + default: + fprintf(stderr,"Unknown Jet version: %x\n", mdb->f->jet_version); + mdb_close(mdb); + return NULL; + } + + unsigned char tmp_key[4] = { 0xC7, 0xDA, 0x39, 0x6B }; + mdbi_rc4(tmp_key, sizeof(tmp_key), + mdb->pg_buf + 0x18, + mdb->f->jet_version == MDB_VER_JET3 ? 126 : 128 + ); + + if (mdb->f->jet_version == MDB_VER_JET3) { + mdb->f->lang_id = mdb_get_int16(mdb->pg_buf, 0x3a); + } else { + mdb->f->lang_id = mdb_get_int16(mdb->pg_buf, 0x6e); + } + mdb->f->code_page = mdb_get_int16(mdb->pg_buf, 0x3c); + mdb->f->db_key = mdb_get_int32(mdb->pg_buf, 0x3e); + if (mdb->f->jet_version == MDB_VER_JET3) { + /* JET4 needs additional masking with the DB creation date, currently unsupported */ + /* Bug - JET3 supports 20 byte passwords, this is currently just 14 bytes */ + memcpy(mdb->f->db_passwd, mdb->pg_buf + 0x42, sizeof(mdb->f->db_passwd)); + } + + mdb_iconv_init(mdb); + + return mdb; +} + +/** + * mdb_open_buffer: + * @buffer A memory buffer containing an MDB file + * @len Length of the buffer + * + * Opens an MDB file in memory and returns an MdbHandle to it. + * + * Return value: point to MdbHandle structure. + */ +MdbHandle *mdb_open_buffer(void *buffer, size_t len, MdbFileFlags flags) { + FILE *file = NULL; +#ifdef HAVE_FMEMOPEN + file = fmemopen(buffer, len, (flags & MDB_WRITABLE) ? "r+" : "r"); +#else + fprintf(stderr, "mdb_open_buffer requires a platform with support for fmemopen(3)\n"); +#endif + if (file == NULL) { + fprintf(stderr, "Couldn't open memory buffer\n"); + return NULL; + } + return mdb_handle_from_stream(file, flags); +} + +/** + * mdb_open: + * @filename: path to MDB (database) file + * @flags: MDB_NOFLAGS for read-only, MDB_WRITABLE for read/write + * + * Opens an MDB file and returns an MdbHandle to it. MDB File may be relative + * to the current directory, a full path to the file, or relative to a + * component of $MDBPATH. + * + * Return value: pointer to MdbHandle structure. + **/ +MdbHandle *mdb_open(const char *filename, MdbFileFlags flags) +{ + FILE *file; + + char *filepath = mdb_find_file(filename); + if (!filepath) { + fprintf(stderr, "File not found\n"); + return NULL; + } +#ifdef _WIN32 + char *mode = (flags & MDB_WRITABLE) ? "rb+" : "rb"; +#else + char *mode = (flags & MDB_WRITABLE) ? "r+" : "r"; +#endif + + if ((file = fopen(filepath, mode)) == NULL) { + fprintf(stderr,"Couldn't open file %s\n",filepath); + g_free(filepath); + return NULL; + } + + g_free(filepath); + + return mdb_handle_from_stream(file, flags); +} + +/** + * mdb_close: + * @mdb: Handle to open MDB database file + * + * Dereferences MDB file, closes if reference count is 0, and destroys handle. + * + **/ +void +mdb_close(MdbHandle *mdb) +{ + if (!mdb) return; + mdb_free_catalog(mdb); + g_free(mdb->stats); + g_free(mdb->backend_name); + + if (mdb->f) { + if (mdb->f->refs > 1) { + mdb->f->refs--; + } else { + if (mdb->f->stream) fclose(mdb->f->stream); + g_free(mdb->f); + } + } + + mdb_iconv_close(mdb); + mdb_remove_backends(mdb); + + g_free(mdb); +} +/** + * mdb_clone_handle: + * @mdb: Handle to open MDB database file + * + * Clones an existing database handle. Cloned handle shares the file descriptor + * but has its own page buffer, page position, and similar internal variables. + * + * Return value: new handle to the database. + */ +MdbHandle *mdb_clone_handle(MdbHandle *mdb) +{ + MdbHandle *newmdb; + MdbCatalogEntry *entry, *data; + unsigned int i; + + newmdb = (MdbHandle *) g_memdup2(mdb, sizeof(MdbHandle)); + + memset(&newmdb->catalog, 0, sizeof(MdbHandle) - offsetof(MdbHandle, catalog)); + + newmdb->catalog = g_ptr_array_new(); + for (i=0;inum_catalog;i++) { + entry = g_ptr_array_index(mdb->catalog,i); + data = g_memdup2(entry,sizeof(MdbCatalogEntry)); + data->mdb = newmdb; + data->props = NULL; + g_ptr_array_add(newmdb->catalog, data); + } + + mdb_iconv_init(newmdb); + mdb_set_default_backend(newmdb, mdb->backend_name); + + // formats for the source handle may have been changed from + // the backend's default formats, so we need to explicitly copy them here + mdb_set_date_fmt(newmdb, mdb->date_fmt); + mdb_set_shortdate_fmt(newmdb, mdb->shortdate_fmt); + mdb_set_repid_fmt(newmdb, mdb->repid_fmt); + + if (mdb->f) { + mdb->f->refs++; + } + + return newmdb; +} + +/* +** mdb_read a wrapper for read that bails if anything is wrong +*/ +ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg) +{ + ssize_t len; + + if (pg && mdb->cur_pg == pg) return mdb->fmt->pg_size; + + len = _mdb_read_pg(mdb, mdb->pg_buf, pg); + //fprintf(stderr, "read page %ld type %02x\n", pg, mdb->pg_buf[0]); + mdb->cur_pg = pg; + /* kan - reset the cur_pos on a new page read */ + mdb->cur_pos = 0; /* kan */ + return len; +} +ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg) +{ + return _mdb_read_pg(mdb, mdb->alt_pg_buf, pg); +} +static ssize_t _mdb_read_pg(MdbHandle *mdb, void *pg_buf, unsigned long pg) +{ + ssize_t len; + off_t offset = pg * mdb->fmt->pg_size; + + if (fseeko(mdb->f->stream, 0, SEEK_END) == -1) { + fprintf(stderr, "Unable to seek to end of file\n"); + return 0; + } + if (ftello(mdb->f->stream) < offset) { + fprintf(stderr,"offset %" PRIu64 " is beyond EOF\n",(uint64_t)offset); + return 0; + } + if (mdb->stats && mdb->stats->collect) + mdb->stats->pg_reads++; + + if (fseeko(mdb->f->stream, offset, SEEK_SET) == -1) { + fprintf(stderr, "Unable to seek to page %lu\n", pg); + return 0; + } + len = fread(pg_buf, 1, mdb->fmt->pg_size, mdb->f->stream); + if (ferror(mdb->f->stream)) { + perror("read"); + return 0; + } + memset(pg_buf + len, 0, mdb->fmt->pg_size - len); + /* + * unencrypt the page if necessary. + * it might make sense to cache the unencrypted data blocks? + */ + if (pg != 0 && mdb->f->db_key != 0) + { + uint32_t tmp_key_i = mdb->f->db_key ^ pg; + unsigned char tmp_key[4] = { + tmp_key_i & 0xFF, (tmp_key_i >> 8) & 0xFF, + (tmp_key_i >> 16) & 0xFF, (tmp_key_i >> 24) & 0xFF }; + mdbi_rc4(tmp_key, sizeof(tmp_key), pg_buf, mdb->fmt->pg_size); + } + + return mdb->fmt->pg_size; +} +void mdb_swap_pgbuf(MdbHandle *mdb) +{ +char tmpbuf[MDB_PGSIZE]; + + memcpy(tmpbuf,mdb->pg_buf, MDB_PGSIZE); + memcpy(mdb->pg_buf,mdb->alt_pg_buf, MDB_PGSIZE); + memcpy(mdb->alt_pg_buf,tmpbuf,MDB_PGSIZE); +} + + +unsigned char mdb_get_byte(void *buf, int offset) +{ + return ((unsigned char *)(buf))[offset]; +} +unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset) +{ + if (offset < 0 || offset+1 > mdb->fmt->pg_size) return -1; + mdb->cur_pos++; + return mdb->pg_buf[offset]; +} + +int mdb_get_int16(void *buf, int offset) +{ + unsigned char *u8_buf = (unsigned char *)buf + offset; + return ((uint32_t)u8_buf[0] << 0) + ((uint32_t)u8_buf[1] << 8); +} +int mdb_pg_get_int16(MdbHandle *mdb, int offset) +{ + if (offset < 0 || offset+2 > mdb->fmt->pg_size) return -1; + mdb->cur_pos+=2; + return mdb_get_int16(mdb->pg_buf, offset); +} + +long mdb_get_int32_msb(void *buf, int offset) +{ + unsigned char *u8_buf = (unsigned char *)buf + offset; + return + ((uint32_t)u8_buf[0] << 24) + + ((uint32_t)u8_buf[1] << 16) + + ((uint32_t)u8_buf[2] << 8) + + ((uint32_t)u8_buf[3] << 0); +} +long mdb_get_int32(void *buf, int offset) +{ + unsigned char *u8_buf = (unsigned char *)buf + offset; + return + ((uint32_t)u8_buf[0] << 0) + + ((uint32_t)u8_buf[1] << 8) + + ((uint32_t)u8_buf[2] << 16) + + ((uint32_t)u8_buf[3] << 24); +} +long mdb_pg_get_int32(MdbHandle *mdb, int offset) +{ + if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1; + mdb->cur_pos+=4; + return mdb_get_int32(mdb->pg_buf, offset); +} + +float mdb_get_single(void *buf, int offset) +{ + union {uint32_t g; float f;} f; + unsigned char *u8_buf = (unsigned char *)buf + offset; + f.g = ((uint32_t)u8_buf[0] << 0) + + ((uint32_t)u8_buf[1] << 8) + + ((uint32_t)u8_buf[2] << 16) + + ((uint32_t)u8_buf[3] << 24); + return f.f; +} +float mdb_pg_get_single(MdbHandle *mdb, int offset) +{ + if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1; + mdb->cur_pos+=4; + return mdb_get_single(mdb->pg_buf, offset); +} + +double mdb_get_double(void *buf, int offset) +{ + union {uint64_t g; double d;} d; + unsigned char *u8_buf = (unsigned char *)buf + offset; + d.g = ((uint64_t)u8_buf[0] << 0) + + ((uint64_t)u8_buf[1] << 8) + + ((uint64_t)u8_buf[2] << 16) + + ((uint64_t)u8_buf[3] << 24) + + ((uint64_t)u8_buf[4] << 32) + + ((uint64_t)u8_buf[5] << 40) + + ((uint64_t)u8_buf[6] << 48) + + ((uint64_t)u8_buf[7] << 56); + return d.d; +} + +double mdb_pg_get_double(MdbHandle *mdb, int offset) +{ + if (offset <0 || offset+8 > mdb->fmt->pg_size) return -1; + mdb->cur_pos+=8; + return mdb_get_double(mdb->pg_buf, offset); +} + +int +mdb_set_pos(MdbHandle *mdb, int pos) +{ + if (pos<0 || pos >= mdb->fmt->pg_size) return 0; + + mdb->cur_pos=pos; + return pos; +} +int mdb_get_pos(MdbHandle *mdb) +{ + return mdb->cur_pos; +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/iconv.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/iconv.c new file mode 100644 index 0000000000..c583c262d8 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/iconv.c @@ -0,0 +1,375 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "mdbtools.h" + +#ifndef MIN +#define MIN(a,b) (a>b ? b : a) +#endif + +static size_t decompress_unicode(const char *src, size_t slen, char *dst, size_t dlen) { + unsigned int compress=1; + size_t tlen = 0; + while (slen > 0 && tlen < dlen) { + if (*src == 0) { + compress = (compress) ? 0 : 1; + src++; + slen--; + } else if (compress) { + dst[tlen++] = *src++; + dst[tlen++] = 0; + slen--; + } else if (slen >= 2){ + dst[tlen++] = *src++; + dst[tlen++] = *src++; + slen-=2; + } else { // Odd # of bytes + break; + } + } + return tlen; +} + +#ifdef HAVE_ICONV +static size_t decompressed_to_utf8_with_iconv(MdbHandle *mdb, const char *in_ptr, size_t len_in, char *dest, size_t dlen) { + char *out_ptr = dest; + size_t len_out = dlen - 1; + + while (len_out) { + iconv(mdb->iconv_in, (ICONV_CONST char **)&in_ptr, &len_in, &out_ptr, &len_out); + /* + * Have seen database with odd number of bytes in UCS-2, shouldn't happen but protect against it + */ + if (!IS_JET3(mdb) && len_in<=1) { + //fprintf(stderr, "Detected invalid number of UCS-2 bytes\n"); + break; + } + if (!len_in || !len_out || errno == E2BIG) break; + /* Don't bail if impossible conversion is encountered */ + in_ptr += (IS_JET3(mdb)) ? 1 : 2; + len_in -= (IS_JET3(mdb)) ? 1 : 2; + *out_ptr++ = '?'; + len_out--; + } + dlen -= len_out + 1; + dest[dlen] = '\0'; + return dlen; +} +#else +static size_t latin1_to_utf8_without_iconv(const char *in_ptr, size_t len_in, char *dest, size_t dlen) { + char *out = dest; + size_t i; + for(i=0; i> 7); i++) { + unsigned char c = in_ptr[i]; + if(c & 0x80) { + *out++ = 0xC0 | (c >> 6); + *out++ = 0x80 | (c & 0x3F); + } else { + *out++ = c; + } + } + *out = '\0'; + return out - dest; +} + +static size_t unicode2ascii_locale(mdb_locale_t locale, const char *in_ptr, size_t len_in, char *dest, size_t dlen) { + size_t i; + size_t count = 0; + size_t len_out = dlen - 1; + wchar_t *w = malloc((len_in/2+1)*sizeof(wchar_t)); + + for(i=0; if->code_page == 1252) { + return latin1_to_utf8_without_iconv(in_ptr, len_in, dest, dlen); + } + int count = 0; + snprintf(dest, dlen, "%.*s%n", (int)len_in, in_ptr, &count); + return count; + } + return unicode2ascii_locale(mdb->locale, in_ptr, len_in, dest, dlen); +} +#endif + +/* + * This function is used in reading text data from an MDB table. + * 'dest' will receive a converted, null-terminated string. + * dlen is the available size of the destination buffer. + * Returns the length of the converted string, not including the terminator. + */ +int +mdb_unicode2ascii(MdbHandle *mdb, const char *src, size_t slen, char *dest, size_t dlen) +{ + char *tmp = NULL; + size_t len_in; + const char *in_ptr = NULL; + + if ((!src) || (!dest) || (!dlen)) + return 0; + + /* Uncompress 'Unicode Compressed' string into tmp */ + if (!IS_JET3(mdb) && (slen>=2) + && ((src[0]&0xff)==0xff) && ((src[1]&0xff)==0xfe)) { + tmp = g_malloc(slen*2); + len_in = decompress_unicode(src + 2, slen - 2, tmp, slen * 2); + in_ptr = tmp; + } else { + len_in = slen; + in_ptr = src; + } + +#ifdef HAVE_ICONV + dlen = decompressed_to_utf8_with_iconv(mdb, in_ptr, len_in, dest, dlen); +#else + dlen = decompressed_to_utf8_without_iconv(mdb, in_ptr, len_in, dest, dlen); +#endif + + if (tmp) g_free(tmp); + return dlen; +} + +/* + * This function is used in writing text data to an MDB table. + * If slen is 0, strlen will be used to calculate src's length. + */ +int +mdb_ascii2unicode(MdbHandle *mdb, const char *src, size_t slen, char *dest, size_t dlen) +{ + size_t len_in, len_out; + const char *in_ptr = NULL; + char *out_ptr = NULL; + + if ((!src) || (!dest) || (!dlen)) + return 0; + + in_ptr = src; + out_ptr = dest; + len_in = (slen) ? slen : strlen(in_ptr); + len_out = dlen; + +#ifdef HAVE_ICONV + iconv(mdb->iconv_out, (ICONV_CONST char **)&in_ptr, &len_in, &out_ptr, &len_out); + //printf("len_in %d len_out %d\n", len_in, len_out); + dlen -= len_out; +#else + if (IS_JET3(mdb)) { + int count; + snprintf(out_ptr, len_out, "%.*s%n", (int)len_in, in_ptr, &count); + dlen = count; + } else { + unsigned int i; + slen = MIN(len_in, len_out/2); + dlen = slen*2; + for (i=0; i4)) { + unsigned char *tmp = g_malloc(dlen); + unsigned int tptr = 0, dptr = 0; + int comp = 1; + + tmp[tptr++] = 0xff; + tmp[tptr++] = 0xfe; + while((dptr < dlen) && (tptr < dlen)) { + if (((dest[dptr+1]==0) && (comp==0)) + || ((dest[dptr+1]!=0) && (comp==1))) { + /* switch encoding mode */ + tmp[tptr++] = 0; + comp = (comp) ? 0 : 1; + } else if (dest[dptr]==0) { + /* this string cannot be compressed */ + tptr = dlen; + } else if (comp==1) { + /* encode compressed character */ + tmp[tptr++] = dest[dptr]; + dptr += 2; + } else if (tptr+1 < dlen) { + /* encode uncompressed character */ + tmp[tptr++] = dest[dptr]; + tmp[tptr++] = dest[dptr+1]; + dptr += 2; + } else { + /* could not encode uncompressed character + * into single byte */ + tptr = dlen; + } + } + if (tptr < dlen) { + memcpy(dest, tmp, tptr); + dlen = tptr; + } + g_free(tmp); + } + + return dlen; +} + +const char* +mdb_target_charset(MdbHandle *mdb) +{ +#ifdef HAVE_ICONV + const char *iconv_code = getenv("MDBICONV"); + if (!iconv_code) + iconv_code = "UTF-8"; + return iconv_code; +#else + if (!IS_JET3(mdb)) + return "ISO-8859-1"; + return NULL; // same as input: unknown +#endif +} + +/* See: https://docs.microsoft.com/en-us/windows/win32/Intl/code-page-identifiers */ +#ifdef HAVE_ICONV +static const char *mdb_iconv_name_from_code_page(int code_page) { + const char *jet3_iconv_code = NULL; + switch (code_page) { + case 437: jet3_iconv_code="IBM437"; break; + case 850: jet3_iconv_code="IBM850"; break; + case 852: jet3_iconv_code="IBM852"; break; + case 855: jet3_iconv_code="IBM855"; break; + case 860: jet3_iconv_code="IBM860"; break; + case 861: jet3_iconv_code="IBM861"; break; + case 862: jet3_iconv_code="IBM862"; break; + case 863: jet3_iconv_code="IBM863"; break; + case 864: jet3_iconv_code="IBM864"; break; + case 865: jet3_iconv_code="IBM865"; break; + case 866: jet3_iconv_code="IBM866"; break; + case 869: jet3_iconv_code="IBM869"; break; + case 874: jet3_iconv_code="WINDOWS-874"; break; + case 932: jet3_iconv_code="SHIFT-JIS"; break; + case 936: jet3_iconv_code="WINDOWS-936"; break; + case 950: jet3_iconv_code="BIG-5"; break; + case 951: jet3_iconv_code="BIG5-HKSCS"; break; + case 1200: jet3_iconv_code="UTF-16LE"; break; + case 1201: jet3_iconv_code="UTF-16BE"; break; + case 1250: jet3_iconv_code="WINDOWS-1250"; break; + case 1251: jet3_iconv_code="WINDOWS-1251"; break; + case 1252: jet3_iconv_code="WINDOWS-1252"; break; + case 1253: jet3_iconv_code="WINDOWS-1253"; break; + case 1254: jet3_iconv_code="WINDOWS-1254"; break; + case 1255: jet3_iconv_code="WINDOWS-1255"; break; + case 1256: jet3_iconv_code="WINDOWS-1256"; break; + case 1257: jet3_iconv_code="WINDOWS-1257"; break; + case 1258: jet3_iconv_code="WINDOWS-1258"; break; + case 1361: jet3_iconv_code="CP1361"; break; + case 12000: jet3_iconv_code="UTF-32LE"; break; + case 12001: jet3_iconv_code="UTF-32BE"; break; + case 20866: jet3_iconv_code="KOI8-R"; break; + case 20932: jet3_iconv_code="EUC-JP"; break; + case 21866: jet3_iconv_code="KOI8-U"; break; + case 28591: jet3_iconv_code="ISO-8859-1"; break; + case 28592: jet3_iconv_code="ISO-8859-2"; break; + case 28593: jet3_iconv_code="ISO-8859-3"; break; + case 28594: jet3_iconv_code="ISO-8859-4"; break; + case 28595: jet3_iconv_code="ISO-8859-5"; break; + case 28596: jet3_iconv_code="ISO-8859-6"; break; + case 28597: jet3_iconv_code="ISO-8859-7"; break; + case 28598: jet3_iconv_code="ISO-8859-8"; break; + case 28599: jet3_iconv_code="ISO-8859-9"; break; + case 28503: jet3_iconv_code="ISO-8859-13"; break; + case 28505: jet3_iconv_code="ISO-8859-15"; break; + case 51932: jet3_iconv_code="EUC-JP"; break; + case 51936: jet3_iconv_code="EUC-CN"; break; + case 51949: jet3_iconv_code="EUC-KR"; break; + case 65000: jet3_iconv_code="UTF-7"; break; + case 65001: jet3_iconv_code="UTF-8"; break; + default: break; + } + return jet3_iconv_code; +} +#endif + +void mdb_iconv_init(MdbHandle *mdb) +{ + const char *iconv_code; + + /* check environment variable */ + if (!(iconv_code=getenv("MDBICONV"))) { + iconv_code="UTF-8"; + } + +#ifdef HAVE_ICONV + if (!IS_JET3(mdb)) { + mdb->iconv_out = iconv_open("UCS-2LE", iconv_code); + mdb->iconv_in = iconv_open(iconv_code, "UCS-2LE"); + } else { + /* check environment variable */ + const char *jet3_iconv_code = getenv("MDB_JET3_CHARSET"); + + if (!jet3_iconv_code) { + /* Use code page embedded in the database */ + /* Note that individual columns can override this value, + * but per-column code pages are not supported by libmdb */ + jet3_iconv_code = mdb_iconv_name_from_code_page(mdb->f->code_page); + } + if (!jet3_iconv_code) { + jet3_iconv_code = "CP1252"; + } + + mdb->iconv_out = iconv_open(jet3_iconv_code, iconv_code); + mdb->iconv_in = iconv_open(iconv_code, jet3_iconv_code); + } +#elif defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || defined(WINDOWS) + mdb->locale = _create_locale(LC_CTYPE, ".65001"); +#else + mdb->locale = newlocale(LC_CTYPE_MASK, "C.UTF-8", NULL); +#endif +} +void mdb_iconv_close(MdbHandle *mdb) +{ +#ifdef HAVE_ICONV + if (mdb->iconv_out != (iconv_t)-1) iconv_close(mdb->iconv_out); + if (mdb->iconv_in != (iconv_t)-1) iconv_close(mdb->iconv_in); +#elif defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || defined(WINDOWS) + if (mdb->locale) _free_locale(mdb->locale); +#else + if (mdb->locale) freelocale(mdb->locale); +#endif +} + + diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/index.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/index.c new file mode 100644 index 0000000000..d25a4150bc --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/index.c @@ -0,0 +1,1150 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000-2004 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mdbtools.h" +#include "mdbprivate.h" +#ifdef HAVE_LIBMSWSTR +#include +#endif + +static MdbIndexPage *mdb_index_read_bottom_pg(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain); +static MdbIndexPage *mdb_chain_add_page(MdbHandle *mdb, MdbIndexChain *chain, guint32 pg); + +char idx_to_text[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0-7 0x00-0x07 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8-15 0x09-0x0f */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 16-23 0x10-0x17 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24-31 0x19-0x1f */ +' ', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 32-39 0x20-0x27 */ +0x00, 0x00, 0x00, 0x00, 0x00, ' ', ' ', 0x00, /* 40-47 0x29-0x2f */ +'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', /* 48-55 0x30-0x37 */ +'^', '_', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 56-63 0x39-0x3f */ +0x00, '`', 'a', 'b', 'd', 'f', 'g', 'h', /* 64-71 0x40-0x47 */ +'i', 'j', 'k', 'l', 'm', 'o', 'p', 'r', /* 72-79 0x49-0x4f H */ +'s', 't', 'u', 'v', 'w', 'x', 'z', '{', /* 80-87 0x50-0x57 P */ +'|', '}', '~', '5', '6', '7', '8', '9', /* 88-95 0x59-0x5f */ +0x00, '`', 'a', 'b', 'd', 'f', 'g', 'h', /* 96-103 0x60-0x67 */ +'i', 'j', 'k', 'l', 'm', 'o', 'p', 'r', /* 014-111 0x69-0x6f h */ +'s', 't', 'u', 'v', 'w', 'x', 'z', '{', /* 112-119 0x70-0x77 p */ +'|', '}', '~', 0x00, 0x00, 0x00, 0x00, 0x00, /* 120-127 0x78-0x7f */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128-135 0x80-0x87 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */ +0x00, 0x00, 0x00, 0x00, 0x00, '`', 0x00, 0x00, /* 0xc0-0xc7 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */ +0x00, '`', 0x00, '`', '`', '`', 0x00, 0x00, /* 0xe0-0xe7 */ +'f', 'f', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */ +0x00, 0x00, 0x00, 'r', 0x00, 0x00, 'r', 0x00, /* 0xf0-0xf7 */ +0x81, 0x00, 0x00, 0x00, 'x', 0x00, 0x00, 0x00, /* 0xf8-0xff */ +}; + +/* This table doesn't really work accurately, as it is missing + * a lot of special processing, therefore do not use! + * This is just some kind of fallback if MSWSTR cannot be used + * for whatever reason and may not work for most indexes, i.e. + * those containing hyphens etc. + */ +char idx_to_text_ling[] = { +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 0-7 0x00-0x07 */ +0x01, 0x08, 0x08, 0x08, 0x08, 0x08, 0x01, 0x01, /* 8-15 0x08-0x0F */ +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 16-23 0x10-0x17 */ +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 24-31 0x18-0x1F */ +0x07, 0x09, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x01, /* 32-39 0x20-0x27 */ +0x14, 0x16, 0x18, ',', 0x1A, 0x01, 0x1C, 0x1E, /* 40-47 0x28-0x2F */ + '6', '8', ':', '<', '>', '@', 'B', 'D', /* 48-55 0x30-0x37 */ + 'F', 'H', ' ', '"', '.', '0', '2', '$', /* 56-63 0x38-0x3F */ + '&', 'J', 'L', 'M', 'O', 'Q', 'S', 'U', /* 64-71 0x40-0x47 */ + 'W', 'Y', '[', '\\', '^', '`', 'b', 'd', /* 72-79 0x48-0x4F */ + 'f', 'h', 'i', 'k', 'm', 'o', 'q', 's', /* 80-87 0x50-0x57 */ + 'u', 'v', 'x', '\'', ')', '*', '+', '+', /* 88-95 0x58-0x5F */ + '+', 'J', 'L', 'M', 'O', 'Q', 'S', 'U', /* 96-103 0x60-0x67 */ + 'W', 'Y', '[', '\\', '^', '`', 'b', 'd', /* 104-111 0x68-0x6F */ + 'f', 'h', 'i', 'k', 'm', 'o', 'q', 's', /* 112-119 0x70-0x77 */ + 'u', 'v', 'x', '+', '+', '+', '+', 0x01, /* 120-127 0x78-0x7F */ +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 128-135 0x80-0x87 */ +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 136-143 0x88-0x8F */ +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 144-151 0x90-0x97 */ +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 152-159 0x98-0x9F */ +0x08, '+', '4', '4', '4', '4', '+', '4', /* 160-167 0xA0-0xA7 */ + '+', '4', 'J', '3', '4', 0x01, '4', '+', /* 168-175 0xA8-0xAF */ + '4', '3', ':', '<', '+', '4', '4', '4', /* 176-183 0xB0-0xB7 */ + '+', '8', 'd', '3', '7', '7', '7', '+', /* 184-191 0xB8-0xBF */ + 'J', 'J', 'J', 'J', 'J', 'J', 'J', 'M', /* 192-199 0xC0-0xC7 */ + 'Q', 'Q', 'Q', 'Q', 'Y', 'Y', 'Y', 'Y', /* 200-207 0xC8-0xCF */ + 'O', 'b', 'd', 'd', 'd', 'd', 'd', '3', /* 208-215 0xD0-0xD7 */ + 'd', 'o', 'o', 'o', 'o', 'v', 'm', 'k', /* 216-223 0xD8-0xDF */ + 'J', 'J', 'J', 'J', 'J', 'J', 'J', 'M', /* 224-231 0xE0-0xE7 */ + 'Q', 'Q', 'Q', 'Q', 'Y', 'Y', 'Y', 'Y', /* 232-239 0xE8-0xEF */ + 'O', 'b', 'd', 'd', 'd', 'd', 'd', '3', /* 240-247 0xF0-0xF7 */ + 'd', 'o', 'o', 'o', 'o', 'v', 'm', 'v', /* 248-255 0xF8-0xFF */ +}; + +/* JET Red (v4) Index definition byte layouts + * + * Based on: + * + * http://jabakobob.net/mdb/table-page.html + * https://github.com/jahlborn/jackcess + * + * plus inspection of JET (Red) 4 databases. (JET 3 format has fewer + * fields -- some of the ones below omitted, and others narrower.) + * + * See also JET Blue (Extensible Storage Engine) format information: + * + * https://github.com/libyal/libesedb/blob/master/documentation/Extensible%20Storage%20Engine%20%28ESE%29%20Database%20File%20%28EDB%29%20format.asciidoc + * + * which is a later Microsoft embedded database format with the same + * early base format. + * + * ---------------------------------------------------------------------- + * Index Column Definitions: + * - for each "non foreign key" index (ie pidx->index_type!=2), a list + * of columns indexed + * + * Repeated table->num_real_idxs times: + * + * Offset Bytes Meaning + * 0x0000 4 UNKNOWN; seems to be type marker, usually 1923 or 0 + * + * 0x0004 2 Column 1 ID + * 0x0006 1 Column 1 Flags + * 0x0007 2 Column 2 ID + * 0x0009 1 Column 2 Flags + * 0x000A 2 Column 3 ID + * 0x000C 1 Column 3 Flags + * 0x000D 2 Column 4 ID + * 0x000F 1 Column 4 Flags + * 0x0010 2 Column 5 ID + * 0x0012 1 Column 5 Flags + * 0x0013 2 Column 6 ID + * 0x0015 1 Column 6 Flags + * 0x0016 2 Column 7 ID + * 0x0018 1 Column 7 Flags + * 0x0019 2 Column 8 ID + * 0x001B 1 Column 8 Flags + * 0x001C 2 Column 9 ID + * 0x001E 1 Column 9 Flags + * 0x001F 2 Column 10 ID + * 0x0021 1 Column 10 Flags + * + * 0x0022 1 Usage Map row + * 0x0023 3 Usage Map page (24-bit) + * 0x0026 4 First index page + * 0x002A 4 UNKNOWN + * 0x002E 2 Index Flags + * 0x0030 4 UNKNOWN; seems to always be 0 + * 0x0034 + * + * Column ID of 0xFFFF (-1) means "not used" or "end of used columns". + * Column Flags: + * - 0x01 = Ascending + * + * Index Flags: + * - 0x0001 = Unique index + * - 0x0002 = Ignore NULLs + * - 0x0008 = Required Index + * + * ---------------------------------------------------------------------- + * Index Definitions + * - for each index (normal, primary key, foreign key), details on the + * index. + * + * - this appears to be the union of information required for normal/ + * primary key indexes, and the information required for foreign key + * indexes. + * + * Repeated table->num_idxs times: + * + * Offset Bytes Meaning + * 0x0000 4 UNKNOWN; apparently a type marker, usually 1625 or 0 + * 0x0004 4 Logical Index Number + * 0x0008 4 Index Column Definition Entry + * 0x000C 1 FK Index Type + * 0x000D 4 FK Index Number + * 0x0011 4 FK Index Table Page Number + * 0x0015 1 Flags: Update Action + * 0x0016 1 Flags: Delete Action + * 0x0017 1 Index Type + * 0x0018 4 UNKNNOWN; seems to always be 0 + * 0x001B + * + * Where Index Type is: + * 0x01 = normal + * 0x01 = primary key + * 0x02 = foreign key index reference + */ + +/* Debugging helper to dump out raw hex values of index definition */ +/* +static void hexdump(unsigned char *tmpbuf, int size) { + mdb_buffer_dump(tmpbuf, 0, size); +} +*/ + + +GPtrArray * +mdb_read_indices(MdbTableDef *table) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + MdbIndex *pidx; + unsigned int i, j, k; + int key_num, col_num, cleaned_col_num; + int cur_pos, name_sz, idx2_sz, type_offset; + int index_start_pg = mdb->cur_pg; + gchar *tmpbuf; + + table->indices = g_ptr_array_new(); + + if (IS_JET3(mdb)) { + cur_pos = table->index_start + 39 * table->num_real_idxs; + idx2_sz = 20; + type_offset = 19; + } else { + cur_pos = table->index_start + 52 * table->num_real_idxs; + idx2_sz = 28; + type_offset = 23; + } + + /* Read in the definitions of table indexes, into table->indices */ + + /* num_real_idxs should be the number of indexes other than type 2. + * It's not always the case. Happens on Northwind Orders table. + */ + //fprintf(stderr, "num_idxs:%d num_real_idxs:%d\n", table->num_idxs, table->num_real_idxs); + + unsigned int num_idxs_type_other_than_2 = 0; + tmpbuf = g_malloc(idx2_sz); + for (i=0;inum_idxs;i++) { + if (!read_pg_if_n(mdb, tmpbuf, &cur_pos, idx2_sz)) { + g_free(tmpbuf); + mdb_free_indices(table->indices); + return table->indices = NULL; + } + //fprintf(stderr, "Index defn: "); + //hexdump((unsigned char *)tmpbuf, idx2_sz); + pidx = g_malloc0(sizeof(MdbIndex)); + pidx->table = table; + pidx->index_num = mdb_get_int16(tmpbuf, 4); + pidx->index_type = tmpbuf[type_offset]; + g_ptr_array_add(table->indices, pidx); + /* + { + gint32 idx_marker = mdb_get_int32(tmpbuf, 0); + gint32 index_col_def_num = mdb_get_int16(tmpbuf, 8); + gint8 rel_idx_type = tmpbuf[0x0c]; + gint32 rel_idx_number = mdb_get_int32(tmpbuf, 0x0d); + gint32 rel_idx_page = mdb_get_int32(tmpbuf, 0x11); + gint8 update_action_flags = tmpbuf[0x15]; + gint8 delete_action_flags = tmpbuf[0x16]; + fprintf(stderr, "idx #%d: num2:%d num3:%d type:%d\n", i, pidx->index_num, index_col_def_num, pidx->index_type); + fprintf(stderr, "idx #%d: %d %d %d %d %d/%d\n", i, idx_marker, rel_idx_type, rel_idx_number, rel_idx_page, update_action_flags, delete_action_flags); + }*/ + if (pidx->index_type!=2) + num_idxs_type_other_than_2++; + } + if (num_idxs_type_other_than_2num_real_idxs) + table->num_real_idxs=num_idxs_type_other_than_2; + + //fprintf(stderr, "num_idxs:%d num_real_idxs:%d\n", table->num_idxs, table->num_real_idxs); + g_free(tmpbuf); + + /* Pick up the names of each index */ + for (i=0;inum_idxs;i++) { + pidx = g_ptr_array_index (table->indices, i); + if (IS_JET3(mdb)) { + name_sz=read_pg_if_8(mdb, &cur_pos); + } else { + name_sz=read_pg_if_16(mdb, &cur_pos); + } + tmpbuf = g_malloc(name_sz); + if (read_pg_if_n(mdb, tmpbuf, &cur_pos, name_sz)) + mdb_unicode2ascii(mdb, tmpbuf, name_sz, pidx->name, sizeof(pidx->name)); + g_free(tmpbuf); + //fprintf(stderr, "index %d type %d name %s\n", pidx->index_num, pidx->index_type, pidx->name); + } + + /* Pick up the column definitions for normal/primary key indexes */ + /* NOTE: Match should possibly be by index_col_def_num, rather + * than index_num; but in files encountered both seem to be the + * same (so left with index_num until a counter example is found). + */ + mdb_read_alt_pg(mdb, entry->table_pg); + mdb_read_pg(mdb, index_start_pg); + cur_pos = table->index_start; + for (i=0;inum_real_idxs;i++) { + /* Debugging print out, commented out + { + gchar *tmpbuf = (gchar *) g_malloc(0x34); + int now_pos = cur_pos; + read_pg_if_n(mdb, tmpbuf, &now_pos, 0x34); + fprintf(stderr, "Index defn: "); + hexdump((unsigned char *)tmpbuf, 0x34); + g_free(tmpbuf); + }*/ + + if (!IS_JET3(mdb)) cur_pos += 4; + /* look for index number i */ + for (j=0; jnum_idxs; ++j) { + pidx = g_ptr_array_index (table->indices, j); + if (pidx->index_type!=2 && (unsigned int)pidx->index_num==i) + break; + } + if (j==table->num_idxs) { + fprintf(stderr, "ERROR: can't find index #%d.\n", i); + continue; + } + //fprintf(stderr, "index %d #%d (%s) index_type:%d\n", i, pidx->index_num, pidx->name, pidx->index_type); + + pidx->num_rows = mdb_get_int32(mdb->alt_pg_buf, + fmt->tab_cols_start_offset + + (pidx->index_num*fmt->tab_ridx_entry_size)); + /* + fprintf(stderr, "ridx block1 i:%d data1:0x%08x data2:0x%08x\n", + i, + (unsigned int)mdb_get_int32(mdb->pg_buf, + fmt->tab_cols_start_offset + pidx->index_num * fmt->tab_ridx_entry_size), + (unsigned int)mdb_get_int32(mdb->pg_buf, + fmt->tab_cols_start_offset + pidx->index_num * fmt->tab_ridx_entry_size +4)); + fprintf(stderr, "pidx->num_rows:%d\n", pidx->num_rows);*/ + + /* Read columns in each index */ + key_num=0; + for (j=0;jnum_cols; k++) { + MdbColumn *col = g_ptr_array_index(table->columns,k); + if (col->col_num == col_num) { + cleaned_col_num = k; + break; + } + } + if (cleaned_col_num==-1) { + fprintf(stderr, "CRITICAL: can't find column with internal id %d in index %s\n", + col_num, pidx->name); + cur_pos++; + continue; + } + /* set column number to a 1 based column number and store */ + pidx->key_col_num[key_num] = cleaned_col_num + 1; + pidx->key_col_order[key_num] = + (read_pg_if_8(mdb, &cur_pos)) ? MDB_ASC : MDB_DESC; + //fprintf(stderr, "component %d using column #%d (internally %d)\n", j, cleaned_col_num, col_num); + key_num++; + } + pidx->num_keys = key_num; + + if (0) // DEBUGGING ONLY + { + gint32 usage_map = read_pg_if_32(mdb, &cur_pos); + fprintf(stderr, "pidx->unknown_pre_first_pg:0x%08x\n", usage_map); + } else { + cur_pos += 4; // Skip Usage map information + } + pidx->first_pg = read_pg_if_32(mdb, &cur_pos); + + if (!IS_JET3(mdb)) cur_pos += 4; + + pidx->flags = read_pg_if_8(mdb, &cur_pos); + //fprintf(stderr, "pidx->first_pg:%d pidx->flags:0x%02x\n", pidx->first_pg, pidx->flags); + if (!IS_JET3(mdb)) cur_pos += 5; + } + return NULL; +} +void +mdb_index_hash_text(MdbHandle *mdb, char *text, char *hash) +{ + unsigned int k, len=strlen(text); + char *transtbl=NULL; + + if (!IS_JET3(mdb)) + { +#ifdef __MSWSTR_H__ + char *out_ptr = alloca((len+1)*2); + unsigned int i; + // mdb_ascii2unicode doesn't work, we don't want unicode compression! + for (i=0; if->lang_id, 0), + LCMAP_LINGUISTIC_CASING | LCMAP_SORTKEY | NORM_IGNORECASE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH, + (WCHAR*)out_ptr, len, (LPBYTE)hash, len*2))) + { + len++; +#endif + transtbl = idx_to_text_ling; +#ifdef __MSWSTR_H__ + } +#endif + } + else + { + transtbl = idx_to_text; + } + if (transtbl) + { + for (k=0;k %s (%d -> %d)\n", text, hash, len, k); +} +/* + * reverse the order of the column for hashing + */ +void +mdb_index_swap_n(unsigned char *src, int sz, unsigned char *dest) +{ + int i, j = 0; + + for (i = sz-1; i >= 0; i--) { + dest[j++] = src[i]; + } +} +void +mdb_index_cache_sarg(MdbColumn *col, MdbSarg *sarg, MdbSarg *idx_sarg) +{ + //guint32 cache_int; + unsigned char *c; + + switch (col->col_type) { + case MDB_TEXT: + mdb_index_hash_text(col->table->mdbidx, sarg->value.s, idx_sarg->value.s); + break; + + case MDB_LONGINT: + idx_sarg->value.i = GUINT32_SWAP_LE_BE(sarg->value.i); + //cache_int = sarg->value.i * -1; + c = (unsigned char *) &(idx_sarg->value.i); + c[0] |= 0x80; + //printf("int %08x %02x %02x %02x %02x\n", sarg->value.i, c[0], c[1], c[2], c[3]); + break; + + case MDB_INT: + break; + + default: + break; + } +} +#if 0 +int +mdb_index_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSarg *sarg, int offset, int len) +{ +char tmpbuf[256]; +int lastchar; + + switch (col->col_type) { + case MDB_BYTE: + return mdb_test_int(sarg, mdb_pg_get_byte(mdb, offset)); + break; + case MDB_INT: + return mdb_test_int(sarg, mdb_pg_get_int16(mdb, offset)); + break; + case MDB_LONGINT: + return mdb_test_int(sarg, mdb_pg_get_int32(mdb, offset)); + break; + case MDB_TEXT: + strncpy(tmpbuf, &mdb->pg_buf[offset],255); + lastchar = len > 255 ? 255 : len; + tmpbuf[lastchar]='\0'; + return mdb_test_string(sarg, tmpbuf); + default: + fprintf(stderr, "Calling mdb_test_sarg on unknown type. Add code to mdb_test_sarg() for type %d\n",col->col_type); + break; + } + return 1; +} +#endif +int +mdb_index_test_sargs(MdbHandle *mdb, MdbIndex *idx, char *buf, int len) +{ + unsigned int i, j; + MdbColumn *col; + MdbTableDef *table = idx->table; + MdbSarg *idx_sarg; + MdbSarg *sarg; + MdbField field; + MdbSargNode node; + //int c_offset = 0, + int c_len; + + //fprintf(stderr,"mdb_index_test_sargs called on "); + //mdb_buffer_dump(buf, 0, len); + //fprintf(stderr,"\n"); + for (i=0;inum_keys;i++) { + //c_offset++; /* the per column null indicator/flags */ + col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1); + /* + * This will go away eventually + */ + if (col->col_type==MDB_TEXT) { + //c_len = strlen(&mdb->pg_buf[offset + c_offset]); + c_len = strlen(buf); + } else { + c_len = col->col_size; + //fprintf(stderr,"Only text types currently supported. How did we get here?\n"); + } + /* + * If we have no cached index values for this column, + * create them. + */ + if (col->num_sargs && !col->idx_sarg_cache) { + col->idx_sarg_cache = g_ptr_array_new(); + for (j=0;jnum_sargs;j++) { + sarg = g_ptr_array_index (col->sargs, j); + idx_sarg = g_memdup2(sarg,sizeof(MdbSarg)); + //printf("calling mdb_index_cache_sarg\n"); + mdb_index_cache_sarg(col, sarg, idx_sarg); + g_ptr_array_add(col->idx_sarg_cache, idx_sarg); + } + } + + for (j=0;jnum_sargs;j++) { + sarg = g_ptr_array_index (col->idx_sarg_cache, j); + /* XXX - kludge */ + node.op = sarg->op; + node.value = sarg->value; + //field.value = &mdb->pg_buf[offset + c_offset]; + field.value = buf; + field.siz = c_len; + field.is_null = FALSE; + /* In Jet 4 Index text hashes don't need to be converted from Unicode */ + if (!IS_JET3(mdb) && col->col_type == MDB_TEXT) + { + if (!mdb_test_string(&node, buf)) return 0; + } + else if (!mdb_test_sarg(mdb, col, &node, &field)) { + /* sarg didn't match, no sense going on */ + return 0; + } + } + } + return 1; +} +/* + * pack the pages bitmap + */ +int +mdb_index_pack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg) +{ + int mask_bit = 0; + int mask_pos = IS_JET3(mdb)?0x16:0x1b; + int mask_byte = 0; + int elem = 0; + int len, start, i; + + start = ipg->idx_starts[elem++]; + + while (start) { + //fprintf(stdout, "elem %d is %d\n", elem, ipg->idx_starts[elem]); + len = ipg->idx_starts[elem] - start; + //fprintf(stdout, "len is %d\n", len); + for (i=0; i < len; i++) { + mask_bit++; + if (mask_bit==8) { + mask_bit=0; + mdb->pg_buf[mask_pos++] = mask_byte; + mask_byte = 0; + } + /* upon reaching the len, set the bit */ + } + mask_byte = (1 << mask_bit) | mask_byte; + //fprintf(stdout, "mask byte is %02x at %d\n", mask_byte, mask_pos); + start = ipg->idx_starts[elem++]; + } + /* flush the last byte if any */ + mdb->pg_buf[mask_pos++] = mask_byte; + /* remember to zero the rest of the bitmap */ + for (i = mask_pos; i < 0xf8; i++) { + mdb->pg_buf[mask_pos++] = 0; + } + return 0; +} +/* + * unpack the pages bitmap + */ +int +mdb_index_unpack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg) +{ + int mask_bit = 0; + int mask_pos = IS_JET3(mdb)?0x16:0x1b; + int mask_byte; + int jet_start = IS_JET3(mdb)?0xf8:0x1e0; + int start = jet_start; + int elem = 0; + int len = 0; + + ipg->idx_starts[elem++]=start; + + //fprintf(stdout, "Unpacking index page %lu\n", ipg->pg); + do { + len = 0; + do { + mask_bit++; + if (mask_bit==8) { + mask_bit=0; + mask_pos++; + } + mask_byte = mdb->pg_buf[mask_pos]; + len++; + } while (mask_pos <= jet_start && !((1 << mask_bit) & mask_byte)); + //fprintf(stdout, "%d %d %d %d\n", mask_pos, mask_bit, mask_byte, len); + + start += len; + if (mask_pos < jet_start) ipg->idx_starts[elem++]=start; + + } while (mask_pos < jet_start); + + /* if we zero the next element, so we don't pick up the last pages starts*/ + ipg->idx_starts[elem]=0; + + return elem; +} +/* + * find the next entry on a page (either index or leaf). Uses state information + * stored in the MdbIndexPage across calls. + */ +int +mdb_index_find_next_on_page(MdbHandle *mdb, MdbIndexPage *ipg) +{ + if (!ipg->pg) return 0; + + /* if this page has not been unpacked to it */ + if (!ipg->idx_starts[0]){ + //fprintf(stdout, "Unpacking page %d\n", ipg->pg); + mdb_index_unpack_bitmap(mdb, ipg); + } + + + if (ipg->idx_starts[ipg->start_pos + 1]==0) return 0; + ipg->len = ipg->idx_starts[ipg->start_pos+1] - ipg->idx_starts[ipg->start_pos]; + ipg->start_pos++; + //fprintf(stdout, "Start pos %d\n", ipg->start_pos); + + return ipg->len; +} +void mdb_index_page_reset(MdbHandle *mdb, MdbIndexPage *ipg) +{ + ipg->offset = IS_JET3(mdb)?0xf8:0x1e0; /* start byte of the index entries */ + ipg->start_pos=0; + ipg->len = 0; + ipg->idx_starts[0]=0; +} +void mdb_index_page_init(MdbHandle *mdb, MdbIndexPage *ipg) +{ + memset(ipg, 0, sizeof(MdbIndexPage)); + mdb_index_page_reset(mdb, ipg); +} +/* + * find the next leaf page if any given a chain. Assumes any exhausted leaf + * pages at the end of the chain have been peeled off before the call. + */ +MdbIndexPage * +mdb_find_next_leaf(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain) +{ + MdbIndexPage *ipg, *newipg; + guint32 pg; + guint passed = 0; + + ipg = mdb_index_read_bottom_pg(mdb, idx, chain); + + /* + * If we are at the first page deep and it's not an index page then + * we are simply done. (there is no page to find + */ + + if (mdb->pg_buf[0]==MDB_PAGE_LEAF) { + /* Indexes can have leaves at the end that don't appear + * in the upper tree, stash the last index found so + * we can follow it at the end. */ + chain->last_leaf_found = ipg->pg; + return ipg; + } + + /* + * apply sargs here, currently we don't + */ + do { + ipg->len = 0; + //printf("finding next on pg %lu\n", ipg->pg); + if (!mdb_index_find_next_on_page(mdb, ipg)) { + //printf("find_next_on_page returned 0\n"); + return 0; + } + pg = mdb_get_int32_msb(mdb->pg_buf, ipg->offset + ipg->len - 3) >> 8; + //printf("Looking at pg %lu at %lu %d\n", pg, ipg->offset, ipg->len); + ipg->offset += ipg->len; + + /* + * add to the chain and call this function + * recursively. + */ + if (!mdb_chain_add_page(mdb, chain, pg)) + break; + newipg = mdb_find_next_leaf(mdb, idx, chain); + //printf("returning pg %lu\n",newipg->pg); + return newipg; + } while (!passed); + /* no more pages */ + return NULL; + +} +static MdbIndexPage * +mdb_chain_add_page(MdbHandle *mdb, MdbIndexChain *chain, guint32 pg) +{ + MdbIndexPage *ipg; + + chain->cur_depth++; + if (chain->cur_depth > MDB_MAX_INDEX_DEPTH) { + fprintf(stderr,"Error! maximum index depth of %d exceeded. This is probably due to a programming bug, If you are confident that your indexes really are this deep, adjust MDB_MAX_INDEX_DEPTH in mdbtools.h and recompile.\n", MDB_MAX_INDEX_DEPTH); + return NULL; + } + ipg = &(chain->pages[chain->cur_depth - 1]); + mdb_index_page_init(mdb, ipg); + ipg->pg = pg; + + return ipg; +} +/* + * returns the bottom page of the IndexChain, if IndexChain is empty it + * initializes it by reading idx->first_pg (the root page) + */ +static MdbIndexPage * +mdb_index_read_bottom_pg(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain) +{ + MdbIndexPage *ipg; + + /* + * if it's new use the root index page (idx->first_pg) + */ + if (!chain->cur_depth) { + ipg = &(chain->pages[0]); + mdb_index_page_init(mdb, ipg); + chain->cur_depth = 1; + ipg->pg = idx->first_pg; + if (!(ipg = mdb_find_next_leaf(mdb, idx, chain))) + return 0; + } else { + ipg = &(chain->pages[chain->cur_depth - 1]); + ipg->len = 0; + } + + mdb_read_pg(mdb, ipg->pg); + + return ipg; +} +/* + * unwind the stack and search for new leaf node + */ +MdbIndexPage * +mdb_index_unwind(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain) +{ + MdbIndexPage *ipg; + + //printf("page %lu finished\n",ipg->pg); + if (chain->cur_depth==1) { + //printf("cur_depth == 1 we're out\n"); + return NULL; + } + /* + * unwind the stack until we find something or reach + * the top. + */ + ipg = NULL; + while (chain->cur_depth>1 && ipg==NULL) { + //printf("chain depth %d\n", chain->cur_depth); + chain->cur_depth--; + ipg = mdb_find_next_leaf(mdb, idx, chain); + if (ipg) mdb_index_find_next_on_page(mdb, ipg); + } + if (chain->cur_depth==1) { + //printf("last leaf %lu\n", chain->last_leaf_found); + return NULL; + } + return ipg; +} +/* + * the main index function. + * caller provides an index chain which is the current traversal of index + * pages from the root page to the leaf. Initially passed as blank, + * mdb_index_find_next will store it's state information here. Each invocation + * then picks up where the last one left off, allowing us to scroll through + * the index one by one. + * + * Sargs are applied here but also need to be applied on the whole row b/c + * text columns may return false positives due to hashing and non-index + * columns with sarg values can't be tested here. + */ +int +mdb_index_find_next(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 *pg, guint16 *row) +{ + MdbIndexPage *ipg; + int passed = 0; + int idx_sz; + int idx_start = 0; + unsigned short compress_bytes; + MdbColumn *col; + guint32 pg_row; + + ipg = mdb_index_read_bottom_pg(mdb, idx, chain); + + /* + * loop while the sargs don't match + */ + do { + ipg->len = 0; + /* + * if no more rows on this leaf, try to find a new leaf + */ + if (!mdb_index_find_next_on_page(mdb, ipg)) { + if (!chain->clean_up_mode) { + if (ipg->rc==1 || !(ipg = mdb_index_unwind(mdb, idx, chain))) + chain->clean_up_mode = 1; + } + if (chain->clean_up_mode) { + //fprintf(stdout,"in cleanup mode\n"); + + if (!chain->last_leaf_found) return 0; + mdb_read_pg(mdb, chain->last_leaf_found); + chain->last_leaf_found = mdb_get_int32( + mdb->pg_buf, 0x0c); + //printf("next leaf %lu\n", chain->last_leaf_found); + mdb_read_pg(mdb, chain->last_leaf_found); + /* reuse the chain for cleanup mode */ + chain->cur_depth = 1; + ipg = &chain->pages[0]; + mdb_index_page_init(mdb, ipg); + ipg->pg = chain->last_leaf_found; + //printf("next on page %d\n", + if (!mdb_index_find_next_on_page(mdb, ipg)) + return 0; + } + } + pg_row = mdb_get_int32_msb(mdb->pg_buf, ipg->offset + ipg->len - 4); + *row = pg_row & 0xff; + *pg = pg_row >> 8; + //printf("row = %d pg = %lu ipg->pg = %lu offset = %lu len = %d\n", *row, *pg, ipg->pg, ipg->offset, ipg->len); + col=g_ptr_array_index(idx->table->columns,idx->key_col_num[0]-1); + idx_sz = mdb_col_fixed_size(col); + /* handle compressed indexes, single key indexes only? */ + if (idx_sz<0) idx_sz = ipg->len - (ipg->start_pos==1?5:4); // Length from Index - the 4 trailing bytes (data page/row), Skip flags on first page + compress_bytes = mdb_get_int16(mdb->pg_buf, IS_JET3(mdb)?0x14:0x18); + if (idx->num_keys==1 && idx_sz>0 && compress_bytes > 1 && ipg->start_pos>1 /*ipg->len - 4 < idx_sz*/) { + //printf("short index found\n"); + //mdb_buffer_dump(ipg->cache_value, 0, idx_sz); + memcpy(&ipg->cache_value[compress_bytes-1], &mdb->pg_buf[ipg->offset], ipg->len); + //mdb_buffer_dump(ipg->cache_value, 0, idx_sz); + } else { + idx_start = ipg->offset + (ipg->len - 4 - idx_sz); + memcpy(ipg->cache_value, &mdb->pg_buf[idx_start], idx_sz); + } + + //idx_start = ipg->offset + (ipg->len - 4 - idx_sz); + passed = mdb_index_test_sargs(mdb, idx, (char *)(ipg->cache_value), idx_sz); + if (passed) ipg->rc=1; else if (ipg->rc) return 0; + + ipg->offset += ipg->len; + } while (!passed); + + //fprintf(stdout,"len = %d pos %d\n", ipg->len, ipg->mask_pos); + //mdb_buffer_dump(mdb->pg_buf, ipg->offset, ipg->len); + + return ipg->len; +} +/* + * XXX - FIX ME + * This function is grossly inefficient. It scans the entire index building + * an IndexChain to a specific row. We should be checking the index pages + * for matches against the indexed fields to find the proper leaf page, but + * getting it working first and then make it fast! + */ +int +mdb_index_find_row(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 pg, guint16 row) +{ + MdbIndexPage *ipg; + int passed = 0; + guint32 pg_row = (pg << 8) | (row & 0xff); + guint32 datapg_row; + + ipg = mdb_index_read_bottom_pg(mdb, idx, chain); + + do { + ipg->len = 0; + /* + * if no more rows on this leaf, try to find a new leaf + */ + if (!mdb_index_find_next_on_page(mdb, ipg)) { + /* back to top? We're done */ + if (chain->cur_depth==1) + return 0; + + /* + * unwind the stack until we find something or reach + * the top. + */ + while (chain->cur_depth>1) { + chain->cur_depth--; + if (!(ipg = mdb_find_next_leaf(mdb, idx, chain))) + return 0; + mdb_index_find_next_on_page(mdb, ipg); + } + if (chain->cur_depth==1) + return 0; + } + /* test row and pg */ + datapg_row = mdb_get_int32_msb(mdb->pg_buf, ipg->offset + ipg->len - 4); + if (pg_row == datapg_row) { + passed = 1; + } + ipg->offset += ipg->len; + } while (!passed); + + /* index chain from root to leaf should now be in "chain" */ + return 1; +} + +void mdb_index_walk(MdbTableDef *table, MdbIndex *idx) +{ +/* + MdbHandle *mdb = table->entry->mdb; + int cur_pos = 0; + unsigned char marker; + MdbColumn *col; + unsigned int i; + + if (idx->num_keys!=1) return; + + mdb_read_pg(mdb, idx->first_pg); + cur_pos = 0xf8; + + for (i=0;inum_keys;i++) { + marker = mdb->pg_buf[cur_pos++]; + col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1); + //printf("column %d coltype %d col_size %d (%d)\n",i,col->col_type, mdb_col_fixed_size(col), col->col_size); + } +*/ +} +void +mdb_index_dump(MdbTableDef *table, MdbIndex *idx) +{ + unsigned int i; + MdbColumn *col; + + fprintf(stdout,"index number %d\n", idx->index_num); + fprintf(stdout,"index name %s\n", idx->name); + fprintf(stdout,"index first page %d\n", idx->first_pg); + fprintf(stdout,"index rows %d\n", idx->num_rows); + if (idx->index_type==1) fprintf(stdout,"index is a primary key\n"); + for (i=0;inum_keys;i++) { + col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1); + fprintf(stdout,"Column %s(%d) Sorted %s Unique: %s\n", + col->name, + idx->key_col_num[i], + idx->key_col_order[i]==MDB_ASC ? "ascending" : "descending", + idx->flags & MDB_IDX_UNIQUE ? "Yes" : "No" + ); + } + mdb_index_walk(table, idx); +} +/* + * compute_cost tries to assign a cost to a given index using the sargs + * available in this query. + * + * Indexes with no matching sargs are assigned 0 + * Unique indexes are preferred over non-uniques + * Operator preference is equal, like, isnull, others + */ +int mdb_index_compute_cost(MdbTableDef *table, MdbIndex *idx) +{ + unsigned int i; + MdbColumn *col; + MdbSarg *sarg = NULL; + int not_all_equal = 0; + + if (!idx->num_keys) return 0; + if (idx->num_keys > 1) { + for (i=0;inum_keys;i++) { + col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1); + if (col->sargs) sarg = g_ptr_array_index (col->sargs, 0); + if (!sarg || sarg->op != MDB_EQUAL) not_all_equal++; + } + } + + col=g_ptr_array_index(table->columns,idx->key_col_num[0]-1); + /* + * if this is the first key column and there are no sargs, + * then this index is useless. + */ + if (!col->num_sargs) return 0; + + sarg = g_ptr_array_index (col->sargs, 0); + + /* + * a like with a wild card first is useless as a sarg */ + if ((sarg->op == MDB_LIKE || sarg->op == MDB_ILIKE) && sarg->value.s[0]=='%') + return 0; + + /* + * this needs a lot of tweaking. + */ + if (idx->flags & MDB_IDX_UNIQUE) { + if (idx->num_keys == 1) { + //printf("op is %d\n", sarg->op); + switch (sarg->op) { + case MDB_EQUAL: + return 1; break; + case MDB_LIKE: + case MDB_ILIKE: + return 4; break; + case MDB_ISNULL: + return 12; break; + default: + return 8; break; + } + } else { + switch (sarg->op) { + case MDB_EQUAL: + if (not_all_equal) return 2; + else return 1; + break; + case MDB_LIKE: + case MDB_ILIKE: + return 6; break; + case MDB_ISNULL: + return 12; break; + default: + return 9; break; + } + } + } else { + if (idx->num_keys == 1) { + switch (sarg->op) { + case MDB_EQUAL: + return 2; break; + case MDB_LIKE: + case MDB_ILIKE: + return 5; break; + case MDB_ISNULL: + return 12; break; + default: + return 10; break; + } + } else { + switch (sarg->op) { + case MDB_EQUAL: + if (not_all_equal) return 3; + else return 2; + break; + case MDB_LIKE: + case MDB_ILIKE: + return 7; break; + case MDB_ISNULL: + return 12; break; + default: + return 11; break; + } + } + } + return 0; +} +/* + * choose_index runs mdb_index_compute_cost for each available index and picks + * the best. + * + * Returns strategy to use (table scan, or index scan) + */ +MdbStrategy +mdb_choose_index(MdbTableDef *table, int *choice) +{ + unsigned int i; + MdbIndex *idx; + int cost = 0; + int least = 99; + + *choice = -1; + for (i=0;inum_idxs;i++) { + idx = g_ptr_array_index (table->indices, i); + cost = mdb_index_compute_cost(table, idx); + //printf("cost for %s is %d\n", idx->name, cost); + if (cost && cost < least) { + least = cost; + *choice = i; + } + } + /* and the winner is: *choice */ + if (least==99) return MDB_TABLE_SCAN; + return MDB_INDEX_SCAN; +} +void +mdb_index_scan_init(MdbHandle *mdb, MdbTableDef *table) +{ + int i; + + if (mdb_get_option(MDB_USE_INDEX) && mdb_choose_index(table, &i) == MDB_INDEX_SCAN) { + table->strategy = MDB_INDEX_SCAN; + table->scan_idx = g_ptr_array_index (table->indices, i); + table->chain = g_malloc0(sizeof(MdbIndexChain)); + table->mdbidx = mdb_clone_handle(mdb); + mdb_read_pg(table->mdbidx, table->scan_idx->first_pg); + //printf("best index is %s\n",table->scan_idx->name); + } + //printf("TABLE SCAN? %d\n", table->strategy); +} +void +mdb_index_scan_free(MdbTableDef *table) +{ + if (table->chain) { + g_free(table->chain); + table->chain = NULL; + } + if (table->mdbidx) { + mdb_close(table->mdbidx); + table->mdbidx = NULL; + } +} + +void mdb_free_indices(GPtrArray *indices) +{ + guint i; + + if (!indices) return; + for (i=0; ilen; i++) + g_free (g_ptr_array_index(indices, i)); + g_ptr_array_free(indices, TRUE); +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/like.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/like.c new file mode 100644 index 0000000000..fe57752580 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/like.c @@ -0,0 +1,91 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "mdbtools.h" + +/** + * + * @param s: String to search within. + * @param r: Search pattern. + * + * Tests the string @s to see if it matches the search pattern @r. In the + * search pattern, a percent sign indicates matching on any number of + * characters, and an underscore indicates matching any single character. + * + * @Returns: 1 if the string matches, 0 if the string does not match. + */ +int mdb_like_cmp(char *s, char *r) +{ + unsigned int i; + int ret; + + mdb_debug(MDB_DEBUG_LIKE, "comparing %s and %s", s, r); + switch (r[0]) { + case '\0': + return (s[0]=='\0'); + case '_': + /* skip one character */ + return mdb_like_cmp(&s[1],&r[1]); + case '%': + /* skip any number of characters */ + /* the strlen(s)+1 is important so the next call can */ + /* if there are trailing characters */ + for(i=0;i= pgnum) ? start_pg-pgnum+1 : 0; + for (; ifmt->pg_size - 4) * 8 pages. + * + * map_ind gives us the starting usage_map entry + * offset gives us a page offset into the bitmap + */ + usage_bitlen = (mdb->fmt->pg_size - 4) * 8; + max_map_pgs = (map_sz - 1) / 4; + map_ind = (start_pg + 1) / usage_bitlen; + offset = (start_pg + 1) % usage_bitlen; + + for (; map_indfmt->pg_size) { + fprintf(stderr, "Oops! didn't get a full page at %d\n", map_pg); + return -1; + } + + usage_bitmap = mdb->alt_pg_buf + 4; + for (i=offset; ientry; + MdbHandle *mdb = entry->mdb; + gint32 pgnum; + guint32 cur_pg = 0; + int free_space; + + do { + pgnum = mdb_map_find_next(mdb, + table->free_usage_map, + table->freemap_sz, cur_pg); + //printf("looking at page %d\n", pgnum); + if (!pgnum) { + /* allocate new page */ + return mdb_alloc_page(table); + } else if (pgnum==-1) { + fprintf(stderr, "Error: mdb_map_find_next_freepage error while reading maps.\n"); + return -1; + } + cur_pg = pgnum; + + mdb_read_pg(mdb, pgnum); + free_space = mdb_pg_get_freespace(mdb); + + } while (free_space < row_size); + + //printf("page %d has %d bytes left\n", pgnum, free_space); + + return pgnum; +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/mdbtools.h b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/mdbtools.h new file mode 100644 index 0000000000..fd38abe741 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/mdbtools.h @@ -0,0 +1,4 @@ +/* Shim: force libmdb sources to use our vendored mdbtools.h, not the + * system-installed mdbtools-dev one which may be an older version + * missing fields like 'locale', 'iconv_in', 'iconv_out' in MdbHandle. */ +#include "../mdbtools/mdbtools.h" diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/money.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/money.c new file mode 100644 index 0000000000..f3c606548f --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/money.c @@ -0,0 +1,156 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 1998-1999 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "mdbtools.h" + +#define MAX_MONEY_PRECISION 20 +#define MAX_NUMERIC_PRECISION 40 +/* +** these routines are copied from the freetds project which does something +** very similiar +*/ + +static int multiply_byte(unsigned char *product, int num, unsigned char *multiplier, size_t len); +static int do_carry(unsigned char *product, size_t len); +static char *array_to_string(unsigned char *array, size_t len, int unsigned scale, int neg); + +/** + * mdb_money_to_string + * @mdb: Handle to open MDB database file + * @start: Offset of the field within the current page + * + * Returns: the allocated string that has received the value. + */ +char *mdb_money_to_string(MdbHandle *mdb, int start) +{ + const int num_bytes=8, scale=4; + int i; + int neg=0; + unsigned char multiplier[MAX_MONEY_PRECISION] = { 1 }; + unsigned char temp[MAX_MONEY_PRECISION]; + unsigned char product[MAX_MONEY_PRECISION] = { 0 }; + unsigned char bytes[num_bytes]; + + memcpy(bytes, mdb->pg_buf + start, num_bytes); + + /* Perform two's complement for negative numbers */ + if (bytes[num_bytes-1] & 0x80) { + neg = 1; + for (i=0;ipg_buf + start + 1, num_bytes); + + /* Negative bit is stored in first byte */ + if (mdb->pg_buf[start] & 0x80) neg = 1; + for (i=0;i9) { + product[j+1]+=product[j]/10; + product[j]%=10; + } + } + if (product[j]>9) { + product[j]%=10; + } + return 0; +} +static char *array_to_string(unsigned char *array, size_t len, unsigned int scale, int neg) +{ + char *s; + unsigned int top, i, j=0; + + for (top=len;(top>0) && (top-1>scale) && !array[top-1];top--); + + /* allocate enough space for all digits + minus sign + decimal point + trailing NULL byte */ + s = g_malloc(len+3); + + if (neg) + s[j++] = '-'; + + if (top == 0) { + s[j++] = '0'; + } else { + for (i=top; i>0; i--) { + if (i == scale) s[j++]='.'; + s[j++]=array[i-1]+'0'; + } + } + s[j]='\0'; + + return s; +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/options.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/options.c new file mode 100644 index 0000000000..7f0a3414e2 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/options.c @@ -0,0 +1,92 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2004 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include "mdbtools.h" + +#define DEBUG 1 + +static TLS unsigned long opts; +static TLS int optset; + +static void load_options(void); + +void +mdb_debug(int klass, char *fmt, ...) +{ +#ifdef DEBUG + va_list ap; + + if (!optset) load_options(); + if (klass & opts) { + va_start(ap, fmt); + vfprintf (stderr,fmt, ap); + va_end(ap); + fprintf(stderr,"\n"); + } +#endif +} + +static void +load_options() +{ + char *opt; + char *s; + char *ctx; + + if (!optset && (s=getenv("MDBOPTS"))) { + opt = strtok_r(s, ":", &ctx); + while (opt) { + if (!strcmp(opt, "use_index")) { +#ifdef HAVE_LIBMSWSTR + opts |= MDB_USE_INDEX; +#else + fprintf(stderr, "The 'use_index' argument was supplied to MDBOPTS environment variable. However, this feature requires the libmswstr library, which was not found when libmdb was compiled. As a result, the 'use_index' argument will be ignored.\n\nTo enable indexes, you will need to download libmswstr from https://github.com/leecher1337/libmswstr and then recompile libmdb. Note that the 'use_index' feature is largely untested, and may have unexpected results.\n\nTo suppress this warning, run the program again after removing the 'use_index' argument from the MDBOPTS environment variable.\n"); +#endif + } + if (!strcmp(opt, "no_memo")) { + fprintf(stderr, "The 'no_memo' argument was supplied to MDBOPTS environment variable. This argument is deprecated, and has no effect.\n\nTo suppress this warning, run the program again after removing the 'no_memo' argument from the MDBOPTS environment variable.\n"); + } + if (!strcmp(opt, "debug_like")) opts |= MDB_DEBUG_LIKE; + if (!strcmp(opt, "debug_write")) opts |= MDB_DEBUG_WRITE; + if (!strcmp(opt, "debug_usage")) opts |= MDB_DEBUG_USAGE; + if (!strcmp(opt, "debug_ole")) opts |= MDB_DEBUG_OLE; + if (!strcmp(opt, "debug_row")) opts |= MDB_DEBUG_ROW; + if (!strcmp(opt, "debug_props")) opts |= MDB_DEBUG_PROPS; + if (!strcmp(opt, "debug_all")) { + opts |= MDB_DEBUG_LIKE; + opts |= MDB_DEBUG_WRITE; + opts |= MDB_DEBUG_USAGE; + opts |= MDB_DEBUG_OLE; + opts |= MDB_DEBUG_ROW; + opts |= MDB_DEBUG_PROPS; + } + opt = strtok_r(NULL,":", &ctx); + } + } + optset = 1; +} +int +mdb_get_option(unsigned long optnum) +{ + if (!optset) load_options(); + return ((opts & optnum) > 0); +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/props.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/props.c new file mode 100644 index 0000000000..4f8a4126cd --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/props.c @@ -0,0 +1,230 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000-2011 Brian Bruns and others + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mdbtools.h" + +static GPtrArray * +mdb_read_props_list(MdbHandle *mdb, gchar *kkd, int len) +{ + guint32 record_len; + int pos = 0; + gchar *name; + GPtrArray *names = NULL; + int i=0; + + names = g_ptr_array_new(); +#if MDB_DEBUG + mdb_buffer_dump(kkd, 0, len); +#endif + pos = 0; + while (pos < len) { + record_len = mdb_get_int16(kkd, pos); + pos += 2; + if (mdb_get_option(MDB_DEBUG_PROPS)) { + fprintf(stderr, "%02d ",i++); + mdb_buffer_dump(kkd, pos - 2, record_len + 2); + } + name = g_malloc(3*record_len + 1); /* worst case scenario is 3 bytes out per byte in */ + mdb_unicode2ascii(mdb, &kkd[pos], record_len, name, 3*record_len + 1); + + pos += record_len; + g_ptr_array_add(names, name); +#if MDB_DEBUG + printf("new len = %d\n", names->len); +#endif + } + return names; +} +static void +free_hash_entry(gpointer key, gpointer value, gpointer user_data) +{ + g_free(key); + g_free(value); +} +void +mdb_free_props(MdbProperties *props) +{ + if (!props) return; + + if (props->name) g_free(props->name); + if (props->hash) { + g_hash_table_foreach(props->hash, free_hash_entry, 0); + g_hash_table_destroy(props->hash); + } + g_free(props); +} + +static void +do_g_free(gpointer ptr, gpointer user_data) { + g_free(ptr); +} + +static void +free_names(GPtrArray *names) { + g_ptr_array_foreach(names, do_g_free, NULL); + g_ptr_array_free(names, TRUE); +} +MdbProperties * +mdb_alloc_props(void) +{ + MdbProperties *props; + + props = g_malloc0(sizeof(MdbProperties)); + + return props; +} +static MdbProperties * +mdb_read_props(MdbHandle *mdb, GPtrArray *names, gchar *kkd, int len) +{ + guint32 record_len, name_len; + int pos = 0; + guint elem; + int dtype, dsize; + gchar *name, *value; + MdbProperties *props; + int i=0; + +#if MDB_DEBUG + mdb_buffer_dump(kkd, 0, len); +#endif + pos = 0; + + record_len = mdb_get_int16(kkd, pos); + pos += 4; + name_len = mdb_get_int16(kkd, pos); + pos += 2; + props = mdb_alloc_props(); + if (name_len) { + props->name = g_malloc(3*name_len + 1); + mdb_unicode2ascii(mdb, kkd+pos, name_len, props->name, 3*name_len + 1); + mdb_debug(MDB_DEBUG_PROPS,"prop block named: %s", props->name); + } + pos += name_len; + + props->hash = g_hash_table_new(g_str_hash, g_str_equal); + + while (pos < len) { + record_len = mdb_get_int16(kkd, pos); + dtype = kkd[pos + 3]; + elem = mdb_get_int16(kkd, pos + 4); + if (elem >= names->len) + break; + dsize = mdb_get_int16(kkd, pos + 6); + if (dsize < 0 || pos + 8 + dsize > len) + break; + value = g_strdup_printf("%.*s", dsize, &kkd[pos+8]); + name = g_ptr_array_index(names,elem); + if (mdb_get_option(MDB_DEBUG_PROPS)) { + fprintf(stderr, "%02d ",i++); + mdb_debug(MDB_DEBUG_PROPS,"elem %d (%s) dsize %d dtype %d", elem, name, dsize, dtype); + mdb_buffer_dump(value, 0, dsize); + } + if (dtype == MDB_MEMO) { + dtype = MDB_TEXT; + } else if (dtype == MDB_BINARY && dsize == 16 && strcmp(name, "GUID") == 0) { + dtype = MDB_REPID; + } + if (dtype == MDB_BOOL) { + g_hash_table_insert(props->hash, g_strdup(name), + g_strdup(kkd[pos + 8] ? "yes" : "no")); + } else if (dtype == MDB_BINARY || dtype == MDB_OLE) { + g_hash_table_insert(props->hash, g_strdup(name), + g_strdup_printf("(binary data of length %d)", dsize)); + } else { + g_hash_table_insert(props->hash, g_strdup(name), + mdb_col_to_string(mdb, kkd, pos + 8, dtype, dsize)); + } + g_free(value); + pos += record_len; + } + return props; + +} + +static void +print_keyvalue(gpointer key, gpointer value, gpointer outfile) +{ + fprintf((FILE*)outfile,"\t%s: %s\n", (gchar *)key, (gchar *)value); +} +void +mdb_dump_props(MdbProperties *props, FILE *outfile, int show_name) { + if (show_name) + fprintf(outfile,"name: %s\n", props->name ? props->name : "(none)"); + g_hash_table_foreach(props->hash, print_keyvalue, outfile); + if (show_name) + fputc('\n', outfile); +} + +/* + * That function takes a raw KKD/MR2 binary buffer, + * typically read from LvProp in table MSysbjects + * and returns a GPtrArray of MdbProps* + */ +GPtrArray* +mdb_kkd_to_props(MdbHandle *mdb, void *buffer, size_t len) { + guint32 record_len; + guint16 record_type; + size_t pos; + GPtrArray *names = NULL; + MdbProperties *props; + GPtrArray *result; + +#if MDB_DEBUG + mdb_buffer_dump(buffer, 0, len); +#endif + mdb_debug(MDB_DEBUG_PROPS,"starting prop parsing of type %s", buffer); + + if (strcmp("KKD", buffer) && strcmp("MR2", buffer)) { + fprintf(stderr, "Unrecognized format.\n"); + mdb_buffer_dump(buffer, 0, len); + return NULL; + } + + result = g_ptr_array_new(); + + pos = 4; + while (pos < len) { + record_len = mdb_get_int32(buffer, pos); + record_type = mdb_get_int16(buffer, pos + 4); + mdb_debug(MDB_DEBUG_PROPS,"prop chunk type:0x%04x len:%d", record_type, record_len); + //mdb_buffer_dump(buffer, pos+4, record_len); + switch (record_type) { + case 0x80: + if (names) free_names(names); + names = mdb_read_props_list(mdb, (char*)buffer+pos+6, record_len - 6); + break; + case 0x00: + case 0x01: + case 0x02: + if (!names) { + fprintf(stderr,"sequence error!\n"); + break; + } + props = mdb_read_props(mdb, names, (char*)buffer+pos+6, record_len - 6); + g_ptr_array_add(result, props); + //mdb_dump_props(props, stderr, 1); + break; + default: + fprintf(stderr,"Unknown record type %d\n", record_type); + break; + } + pos += record_len; + } + if (names) free_names(names); + return result; +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/rc4.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/rc4.c new file mode 100644 index 0000000000..5596736afa --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/rc4.c @@ -0,0 +1,85 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mdbprivate.h" + +typedef struct +{ + unsigned char state[256]; + unsigned char x; + unsigned char y; +} RC4_KEY; + +#define swap_byte(x,y) t = *(x); *(x) = *(y); *(y) = t + + +static void RC4_set_key(RC4_KEY *key, int key_data_len, unsigned char *key_data_ptr) +{ + unsigned char t; + unsigned char index1; + unsigned char index2; + unsigned char* state; + short counter; + + state = &key->state[0]; + for(counter = 0; counter < 256; counter++) + state[counter] = counter; + key->x = 0; + key->y = 0; + index1 = 0; + index2 = 0; + for(counter = 0; counter < 256; counter++) { + index2 = (key_data_ptr[index1] + state[counter] + index2) % 256; + swap_byte(&state[counter], &state[index2]); + index1 = (index1 + 1) % key_data_len; + } +} + +/* + * this algorithm does 'encrypt in place' instead of inbuff/outbuff + * note also: encryption and decryption use same routine + * implementation supplied by (Adam Back) at + */ +static void RC4(RC4_KEY *key, int buffer_len, unsigned char * buff) +{ + unsigned char t; + unsigned char x; + unsigned char y; + unsigned char* state; + unsigned char xorIndex; + short counter; + + x = key->x; + y = key->y; + state = &key->state[0]; + for(counter = 0; counter < buffer_len; counter++) { + x = (x + 1) % 256; + y = (state[x] + y) % 256; + swap_byte(&state[x], &state[y]); + xorIndex = (state[x] + state[y]) % 256; + buff[counter] ^= state[xorIndex]; + } + key->x = x; + key->y = y; +} + +void mdbi_rc4(unsigned char *key, guint32 key_len, unsigned char *buf, guint32 buf_len) { + RC4_KEY rc4_key; + RC4_set_key(&rc4_key, key_len, key); + RC4(&rc4_key, buf_len, buf); +} \ No newline at end of file diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/sargs.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/sargs.c new file mode 100644 index 0000000000..d8c3c9cbe4 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/sargs.c @@ -0,0 +1,375 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * code for handling searchable arguments (sargs) used primary by the sql + * engine to support where clause handling. The sargs are configured in + * a tree with AND/OR operators connecting the child nodes. NOT operations + * have only one child on the left side. Logical operators (=,<,>,etc..) + * have no children. + * + * datatype support is a bit weak at this point. To add more types create + * a mdb_test_[type]() function and invoke it from mdb_test_sarg() + */ + +#include +#include "mdbtools.h" +#include "mdbprivate.h" + +void +mdb_sql_walk_tree(MdbSargNode *node, MdbSargTreeFunc func, gpointer data) +{ + if (func(node, data)) + return; + if (node->left) mdb_sql_walk_tree(node->left, func, data); + if (node->right) mdb_sql_walk_tree(node->right, func, data); +} +int +mdb_test_string(MdbSargNode *node, char *s) +{ +int rc; + + if (node->op == MDB_LIKE) { + return mdb_like_cmp(s,node->value.s); + } + if (node->op == MDB_ILIKE) { + return mdb_ilike_cmp(s,node->value.s); + } + rc = strcoll(node->value.s, s); + switch (node->op) { + case MDB_EQUAL: + if (rc==0) return 1; + break; + case MDB_GT: + if (rc<0) return 1; + break; + case MDB_LT: + if (rc>0) return 1; + break; + case MDB_GTEQ: + if (rc<=0) return 1; + break; + case MDB_LTEQ: + if (rc>=0) return 1; + break; + case MDB_NEQ: + if (rc!=0) return 1; + break; + default: + fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_string() for operator %d\n",node->op); + break; + } + return 0; +} +int mdb_test_int(MdbSargNode *node, gint32 i) +{ + gint32 val = node->val_type == MDB_INT ? node->value.i : node->value.d; + switch (node->op) { + case MDB_EQUAL: + //fprintf(stderr, "comparing %ld and %ld\n", i, node->value.i); + if (val == i) return 1; + break; + case MDB_GT: + if (val < i) return 1; + break; + case MDB_LT: + if (val > i) return 1; + break; + case MDB_GTEQ: + if (val <= i) return 1; + break; + case MDB_LTEQ: + if (val >= i) return 1; + break; + case MDB_NEQ: + if (val != i) return 1; + break; + default: + fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_int() for operator %d\n",node->op); + break; + } + return 0; +} + +/* Actually used to not rely on libm. + * Maybe there is a cleaner and faster solution? */ +static double poor_mans_trunc(double x) +{ + char buf[16]; + snprintf(buf, sizeof(buf), "%.6f", x); + sscanf(buf, "%lf", &x); + return x; +} + +int mdb_test_double(int op, double vd, double d) +{ + int ret = 0; + switch (op) { + case MDB_EQUAL: + //fprintf(stderr, "comparing %lf and %lf\n", d, node->value.d); + ret = (vd == d); + break; + case MDB_GT: + ret = (vd < d); + break; + case MDB_LT: + ret = (vd > d); + break; + case MDB_GTEQ: + ret = (vd <= d); + break; + case MDB_LTEQ: + ret = (vd >= d); + break; + case MDB_NEQ: + ret = (vd != d); + break; + default: + fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_double() for operator %d\n",op); + break; + } + return ret; +} + +#if 0 // Obsolete +int +mdb_test_date(MdbSargNode *node, double td) +{ + struct tm found; + /* TODO: you should figure out a way to pull mdb_date_to_string in here + * char date_tmp[MDB_BIND_SIZE]; + */ + + time_t found_t; + time_t asked_t; + + double diff; + + mdb_date_to_tm(td, &found); + + asked_t = node->value.i; + found_t = mktime(&found); + + diff = difftime(asked_t, found_t); + + switch (node->op) { + case MDB_EQUAL: + if (diff==0) return 1; + break; + case MDB_GT: + if (diff<0) return 1; + break; + case MDB_LT: + if (diff>0) return 1; + break; + case MDB_GTEQ: + if (diff<=0) return 1; + break; + case MDB_LTEQ: + if (diff>=0) return 1; + break; + default: + fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_date() for operator %d\n", node->op); + break; + } + return 0; +} +#endif + + +int +mdb_find_indexable_sargs(MdbSargNode *node, gpointer data) +{ + MdbSarg sarg; + + if (node->op == MDB_OR || node->op == MDB_NOT) return 1; + + /* + * right now all we do is look for sargs that are anded together from + * the root. Later we may put together OR ops into a range, and then + * range scan the leaf pages. That is col1 = 2 or col1 = 4 becomes + * col1 >= 2 and col1 <= 4 for the purpose of index scans, and then + * extra rows are thrown out when the row is tested against the main + * sarg tree. range scans are generally only a bit better than table + * scanning anyway. + * + * also, later we should support the NOT operator, but it's generally + * a pretty worthless test for indexes, ie NOT col1 = 3, we are + * probably better off table scanning. + */ + if (mdb_is_relational_op(node->op) && node->col) { + //printf("op = %d value = %s\n", node->op, node->value.s); + sarg.op = node->op; + sarg.value = node->value; + mdb_add_sarg(node->col, &sarg); + } + return 0; +} +int +mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field) +{ + char tmpbuf[256]; + char* val; + int ret = 1; + + if (node->op == MDB_ISNULL) + ret = field->is_null; + else if (node->op == MDB_NOTNULL) + ret = !field->is_null; + switch (col->col_type) { + case MDB_BOOL: + ret = mdb_test_int(node, !field->is_null); + break; + case MDB_BYTE: + ret = mdb_test_int(node, (gint32)((char *)field->value)[0]); + break; + case MDB_INT: + ret = mdb_test_int(node, (gint32)mdb_get_int16(field->value, 0)); + break; + case MDB_LONGINT: + ret = mdb_test_int(node, (gint32)mdb_get_int32(field->value, 0)); + break; + case MDB_FLOAT: + ret = mdb_test_double(node->op, node->val_type == MDB_INT ? node->value.i : node->value.d, mdb_get_single(field->value, 0)); + break; + case MDB_DOUBLE: + ret = mdb_test_double(node->op, node->val_type == MDB_INT ? node->value.i : node->value.d, mdb_get_double(field->value, 0)); + break; + case MDB_TEXT: + mdb_unicode2ascii(mdb, field->value, field->siz, tmpbuf, sizeof(tmpbuf)); + ret = mdb_test_string(node, tmpbuf); + break; + case MDB_MEMO: + case MDB_REPID: + val = mdb_col_to_string(mdb, mdb->pg_buf, field->start, col->col_type, (gint32)mdb_get_int32(field->value, 0)); + //printf("%s\n",val); + ret = mdb_test_string(node, val); + g_free(val); + break; + case MDB_DATETIME: + ret = mdb_test_double(node->op, poor_mans_trunc(node->value.d), poor_mans_trunc(mdb_get_double(field->value, 0))); + break; + default: + fprintf(stderr, "Calling mdb_test_sarg on unknown type. Add code to mdb_test_sarg() for type %d\n",col->col_type); + break; + } + return ret; +} +int +mdb_find_field(int col_num, MdbField *fields, int num_fields) +{ + int i; + + for (i=0;iop)) { + col = node->col; + /* for const = const expressions */ + if (!col) { + return (node->value.i); + } + elem = mdb_find_field(col->col_num, fields, num_fields); + if (!mdb_test_sarg(mdb, col, node, &fields[elem])) + return 0; + } else { /* logical op */ + switch (node->op) { + case MDB_NOT: + rc = mdb_test_sarg_node(mdb, node->left, fields, num_fields); + return !rc; + break; + case MDB_AND: + if (!mdb_test_sarg_node(mdb, node->left, fields, num_fields)) + return 0; + return mdb_test_sarg_node(mdb, node->right, fields, num_fields); + break; + case MDB_OR: + if (mdb_test_sarg_node(mdb, node->left, fields, num_fields)) + return 1; + return mdb_test_sarg_node(mdb, node->right, fields, num_fields); + break; + } + } + return 1; +} +int +mdb_test_sargs(MdbTableDef *table, MdbField *fields, int num_fields) +{ + MdbSargNode *node; + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + + node = table->sarg_tree; + + /* there may not be a sarg tree */ + if (!node) return 1; + + return mdb_test_sarg_node(mdb, node, fields, num_fields); +} +#if 0 +int mdb_test_sargs(MdbHandle *mdb, MdbColumn *col, int offset, int len) +{ +MdbSarg *sarg; +int i; + + for (i=0;inum_sargs;i++) { + sarg = g_ptr_array_index (col->sargs, i); + if (!mdb_test_sarg(mdb, col, sarg, offset, len)) { + /* sarg didn't match, no sense going on */ + return 0; + } + } + + return 1; +} +#endif +int mdb_add_sarg(MdbColumn *col, MdbSarg *in_sarg) +{ +MdbSarg *sarg; + if (!col->sargs) { + col->sargs = g_ptr_array_new(); + } + sarg = g_memdup2(in_sarg,sizeof(MdbSarg)); + g_ptr_array_add(col->sargs, sarg); + col->num_sargs++; + + return 1; +} +int mdb_add_sarg_by_name(MdbTableDef *table, char *colname, MdbSarg *in_sarg) +{ + MdbColumn *col; + unsigned int i; + + for (i=0;inum_cols;i++) { + col = g_ptr_array_index (table->columns, i); + if (!g_ascii_strcasecmp(col->name,colname)) { + return mdb_add_sarg(col, in_sarg); + } + } + /* else didn't find the column return 0! */ + return 0; +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/stats.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/stats.c new file mode 100644 index 0000000000..f60280dc13 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/stats.c @@ -0,0 +1,67 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mdbtools.h" + +/** + * @brief Begins collection of statistics on an MDBHandle. + * @param mdb: Handle to the (open) MDB file to collect stats on. + * + * + * Statistics in LibMDB will track the number of reads from the MDB file. The + * collection of statistics is started and stopped with the mdb_stats_on and + * mdb_stats_off functions. Collected statistics are accessed by reading the + * MdbStatistics structure or calling mdb_dump_stats. + * + */ +void +mdb_stats_on(MdbHandle *mdb) +{ + if (!mdb->stats) + mdb->stats = g_malloc0(sizeof(MdbStatistics)); + + mdb->stats->collect = TRUE; +} +/** + * @brief Turns off statistics collection. + * @param mdb: pointer to handle of MDB file with active stats collection. + * + * + * If mdb_stats_off() is not called, statistics will be turned off when handle + * is freed using mdb_close(). + **/ +void +mdb_stats_off(MdbHandle *mdb) +{ + if (!mdb->stats) return; + + mdb->stats->collect = FALSE; +} +/** + * @brief Dumps current statistics to stdout. + * @param mdb: pointer to handle of MDB file with active stats collection. + * + * + **/ +void +mdb_dump_stats(MdbHandle *mdb) +{ + if (!mdb->stats) return; + + fprintf(stdout, "Physical Page Reads: %lu\n", mdb->stats->pg_reads); +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/table.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/table.c new file mode 100644 index 0000000000..1a758e49ef --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/table.c @@ -0,0 +1,448 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mdbtools.h" +#include "mdbprivate.h" + +static gint mdb_col_comparer(MdbColumn **a, MdbColumn **b) +{ + if ((*a)->col_num > (*b)->col_num) + return 1; + else if ((*a)->col_num < (*b)->col_num) + return -1; + else + return 0; +} + +MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry) +{ + MdbTableDef *table = g_malloc0(sizeof(MdbTableDef)); + table->entry=entry; + snprintf(table->name, sizeof(table->name), "%s", entry->object_name); + + return table; +} +void mdb_free_tabledef(MdbTableDef *table) +{ + if (!table) return; + if (table->is_temp_table) { + guint i; + /* Temp table pages are being stored in memory */ + for (i=0; itemp_table_pages->len; i++) + g_free(g_ptr_array_index(table->temp_table_pages,i)); + g_ptr_array_free(table->temp_table_pages, TRUE); + /* Temp tables use dummy entries */ + g_free(table->entry); + } + mdb_free_columns(table->columns); + mdb_free_indices(table->indices); + g_free(table->usage_map); + g_free(table->free_usage_map); + g_free(table); +} +MdbTableDef *mdb_read_table(MdbCatalogEntry *entry) +{ + MdbTableDef *table; + MdbHandle *mdb = entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + int row_start, pg_row; + void *buf, *pg_buf = mdb->pg_buf; + guint i; + + if (!mdb_read_pg(mdb, entry->table_pg)) { + fprintf(stderr, "mdb_read_table: Unable to read page %lu\n", entry->table_pg); + return NULL; + } + if (mdb_get_byte(pg_buf, 0) != 0x02) { + fprintf(stderr, "mdb_read_table: Page %lu [size=%d] is not a valid table definition page (First byte = 0x%02X, expected 0x02)\n", + entry->table_pg, (int)fmt->pg_size, mdb_get_byte(pg_buf, 0)); + return NULL; + } + table = mdb_alloc_tabledef(entry); + + mdb_get_int16(pg_buf, 8); /* len */ + + /* Note that num_rows may be zero if the database was improperly closed. + * See https://github.com/mdbtools/mdbtools/issues/120 for discussion. */ + table->num_rows = mdb_get_int32(pg_buf, fmt->tab_num_rows_offset); + table->num_var_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset-2); + table->num_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset); + table->num_idxs = mdb_get_int32(pg_buf, fmt->tab_num_idxs_offset); + table->num_real_idxs = mdb_get_int32(pg_buf, fmt->tab_num_ridxs_offset); + + /* grab a copy of the usage map */ + pg_row = mdb_get_int32(pg_buf, fmt->tab_usage_map_offset); + if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->map_sz))) { + fprintf(stderr, "mdb_read_table: Unable to find page row %d\n", pg_row); + mdb_free_tabledef(table); + return NULL; + } + /* First byte of usage_map is the map-type and must always be present */ + if (table->map_sz < 1) { + fprintf(stderr, "mdb_read_table: invalid map-size: %zu\n", table->map_sz); + mdb_free_tabledef(table); + return NULL; + } + table->usage_map = g_memdup2((char*)buf + row_start, table->map_sz); + if (mdb_get_option(MDB_DEBUG_USAGE)) + mdb_buffer_dump(buf, row_start, table->map_sz); + mdb_debug(MDB_DEBUG_USAGE,"usage map found on page %ld row %d start %d len %d", + pg_row >> 8, pg_row & 0xff, row_start, table->map_sz); + + /* grab a copy of the free space page map */ + pg_row = mdb_get_int32(pg_buf, fmt->tab_free_map_offset); + if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->freemap_sz))) { + fprintf(stderr, "mdb_read_table: Unable to find page row %d\n", pg_row); + mdb_free_tabledef(table); + return NULL; + } + table->free_usage_map = g_memdup2((char*)buf + row_start, table->freemap_sz); + mdb_debug(MDB_DEBUG_USAGE,"free map found on page %ld row %d start %d len %d\n", + pg_row >> 8, pg_row & 0xff, row_start, table->freemap_sz); + + table->first_data_pg = mdb_get_int16(pg_buf, fmt->tab_first_dpg_offset); + + if (entry->props) + for (i=0; iprops->len; ++i) { + MdbProperties *props = g_ptr_array_index(entry->props, i); + if (!props->name) + table->props = props; + } + + return table; +} +MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type) +{ + unsigned int i; + MdbCatalogEntry *entry; + + mdb_read_catalog(mdb, obj_type); + + for (i=0; inum_catalog; i++) { + entry = g_ptr_array_index(mdb->catalog, i); + if (!g_ascii_strcasecmp(entry->object_name, table_name)) + return mdb_read_table(entry); + } + + return NULL; +} + + +guint32 +read_pg_if_32(MdbHandle *mdb, int *cur_pos) +{ + char c[4]; + + read_pg_if_n(mdb, c, cur_pos, 4); + return mdb_get_int32(c, 0); +} +guint16 +read_pg_if_16(MdbHandle *mdb, int *cur_pos) +{ + char c[2]; + + read_pg_if_n(mdb, c, cur_pos, 2); + return mdb_get_int16(c, 0); +} +guint8 +read_pg_if_8(MdbHandle *mdb, int *cur_pos) +{ + guint8 c; + + read_pg_if_n(mdb, &c, cur_pos, 1); + return c; +} +/* + * Read data into a buffer, advancing pages and setting the + * page cursor as needed. In the case that buf in NULL, pages + * are still advanced and the page cursor is still updated. + */ +void * +read_pg_if_n(MdbHandle *mdb, void *buf, int *cur_pos, size_t len) +{ + char* _buf = buf; + char* _end = buf ? buf + len : NULL; + + if (*cur_pos < 0) + return NULL; + + /* Advance to page which contains the first byte */ + while (*cur_pos >= mdb->fmt->pg_size) { + if (!mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4))) + return NULL; + *cur_pos -= (mdb->fmt->pg_size - 8); + } + /* Copy pages into buffer */ + while (*cur_pos + len >= (size_t)mdb->fmt->pg_size) { + size_t piece_len = mdb->fmt->pg_size - *cur_pos; + if (_buf) { + if (_buf + piece_len > _end) + return NULL; + memcpy(_buf, mdb->pg_buf + *cur_pos, piece_len); + _buf += piece_len; + } + len -= piece_len; + if (!mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4))) + return NULL; + *cur_pos = 8; + } + /* Copy into buffer from final page */ + if (len && _buf) { + if (_buf + len > _end) + return NULL; + memcpy(_buf, mdb->pg_buf + *cur_pos, len); + } + *cur_pos += len; + return _buf; +} + + +void mdb_append_column(GPtrArray *columns, MdbColumn *in_col) +{ + g_ptr_array_add(columns, g_memdup2(in_col,sizeof(MdbColumn))); +} +void mdb_free_columns(GPtrArray *columns) +{ + guint i, j; + MdbColumn *col; + + if (!columns) return; + for (i=0; ilen; i++) { + col = (MdbColumn *) g_ptr_array_index(columns, i); + if (col->sargs) { + for (j=0; jsargs->len; j++) { + g_free( g_ptr_array_index(col->sargs, j)); + } + g_ptr_array_free(col->sargs, TRUE); + } + g_free(col); + } + g_ptr_array_free(columns, TRUE); +} +GPtrArray *mdb_read_columns(MdbTableDef *table) +{ + MdbHandle *mdb = table->entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + MdbColumn *pcol; + unsigned char *col; + unsigned int i; + guint j; + int cur_pos; + size_t name_sz; + GPtrArray *allprops; + + table->columns = g_ptr_array_new(); + + col = g_malloc(fmt->tab_col_entry_size); + + cur_pos = fmt->tab_cols_start_offset + + (table->num_real_idxs * fmt->tab_ridx_entry_size); + + /* new code based on patch submitted by Tim Nelson 2000.09.27 */ + + /* + ** column attributes + */ + for (i=0;inum_cols;i++) { +#ifdef MDB_DEBUG + /* printf("column %d\n", i); + mdb_buffer_dump(mdb->pg_buf, cur_pos, fmt->tab_col_entry_size); */ +#endif + if (!read_pg_if_n(mdb, col, &cur_pos, fmt->tab_col_entry_size)) { + g_free(col); + mdb_free_columns(table->columns); + return table->columns = NULL; + } + pcol = g_malloc0(sizeof(MdbColumn)); + + pcol->table = table; + + pcol->col_type = col[0]; + + // col_num_offset == 1 or 5 + pcol->col_num = col[fmt->col_num_offset]; + + //fprintf(stdout,"----- column %d -----\n",pcol->col_num); + // col_var == 3 or 7 + pcol->var_col_num = mdb_get_int16(col, fmt->tab_col_offset_var); + //fprintf(stdout,"var column pos %d\n",pcol->var_col_num); + + // col_var == 5 or 9 + pcol->row_col_num = mdb_get_int16(col, fmt->tab_row_col_num_offset); + //fprintf(stdout,"row column num %d\n",pcol->row_col_num); + + if (pcol->col_type == MDB_NUMERIC || pcol->col_type == MDB_MONEY || + pcol->col_type == MDB_FLOAT || pcol->col_type == MDB_DOUBLE) { + pcol->col_scale = col[fmt->col_scale_offset]; + pcol->col_prec = col[fmt->col_prec_offset]; + } + + // col_flags_offset == 13 or 15 + pcol->is_fixed = col[fmt->col_flags_offset] & 0x01 ? 1 : 0; + pcol->is_long_auto = col[fmt->col_flags_offset] & 0x04 ? 1 : 0; + pcol->is_uuid_auto = col[fmt->col_flags_offset] & 0x40 ? 1 : 0; + + // tab_col_offset_fixed == 14 or 21 + pcol->fixed_offset = mdb_get_int16(col, fmt->tab_col_offset_fixed); + //fprintf(stdout,"fixed column offset %d\n",pcol->fixed_offset); + //fprintf(stdout,"col type %s\n",pcol->is_fixed ? "fixed" : "variable"); + + if (pcol->col_type != MDB_BOOL) { + // col_size_offset == 16 or 23 + pcol->col_size = mdb_get_int16(col, fmt->col_size_offset); + } else { + pcol->col_size=0; + } + + g_ptr_array_add(table->columns, pcol); + } + + g_free (col); + + /* + ** column names - ordered the same as the column attributes table + */ + for (i=0;inum_cols;i++) { + char *tmp_buf; + pcol = g_ptr_array_index(table->columns, i); + + if (IS_JET3(mdb)) + name_sz = read_pg_if_8(mdb, &cur_pos); + else + name_sz = read_pg_if_16(mdb, &cur_pos); + tmp_buf = g_malloc(name_sz); + if (read_pg_if_n(mdb, tmp_buf, &cur_pos, name_sz)) + mdb_unicode2ascii(mdb, tmp_buf, name_sz, pcol->name, sizeof(pcol->name)); + g_free(tmp_buf); + } + + /* Sort the columns by col_num */ + g_ptr_array_sort(table->columns, (GCompareFunc)mdb_col_comparer); + + allprops = table->entry->props; + if (allprops) + for (i=0;inum_cols;i++) { + pcol = g_ptr_array_index(table->columns, i); + for (j=0; jlen; ++j) { + MdbProperties *props = g_ptr_array_index(allprops, j); + if (props->name && !strcmp(props->name, pcol->name)) { + pcol->props = props; + break; + } + + } + } + table->index_start = cur_pos; + return table->columns; +} + +void mdb_table_dump(MdbCatalogEntry *entry) +{ +MdbTableDef *table; +MdbColumn *col; +int coln; +MdbIndex *idx; +unsigned int i, bitn; +guint32 pgnum; + + table = mdb_read_table(entry); + if (!table) + return; + + fprintf(stdout,"definition page = %lu\n",entry->table_pg); + fprintf(stdout,"number of datarows = %d\n",table->num_rows); + fprintf(stdout,"number of columns = %d\n",table->num_cols); + fprintf(stdout,"number of indices = %d\n",table->num_real_idxs); + + if (table->props) + mdb_dump_props(table->props, stdout, 0); + mdb_read_columns(table); + mdb_read_indices(table); + + for (i=0;inum_cols;i++) { + col = g_ptr_array_index(table->columns,i); + + fprintf(stdout,"column %d Name: %-20s Type: %s(%d)\n", + i, col->name, + mdb_get_colbacktype_string(col), + col->col_size); + if (col->props) + mdb_dump_props(col->props, stdout, 0); + } + + for (i=0;inum_idxs;i++) { + idx = g_ptr_array_index (table->indices, i); + mdb_index_dump(table, idx); + } + if (table->usage_map) { + printf("pages reserved by this object\n"); + printf("usage map pg %" G_GUINT32_FORMAT "\n", + table->map_base_pg); + printf("free map pg %" G_GUINT32_FORMAT "\n", + table->freemap_base_pg); + pgnum = mdb_get_int32(table->usage_map,1); + /* the first 5 bytes of the usage map mean something */ + coln = 0; + for (i=5;imap_sz;i++) { + for (bitn=0;bitn<8;bitn++) { + if (table->usage_map[i] & 1 << bitn) { + coln++; + printf("%6" G_GUINT32_FORMAT, pgnum); + if (coln==10) { + printf("\n"); + coln = 0; + } else { + printf(" "); + } + } + pgnum++; + } + } + printf("\n"); + } +} + +int mdb_is_user_table(MdbCatalogEntry *entry) +{ + return ((entry->object_type == MDB_TABLE) + && !(entry->flags & 0x80000002)) ? 1 : 0; +} +int mdb_is_system_table(MdbCatalogEntry *entry) +{ + return ((entry->object_type == MDB_TABLE) + && (entry->flags & 0x80000002)) ? 1 : 0; +} + +const char * +mdb_table_get_prop(const MdbTableDef *table, const gchar *key) { + if (!table->props) + return NULL; + return g_hash_table_lookup(table->props->hash, key); +} + +const char * +mdb_col_get_prop(const MdbColumn *col, const gchar *key) { + if (!col->props) + return NULL; + return g_hash_table_lookup(col->props->hash, key); +} + +int mdb_col_is_shortdate(const MdbColumn *col) { + const char *format = mdb_col_get_prop(col, "Format"); + return format && !strcmp(format, "Short Date"); +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/version.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/version.c new file mode 100644 index 0000000000..25813c4403 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/version.c @@ -0,0 +1,24 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2020 Evan Miller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mdbver.h" +#include "mdbtools.h" + +const char *mdb_get_version() { + return MDB_VERSION_NO; +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/worktable.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/worktable.c new file mode 100644 index 0000000000..720ec6f7fa --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/worktable.c @@ -0,0 +1,96 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2004 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mdbtools.h" +#include "mdbprivate.h" + +/* + * Temp table routines. These are currently used to generate mock results for + * commands like "list tables" and "describe table" + */ + +void +mdb_fill_temp_col(MdbColumn *tcol, char *col_name, int col_size, int col_type, int is_fixed) +{ + memset(tcol,0,sizeof(MdbColumn)); + snprintf(tcol->name, sizeof(tcol->name), "%s", col_name); + tcol->col_type = col_type; + if ((col_type == MDB_TEXT) || (col_type == MDB_MEMO)) { + tcol->col_size = col_size; + } else { + tcol->col_size = mdb_col_fixed_size(tcol); + } + tcol->is_fixed = is_fixed; +} +void +mdb_fill_temp_field(MdbField *field, void *value, int siz, int is_fixed, int is_null, int start, int colnum) +{ + field->value = value; + field->siz = siz; + field->is_fixed = is_fixed; + field->is_null = is_null; + field->start = start; + field->colnum = colnum; +} +MdbTableDef * +mdb_create_temp_table(MdbHandle *mdb, char *name) +{ + MdbCatalogEntry *entry; + MdbTableDef *table; + + /* dummy up a catalog entry */ + entry = g_malloc0(sizeof(MdbCatalogEntry)); + entry->mdb = mdb; + entry->object_type = MDB_TABLE; + entry->table_pg = 0; + snprintf(entry->object_name, sizeof(entry->object_name), "%s", name); + + table = mdb_alloc_tabledef(entry); + table->columns = g_ptr_array_new(); + table->is_temp_table = 1; + table->temp_table_pages = g_ptr_array_new(); + + return table; +} +void +mdb_temp_table_add_col(MdbTableDef *table, MdbColumn *col) +{ + col->table = table, + col->col_num = table->num_cols; + if (!col->is_fixed) + col->var_col_num = table->num_var_cols++; + g_ptr_array_add(table->columns, g_memdup2(col, sizeof(MdbColumn))); + table->num_cols++; +} +/* + * Should be called after setting up all temp table columns + */ +void mdb_temp_columns_end(MdbTableDef *table) +{ + MdbColumn *col; + unsigned int i; + unsigned int start = 0; + + for (i=0; inum_cols; i++) { + col = g_ptr_array_index(table->columns, i); + if (col->is_fixed) { + col->fixed_offset = start; + start += col->col_size; + } + } +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/write.c b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/write.c new file mode 100644 index 0000000000..44a48f248b --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/libmdb/write.c @@ -0,0 +1,934 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "mdbprivate.h" + +//static int mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg); +static int mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum); + +void +mdb_put_int16(void *buf, guint32 offset, guint32 value) +{ + unsigned char *u8_buf = (unsigned char *)buf + offset; + u8_buf[0] = (value & 0xFF); + u8_buf[1] = (value >> 8) & 0xFF; +} +void +_mdb_put_int16(void *buf, guint32 offset, guint32 value) +#ifdef HAVE_ATTRIBUTE_ALIAS +__attribute__((alias("mdb_put_int16"))); +#else +{ mdb_put_int16(buf, offset, value); } +#endif + +void +mdb_put_int32(void *buf, guint32 offset, guint32 value) +{ + unsigned char *u8_buf = (unsigned char *)buf + offset; + u8_buf[0] = (value & 0xFF); + u8_buf[1] = (value >> 8) & 0xFF; + u8_buf[2] = (value >> 16) & 0xFF; + u8_buf[3] = (value >> 24) & 0xFF; +} +void +_mdb_put_int32(void *buf, guint32 offset, guint32 value) +#ifdef HAVE_ATTRIBUTE_ALIAS +__attribute__((alias("mdb_put_int32"))); +#else +{ mdb_put_int32(buf, offset, value); } +#endif + +void +mdb_put_int32_msb(void *buf, guint32 offset, guint32 value) +{ + unsigned char *u8_buf = (unsigned char *)buf + offset; + u8_buf[3] = (value & 0xFF); + u8_buf[2] = (value >> 8) & 0xFF; + u8_buf[1] = (value >> 16) & 0xFF; + u8_buf[0] = (value >> 24) & 0xFF; +} +void +_mdb_put_int32_mdb(void *buf, guint32 offset, guint32 value) +#ifdef HAVE_ATTRIBUTE_ALIAS +__attribute__((alias("mdb_put_int32_msb"))); +#else +{ mdb_put_int32_msb(buf, offset, value); } +#endif + +ssize_t +mdb_write_pg(MdbHandle *mdb, unsigned long pg) +{ + ssize_t len; + off_t offset = pg * mdb->fmt->pg_size; + unsigned char *buf = mdb->pg_buf; + + fseeko(mdb->f->stream, 0, SEEK_END); + /* is page beyond current size + 1 ? */ + if (ftello(mdb->f->stream) < offset + mdb->fmt->pg_size) { + fprintf(stderr,"offset %" PRIu64 " is beyond EOF\n",(uint64_t)offset); + return 0; + } + fseeko(mdb->f->stream, offset, SEEK_SET); + + if (pg != 0 && mdb->f->db_key != 0) + { + buf = g_memdup2(mdb->pg_buf, mdb->fmt->pg_size); + unsigned int tmp_key = mdb->f->db_key ^ pg; + mdbi_rc4((unsigned char*)&tmp_key, 4, buf, mdb->fmt->pg_size); + } + + len = fwrite(buf, 1, mdb->fmt->pg_size, mdb->f->stream); + + if (buf != mdb->pg_buf) { + g_free(buf); + } + + if (ferror(mdb->f->stream)) { + perror("write"); + return 0; + } else if (lenfmt->pg_size) { + /* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->pg_size); */ + return 0; + } + mdb->cur_pos = 0; + return len; +} + +static int +mdb_is_col_indexed(MdbTableDef *table, int colnum) +{ + unsigned int i, j; + MdbIndex *idx; + + for (i=0;inum_idxs;i++) { + idx = g_ptr_array_index (table->indices, i); + for (j=0;jnum_keys;j++) { + if (idx->key_col_num[j]==colnum) return 1; + } + } + return 0; +} + +static int +mdb_crack_row4(MdbHandle *mdb, unsigned int row_start, unsigned int row_end, + unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets) +{ + unsigned int i; + + if (bitmask_sz + 3 + row_var_cols*2 + 2 > row_end) + return 0; + + for (i=0; ipg_buf, + row_end - bitmask_sz - 3 - (i*2)); + } + + return 1; +} +static int +mdb_crack_row3(MdbHandle *mdb, unsigned int row_start, unsigned int row_end, + unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets) +{ + unsigned int i; + unsigned int num_jumps = 0, jumps_used = 0; + unsigned int col_ptr, row_len; + + row_len = row_end - row_start + 1; + num_jumps = (row_len - 1) / 256; + col_ptr = row_end - bitmask_sz - num_jumps - 1; + /* If last jump is a dummy value, ignore it */ + if ((col_ptr-row_start-row_var_cols)/256 < num_jumps) + num_jumps--; + + if (bitmask_sz + num_jumps + 1 > row_end) + return 0; + + if (col_ptr >= (size_t)mdb->fmt->pg_size || col_ptr < row_var_cols) + return 0; + + jumps_used = 0; + for (i=0; ipg_buf[row_end-bitmask_sz-jumps_used-1])) { + jumps_used++; + } + var_col_offsets[i] = mdb->pg_buf[col_ptr-i]+(jumps_used*256); + } + + return 1; +} +/** + * @brief Cracks a row buffer apart into its component fields. + * @param table: Table that the row belongs to + * @param row_start: offset to start of row on current page + * @param row_size: offset to end of row on current page + * @param fields: pointer to MdbField array to be populated by mdb_crack_row + * + * + * A row buffer is that portion of a data page which contains the values for + * that row. Its beginning and end can be found in the row offset table. + * + * The resulting MdbField array contains pointers into the row for each field + * present. Be aware that by modifying field[]->value, you would be modifying + * the row buffer itself, not a copy. + * + * This routine is mostly used internally by mdb_fetch_row() but may have some + * applicability for advanced application programs. + * + * @return: number of fields present, or -1 if the buffer is invalid. + */ +int +mdb_crack_row(MdbTableDef *table, int row_start, size_t row_size, MdbField *fields) +{ + MdbColumn *col; + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + void *pg_buf = mdb->pg_buf; + unsigned int row_var_cols=0, row_cols; + unsigned char *nullmask; + unsigned int bitmask_sz; + unsigned int *var_col_offsets = NULL; + unsigned int fixed_cols_found, row_fixed_cols; + unsigned int col_count_size; + unsigned int i; + unsigned int row_end = row_start + row_size - 1; + + if (mdb_get_option(MDB_DEBUG_ROW)) { + mdb_buffer_dump(pg_buf, row_start, row_size); + } + + if (IS_JET3(mdb)) { + row_cols = mdb_get_byte(pg_buf, row_start); + col_count_size = 1; + } else { + row_cols = mdb_get_int16(pg_buf, row_start); + col_count_size = 2; + } + + bitmask_sz = (row_cols + 7) / 8; + if (bitmask_sz + !IS_JET3(mdb) >= row_end) { + fprintf(stderr, "warning: Invalid page buffer detected in mdb_crack_row.\n"); + return -1; + } + + nullmask = (unsigned char*)pg_buf + row_end - bitmask_sz + 1; + + /* read table of variable column locations */ + if (table->num_var_cols > 0) { + row_var_cols = IS_JET3(mdb) ? + mdb_get_byte(pg_buf, row_end - bitmask_sz) : + mdb_get_int16(pg_buf, row_end - bitmask_sz - 1); + var_col_offsets = g_malloc((row_var_cols+1)*sizeof(int)); + int success = 0; + if (IS_JET3(mdb)) { + success = mdb_crack_row3(mdb, row_start, row_end, bitmask_sz, + row_var_cols, var_col_offsets); + } else { + success = mdb_crack_row4(mdb, row_start, row_end, bitmask_sz, + row_var_cols, var_col_offsets); + } + if (!success) { + fprintf(stderr, "warning: Invalid page buffer detected in mdb_crack_row.\n"); + g_free(var_col_offsets); + return -1; + } + } + + fixed_cols_found = 0; + row_fixed_cols = row_cols - row_var_cols; + + if (mdb_get_option(MDB_DEBUG_ROW)) { + fprintf(stdout,"bitmask_sz %d\n", bitmask_sz); + fprintf(stdout,"row_var_cols %d\n", row_var_cols); + fprintf(stdout,"row_fixed_cols %d\n", row_fixed_cols); + } + + for (i=0;inum_cols;i++) { + unsigned int byte_num, bit_num; + unsigned int col_start; + col = g_ptr_array_index(table->columns,i); + fields[i].colnum = i; + fields[i].is_fixed = col->is_fixed; + byte_num = col->col_num / 8; + bit_num = col->col_num % 8; + /* logic on nulls is reverse, 1 is not null, 0 is null */ + fields[i].is_null = nullmask[byte_num] & (1 << bit_num) ? 0 : 1; + + if ((fields[i].is_fixed) + && (fixed_cols_found < row_fixed_cols)) { + col_start = col->fixed_offset + col_count_size; + fields[i].start = row_start + col_start; + fields[i].value = (char*)pg_buf + row_start + col_start; + fields[i].siz = col->col_size; + fixed_cols_found++; + /* Use col->var_col_num because a deleted column is still + * present in the variable column offsets table for the row */ + } else if ((!fields[i].is_fixed) + && (col->var_col_num < row_var_cols)) { + col_start = var_col_offsets[col->var_col_num]; + fields[i].start = row_start + col_start; + fields[i].value = (char*)pg_buf + row_start + col_start; + fields[i].siz = var_col_offsets[(col->var_col_num)+1] - + col_start; + } else { + fields[i].start = 0; + fields[i].value = NULL; + fields[i].siz = 0; + fields[i].is_null = 1; + } + if ((size_t)(fields[i].start + fields[i].siz) > row_start + row_size) { + fprintf(stderr, "warning: Invalid data location detected in mdb_crack_row. Table:%s Column:%i\n",table->name, i); + g_free(var_col_offsets); + return -1; + } + } + + g_free(var_col_offsets); + return row_cols; +} + +static int +mdb_pack_null_mask(unsigned char *buffer, int num_fields, MdbField *fields) +{ + int pos = 0, bit = 0, byte = 0; + int i; + + /* 'Not null' bitmap */ + for (i=0; i> 8) & 0xff; + + /* Fixed length columns */ + for (i=0;inum_var_cols == 0) { + pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); + return pos; + } + /* Variable length columns */ + for (i=0;i> 8) & 0xff; + pos += 2; + + /* Offsets of the variable-length columns */ + for (i=num_fields; i>0; i--) { + if (!fields[i-1].is_fixed) { + row_buffer[pos++] = fields[i-1].offset & 0xff; + row_buffer[pos++] = (fields[i-1].offset >> 8) & 0xff; + } + } + /* Number of variable-length columns */ + row_buffer[pos++] = var_cols & 0xff; + row_buffer[pos++] = (var_cols >> 8) & 0xff; + + pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); + return pos; +} + +static int +mdb_pack_row3(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields) +{ + unsigned int pos = 0; + unsigned int var_cols = 0; + unsigned int i, j; + unsigned char *offset_high; + + row_buffer[pos++] = num_fields; + + /* Fixed length columns */ + for (i=0;inum_var_cols == 0) { + pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); + return pos; + } + /* Variable length columns */ + for (i=0;i> 8) & 0xff; + j = 1; + + /* EOD */ + row_buffer[pos] = pos & 0xff; + pos++; + + /* Variable length column offsets */ + for (i=num_fields; i>0; i--) { + if (!fields[i-1].is_fixed) { + row_buffer[pos++] = fields[i-1].offset & 0xff; + offset_high[j++] = (fields[i-1].offset >> 8) & 0xff; + } + } + + /* Dummy jump table entry */ + if (offset_high[0] < (pos+(num_fields+7)/8-1)/255) { + row_buffer[pos++] = 0xff; + } + /* Jump table */ + for (i=0; i offset_high[i+1]) { + row_buffer[pos++] = var_cols-i; + } + } + g_free(offset_high); + + row_buffer[pos++] = var_cols; + + pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); + return pos; +} +int +mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, int unsigned num_fields, MdbField *fields) +{ + if (table->is_temp_table) { + unsigned int i; + for (i=0; icolumns, i); + fields[i].is_null = (fields[i].value) ? 0 : 1; + fields[i].colnum = i; + fields[i].is_fixed = c->is_fixed; + if ((c->col_type != MDB_TEXT) + && (c->col_type != MDB_MEMO)) { + fields[i].siz = c->col_size; + } + } + } + if (IS_JET3(table->entry->mdb)) { + return mdb_pack_row3(table, row_buffer, num_fields, fields); + } else { + return mdb_pack_row4(table, row_buffer, num_fields, fields); + } +} +int +mdb_pg_get_freespace(MdbHandle *mdb) +{ + int rows, free_start, free_end; + int row_count_offset = mdb->fmt->row_count_offset; + + rows = mdb_get_int16(mdb->pg_buf, row_count_offset); + free_start = row_count_offset + 2 + (rows * 2); + free_end = mdb_get_int16(mdb->pg_buf, row_count_offset + (rows * 2)); + mdb_debug(MDB_DEBUG_WRITE,"free space left on page = %d", free_end - free_start); + return (free_end - free_start); +} +void * +mdb_new_leaf_pg(MdbCatalogEntry *entry) +{ + MdbHandle *mdb = entry->mdb; + void *new_pg = g_malloc0(mdb->fmt->pg_size); + + mdb_put_int16(new_pg, 0, 0x0104); + mdb_put_int32(new_pg, 4, entry->table_pg); + + return new_pg; +} +void * +mdb_new_data_pg(MdbCatalogEntry *entry) +{ + MdbFormatConstants *fmt = entry->mdb->fmt; + void *new_pg = g_malloc0(fmt->pg_size); + + mdb_put_int16(new_pg, 0, 0x0101); + mdb_put_int16(new_pg, 2, fmt->pg_size - fmt->row_count_offset - 2); + mdb_put_int32(new_pg, 4, entry->table_pg); + + return new_pg; +} + +/* could be static */ +int +mdb_update_indexes(MdbTableDef *table, int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum) +{ + unsigned int i; + MdbIndex *idx; + + for (i=0;inum_idxs;i++) { + idx = g_ptr_array_index (table->indices, i); + mdb_debug(MDB_DEBUG_WRITE,"Updating %s (%d).", idx->name, idx->index_type); + if (idx->index_type==1) { + mdb_update_index(table, idx, num_fields, fields, pgnum, rownum); + } + } + return 1; +} + +int +mdb_init_index_chain(MdbTableDef *table, MdbIndex *idx) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + + table->scan_idx = idx; + table->chain = g_malloc0(sizeof(MdbIndexChain)); + table->mdbidx = mdb_clone_handle(mdb); + mdb_read_pg(table->mdbidx, table->scan_idx->first_pg); + + return 1; +} + +/* could be static */ +int +mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + /*int idx_xref[16];*/ + unsigned int i, j; + MdbIndexChain *chain; + MdbField idx_fields[10]; + + for (i = 0; i < idx->num_keys; i++) { + for (j = 0; j < num_fields; j++) { + // key_col_num is 1 based, can't remember why though + if (fields[j].colnum == idx->key_col_num[i]-1) { + /* idx_xref[i] = j; */ + idx_fields[i] = fields[j]; + } + } + } +/* + for (i = 0; i < idx->num_keys; i++) { + fprintf(stdout, "key col %d (%d) is mapped to field %d (%d %d)\n", + i, idx->key_col_num[i], idx_xref[i], fields[idx_xref[i]].colnum, + fields[idx_xref[i]].siz); + } + for (i = 0; i < num_fields; i++) { + fprintf(stdout, "%d (%d %d)\n", + i, fields[i].colnum, + fields[i].siz); + } +*/ + + chain = g_malloc0(sizeof(MdbIndexChain)); + + mdb_index_find_row(mdb, idx, chain, pgnum, rownum); + //printf("chain depth = %d\n", chain->cur_depth); + //printf("pg = %" G_GUINT32_FORMAT "\n", + //chain->pages[chain->cur_depth-1].pg); + //mdb_copy_index_pg(table, idx, &chain->pages[chain->cur_depth-1]); + mdb_add_row_to_leaf_pg(table, idx, &chain->pages[chain->cur_depth-1], idx_fields, pgnum, rownum); + + return 1; +} + +int +mdb_insert_row(MdbTableDef *table, int num_fields, MdbField *fields) +{ + int new_row_size; + unsigned char row_buffer[4096]; + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + gint32 pgnum; + guint16 rownum; + + if (!mdb->f->writable) { + fprintf(stderr, "File is not open for writing\n"); + return 0; + } + new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields); + if (mdb_get_option(MDB_DEBUG_WRITE)) { + mdb_buffer_dump(row_buffer, 0, new_row_size); + } + pgnum = mdb_map_find_next_freepage(table, new_row_size); + if (!pgnum || pgnum == -1) { + fprintf(stderr, "Unable to allocate new page.\n"); + return 0; + } + + rownum = mdb_add_row_to_pg(table, row_buffer, new_row_size); + + if (mdb_get_option(MDB_DEBUG_WRITE)) { + mdb_buffer_dump(mdb->pg_buf, 0, 40); + mdb_buffer_dump(mdb->pg_buf, fmt->pg_size - 160, 160); + } + mdb_debug(MDB_DEBUG_WRITE, "writing page %d", pgnum); + if (!mdb_write_pg(mdb, pgnum)) { + fprintf(stderr, "write failed!\n"); + return 0; + } + + mdb_update_indexes(table, num_fields, fields, pgnum, rownum); + + return 1; +} +/* + * Assumes caller has verfied space is available on page and adds the new + * row to the current pg_buf. + */ +guint16 +mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size) +{ + void *new_pg; + int num_rows, i, pos, row_start; + size_t row_size; + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + + if (table->is_temp_table) { + GPtrArray *pages = table->temp_table_pages; + if (pages->len == 0) { + new_pg = mdb_new_data_pg(entry); + g_ptr_array_add(pages, new_pg); + } else { + new_pg = g_ptr_array_index(pages, pages->len - 1); + if (mdb_get_int16(new_pg, 2) < new_row_size + 2) { + new_pg = mdb_new_data_pg(entry); + g_ptr_array_add(pages, new_pg); + } + } + + num_rows = mdb_get_int16(new_pg, fmt->row_count_offset); + pos = (num_rows == 0) ? fmt->pg_size : + mdb_get_int16(new_pg, fmt->row_count_offset + (num_rows*2)); + } else { /* is not a temp table */ + new_pg = mdb_new_data_pg(entry); + + num_rows = mdb_get_int16(mdb->pg_buf, fmt->row_count_offset); + pos = fmt->pg_size; + + /* copy existing rows */ + for (i=0;ipg_buf + row_start, row_size); + mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos); + } + } + + /* add our new row */ + pos -= new_row_size; + memcpy((char*)new_pg + pos, row_buffer, new_row_size); + /* add row to the row offset table */ + mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (num_rows*2), pos); + + /* update number rows on this page */ + num_rows++; + mdb_put_int16(new_pg, fmt->row_count_offset, num_rows); + + /* update the freespace */ + mdb_put_int16(new_pg,2,pos - fmt->row_count_offset - 2 - (num_rows*2)); + + /* copy new page over old */ + if (!table->is_temp_table) { + memcpy(mdb->pg_buf, new_pg, fmt->pg_size); + g_free(new_pg); + } + + return num_rows; +} +int +mdb_update_row(MdbTableDef *table) +{ + int row_start, row_end; + unsigned int i; + MdbColumn *col; + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbField fields[256]; + unsigned char row_buffer[4096]; + size_t old_row_size, new_row_size; + int num_fields; + + if (!mdb->f->writable) { + fprintf(stderr, "File is not open for writing\n"); + return 0; + } + mdb_find_row(mdb, table->cur_row-1, &row_start, &old_row_size); + row_end = row_start + old_row_size - 1; + + row_start &= 0x0FFF; /* remove flags */ + + mdb_debug(MDB_DEBUG_WRITE,"page %lu row %d start %d end %d", (unsigned long) table->cur_phys_pg, table->cur_row-1, row_start, row_end); + if (mdb_get_option(MDB_DEBUG_LIKE)) + mdb_buffer_dump(mdb->pg_buf, row_start, old_row_size); + + for (i=0;inum_cols;i++) { + col = g_ptr_array_index(table->columns,i); + if (col->bind_ptr && mdb_is_col_indexed(table,i)) { + fprintf(stderr, "Attempting to update column that is part of an index\n"); + return 0; + } + } + num_fields = mdb_crack_row(table, row_start, old_row_size, fields); + if (num_fields == -1) { + fprintf(stderr, "Invalid row buffer, update will not occur\n"); + return 0; + } + + if (mdb_get_option(MDB_DEBUG_WRITE)) { + /* + for (i=0;inum_cols;i++) { + col = g_ptr_array_index(table->columns,i); + if (col->bind_ptr) { + fields[i].value = col->bind_ptr; + fields[i].siz = *(col->len_ptr); + } + } + + new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields); + if (mdb_get_option(MDB_DEBUG_WRITE)) + mdb_buffer_dump(row_buffer, 0, new_row_size); + if (new_row_size > (old_row_size + mdb_pg_get_freespace(mdb))) { + fprintf(stderr, "No space left on this page, update will not occur\n"); + return 0; + } + /* do it! */ + mdb_replace_row(table, table->cur_row-1, row_buffer, new_row_size); + return 0; /* FIXME */ +} + +/** + * \warning the return code is opposite to convention used elsewhere: + * @return: + * - 0 on success + * - 1 on failure + * \note This might change on next ABI break. + */ +int +mdb_replace_row(MdbTableDef *table, int row, void *new_row, int new_row_size) +{ +MdbCatalogEntry *entry = table->entry; +MdbHandle *mdb = entry->mdb; +int pg_size = mdb->fmt->pg_size; +int rco = mdb->fmt->row_count_offset; + void *new_pg; +guint16 num_rows; + int row_start; + size_t row_size; +int i, pos; + + if (mdb_get_option(MDB_DEBUG_WRITE)) { + mdb_buffer_dump(mdb->pg_buf, 0, 40); + mdb_buffer_dump(mdb->pg_buf, pg_size - 160, 160); + } + mdb_debug(MDB_DEBUG_WRITE,"updating row %d on page %lu", row, (unsigned long) table->cur_phys_pg); + new_pg = mdb_new_data_pg(entry); + + num_rows = mdb_get_int16(mdb->pg_buf, rco); + mdb_put_int16(new_pg, rco, num_rows); + + pos = pg_size; + + /* rows before */ + for (i=0;ipg_buf + row_start, row_size); + mdb_put_int16(new_pg, rco + 2 + i*2, pos); + } + + /* our row */ + pos -= new_row_size; + memcpy((char*)new_pg + pos, new_row, new_row_size); + mdb_put_int16(new_pg, rco + 2 + row*2, pos); + + /* rows after */ + for (i=row+1;ipg_buf + row_start, row_size); + mdb_put_int16(new_pg, rco + 2 + i*2, pos); + } + + /* almost done, copy page over current */ + memcpy(mdb->pg_buf, new_pg, pg_size); + + g_free(new_pg); + + mdb_put_int16(mdb->pg_buf, 2, mdb_pg_get_freespace(mdb)); + if (mdb_get_option(MDB_DEBUG_WRITE)) { + mdb_buffer_dump(mdb->pg_buf, 0, 40); + mdb_buffer_dump(mdb->pg_buf, pg_size - 160, 160); + } + /* drum roll, please */ + if (!mdb_write_pg(mdb, table->cur_phys_pg)) { + fprintf(stderr, "write failed!\n"); + return 1; + } + return 0; +} +static int +mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum) +/*, guint32 pgnum, guint16 rownum) +static int +mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg) +*/ +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbColumn *col; + guint32 pg_row; + guint16 row = 0; + void *new_pg; + unsigned char key_hash[256]; + int keycol; + + new_pg = mdb_new_leaf_pg(entry); + + /* reinitial ipg pointers to start of page */ + mdb_index_page_reset(mdb, ipg); + mdb_read_pg(mdb, ipg->pg); + + /* do we support this index type yet? */ + if (idx->num_keys > 1) { + fprintf(stderr,"multikey indexes not yet supported, aborting\n"); + return 0; + } + keycol = idx->key_col_num[0]; + col = g_ptr_array_index (table->columns, keycol - 1); + if (!col->is_fixed) { + fprintf(stderr,"variable length key columns not yet supported, aborting\n"); + return 0; + } + + while (mdb_index_find_next_on_page(mdb, ipg)) { + + /* check for compressed indexes. */ + if (ipg->len < col->col_size + 1) { + fprintf(stderr,"compressed indexes not yet supported, aborting\n"); + return 0; + } + + pg_row = mdb_get_int32_msb(mdb->pg_buf, ipg->offset + ipg->len - 4); + /* guint32 pg = pg_row >> 8; */ + row = pg_row & 0xff; + /* unsigned char iflag = mdb->pg_buf[ipg->offset]; */ + + /* turn the key hash back into a value */ + mdb_index_swap_n(&mdb->pg_buf[ipg->offset + 1], col->col_size, key_hash); + key_hash[col->col_size - 1] &= 0x7f; + + if (mdb_get_option(MDB_DEBUG_WRITE)) { + mdb_buffer_dump(mdb->pg_buf, ipg->offset, ipg->len); + mdb_buffer_dump(mdb->pg_buf, ipg->offset + 1, col->col_size); + mdb_buffer_dump(key_hash, 0, col->col_size); + } + + memcpy((char*)new_pg + ipg->offset, mdb->pg_buf + ipg->offset, ipg->len); + ipg->offset += ipg->len; + ipg->len = 0; + + row++; + } + + if (!row) { + fprintf(stderr,"missing indexes not yet supported, aborting\n"); + return 0; + } + //mdb_put_int16(new_pg, mdb->fmt->row_count_offset, row); + /* free space left */ + mdb_put_int16(new_pg, 2, mdb->fmt->pg_size - ipg->offset); + //printf("offset = %d\n", ipg->offset); + + mdb_index_swap_n(idx_fields[0].value, col->col_size, key_hash); + key_hash[0] |= 0x080; + if (mdb_get_option(MDB_DEBUG_WRITE)) { + printf("key_hash\n"); + mdb_buffer_dump(idx_fields[0].value, 0, col->col_size); + mdb_buffer_dump(key_hash, 0, col->col_size); + printf("--------\n"); + } + ((char *)new_pg)[ipg->offset] = 0x7f; + memcpy((char*)new_pg + ipg->offset + 1, key_hash, col->col_size); + pg_row = (pgnum << 8) | ((rownum-1) & 0xff); + mdb_put_int32_msb(new_pg, ipg->offset + 5, pg_row); + ipg->idx_starts[row++] = ipg->offset + ipg->len; + //ipg->idx_starts[row] = ipg->offset + ipg->len; + if (mdb_get_option(MDB_DEBUG_WRITE)) { + mdb_buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size); + } + memcpy(mdb->pg_buf, new_pg, mdb->fmt->pg_size); + mdb_index_pack_bitmap(mdb, ipg); + if (mdb_get_option(MDB_DEBUG_WRITE)) { + mdb_buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size); + } + g_free(new_pg); + + return ipg->len; +} diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbfakeglib.h b/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbfakeglib.h new file mode 100644 index 0000000000..daf93c17c9 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbfakeglib.h @@ -0,0 +1,193 @@ +/* fakeglib.c - A shim for applications that require GLib + * without the whole kit and kaboodle. + * + * Copyright (C) 2020 Evan Miller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _mdbfakeglib_h_ +#define _mdbfakeglib_h_ + +#include +#include +#include +#include + +typedef uint16_t guint16; +typedef uint32_t guint32; +typedef uint64_t guint64; +typedef int32_t gint32; +typedef char gchar; +typedef int gboolean; +typedef int gint; +typedef unsigned int guint; +typedef void * gpointer; +typedef const void * gconstpointer; +typedef uint8_t guint8; +typedef guint32 GQuark; +typedef guint32 gunichar; +typedef signed long gssize; + +typedef guint (*GHashFunc)(gconstpointer); +typedef int (*GCompareFunc)(gconstpointer, gconstpointer); +typedef gboolean (*GEqualFunc)(gconstpointer, gconstpointer); +typedef void (*GFunc) (gpointer data, gpointer user_data); +typedef void (*GHFunc)(gpointer key, gpointer value, gpointer data); +typedef gboolean (*GHRFunc)(gpointer key, gpointer value, gpointer data); + +typedef struct GString { + gchar *str; + size_t len; + size_t allocated_len; +} GString; + +typedef struct GPtrArray { + void **pdata; + guint len; +} GPtrArray; + +typedef struct GList { + gpointer data; + struct GList *next; + struct GList *prev; +} GList; + +typedef struct GHashTable { + GEqualFunc compare; + GPtrArray *array; +} GHashTable; + +typedef struct GError { + GQuark domain; + gint code; + gchar *message; +} GError; + +typedef enum GOptionArg { + G_OPTION_ARG_NONE, + G_OPTION_ARG_STRING, + G_OPTION_ARG_INT, + G_OPTION_ARG_CALLBACK, + G_OPTION_ARG_FILENAME +} GOptionArg; + +typedef enum GOptionFlags { + G_OPTION_FLAG_NONE, + G_OPTION_FLAG_REVERSE +} GOptionFlags; + +typedef struct GOptionEntry { + const gchar *long_name; + gchar short_name; + gint flags; + + GOptionArg arg; + gpointer arg_data; + + const gchar *description; + const gchar *arg_description; +} GOptionEntry; + +typedef struct GOptionContext { + const char *desc; + const GOptionEntry *entries; +} GOptionContext; + +#define g_str_hash NULL + +#define G_GUINT32_FORMAT PRIu32 + +#define g_return_val_if_fail(a, b) if (!a) { return b; } + +#define g_ascii_strcasecmp strcasecmp +#define g_malloc0(len) calloc(1, len) +#define g_malloc malloc +#define g_free free +#define g_realloc realloc +#define g_memdup2 g_memdup + +#define G_STR_DELIMITERS "_-|> <." + +#define g_ptr_array_index(array, i) \ + ((void **)array->pdata)[i] + +#define TRUE 1 +#define FALSE 0 + +#define GUINT32_SWAP_LE_BE(l) __builtin_bswap32((uint32_t)(l)) + +/* string functions */ +void *g_memdup(const void *src, size_t len); +int g_str_equal(const void *str1, const void *str2); +char **g_strsplit(const char *haystack, const char *needle, int max_tokens); +void g_strfreev(char **dir); +char *g_strconcat(const char *first, ...); +char *g_strdup(const char *src); +char *g_strndup(const char *src, size_t len); +char *g_strdup_printf(const char *format, ...); +gchar *g_strdelimit(gchar *string, const gchar *delimiters, gchar new_delimiter); +void g_printerr(const gchar *format, ...); + +/* conversion */ +gint g_unichar_to_utf8(gunichar c, gchar *dst); +gchar *g_locale_to_utf8(const gchar *opsysstring, size_t len, + size_t *bytes_read, size_t *bytes_written, GError **error); +gchar *g_utf8_casefold(const gchar *str, gssize len); +gchar *g_utf8_strdown(const gchar *str, gssize len); + +/* GString */ +GString *g_string_new(const gchar *init); +GString *g_string_assign(GString *string, const gchar *rval); +GString * g_string_append (GString *string, const gchar *val); +gchar *g_string_free (GString *string, gboolean free_segment); + +/* GHashTable */ +void *g_hash_table_lookup(GHashTable *tree, const void *key); +gboolean g_hash_table_lookup_extended(GHashTable *table, const void *lookup_key, + void **orig_key, void **value); +void g_hash_table_insert(GHashTable *tree, void *key, void *value); +gboolean g_hash_table_remove(GHashTable *hash_table, const void *key); +GHashTable *g_hash_table_new(GHashFunc hashes, GEqualFunc equals); +void g_hash_table_foreach(GHashTable *tree, GHFunc function, void *data); +void g_hash_table_foreach_remove(GHashTable *tree, GHRFunc function, void *data); +void g_hash_table_destroy(GHashTable *tree); + +/* GPtrArray */ +void g_ptr_array_sort(GPtrArray *array, GCompareFunc func); +void g_ptr_array_foreach(GPtrArray *array, GFunc function, gpointer user_data); +GPtrArray *g_ptr_array_new(void); +void g_ptr_array_add(GPtrArray *array, void *entry); +gboolean g_ptr_array_remove (GPtrArray *array, gpointer data); +void g_ptr_array_free(GPtrArray *array, gboolean something); + +/* GList */ +GList *g_list_append(GList *list, void *data); +GList *g_list_last(GList *list); +GList *g_list_remove(GList *list, void *data); +void g_list_free(GList *list); + +/* GOption */ +GOptionContext *g_option_context_new(const char *description); +void g_option_context_add_main_entries (GOptionContext *context, + const GOptionEntry *entries, + const gchar *translation_domain); +gchar *g_option_context_get_help (GOptionContext *context, + gboolean main_help, void *group); +gboolean g_option_context_parse (GOptionContext *context, + gint *argc, gchar ***argv, GError **error); +void g_option_context_free (GOptionContext *context); + +#endif diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbprivate.h b/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbprivate.h new file mode 100644 index 0000000000..b793e9ae7f --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbprivate.h @@ -0,0 +1,51 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MDBPRIVATE_H +#define MDBPRIVATE_H + +#include "mdbtools.h" + +/* + * This header is for stuff lacking a MDB_ or mdb_ something, or functions only + * used within mdbtools so they won't be exported to calling programs. + */ + +#ifdef __cplusplus + extern "C" { +#endif + +void mdbi_rc4(unsigned char *key, guint32 key_len, unsigned char *buf, guint32 buf_len); +MdbBackend *mdbi_register_backend2(MdbHandle *mdb, char *backend_name, guint32 capabilities, + const MdbBackendType *backend_type, + const MdbBackendType *type_shortdate, + const MdbBackendType *type_autonum, + const char *short_now, const char *long_now, + const char *date_fmt, const char *shortdate_fmt, + const char *charset_statement, const char *create_table_statement, + const char *drop_statement, const char *constaint_not_empty_statement, + const char *column_comment_statement, const char *per_column_comment_statement, + const char *table_comment_statement, const char *per_table_comment_statement, + gchar* (*quote_schema_name)(const gchar*, const gchar*), + gchar* (*normalise_case)(const gchar*)); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbsql.h b/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbsql.h new file mode 100644 index 0000000000..4225a81dde --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbsql.h @@ -0,0 +1,112 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _mdbsql_h_ +#define _mdbsql_h_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include +#include +#include + +typedef struct MdbSQL +{ + MdbHandle *mdb; + int all_columns; + int sel_count; + unsigned int num_columns; + GPtrArray *columns; + unsigned int num_tables; + GPtrArray *tables; + MdbTableDef *cur_table; + MdbSargNode *sarg_tree; + GList *sarg_stack; + GPtrArray *bound_values; + unsigned char *kludge_ttable_pg; + long max_rows; + char error_msg[1024]; + int limit; + int limit_percent; + long row_count; +} MdbSQL; + +typedef struct { + char *name; + int disp_size; + void *bind_addr; /* if !NULL then cp parameter to here */ + int bind_type; + int *bind_len; + int bind_max; +} MdbSQLColumn; + +typedef struct { + char *name; + char *alias; +} MdbSQLTable; + +typedef struct { + char *col_name; + MdbSarg *sarg; +} MdbSQLSarg; + +#define mdb_sql_has_error(sql) ((sql)->error_msg[0] ? 1 : 0) +#define mdb_sql_last_error(sql) ((sql)->error_msg) + +void mdb_sql_error(MdbSQL* sql, const char *fmt, ...); +MdbSQL *mdb_sql_init(void); +MdbSQLSarg *mdb_sql_alloc_sarg(void); +MdbHandle *mdb_sql_open(MdbSQL *sql, char *db_name); +void mdb_sql_free_tree(MdbSargNode *tree); +int mdb_sql_add_sarg(MdbSQL *sql, char *col_name, int op, char *constant); +void mdb_sql_all_columns(MdbSQL *sql); +void mdb_sql_sel_count(MdbSQL *sql); +int mdb_sql_add_column(MdbSQL *sql, char *column_name); +int mdb_sql_add_table(MdbSQL *sql, char *table_name); +char *mdb_sql_strptime(MdbSQL *sql, char *data, char *format); +void mdb_sql_dump(MdbSQL *sql); +void mdb_sql_exit(MdbSQL *sql); +void mdb_sql_reset(MdbSQL *sql); +void mdb_sql_listtables(MdbSQL *sql); +void mdb_sql_select(MdbSQL *sql); +void mdb_sql_dump_node(MdbSargNode *node, int level); +void mdb_sql_close(MdbSQL *sql); +void mdb_sql_add_or(MdbSQL *sql); +void mdb_sql_add_and(MdbSQL *sql); +void mdb_sql_add_not(MdbSQL *sql); +void mdb_sql_describe_table(MdbSQL *sql); +MdbSQL* mdb_sql_run_query (MdbSQL*, const gchar*); +void mdb_sql_set_maxrow(MdbSQL *sql, int maxrow); +int mdb_sql_eval_expr(MdbSQL *sql, char *const1, int op, char *const2); +int mdb_sql_bind_all(MdbSQL *sql); +void mdb_sql_unbind_all(MdbSQL *sql); +int mdb_sql_fetch_row(MdbSQL *sql, MdbTableDef *table); +int mdb_sql_add_temp_col(MdbSQL *sql, MdbTableDef *ttable, int col_num, char *name, int col_type, int col_size, int is_fixed); +int mdb_sql_bind_column(MdbSQL *sql, int colnum, void *varaddr, int *len_ptr); +int mdb_sql_add_limit(MdbSQL *sql, char *limit, int percent); +int mdb_sql_get_limit(MdbSQL *sql); + +int parse_sql(MdbSQL * mdb, const gchar* str); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbtools.h b/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbtools.h new file mode 100644 index 0000000000..280b4e30ad --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbtools.h @@ -0,0 +1,696 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef _mdbtools_h_ +#define _mdbtools_h_ + +#ifndef TLS +#if defined(_MSC_VER) +#define TLS __declspec(thread) +#elif defined(__GNUC__) +#define TLS __thread +#elif defined(__cplusplus) +#define TLS thread_local +#elif __STDC_VERSION__ >= 201112L +#define TLS _Thread_local +#else +#define TLS +#endif +#endif + +#define MDBTOOLS_H_HAVE_ICONV_H 1 +#define MDBTOOLS_H_HAVE_XLOCALE_H 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MDBTOOLS_H_HAVE_ICONV_H +#include +#endif + +#if MDBTOOLS_H_HAVE_XLOCALE_H +#include +#endif + +#ifdef _WIN32 +#include +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/** \addtogroup mdbtools + * @{ + */ + +#define MDB_DEBUG 0 + +#define MDB_PGSIZE 4096 +//#define MDB_MAX_OBJ_NAME (256*3) /* unicode 16 -> utf-8 worst case */ +#define MDB_MAX_OBJ_NAME 256 +#define MDB_MAX_COLS 256 +#define MDB_MAX_IDX_COLS 10 +#define MDB_CATALOG_PG 18 +#define MDB_MEMO_OVERHEAD 12 +#define MDB_BIND_SIZE 16384 // override with mdb_set_bind_size(MdbHandle*, size_t) + +// This attribute is not supported by all compilers: +// M$VC see http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc +#define MDB_DEPRECATED(type, funcname) type __attribute__((deprecated)) funcname + +#ifdef __MINGW32__ + #include + #ifndef locale_t + typedef _locale_t locale_t; + #endif +#endif + +typedef locale_t mdb_locale_t; + +enum { + MDB_PAGE_DB = 0, + MDB_PAGE_DATA, + MDB_PAGE_TABLE, + MDB_PAGE_INDEX, + MDB_PAGE_LEAF, + MDB_PAGE_MAP +}; +enum { + MDB_VER_JET3 = 0, + MDB_VER_JET4 = 0x01, + MDB_VER_ACCDB_2007 = 0x02, + MDB_VER_ACCDB_2010 = 0x03, + MDB_VER_ACCDB_2013 = 0x04, + MDB_VER_ACCDB_2016 = 0x05, + MDB_VER_ACCDB_2019 = 0x06 +}; +enum { + MDB_FORM = 0, + MDB_TABLE, + MDB_MACRO, + MDB_SYSTEM_TABLE, + MDB_REPORT, + MDB_QUERY, + MDB_LINKED_TABLE, + MDB_MODULE, + MDB_RELATIONSHIP, + MDB_UNKNOWN_09, + MDB_UNKNOWN_0A, /* User access */ + MDB_DATABASE_PROPERTY, + MDB_ANY = -1 +}; +enum { + MDB_BOOL = 0x01, + MDB_BYTE = 0x02, + MDB_INT = 0x03, + MDB_LONGINT = 0x04, + MDB_MONEY = 0x05, + MDB_FLOAT = 0x06, + MDB_DOUBLE = 0x07, + MDB_DATETIME = 0x08, + MDB_BINARY = 0x09, + MDB_TEXT = 0x0a, + MDB_OLE = 0x0b, + MDB_MEMO = 0x0c, + MDB_REPID = 0x0f, + MDB_NUMERIC = 0x10, + MDB_COMPLEX = 0x12 +}; + +/* SARG operators */ +enum { + MDB_OR = 1, + MDB_AND, + MDB_NOT, + MDB_EQUAL, + MDB_GT, + MDB_LT, + MDB_GTEQ, + MDB_LTEQ, + MDB_LIKE, + MDB_ISNULL, + MDB_NOTNULL, + MDB_ILIKE, + MDB_NEQ, +}; + +typedef enum { + MDB_TABLE_SCAN, + MDB_LEAF_SCAN, + MDB_INDEX_SCAN +} MdbStrategy; + +typedef enum { + MDB_NOFLAGS = 0x00, + MDB_WRITABLE = 0x01 +} MdbFileFlags; + +enum { + MDB_DEBUG_LIKE = 0x0001, + MDB_DEBUG_WRITE = 0x0002, + MDB_DEBUG_USAGE = 0x0004, + MDB_DEBUG_OLE = 0x0008, + MDB_DEBUG_ROW = 0x0010, + MDB_DEBUG_PROPS = 0x0020, + MDB_USE_INDEX = 0x0040, + MDB_NO_MEMO = 0x0080, /* don't follow memo fields */ +}; + +typedef enum { + MDB_BRACES_4_2_2_8, /* "{XXXX-XX-XX-XXXXXXXX}" format */ + MDB_NOBRACES_4_2_2_2_6, /* "XXXX-XX-XX-XX-XXXXXX" format (matches MS Access ODBC driver) */ +} MdbUuidFormat; + +#define mdb_is_logical_op(x) (x == MDB_OR || \ + x == MDB_AND || \ + x == MDB_NOT ) + +#define mdb_is_relational_op(x) (x == MDB_EQUAL || \ + x == MDB_GT || \ + x == MDB_LT || \ + x == MDB_GTEQ || \ + x == MDB_LTEQ || \ + x == MDB_NEQ || \ + x == MDB_LIKE || \ + x == MDB_ILIKE || \ + x == MDB_ISNULL || \ + x == MDB_NOTNULL ) + +enum { + MDB_ASC, + MDB_DESC +}; + +enum { + MDB_IDX_UNIQUE = 0x01, + MDB_IDX_IGNORENULLS = 0x02, + MDB_IDX_REQUIRED = 0x08 +}; + +/* export schema options */ +enum { + MDB_SHEXP_DROPTABLE = 1<<0, /* issue drop table during export */ + MDB_SHEXP_CST_NOTNULL = 1<<1, /* generate NOT NULL constraints */ + MDB_SHEXP_CST_NOTEMPTY = 1<<2, /* <>'' constraints */ + MDB_SHEXP_COMMENTS = 1<<3, /* export comments on columns & tables */ + MDB_SHEXP_DEFVALUES = 1<<4, /* export default values */ + MDB_SHEXP_INDEXES = 1<<5, /* export indices */ + MDB_SHEXP_RELATIONS = 1<<6, /* export relation (foreign keys) */ + MDB_SHEXP_BULK_INSERT = 1 << 7 /* export data in bulk inserts */ +}; +#define MDB_SHEXP_DEFAULT (MDB_SHEXP_CST_NOTNULL | MDB_SHEXP_COMMENTS | MDB_SHEXP_INDEXES | MDB_SHEXP_RELATIONS) + +/* csv export binary options */ +enum { + MDB_BINEXPORT_STRIP, + MDB_BINEXPORT_RAW, + MDB_BINEXPORT_OCTAL, + MDB_BINEXPORT_HEXADECIMAL, + + /* Flags that can be OR'ed into the above when calling mdb_print_col */ + MDB_EXPORT_ESCAPE_CONTROL_CHARS = (1 << 4) +}; + +#define IS_JET4(mdb) (mdb->f->jet_version==MDB_VER_JET4) /* obsolete */ +#define IS_JET3(mdb) (mdb->f->jet_version==MDB_VER_JET3) + +/* forward declarations */ +typedef struct mdbindex MdbIndex; +typedef struct mdbsargtree MdbSargNode; + +typedef struct { + char *name; + unsigned char needs_precision; + unsigned char needs_scale; + unsigned char needs_byte_length; + unsigned char needs_char_length; +} MdbBackendType; + +typedef struct { + guint32 capabilities; /* see MDB_SHEXP_* */ + const MdbBackendType *types_table; + const MdbBackendType *type_shortdate; + const MdbBackendType *type_autonum; + const char *short_now; + const char *long_now; + const char *date_fmt; + const char *shortdate_fmt; + const char *charset_statement; + const char *drop_statement; + const char *constaint_not_empty_statement; + const char *column_comment_statement; + const char *per_column_comment_statement; + const char *table_comment_statement; + const char *per_table_comment_statement; + gchar* (*quote_schema_name)(const gchar*, const gchar*); + const char *create_table_statement; + gchar* (*normalise_case)(const gchar*); +} MdbBackend; + +typedef struct { + gboolean collect; + unsigned long pg_reads; +} MdbStatistics; + +typedef struct { + FILE *stream; + gboolean writable; + guint32 jet_version; + guint32 db_key; + char db_passwd[14]; + MdbStatistics *stats; + /* free map */ + int map_sz; + unsigned char *free_map; + /* reference count */ + int refs; + guint16 code_page; + guint16 lang_id; +} MdbFile; + +/* offset to row count on data pages...version dependant */ +typedef struct { + ssize_t pg_size; + guint16 row_count_offset; + guint16 tab_num_rows_offset; + guint16 tab_num_cols_offset; + guint16 tab_num_idxs_offset; + guint16 tab_num_ridxs_offset; + guint16 tab_usage_map_offset; + guint16 tab_first_dpg_offset; + guint16 tab_cols_start_offset; + guint16 tab_ridx_entry_size; + guint16 col_flags_offset; + guint16 col_size_offset; + guint16 col_num_offset; + guint16 tab_col_entry_size; + guint16 tab_free_map_offset; + guint16 tab_col_offset_var; + guint16 tab_col_offset_fixed; + guint16 tab_row_col_num_offset; + guint16 col_scale_offset; + guint16 col_prec_offset; +} MdbFormatConstants; + +typedef struct { + MdbFile *f; + guint32 cur_pg; + guint16 row_num; + unsigned int cur_pos; + unsigned char pg_buf[MDB_PGSIZE]; + unsigned char alt_pg_buf[MDB_PGSIZE]; + MdbFormatConstants *fmt; + size_t bind_size; + char date_fmt[64]; + char shortdate_fmt[64]; + MdbUuidFormat repid_fmt; + const char *boolean_false_value; + const char *boolean_true_value; + unsigned int num_catalog; + + // Non-cloneable fields start here + GPtrArray *catalog; + MdbBackend *default_backend; + char *backend_name; + struct S_MdbTableDef *relationships_table; + char *relationships_values[5]; + MdbStatistics *stats; + GHashTable *backends; +#if MDBTOOLS_H_HAVE_ICONV_H + iconv_t iconv_in; + iconv_t iconv_out; +#else + mdb_locale_t locale; +#endif +} MdbHandle; + +typedef struct { + MdbHandle *mdb; + char object_name[MDB_MAX_OBJ_NAME+1]; + int object_type; + unsigned long table_pg; /* misnomer since object may not be a table */ + //int num_props; please use props->len + GPtrArray *props; /* GPtrArray of MdbProperties */ + int flags; +} MdbCatalogEntry; + +typedef struct { + gchar *name; + GHashTable *hash; +} MdbProperties; + +typedef union { + int i; + double d; + char s[256]; +} MdbAny; + +struct S_MdbTableDef; /* forward definition */ +typedef struct { + struct S_MdbTableDef *table; + char name[MDB_MAX_OBJ_NAME+1]; + int col_type; + int col_size; + void *bind_ptr; + int *len_ptr; + GHashTable *properties; + unsigned int num_sargs; + GPtrArray *sargs; + GPtrArray *idx_sarg_cache; + unsigned char is_fixed; + int query_order; + /* col_num is the current column order, + * does not include deletes */ + int col_num; + int cur_value_start; + int cur_value_len; + /* MEMO/OLE readers */ + guint32 cur_blob_pg_row; + int chunk_size; + /* numerics only */ + int col_prec; + int col_scale; + unsigned char is_long_auto; + unsigned char is_uuid_auto; + MdbProperties *props; + /* info needed for handling deleted/added columns */ + int fixed_offset; + unsigned int var_col_num; + /* row_col_num is the row column number order, + * including deleted columns */ + int row_col_num; +} MdbColumn; + +struct mdbsargtree { + int op; + MdbColumn *col; + unsigned char val_type; + MdbAny value; + void *parent; + MdbSargNode *left; + MdbSargNode *right; +}; + +typedef struct { + guint32 pg; + int start_pos; + int offset; + int len; + int rc; + guint16 idx_starts[2000]; + unsigned char cache_value[256]; +} MdbIndexPage; + +typedef int (*MdbSargTreeFunc)(MdbSargNode *, gpointer data); + +#define MDB_MAX_INDEX_DEPTH 10 + +typedef struct { + int cur_depth; + guint32 last_leaf_found; + int clean_up_mode; + MdbIndexPage pages[MDB_MAX_INDEX_DEPTH]; +} MdbIndexChain; + +typedef struct S_MdbTableDef { + MdbCatalogEntry *entry; + char name[MDB_MAX_OBJ_NAME+1]; + unsigned int num_cols; + GPtrArray *columns; + unsigned int num_rows; + int index_start; + unsigned int num_real_idxs; + unsigned int num_idxs; + GPtrArray *indices; + guint32 first_data_pg; + guint32 cur_pg_num; + guint32 cur_phys_pg; + unsigned int cur_row; + int noskip_del; /* don't skip deleted rows */ + /* object allocation map */ + guint32 map_base_pg; + size_t map_sz; + unsigned char *usage_map; + /* pages with free space left */ + guint32 freemap_base_pg; + size_t freemap_sz; + unsigned char *free_usage_map; + /* query planner */ + MdbSargNode *sarg_tree; + MdbStrategy strategy; + MdbIndex *scan_idx; + MdbHandle *mdbidx; + MdbIndexChain *chain; + MdbProperties *props; + unsigned int num_var_cols; /* to know if row has variable columns */ + /* temp table */ + unsigned int is_temp_table; + GPtrArray *temp_table_pages; +} MdbTableDef; + +struct mdbindex { + int index_num; + char name[MDB_MAX_OBJ_NAME+1]; + unsigned char index_type; + guint32 first_pg; + int num_rows; /* number rows in index */ + unsigned int num_keys; + short key_col_num[MDB_MAX_IDX_COLS]; + unsigned char key_col_order[MDB_MAX_IDX_COLS]; + unsigned char flags; + MdbTableDef *table; +}; + +typedef struct { + char name[MDB_MAX_OBJ_NAME+1]; +} MdbColumnProp; + +typedef struct { + void *value; + int siz; + int start; + unsigned char is_null; + unsigned char is_fixed; + int colnum; + int offset; +} MdbField; + +typedef struct { + int op; + MdbAny value; +} MdbSarg; + +/* version.c */ +const char *mdb_get_version(void); + +/* file.c */ +ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg); +ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg); +unsigned char mdb_get_byte(void *buf, int offset); +int mdb_get_int16(void *buf, int offset); +long mdb_get_int32(void *buf, int offset); +long mdb_get_int32_msb(void *buf, int offset); +float mdb_get_single(void *buf, int offset); +double mdb_get_double(void *buf, int offset); +unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset); +int mdb_pg_get_int16(MdbHandle *mdb, int offset); +long mdb_pg_get_int32(MdbHandle *mdb, int offset); +float mdb_pg_get_single(MdbHandle *mdb, int offset); +double mdb_pg_get_double(MdbHandle *mdb, int offset); +MdbHandle *mdb_open(const char *filename, MdbFileFlags flags); +MdbHandle *mdb_open_buffer(void *buffer, size_t len, MdbFileFlags flags); +void mdb_close(MdbHandle *mdb); +MdbHandle *mdb_clone_handle(MdbHandle *mdb); +void mdb_swap_pgbuf(MdbHandle *mdb); + +/* catalog.c */ +void mdb_free_catalog(MdbHandle *mdb); +GPtrArray *mdb_read_catalog(MdbHandle *mdb, int obj_type); +MdbCatalogEntry *mdb_get_catalogentry_by_name(MdbHandle *mdb, const gchar* name); +void mdb_dump_catalog(MdbHandle *mdb, int obj_type); +const char *mdb_get_objtype_string(int obj_type); + +/* table.c */ +MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry); +void mdb_free_tabledef(MdbTableDef *table); +MdbTableDef *mdb_read_table(MdbCatalogEntry *entry); +MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type); +void mdb_append_column(GPtrArray *columns, MdbColumn *in_col); +void mdb_free_columns(GPtrArray *columns); +GPtrArray *mdb_read_columns(MdbTableDef *table); +void mdb_table_dump(MdbCatalogEntry *entry); +guint8 read_pg_if_8(MdbHandle *mdb, int *cur_pos); +guint16 read_pg_if_16(MdbHandle *mdb, int *cur_pos); +guint32 read_pg_if_32(MdbHandle *mdb, int *cur_pos); +void *read_pg_if_n(MdbHandle *mdb, void *buf, int *cur_pos, size_t len); +int mdb_is_user_table(MdbCatalogEntry *entry); +int mdb_is_system_table(MdbCatalogEntry *entry); +const char *mdb_table_get_prop(const MdbTableDef *table, const gchar *key); +const char *mdb_col_get_prop(const MdbColumn *col, const gchar *key); +int mdb_col_is_shortdate(const MdbColumn *col); + +/* data.c */ +int mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr, int *len_ptr); +void mdb_data_dump(MdbTableDef *table); +void mdb_date_to_tm(double td, struct tm *t); +void mdb_tm_to_date(struct tm *t, double *td); +char *mdb_uuid_to_string(const void *buf, int start); /* Uses default MDB_BRACES_4_2_2_8 format */ +char *mdb_uuid_to_string_fmt(const void *buf, int start, MdbUuidFormat format); +int mdb_bind_column(MdbTableDef *table, int col_num, void *bind_ptr, int *len_ptr); +int mdb_rewind_table(MdbTableDef *table); +int mdb_fetch_row(MdbTableDef *table); +int mdb_is_fixed_col(MdbColumn *col); +char *mdb_col_to_string(MdbHandle *mdb, void *buf, int start, int datatype, int size); +int mdb_find_pg_row(MdbHandle *mdb, int pg_row, void **buf, int *off, size_t *len); +int mdb_find_row(MdbHandle *mdb, int row, int *start, size_t *len); +int mdb_find_end_of_row(MdbHandle *mdb, int row); +int mdb_col_fixed_size(MdbColumn *col); +int mdb_col_disp_size(MdbColumn *col); +size_t mdb_ole_read_next(MdbHandle *mdb, MdbColumn *col, void *ole_ptr); +size_t mdb_ole_read(MdbHandle *mdb, MdbColumn *col, void *ole_ptr, size_t chunk_size); +void* mdb_ole_read_full(MdbHandle *mdb, MdbColumn *col, size_t *size); +void mdb_set_bind_size(MdbHandle *mdb, size_t bind_size); +void mdb_set_date_fmt(MdbHandle *mdb, const char *); +void mdb_set_shortdate_fmt(MdbHandle *mdb, const char *); +void mdb_set_repid_fmt(MdbHandle *mdb, MdbUuidFormat format); +void mdb_set_boolean_fmt_words(MdbHandle *mdb); +void mdb_set_boolean_fmt_numbers(MdbHandle *mdb); +int mdb_read_row(MdbTableDef *table, unsigned int row); +int mdb_read_next_dpg(MdbTableDef *table); + +/* money.c */ +char *mdb_money_to_string(MdbHandle *mdb, int start); +char *mdb_numeric_to_string(MdbHandle *mdb, int start, int scale, int prec); + +/* dump.c */ +void mdb_buffer_dump(const void *buf, off_t start, size_t len); + +/* backend.c */ +void mdb_init_backends(MdbHandle *mdb); +void mdb_remove_backends(MdbHandle *mdb); +const MdbBackendType* mdb_get_colbacktype(const MdbColumn *col); +const char* mdb_get_colbacktype_string(const MdbColumn *col); +int mdb_colbacktype_takes_length(const MdbColumn *col); +void mdb_register_backend(MdbHandle *mdb, char *backend_name, guint32 capabilities, + const MdbBackendType *backend_type, + const MdbBackendType *type_shortdate, + const MdbBackendType *type_autonum, + const char *short_now, const char *long_now, + const char *date_fmt, const char *shortdate_fmt, + const char *charset_statement, const char *drop_statement, const char *constaint_not_empty_statement, + const char *column_comment_statement, const char *per_column_comment_statement, + const char *table_comment_statement, const char *per_table_comment_statement, + gchar* (*quote_schema_name)(const gchar*, const gchar*)); +int mdb_set_default_backend(MdbHandle *mdb, const char *backend_name); +int mdb_print_schema(MdbHandle *mdb, FILE *outfile, char *tabname, char *dbnamespace, guint32 export_options); +void mdb_print_col(FILE *outfile, gchar *col_val, int quote_text, int col_type, int bin_len, char *quote_char, char *escape_char, int flags); +gchar *mdb_normalise_and_replace(MdbHandle *mdb, gchar **str); + +/* sargs.c */ +int mdb_test_sargs(MdbTableDef *table, MdbField *fields, int num_fields); +int mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field); +void mdb_sql_walk_tree(MdbSargNode *node, MdbSargTreeFunc func, gpointer data); +int mdb_find_indexable_sargs(MdbSargNode *node, gpointer data); +int mdb_add_sarg_by_name(MdbTableDef *table, char *colname, MdbSarg *in_sarg); +int mdb_test_string(MdbSargNode *node, char *s); +int mdb_test_int(MdbSargNode *node, gint32 i); +int mdb_add_sarg(MdbColumn *col, MdbSarg *in_sarg); + + + +/* index.c */ +GPtrArray *mdb_read_indices(MdbTableDef *table); +void mdb_index_dump(MdbTableDef *table, MdbIndex *idx); +void mdb_index_scan_free(MdbTableDef *table); +int mdb_index_find_next_on_page(MdbHandle *mdb, MdbIndexPage *ipg); +int mdb_index_find_next(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 *pg, guint16 *row); +void mdb_index_hash_text(MdbHandle *mdb, char *text, char *hash); +void mdb_index_scan_init(MdbHandle *mdb, MdbTableDef *table); +int mdb_index_find_row(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 pg, guint16 row); +void mdb_index_swap_n(unsigned char *src, int sz, unsigned char *dest); +void mdb_free_indices(GPtrArray *indices); +void mdb_index_page_reset(MdbHandle *mdb, MdbIndexPage *ipg); +int mdb_index_pack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg); + +/* stats.c */ +void mdb_stats_on(MdbHandle *mdb); +void mdb_stats_off(MdbHandle *mdb); +void mdb_dump_stats(MdbHandle *mdb); + +/* like.c */ +int mdb_like_cmp(char *s, char *r); +int mdb_ilike_cmp(char *s, char *r); + +/* write.c */ +void mdb_put_int16(void *buf, guint32 offset, guint32 value); +void mdb_put_int32(void *buf, guint32 offset, guint32 value); +void mdb_put_int32_msb(void *buf, guint32 offset, guint32 value); +int mdb_crack_row(MdbTableDef *table, int row_start, size_t row_size, MdbField *fields); +guint16 mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size); +int mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum); +int mdb_insert_row(MdbTableDef *table, int num_fields, MdbField *fields); +int mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields); +int mdb_replace_row(MdbTableDef *table, int row, void *new_row, int new_row_size); +int mdb_pg_get_freespace(MdbHandle *mdb); +int mdb_update_row(MdbTableDef *table); +void *mdb_new_data_pg(MdbCatalogEntry *entry); + +/* map.c */ +gint32 mdb_map_find_next_freepage(MdbTableDef *table, int row_size); +gint32 mdb_map_find_next(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg); + +/* props.c */ +void mdb_free_props(MdbProperties *props); +void mdb_dump_props(MdbProperties *props, FILE *outfile, int show_name); +GPtrArray* mdb_kkd_to_props(MdbHandle *mdb, void *kkd, size_t len); + + +/* worktable.c */ +MdbTableDef *mdb_create_temp_table(MdbHandle *mdb, char *name); +void mdb_temp_table_add_col(MdbTableDef *table, MdbColumn *col); +void mdb_fill_temp_col(MdbColumn *tcol, char *col_name, int col_size, int col_type, int is_fixed); +void mdb_fill_temp_field(MdbField *field, void *value, int siz, int is_fixed, int is_null, int start, int column); +void mdb_temp_columns_end(MdbTableDef *table); + +/* options.c */ +int mdb_get_option(unsigned long optnum); +void mdb_debug(int klass, char *fmt, ...); + +/* iconv.c */ +int mdb_unicode2ascii(MdbHandle *mdb, const char *src, size_t slen, char *dest, size_t dlen); +int mdb_ascii2unicode(MdbHandle *mdb, const char *src, size_t slen, char *dest, size_t dlen); +void mdb_iconv_init(MdbHandle *mdb); +void mdb_iconv_close(MdbHandle *mdb); +const char* mdb_target_charset(MdbHandle *mdb); + +/** @}*/ + +#ifdef __cplusplus + } +#endif + +#endif /* _mdbtools_h_ */ diff --git a/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbver.h b/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbver.h new file mode 100644 index 0000000000..e5649105fa --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseEngines/mdbtools/mdbver.h @@ -0,0 +1,25 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _mdbver_h_ +#define _mdbver_h_ + +#define MDB_FULL_VERSION "mdbtools v1.0.1" +#define MDB_VERSION_NO "1.0.1" + +#endif diff --git a/OOXML/Binary/Sheets/Reader/DatabaseReader.cpp b/OOXML/Binary/Sheets/Reader/DatabaseReader.cpp new file mode 100644 index 0000000000..bfc2a127a2 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseReader.cpp @@ -0,0 +1,230 @@ +#define DONT_WRITE_EMBEDDED_FONTS +#include "DatabaseReader.h" +#include "../../../../Common/ATLDefine.h" +#include "../../../../DesktopEditor/common/Path.h" +#include "../../../../DesktopEditor/common/Directory.h" +#include "../../../DocxFormat/Math/oMathPara.h" +#include "../../../DocxFormat/Math/oMathContent.h" +#include "../../../DocxFormat/Drawing/DrawingExt.h" +#include "../../../XlsxFormat/ComplexTypes_Spreadsheet.h" +#include "../../../XlsxFormat/Styles/Styles.h" +#include "../../../XlsxFormat/Styles/Borders.h" +#include "../../../XlsxFormat/Styles/Fills.h" +#include "../../../XlsxFormat/Styles/Fonts.h" +#include "../../../XlsxFormat/Styles/NumFmts.h" +#include "../../../XlsxFormat/Styles/Xfs.h" +#include "../../../XlsxFormat/Styles/dxf.h" +#include "../../../XlsxFormat/Styles/CellStyles.h" +#include "../../../XlsxFormat/Styles/TableStyles.h" +#include "CellFormatController/CellFormatController.h" +#include "../../../XlsxFormat/Xlsx.h" +#include "../../../XlsxFormat/Worksheets/Worksheet.h" +#include "../../../../DesktopEditor/common/File.h" +#include "../../../XlsxFormat/Workbook/Workbook.h" +#include "../../../XlsxFormat/SharedStrings/SharedStrings.h" +#include "../../../XlsxFormat/Styles/Styles.h" +#include "DatabaseEngines/MdbEngine.h" +#include "DatabaseEngines/SqliteEngine.h" +#include "DatabaseEngines/DuckDbEngine.h" +#include "../../../XlsxFormat/Worksheets/DataValidation.h" +#include "../../../../Common/OfficeFileErrorDescription.h" +#include +#include + +using namespace NExtractTools; + +DatabaseReader::DatabaseReader() {} +DatabaseReader::~DatabaseReader() {} + +_UINT32 DatabaseReader::Read(const std::wstring &sFileName, OOX::Spreadsheet::CXlsx &oXlsx, _INT32 lcid, bool readToCache) +{ + oXlsx.CreateWorkbook(); + oXlsx.CreateStyles(); + if (!oXlsx.m_pSharedStrings) + { + oXlsx.CreateSharedStrings(); + } + + std::shared_ptr cellFormatController = std::make_shared(oXlsx.m_pStyles, lcid); + + std::wstring sExt; + std::wstring::size_type nExtPos = sFileName.rfind(L'.'); + if (nExtPos != std::wstring::npos) { + sExt = sFileName.substr(nExtPos); + std::transform(sExt.begin(), sExt.end(), sExt.begin(), ::tolower); + } + + std::unique_ptr engine; + if (sExt == L".sqlite" || sExt == L".sqlite3" || sExt == L".db" || sExt == L".db3") { + engine.reset(new SqliteEngine()); + } else if (sExt == L".duckdb" || sExt == L".parquet" || sExt == L".pq") { + engine.reset(new DuckDbEngine()); + } else if (sExt == L".mdb" || sExt == L".accdb") { + engine.reset(new MdbEngine()); + } else { + return AVS_FILEUTILS_ERROR_CONVERT; + } + + if (!engine->Open(sFileName)) { + return AVS_FILEUTILS_ERROR_CONVERT; + } + + std::vector tables = engine->GetTableNames(); + int sheetIndex = 1; + + struct TableData { + std::wstring name; + TableSchema schema; + std::vector> rows; + }; + std::vector allData; + + for (const auto& tableName : tables) { + TableData tdata; + tdata.name = tableName; + tdata.schema = engine->GetTableSchema(tableName); + std::unique_ptr rs = engine->QueryTable(tableName); + if (rs) { + while (rs->Next()) { + std::vector rowData; + for (size_t colIdx = 0; colIdx < tdata.schema.columns.size(); ++colIdx) { + rowData.push_back(rs->GetString(colIdx)); + } + tdata.rows.push_back(rowData); + } + } + allData.push_back(tdata); + } + + auto getColLetter = [](int colIdx) -> std::wstring { + std::wstring res; + while (colIdx >= 0) { + res.insert(res.begin(), (wchar_t)(L'A' + (colIdx % 26))); + colIdx = (colIdx / 26) - 1; + } + return res; + }; + + std::map pkCellMap; // "Table.Col.Value" -> "Table!A5" + for (const auto& tdata : allData) { + for (size_t rowIdx = 0; rowIdx < tdata.rows.size(); ++rowIdx) { + for (size_t colIdx = 0; colIdx < tdata.schema.columns.size(); ++colIdx) { + const std::wstring& colName = tdata.schema.columns[colIdx]; + // check if pk + if (std::find(tdata.schema.primaryKeys.begin(), tdata.schema.primaryKeys.end(), colName) != tdata.schema.primaryKeys.end()) { + std::wstring val = tdata.rows[rowIdx][colIdx]; + std::wstring cellRef = L"'" + tdata.name + L"'!" + getColLetter(colIdx) + std::to_wstring(rowIdx + 2); + pkCellMap[tdata.name + L"." + colName + L"." + val] = cellRef; + } + } + } + } + + for (const auto& tdata : allData) { + smart_ptr oWorksheetFile(new OOX::Spreadsheet::CWorksheet(NULL)); + OOX::Spreadsheet::CWorksheet *pWorksheet = (OOX::Spreadsheet::CWorksheet *)oWorksheetFile.GetPointer(); + pWorksheet->m_oSheetData.Init(); + pWorksheet->m_oSheetFormatPr.Init(); + pWorksheet->m_oSheetFormatPr->m_oBaseColWidth = 9; + + cellFormatController->m_pWorksheet = pWorksheet; + + // Write Headers + OOX::Spreadsheet::CRow *pHeaderRow = new OOX::Spreadsheet::CRow(); + pHeaderRow->m_oR.Init(); + pHeaderRow->m_oR->SetValue(1); + + for (size_t colIdx = 0; colIdx < tdata.schema.columns.size(); ++colIdx) { + OOX::Spreadsheet::CCell *pCell = new OOX::Spreadsheet::CCell(); + pCell->m_oType.Init(); + pCell->setRowCol(0, colIdx); + std::wstring colName = tdata.schema.columns[colIdx]; + pCell->m_oCacheValue = colName; + cellFormatController->ProcessCellType(pCell, colName, false); + pHeaderRow->m_arrItems.push_back(pCell); + } + pWorksheet->m_oSheetData->m_arrItems.push_back(pHeaderRow); + + // Write Data + int rowIndex = 1; + for (const auto& rowData : tdata.rows) { + OOX::Spreadsheet::CRow *pRow = new OOX::Spreadsheet::CRow(); + pRow->m_oR.Init(); + pRow->m_oR->SetValue(rowIndex + 1); + + for (size_t colIdx = 0; colIdx < tdata.schema.columns.size(); ++colIdx) { + OOX::Spreadsheet::CCell *pCell = new OOX::Spreadsheet::CCell(); + pCell->m_oType.Init(); + pCell->setRowCol(rowIndex, colIdx); + + std::wstring val = rowData[colIdx]; + std::wstring colName = tdata.schema.columns[colIdx]; + pCell->m_oCacheValue = val; + + // check if fk + bool isFk = false; + for (const auto& fk : tdata.schema.foreignKeys) { + if (fk.columnName == colName) { + std::wstring key = fk.referencedTable + L"." + fk.referencedColumn + L"." + val; + if (pkCellMap.find(key) != pkCellMap.end()) { + pCell->m_oFormula.Init(); + pCell->m_oFormula->m_sText = pkCellMap[key]; + isFk = true; + } + break; + } + } + + cellFormatController->ProcessCellType(pCell, val, false); + pRow->m_arrItems.push_back(pCell); + } + pWorksheet->m_oSheetData->m_arrItems.push_back(pRow); + rowIndex++; + } + + // Data validation for PK uniqueness + if (tdata.schema.primaryKeys.size() > 0) { + if (!pWorksheet->m_oDataValidations.IsInit()) + pWorksheet->m_oDataValidations.Init(); + + for (const auto& pkName : tdata.schema.primaryKeys) { + auto it = std::find(tdata.schema.columns.begin(), tdata.schema.columns.end(), pkName); + if (it != tdata.schema.columns.end()) { + int pkColIdx = std::distance(tdata.schema.columns.begin(), it); + std::wstring colLetter = getColLetter(pkColIdx); + + OOX::Spreadsheet::CDataValidation* pValidation = new OOX::Spreadsheet::CDataValidation(); + pValidation->m_oType.Init(); + pValidation->m_oType->SetValue(SimpleTypes::Spreadsheet::validationTypeCustom); + + pValidation->m_oSqRef = colLetter + L"2:" + colLetter + L"1048576"; + + pValidation->m_oFormula1.Init(); + pValidation->m_oFormula1->m_sText = L"COUNTIF(" + colLetter + L":" + colLetter + L", " + colLetter + L"2)<=1"; + + pWorksheet->m_oDataValidations->m_arrItems.push_back(pValidation); + } + } + } + + oXlsx.m_arWorksheets.push_back(pWorksheet); + + const OOX::RId oRid = oXlsx.m_pWorkbook->Add(oWorksheetFile); + + oXlsx.m_mapWorksheets.insert(std::make_pair(oRid.ToString(), pWorksheet)); + + OOX::Spreadsheet::CSheet *pSheet = new OOX::Spreadsheet::CSheet(); + pSheet->m_oName = tdata.name; + pSheet->m_oSheetId.Init(); + pSheet->m_oSheetId->SetValue(sheetIndex++); + pSheet->m_oRid.Init(); + pSheet->m_oRid->SetValue(oRid.ToString()); + + if (!oXlsx.m_pWorkbook->m_oSheets.IsInit()) + oXlsx.m_pWorkbook->m_oSheets.Init(); + oXlsx.m_pWorkbook->m_oSheets->m_arrItems.push_back(pSheet); + } + + return 0; // S_OK +} + diff --git a/OOXML/Binary/Sheets/Reader/DatabaseReader.h b/OOXML/Binary/Sheets/Reader/DatabaseReader.h new file mode 100644 index 0000000000..090c76b7b0 --- /dev/null +++ b/OOXML/Binary/Sheets/Reader/DatabaseReader.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include "../../../Base/Base.h" +#include + +namespace OOX { + namespace Spreadsheet { + class CXlsx; + } +} + +namespace NExtractTools +{ + class InputParams; + class ConvertParams; + + _UINT32 db2xlsx_dir(const std::wstring& sFrom, const std::wstring& sTo, InputParams& params, ConvertParams& convertParams); +} + +class DatabaseReader +{ +public: + DatabaseReader(); + ~DatabaseReader(); + + _UINT32 Read(const std::wstring &sFileName, OOX::Spreadsheet::CXlsx &oXlsx, _INT32 lcid, bool readToCache); +}; diff --git a/OOXML/Projects/Linux/BinDocument/BinDocument.pro b/OOXML/Projects/Linux/BinDocument/BinDocument.pro index 71eb49dd1d..92b906045f 100644 --- a/OOXML/Projects/Linux/BinDocument/BinDocument.pro +++ b/OOXML/Projects/Linux/BinDocument/BinDocument.pro @@ -14,9 +14,12 @@ include(../../../../Common/base.pri) #BOOST include($$PWD/../../../../Common/3dParty/boost/boost.pri) +LIBS += -lsqlite3 $$PWD/../../../Binary/Sheets/Reader/DatabaseEngines/duckdb/libduckdb.so + INCLUDEPATH += ../../../../MsBinaryFile/XlsFile/Format INCLUDEPATH += ../../../../MsBinaryFile/Common/common_xls INCLUDEPATH += ../../../XlsbFormat +INCLUDEPATH += ../../../Binary/Sheets/Reader/DatabaseEngines/mdbtools DEFINES += UNICODE \ _UNICODE \ @@ -41,6 +44,30 @@ SOURCES += \ ../../../Binary/Sheets/Reader/ChartFromToBinary.cpp \ ../../../Binary/Sheets/Reader/CommonWriter.cpp \ ../../../Binary/Sheets/Reader/CSVReader.cpp \ + ../../../Binary/Sheets/Reader/DatabaseReader.cpp \ + ../../../Binary/Sheets/Reader/DatabaseEngines/MdbEngine.cpp \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/backend.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/catalog.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/data.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/dump.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/fakeglib.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/file.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/iconv.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/index.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/like.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/map.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/money.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/options.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/props.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/rc4.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/sargs.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/stats.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/table.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/version.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/worktable.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/libmdb/write.c \ + ../../../Binary/Sheets/Reader/DatabaseEngines/SqliteEngine.cpp \ + ../../../Binary/Sheets/Reader/DatabaseEngines/DuckDbEngine.cpp \ ../../../Binary/Sheets/Reader/BinaryWriterS.cpp \ ../../../Binary/Sheets/Writer/BinaryReaderS.cpp \ ../../../Binary/Sheets/Writer/CSVWriter.cpp \ @@ -102,6 +129,8 @@ HEADERS += \ ../../../Binary/Sheets/Reader/ChartFromToBinary.h \ ../../../Binary/Sheets/Reader/CommonWriter.h \ ../../../Binary/Sheets/Reader/CSVReader.h \ + ../../../Binary/Sheets/Reader/DatabaseEngines/SqliteEngine.h \ + ../../../Binary/Sheets/Reader/DatabaseEngines/DuckDbEngine.h \ ../../../Binary/Sheets/Writer/BinaryCommonReader.h \ ../../../Binary/Sheets/Writer/BinaryReaderS.h \ ../../../Binary/Sheets/Writer/CSVWriter.h \ diff --git a/OOXML/Projects/Linux/BinDocument/CMakeLists.txt b/OOXML/Projects/Linux/BinDocument/CMakeLists.txt index 1b830dc2c4..9f0f853beb 100644 --- a/OOXML/Projects/Linux/BinDocument/CMakeLists.txt +++ b/OOXML/Projects/Linux/BinDocument/CMakeLists.txt @@ -9,6 +9,44 @@ include(${CORE_ROOT_DIR}/common.cmake) set(OOXML_ROOT_DIR "${CORE_ROOT_DIR}/OOXML") set(BIN_SOURCES_DIR "${OOXML_ROOT_DIR}/Binary") set(MS_BINARY_FILE_ROOT "${CORE_ROOT_DIR}/MsBinaryFile") +set(DB_ENGINES_DIR "${BIN_SOURCES_DIR}/Sheets/Reader/DatabaseEngines") + +# Built as a separate shared library (not compiled directly into BinDocument) +# so that mdbtools' LGPLv2+ source is dynamically linked, not statically bound +# into the AGPL-licensed application. Vendored verbatim from upstream mdbtools +# v1.0.1 (matches the version already recorded in mdbver.h); see +# desktop-apps/package/common/license/3dparty/3DPARTYLICENSE for the notice. +add_library(mdbtools SHARED + ${DB_ENGINES_DIR}/libmdb/backend.c + ${DB_ENGINES_DIR}/libmdb/catalog.c + ${DB_ENGINES_DIR}/libmdb/data.c + ${DB_ENGINES_DIR}/libmdb/dump.c + ${DB_ENGINES_DIR}/libmdb/fakeglib.c + ${DB_ENGINES_DIR}/libmdb/file.c + ${DB_ENGINES_DIR}/libmdb/iconv.c + ${DB_ENGINES_DIR}/libmdb/index.c + ${DB_ENGINES_DIR}/libmdb/like.c + ${DB_ENGINES_DIR}/libmdb/map.c + ${DB_ENGINES_DIR}/libmdb/money.c + ${DB_ENGINES_DIR}/libmdb/options.c + ${DB_ENGINES_DIR}/libmdb/props.c + ${DB_ENGINES_DIR}/libmdb/rc4.c + ${DB_ENGINES_DIR}/libmdb/sargs.c + ${DB_ENGINES_DIR}/libmdb/stats.c + ${DB_ENGINES_DIR}/libmdb/table.c + ${DB_ENGINES_DIR}/libmdb/version.c + ${DB_ENGINES_DIR}/libmdb/worktable.c + ${DB_ENGINES_DIR}/libmdb/write.c +) +set_default_options(mdbtools) +target_include_directories(mdbtools PRIVATE ${DB_ENGINES_DIR}/mdbtools) +target_compile_definitions(mdbtools PRIVATE HAVE_ICONV "ICONV_CONST=") +set_property(TARGET mdbtools PROPERTY POSITION_INDEPENDENT_CODE ON) +# set_default_options() applies -fvisibility=hidden project-wide, which would +# hide the mdb_* C API entirely from mdbtools.so's exported symbol table. +# This library's whole purpose is to be linked externally, so override back +# to default visibility. +target_compile_options(mdbtools PRIVATE -fvisibility=default) add_library(BinDocument STATIC ${BIN_SOURCES_DIR}/Document/DocWrapper/DocxSerializer.cpp @@ -21,6 +59,10 @@ add_library(BinDocument STATIC ${BIN_SOURCES_DIR}/Sheets/Reader/ChartFromToBinary.cpp ${BIN_SOURCES_DIR}/Sheets/Reader/CommonWriter.cpp ${BIN_SOURCES_DIR}/Sheets/Reader/CSVReader.cpp + ${BIN_SOURCES_DIR}/Sheets/Reader/DatabaseReader.cpp + ${BIN_SOURCES_DIR}/Sheets/Reader/DatabaseEngines/MdbEngine.cpp + ${BIN_SOURCES_DIR}/Sheets/Reader/DatabaseEngines/SqliteEngine.cpp + ${BIN_SOURCES_DIR}/Sheets/Reader/DatabaseEngines/DuckDbEngine.cpp ${BIN_SOURCES_DIR}/Sheets/Reader/BinaryWriterS.cpp ${BIN_SOURCES_DIR}/Sheets/Writer/BinaryReaderS.cpp ${BIN_SOURCES_DIR}/Sheets/Writer/CSVWriter.cpp @@ -120,23 +162,38 @@ target_compile_definitions(BinDocument PRIVATE SOLUTION_ASCOFFICEDOCXFILE2 #DISABLE_FILE_DOWNLOADER AVS_USE_CONVERT_PPTX_TOCUSTOM_VML + HAVE_ICONV + "ICONV_CONST=" ) target_precompile_headers(BinDocument PRIVATE precompiled.h ) -target_include_directories(BinDocument PRIVATE +target_include_directories(BinDocument + PRIVATE ${MS_BINARY_FILE_ROOT}/XlsFile/Format ${MS_BINARY_FILE_ROOT}/Common/common_xls - ${OOXML_ROOT_DIR}/XlsbFormat + ${OOXML_ROOT_DIR}/Binary/XlsbFormat + ${BIN_SOURCES_DIR}/Sheets/Reader/DatabaseEngines/mdbtools ) -target_link_libraries(BinDocument PRIVATE +find_package(PkgConfig REQUIRED) +pkg_check_modules(GLIB REQUIRED glib-2.0) +target_include_directories(BinDocument PRIVATE ${GLIB_INCLUDE_DIRS}) + +find_package(unofficial-sqlite3 CONFIG REQUIRED) +find_package(DuckDB CONFIG REQUIRED) + +target_link_libraries(BinDocument PRIVATE Boost::system Boost::filesystem Boost::regex Boost::date_time + unofficial::sqlite3::sqlite3 + $,duckdb,duckdb_static> + mdbtools + ${GLIB_LIBRARIES} ) set_property(TARGET BinDocument PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/X2tConverter/build/cmake/library/CMakeLists.txt b/X2tConverter/build/cmake/library/CMakeLists.txt index a13d37dee5..21b2add900 100644 --- a/X2tConverter/build/cmake/library/CMakeLists.txt +++ b/X2tConverter/build/cmake/library/CMakeLists.txt @@ -211,6 +211,15 @@ target_link_libraries(x2tlib PRIVATE CryptoPPLib ) +find_package(unofficial-sqlite3 CONFIG REQUIRED) +find_package(DuckDB CONFIG REQUIRED) + +target_link_libraries(x2tlib PRIVATE + unofficial::sqlite3::sqlite3 + mdbtools + $,duckdb,duckdb_static> +) + # dynamic libs target_link_libraries(x2tlib PUBLIC graphics diff --git a/X2tConverter/src/ASCConverters.cpp b/X2tConverter/src/ASCConverters.cpp index e50876d5ec..08ab370106 100644 --- a/X2tConverter/src/ASCConverters.cpp +++ b/X2tConverter/src/ASCConverters.cpp @@ -1,4 +1,4 @@ -/* +/* * (c) Copyright Ascensio System SIA 2010-2023 * * This program is a free software product. You can redistribute it and/or @@ -38,6 +38,7 @@ #include "lib/xls.h" #include "lib/csv.h" +#include "lib/db.h" #include "lib/html.h" @@ -1147,6 +1148,15 @@ namespace NExtractTools nRes = csv2xlsx_dir(sFrom, sXlsxDir, params, convertParams); *params.m_nFormatFrom = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSX; } + else if ( AVS_OFFICESTUDIO_FILE_SPREADSHEET_SQLITE == nFormatFrom || + AVS_OFFICESTUDIO_FILE_SPREADSHEET_DUCKDB == nFormatFrom || + AVS_OFFICESTUDIO_FILE_SPREADSHEET_PARQUET == nFormatFrom || + AVS_OFFICESTUDIO_FILE_SPREADSHEET_MDB == nFormatFrom || + AVS_OFFICESTUDIO_FILE_SPREADSHEET_FDB == nFormatFrom) + { + nRes = db2xlsx_dir(sFrom, sXlsxDir, params, convertParams); + *params.m_nFormatFrom = AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSX; + } else if (AVS_OFFICESTUDIO_FILE_DOCUMENT_XML == nFormatFrom) { nRes = xml2xlsx_dir(sFrom, sXlsxDir, params, convertParams); diff --git a/X2tConverter/src/ASCConverters.h b/X2tConverter/src/ASCConverters.h index b91c41fdf9..5a544171f3 100644 --- a/X2tConverter/src/ASCConverters.h +++ b/X2tConverter/src/ASCConverters.h @@ -1,4 +1,4 @@ -/* +/* * (c) Copyright Ascensio System SIA 2010-2023 * * This program is a free software product. You can redistribute it and/or @@ -221,6 +221,7 @@ namespace NExtractTools DECLARE_CONVERT_FUNC(csv2xlst); DECLARE_CONVERT_FUNC(csv2xlsx); DECLARE_CONVERT_FUNC(csv2xlsx_dir); + DECLARE_CONVERT_FUNC(db2xlsx_dir); DECLARE_CONVERT_FUNC(csv2xlst_bin); DECLARE_CONVERT_FUNC(xlst2csv); DECLARE_CONVERT_FUNC(xlsx_dir2csv); diff --git a/X2tConverter/src/lib/db.h b/X2tConverter/src/lib/db.h new file mode 100644 index 0000000000..5362f1272b --- /dev/null +++ b/X2tConverter/src/lib/db.h @@ -0,0 +1,27 @@ +#include "../../../../OOXML/Binary/Sheets/Common/Common.h" +#include "../../../../OOXML/Binary/Sheets/Reader/DatabaseReader.h" +#include "../../../../OOXML/XlsxFormat/Xlsx.h" +#include "common.h" + +namespace NExtractTools +{ + _UINT32 db2xlsx_dir(const std::wstring& sFrom, const std::wstring& sTo, InputParams& params, ConvertParams& convertParams) + { + params.m_bMacro = false; + + OOX::Spreadsheet::CXlsx oXlsx; + _INT32 lcid = -1; + + DatabaseReader dbReader; + _UINT32 nRes = dbReader.Read(sFrom, oXlsx, lcid, false); + + if (SUCCEEDED_X2T(nRes)) + { + oXlsx.PrepareToWrite(); + + OOX::CContentTypes oContentTypes; + nRes = oXlsx.Write(sTo, oContentTypes) ? nRes : AVS_FILEUTILS_ERROR_CONVERT; + } + return nRes; + } +} diff --git a/vcpkg.json b/vcpkg.json index 71681836d4..0614e25966 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -2,7 +2,9 @@ "name": "euro-office", "version": "1.0.0", "dependencies": [ - "hunspell" + "hunspell", + "sqlite3", + "duckdb" ], "features": { "tests": {