diff -r 000000000000 -r b16258d2340f applayerprotocols/wappushsupport/WbxmlLib/WbConverter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/applayerprotocols/wappushsupport/WbxmlLib/WbConverter.cpp Tue Feb 02 01:09:52 2010 +0200 @@ -0,0 +1,1418 @@ +// Copyright (c) 1997-2009 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: +// + +#include + +// #define __OUTPUT_WMLC // Debug Stuff - uncomment to get wmlc output. + +#include +#include "WbToken.h" +#include +#include "WbEnum.h" +#include "ByteList.h" + +#include + +// Wap specific error codes defined here +#include + +// +// Library routines +// + +#ifndef EKA2 +GLDEF_C TInt E32Dll(TDllReason /*aReason*/) +{ + return (KErrNone); +} +#endif + +GLDEF_C void Panic (TWbxmlPanic aPanic) +{ + _LIT(KWbPanic,"WbxmlConverter"); + User::Panic(KWbPanic ,aPanic); +} + +// +// class CWbxmlConverter - external API +// + +const TInt KOutputBufferLength=1024; + +CWbxmlConverter::CWbxmlConverter(RFs& aFs) +: iFs(aFs) +{ + __DECLARE_NAME(_S("CWbxmlConverter")); +} + +EXPORT_C CWbxmlConverter* CWbxmlConverter::NewL(RFs& aFs, MWbxmlConverterObserver* aObserver) +{ + CWbxmlConverter* instance = NewLC(aFs, aObserver); + CleanupStack::Pop(); + return instance; +} + +EXPORT_C CWbxmlConverter* CWbxmlConverter::NewLC(RFs& aFs, MWbxmlConverterObserver* aObserver) +{ + CWbxmlConverter* instance = new (ELeave) CWbxmlConverter(aFs); + CleanupStack::PushL(instance); + instance->ConstructL(aObserver, NULL); + return instance; +} + +EXPORT_C CWbxmlConverter* CWbxmlConverter::NewL(RFs& aFs, MWbxmlConverterObserver* aObserver, const TDesC* aCharacterEncoding) +{ + CWbxmlConverter* instance = new (ELeave) CWbxmlConverter(aFs); + CleanupStack::PushL(instance); + instance->ConstructL(aObserver, aCharacterEncoding); + CleanupStack::Pop(); + return instance; +} + +CWbxmlConverter::~CWbxmlConverter() +{ + delete[] iStringTable; + delete iPublicIdStr; + delete iByteList; + delete iCharsetConverter; + delete iOutputBuffer; + + if( iTagStack ) + { + iTagStack->Reset(); + delete iTagStack; + } + + if (iTagArray) + { + if (iTagArray->Count()) + iTagArray->ResetAndDestroy(); + delete iTagArray; + } + if (iAttArray) + { + if (iAttArray->Count()) + iAttArray->ResetAndDestroy(); + delete iAttArray; + } +#ifdef __OUTPUT_WMLC + iWmlcFile.Close(); + iWmlcFs.Close(); +#endif +} + +void CWbxmlConverter::ConstructL(MWbxmlConverterObserver* aObserver, const TDesC* aCharacterEncoding) +{ + iObserver = aObserver; + iState = EConvertVersion; + iByteList = CByteList::NewL(); + iParsing = EParseTag; + iOpenQuote = EFalse; + + iTagStack = new CArrayFixFlat(7); + + iOutputBuffer = HBufC8::NewL(KOutputBufferLength); + + iCharsetConverter = CCnvCharacterSetConverter::NewL(); + if( aCharacterEncoding != NULL ) + { + HBufC8* charset = HBufC8::NewLC(aCharacterEncoding->Length()); + charset->Des().Copy(*aCharacterEncoding); + iWbxmlCharset = iCharsetConverter->ConvertStandardNameOfCharacterSetToIdentifierL(*charset, iFs); + CleanupStack::PopAndDestroy(); + } +#ifdef __OUTPUT_WMLC + _LIT(KWmlcFileName, "c:\\WmlcFile.wmlc"); + User::LeaveIfError(iWmlcFs.Connect()); + TInt error = iWmlcFile.Replace(iWmlcFs, KWmlcFileName, EFileWrite); + if( error != KErrNone ) + User::Leave(EWapErrAccessViolation); +#endif +} + +EXPORT_C void CWbxmlConverter::ProcessDataL(HBufC8& aBuffer) +{ +#ifdef __OUTPUT_WMLC + iWmlcFile.Write(aBuffer); +#endif + iByteList->AddToListL(aBuffer); + HandleDataL(); +} + +EXPORT_C void CWbxmlConverter::CWbxmlConverter_Reserved1() +{ + // Must exist for Binary Compatibility +} + +EXPORT_C void CWbxmlConverter::CommitL() +{ + HandleDataL(); + + // Document should be finished + if (iState != EConvertFinished) + User::Leave(EWapErrDocumentCorrupted); +} + + +EXPORT_C void MWbxmlConverterObserver::MWbxmlConverterObserver_Reserved1() +{ + // Must exist for Binary Compatibility +} + + +void CWbxmlConverter::SetNextStateL(TInt aResult, TConvertState aState) +{ + switch (aResult) + { + case KErrNone: + iState = aState; + break; + case KErrNotFound: + iByteList->RollBack(); + break; + default: + User::Leave(EWapErrDocumentCorrupted); + } + iByteList->Commit(); +} + +TInt CWbxmlConverter::ConvertWbxmlVersionL() +{ + if (iByteList->Byte(&iWbxmlVersion,TRUE) != KErrNone) + return KErrNotFound; + + BufferAndOutputL(KWbXmlVersion10); + + return KErrNone; +} + +TInt CWbxmlConverter::ConvertPublicIdValueL() +{ + TUint8 pubIDFirstByte = 0; + if( iByteList->Byte(&pubIDFirstByte,EFalse) != KErrNone ) + return KErrNotFound; + + if( pubIDFirstByte == 0 ) + { + // The public id is in string table, index follows + iByteList->Inc(1); + if( ExtractMultiByte(&iPublicIdIndex,0) != KErrNone ) + return KErrNotFound; + iPublicId = 0; + } + else + { + // "Normal" publicId, a multibyte + if (ExtractMultiByte(&iPublicId,0) != KErrNone) + return KErrNotFound; + } + + return KErrNone; +} + +TInt CWbxmlConverter::ConvertCharsetL() + { + TUint32 charsetMIB = 0; + // The chrset part of bytecode document was only introduced + // in WML 1.1. + if (iWbxmlVersion >= (TInt8) 0x01) + { + if (ExtractMultiByte(&charsetMIB,0) != KErrNone) + return KErrNotFound; + } + + // if the charset wasn't already provided by HTTP, we set it according + // to the bytecode content charset + if( !iWbxmlCharset ) + { + if( charsetMIB != 0 ) + { + iWbxmlCharset = iCharsetConverter->ConvertMibEnumOfCharacterSetToIdentifierL(charsetMIB, iFs); + } + + if( iWbxmlCharset == 0 ) + { + // Patch in the most common results + switch(charsetMIB) + { + case 4: + iWbxmlCharset = KCharacterSetIdentifierIso88591; + break; + case 5: + iWbxmlCharset = KCharacterSetIdentifierIso88592; + break; + case 7: + iWbxmlCharset = KCharacterSetIdentifierIso88594; + break; + case 8: + iWbxmlCharset = KCharacterSetIdentifierIso88595; + break; + case 10: + iWbxmlCharset = KCharacterSetIdentifierIso88597; + break; + case 12: + iWbxmlCharset = KCharacterSetIdentifierIso88599; + break; + case 103: // UTF7 + iWbxmlCharset = KCharacterSetIdentifierUtf7; + break; + case 104: + iWbxmlCharset = KCharacterSetIdentifierImapUtf7; + break; + case 0: + // Default case + case 106: // UTF8 + // If the charset from bytecode was 'unknown' (0) or the converter couldn't + // match the mib number to known charset identifiers we use + // the default charset: UTF-8 + iWbxmlCharset = KCharacterSetIdentifierUtf8; + break; + default: + return KErrNotSupported; + } + } + // Now we need to inform our observer what charset we are using + HBufC8* charSetName = iCharsetConverter->ConvertCharacterSetIdentifierToStandardNameL(iWbxmlCharset, iFs); + CleanupStack::PushL(charSetName); + iObserver->HandleWbxmlEncodingUpdateL(charSetName); + CleanupStack::PopAndDestroy(); // charSetName + } + return KErrNone; + } + +TInt CWbxmlConverter::ConvertStringTableL() +{ + if (iStringTable == NULL) + { + // Need "static" instance variable for streaming iStringTable! + if( ExtractMultiByte(&iContinue,0) != KErrNone ) + return KErrNotFound; + + if (iContinue == 0) + return KErrNone; + + iStringTable = HBufC8::NewL(iContinue); + } + + if (iContinue != 0) + { + // Note: String table is 8bit data + TUint8 byte = 0; + while (iContinue) + { + if ((iByteList->Byte(&byte,TRUE)) != KErrNone) + { + iByteList->Commit(); + return KErrNotFound; + } + + iStringTable->Des().Append(byte); + iContinue--; + } + } + // Ensure that string table is zero terminated + TInt length = iStringTable->Length(); + if( iStringTable->Des()[length-1] != 0 ) + { + // Make table one character bigger, and make sure last character is zero terminator + iStringTable = iStringTable->ReAllocL(length + 1); + iStringTable->Des().Append(0); + } + return KErrNone; +} + +TInt CWbxmlConverter::ConvertPublicIdNameL() +{ + HBufC* fileName = NULL; + + switch (iPublicId) + { + case KWbxmlIdIndex: + // Meaning: Followed by string table index for public id + User::Leave(EWapErrUnknownDocument); + break; + case KWbxmlIdUnknown: + // Meaning: unknown or missing + // WML spec says that unknown DTD is handled as well as possible + // so we presume it's supposed to be WML 1.1 + fileName = FindTokenFileL(KWbTokenDatabaseWml11); + CleanupStack::PushL(fileName); + OpenTokenFileL(*fileName); + CleanupStack::PopAndDestroy(); // fileName + + // Waste the publicID string received from the token file + delete iPublicIdStr; + // ...and replace it with the one from the string table + { + TPtrC8 remainderFromIndex = iStringTable->Des().Mid(iPublicIdIndex); + TInt idLen = remainderFromIndex.Locate('\0'); + // We trust that the string table is properly constructed and + // contains the terminating null... + if( idLen == KErrNotFound ) + return KErrNotFound; // Ah well, Our trust was misplaced! + TPtrC8 publicIdString = remainderFromIndex.Left(idLen); + HBufC16* convertedBuffer = CharsetConvertDataL(publicIdString); + CleanupStack::PushL(convertedBuffer); + if( convertedBuffer == NULL ) + { + // Conversion failed!!! + CleanupStack::PopAndDestroy(); // convertedBuffer + return KErrCorrupt; + } + iPublicIdStr = HBufC8::NewL(convertedBuffer->Length()); + iPublicIdStr->Des().Copy(*convertedBuffer); + CleanupStack::PopAndDestroy(); // convertedBuffer + } + break; + case KWbxmlIdWta10: + // Meaning: "-//WAPFORUM//DTD WTA 1.0//EN" (WTA Event 1.0) + // WTA is not supported + User::Leave(EWapErrUnknownDocument); + break; + case KWbxmlIdWml11: + // Meaning: "-//WAPFORUM//DTD WML 1.1//EN" (WML 1.1) + fileName = FindTokenFileL(KWbTokenDatabaseWml11); + CleanupStack::PushL(fileName); + OpenTokenFileL(*fileName); + CleanupStack::PopAndDestroy(); // fileName + break; + case KWbxmlIdWml12: + // Meaning: "-//WAPFORUM//DTD WML 1.1//EN" (WML 1.2) + fileName = FindTokenFileL(KWbTokenDatabaseWml12); + CleanupStack::PushL(fileName); + OpenTokenFileL(*fileName); + CleanupStack::PopAndDestroy(); // fileName + break; + case KWbxmlIdSI: + // Meaning: "-//WAPFORUM//DTD SI 1.0//EN" (Push Service Indication 1.0) + fileName = FindTokenFileL(KWbTokenDatabaseSI10); + CleanupStack::PushL(fileName); + OpenTokenFileL(*fileName); + CleanupStack::PopAndDestroy(); // fileName + break; + case KWbxmlIdSL: + // Meaning: "-//WAPFORUM//DTD SL 1.0//EN" (Push Service Load 1.0) + fileName = FindTokenFileL(KWbTokenDatabaseSL10); + CleanupStack::PushL(fileName); + OpenTokenFileL(*fileName); + CleanupStack::PopAndDestroy(); // fileName + break; + default: + // Meaning: 0x05 - 0x7F recerved + // WML spec says that unknown DTD is handled as well as possible + // so we presume it's supposed to be WML 1.1 + fileName = FindTokenFileL(KWbTokenDatabaseWml11); + CleanupStack::PushL(fileName); + OpenTokenFileL(*fileName); + CleanupStack::PopAndDestroy(); // fileName + break; + } + + switch (iPublicId) + { + case KWbxmlIdWml11: + BufferAndOutputL(KWbPublicIdStartWml); + BufferAndOutputL(*iPublicIdStr); // ...you get HBufC8& + BufferAndOutputL(KWbPublicIdEnd11); + break; + case KWbxmlIdSI: + BufferAndOutputL(KWbPublicIdStartSi); + BufferAndOutputL(*iPublicIdStr); // ...you get HBufC8& + BufferAndOutputL(KWbPublicIdEndSI10); + break; + case KWbxmlIdSL: + BufferAndOutputL(KWbPublicIdStartSl); + BufferAndOutputL(*iPublicIdStr); // ...you get HBufC8& + BufferAndOutputL(KWbPublicIdEndSL10); + break; + case KWbxmlIdWml12: + default: + BufferAndOutputL(KWbPublicIdStartWml); + BufferAndOutputL(*iPublicIdStr); // ...you get HBufC8& + BufferAndOutputL(KWbPublicIdEnd12); + break; + } + + return KErrNone; +} + + +void CWbxmlConverter::HandleDataL() +{ + TInt result = KErrNone; + + do + { + switch (iState) + { + case EConvertVersion: + result = ConvertWbxmlVersionL(); + SetNextStateL(result,EConvertPublicIdValue); + break; + case EConvertPublicIdValue: + result = ConvertPublicIdValueL(); + SetNextStateL(result,EConvertCharset); + break; + case EConvertCharset: + result = ConvertCharsetL(); + SetNextStateL(result,EConvertStringTable); + break; + case EConvertStringTable: + result = ConvertStringTableL(); + SetNextStateL(result,EConvertPublicIdName); + break; + case EConvertPublicIdName: + result = ConvertPublicIdNameL(); + SetNextStateL(result,EConvertData); + break; + case EConvertData: + result = ConvertDataL(); + SetNextStateL(result,EConvertFinished); + break; + case EConvertFinished: + if(iOutputBuffer!=NULL && iOutputBuffer->Length()) + { + // Buffer still has some data : Lose ownership to the observer + TInt returnValue = iObserver->HandleWbxmlOutputL(iOutputBuffer); + if( returnValue != KErrNone ) + User::Leave(returnValue); + iOutputBuffer = NULL; + } + return; + default: + iState = EConvertError; + return; + } + } + while (result == KErrNone && iState != EConvertFinished); +} + +TInt CWbxmlConverter::ConvertDataL() +{ + // Continue streaming in the middle of string + if (iContinue != 0) + { + switch (iContinue) + { + case EGotoStreaming: + if (ConvertDataStreamingL() != KErrNone) + return KErrNotFound; + break; + case EGotoString: + if (ConvertDataStringL() != KErrNone) + return KErrNotFound; + break; + case EGotoOpaque: + if (ConvertOpaqueDataL() != KErrNone) + return KErrNotFound; + break; + } + iContinue = 0; + } + + TUint32 multiByte = 0; + TUint8 byte = 0; + + FOREVER + { + byte = (TUint8) -1; + if (iByteList->Byte(&byte,TRUE) != KErrNone) + { + // HACK: At the end of all data inc(1) fails and we get KErrNotFound + if (iTagIndex != 1 || byte != 1) + { + // it was real error + return KErrNotFound; + } + + } + + switch (byte) + { + case 0x00: // SWITCH_PAGE + // NOTE: not supported, not used in WML 1.1 + // bytecode SWITCH_PAGE not implemented + iByteList->Commit(); + break; + + case 0x01: // END + if (iParsing == EParseTag || iParsing == EParseNot) + { + if (iParsing == EParseNot) + { + iParsing = EParseTag; + } + + if (iTagIndex) + { + iTagIndex--; + TUint16 stackEntry = iTagStack->At(iTagIndex); + if( stackEntry != KWbxmlSkipLiteralEndTagMagicToken ) + { + BufferAndOutputL(KWbTagStartClose); + BufferAndOutputL(*(iTagArray->At(stackEntry&~0xc0)->iBuf)); + BufferAndOutputL(KWbTagEndWithContent); + } + iTagStack->Delete(iTagIndex); + + // Done, get out + if (iTagIndex == 0) + { + iByteList->Commit(); + return KErrNone; + } + } + else + { + // Corrupted bytecode + User::Leave(EWapErrDocumentCorrupted); + } + } + else if (iParsing == EParseAttrs) + { + iParsing = EParseTag; + + if( !iRestrictedOutput ) + { + if (iOpenQuote) + { + BufferAndOutputL(KWbQuote); + iOpenQuote = EFalse; + } + BufferAndOutputL(hasContent?KWbTagEndWithContent():KWbTagEndWithoutContent()); + } + else + iRestrictedOutput = EFalse; + } + else + { + // default case to catch impossible errors + // Corrupted bytecode + User::Leave(EWapErrDocumentCorrupted); + } + iByteList->Commit(); + + // This will be true when a message is defined with attributes + // but no data + if (iTagIndex == 0) + { + return KErrNone; + } + break; + + case 0x02: // ENTITY + if (ExtractMultiByte(&multiByte,0) != KErrNone) + return KErrNotFound; + OutputL(multiByte); + iByteList->Commit(); + break; + + case 0x40: // EXT_I_0 + case 0x41: // EXT_I_1 + case 0x42: // EXT_I_2 + iVariable = byte; + BufferAndOutputL(KWbVariableStart); + if (ConvertDataStreamingL() != KErrNone) + { + iContinue = EGotoStreaming; + return KErrNotFound; + } + iByteList->Commit(); + break; + + case 0x03: // INLINE STRING + if (iParsing == EParseTag) + { + iParsing = EParseNot; + } + + if (ConvertDataStreamingL() != KErrNone) + { + iContinue = EGotoStreaming; + return KErrNotFound; + } + iByteList->Commit(); + break; + + case 0x80: // EXT_T_0 + case 0x81: // EXT_T_1 + case 0x82: // EXT_T_2 + iVariable = byte; + BufferAndOutputL(KWbVariableStart); + if (ConvertDataStringL() != KErrNone) + { + iContinue = EGotoString; + return KErrNotFound; + } + iByteList->Commit(); + break; + + case 0x83: // STR_T + if (ConvertDataStringL() != KErrNone) + { + iContinue = EGotoString; + return KErrNotFound; + } + iByteList->Commit(); + break; + + case 0x43: // PI + // not supported, not used in WML 1.1 + // bytecode PI not implemented + iByteList->Commit(); + break; + + // We will ignore all the uknown tags and their attributes encoded as literals + // (LITERAL, LITERAL_C and LITERAL_CA). The content of these elements is, however, + // treated normally (unless it contains literals, too :) + case 0x04: // LITERAL + iByteList->Commit(); + break; + case 0xC3: // OPAQUE + if (ConvertOpaqueDataL() != KErrNone) + { + iContinue = EGotoOpaque; + return KErrNotFound; + } + iByteList->Commit(); + break; + case 0x44: // LITERAL_C + case 0xC4: // LITERAL_CA + iRestrictedOutput = ETrue; + // FLOWTHROUGH + default: + { + if (iParsing == EParseTag || iParsing == EParseNot) + { + if (iParsing == EParseNot) + { + iParsing = EParseTag; + } + + if( !iRestrictedOutput ) + { + BufferAndOutputL(KWbTagStart); + + // tag name + BufferAndOutputL(*(iTagArray->At(byte&0x3F)->iBuf)); + } + + hasAttrs = (TUint8) (byte & 0x80); + hasContent = (TUint8) (byte & 0x40); + + if (hasContent) + { + iTagIndex++; + if( iRestrictedOutput ) + iTagStack->AppendL( KWbxmlSkipLiteralEndTagMagicToken ); + else + iTagStack->AppendL( TUint16(byte) ); + } + + if (hasAttrs) + iParsing = EParseAttrs; + else if( !iRestrictedOutput ) + BufferAndOutputL(hasContent ? KWbTagEndWithContent() : KWbTagEndWithoutContent() ); + + if( iRestrictedOutput && !hasAttrs ) + // The content will be processed as usual. However, if we had attributes + // we shall discard them, too + iRestrictedOutput = EFalse; + } + + // Attr names and values + else if( !iRestrictedOutput ) + { + + + // starting new attribute + if (byte<128) + { + if (iOpenQuote) + { + BufferAndOutputL(KWbQuote); + iOpenQuote = EFalse; + } + + // attribute name requires starting space, value not + BufferAndOutputL(KWbSpace); + } + + if( byte >= iAttArray->Count() ) + User::Leave(EWapErrDocumentCorrupted); + + // AttrName can be either 'ALIGN="CENTER"' or 'ALIGN="' + // format and followed by several AttrValues or strings. + // We have to scan AttrName to know when quotes are open + // so that we close them no more and no less than once. + // NOTE: quotes in strings are data, not attr escaping. + + if (OutputCheckQuoteL(*(iAttArray->At(byte)->iBuf))) + iOpenQuote = ETrue; + } + iByteList->Commit(); + break; + } + + } // switch (byte) + + } // FOREVER +} + +// Output Opaque data +TInt CWbxmlConverter::ConvertOpaqueDataL() + { + iByteList->Commit(); + + TUint32 multiByte = 0; + if (ExtractMultiByte(&multiByte,0) != KErrNone) + return KErrNotFound; + + TUint32 count = multiByte; + TUint8 byte = 0; + const TInt KBufferSegment = 20; + HBufC8* buffer = HBufC8::NewLC(KBufferSegment); + + while (count != 0) + { + if (iByteList->Byte(&byte,TRUE) != KErrNone) + { + CleanupStack::PopAndDestroy(); // buffer + return KErrNotFound; + } + + buffer->Des().Append(&byte,1); + if( buffer->Des().MaxLength() == buffer->Length() ) + { + buffer = buffer->ReAllocL(buffer->Length() + KBufferSegment); + CleanupStack::Pop(); // the old buffer + CleanupStack::PushL(buffer); + } + count--; + } + + HBufC16* convertedBuffer = FormatDateStringL(*buffer); + CleanupStack::PopAndDestroy(); // buffer + CleanupStack::PushL(convertedBuffer); + + if( convertedBuffer == NULL ) + { + // We could not convert the document! + // but no one checks the return value + // so I don't care... :( + CleanupStack::PopAndDestroy(); // convertedBuffer + iByteList->Commit(); // We can do nothing more with this data so dump it + return KErrCorrupt; + } + + buffer = HBufC8::NewL(convertedBuffer->Length()); + buffer->Des().Copy(*convertedBuffer); + CleanupStack::PopAndDestroy(); // convertedBuffer + CleanupStack::PushL(buffer); + + BufferAndOutputL(*buffer); + + iByteList->Commit(); + CleanupStack::PopAndDestroy(); // buffer + + FinishStringL(); + iByteList->Commit(); + return KErrNone; + } + +// OutputL bytes from streamed inline data +TInt CWbxmlConverter::ConvertDataStreamingL() +{ + iByteList->Commit(); + + TUint8 byte = 0; + const TInt KBufferSegment = 20; + HBufC8* buffer = HBufC8::NewLC(KBufferSegment); + + // Todo: optimization check how much data is already available and OutputL all at the same time + FOREVER + { + if (iByteList->Byte(&byte,TRUE) != KErrNone) + { + CleanupStack::PopAndDestroy(); // buffer + return KErrNotFound; + } + + if (byte == 0) + break; + + buffer->Des().Append(&byte,1); + if( buffer->Des().MaxLength() == buffer->Length() ) + { + buffer = buffer->ReAllocL(buffer->Length() + KBufferSegment); + CleanupStack::Pop(); // the old buffer + CleanupStack::PushL(buffer); + } + } + + HBufC16* convertedBuffer = CharsetConvertDataL(*buffer); + CleanupStack::PopAndDestroy(); // buffer + CleanupStack::PushL(convertedBuffer); + if( convertedBuffer == NULL ) + { + // We could not convert the document! + // but no one checks the return value + // so I don't care... :( + CleanupStack::PopAndDestroy(); // convertedBuffer + iByteList->Commit(); // We can do nothing more with this data so dump it + return KErrCorrupt; + } + + convertedBuffer = ReplaceCharacterEntitiesL(convertedBuffer); + CleanupStack::Pop(); // convertedBuffer, which might have been reallocated + CleanupStack::PushL(convertedBuffer); // the new converted buffer + + buffer = HBufC8::NewL(convertedBuffer->Length()); + buffer->Des().Copy(*convertedBuffer); + CleanupStack::PopAndDestroy(); // convertedBuffer + CleanupStack::PushL(buffer); + + BufferAndOutputL(*buffer); + + iByteList->Commit(); + CleanupStack::PopAndDestroy(); // buffer + + FinishStringL(); + iByteList->Commit(); + return KErrNone; +} + +// OutputL bytes from internal stringtable +TInt CWbxmlConverter::ConvertDataStringL() +{ + if( iStringTable == NULL ) + // There can not be string table references withing a document that hasn't got a string table! + User::Leave(EWapErrDocumentCorrupted); + + iByteList->Commit(); + + TUint32 multiByte = 0; + if (ExtractMultiByte(&multiByte,0) != KErrNone) + return KErrNotFound; + + TInt count = 0; + while (iStringTable->Ptr()[multiByte + count] != 0) + count++; + + if (!count) + return KErrNone; + + if (TUint ((*iStringTable).Length()) < multiByte + count) + return KErrCorrupt; + + TPtrC8 referedString = (*iStringTable).Mid(multiByte,count); + HBufC16* convertedBuffer = CharsetConvertDataL(referedString); + CleanupStack::PushL(convertedBuffer); + if( convertedBuffer == NULL ) + { + // We could not convert the document! + // but no one checks the return value + // so I don't care... :( + CleanupStack::PopAndDestroy(); // convertedBuffer + iByteList->Commit(); // We can do nothing more with this data so dump it + return KErrCorrupt; + } + + convertedBuffer = ReplaceCharacterEntitiesL(convertedBuffer); + CleanupStack::Pop(); // convertedBuffer, which might have been reallocated + CleanupStack::PushL(convertedBuffer); // the new converted buffer + + HBufC8* buffer = HBufC8::NewL(convertedBuffer->Length()); + buffer->Des().Copy(*convertedBuffer); // to 8-bit buffer... + CleanupStack::PopAndDestroy(); // convertedBuffer + CleanupStack::PushL(buffer); + BufferAndOutputL(*buffer); + CleanupStack::PopAndDestroy(); // buffer + + FinishStringL(); + iByteList->Commit(); + + return KErrNone; +} + +TInt CWbxmlConverter::OpenTokenFileL(HBufC& aFileName) +{ + // Design note: we should ask library user to give us token file + // but since this is used only by WML 1.1, it's not our problem. + // Anyway, that's why interface is HBufC& aFileName. + + if (iTagArray) + { + if (iTagArray->Count()) + iTagArray->ResetAndDestroy(); + delete iTagArray; + iTagArray = NULL; + } + if (iAttArray) + { + if (iAttArray->Count()) + iAttArray->ResetAndDestroy(); + delete iAttArray; + iAttArray = NULL; + } + + // + + RFileReadStream stream; + if (stream.Open(iFs,aFileName,EFileShareReadersOnly) != KErrNone) + { + User::Leave(EWapErrUnknownDocument); + } + else + { + CleanupClosePushL(stream); + + HBufC8* strBuffer = HBufC8::NewL(256); + CleanupStack::PushL(strBuffer); + TInt i; + CWbxmlToken* token; + + // + // PublicId + TRAPD(error,ReadTokenFileL(stream,*strBuffer)); + if (error != KErrNone) + { + User::Leave(EWapErrUnknownDocument); + } + + iPublicIdStr = strBuffer->AllocL(); + + // + // Tag tokens + iTagArray = new (ELeave) CArrayPtrFlat(0x40); + for (i = 0; i < 0x40; i++) + { + TRAP(error,ReadTokenFileL(stream,*strBuffer)); + if (error == KErrNone) + { + token = new (ELeave) CWbxmlToken; + CleanupStack::PushL(token); + token->SetTextL(*strBuffer); + iTagArray->AppendL(token); + CleanupStack::Pop(token); + } + else + { + User::Leave(EWapErrUnknownDocument); + } + } + + // + // Attribute names + iAttArray = new (ELeave) CArrayPtrFlat(256); + for (i = 0; i < 256; i++) + { + TRAP(error,ReadTokenFileL(stream,*strBuffer)); + if (error == KErrEof) + break; + if (error == KErrNone) + { + token = new (ELeave) CWbxmlToken; + CleanupStack::PushL(token); + token->SetTextL(*strBuffer); + iAttArray->AppendL(token); + CleanupStack::Pop(token); + } + else + { + User::Leave(EWapErrUnknownDocument); + } + } + + CleanupStack::PopAndDestroy(2); // strBuffer and stream + } + + // reset iTagIndex, was used temporarily as offset variable + iTagIndex = 0; + + return KErrNone; +} + +void CWbxmlConverter::ReadTokenFileL(RFileReadStream& aStream, HBufC8& aBuffer) + { + // Read data from aFile into aBuffer. Skip linebreak(s), not into aBuffer + TBuf8<1> data; + TInt startOffset = iTagIndex; + TPtr8 ptr = aBuffer.Des(); + + // Note: Reusing class instance variable iTagIndex as file offset + aStream.Source()->SeekL(MStreamBuf::ERead,EStreamBeginning,iTagIndex); + FOREVER + { + aStream.ReadL(data,1); + if (data[0] == '\r' || data[0] == '\n') + { + aStream.Source()->SeekL(MStreamBuf::ERead,EStreamBeginning,startOffset); + aStream.ReadL(ptr,iTagIndex-startOffset); + break; + } + iTagIndex++; + } + +// Consume linebreaks, which btw might be missing at last row + FOREVER + { + aStream.ReadL(data,1); + if (data[0] != '\r' && data[0] != '\n') + break; + iTagIndex++; + } + } + +// Note: check wbxml specification for multibyte definition +TInt CWbxmlConverter::ExtractMultiByte(TUint32* aMultiByte, TInt aSkipHeader) +{ + if (iByteList->Inc(aSkipHeader) != KErrNone) + return KErrNotFound; + + TUint8 byte = 0; + *aMultiByte = 0; + do + { + *aMultiByte <<= 7; + if (iByteList->Byte(&byte,TRUE) != KErrNone) + return KErrNotFound; + + *aMultiByte |= byte & ~0x80; + } while (byte & 0x80); + + return KErrNone; +} + +void CWbxmlConverter::FinishStringL() +{ + switch (iVariable) + { + case 0x00: // was not a variable + break; + case 0x40: // variable substitution - escaped + case 0x80: // variable reference to string table - escaped + BufferAndOutputL(KWbVariableEscaped); + break; + case 0x41: // variable substitution - unescaped + case 0x81: // variable reference to string table - unescaped + BufferAndOutputL(KWbVariableUnescaped); + break; + case 0x42: // variable substitution - no transformation + case 0x82: // variable reference to string table - no transformation + // FLOWTHROUGH + default: + BufferAndOutputL(KWbVariableNormal); + break; + } + iVariable = 0; +} + +HBufC16* CWbxmlConverter::FormatDateStringL(TDesC8& aData) +// Return value NULL indicates failed conversion! +// Ownership of the resulting string is passed up to caller. +{ + const TInt KDateTimePart = 7; + enum TDateTimeParts {EYearTop, EYearBot, EMon, EDay, EHour, EMin, ESec}; + TInt dateData[KDateTimePart]; + + for (TInt i = 0; i < KDateTimePart; i++) + dateData[i] = 0; + + TInt offSet = 0; + TInt dataLen = aData.Length(); + + while (offSet < KDateTimePart && dataLen > 0) + { + dateData[offSet] = aData[offSet]; + offSet++; + dataLen--; + } + + _LIT(KDateFormat,"%2x%02x-%02x-%02xT%02x:%02x:%02xZ"); + const TInt KDateFormatLen = 20; + + TBuf16 dateBuf; + dateBuf.Format(KDateFormat, dateData[EYearTop], + dateData[EYearBot], + dateData[EMon], + dateData[EDay], + dateData[EHour], + dateData[EMin], + dateData[ESec]); + return( dateBuf.AllocL() ); +} + +HBufC16* CWbxmlConverter::CharsetConvertDataL(TDesC8& aData) +// Return value NULL indicates failed conversion! +// Ownership of the resulting string is passed up to caller. +{ + if( iCharsetConverter->PrepareToConvertToOrFromL(iWbxmlCharset,iFs) == CCnvCharacterSetConverter::ENotAvailable ) + return( NULL ); + + HBufC* convertedString = HBufC::NewLC(aData.Length()); + TInt state = CCnvCharacterSetConverter::KStateDefault; + TPtr16 convertedStringDes = convertedString->Des(); + TInt convertResult = iCharsetConverter->ConvertToUnicode(convertedStringDes, aData, state); + CleanupStack::Pop(); // convertedString + + if (convertResult) + { + delete convertedString; + convertedString = NULL; + } + + return( convertedString ); +} + + +HBufC* CWbxmlConverter::ReplaceCharacterEntitiesL(HBufC* aString) + { + _LIT(KDollarRef, "$$"); + _LIT(KAmpersandRef, "&"); + _LIT(KLesserRef, "<"); + _LIT(KGreaterRef, ">"); + _LIT(KQuoteRef, """); + _LIT(KAposRef, "'"); + _LIT(KNonBreakingSpaceRef, " "); + _LIT(KShyRef, "­"); + + // Calculate the need for extra characters to be allocated. + // The additions to lengthExtension are subtracted by one, since the original + // chacracter will be overwritten. + TInt i=0; + TInt lengthExtension = 0; // The amount of extra characters needed + const TText* stringPtr=aString->Ptr(); + const TDesC* ext=NULL; + for( i=0; i < aString->Length(); i++) + { + switch( *stringPtr++ ) + { + case '$': + ext = &KDollarRef; + break; + case '&': + ext = &KAmpersandRef; + break; + case '<': + ext = &KLesserRef; + break; + case '>': + ext = &KGreaterRef; + break; + case '"': + ext = &KQuoteRef; + break; + case 39: + ext = &KAposRef; + break; + case 160: + ext = &KNonBreakingSpaceRef; + break; + case 173: + ext = &KShyRef; + break; + default: + // Normal character cause no added length + break; + } + if(ext) + { + lengthExtension+=ext->Length()-1; + ext=NULL; + } + } + + if( lengthExtension == 0 ) + return aString; + + if( aString->Des().MaxLength() < aString->Length()+lengthExtension ) + aString = aString->ReAllocL(aString->Length()+lengthExtension); + + // Do the replacement thing... + + stringPtr=aString->Ptr(); + ext=NULL; + + for( i=0; i < aString->Length(); i++, stringPtr++) + { + switch( *stringPtr ) + { + case '$': + ext=&KDollarRef; + break; + case '&': + ext=&KAmpersandRef; + break; + case '<': + ext=&KLesserRef; + break; + case '>': + ext=&KGreaterRef; + break; + case '"': + ext=&KQuoteRef; + break; + case 39: + ext=&KAposRef; + break; + case 160: + ext=&KNonBreakingSpaceRef; + break; + case 173: + ext=&KShyRef; + break; + default: + ext=NULL; + break; + } + + if( ext != NULL ) + { + aString->Des().Delete(i,1); + aString->Des().Insert(i,*ext); + i += ext->Length()-1; // one will be added to the counter at the end of the loop + stringPtr += ext->Length()-1; // one will be added to the counter at the end of the loop + } + } + + return( aString ); + } + +// Entity output +void CWbxmlConverter::OutputL(TUint32 aSource) const +{ + TBuf8<4> buf; + const TDesC8* data=NULL; + // This is the dodgy bit : we need to buffer these correctly + // We are WML specific, so we DO know about these special cases + switch(aSource) + { + case '$': + { + // Can this be added here, too??? JOM 231199 + data = &KEntityDollar; + } + break; + case '&': // ampersand && + { + data = &KEntityAmp; + } + break; + case '<': // less than &< + { + data = &KEntityLt; + } + break; + case '>': // greater than > + { + data = &KEntityGt; + } + break; + case '"': // quotation mark " + { + data = &KEntityQuot; + } + break; + case 39: // apostrophe ' + { + data = &KEntityApos; + } + break; + case 160: // non-breaking space   + { + data = &KEntityNbsp; + } + break; + case 173: // soft hyphen ­ + { + data = &KEntityShy; + } + break; + default: + { + buf.Append(aSource); + data = &buf; + } + } + if(data) + BufferAndOutputL(*data); +} + +TBool CWbxmlConverter::OutputCheckQuoteL(HBufC8& aSource) const +{ + TBool openQuote = EFalse; + + // Scan for quote symbol + TInt len = aSource.Length(); + const TText8* stringPtr=aSource.Ptr(); + for (TInt i = 0; i < len; i++) + { + if (*stringPtr++ == '"') + openQuote = !openQuote; + } + BufferAndOutputL(aSource); + + return openQuote; +} + +void CWbxmlConverter::BufferAndOutputL(const TDesC8& aSource) const + { + // Append to buffer here and output when full + if(aSource.Length() + iOutputBuffer->Length() > KOutputBufferLength) + { + // Buffer is full : Keep ownership but reset buffer length to zero + // No point passing ownership as the observer copies the buffer + // and then deletes it. Passing it by reference means the observer + // copies it and we can reuse it + TInt returnValue = iObserver->HandleWbxmlOutputL(*iOutputBuffer); + if( returnValue != KErrNone ) + User::Leave(returnValue); + TPtr8 ptr(iOutputBuffer->Des()); + ptr.SetLength(0); + } + // Check input source is smaller than buffer + if(aSource.Length() >= KOutputBufferLength) + { + // We have to allocate a new buffer here and give ownership to the observer + HBufC8* tmp = aSource.AllocLC(); + TInt returnValue = iObserver->HandleWbxmlOutputL(tmp); + if( returnValue != KErrNone ) + User::Leave(returnValue); + CleanupStack::Pop(); + return; + } + // Buffer the current output + TPtr8 ptr = iOutputBuffer->Des(); + ptr.Append(aSource); + } + +HBufC* CWbxmlConverter::FindTokenFileL(const TDesC& aTokenFilePath) const +// +// Search drives for file specified by aTokenFilePath, and return the full path. + { + TFindFile finder = iFs; + User::LeaveIfError(finder.FindByDir(aTokenFilePath, KNullDesC())); + + HBufC* fullPath = NULL; + fullPath = finder.File().AllocL(); + + return fullPath; + } + + +// +// End of file +//