diff --git a/sdk/storage/README.md b/sdk/storage/README.md index 1910857c0..7a3ec5795 100644 --- a/sdk/storage/README.md +++ b/sdk/storage/README.md @@ -47,7 +47,7 @@ For general suggestions about Azure, use our [Azure feedback forum](https://feed On Windows, dependencies are managed by [vcpkg](https://github.com/microsoft/vcpkg). You can reference the [Quick Start](https://github.com/microsoft/vcpkg#quick-start-windows) to quickly set yourself up. After Vcpkg is initialized and bootstrapped, you can install the dependencies: ```BatchFile -vcpkg.exe install libxml2:x64-windows-static curl:x64-windows-static +vcpkg.exe install curl:x64-windows-static ``` #### POSIX Platforms diff --git a/sdk/storage/azure-storage-blobs/test/ut/blob_container_client_test.cpp b/sdk/storage/azure-storage-blobs/test/ut/blob_container_client_test.cpp index 8a9cda752..d252100e4 100644 --- a/sdk/storage/azure-storage-blobs/test/ut/blob_container_client_test.cpp +++ b/sdk/storage/azure-storage-blobs/test/ut/blob_container_client_test.cpp @@ -1133,10 +1133,11 @@ namespace Azure { namespace Storage { namespace Test { { const std::string non_ascii_word = "\xE6\xB5\x8B\xE8\xAF\x95"; const std::string encoded_non_ascii_word = "%E6%B5%8B%E8%AF%95"; - std::string baseBlobName = "a b c / !@#$%^&*(?/<>,.;:'\"[]{}|`~\\) def" + non_ascii_word; + ASSERT_EQ(_internal::UrlEncodePath(non_ascii_word), encoded_non_ascii_word); + const std::string baseBlobName = "a b c / !@#$%^&*(?/<>,.;:'\"[]{}|`~\\) def" + non_ascii_word; { - std::string blobName = baseBlobName + RandomString(); + const std::string blobName = baseBlobName + RandomString(); auto blobClient = m_blobContainerClient->GetAppendBlobClient(blobName); EXPECT_NO_THROW(blobClient.Create()); auto blobUrl = blobClient.GetUrl(); @@ -1144,7 +1145,7 @@ namespace Azure { namespace Storage { namespace Test { blobUrl, m_blobContainerClient->GetUrl() + "/" + _internal::UrlEncodePath(blobName)); } { - std::string blobName = baseBlobName + RandomString(); + const std::string blobName = baseBlobName + RandomString(); auto blobClient = m_blobContainerClient->GetPageBlobClient(blobName); EXPECT_NO_THROW(blobClient.Create(1024)); auto blobUrl = blobClient.GetUrl(); @@ -1152,16 +1153,15 @@ namespace Azure { namespace Storage { namespace Test { blobUrl, m_blobContainerClient->GetUrl() + "/" + _internal::UrlEncodePath(blobName)); } { - std::string blobName = baseBlobName + RandomString(); + const std::string blobName = baseBlobName + RandomString(); auto blobClient = m_blobContainerClient->GetBlockBlobClient(blobName); EXPECT_NO_THROW(blobClient.UploadFrom(nullptr, 0)); auto blobUrl = blobClient.GetUrl(); EXPECT_EQ( blobUrl, m_blobContainerClient->GetUrl() + "/" + _internal::UrlEncodePath(blobName)); } - { - std::string blobName = baseBlobName + RandomString(); + const std::string blobName = baseBlobName + RandomString(); auto blobClient = Blobs::AppendBlobClient::CreateFromConnectionString( StandardStorageConnectionString(), m_containerName, blobName); EXPECT_NO_THROW(blobClient.Create()); @@ -1170,7 +1170,7 @@ namespace Azure { namespace Storage { namespace Test { blobUrl, m_blobContainerClient->GetUrl() + "/" + _internal::UrlEncodePath(blobName)); } { - std::string blobName = baseBlobName + RandomString(); + const std::string blobName = baseBlobName + RandomString(); auto blobClient = Blobs::PageBlobClient::CreateFromConnectionString( StandardStorageConnectionString(), m_containerName, blobName); EXPECT_NO_THROW(blobClient.Create(1024)); @@ -1179,7 +1179,7 @@ namespace Azure { namespace Storage { namespace Test { blobUrl, m_blobContainerClient->GetUrl() + "/" + _internal::UrlEncodePath(blobName)); } { - std::string blobName = baseBlobName + RandomString(); + const std::string blobName = baseBlobName + RandomString(); auto blobClient = Blobs::BlockBlobClient::CreateFromConnectionString( StandardStorageConnectionString(), m_containerName, blobName); EXPECT_NO_THROW(blobClient.UploadFrom(nullptr, 0)); diff --git a/sdk/storage/azure-storage-common/CHANGELOG.md b/sdk/storage/azure-storage-common/CHANGELOG.md index dc2e13ea1..129aad2b4 100644 --- a/sdk/storage/azure-storage-common/CHANGELOG.md +++ b/sdk/storage/azure-storage-common/CHANGELOG.md @@ -4,10 +4,14 @@ ### Features Added +- Used new xml library on Windows, dropped dependency for libxml2. + ### Breaking Changes ### Bugs Fixed +- Fixed a bug that may cause crash when parsing XML. + ### Other Changes ## 12.1.0 (2021-08-10) diff --git a/sdk/storage/azure-storage-common/CMakeLists.txt b/sdk/storage/azure-storage-common/CMakeLists.txt index 94fbec8a2..6ec570bf1 100644 --- a/sdk/storage/azure-storage-common/CMakeLists.txt +++ b/sdk/storage/azure-storage-common/CMakeLists.txt @@ -27,7 +27,6 @@ if(NOT AZ_ALL_LIBRARIES) endif() find_package(Threads REQUIRED) -find_package(LibXml2 REQUIRED) set( AZURE_STORAGE_COMMON_HEADER @@ -78,14 +77,15 @@ target_include_directories( ) target_link_libraries(azure-storage-common PUBLIC Azure::azure-core) -target_include_directories(azure-storage-common PRIVATE ${LIBXML2_INCLUDE_DIRS}) -target_link_libraries(azure-storage-common PRIVATE ${LIBXML2_LIBRARIES}) if(WIN32) - target_link_libraries(azure-storage-common PRIVATE bcrypt) + target_link_libraries(azure-storage-common PRIVATE bcrypt webservices) # C28020 and C28204 are introduced by nlohmann/json target_compile_options(azure-storage-common PUBLIC /wd28204 /wd28020) else() + find_package(LibXml2 REQUIRED) + target_include_directories(azure-storage-common PRIVATE ${LIBXML2_INCLUDE_DIRS}) + target_link_libraries(azure-storage-common PRIVATE ${LIBXML2_LIBRARIES}) find_package(OpenSSL REQUIRED) target_link_libraries(azure-storage-common PRIVATE OpenSSL::SSL OpenSSL::Crypto) endif() diff --git a/sdk/storage/azure-storage-common/inc/azure/storage/common/internal/xml_wrapper.hpp b/sdk/storage/azure-storage-common/inc/azure/storage/common/internal/xml_wrapper.hpp index 85cf1ba71..ade48e1aa 100644 --- a/sdk/storage/azure-storage-common/inc/azure/storage/common/internal/xml_wrapper.hpp +++ b/sdk/storage/azure-storage-common/inc/azure/storage/common/internal/xml_wrapper.hpp @@ -36,18 +36,35 @@ namespace Azure { namespace Storage { namespace _internal { class XmlReader final { public: explicit XmlReader(const char* data, size_t length); + XmlReader(const XmlReader& other) = delete; + XmlReader& operator=(const XmlReader& other) = delete; + XmlReader(XmlReader&& other) noexcept { *this = std::move(other); } + XmlReader& operator=(XmlReader&& other) noexcept + { + m_context = other.m_context; + other.m_context = nullptr; + return *this; + } ~XmlReader(); XmlNode Read(); private: - void* m_reader = nullptr; - bool m_readingAttributes = false; + void* m_context = nullptr; }; class XmlWriter final { public: explicit XmlWriter(); + XmlWriter(const XmlWriter& other) = delete; + XmlWriter& operator=(const XmlWriter& other) = delete; + XmlWriter(XmlWriter&& other) noexcept { *this = std::move(other); } + XmlWriter& operator=(XmlWriter&& other) noexcept + { + m_context = other.m_context; + other.m_context = nullptr; + return *this; + } ~XmlWriter(); void Write(XmlNode node); @@ -55,8 +72,7 @@ namespace Azure { namespace Storage { namespace _internal { std::string GetDocument(); private: - void* m_buffer = nullptr; - void* m_writer = nullptr; + void* m_context = nullptr; }; }}} // namespace Azure::Storage::_internal diff --git a/sdk/storage/azure-storage-common/src/xml_wrapper.cpp b/sdk/storage/azure-storage-common/src/xml_wrapper.cpp index 213b6c1d6..b835bbe06 100644 --- a/sdk/storage/azure-storage-common/src/xml_wrapper.cpp +++ b/sdk/storage/azure-storage-common/src/xml_wrapper.cpp @@ -4,13 +4,381 @@ #include "azure/storage/common/internal/xml_wrapper.hpp" #include +#include #include +#include + +#if defined(AZ_PLATFORM_WINDOWS) +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif +#if !defined(NOMINMAX) +#define NOMINMAX +#endif +#include +#else #include #include +#endif namespace Azure { namespace Storage { namespace _internal { +#if defined(AZ_PLATFORM_WINDOWS) + + struct XmlReaderContext + { + XmlReaderContext() + { + HRESULT ret = WsCreateError(nullptr, 0, &error); + if (ret != NO_ERROR) + { + throw std::runtime_error("Failed to initialize xml reader."); + } + ret = WsCreateReader(nullptr, 0, &reader, error); + if (ret != NO_ERROR) + { + WsFreeError(error); + throw std::runtime_error("Failed to initialize xml reader."); + } + } + XmlReaderContext(const XmlReaderContext&) = delete; + XmlReaderContext& operator=(const XmlReaderContext&) = delete; + ~XmlReaderContext() + { + WsFreeReader(reader); + WsFreeError(error); + } + + WS_XML_READER* reader = nullptr; + WS_ERROR* error = nullptr; + bool readingAttributes = false; + ULONG attributeIndex = 0; + const WS_XML_ELEMENT_NODE* attributeElementNode = nullptr; + }; + + XmlReader::XmlReader(const char* data, size_t length) + { + if (length > static_cast(std::numeric_limits::max())) + { + throw std::runtime_error("Xml data too big."); + } + + auto context = std::make_unique(); + + WS_XML_READER_BUFFER_INPUT bufferInput; + ZeroMemory(&bufferInput, sizeof(bufferInput)); + bufferInput.input.inputType = WS_XML_READER_INPUT_TYPE_BUFFER; + bufferInput.encodedData = const_cast(data); + bufferInput.encodedDataSize = static_cast(length); + WS_XML_READER_TEXT_ENCODING textEncoding; + ZeroMemory(&textEncoding, sizeof(textEncoding)); + textEncoding.encoding.encodingType = WS_XML_READER_ENCODING_TYPE_TEXT; + textEncoding.charSet = WS_CHARSET_AUTO; + HRESULT ret = WsSetInput( + context->reader, &textEncoding.encoding, &bufferInput.input, nullptr, 0, context->error); + if (ret != S_OK) + { + throw std::runtime_error("Failed to initialize xml reader."); + } + + WS_CHARSET charSet; + ret = WsGetReaderProperty( + context->reader, WS_XML_READER_PROPERTY_CHARSET, &charSet, sizeof(charSet), context->error); + if (ret != S_OK) + { + throw std::runtime_error("Failed to get xml encoding."); + } + if (charSet != WS_CHARSET_UTF8) + { + throw std::runtime_error("Unsupported xml encoding."); + } + + m_context = context.release(); + } + + XmlReader::~XmlReader() + { + if (m_context) + { + delete static_cast(m_context); + } + } + + XmlNode XmlReader::Read() + { + auto context = static_cast(m_context); + + if (context->readingAttributes) + { + const WS_XML_ATTRIBUTE* attribute + = context->attributeElementNode->attributes[context->attributeIndex++]; + if (context->attributeIndex == context->attributeElementNode->attributeCount) + { + context->readingAttributes = false; + context->attributeElementNode = nullptr; + context->attributeIndex = 0; + } + + std::string name( + reinterpret_cast(attribute->localName->bytes), attribute->localName->length); + + if (attribute->value->textType != WS_XML_TEXT_TYPE_UTF8) + { + throw std::runtime_error("Unsupported xml encoding."); + } + + const WS_XML_UTF8_TEXT* utf8Text + = reinterpret_cast(attribute->value); + std::string value( + reinterpret_cast(utf8Text->value.bytes), utf8Text->value.length); + + return XmlNode{XmlNodeType::Attribute, std::move(name), std::move(value)}; + } + + auto moveToNext = [&]() { + HRESULT ret = WsReadNode(context->reader, context->error); + if (!SUCCEEDED(ret)) + { + throw std::runtime_error("Failed to parse xml."); + } + }; + // This will skip WS_XML_NODE_TYPE_BOF when parsing the very beginning of an xml document. But + // it's fine. We don't care about that xml node anyway. + moveToNext(); + + const WS_XML_NODE* node; + HRESULT ret = WsGetReaderNode(context->reader, &node, context->error); + if (!SUCCEEDED(ret)) + { + throw std::runtime_error("Failed to parse xml."); + } + switch (node->nodeType) + { + case WS_XML_NODE_TYPE_ELEMENT: { + const WS_XML_ELEMENT_NODE* elementNode = reinterpret_cast(node); + std::string name( + reinterpret_cast(elementNode->localName->bytes), + elementNode->localName->length); + if (elementNode->attributeCount != 0) + { + context->readingAttributes = true; + context->attributeElementNode = elementNode; + context->attributeIndex = 0; + } + if (elementNode->isEmpty) + { + // Skip the end tag. + moveToNext(); + return XmlNode{XmlNodeType::SelfClosingTag, std::move(name)}; + } + else + { + return XmlNode{XmlNodeType::StartTag, std::move(name)}; + } + } + case WS_XML_NODE_TYPE_TEXT: { + const WS_XML_TEXT_NODE* textNode = (const WS_XML_TEXT_NODE*)node; + if (textNode->text->textType != WS_XML_TEXT_TYPE_UTF8) + { + throw std::runtime_error("Unsupported xml encoding."); + } + const WS_XML_UTF8_TEXT* utf8Text + = reinterpret_cast(textNode->text); + std::string value( + reinterpret_cast(utf8Text->value.bytes), utf8Text->value.length); + return XmlNode{XmlNodeType::Text, std::string(), std::move(value)}; + } + case WS_XML_NODE_TYPE_END_ELEMENT: + return XmlNode{XmlNodeType::EndTag, std::string()}; + case WS_XML_NODE_TYPE_EOF: + return XmlNode{XmlNodeType::End}; + case WS_XML_NODE_TYPE_CDATA: + case WS_XML_NODE_TYPE_END_CDATA: + case WS_XML_NODE_TYPE_COMMENT: + case WS_XML_NODE_TYPE_BOF: + return Read(); + default: + throw std::runtime_error( + "Unknown type " + std::to_string(node->nodeType) + " while parsing xml."); + } + } + + struct XmlWriterContext + { + XmlWriterContext() + { + HRESULT ret = WsCreateError(nullptr, 0, &error); + if (ret != NO_ERROR) + { + throw std::runtime_error("Failed to initialize xml writer."); + } + ret = WsCreateWriter(nullptr, 0, &writer, error); + if (ret != NO_ERROR) + { + WsFreeError(error); + throw std::runtime_error("Failed to initialize xml writer."); + } + ret = WsCreateHeap(1024 * 1024 * 1024, 512, nullptr, 0, &heap, error); + if (ret != NO_ERROR) + { + WsFreeWriter(writer); + WsFreeError(error); + throw std::runtime_error("Failed to initialize xml writer."); + } + } + XmlWriterContext(const XmlWriterContext&) = delete; + XmlWriterContext& operator=(const XmlWriterContext&) = delete; + ~XmlWriterContext() + { + WsFreeHeap(heap); + WsFreeWriter(writer); + WsFreeError(error); + } + + WS_XML_WRITER* writer = nullptr; + WS_ERROR* error = nullptr; + WS_HEAP* heap = nullptr; + WS_XML_BUFFER* buffer = nullptr; + }; + + XmlWriter::XmlWriter() + { + auto context = std::make_unique(); + + HRESULT ret = WsCreateXmlBuffer(context->heap, nullptr, 0, &context->buffer, context->error); + if (ret != NO_ERROR) + { + throw std::runtime_error("Failed to initialize xml writer."); + } + + ret = WsSetOutputToBuffer(context->writer, context->buffer, nullptr, 0, context->error); + if (ret != NO_ERROR) + { + throw std::runtime_error("Failed to initialize xml writer."); + } + + m_context = context.release(); + } + + XmlWriter::~XmlWriter() + { + if (m_context) + { + delete static_cast(m_context); + } + } + + void XmlWriter::Write(XmlNode node) + { + auto context = static_cast(m_context); + if (node.Type == XmlNodeType::StartTag) + { + if (!node.Value.empty()) + { + Write(XmlNode{XmlNodeType::StartTag, std::move(node.Name)}); + Write(XmlNode{XmlNodeType::Text, std::string(), std::move(node.Value)}); + Write(XmlNode{XmlNodeType::EndTag}); + return; + } + WS_XML_STRING name; + name.bytes = reinterpret_cast(&node.Name[0]); + name.length = static_cast(node.Name.length()); + name.dictionary = nullptr; + WS_XML_STRING ns = WS_XML_STRING_NULL; + HRESULT ret = WsWriteStartElement(context->writer, nullptr, &name, &ns, context->error); + if (!SUCCEEDED(ret)) + { + throw std::runtime_error("Failed to write xml."); + } + } + else if (node.Type == XmlNodeType::EndTag) + { + HRESULT ret = WsWriteEndElement(context->writer, context->error); + if (!SUCCEEDED(ret)) + { + throw std::runtime_error("Failed to write xml."); + } + } + else if (node.Type == XmlNodeType::SelfClosingTag) + { + Write(XmlNode{XmlNodeType::StartTag, std::move(node.Name), std::move(node.Value)}); + Write(XmlNode{XmlNodeType::EndTag}); + } + else if (node.Type == XmlNodeType::Text) + { + HRESULT ret = WsWriteCharsUtf8( + context->writer, + reinterpret_cast(node.Value.data()), + static_cast(node.Value.size()), + context->error); + if (!SUCCEEDED(ret)) + { + throw std::runtime_error("Failed to write xml."); + } + } + else if (node.Type == XmlNodeType::Attribute) + { + WS_XML_STRING name; + name.bytes = reinterpret_cast(&node.Name[0]); + name.length = static_cast(node.Name.length()); + name.dictionary = nullptr; + WS_XML_STRING ns = WS_XML_STRING_NULL; + HRESULT ret + = WsWriteStartAttribute(context->writer, nullptr, &name, &ns, FALSE, context->error); + if (!SUCCEEDED(ret)) + { + throw std::runtime_error("Failed to write xml."); + } + Write(XmlNode{XmlNodeType::Text, std::string(), std::move(node.Value)}); + ret = WsWriteEndAttribute(context->writer, context->error); + if (!SUCCEEDED(ret)) + { + throw std::runtime_error("Failed to write xml."); + } + } + else if (node.Type == XmlNodeType::End) + { + } + else + { + throw std::runtime_error( + "Unsupported XmlNode type " + + std::to_string(static_cast::type>(node.Type)) + "."); + } + } + + std::string XmlWriter::GetDocument() + { + auto context = static_cast(m_context); + + BOOL boolValueTrue = TRUE; + WS_XML_WRITER_PROPERTY writerProperty; + writerProperty.id = WS_XML_WRITER_PROPERTY_WRITE_DECLARATION; + writerProperty.value = &boolValueTrue; + writerProperty.valueSize = sizeof(boolValueTrue); + void* xml = nullptr; + ULONG xmlLength = 0; + HRESULT ret = WsWriteXmlBufferToBytes( + context->writer, + context->buffer, + nullptr, + &writerProperty, + 1, + context->heap, + &xml, + &xmlLength, + context->error); + if (!SUCCEEDED(ret)) + { + throw std::runtime_error("Failed to write xml."); + } + + return std::string(static_cast(xml), xmlLength); + } + +#else + struct XmlGlobalInitializer final { XmlGlobalInitializer() { xmlInitParser(); } @@ -19,6 +387,12 @@ namespace Azure { namespace Storage { namespace _internal { static void XmlGlobalInitialize() { static XmlGlobalInitializer globalInitializer; } + struct XmlReaderContext + { + xmlTextReaderPtr reader = nullptr; + bool readingAttributes = false; + }; + XmlReader::XmlReader(const char* data, size_t length) { XmlGlobalInitialize(); @@ -28,30 +402,43 @@ namespace Azure { namespace Storage { namespace _internal { throw std::runtime_error("Xml data too big."); } - m_reader = xmlReaderForMemory(data, static_cast(length), nullptr, nullptr, 0); - if (!m_reader) + xmlTextReaderPtr reader + = xmlReaderForMemory(data, static_cast(length), nullptr, nullptr, 0); + if (!reader) { throw std::runtime_error("Failed to parse xml."); } + + XmlReaderContext* context = new XmlReaderContext(); + context->reader = reader; + m_context = context; } - XmlReader::~XmlReader() { xmlFreeTextReader(static_cast(m_reader)); } + XmlReader::~XmlReader() + { + if (m_context) + { + auto context = static_cast(m_context); + xmlFreeTextReader(static_cast(context->reader)); + delete context; + } + } XmlNode XmlReader::Read() { - xmlTextReaderPtr reader = static_cast(m_reader); - if (m_readingAttributes) + auto context = static_cast(m_context); + if (context->readingAttributes) { - int ret = xmlTextReaderMoveToNextAttribute(reader); + int ret = xmlTextReaderMoveToNextAttribute(context->reader); if (ret == 1) { - const char* name = reinterpret_cast(xmlTextReaderConstName(reader)); - const char* value = reinterpret_cast(xmlTextReaderConstValue(reader)); + const char* name = reinterpret_cast(xmlTextReaderConstName(context->reader)); + const char* value = reinterpret_cast(xmlTextReaderConstValue(context->reader)); return XmlNode{XmlNodeType::Attribute, name, value}; } else if (ret == 0) { - m_readingAttributes = false; + context->readingAttributes = false; } else { @@ -59,7 +446,7 @@ namespace Azure { namespace Storage { namespace _internal { } } - int ret = xmlTextReaderRead(reader); + int ret = xmlTextReaderRead(context->reader); if (ret == 0) { return XmlNode{XmlNodeType::End}; @@ -69,17 +456,17 @@ namespace Azure { namespace Storage { namespace _internal { throw std::runtime_error("Failed to parse xml."); } - int type = xmlTextReaderNodeType(reader); - bool is_empty = xmlTextReaderIsEmptyElement(reader) == 1; - bool has_value = xmlTextReaderHasValue(reader) == 1; - bool has_attributes = xmlTextReaderHasAttributes(reader) == 1; + int type = xmlTextReaderNodeType(context->reader); + bool is_empty = xmlTextReaderIsEmptyElement(context->reader) == 1; + bool has_value = xmlTextReaderHasValue(context->reader) == 1; + bool has_attributes = xmlTextReaderHasAttributes(context->reader) == 1; - const char* name = reinterpret_cast(xmlTextReaderConstName(reader)); - const char* value = reinterpret_cast(xmlTextReaderConstValue(reader)); + const char* name = reinterpret_cast(xmlTextReaderConstName(context->reader)); + const char* value = reinterpret_cast(xmlTextReaderConstValue(context->reader)); if (has_attributes) { - m_readingAttributes = true; + context->readingAttributes = true; } if (type == XML_READER_TYPE_ELEMENT && is_empty) @@ -113,18 +500,47 @@ namespace Azure { namespace Storage { namespace _internal { return Read(); } + struct XmlWriterContext + { + xmlBufferPtr buffer; + xmlTextWriterPtr writer; + }; + XmlWriter::XmlWriter() { XmlGlobalInitialize(); - m_buffer = xmlBufferCreate(); - m_writer = xmlNewTextWriterMemory(static_cast(m_buffer), 0); - xmlTextWriterStartDocument(static_cast(m_writer), nullptr, nullptr, nullptr); + auto buffer = xmlBufferCreate(); + + if (!buffer) + { + throw std::runtime_error("Failed to initialize xml writer."); + } + + auto writer = xmlNewTextWriterMemory(static_cast(buffer), 0); + + if (!writer) + { + xmlBufferFree(static_cast(buffer)); + throw std::runtime_error("Failed to initialize xml writer."); + } + + xmlTextWriterStartDocument(static_cast(writer), nullptr, nullptr, nullptr); + + auto context = new XmlWriterContext; + context->buffer = buffer; + context->writer = writer; + m_context = context; } XmlWriter::~XmlWriter() { - xmlFreeTextWriter(static_cast(m_writer)); - xmlBufferFree(static_cast(m_buffer)); + if (m_context) + { + auto context = static_cast(m_context); + xmlFreeTextWriter(static_cast(context->writer)); + xmlBufferFree(static_cast(context->buffer)); + delete context; + } } namespace { @@ -136,7 +552,8 @@ namespace Azure { namespace Storage { namespace _internal { void XmlWriter::Write(XmlNode node) { - xmlTextWriterPtr writer = static_cast(m_writer); + auto context = static_cast(m_context); + xmlTextWriterPtr writer = context->writer; if (node.Type == XmlNodeType::StartTag) { if (node.Value.empty()) @@ -179,10 +596,13 @@ namespace Azure { namespace Storage { namespace _internal { std::string XmlWriter::GetDocument() { - xmlTextWriterPtr writer = static_cast(m_writer); - xmlBufferPtr buffer = static_cast(m_buffer); + auto context = static_cast(m_context); + xmlTextWriterPtr writer = context->writer; + xmlBufferPtr buffer = context->buffer; xmlTextWriterFlush(writer); return std::string(reinterpret_cast(buffer->content), buffer->use); } +#endif + }}} // namespace Azure::Storage::_internal diff --git a/sdk/storage/azure-storage-common/vcpkg/Config.cmake.in b/sdk/storage/azure-storage-common/vcpkg/Config.cmake.in index e44cca7af..5a241ca13 100644 --- a/sdk/storage/azure-storage-common/vcpkg/Config.cmake.in +++ b/sdk/storage/azure-storage-common/vcpkg/Config.cmake.in @@ -4,11 +4,11 @@ @PACKAGE_INIT@ include(CMakeFindDependencyMacro) -find_dependency(LibXml2) find_dependency(Threads) find_dependency(azure-core-cpp "1.0.0") if(NOT WIN32) + find_dependency(LibXml2) find_dependency(OpenSSL) endif() diff --git a/sdk/storage/azure-storage-common/vcpkg/vcpkg.json b/sdk/storage/azure-storage-common/vcpkg/vcpkg.json index 851f77d45..e260667fa 100644 --- a/sdk/storage/azure-storage-common/vcpkg/vcpkg.json +++ b/sdk/storage/azure-storage-common/vcpkg/vcpkg.json @@ -16,7 +16,10 @@ "default-features": false, "version>=": "1.0.0" }, - "libxml2", + { + "name": "libxml2", + "platform": "!windows" + } { "name": "openssl", "platform": "!windows"