// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. #include "precomp.h" // Exit macros #define RssExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) #define RssExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) #define RssExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) #define RssExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) #define RssExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) #define RssExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) #define RssExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) #define RssExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) #define RssExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) #define RssExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) #define RssExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RSSUTIL, e, x, s, __VA_ARGS__) #define RssExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RSSUTIL, g, x, s, __VA_ARGS__) static HRESULT ParseRssDocument( __in IXMLDOMDocument *pixd, __out RSS_CHANNEL **ppChannel ); static HRESULT ParseRssChannel( __in IXMLDOMNode *pixnChannel, __out RSS_CHANNEL **ppChannel ); static HRESULT ParseRssItem( __in IXMLDOMNode *pixnItem, __in DWORD cItem, __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel ); static HRESULT ParseRssUnknownElement( __in IXMLDOMNode *pNode, __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement ); static HRESULT ParseRssUnknownAttribute( __in IXMLDOMNode *pNode, __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute ); static void FreeRssUnknownElementList( __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement ); static void FreeRssUnknownAttributeList( __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute ); /******************************************************************** RssInitialize - Initialize RSS utilities. *********************************************************************/ extern "C" HRESULT DAPI RssInitialize() { return XmlInitialize(); } /******************************************************************** RssUninitialize - Uninitialize RSS utilities. *********************************************************************/ extern "C" void DAPI RssUninitialize() { XmlUninitialize(); } /******************************************************************** RssParseFromString - parses out an RSS channel from a string. *********************************************************************/ extern "C" HRESULT DAPI RssParseFromString( __in_z LPCWSTR wzRssString, __out RSS_CHANNEL **ppChannel ) { Assert(wzRssString); Assert(ppChannel); HRESULT hr = S_OK; RSS_CHANNEL *pNewChannel = NULL; IXMLDOMDocument *pixdRss = NULL; hr = XmlLoadDocument(wzRssString, &pixdRss); RssExitOnFailure(hr, "Failed to load RSS string as XML document."); hr = ParseRssDocument(pixdRss, &pNewChannel); RssExitOnFailure(hr, "Failed to parse RSS document."); *ppChannel = pNewChannel; pNewChannel = NULL; LExit: ReleaseObject(pixdRss); ReleaseRssChannel(pNewChannel); return hr; } /******************************************************************** RssParseFromFile - parses out an RSS channel from a file path. *********************************************************************/ extern "C" HRESULT DAPI RssParseFromFile( __in_z LPCWSTR wzRssFile, __out RSS_CHANNEL **ppChannel ) { Assert(wzRssFile); Assert(ppChannel); HRESULT hr = S_OK; RSS_CHANNEL *pNewChannel = NULL; IXMLDOMDocument *pixdRss = NULL; hr = XmlLoadDocumentFromFile(wzRssFile, &pixdRss); RssExitOnFailure(hr, "Failed to load RSS string as XML document."); hr = ParseRssDocument(pixdRss, &pNewChannel); RssExitOnFailure(hr, "Failed to parse RSS document."); *ppChannel = pNewChannel; pNewChannel = NULL; LExit: ReleaseObject(pixdRss); ReleaseRssChannel(pNewChannel); return hr; } /******************************************************************** RssFreeChannel - parses out an RSS channel from a string. *********************************************************************/ extern "C" void DAPI RssFreeChannel( __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel ) { if (pChannel) { for (DWORD i = 0; i < pChannel->cItems; ++i) { ReleaseStr(pChannel->rgItems[i].wzTitle); ReleaseStr(pChannel->rgItems[i].wzLink); ReleaseStr(pChannel->rgItems[i].wzDescription); ReleaseStr(pChannel->rgItems[i].wzGuid); ReleaseStr(pChannel->rgItems[i].wzEnclosureUrl); ReleaseStr(pChannel->rgItems[i].wzEnclosureType); FreeRssUnknownElementList(pChannel->rgItems[i].pUnknownElements); } ReleaseStr(pChannel->wzTitle); ReleaseStr(pChannel->wzLink); ReleaseStr(pChannel->wzDescription); FreeRssUnknownElementList(pChannel->pUnknownElements); MemFree(pChannel); } } /******************************************************************** ParseRssDocument - parses out an RSS channel from a loaded XML DOM document. *********************************************************************/ static HRESULT ParseRssDocument( __in IXMLDOMDocument *pixd, __out RSS_CHANNEL **ppChannel ) { Assert(pixd); Assert(ppChannel); HRESULT hr = S_OK; IXMLDOMElement *pRssElement = NULL; IXMLDOMNodeList *pChannelNodes = NULL; IXMLDOMNode *pNode = NULL; BSTR bstrNodeName = NULL; RSS_CHANNEL *pNewChannel = NULL; // // Get the document element and start processing channels. // hr = pixd ->get_documentElement(&pRssElement); RssExitOnFailure(hr, "failed get_documentElement in ParseRssDocument"); hr = pRssElement->get_childNodes(&pChannelNodes); RssExitOnFailure(hr, "Failed to get child nodes of Rss Document element."); while (S_OK == (hr = XmlNextElement(pChannelNodes, &pNode, &bstrNodeName))) { if (0 == lstrcmpW(bstrNodeName, L"channel")) { hr = ParseRssChannel(pNode, &pNewChannel); RssExitOnFailure(hr, "Failed to parse RSS channel."); } else if (0 == lstrcmpW(bstrNodeName, L"link")) { } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } if (S_FALSE == hr) { hr = S_OK; } *ppChannel = pNewChannel; pNewChannel = NULL; LExit: ReleaseBSTR(bstrNodeName); ReleaseObject(pNode); ReleaseObject(pChannelNodes); ReleaseObject(pRssElement); ReleaseRssChannel(pNewChannel); return hr; } /******************************************************************** ParseRssChannel - parses out an RSS channel from a loaded XML DOM element. *********************************************************************/ static HRESULT ParseRssChannel( __in IXMLDOMNode *pixnChannel, __out RSS_CHANNEL **ppChannel ) { Assert(pixnChannel); Assert(ppChannel); HRESULT hr = S_OK; IXMLDOMNodeList *pNodeList = NULL; RSS_CHANNEL *pNewChannel = NULL; long cItems = 0; IXMLDOMNode *pNode = NULL; BSTR bstrNodeName = NULL; BSTR bstrNodeValue = NULL; // // First, calculate how many RSS items we're going to have and allocate // the RSS_CHANNEL structure // hr = XmlSelectNodes(pixnChannel, L"item", &pNodeList); RssExitOnFailure(hr, "Failed to select all RSS items in an RSS channel."); hr = pNodeList->get_length(&cItems); RssExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel."); pNewChannel = static_cast(MemAlloc(sizeof(RSS_CHANNEL) + sizeof(RSS_ITEM) * cItems, TRUE)); RssExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure."); pNewChannel->cItems = cItems; // // Process the elements under a channel now. // hr = pixnChannel->get_childNodes(&pNodeList); RssExitOnFailure(hr, "Failed to get child nodes of RSS channel element."); cItems = 0; // reset the counter and use this to walk through the channel items while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { if (0 == lstrcmpW(bstrNodeName, L"title")) { hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS channel title."); hr = StrAllocString(&pNewChannel->wzTitle, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS channel title."); } else if (0 == lstrcmpW(bstrNodeName, L"link")) { hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS channel link."); hr = StrAllocString(&pNewChannel->wzLink, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS channel link."); } else if (0 == lstrcmpW(bstrNodeName, L"description")) { hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS channel description."); hr = StrAllocString(&pNewChannel->wzDescription, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS channel description."); } else if (0 == lstrcmpW(bstrNodeName, L"ttl")) { hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS channel description."); pNewChannel->dwTimeToLive = (DWORD)wcstoul(bstrNodeValue, NULL, 10); } else if (0 == lstrcmpW(bstrNodeName, L"item")) { hr = ParseRssItem(pNode, cItems, pNewChannel); RssExitOnFailure(hr, "Failed to parse RSS item."); ++cItems; } else { hr = ParseRssUnknownElement(pNode, &pNewChannel->pUnknownElements); RssExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName); } ReleaseNullBSTR(bstrNodeValue); ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } *ppChannel = pNewChannel; pNewChannel = NULL; LExit: ReleaseBSTR(bstrNodeName); ReleaseObject(pNode); ReleaseObject(pNodeList); ReleaseRssChannel(pNewChannel); return hr; } /******************************************************************** ParseRssItem - parses out an RSS item from a loaded XML DOM node. *********************************************************************/ static HRESULT ParseRssItem( __in IXMLDOMNode *pixnItem, __in DWORD cItem, __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel ) { HRESULT hr = S_OK; RSS_ITEM *pItem = NULL; IXMLDOMNodeList *pNodeList = NULL; IXMLDOMNode *pNode = NULL; BSTR bstrNodeName = NULL; BSTR bstrNodeValue = NULL; // // First make sure we're dealing with a valid item. // if (pChannel->cItems <= cItem) { hr = E_UNEXPECTED; RssExitOnFailure(hr, "Unexpected number of items parsed."); } pItem = pChannel->rgItems + cItem; // // Process the elements under an item now. // hr = pixnItem->get_childNodes(&pNodeList); RssExitOnFailure(hr, "Failed to get child nodes of RSS item element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { if (0 == lstrcmpW(bstrNodeName, L"title")) { hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS channel title."); hr = StrAllocString(&pItem->wzTitle, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS item title."); } else if (0 == lstrcmpW(bstrNodeName, L"link")) { hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS channel link."); hr = StrAllocString(&pItem->wzLink, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS item link."); } else if (0 == lstrcmpW(bstrNodeName, L"description")) { hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS item description."); hr = StrAllocString(&pItem->wzDescription, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS item description."); } else if (0 == lstrcmpW(bstrNodeName, L"guid")) { hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS item guid."); hr = StrAllocString(&pItem->wzGuid, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS item guid."); } else if (0 == lstrcmpW(bstrNodeName, L"pubDate")) { hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS item guid."); hr = TimeFromString(bstrNodeValue, &pItem->ftPublished); RssExitOnFailure(hr, "Failed to convert RSS item time."); } else if (0 == lstrcmpW(bstrNodeName, L"enclosure")) { hr = XmlGetAttribute(pNode, L"url", &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS item enclosure url."); hr = StrAllocString(&pItem->wzEnclosureUrl, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS item enclosure url."); ReleaseNullBSTR(bstrNodeValue); hr = XmlGetAttributeNumber(pNode, L"length", &pItem->dwEnclosureSize); RssExitOnFailure(hr, "Failed to get RSS item enclosure length."); hr = XmlGetAttribute(pNode, L"type", &bstrNodeValue); RssExitOnFailure(hr, "Failed to get RSS item enclosure type."); hr = StrAllocString(&pItem->wzEnclosureType, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS item enclosure type."); } else { hr = ParseRssUnknownElement(pNode, &pItem->pUnknownElements); RssExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName); } ReleaseNullBSTR(bstrNodeValue); ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } LExit: ReleaseBSTR(bstrNodeValue); ReleaseBSTR(bstrNodeName); ReleaseObject(pNode); ReleaseObject(pNodeList); return hr; } /******************************************************************** ParseRssUnknownElement - parses out an unknown item from the RSS feed from a loaded XML DOM node. *********************************************************************/ static HRESULT ParseRssUnknownElement( __in IXMLDOMNode *pNode, __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement ) { Assert(ppUnknownElement); HRESULT hr = S_OK; BSTR bstrNodeNamespace = NULL; BSTR bstrNodeName = NULL; BSTR bstrNodeValue = NULL; IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; IXMLDOMNode* pixnAttribute = NULL; RSS_UNKNOWN_ELEMENT* pNewUnknownElement; pNewUnknownElement = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ELEMENT), TRUE)); RssExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); hr = pNode->get_namespaceURI(&bstrNodeNamespace); if (S_OK == hr) { hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); RssExitOnFailure(hr, "Failed to allocate RSS unknown element namespace."); } else if (S_FALSE == hr) { hr = S_OK; } RssExitOnFailure(hr, "Failed to get unknown element namespace."); hr = pNode->get_baseName(&bstrNodeName); RssExitOnFailure(hr, "Failed to get unknown element name."); hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); RssExitOnFailure(hr, "Failed to allocate RSS unknown element name."); hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get unknown element value."); hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS unknown element value."); hr = pNode->get_attributes(&pixnnmAttributes); RssExitOnFailure(hr, "Failed get attributes on RSS unknown element."); while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) { hr = ParseRssUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); RssExitOnFailure(hr, "Failed to parse attribute on RSS unknown element."); ReleaseNullObject(pixnAttribute); } if (S_FALSE == hr) { hr = S_OK; } RssExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element."); RSS_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; while (*ppTail) { ppTail = &(*ppTail)->pNext; } *ppTail = pNewUnknownElement; pNewUnknownElement = NULL; LExit: FreeRssUnknownElementList(pNewUnknownElement); ReleaseBSTR(bstrNodeNamespace); ReleaseBSTR(bstrNodeName); ReleaseBSTR(bstrNodeValue); ReleaseObject(pixnnmAttributes); ReleaseObject(pixnAttribute); return hr; } /******************************************************************** ParseRssUnknownAttribute - parses out attribute from an unknown element *********************************************************************/ static HRESULT ParseRssUnknownAttribute( __in IXMLDOMNode *pNode, __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute ) { Assert(ppUnknownAttribute); HRESULT hr = S_OK; BSTR bstrNodeNamespace = NULL; BSTR bstrNodeName = NULL; BSTR bstrNodeValue = NULL; RSS_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; pNewUnknownAttribute = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ATTRIBUTE), TRUE)); RssExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); hr = pNode->get_namespaceURI(&bstrNodeNamespace); if (S_OK == hr) { hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace."); } else if (S_FALSE == hr) { hr = S_OK; } RssExitOnFailure(hr, "Failed to get unknown attribute namespace."); hr = pNode->get_baseName(&bstrNodeName); RssExitOnFailure(hr, "Failed to get unknown attribute name."); hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute name."); hr = XmlGetText(pNode, &bstrNodeValue); RssExitOnFailure(hr, "Failed to get unknown attribute value."); hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute value."); RSS_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; while (*ppTail) { ppTail = &(*ppTail)->pNext; } *ppTail = pNewUnknownAttribute; pNewUnknownAttribute = NULL; LExit: FreeRssUnknownAttributeList(pNewUnknownAttribute); ReleaseBSTR(bstrNodeNamespace); ReleaseBSTR(bstrNodeName); ReleaseBSTR(bstrNodeValue); return hr; } /******************************************************************** FreeRssUnknownElement - releases all of the memory used by a list of unknown elements *********************************************************************/ static void FreeRssUnknownElementList( __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement ) { while (pUnknownElement) { RSS_UNKNOWN_ELEMENT* pFree = pUnknownElement; pUnknownElement = pUnknownElement->pNext; FreeRssUnknownAttributeList(pFree->pAttributes); ReleaseStr(pFree->wzNamespace); ReleaseStr(pFree->wzElement); ReleaseStr(pFree->wzValue); MemFree(pFree); } } /******************************************************************** FreeRssUnknownAttribute - releases all of the memory used by a list of unknown attributes *********************************************************************/ static void FreeRssUnknownAttributeList( __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute ) { while (pUnknownAttribute) { RSS_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; pUnknownAttribute = pUnknownAttribute->pNext; ReleaseStr(pFree->wzNamespace); ReleaseStr(pFree->wzAttribute); ReleaseStr(pFree->wzValue); MemFree(pFree); } }