aboutsummaryrefslogtreecommitdiff
path: root/src/dutil/rssutil.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-09-03 11:22:38 -0700
committerRob Mensching <rob@firegiant.com>2017-09-03 13:33:33 -0700
commit5d8375007754101ff2889d0e79486c8f9b7cf5ab (patch)
treea76d6fb6a38dd9f04a93ffcfd9d64e76779b3414 /src/dutil/rssutil.cpp
parent8e8da6dbc051ec884b5d439bb4f44dc027d05bbf (diff)
downloadwix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.gz
wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.bz2
wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.zip
Initial commit
Diffstat (limited to 'src/dutil/rssutil.cpp')
-rw-r--r--src/dutil/rssutil.cpp632
1 files changed, 632 insertions, 0 deletions
diff --git a/src/dutil/rssutil.cpp b/src/dutil/rssutil.cpp
new file mode 100644
index 00000000..db49d954
--- /dev/null
+++ b/src/dutil/rssutil.cpp
@@ -0,0 +1,632 @@
1// 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.
2
3#include "precomp.h"
4
5static HRESULT ParseRssDocument(
6 __in IXMLDOMDocument *pixd,
7 __out RSS_CHANNEL **ppChannel
8 );
9static HRESULT ParseRssChannel(
10 __in IXMLDOMNode *pixnChannel,
11 __out RSS_CHANNEL **ppChannel
12 );
13static HRESULT ParseRssItem(
14 __in IXMLDOMNode *pixnItem,
15 __in DWORD cItem,
16 __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
17 );
18static HRESULT ParseRssUnknownElement(
19 __in IXMLDOMNode *pNode,
20 __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement
21 );
22static HRESULT ParseRssUnknownAttribute(
23 __in IXMLDOMNode *pNode,
24 __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
25 );
26static void FreeRssUnknownElementList(
27 __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement
28 );
29static void FreeRssUnknownAttributeList(
30 __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute
31 );
32
33
34/********************************************************************
35 RssInitialize - Initialize RSS utilities.
36
37*********************************************************************/
38extern "C" HRESULT DAPI RssInitialize()
39{
40 return XmlInitialize();
41}
42
43
44/********************************************************************
45 RssUninitialize - Uninitialize RSS utilities.
46
47*********************************************************************/
48extern "C" void DAPI RssUninitialize()
49{
50 XmlUninitialize();
51}
52
53
54/********************************************************************
55 RssParseFromString - parses out an RSS channel from a string.
56
57*********************************************************************/
58extern "C" HRESULT DAPI RssParseFromString(
59 __in_z LPCWSTR wzRssString,
60 __out RSS_CHANNEL **ppChannel
61 )
62{
63 Assert(wzRssString);
64 Assert(ppChannel);
65
66 HRESULT hr = S_OK;
67 RSS_CHANNEL *pNewChannel = NULL;
68 IXMLDOMDocument *pixdRss = NULL;
69
70 hr = XmlLoadDocument(wzRssString, &pixdRss);
71 ExitOnFailure(hr, "Failed to load RSS string as XML document.");
72
73 hr = ParseRssDocument(pixdRss, &pNewChannel);
74 ExitOnFailure(hr, "Failed to parse RSS document.");
75
76 *ppChannel = pNewChannel;
77 pNewChannel = NULL;
78
79LExit:
80 ReleaseObject(pixdRss);
81
82 ReleaseRssChannel(pNewChannel);
83
84 return hr;
85}
86
87
88/********************************************************************
89 RssParseFromFile - parses out an RSS channel from a file path.
90
91*********************************************************************/
92extern "C" HRESULT DAPI RssParseFromFile(
93 __in_z LPCWSTR wzRssFile,
94 __out RSS_CHANNEL **ppChannel
95 )
96{
97 Assert(wzRssFile);
98 Assert(ppChannel);
99
100 HRESULT hr = S_OK;
101 RSS_CHANNEL *pNewChannel = NULL;
102 IXMLDOMDocument *pixdRss = NULL;
103
104 hr = XmlLoadDocumentFromFile(wzRssFile, &pixdRss);
105 ExitOnFailure(hr, "Failed to load RSS string as XML document.");
106
107 hr = ParseRssDocument(pixdRss, &pNewChannel);
108 ExitOnFailure(hr, "Failed to parse RSS document.");
109
110 *ppChannel = pNewChannel;
111 pNewChannel = NULL;
112
113LExit:
114 ReleaseObject(pixdRss);
115
116 ReleaseRssChannel(pNewChannel);
117
118 return hr;
119}
120
121
122/********************************************************************
123 RssFreeChannel - parses out an RSS channel from a string.
124
125*********************************************************************/
126extern "C" void DAPI RssFreeChannel(
127 __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
128 )
129{
130 if (pChannel)
131 {
132 for (DWORD i = 0; i < pChannel->cItems; ++i)
133 {
134 ReleaseStr(pChannel->rgItems[i].wzTitle);
135 ReleaseStr(pChannel->rgItems[i].wzLink);
136 ReleaseStr(pChannel->rgItems[i].wzDescription);
137 ReleaseStr(pChannel->rgItems[i].wzGuid);
138 ReleaseStr(pChannel->rgItems[i].wzEnclosureUrl);
139 ReleaseStr(pChannel->rgItems[i].wzEnclosureType);
140
141 FreeRssUnknownElementList(pChannel->rgItems[i].pUnknownElements);
142 }
143
144 ReleaseStr(pChannel->wzTitle);
145 ReleaseStr(pChannel->wzLink);
146 ReleaseStr(pChannel->wzDescription);
147 FreeRssUnknownElementList(pChannel->pUnknownElements);
148
149 MemFree(pChannel);
150 }
151}
152
153
154/********************************************************************
155 ParseRssDocument - parses out an RSS channel from a loaded XML DOM document.
156
157*********************************************************************/
158static HRESULT ParseRssDocument(
159 __in IXMLDOMDocument *pixd,
160 __out RSS_CHANNEL **ppChannel
161 )
162{
163 Assert(pixd);
164 Assert(ppChannel);
165
166 HRESULT hr = S_OK;
167 IXMLDOMElement *pRssElement = NULL;
168 IXMLDOMNodeList *pChannelNodes = NULL;
169 IXMLDOMNode *pNode = NULL;
170 BSTR bstrNodeName = NULL;
171
172 RSS_CHANNEL *pNewChannel = NULL;
173
174 //
175 // Get the document element and start processing channels.
176 //
177 hr = pixd ->get_documentElement(&pRssElement);
178 ExitOnFailure(hr, "failed get_documentElement in ParseRssDocument");
179
180 hr = pRssElement->get_childNodes(&pChannelNodes);
181 ExitOnFailure(hr, "Failed to get child nodes of Rss Document element.");
182
183 while (S_OK == (hr = XmlNextElement(pChannelNodes, &pNode, &bstrNodeName)))
184 {
185 if (0 == lstrcmpW(bstrNodeName, L"channel"))
186 {
187 hr = ParseRssChannel(pNode, &pNewChannel);
188 ExitOnFailure(hr, "Failed to parse RSS channel.");
189 }
190 else if (0 == lstrcmpW(bstrNodeName, L"link"))
191 {
192 }
193
194 ReleaseNullBSTR(bstrNodeName);
195 ReleaseNullObject(pNode);
196 }
197
198 if (S_FALSE == hr)
199 {
200 hr = S_OK;
201 }
202
203 *ppChannel = pNewChannel;
204 pNewChannel = NULL;
205
206LExit:
207 ReleaseBSTR(bstrNodeName);
208 ReleaseObject(pNode);
209 ReleaseObject(pChannelNodes);
210 ReleaseObject(pRssElement);
211
212 ReleaseRssChannel(pNewChannel);
213
214 return hr;
215}
216
217
218/********************************************************************
219 ParseRssChannel - parses out an RSS channel from a loaded XML DOM element.
220
221*********************************************************************/
222static HRESULT ParseRssChannel(
223 __in IXMLDOMNode *pixnChannel,
224 __out RSS_CHANNEL **ppChannel
225 )
226{
227 Assert(pixnChannel);
228 Assert(ppChannel);
229
230 HRESULT hr = S_OK;
231 IXMLDOMNodeList *pNodeList = NULL;
232
233 RSS_CHANNEL *pNewChannel = NULL;
234 long cItems = 0;
235
236 IXMLDOMNode *pNode = NULL;
237 BSTR bstrNodeName = NULL;
238 BSTR bstrNodeValue = NULL;
239
240 //
241 // First, calculate how many RSS items we're going to have and allocate
242 // the RSS_CHANNEL structure
243 //
244 hr = XmlSelectNodes(pixnChannel, L"item", &pNodeList);
245 ExitOnFailure(hr, "Failed to select all RSS items in an RSS channel.");
246
247 hr = pNodeList->get_length(&cItems);
248 ExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel.");
249
250 pNewChannel = static_cast<RSS_CHANNEL*>(MemAlloc(sizeof(RSS_CHANNEL) + sizeof(RSS_ITEM) * cItems, TRUE));
251 ExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure.");
252
253 pNewChannel->cItems = cItems;
254
255 //
256 // Process the elements under a channel now.
257 //
258 hr = pixnChannel->get_childNodes(&pNodeList);
259 ExitOnFailure(hr, "Failed to get child nodes of RSS channel element.");
260
261 cItems = 0; // reset the counter and use this to walk through the channel items
262 while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
263 {
264 if (0 == lstrcmpW(bstrNodeName, L"title"))
265 {
266 hr = XmlGetText(pNode, &bstrNodeValue);
267 ExitOnFailure(hr, "Failed to get RSS channel title.");
268
269 hr = StrAllocString(&pNewChannel->wzTitle, bstrNodeValue, 0);
270 ExitOnFailure(hr, "Failed to allocate RSS channel title.");
271 }
272 else if (0 == lstrcmpW(bstrNodeName, L"link"))
273 {
274 hr = XmlGetText(pNode, &bstrNodeValue);
275 ExitOnFailure(hr, "Failed to get RSS channel link.");
276
277 hr = StrAllocString(&pNewChannel->wzLink, bstrNodeValue, 0);
278 ExitOnFailure(hr, "Failed to allocate RSS channel link.");
279 }
280 else if (0 == lstrcmpW(bstrNodeName, L"description"))
281 {
282 hr = XmlGetText(pNode, &bstrNodeValue);
283 ExitOnFailure(hr, "Failed to get RSS channel description.");
284
285 hr = StrAllocString(&pNewChannel->wzDescription, bstrNodeValue, 0);
286 ExitOnFailure(hr, "Failed to allocate RSS channel description.");
287 }
288 else if (0 == lstrcmpW(bstrNodeName, L"ttl"))
289 {
290 hr = XmlGetText(pNode, &bstrNodeValue);
291 ExitOnFailure(hr, "Failed to get RSS channel description.");
292
293 pNewChannel->dwTimeToLive = (DWORD)wcstoul(bstrNodeValue, NULL, 10);
294 }
295 else if (0 == lstrcmpW(bstrNodeName, L"item"))
296 {
297 hr = ParseRssItem(pNode, cItems, pNewChannel);
298 ExitOnFailure(hr, "Failed to parse RSS item.");
299
300 ++cItems;
301 }
302 else
303 {
304 hr = ParseRssUnknownElement(pNode, &pNewChannel->pUnknownElements);
305 ExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName);
306 }
307
308 ReleaseNullBSTR(bstrNodeValue);
309 ReleaseNullBSTR(bstrNodeName);
310 ReleaseNullObject(pNode);
311 }
312
313 *ppChannel = pNewChannel;
314 pNewChannel = NULL;
315
316LExit:
317 ReleaseBSTR(bstrNodeName);
318 ReleaseObject(pNode);
319 ReleaseObject(pNodeList);
320
321 ReleaseRssChannel(pNewChannel);
322
323 return hr;
324}
325
326
327/********************************************************************
328 ParseRssItem - parses out an RSS item from a loaded XML DOM node.
329
330*********************************************************************/
331static HRESULT ParseRssItem(
332 __in IXMLDOMNode *pixnItem,
333 __in DWORD cItem,
334 __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
335 )
336{
337 HRESULT hr = S_OK;
338
339 RSS_ITEM *pItem = NULL;
340 IXMLDOMNodeList *pNodeList = NULL;
341
342 IXMLDOMNode *pNode = NULL;
343 BSTR bstrNodeName = NULL;
344 BSTR bstrNodeValue = NULL;
345
346 //
347 // First make sure we're dealing with a valid item.
348 //
349 if (pChannel->cItems <= cItem)
350 {
351 hr = E_UNEXPECTED;
352 ExitOnFailure(hr, "Unexpected number of items parsed.");
353 }
354
355 pItem = pChannel->rgItems + cItem;
356
357 //
358 // Process the elements under an item now.
359 //
360 hr = pixnItem->get_childNodes(&pNodeList);
361 ExitOnFailure(hr, "Failed to get child nodes of RSS item element.");
362 while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
363 {
364 if (0 == lstrcmpW(bstrNodeName, L"title"))
365 {
366 hr = XmlGetText(pNode, &bstrNodeValue);
367 ExitOnFailure(hr, "Failed to get RSS channel title.");
368
369 hr = StrAllocString(&pItem->wzTitle, bstrNodeValue, 0);
370 ExitOnFailure(hr, "Failed to allocate RSS item title.");
371 }
372 else if (0 == lstrcmpW(bstrNodeName, L"link"))
373 {
374 hr = XmlGetText(pNode, &bstrNodeValue);
375 ExitOnFailure(hr, "Failed to get RSS channel link.");
376
377 hr = StrAllocString(&pItem->wzLink, bstrNodeValue, 0);
378 ExitOnFailure(hr, "Failed to allocate RSS item link.");
379 }
380 else if (0 == lstrcmpW(bstrNodeName, L"description"))
381 {
382 hr = XmlGetText(pNode, &bstrNodeValue);
383 ExitOnFailure(hr, "Failed to get RSS item description.");
384
385 hr = StrAllocString(&pItem->wzDescription, bstrNodeValue, 0);
386 ExitOnFailure(hr, "Failed to allocate RSS item description.");
387 }
388 else if (0 == lstrcmpW(bstrNodeName, L"guid"))
389 {
390 hr = XmlGetText(pNode, &bstrNodeValue);
391 ExitOnFailure(hr, "Failed to get RSS item guid.");
392
393 hr = StrAllocString(&pItem->wzGuid, bstrNodeValue, 0);
394 ExitOnFailure(hr, "Failed to allocate RSS item guid.");
395 }
396 else if (0 == lstrcmpW(bstrNodeName, L"pubDate"))
397 {
398 hr = XmlGetText(pNode, &bstrNodeValue);
399 ExitOnFailure(hr, "Failed to get RSS item guid.");
400
401 hr = TimeFromString(bstrNodeValue, &pItem->ftPublished);
402 ExitOnFailure(hr, "Failed to convert RSS item time.");
403 }
404 else if (0 == lstrcmpW(bstrNodeName, L"enclosure"))
405 {
406 hr = XmlGetAttribute(pNode, L"url", &bstrNodeValue);
407 ExitOnFailure(hr, "Failed to get RSS item enclosure url.");
408
409 hr = StrAllocString(&pItem->wzEnclosureUrl, bstrNodeValue, 0);
410 ExitOnFailure(hr, "Failed to allocate RSS item enclosure url.");
411 ReleaseNullBSTR(bstrNodeValue);
412
413 hr = XmlGetAttributeNumber(pNode, L"length", &pItem->dwEnclosureSize);
414 ExitOnFailure(hr, "Failed to get RSS item enclosure length.");
415
416 hr = XmlGetAttribute(pNode, L"type", &bstrNodeValue);
417 ExitOnFailure(hr, "Failed to get RSS item enclosure type.");
418
419 hr = StrAllocString(&pItem->wzEnclosureType, bstrNodeValue, 0);
420 ExitOnFailure(hr, "Failed to allocate RSS item enclosure type.");
421 }
422 else
423 {
424 hr = ParseRssUnknownElement(pNode, &pItem->pUnknownElements);
425 ExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName);
426 }
427
428 ReleaseNullBSTR(bstrNodeValue);
429 ReleaseNullBSTR(bstrNodeName);
430 ReleaseNullObject(pNode);
431 }
432
433LExit:
434 ReleaseBSTR(bstrNodeValue);
435 ReleaseBSTR(bstrNodeName);
436 ReleaseObject(pNode);
437 ReleaseObject(pNodeList);
438
439 return hr;
440}
441
442
443/********************************************************************
444 ParseRssUnknownElement - parses out an unknown item from the RSS feed from a loaded XML DOM node.
445
446*********************************************************************/
447static HRESULT ParseRssUnknownElement(
448 __in IXMLDOMNode *pNode,
449 __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement
450 )
451{
452 Assert(ppUnknownElement);
453
454 HRESULT hr = S_OK;
455 BSTR bstrNodeNamespace = NULL;
456 BSTR bstrNodeName = NULL;
457 BSTR bstrNodeValue = NULL;
458 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
459 IXMLDOMNode* pixnAttribute = NULL;
460 RSS_UNKNOWN_ELEMENT* pNewUnknownElement;
461
462 pNewUnknownElement = static_cast<RSS_UNKNOWN_ELEMENT*>(MemAlloc(sizeof(RSS_UNKNOWN_ELEMENT), TRUE));
463 ExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element.");
464
465 hr = pNode->get_namespaceURI(&bstrNodeNamespace);
466 if (S_OK == hr)
467 {
468 hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0);
469 ExitOnFailure(hr, "Failed to allocate RSS unknown element namespace.");
470 }
471 else if (S_FALSE == hr)
472 {
473 hr = S_OK;
474 }
475 ExitOnFailure(hr, "Failed to get unknown element namespace.");
476
477 hr = pNode->get_baseName(&bstrNodeName);
478 ExitOnFailure(hr, "Failed to get unknown element name.");
479
480 hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0);
481 ExitOnFailure(hr, "Failed to allocate RSS unknown element name.");
482
483 hr = XmlGetText(pNode, &bstrNodeValue);
484 ExitOnFailure(hr, "Failed to get unknown element value.");
485
486 hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0);
487 ExitOnFailure(hr, "Failed to allocate RSS unknown element value.");
488
489 hr = pNode->get_attributes(&pixnnmAttributes);
490 ExitOnFailure(hr, "Failed get attributes on RSS unknown element.");
491
492 while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute)))
493 {
494 hr = ParseRssUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes);
495 ExitOnFailure(hr, "Failed to parse attribute on RSS unknown element.");
496
497 ReleaseNullObject(pixnAttribute);
498 }
499
500 if (S_FALSE == hr)
501 {
502 hr = S_OK;
503 }
504 ExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element.");
505
506 RSS_UNKNOWN_ELEMENT** ppTail = ppUnknownElement;
507 while (*ppTail)
508 {
509 ppTail = &(*ppTail)->pNext;
510 }
511
512 *ppTail = pNewUnknownElement;
513 pNewUnknownElement = NULL;
514
515LExit:
516 FreeRssUnknownElementList(pNewUnknownElement);
517
518 ReleaseBSTR(bstrNodeNamespace);
519 ReleaseBSTR(bstrNodeName);
520 ReleaseBSTR(bstrNodeValue);
521 ReleaseObject(pixnnmAttributes);
522 ReleaseObject(pixnAttribute);
523
524 return hr;
525}
526
527
528/********************************************************************
529 ParseRssUnknownAttribute - parses out attribute from an unknown element
530
531*********************************************************************/
532static HRESULT ParseRssUnknownAttribute(
533 __in IXMLDOMNode *pNode,
534 __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
535 )
536{
537 Assert(ppUnknownAttribute);
538
539 HRESULT hr = S_OK;
540 BSTR bstrNodeNamespace = NULL;
541 BSTR bstrNodeName = NULL;
542 BSTR bstrNodeValue = NULL;
543 RSS_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute;
544
545 pNewUnknownAttribute = static_cast<RSS_UNKNOWN_ATTRIBUTE*>(MemAlloc(sizeof(RSS_UNKNOWN_ATTRIBUTE), TRUE));
546 ExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute.");
547
548 hr = pNode->get_namespaceURI(&bstrNodeNamespace);
549 if (S_OK == hr)
550 {
551 hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0);
552 ExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace.");
553 }
554 else if (S_FALSE == hr)
555 {
556 hr = S_OK;
557 }
558 ExitOnFailure(hr, "Failed to get unknown attribute namespace.");
559
560 hr = pNode->get_baseName(&bstrNodeName);
561 ExitOnFailure(hr, "Failed to get unknown attribute name.");
562
563 hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0);
564 ExitOnFailure(hr, "Failed to allocate RSS unknown attribute name.");
565
566 hr = XmlGetText(pNode, &bstrNodeValue);
567 ExitOnFailure(hr, "Failed to get unknown attribute value.");
568
569 hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0);
570 ExitOnFailure(hr, "Failed to allocate RSS unknown attribute value.");
571
572 RSS_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute;
573 while (*ppTail)
574 {
575 ppTail = &(*ppTail)->pNext;
576 }
577
578 *ppTail = pNewUnknownAttribute;
579 pNewUnknownAttribute = NULL;
580
581LExit:
582 FreeRssUnknownAttributeList(pNewUnknownAttribute);
583
584 ReleaseBSTR(bstrNodeNamespace);
585 ReleaseBSTR(bstrNodeName);
586 ReleaseBSTR(bstrNodeValue);
587
588 return hr;
589}
590
591
592/********************************************************************
593 FreeRssUnknownElement - releases all of the memory used by a list of unknown elements
594
595*********************************************************************/
596static void FreeRssUnknownElementList(
597 __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement
598 )
599{
600 while (pUnknownElement)
601 {
602 RSS_UNKNOWN_ELEMENT* pFree = pUnknownElement;
603 pUnknownElement = pUnknownElement->pNext;
604
605 FreeRssUnknownAttributeList(pFree->pAttributes);
606 ReleaseStr(pFree->wzNamespace);
607 ReleaseStr(pFree->wzElement);
608 ReleaseStr(pFree->wzValue);
609 MemFree(pFree);
610 }
611}
612
613
614/********************************************************************
615 FreeRssUnknownAttribute - releases all of the memory used by a list of unknown attributes
616
617*********************************************************************/
618static void FreeRssUnknownAttributeList(
619 __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute
620 )
621{
622 while (pUnknownAttribute)
623 {
624 RSS_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute;
625 pUnknownAttribute = pUnknownAttribute->pNext;
626
627 ReleaseStr(pFree->wzNamespace);
628 ReleaseStr(pFree->wzAttribute);
629 ReleaseStr(pFree->wzValue);
630 MemFree(pFree);
631 }
632}