browserutilities/feedsengine/FeedsServer/XmlUtils/src/XmlUtils.cpp
changeset 0 dd21522fd290
child 36 0ed94ceaa377
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/browserutilities/feedsengine/FeedsServer/XmlUtils/src/XmlUtils.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,899 @@
+/*
+* Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  Misc. libxml2 related utilities.
+*
+*/
+
+
+
+#include <charconv.h>
+#include <EscapeUtils.h>
+#include <utf.h>
+
+#include "CleanupLibXml2.h"  
+#include "LeakTracker.h"
+#include "Logger.h"
+#include "XmlEncoding.h"
+#include "XmlEntity.h"
+#include "XmlUtils.h"
+
+// Local data-types
+struct ErrorContext
+    {
+    TInt   error;
+    TBool  retry;
+    };
+
+// Static variables
+static CXmlEntity*  sXmlEntity = NULL;
+
+
+// -----------------------------------------------------------------------------
+// EntityResolverL
+//
+// A local function to resolve entities found in the buffer being parsed. 
+// -----------------------------------------------------------------------------
+//
+static xmlEntityPtr EntityResolverL(void* /*aContext*/, const xmlChar *aName) 
+    {
+    xmlEntityPtr  entity = NULL;
+	
+    // Use the standard resolver.
+    entity = xmlGetPredefinedEntity(aName);
+    
+    // Resolve other entities too.
+    if ((sXmlEntity != NULL) && (entity == NULL))
+        {
+        entity = const_cast<xmlEntityPtr>(sXmlEntity->ResolveL(aName));
+        }
+
+    return entity;
+    }
+
+
+// -----------------------------------------------------------------------------
+// StructuredErrorReporter
+//
+// A local function to collect errors found while parsing the buffer.
+// See http://www.xmlsoft.org/html/libxml-xmlerror.html.
+// -----------------------------------------------------------------------------
+//
+static void StructuredErrorReporter(void* aContext, xmlErrorPtr error)
+    {
+    _LIT(KErr, "error");
+    
+    TBool          ignore = EFalse;
+    TPtrC          type(KErr);
+    ErrorContext*  context = static_cast<ErrorContext*>(aContext);
+
+    if (context != NULL)
+        {        
+        context->error = error->code; 
+
+        switch (error->code)
+            {
+            case XML_ERR_UNSUPPORTED_ENCODING:          // 32
+                // Try to reparse the buffer with another char-encoding.
+                context->retry = ETrue;
+                ignore = ETrue;
+                break;
+
+            case XML_ERR_UNDECLARED_ENTITY:             // 26
+            case XML_WAR_UNDECLARED_ENTITY:             // 27
+                type.Set(_L("warning"));
+                break;
+
+            case XML_ERR_INTERNAL_ERROR:                // 1
+            case XML_ERR_DOCUMENT_EMPTY:                // 4
+            case XML_ERR_DOCUMENT_END:                  // 5
+            case XML_ERR_INVALID_CHAR:                  // 9
+            case XML_ERR_INVALID_DEC_CHARREF:           // 7
+            case XML_ERR_ENTITYREF_SEMICOL_MISSING:     // 23
+            case XML_NS_ERR_QNAME:                      // 38
+            case XML_ERR_LT_IN_ATTRIBUTE:               // 39
+            case XML_ERR_ATTRIBUTE_NOT_STARTED:         // 41
+            case XML_ERR_ATTRIBUTE_WITHOUT_VALUE:       // 42
+            case XML_ERR_ATTRIBUTE_REDEFINED:           // 43
+            case XML_ERR_LITERAL_NOT_STARTED:           // 46
+            case XML_ERR_PI_NOT_STARTED:                // 47
+            case XML_ERR_PI_NOT_FINISHED:               // 61
+            case XML_ERR_DOCTYPE_NOT_FINISHED:          // 62
+            case XML_ERR_MISPLACED_CDATA_END:           // 63
+            case XML_ERR_RESERVED_XML_NAME:             // 64
+            case XML_ERR_SPACE_REQUIRED:                // 65
+            case XML_ERR_NAME_REQUIRED:                 // 68
+            case XML_ERR_URI_REQUIRED:                  // 70
+            case XML_ERR_GT_REQUIRED:                   // 73
+            case XML_ERR_LTSLASH_REQUIRED:              // 74
+            case XML_ERR_HYPHEN_IN_COMMENT:             // 80
+            case XML_ERR_TAG_NAME_MISMATCH:             // 76
+            case XML_ERR_TAG_NOT_FINISHED:              // 77
+            case XML_NS_ERR_UNDEFINED_NAMESPACE:        // 201
+                // Just log these as errors and continue if possible.
+                break;
+
+            default:
+                break;
+            }
+        }
+        
+    if (ignore == EFalse)
+        {
+        FEED_LOG2(_L("Feeds"), _L("Feeds.log"), 
+                EFileLoggingModeAppend, _L("FEEDS: parser %S!!! Code:%d"), &type, error->code);
+        }
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::NewL
+//
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CXmlUtils* CXmlUtils::NewL()
+    {
+    CXmlUtils* self = new (ELeave) CXmlUtils();
+    
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop();
+
+    return self;
+    }
+
+        
+// -----------------------------------------------------------------------------
+// CXmlUtils::CXmlUtils
+//
+// C++ default constructor.
+// -----------------------------------------------------------------------------
+//
+CXmlUtils::CXmlUtils():
+        iLeakTracker(CLeakTracker::ELibXml2)
+    {
+    }
+        
+
+// -----------------------------------------------------------------------------
+// CXmlEncoding::ConstructL
+//
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CXmlUtils::ConstructL()
+    {
+    // will call XmlEngine::XmlEngineAttachL()
+    iImpl.OpenL();
+
+    // Create the XmlEncoding singleton.
+    iXmlEncoding = CXmlEncoding::NewL();
+    
+    // Create the XmlEntity singleton.
+    iXmlEntity = CXmlEntity::NewL();
+    }
+        
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::~CXmlUtils
+//
+// Deconstructor.
+// -----------------------------------------------------------------------------
+//
+CXmlUtils::~CXmlUtils()
+    {
+    delete iXmlEncoding;
+    delete iXmlEntity;
+
+    // will call XmlEngine::XmlEngineCleanup()
+    iImpl.Close();
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::ParseBufferL
+//
+// Returns a libxml2 document from the provided buffer.
+// -----------------------------------------------------------------------------
+//
+RXmlEngDocument CXmlUtils::ParseBufferL(const TDesC8& aBuffer, const TDesC& aCharSet) const
+    {
+    xmlDocPtr         document = NULL;
+    xmlParserCtxtPtr  contxt = NULL;
+    ErrorContext      errorContext;
+    HBufC8*           utf8Buffer = NULL;
+    const xmlChar*    parseBuffer = NULL;
+    TInt              parseLength = 0;
+    TUint             encoding;
+    RXmlEngDocument   doc;
+    
+    if (aBuffer.Length() == 0)
+        {
+        User::Leave(KErrArgument);
+        }
+    
+    // Prepare the parser's context.
+  	User::LeaveIfNull(contxt = xmlNewParserCtxt());
+    
+    sXmlEntity = iXmlEntity;
+    contxt->sax->getEntity = EntityResolverL;
+    CleanupStack::PushL(TCleanupItem(&CleanupParseBuffer, contxt));
+    
+    // Set the error callback
+    xmlSetStructuredErrorFunc(&errorContext, StructuredErrorReporter);
+
+    // If need be convert the buffer to utf8.
+    if (iXmlEncoding->DetermineCharEncodingL(aBuffer, aCharSet, encoding))
+        {
+        if (encoding != KCharacterSetIdentifierUtf8)
+            {
+            utf8Buffer = iXmlEncoding->ConvertToUtf8L(encoding, aBuffer);
+            CleanupStack::PushL(utf8Buffer);
+
+            // Skip anything before the xml prolog.
+            parseBuffer = utf8Buffer->Ptr();
+            parseLength = utf8Buffer->Length();
+
+            SkipCharsBeforeXmlProlog(&parseBuffer, parseLength);
+            }
+        }
+
+    // Otherwise its already utf8 so use the provided buffer.
+    if (utf8Buffer == NULL)
+        {
+        // Skip anything before the xml prolog.
+        parseBuffer = aBuffer.Ptr();
+        parseLength = aBuffer.Length();
+
+        SkipCharsBeforeXmlProlog(&parseBuffer, parseLength);
+        }
+
+    // Parse the document.
+    errorContext.error = XML_ERR_OK;
+    errorContext.retry = EFalse;        
+
+    document = xmlSAXParseMemory(contxt->sax, (char*) parseBuffer, parseLength, FALSE);
+    
+    if (document == NULL)
+        {
+        User::Leave(KErrCorrupt);
+        }
+
+    if (utf8Buffer != NULL)
+        {
+        CleanupStack::PopAndDestroy(utf8Buffer);
+        }
+    
+    CleanupStack::PopAndDestroy(/*contxt*/);    
+
+    // Wrap and return the document.
+    CleanupStack::PushL(document);
+    doc.OpenL( (RXmlEngDOMImplementation&)iImpl, document );
+    CleanupStack::Pop(/*document*/);
+    
+    return doc;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::CleanupParseBuffer
+// 
+// The cleanup method for ParseBufferL
+// -----------------------------------------------------------------------------
+//
+void CXmlUtils::CleanupParseBuffer(TAny *aPtr)
+    {
+    xmlResetLastError();
+    xmlSetGenericErrorFunc(NULL, NULL);
+    xmlFreeParserCtxt(static_cast<xmlParserCtxtPtr>(aPtr));
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::SkipCharsBeforeXmlProlog
+// 
+// Skip any chars before the XML-prolog.
+// -----------------------------------------------------------------------------
+//
+void CXmlUtils::SkipCharsBeforeXmlProlog(const TUint8** aString, TInt& aLen) const
+    {
+    _LIT8(KStart, "<?xml");
+    
+    const xmlChar*  origStr = *aString;
+    TInt            origLen = aLen;
+
+    while (aLen > 0)
+        {
+        if ((aLen >= 5) && (xmlStrncasecmp(*aString, KStart().Ptr(), 5) == 0))
+            {
+            break;
+            }
+        else
+            {
+            (*aString)++;
+            aLen--;
+            }
+        }
+
+    // If the xml prolog is missing then do nothing.
+    if (aLen == 0)
+        {
+        *aString = origStr;
+        aLen = origLen;
+        }
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::CleanupUrlL
+// 
+// Resolves any entities and escaped chars in the given url.  
+// -----------------------------------------------------------------------------
+//
+void CXmlUtils::CleanupUrlL(TDes& aUrl) const
+    {
+    HBufC*  temp = NULL;
+
+    // Resolve entities.
+    (void) ResolveEntitiesL(aUrl);
+
+    // Unescape the url.
+    temp = EscapeUtils::EscapeDecodeL(aUrl);
+    CleanupStack::PushL(temp);
+
+    // Trim leading and trailing whitespace.
+    temp->Des().Trim();
+
+    // Copy temp back to aUrl.  This is always safe because temp->Des().Length()
+    // is always <= aUrl.Length().
+    aUrl.Copy(*temp);
+
+    CleanupStack::PopAndDestroy(temp);
+    }    
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::CleanupMarkupL
+// 
+// Resolves any html entities and removes any markup found in the given descriptor.
+// -----------------------------------------------------------------------------
+//
+TBool CXmlUtils::CleanupMarkupL(TDes& aBuffer, TInt aNewLineChar) const
+    {
+    TBool   modified = EFalse;
+
+    // Strip all markup.
+    modified |= StripMarkupL(aBuffer, aNewLineChar);
+    
+    // Resolve entities.
+    modified |= ResolveEntitiesL(aBuffer);
+
+    // Strip the CDATA markers.
+    modified |= StripCDataMarkers(aBuffer);
+
+    // Strip all markup created after entities were resolved.
+    modified |= StripMarkupL(aBuffer, aNewLineChar);
+    
+    // Strip leading and trailing whitespace.
+    aBuffer.Trim();
+
+    return modified;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::ResolveEntitiesL
+// 
+// Resolves any entities found in aOrig.
+// -----------------------------------------------------------------------------
+//
+TBool CXmlUtils::ResolveEntitiesL(TDes& aBuffer) const
+	{
+    const TUint  KEntityStart =	'&';
+    const TUint  KEntityEnd = ';';
+
+	TInt         entityStart = -1;
+    TInt         currentPos =  0;    
+    TBool        entityFound = EFalse;
+	TBool        textHasChanged = EFalse;
+
+    while (currentPos < aBuffer.Length())
+        {
+        // If this is the beginning of an entity...
+        if (aBuffer[currentPos] == KEntityStart) 
+            {
+            entityStart = currentPos;
+            entityFound = ETrue;
+            currentPos++;
+            }
+
+        // If this is the end of an entity...
+        else if ((aBuffer[currentPos] == KEntityEnd) && entityFound)
+            {
+            TUint16    ucs2Value;
+            TInt       entityLength = currentPos - entityStart + 1;
+            TPtrC      entityStr(aBuffer.Mid(entityStart + 1, entityLength - 2));
+            TBuf16<1>  entityCode;
+            
+            // Resolve the entity inline.
+            iXmlEntity->ResolveL(entityStr, ucs2Value);            
+            entityCode.SetLength(1);
+            entityCode[0] = ucs2Value;
+
+            aBuffer.Replace(entityStart, entityLength, entityCode);
+
+            currentPos = entityStart;
+			textHasChanged = ETrue;
+            entityStart = -1;
+            entityFound = EFalse;
+            }
+
+        // Otherwise just move on.
+        else
+            {
+            currentPos++;
+            }
+        }
+
+	return textHasChanged;
+	}
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::StripMarkup
+// 
+// Remove any markup found in the given descriptor.
+// -----------------------------------------------------------------------------
+//
+TBool CXmlUtils::StripMarkupL(TDes& aBuffer, TInt aNewLineChar) const
+    {
+	TInt    tagStart = -1;
+    TInt    currentPos = 0;    
+    TBool   tagStartFound = EFalse;
+	TBool   textHasChanged = EFalse;
+
+    // TODO: 1) Remove xml comments as well.
+
+    while (currentPos < aBuffer.Length())
+        {
+        // It's at the beginning of a tag.
+        if (aBuffer.Mid(currentPos, 1).Compare(_L("<")) == 0)
+            {
+            tagStartFound = ETrue;
+            tagStart = currentPos;
+            currentPos++;
+            }
+
+        // It's at the end of a tag.
+        else if ((aBuffer.Mid(currentPos,1).Compare(_L(">")) == 0) && 
+                tagStartFound)
+            {
+			TBuf<6>  tag;
+			TInt     tagLength;
+
+            // Extract enough of the tag to determine if it needs to insert a
+            // newline.
+            tagLength = Min(6, currentPos - tagStart + 1);
+			tag.Copy(aBuffer.Mid(tagStart, tagLength));
+			tag.LowerCase();
+
+            // If it found a <p> or <br> tag insert a newline.
+            // TODO: This fails if the tag includes attributes (i.e. style).
+			if ((aNewLineChar != 0) && 
+			        ((tag.Compare(_L("<br>")) == 0) ||
+                    (tag.Compare(_L("<br/>")) == 0) ||
+                    (tag.Compare(_L("<br />")) == 0) ||
+                    (tag.Compare(_L("<p>")) == 0)))
+				{
+                // Delete the tag.
+				aBuffer.Delete(tagStart, currentPos - tagStart + 1);
+
+                // Insert the newline.
+				TBuf<1> chr;
+				_LIT(KFormat,"%c");
+
+				chr.Format(KFormat, aNewLineChar);
+				aBuffer.Insert(tagStart, chr);
+
+				currentPos = tagStart + 1;
+				textHasChanged = ETrue;
+				}
+
+            // Otherwise just delete the tag.
+			else
+				{
+				aBuffer.Delete(tagStart, currentPos - tagStart + 1);
+				currentPos = tagStart;
+				textHasChanged = ETrue;
+				}
+
+            tagStart = -1;
+			tagStartFound = EFalse;
+            }
+
+        // Otherwise just skip to the next char.
+        else
+            {
+            currentPos++;
+            }
+        }
+
+    if (textHasChanged)
+        {
+        aBuffer.TrimAll();
+        }
+
+	return textHasChanged;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::StripCDataMarkers
+// 
+// Remove any CDATA markers in the given descriptor.
+// -----------------------------------------------------------------------------
+//
+TBool CXmlUtils::StripCDataMarkers(TDes& aBuffer) const
+    {
+    _LIT(KMarkerBegin, "<![CDATA[");
+    _LIT(KMarkerEnd, "]]>");
+
+    const TInt KMarkerBeginLen = KMarkerBegin().Length();
+    const TInt KMarkerEndLen = KMarkerEnd().Length();
+    
+    TInt    currentPos = 0;
+    TBool   markerStartFound = EFalse;
+	TBool   textHasChanged = EFalse;
+
+    while (currentPos < aBuffer.Length())
+        {
+        // Handle the the beginning of a marker.                
+        if ((currentPos <= (aBuffer.Length() - KMarkerBeginLen)) && 
+                aBuffer.Mid(currentPos, KMarkerBeginLen).Compare(KMarkerBegin) == 0)
+            {
+            markerStartFound = ETrue;
+            aBuffer.Delete(currentPos, KMarkerBeginLen);
+            textHasChanged = ETrue;
+            }
+
+        // Handle the the end of a marker.                
+        else if (markerStartFound && (currentPos <= (aBuffer.Length() - KMarkerEndLen)) && 
+                aBuffer.Mid(currentPos, KMarkerEndLen).Compare(KMarkerEnd) == 0)
+            {
+            markerStartFound = EFalse;
+            aBuffer.Delete(currentPos, KMarkerEndLen);
+            textHasChanged = ETrue;
+            }
+
+        // Otherwise just skip to the next char.
+        else
+            {
+            currentPos++;
+            }
+        }
+
+    if (textHasChanged)
+        {
+        aBuffer.TrimAll();
+        }
+
+	return textHasChanged;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::ExtractTextL
+//
+// Performs a deep extraction of the text children of the given node.  The result
+// is returned as a 16-bit descriptor.    The char encoding is always ucs2.
+// -----------------------------------------------------------------------------
+//
+HBufC* CXmlUtils::ExtractTextL(TXmlEngElement aElement) const
+    {
+    TInt     size;
+    HBufC*   ucs2Des = NULL;
+    RBuf8    rbuf;
+    
+    if (aElement.IsNull())
+        {
+        return NULL;
+        }
+        
+    // Perform the deep extraction.
+    size = aElement.InnerXmlL( rbuf );
+    rbuf.CleanupClosePushL();
+
+    // Resolve any remaining entities and strip any CDATA markers.
+    if (size > 0)
+        {
+        ucs2Des = HBufC::NewLC( rbuf.Length() );
+        TPtr  ptr(ucs2Des->Des());
+        //ptr.Copy( rbuf );
+        TInt unconverted = CnvUtfConverter::ConvertToUnicodeFromUtf8(ptr,rbuf);
+        while( unconverted>0)
+        {
+        	
+        	ucs2Des->ReAllocL(rbuf.Length()+unconverted);
+        	ptr.Set(ucs2Des->Des());
+        	unconverted = CnvUtfConverter::ConvertToUnicodeFromUtf8(ptr,rbuf);
+        }
+        
+        ResolveEntitiesL(ptr);
+        StripCDataMarkers(ptr);
+        ptr.TrimAll();
+
+        CleanupStack::Pop(ucs2Des);
+        }
+
+    CleanupStack::PopAndDestroy( /*rbuf*/ );
+
+    return ucs2Des;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::ExtractSimpleTextL
+//
+// Performs a deep extraction of the text children of the given node.  The result
+// is returned as a 16-bit descriptor.    The char encoding is always ucs2.  This
+// method is different from ExtractTextL in that it doesn't resolve entities or
+// do any other clean up.  It also has a length param.
+// -----------------------------------------------------------------------------
+//
+HBufC* CXmlUtils::ExtractSimpleTextL(TXmlEngElement aElement, TInt aMaxLength, 
+        TBool aFromEnd) const
+    {
+    xmlChar*  xmlStr;
+    HBufC*    ucs2Des = NULL;
+    RBuf8   rbuf;
+
+    if (aElement.IsNull())
+        {
+        return NULL;
+        }
+        
+    // Extract the text from the element.
+    aElement.WholeTextContentsCopyL( rbuf );
+    rbuf.CleanupClosePushL();
+    xmlStr = (xmlChar*) rbuf.Ptr();
+        
+    // If need be, trim the string.
+    if (aMaxLength > 0)
+        {
+        if (aFromEnd)
+            {
+            if (rbuf.Length() < aMaxLength)
+                {
+                xmlStr = xmlUTF8Strndup(xmlStr, aMaxLength);
+                }
+            else
+                {
+                xmlStr = xmlUTF8Strndup(xmlStr + rbuf.Length() - aMaxLength, aMaxLength);
+                }
+            }
+        else
+            {            
+            xmlStr = xmlUTF8Strndup(xmlStr, aMaxLength);
+            }
+        CleanupLibXml2::PushL(xmlStr);
+        }
+
+    // Convert the value into a TDesC
+    TXmlEngString  temp((char*) xmlStr);
+    
+    ucs2Des = temp.AllocL();
+    ucs2Des->Des().Trim();
+
+    if (aMaxLength > 0)
+        {
+        CleanupStack::PopAndDestroy(/*xmlStr*/);
+        }
+        
+    CleanupStack::PopAndDestroy( /*rbuf*/ );
+
+    return ucs2Des;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::GetDocumentFirstElement
+// 
+// Returns the first element in the given document.
+// -----------------------------------------------------------------------------
+//
+TXmlEngElement CXmlUtils::GetDocumentFirstElement(RXmlEngDocument aDocument) const
+    {
+    const TXmlEngElement  KNullElement;
+    TXmlEngElement        element;    
+
+    // Get the first child.
+    element = aDocument.DocumentElement();
+        
+    // Loop through the children looking for the first element.
+    while (element.NotNull())
+        {
+        if ((element.NodeType() == TXmlEngNode::EElement))
+            {
+            return element;
+            }
+            
+        element = element.NextSibling().AsElement();
+        }
+        
+    // Element not found, return a "null" node.
+    return KNullElement;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::GetFirstElementChild
+// 
+// Returns the first child of the given node.
+// -----------------------------------------------------------------------------
+//
+TXmlEngElement CXmlUtils::GetFirstElementChild(TXmlEngElement aElement) const
+    {
+    const TXmlEngElement      KNullElement;
+    RXmlEngNodeList<TXmlEngElement> children;
+    TXmlEngElement            child;
+        
+    // Get the first element -- children only contains TElements...
+    aElement.GetChildElements(children);
+    if (children.HasNext())
+        {
+        return children.Next();
+        }
+            
+    return KNullElement;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::GetFirstNamedChild
+// 
+// Returns the first child of the given node with the provided name.  aUtf8Name
+// MUST be null terminated.
+// -----------------------------------------------------------------------------
+//
+TXmlEngElement CXmlUtils::GetFirstNamedChild(TXmlEngElement aElement, const TDesC8& aUtf8Name) const
+    {
+    const TXmlEngElement      KNullElement;
+    RXmlEngNodeList<TXmlEngElement> children;
+    TXmlEngElement            child;
+    
+    // Get the first element -- children only contains TElements...
+    aElement.GetChildElements(children);
+    while (children.HasNext())
+        {
+        child = children.Next();
+        
+        if (IsNamed(child, aUtf8Name))
+            {
+            return child;
+            }
+        }
+            
+    return KNullElement;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::GetNextSiblingElement
+// 
+// Returns the next sibling of the given node.
+// -----------------------------------------------------------------------------
+//
+TXmlEngElement CXmlUtils::GetNextSiblingElement(TXmlEngElement aElement)
+    {
+    const TXmlEngElement  KNullElement;
+    TXmlEngElement        node;
+    
+    if (aElement.NotNull())
+        {
+        node = aElement.NextSibling().AsElement();
+
+        // Loop through the siblings looking for the next element.
+        while (node.NotNull())
+            {
+            if ((node.NodeType() == TXmlEngNode::EElement))
+                {
+                return node;
+                }
+            
+            node = node.NextSibling().AsElement();
+            }
+        }
+        
+    return KNullElement;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::IsNamed
+// 
+// Returns true if the given node has a name of aName.  This method doesn't 
+// compare the namespace.
+// -----------------------------------------------------------------------------
+//
+TBool CXmlUtils::IsNamed(TXmlEngElement aElement, const TDesC8& aUtf8Name) const
+    {
+    return (xmlStrcasecmp((const xmlChar *) aElement.Name().Ptr(), 
+            (const xmlChar *) aUtf8Name.Ptr()) == 0);
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::IsNamed
+// 
+// Returns true if the given node has match the given namespace and name.
+// -----------------------------------------------------------------------------
+//
+TBool CXmlUtils::IsNamed(TXmlEngElement aElement, const TDesC8& aUtf8NameSpace, 
+        const TDesC8& aUtf8Name) const
+    {
+    TPtrC8  ns = aElement.Prefix();
+    
+    // First ensure the names match.
+    if (!IsNamed(aElement, aUtf8Name))
+        {
+        return EFalse;
+        }
+
+    // Return false if one of the namespaces is NULL and the other isn't.
+    if ((ns.Length() != 0 && (aUtf8NameSpace.Length() == 0)) ||
+            (ns.Length() == 0 && (aUtf8NameSpace.Length() > 0)))
+        {
+        return EFalse;
+        }
+        
+    // Return false if both are non-null, but don't match.
+    if (ns.Length() != 0 && (aUtf8NameSpace.Length() >= 0) &&
+            (xmlStrcasecmp((const xmlChar *) ns.Ptr(), 
+            (const xmlChar *) aUtf8NameSpace.Ptr()) != 0))
+        {
+        return EFalse;
+        }
+
+    // Its a match.
+    return ETrue;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CXmlUtils::AttributeL
+// 
+// Returns the attribute's value or NULL if the attribute wasn't present.
+// -----------------------------------------------------------------------------
+//
+HBufC* CXmlUtils::AttributeL(TXmlEngElement aElement, const TDesC8& aAttribute) const
+    {    
+    TXmlEngAttr     attr;
+    HBufC*          value = NULL;
+    RBuf8           rbuf;
+    
+    // Get the value.
+    attr = aElement.AttributeNodeL( aAttribute );    
+    if (attr.NotNull())
+        {
+        attr.WholeValueCopyL( rbuf );
+        rbuf.CleanupClosePushL();
+        value = HBufC::NewL( rbuf.Length() + 32 );
+        TPtr ptr(value->Des());
+        TInt unconverted = CnvUtfConverter::ConvertToUnicodeFromUtf8(ptr,rbuf);
+        CleanupStack::PopAndDestroy( /*rbuf*/ );
+        }
+    return value;
+    }
+
+