diff -r 000000000000 -r dab8a81a92de omads/omadsextensions/dsutils/emailxmlutils/src/NSmlEmailFileParser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omads/omadsextensions/dsutils/emailxmlutils/src/NSmlEmailFileParser.cpp Mon Nov 23 14:46:41 2009 +0200 @@ -0,0 +1,814 @@ +/* +* Copyright (c) 2004 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "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: Sources +* +*/ + + + +// INCLUDE FILES +#include +#include + +#include "nsmlxmlparser.h" + + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +EXPORT_C CNSmlEmailFileParser* CNSmlEmailFileParser::NewL() + { + CNSmlEmailFileParser* self = CNSmlEmailFileParser::NewLC(); + CleanupStack::Pop(); + + return self; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::NewLC +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +EXPORT_C CNSmlEmailFileParser* CNSmlEmailFileParser::NewLC() + { + CNSmlEmailFileParser* self = new (ELeave) CNSmlEmailFileParser(); + CleanupStack::PushL(self); + self->ConstructL(); + + return self; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::~CNSmlEmailFileParser +// Destructor. +// ----------------------------------------------------------------------------- +// +EXPORT_C CNSmlEmailFileParser::~CNSmlEmailFileParser() + { + // close rfs and open streams + if ( iWriteStream.Sink() ) + iWriteStream.Close(); + + iRfs.Close(); + delete iEmailItemFile; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::ParseXml +// Parses the email xml, that is located in the file whose name is given +// as aXml (path and filename) +// ----------------------------------------------------------------------------- +// +EXPORT_C TNSmlParserGeneratorError CNSmlEmailFileParser::ParseXml( HBufC8* aXml ) + { + if ( !aXml ) + return EInvalidFilename; + + HBufC* filename = Buf8ToBuf16( aXml ); + if ( !filename ) + { + return EOutOfMemory; + } + + // open the stream and parse + TNSmlParserGeneratorError err = EErrorNone; + RFileReadStream rs; + if ( rs.Open(iRfs, *filename, KFileReadAccess) != KErrNone ) + { + err = EInvalidFilename; + } + else + { + err = ParseXml( rs ); + rs.Close(); + } + + delete filename; + + return err; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::ParseXml +// Parses the xml from the given stream, and writes the emailitem - if present - to a temp file +// ----------------------------------------------------------------------------- +// +EXPORT_C TNSmlParserGeneratorError CNSmlEmailFileParser::ParseXml( RReadStream& aRs ) + { + iSetValues.Reset(); + iCurrentState = ENone; + iLastState = ENone; + + TRAPD(err, ParseFromStreamL( aRs )); + + if( err == EErrorNone && iCurrentState != ENone ) + return EInvalidXmlError; + + return CheckError(err); + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::GenerateXml +// Generates the xml and writes it to the file whose name is given in aXml (path +// and filename). +// ----------------------------------------------------------------------------- +// +EXPORT_C TNSmlParserGeneratorError CNSmlEmailFileParser::GenerateXml( HBufC8*& aXml ) + { + if ( !aXml ) + return EInvalidFilename; + + HBufC* filename = Buf8ToBuf16( aXml ); + if ( !filename ) + { + return EOutOfMemory; + } + + // open the stream and generate + iWriteStream.Replace(iRfs, *filename, KFileWriteAccess); + TNSmlParserGeneratorError err = GenerateXml( iWriteStream ); + iWriteStream.Close(); + + delete filename; + + return err; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::GenerateXml +// Generates the xml and writes it to the given stream. +// ----------------------------------------------------------------------------- +// +EXPORT_C TNSmlParserGeneratorError CNSmlEmailFileParser::GenerateXml( RWriteStream& aWs ) + { + TRAPD( err, GenerateEmailXmlL( aWs ) ); + + return CheckError(err); + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::GenerateEmailXmlL +// ----------------------------------------------------------------------------- +// +void CNSmlEmailFileParser::GenerateEmailXmlL( RWriteStream& aWs ) + { + ConvertIntoEntitiesL(); + + // count the size of the xml (does not include emailitem, since it is - if present - in a file) + TInt size = CNSmlEmailParser::CountXmlSizeL(); + if ( iUseEmailItem ) size += 20; // extra for emailitem tags + + // create a buffer for using the size + HBufC8* xml = HBufC8::NewLC(size); + TPtr8 ptr = xml->Des(); + + // append data to buffer + AppendElement(ptr, KEmailElement()); + + if ( iRead ) + { + AppendElement(ptr, KEmailReadElement(), BooleanToString( iRead )); + } + + if ( iForwarded ) + { + AppendElement(ptr, KEmailForwardedElement(), BooleanToString( iForwarded )); + } + + if ( iReplied ) + { + AppendElement(ptr, KEmailRepliedElement(), BooleanToString( iReplied )); + } + + if (iReceived != Time::NullTTime()) + { + AppendElement(ptr, KEmailReceivedElement(), DateTimeToStringL(iReceived)); + } + + if (iCreated != Time::NullTTime()) + { + AppendElement(ptr, KEmailCreatedElement(), DateTimeToStringL(iCreated)); + } + + if (iModified != Time::NullTTime()) + { + AppendElement(ptr, KEmailModifiedElement(), DateTimeToStringL(iModified)); + } + + if ( iDeleted ) + { + AppendElement(ptr, KEmailDeletedElement(), BooleanToString( iDeleted )); + } + + if ( iFlagged ) + { + AppendElement(ptr, KEmailFlaggedElement(), BooleanToString( iFlagged )); + } + + // if emailitem should be used in generation, write it to the stream + if ( iUseEmailItem ) + { + // append start tag and cdata start + AppendElement(ptr, KEmailItemElement()); + ptr.Append(KCDataStart); + + // open a stream to emailitem + RFileReadStream rs; + TInt err = rs.Open( iRfs, *iEmailItemFile, KFileReadAccess ); + if ( err != KErrNone ) + User::Leave( EInvalidFilename ); + + CleanupClosePushL(rs); + + // write the buffer to the stream + aWs.WriteL(ptr); + + // write emailitem from stream + aWs.WriteL(rs); + + // close the opened read stream + CleanupStack::Pop(); // rs + + // empty the buffer (i.e. set the length to zero) + ptr.SetLength(0); + + // append emailitem end tag and cdata inner end + ptr.Append(KCDataEnd); + AppendEndElement(ptr, KEmailItemElement()); + } + + // append the rest of the data to the buffer + if ( iTruncated ) + { + iTruncated->GenerateXmlL(ptr, this); + } + + if ( iExt ) + { + for (TInt i=0; i < iExt->Count(); ++i) + { + iExt->At(i)->GenerateXmlL(ptr, this); + } + + } + + AppendEndElement(ptr, KEmailElement()); + + // write the buffer to the stream and commit + aWs.WriteL(ptr); + aWs.CommitL(); + + CleanupStack::PopAndDestroy(); // xml + + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::CountXmlSizeL +// ----------------------------------------------------------------------------- +// +EXPORT_C TInt CNSmlEmailFileParser::CountXmlSizeL() + { + // counting the size is not essential, since xml is written to a file + return KErrNotSupported; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::HandleNextEmailItemDataL +// Next emailitem data +// ----------------------------------------------------------------------------- +// +void CNSmlEmailFileParser::HandleNextEmailItemDataL( TPtrC8 aData ) + { + // write the given piece of data into a file (stream) + iWriteStream.WriteL(aData); + } + + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::HandleNextEmailItemStartL +// Emailitem element starts +// ----------------------------------------------------------------------------- +// +void CNSmlEmailFileParser::HandleNextEmailItemStartL() + { + // open the write stream + User::LeaveIfError( iWriteStream.Replace( iRfs, *iEmailItemFile, KFileWriteAccess ) ); + iReadingEmailItemData = ETrue; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::HandleNextEmailItemEndL +// Emailitem element ends +// ----------------------------------------------------------------------------- +// +void CNSmlEmailFileParser::HandleNextEmailItemEndL() + { + // close the write stream + iWriteStream.Close(); + iReadingEmailItemData = EFalse; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::ParseFromStreamL +// ----------------------------------------------------------------------------- +// +void CNSmlEmailFileParser::ParseFromStreamL( RReadStream& aRs ) + { + + // reset preprocess variables and parser + ResetPreProcessor(); + ResetParserL(); + + // read and process the data piece by piece + HBufC8* piece = HBufC8::NewLC(KReadDataLength); + TPtr8 ptr = piece->Des(); + TInt dataSize = aRs.Source()->SizeL(); + + TInt pos = 0; + iFirstPiece = ETrue; + for( pos = KReadDataLength; pos <= dataSize ; pos += KReadDataLength ) + { + // read piece of data from the stream + aRs.ReadL(ptr, KReadDataLength); + + // check that there are no incomplete cdata/comment/entity at the end + dataSize += RemoveIncompleteL( piece, aRs ); + + // pre-process + PreProcessPieceL( piece ); + + // parsing + TPtrC8 temp( *piece ); + ParsePieceL( temp ); + + iFirstPiece = EFalse; + } + + TInt lastPartLength = dataSize - pos + KReadDataLength; + aRs.ReadL(ptr, lastPartLength); + + PreProcessPieceL( piece ); + + // parsing + TPtrC8 temp( *piece ); + ParsePieceL( temp, ETrue ); + + CleanupStack::PopAndDestroy(); // piece + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::ParsePieceL +// ----------------------------------------------------------------------------- +// +void CNSmlEmailFileParser::ParsePieceL( TPtrC8& aXml, TBool aLastPiece ) + { + + // check if last cdata has not ended yet + if ( iReadingCData ) + { + TInt endPos = aXml.Find(KCDataEnd); + TPtrC8 cdata = _L8(""); + + if ( endPos == KErrNotFound && aLastPiece ) + { + User::Leave(EInvalidCDataStructure); + } + else if ( endPos == KErrNotFound ) + { + // cdata end was not found, read the whole piece as cdata + NextDataL( aXml ); + return; + } + else + { + // add data until cdata end to buffer + cdata.Set( aXml.Left( endPos ) ); + aXml.Set( aXml.Right( aXml.Length() - endPos - KCDataEnd().Length() ) ); + iReadingCData = EFalse; + } + + AddToCompleteL(cdata); + NextDataL(*iCompleteBuffer); + ResetBufferL(iCompleteBuffer); + + } +#ifndef __NO_XML_COMMENTS_ + else if ( iReadingComment ) // check if last comment has not ended yet + { + TInt endPos = aXml.Find(KCommentEnd); + + if ( endPos == KErrNotFound && aLastPiece ) + { // this is the last piece of data and end of comment has not been found -> error + User::Leave(EInvalidXmlError); + } + else if ( endPos == KErrNotFound ) + { + // end of comment was not found, no need to parse this piece of data + return; + } + else + { + // end of cdata was found, jump to that position + aXml.Set( aXml.Right( aXml.Length() - endPos - KCommentEnd().Length() ) ); + iReadingComment = EFalse; + } + } +#endif + + // parse through the string + TText c; + + for( TInt i=0; i < aXml.Length(); ++i ) + { + c = aXml[i]; + switch(c) + { + case KElementStart: + // if currently reading element, error + if( iReadingElementName ) + { + User::Leave(EInvalidXmlError); + } + + if( aXml.Length()-i >= KCDataStart().Length() && + !aXml.Mid(i, KCDataStart().Length()).Compare(KCDataStart()) ) + { // cdata + aXml.Set( aXml.Right( aXml.Length() - i ) ); + TInt endPos = aXml.Find(KCDataEnd); + + TPtrC8 cdata = _L8(""); + if ( endPos == KErrNotFound && aLastPiece ) + { + User::Leave(EInvalidCDataStructure); + } + else if ( endPos == KErrNotFound ) + { + // cdata end was not found, read all the rest as cdata + cdata.Set( aXml.Right( aXml.Length() - KCDataStart().Length() ) ); + i = aXml.Length(); // move to the end + iReadingCData = ETrue; + } + else + { + cdata.Set( aXml.Mid( KCDataStart().Length(), endPos - KCDataStart().Length() ) ); + aXml.Set( aXml.Right( aXml.Length() - endPos - KCDataEnd().Length() ) ); + + i = -1; + } + + // add current buffer to complete buffer + EntitiesToCharactersL( iBuffer, 0, iBuffer->Length() ); + AddToCompleteL(*iBuffer); + ResetBufferL(iBuffer); + AddToCompleteL(cdata); + } +#ifndef __NO_XML_COMMENTS_ + else if( aXml.Length()-i >= KCommentStart().Length() && + !aXml.Mid(i, KCommentStart().Length()).Compare(KCommentStart()) ) + { // comment + aXml.Set( aXml.Right( aXml.Length() - i ) ); + TInt endPos = aXml.Find(KCommentEnd); + + if ( endPos == KErrNotFound && aLastPiece) + { + // this is the last piece of data, and end of comment was not found -> error + User::Leave(EInvalidXmlError); + } + else if ( endPos == KErrNotFound ) + { + // the end was not found, no need to parse the rest since it is comment + iReadingComment = ETrue; + return; + } + else + { + // the end of comment was found, move to the end of comment and start parsing + // from there on + aXml.Set( aXml.Right( aXml.Length() - endPos - KCommentEnd().Length() ) ); + } + + i = -1; + } +#endif + else + { + // send the buffer + EntitiesToCharactersL( iBuffer, 0, iBuffer->Length() ); + AddToCompleteL(*iBuffer); + NextDataL(*iCompleteBuffer); + ResetBufferL(iBuffer); + ResetBufferL(iCompleteBuffer); + iReadingElementName = ETrue; + } + + break; + case KElementEnd: + // stop reading element name + if( !iReadingElementName ) + { + User::Leave(EInvalidXmlError); + } + else + { + NextElementL(*iBuffer); + ResetBufferL(iBuffer); + iReadingElementName = EFalse; + } + break; + default: + // add char to buffer + AddToBufferL(c, iBuffer); + break; + } + } + + // send the data in the buffer + if ( ( iReadingEmailItemData || aLastPiece ) && !iReadingElementName ) + { + EntitiesToCharactersL( iBuffer, 0, iBuffer->Length() ); + AddToCompleteL(*iBuffer); + NextDataL(*iCompleteBuffer); + ResetBufferL(iBuffer); + ResetBufferL(iCompleteBuffer); + } + + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::RemoveIncompleteL +// Checks if there is incomplete cdata/comment/entity at the end of the given +// piece. If one is found, it is removed from aPiece and the read mark of the +// stream is moved backwards the length of the removed item (i.e. the incomplete +// item will be read to the next piece as a whole). +// ----------------------------------------------------------------------------- +// +TUint CNSmlEmailFileParser::RemoveIncompleteL( HBufC8* aPiece, RReadStream& aStream ) + { + // we need to check only the last nine characters + TUint removeSize = CheckPiece( aPiece->Right( 9 ) ); + + if ( removeSize ) + { + // remove the incomplete item from aPiece + TPtr8 ptr = aPiece->Des(); + ptr.Delete( aPiece->Length() - removeSize, removeSize ); + + // move backwards in reading the stream + aStream.Source()->SeekL( MStreamBuf::ERead, aStream.Source()->TellL(MStreamBuf::ERead) - removeSize ); + } + + // no need to loop the rest of aPiece. If xml is not correctly formed, + // an error occures later in the parser + + return removeSize; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::CheckPiece +// ----------------------------------------------------------------------------- +// +TUint CNSmlEmailFileParser::CheckPiece( const TPtrC8 aEnd ) const + { + // check that there are no incomplete cdata/comment/entity at the end + // (cdata start, cdata end, cdata inner end, comment start, comment end, entities) + // ( , , &xxxx; , ]]]]>> , ]]]]>> ) + // do not change the order + + // possible inner cdata end + TInt pos = aEnd.Find( _L8("]]]") ); + if ( pos != KErrNotFound ) + { + return aEnd.Length() - pos; + } + + // beginning part of comment or cdata + pos = aEnd.Find( _L8("Des(); + + // the string used for searching and moving in the string + TPtrC8 searchString(*aXml); + + // the current position in the original string + TInt searchStartPos = 0; + + // if start has been found and end has not, try to find end first + if ( iCdataEndPos == KErrNotFound && iCdataStartPos != KErrNotFound && iCdataFoundFromFirstPiece ) + { + // we're looking for a cdata end + iCdataEndPos = searchString.Find(KCDataEnd); + + // if the end was found, remove it and start looking for a cdata start + if ( iCdataEndPos != KErrNotFound ) + { + // remove cdata end + xml.Delete(iCdataEndPos, KCDataEnd().Length()); + searchStartPos = iCdataEndPos; + searchString.Set( xml.Right(xml.Length() - searchStartPos) ); + } + else + { + // end was not found, return + return; + } + } + + iCdataStartPos = searchString.Find(KCDataStart); + + // If CDATA is not found from beginning then data is not inside CDATA and then + // preprocessing is not needed + if ( iCdataStartPos != 0 && iFirstPiece ) + { + return; + } + else if ( !iFirstPiece && !iCdataFoundFromFirstPiece ) + { + return; + } + else if ( iCdataStartPos == 0 && iFirstPiece ) + { + iCdataFoundFromFirstPiece = ETrue; + } + + + // while cdata is found + while ( iCdataStartPos != KErrNotFound ) + { + iCdataStartPos += searchStartPos; + + // find an end of cdata before entities are converted + iCdataEndPos = searchString.Find(KCDataEnd); + + // convert entities between search start and cdata start + TInt entityChange = EntitiesToCharactersL(aXml, searchStartPos, iCdataStartPos); + xml.Set(aXml->Des()); + iCdataStartPos += entityChange; + + if ( iCdataEndPos != KErrNotFound ) + { + iCdataEndPos += entityChange; + iCdataEndPos += searchStartPos; + + // if the end is before start -> error + if ( iCdataEndPos < iCdataStartPos ) + User::Leave( EInvalidCDataStructure ); + + // remove cdata end + xml.Delete(iCdataEndPos, KCDataEnd().Length()); + // remove cdata start + xml.Delete(iCdataStartPos, KCDataStart().Length()); + + searchStartPos = iCdataEndPos - KCDataStart().Length(); + searchString.Set( xml.Right(xml.Length() - searchStartPos) ); + } + else + { + // remove cdata start + xml.Delete(iCdataStartPos, KCDataStart().Length()); + + // since there was no end found, return + return; + } + + // find a new cdata start that is after the found end + iCdataStartPos = searchString.Find(KCDataStart); + + } + + if ( iCdataStartPos == KErrNotFound ) + { + EntitiesToCharactersL(aXml, searchStartPos, aXml->Length()); + } + + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::ResetParserL +// Resets the state of the parser, should be called before starting the parsing. +// ----------------------------------------------------------------------------- +// +void CNSmlEmailFileParser::ResetParserL() + { + ResetBufferL(iBuffer); + ResetBufferL(iCompleteBuffer); + iReadingElementName = EFalse; + iReadingCData = EFalse; + iReadingEmailItemData = EFalse; + iFirstPiece = EFalse; + iCdataFoundFromFirstPiece = EFalse; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::ResetPreProcessor +// Resets the variables used in preprocessor. +// ----------------------------------------------------------------------------- +// +void CNSmlEmailFileParser::ResetPreProcessor() + { + iCdataStartPos = KErrNotFound; + iCdataEndPos = KErrNotFound; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::Buf8ToBuf16 +// ----------------------------------------------------------------------------- +// +HBufC* CNSmlEmailFileParser::Buf8ToBuf16( const HBufC8* buf8 ) const + { + HBufC* buf16 = HBufC::New(buf8->Length()); + if ( buf16 ) + { + TPtr ptr = buf16->Des(); + ptr.Copy( *buf8 ); + } + + return buf16; + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::CNSmlEmailFileParser +// Constructor. +// ----------------------------------------------------------------------------- +// +CNSmlEmailFileParser::CNSmlEmailFileParser() + : iUseEmailItem(ETrue) + { + } + +// ----------------------------------------------------------------------------- +// CNSmlEmailFileParser::ConstructL +// Second phase construction. +// ----------------------------------------------------------------------------- +// +void CNSmlEmailFileParser::ConstructL() + { + CNSmlEmailParser::ConstructL(); + User::LeaveIfError( iRfs.Connect() ); + + // create path+filename for emailitem + iEmailItemFile = HBufC::NewL( KMaxEmailItemFileNameLength ); + TPtr file = iEmailItemFile->Des(); + + file.Append( KEmailItemFile ); + + } + +// End of File