// 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 AtomExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) #define AtomExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) #define AtomExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) #define AtomExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) #define AtomExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) #define AtomExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) #define AtomExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) #define AtomExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) #define AtomExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) #define AtomExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) #define AtomExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ATOMUTIL, e, x, s, __VA_ARGS__) #define AtomExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ATOMUTIL, g, x, s, __VA_ARGS__) static HRESULT ParseAtomDocument( __in IXMLDOMDocument *pixd, __out ATOM_FEED **ppFeed ); static HRESULT ParseAtomFeed( __in IXMLDOMNode *pixnFeed, __out ATOM_FEED **ppFeed ); static HRESULT ParseAtomAuthor( __in IXMLDOMNode* pixnAuthor, __in ATOM_AUTHOR* pAuthor ); static HRESULT ParseAtomCategory( __in IXMLDOMNode* pixnCategory, __in ATOM_CATEGORY* pCategory ); static HRESULT ParseAtomEntry( __in IXMLDOMNode* pixnEntry, __in ATOM_ENTRY* pEntry ); static HRESULT ParseAtomLink( __in IXMLDOMNode* pixnLink, __in ATOM_LINK* pLink ); static HRESULT ParseAtomUnknownElement( __in IXMLDOMNode *pNode, __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement ); static HRESULT ParseAtomUnknownAttribute( __in IXMLDOMNode *pNode, __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute ); static HRESULT AssignDateTime( __in FILETIME* pft, __in IXMLDOMNode* pNode ); static HRESULT AssignString( __out_z LPWSTR* pwzValue, __in IXMLDOMNode* pNode ); static void FreeAtomAuthor( __in_opt ATOM_AUTHOR* pAuthor ); static void FreeAtomContent( __in_opt ATOM_CONTENT* pContent ); static void FreeAtomCategory( __in_opt ATOM_CATEGORY* pCategory ); static void FreeAtomEntry( __in_opt ATOM_ENTRY* pEntry ); static void FreeAtomLink( __in_opt ATOM_LINK* pLink ); static void FreeAtomUnknownElementList( __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement ); static void FreeAtomUnknownAttributeList( __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute ); template static HRESULT AllocateAtomType( __in IXMLDOMNode* pixnParent, __in LPCWSTR wzT, __out T** pprgT, __out DWORD* pcT ); /******************************************************************** AtomInitialize - Initialize ATOM utilities. *********************************************************************/ extern "C" HRESULT DAPI AtomInitialize() { return XmlInitialize(); } /******************************************************************** AtomUninitialize - Uninitialize ATOM utilities. *********************************************************************/ extern "C" void DAPI AtomUninitialize() { XmlUninitialize(); } /******************************************************************** AtomParseFromString - parses out an ATOM feed from a string. *********************************************************************/ extern "C" HRESULT DAPI AtomParseFromString( __in_z LPCWSTR wzAtomString, __out ATOM_FEED **ppFeed ) { Assert(wzAtomString); Assert(ppFeed); HRESULT hr = S_OK; ATOM_FEED *pNewFeed = NULL; IXMLDOMDocument *pixdAtom = NULL; hr = XmlLoadDocument(wzAtomString, &pixdAtom); AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); hr = ParseAtomDocument(pixdAtom, &pNewFeed); AtomExitOnFailure(hr, "Failed to parse ATOM document."); *ppFeed = pNewFeed; pNewFeed = NULL; LExit: ReleaseAtomFeed(pNewFeed); ReleaseObject(pixdAtom); return hr; } /******************************************************************** AtomParseFromFile - parses out an ATOM feed from a file path. *********************************************************************/ extern "C" HRESULT DAPI AtomParseFromFile( __in_z LPCWSTR wzAtomFile, __out ATOM_FEED **ppFeed ) { Assert(wzAtomFile); Assert(ppFeed); HRESULT hr = S_OK; ATOM_FEED *pNewFeed = NULL; IXMLDOMDocument *pixdAtom = NULL; hr = XmlLoadDocumentFromFile(wzAtomFile, &pixdAtom); AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); hr = ParseAtomDocument(pixdAtom, &pNewFeed); AtomExitOnFailure(hr, "Failed to parse ATOM document."); *ppFeed = pNewFeed; pNewFeed = NULL; LExit: ReleaseAtomFeed(pNewFeed); ReleaseObject(pixdAtom); return hr; } /******************************************************************** AtomParseFromDocument - parses out an ATOM feed from an XML document. *********************************************************************/ extern "C" HRESULT DAPI AtomParseFromDocument( __in IXMLDOMDocument* pixdDocument, __out ATOM_FEED **ppFeed ) { Assert(pixdDocument); Assert(ppFeed); HRESULT hr = S_OK; ATOM_FEED *pNewFeed = NULL; hr = ParseAtomDocument(pixdDocument, &pNewFeed); AtomExitOnFailure(hr, "Failed to parse ATOM document."); *ppFeed = pNewFeed; pNewFeed = NULL; LExit: ReleaseAtomFeed(pNewFeed); return hr; } /******************************************************************** AtomFreeFeed - parses out an ATOM feed from a string. *********************************************************************/ extern "C" void DAPI AtomFreeFeed( __in_xcount(pFeed->cItems) ATOM_FEED* pFeed ) { if (pFeed) { FreeAtomUnknownElementList(pFeed->pUnknownElements); ReleaseObject(pFeed->pixn); for (DWORD i = 0; i < pFeed->cLinks; ++i) { FreeAtomLink(pFeed->rgLinks + i); } ReleaseMem(pFeed->rgLinks); for (DWORD i = 0; i < pFeed->cEntries; ++i) { FreeAtomEntry(pFeed->rgEntries + i); } ReleaseMem(pFeed->rgEntries); for (DWORD i = 0; i < pFeed->cCategories; ++i) { FreeAtomCategory(pFeed->rgCategories + i); } ReleaseMem(pFeed->rgCategories); for (DWORD i = 0; i < pFeed->cAuthors; ++i) { FreeAtomAuthor(pFeed->rgAuthors + i); } ReleaseMem(pFeed->rgAuthors); ReleaseStr(pFeed->wzGenerator); ReleaseStr(pFeed->wzIcon); ReleaseStr(pFeed->wzId); ReleaseStr(pFeed->wzLogo); ReleaseStr(pFeed->wzSubtitle); ReleaseStr(pFeed->wzTitle); MemFree(pFeed); } } /******************************************************************** ParseAtomDocument - parses out an ATOM feed from a loaded XML DOM document. *********************************************************************/ static HRESULT ParseAtomDocument( __in IXMLDOMDocument *pixd, __out ATOM_FEED **ppFeed ) { Assert(pixd); Assert(ppFeed); HRESULT hr = S_OK; IXMLDOMElement *pFeedElement = NULL; ATOM_FEED *pNewFeed = NULL; // // Get the document element and start processing feeds. // hr = pixd->get_documentElement(&pFeedElement); AtomExitOnFailure(hr, "failed get_documentElement in ParseAtomDocument"); hr = ParseAtomFeed(pFeedElement, &pNewFeed); AtomExitOnFailure(hr, "Failed to parse ATOM feed."); if (S_FALSE == hr) { hr = S_OK; } *ppFeed = pNewFeed; pNewFeed = NULL; LExit: ReleaseObject(pFeedElement); ReleaseAtomFeed(pNewFeed); return hr; } /******************************************************************** ParseAtomFeed - parses out an ATOM feed from a loaded XML DOM element. *********************************************************************/ static HRESULT ParseAtomFeed( __in IXMLDOMNode *pixnFeed, __out ATOM_FEED **ppFeed ) { Assert(pixnFeed); Assert(ppFeed); HRESULT hr = S_OK; IXMLDOMNodeList *pNodeList = NULL; ATOM_FEED *pNewFeed = NULL; DWORD cAuthors = 0; DWORD cCategories = 0; DWORD cEntries = 0; DWORD cLinks = 0; IXMLDOMNode *pNode = NULL; BSTR bstrNodeName = NULL; // First, allocate the new feed and all the possible sub elements. pNewFeed = (ATOM_FEED*)MemAlloc(sizeof(ATOM_FEED), TRUE); AtomExitOnNull(pNewFeed, hr, E_OUTOFMEMORY, "Failed to allocate ATOM feed structure."); pNewFeed->pixn = pixnFeed; pNewFeed->pixn->AddRef(); hr = AllocateAtomType(pixnFeed, L"author", &pNewFeed->rgAuthors, &pNewFeed->cAuthors); AtomExitOnFailure(hr, "Failed to allocate ATOM feed authors."); hr = AllocateAtomType(pixnFeed, L"category", &pNewFeed->rgCategories, &pNewFeed->cCategories); AtomExitOnFailure(hr, "Failed to allocate ATOM feed categories."); hr = AllocateAtomType(pixnFeed, L"entry", &pNewFeed->rgEntries, &pNewFeed->cEntries); AtomExitOnFailure(hr, "Failed to allocate ATOM feed entries."); hr = AllocateAtomType(pixnFeed, L"link", &pNewFeed->rgLinks, &pNewFeed->cLinks); AtomExitOnFailure(hr, "Failed to allocate ATOM feed links."); // Second, process the elements under a feed. hr = pixnFeed->get_childNodes(&pNodeList); AtomExitOnFailure(hr, "Failed to get child nodes of ATOM feed element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"generator", -1)) { hr = AssignString(&pNewFeed->wzGenerator, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM feed generator."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"icon", -1)) { hr = AssignString(&pNewFeed->wzIcon, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM feed icon."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) { hr = AssignString(&pNewFeed->wzId, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM feed id."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"logo", -1)) { hr = AssignString(&pNewFeed->wzLogo, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM feed logo."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"subtitle", -1)) { hr = AssignString(&pNewFeed->wzSubtitle, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM feed subtitle."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) { hr = AssignString(&pNewFeed->wzTitle, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM feed title."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) { hr = AssignDateTime(&pNewFeed->ftUpdated, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM feed updated."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) { hr = ParseAtomAuthor(pNode, &pNewFeed->rgAuthors[cAuthors]); AtomExitOnFailure(hr, "Failed to parse ATOM author."); ++cAuthors; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) { hr = ParseAtomCategory(pNode, &pNewFeed->rgCategories[cCategories]); AtomExitOnFailure(hr, "Failed to parse ATOM category."); ++cCategories; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"entry", -1)) { hr = ParseAtomEntry(pNode, &pNewFeed->rgEntries[cEntries]); AtomExitOnFailure(hr, "Failed to parse ATOM entry."); ++cEntries; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) { hr = ParseAtomLink(pNode, &pNewFeed->rgLinks[cLinks]); AtomExitOnFailure(hr, "Failed to parse ATOM link."); ++cLinks; } else { hr = ParseAtomUnknownElement(pNode, &pNewFeed->pUnknownElements); AtomExitOnFailure(hr, "Failed to parse unknown ATOM feed element: %ls", bstrNodeName); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } if (!pNewFeed->wzId || !*pNewFeed->wzId) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); AtomExitOnRootFailure(hr, "Failed to find required feed/id element."); } else if (!pNewFeed->wzTitle || !*pNewFeed->wzTitle) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); AtomExitOnRootFailure(hr, "Failed to find required feed/title element."); } else if (0 == pNewFeed->ftUpdated.dwHighDateTime && 0 == pNewFeed->ftUpdated.dwLowDateTime) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); AtomExitOnRootFailure(hr, "Failed to find required feed/updated element."); } *ppFeed = pNewFeed; pNewFeed = NULL; LExit: ReleaseBSTR(bstrNodeName); ReleaseObject(pNode); ReleaseObject(pNodeList); ReleaseAtomFeed(pNewFeed); return hr; } /******************************************************************** AllocateAtomType - allocates enough space for all of the ATOM elements of a particular type under a particular node. *********************************************************************/ template static HRESULT AllocateAtomType( __in IXMLDOMNode* pixnParent, __in LPCWSTR wzT, __out T** pprgT, __out DWORD* pcT ) { HRESULT hr = S_OK; IXMLDOMNodeList *pNodeList = NULL; long cT = 0; T* prgT = NULL; hr = XmlSelectNodes(pixnParent, wzT, &pNodeList); AtomExitOnFailure(hr, "Failed to select all ATOM %ls.", wzT); if (S_OK == hr) { hr = pNodeList->get_length(&cT); AtomExitOnFailure(hr, "Failed to count the number of ATOM %ls.", wzT); if (cT == 0) { ExitFunction(); } prgT = static_cast(MemAlloc(sizeof(T) * cT, TRUE)); AtomExitOnNull(prgT, hr, E_OUTOFMEMORY, "Failed to allocate ATOM."); *pcT = cT; *pprgT = prgT; prgT = NULL; } else { *pprgT = NULL; *pcT = 0; } LExit: ReleaseMem(prgT); ReleaseObject(pNodeList); return hr; } /******************************************************************** ParseAtomAuthor - parses out an ATOM author from a loaded XML DOM node. *********************************************************************/ static HRESULT ParseAtomAuthor( __in IXMLDOMNode* pixnAuthor, __in ATOM_AUTHOR* pAuthor ) { HRESULT hr = S_OK; IXMLDOMNodeList *pNodeList = NULL; IXMLDOMNode *pNode = NULL; BSTR bstrNodeName = NULL; hr = pixnAuthor->get_childNodes(&pNodeList); AtomExitOnFailure(hr, "Failed to get child nodes of ATOM author element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"name", -1)) { hr = AssignString(&pAuthor->wzName, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM author name."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"email", -1)) { hr = AssignString(&pAuthor->wzEmail, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM author email."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"uri", -1)) { hr = AssignString(&pAuthor->wzUrl, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM author uri."); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } AtomExitOnFailure(hr, "Failed to process all ATOM author elements."); hr = S_OK; LExit: ReleaseBSTR(bstrNodeName); ReleaseObject(pNode); ReleaseObject(pNodeList); return hr; } /******************************************************************** ParseAtomCategory - parses out an ATOM category from a loaded XML DOM node. *********************************************************************/ static HRESULT ParseAtomCategory( __in IXMLDOMNode* pixnCategory, __in ATOM_CATEGORY* pCategory ) { HRESULT hr = S_OK; IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; IXMLDOMNodeList *pNodeList = NULL; IXMLDOMNode *pNode = NULL; BSTR bstrNodeName = NULL; // Process attributes first. hr = pixnCategory->get_attributes(&pixnnmAttributes); AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"label", -1)) { hr = AssignString(&pCategory->wzLabel, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM category label."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"scheme", -1)) { hr = AssignString(&pCategory->wzScheme, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM category scheme."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"term", -1)) { hr = AssignString(&pCategory->wzTerm, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM category term."); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } AtomExitOnFailure(hr, "Failed to process all ATOM category attributes."); // Process elements second. hr = pixnCategory->get_childNodes(&pNodeList); AtomExitOnFailure(hr, "Failed to get child nodes of ATOM category element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { hr = ParseAtomUnknownElement(pNode, &pCategory->pUnknownElements); AtomExitOnFailure(hr, "Failed to parse unknown ATOM category element: %ls", bstrNodeName); ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } AtomExitOnFailure(hr, "Failed to process all ATOM category elements."); hr = S_OK; LExit: ReleaseBSTR(bstrNodeName); ReleaseObject(pNode); ReleaseObject(pNodeList); ReleaseObject(pixnnmAttributes); return hr; } /******************************************************************** ParseAtomContent - parses out an ATOM content from a loaded XML DOM node. *********************************************************************/ static HRESULT ParseAtomContent( __in IXMLDOMNode* pixnContent, __in ATOM_CONTENT* pContent ) { HRESULT hr = S_OK; IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; IXMLDOMNodeList *pNodeList = NULL; IXMLDOMNode *pNode = NULL; BSTR bstrNodeName = NULL; // Process attributes first. hr = pixnContent->get_attributes(&pixnnmAttributes); AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) { hr = AssignString(&pContent->wzType, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM content type."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"url", -1)) { hr = AssignString(&pContent->wzUrl, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM content scheme."); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } AtomExitOnFailure(hr, "Failed to process all ATOM content attributes."); // Process elements second. hr = pixnContent->get_childNodes(&pNodeList); AtomExitOnFailure(hr, "Failed to get child nodes of ATOM content element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { hr = ParseAtomUnknownElement(pNode, &pContent->pUnknownElements); AtomExitOnFailure(hr, "Failed to parse unknown ATOM content element: %ls", bstrNodeName); ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } AtomExitOnFailure(hr, "Failed to process all ATOM content elements."); hr = AssignString(&pContent->wzValue, pixnContent); AtomExitOnFailure(hr, "Failed to allocate ATOM content value."); LExit: ReleaseBSTR(bstrNodeName); ReleaseObject(pNode); ReleaseObject(pNodeList); ReleaseObject(pixnnmAttributes); return hr; } /******************************************************************** ParseAtomEntry - parses out an ATOM entry from a loaded XML DOM node. *********************************************************************/ static HRESULT ParseAtomEntry( __in IXMLDOMNode* pixnEntry, __in ATOM_ENTRY* pEntry ) { HRESULT hr = S_OK; IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; IXMLDOMNodeList *pNodeList = NULL; IXMLDOMNode *pNode = NULL; BSTR bstrNodeName = NULL; DWORD cAuthors = 0; DWORD cCategories = 0; DWORD cLinks = 0; pEntry->pixn = pixnEntry; pEntry->pixn->AddRef(); // First, allocate all the possible sub elements. hr = AllocateAtomType(pixnEntry, L"author", &pEntry->rgAuthors, &pEntry->cAuthors); AtomExitOnFailure(hr, "Failed to allocate ATOM entry authors."); hr = AllocateAtomType(pixnEntry, L"category", &pEntry->rgCategories, &pEntry->cCategories); AtomExitOnFailure(hr, "Failed to allocate ATOM entry categories."); hr = AllocateAtomType(pixnEntry, L"link", &pEntry->rgLinks, &pEntry->cLinks); AtomExitOnFailure(hr, "Failed to allocate ATOM entry links."); // Second, process elements. hr = pixnEntry->get_childNodes(&pNodeList); AtomExitOnFailure(hr, "Failed to get child nodes of ATOM entry element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) { hr = AssignString(&pEntry->wzId, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM entry id."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"summary", -1)) { hr = AssignString(&pEntry->wzSummary, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM entry summary."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) { hr = AssignString(&pEntry->wzTitle, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM entry title."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"published", -1)) { hr = AssignDateTime(&pEntry->ftPublished, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM entry published."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) { hr = AssignDateTime(&pEntry->ftUpdated, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM entry updated."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) { hr = ParseAtomAuthor(pNode, &pEntry->rgAuthors[cAuthors]); AtomExitOnFailure(hr, "Failed to parse ATOM entry author."); ++cAuthors; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) { hr = ParseAtomCategory(pNode, &pEntry->rgCategories[cCategories]); AtomExitOnFailure(hr, "Failed to parse ATOM entry category."); ++cCategories; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"content", -1)) { if (NULL != pEntry->pContent) { hr = E_UNEXPECTED; AtomExitOnFailure(hr, "Cannot have two content elements in ATOM entry."); } pEntry->pContent = static_cast(MemAlloc(sizeof(ATOM_CONTENT), TRUE)); AtomExitOnNull(pEntry->pContent, hr, E_OUTOFMEMORY, "Failed to allocate ATOM entry content."); hr = ParseAtomContent(pNode, pEntry->pContent); AtomExitOnFailure(hr, "Failed to parse ATOM entry content."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) { hr = ParseAtomLink(pNode, &pEntry->rgLinks[cLinks]); AtomExitOnFailure(hr, "Failed to parse ATOM entry link."); ++cLinks; } else { hr = ParseAtomUnknownElement(pNode, &pEntry->pUnknownElements); AtomExitOnFailure(hr, "Failed to parse unknown ATOM entry element: %ls", bstrNodeName); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } AtomExitOnFailure(hr, "Failed to process all ATOM entry elements."); if (!pEntry->wzId || !*pEntry->wzId) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); AtomExitOnRootFailure(hr, "Failed to find required feed/entry/id element."); } else if (!pEntry->wzTitle || !*pEntry->wzTitle) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); AtomExitOnRootFailure(hr, "Failed to find required feed/entry/title element."); } else if (0 == pEntry->ftUpdated.dwHighDateTime && 0 == pEntry->ftUpdated.dwLowDateTime) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); AtomExitOnRootFailure(hr, "Failed to find required feed/entry/updated element."); } hr = S_OK; LExit: ReleaseBSTR(bstrNodeName); ReleaseObject(pNode); ReleaseObject(pNodeList); ReleaseObject(pixnnmAttributes); return hr; } /******************************************************************** ParseAtomLink - parses out an ATOM link from a loaded XML DOM node. *********************************************************************/ static HRESULT ParseAtomLink( __in IXMLDOMNode* pixnLink, __in ATOM_LINK* pLink ) { HRESULT hr = S_OK; IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; IXMLDOMNodeList *pNodeList = NULL; IXMLDOMNode *pNode = NULL; BSTR bstrNodeName = NULL; // Process attributes first. hr = pixnLink->get_attributes(&pixnnmAttributes); AtomExitOnFailure(hr, "Failed get attributes for ATOM link."); while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"rel", -1)) { hr = AssignString(&pLink->wzRel, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM link rel."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"href", -1)) { hr = AssignString(&pLink->wzUrl, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM link href."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"length", -1)) { hr = XmlGetAttributeUInt64(pixnLink, bstrNodeName, &pLink->dw64Length); if (E_INVALIDARG == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } AtomExitOnFailure(hr, "Failed to parse ATOM link length."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) { hr = AssignString(&pLink->wzTitle, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM link title."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) { hr = AssignString(&pLink->wzType, pNode); AtomExitOnFailure(hr, "Failed to allocate ATOM link type."); } else { hr = ParseAtomUnknownAttribute(pNode, &pLink->pUnknownAttributes); AtomExitOnFailure(hr, "Failed to parse unknown ATOM link attribute: %ls", bstrNodeName); } ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } AtomExitOnFailure(hr, "Failed to process all ATOM link attributes."); // Process elements second. hr = pixnLink->get_childNodes(&pNodeList); AtomExitOnFailure(hr, "Failed to get child nodes of ATOM link element."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) { hr = ParseAtomUnknownElement(pNode, &pLink->pUnknownElements); AtomExitOnFailure(hr, "Failed to parse unknown ATOM link element: %ls", bstrNodeName); ReleaseNullBSTR(bstrNodeName); ReleaseNullObject(pNode); } AtomExitOnFailure(hr, "Failed to process all ATOM link elements."); hr = AssignString(&pLink->wzValue, pixnLink); AtomExitOnFailure(hr, "Failed to allocate ATOM link value."); LExit: ReleaseBSTR(bstrNodeName); ReleaseObject(pNode); ReleaseObject(pNodeList); ReleaseObject(pixnnmAttributes); return hr; } /******************************************************************** ParseAtomUnknownElement - parses out an unknown item from the ATOM feed from a loaded XML DOM node. *********************************************************************/ static HRESULT ParseAtomUnknownElement( __in IXMLDOMNode *pNode, __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement ) { Assert(ppUnknownElement); HRESULT hr = S_OK; BSTR bstrNodeNamespace = NULL; BSTR bstrNodeName = NULL; BSTR bstrNodeValue = NULL; IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; IXMLDOMNode* pixnAttribute = NULL; ATOM_UNKNOWN_ELEMENT* pNewUnknownElement; pNewUnknownElement = (ATOM_UNKNOWN_ELEMENT*)MemAlloc(sizeof(ATOM_UNKNOWN_ELEMENT), TRUE); AtomExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); hr = pNode->get_namespaceURI(&bstrNodeNamespace); if (S_OK == hr) { hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element namespace."); } else if (S_FALSE == hr) { hr = S_OK; } AtomExitOnFailure(hr, "Failed to get unknown element namespace."); hr = pNode->get_baseName(&bstrNodeName); AtomExitOnFailure(hr, "Failed to get unknown element name."); hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element name."); hr = XmlGetText(pNode, &bstrNodeValue); AtomExitOnFailure(hr, "Failed to get unknown element value."); hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element value."); hr = pNode->get_attributes(&pixnnmAttributes); AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) { hr = ParseAtomUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); AtomExitOnFailure(hr, "Failed to parse attribute on ATOM unknown element."); ReleaseNullObject(pixnAttribute); } if (S_FALSE == hr) { hr = S_OK; } AtomExitOnFailure(hr, "Failed to enumerate all attributes on ATOM unknown element."); ATOM_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; while (*ppTail) { ppTail = &(*ppTail)->pNext; } *ppTail = pNewUnknownElement; pNewUnknownElement = NULL; LExit: FreeAtomUnknownElementList(pNewUnknownElement); ReleaseBSTR(bstrNodeNamespace); ReleaseBSTR(bstrNodeName); ReleaseBSTR(bstrNodeValue); ReleaseObject(pixnnmAttributes); ReleaseObject(pixnAttribute); return hr; } /******************************************************************** ParseAtomUnknownAttribute - parses out attribute from an unknown element *********************************************************************/ static HRESULT ParseAtomUnknownAttribute( __in IXMLDOMNode *pNode, __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute ) { Assert(ppUnknownAttribute); HRESULT hr = S_OK; BSTR bstrNodeNamespace = NULL; BSTR bstrNodeName = NULL; BSTR bstrNodeValue = NULL; ATOM_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; pNewUnknownAttribute = (ATOM_UNKNOWN_ATTRIBUTE*)MemAlloc(sizeof(ATOM_UNKNOWN_ATTRIBUTE), TRUE); AtomExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); hr = pNode->get_namespaceURI(&bstrNodeNamespace); if (S_OK == hr) { hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute namespace."); } else if (S_FALSE == hr) { hr = S_OK; } AtomExitOnFailure(hr, "Failed to get unknown attribute namespace."); hr = pNode->get_baseName(&bstrNodeName); AtomExitOnFailure(hr, "Failed to get unknown attribute name."); hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute name."); hr = XmlGetText(pNode, &bstrNodeValue); AtomExitOnFailure(hr, "Failed to get unknown attribute value."); hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute value."); ATOM_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; while (*ppTail) { ppTail = &(*ppTail)->pNext; } *ppTail = pNewUnknownAttribute; pNewUnknownAttribute = NULL; LExit: FreeAtomUnknownAttributeList(pNewUnknownAttribute); ReleaseBSTR(bstrNodeNamespace); ReleaseBSTR(bstrNodeName); ReleaseBSTR(bstrNodeValue); return hr; } /******************************************************************** AssignDateTime - assigns the value of a node to a FILETIME struct. *********************************************************************/ static HRESULT AssignDateTime( __in FILETIME* pft, __in IXMLDOMNode* pNode ) { HRESULT hr = S_OK; BSTR bstrValue = NULL; if (0 != pft->dwHighDateTime || 0 != pft->dwLowDateTime) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); AtomExitOnRootFailure(hr, "Already process this datetime value."); } hr = XmlGetText(pNode, &bstrValue); AtomExitOnFailure(hr, "Failed to get value."); if (S_OK == hr) { hr = TimeFromString3339(bstrValue, pft); AtomExitOnFailure(hr, "Failed to convert value to time."); } else { ZeroMemory(pft, sizeof(FILETIME)); hr = S_OK; } LExit: ReleaseBSTR(bstrValue); return hr; } /******************************************************************** AssignString - assigns the value of a node to a dynamic string. *********************************************************************/ static HRESULT AssignString( __out_z LPWSTR* pwzValue, __in IXMLDOMNode* pNode ) { HRESULT hr = S_OK; BSTR bstrValue = NULL; if (pwzValue && *pwzValue) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); AtomExitOnRootFailure(hr, "Already processed this value."); } hr = XmlGetText(pNode, &bstrValue); AtomExitOnFailure(hr, "Failed to get value."); if (S_OK == hr) { hr = StrAllocString(pwzValue, bstrValue, 0); AtomExitOnFailure(hr, "Failed to allocate value."); } else { ReleaseNullStr(pwzValue); hr = S_OK; } LExit: ReleaseBSTR(bstrValue); return hr; } /******************************************************************** FreeAtomAuthor - releases all of the memory used by an ATOM author. *********************************************************************/ static void FreeAtomAuthor( __in_opt ATOM_AUTHOR* pAuthor ) { if (pAuthor) { ReleaseStr(pAuthor->wzUrl); ReleaseStr(pAuthor->wzEmail); ReleaseStr(pAuthor->wzName); } } /******************************************************************** FreeAtomCategory - releases all of the memory used by an ATOM category. *********************************************************************/ static void FreeAtomCategory( __in_opt ATOM_CATEGORY* pCategory ) { if (pCategory) { FreeAtomUnknownElementList(pCategory->pUnknownElements); ReleaseStr(pCategory->wzTerm); ReleaseStr(pCategory->wzScheme); ReleaseStr(pCategory->wzLabel); } } /******************************************************************** FreeAtomContent - releases all of the memory used by an ATOM content. *********************************************************************/ static void FreeAtomContent( __in_opt ATOM_CONTENT* pContent ) { if (pContent) { FreeAtomUnknownElementList(pContent->pUnknownElements); ReleaseStr(pContent->wzValue); ReleaseStr(pContent->wzUrl); ReleaseStr(pContent->wzType); } } /******************************************************************** FreeAtomEntry - releases all of the memory used by an ATOM entry. *********************************************************************/ static void FreeAtomEntry( __in_opt ATOM_ENTRY* pEntry ) { if (pEntry) { FreeAtomUnknownElementList(pEntry->pUnknownElements); ReleaseObject(pEntry->pixn); for (DWORD i = 0; i < pEntry->cLinks; ++i) { FreeAtomLink(pEntry->rgLinks + i); } ReleaseMem(pEntry->rgLinks); for (DWORD i = 0; i < pEntry->cCategories; ++i) { FreeAtomCategory(pEntry->rgCategories + i); } ReleaseMem(pEntry->rgCategories); for (DWORD i = 0; i < pEntry->cAuthors; ++i) { FreeAtomAuthor(pEntry->rgAuthors + i); } ReleaseMem(pEntry->rgAuthors); FreeAtomContent(pEntry->pContent); ReleaseMem(pEntry->pContent); ReleaseStr(pEntry->wzTitle); ReleaseStr(pEntry->wzSummary); ReleaseStr(pEntry->wzId); } } /******************************************************************** FreeAtomLink - releases all of the memory used by an ATOM link. *********************************************************************/ static void FreeAtomLink( __in_opt ATOM_LINK* pLink ) { if (pLink) { FreeAtomUnknownElementList(pLink->pUnknownElements); FreeAtomUnknownAttributeList(pLink->pUnknownAttributes); ReleaseStr(pLink->wzValue); ReleaseStr(pLink->wzUrl); ReleaseStr(pLink->wzType); ReleaseStr(pLink->wzTitle); ReleaseStr(pLink->wzRel); } } /******************************************************************** FreeAtomUnknownElement - releases all of the memory used by a list of unknown elements *********************************************************************/ static void FreeAtomUnknownElementList( __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement ) { while (pUnknownElement) { ATOM_UNKNOWN_ELEMENT* pFree = pUnknownElement; pUnknownElement = pUnknownElement->pNext; FreeAtomUnknownAttributeList(pFree->pAttributes); ReleaseStr(pFree->wzNamespace); ReleaseStr(pFree->wzElement); ReleaseStr(pFree->wzValue); MemFree(pFree); } } /******************************************************************** FreeAtomUnknownAttribute - releases all of the memory used by a list of unknown attributes *********************************************************************/ static void FreeAtomUnknownAttributeList( __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute ) { while (pUnknownAttribute) { ATOM_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; pUnknownAttribute = pUnknownAttribute->pNext; ReleaseStr(pFree->wzNamespace); ReleaseStr(pFree->wzAttribute); ReleaseStr(pFree->wzValue); MemFree(pFree); } }