messagingappbase/smartmessaging/gmsmodel/src/GmsModel.cpp
changeset 0 72b543305e3a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingappbase/smartmessaging/gmsmodel/src/GmsModel.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,692 @@
+/*
+* Copyright (c) 2002 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:  
+*     Picture Message model and converter.
+*
+*/
+
+
+
+// INCLUDE FILES
+#include "gmsModel.h"
+
+#include <txtrich.h>                //for CRichText
+#include <s32file.h>
+
+// LOCAL CONSTANTS AND MACROS
+
+// size constants
+const TInt KGmsVersionLength          = 1;
+const TInt KGmsItemLengthFieldLength  = 2;
+const TInt KGmsItemHeaderLength       = 3;
+
+// tokens
+const TUint8 KGmsVersionAsciiZero     = 48; // "0"
+const TUint8 KGmsItemTypeLatin1       = 0;  // "0"
+const TUint8 KGmsItemTypeUnicode      = 1;  // "1"
+const TUint8 KGmsItemTypeOTABitmap    = 2;  // "2"
+
+const TUint8 KGmsLF = 10;
+_LIT(KGmsModelDll,"GmsModel.dll");
+
+//  MEMBER FUNCTIONS
+
+
+EXPORT_C CGmsModel* CGmsModel::NewL(RFs& aFs, TInt aLeaveCodeForCorrupt)
+    {
+    CGmsModel* self = new (ELeave) CGmsModel(aFs, aLeaveCodeForCorrupt);
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+void CGmsModel::ConstructL()
+    {
+    }
+
+EXPORT_C CGmsModel::~CGmsModel()
+    {
+    delete iText;
+    delete iOTABitmap;
+    }
+
+EXPORT_C void CGmsModel::ImportGMSL(const TDesC8& aInput)
+    {
+    Reset();
+    TInt index = 0;
+    CheckVersionL(aInput, index); //this leaves if version is invalid
+
+    TInt length = aInput.Length();
+    while (index < length)
+        {
+        //either text, a picture or something unsupported
+        ExtractGMSSubItemL(aInput, index);
+        }
+    __ASSERT_ALWAYS(HasPicture(), User::Leave(iLeaveCorrupt));
+    }
+
+EXPORT_C void CGmsModel::ImportGMSL(const CRichText& aRichText)
+    {
+    Reset();
+
+    // Create a temporary HBufC16 for extracting from CRichText.
+    // (CRichText has no interface for extracting to 8 bit descriptor)
+    TInt docLength = aRichText.DocumentLength();
+    HBufC16* buf = HBufC16::NewLC(docLength);
+    TPtr16 ptr16(buf->Des());
+    aRichText.Extract(ptr16, 0, docLength);
+
+    //Create a temporary HBufC8 for importing to converter.
+    HBufC8* buf8 = HBufC8::NewLC(docLength);
+    TPtr8 ptr8 = buf8->Des();
+
+    //Copy from 16 to 8 bit descriptor.
+    ptr8.Copy(ptr16);
+
+    ImportGMSL(*buf8);
+    CleanupStack::PopAndDestroy(2); //buf8 buf16
+    }
+
+EXPORT_C HBufC8* CGmsModel::ExportGMS8LC()
+    {
+    // There has to be at least picture data.
+    __ASSERT_ALWAYS(HasPicture(), Panic(EGmsMPanicNoPicData1));
+
+    HBufC8* buf = NULL;
+    buf = HBufC8::NewLC(TotalSize());
+    TPtr8 gms(buf->Des());
+    AppendVersionL(gms);        // <version>
+    AppendTextSubItemL(gms);    // <item>
+    AppendPictureSubItemL(gms); // <item>
+    return buf;
+    }
+
+EXPORT_C HBufC16* CGmsModel::ExportGMS16LC()
+    {
+    HBufC16* buf16 = HBufC16::NewLC(TotalSize());
+    TPtr ptr16(buf16->Des());
+    HBufC8* buf8 = ExportGMS8LC();
+    TPtr8 ptr8(buf8->Des());
+    ptr16.Copy(ptr8);
+    CleanupStack::PopAndDestroy(); //buf8
+    return buf16;
+    }
+
+EXPORT_C HBufC* CGmsModel::TextAsHBufC16LC() const
+    {
+    if (iText)
+        {
+        return iText->AllocLC();
+        }
+    return KNullDesC().AllocLC();
+    }
+
+EXPORT_C void CGmsModel::SetTextL(const TDesC& aSource)
+    {
+    if (ContainsUnicodeL(aSource))
+        {
+        SetTextUnicodeL(aSource);
+        }
+    else
+        {
+        SetTextLatinL(aSource);
+        }
+    }
+
+EXPORT_C void CGmsModel::SetTextLatinL(const TDesC& aSource)
+    {
+    HBufC* text = aSource.AllocL();
+    delete iText;
+    iText  = text;
+    iMode = EGmsModeLatin;
+    }
+
+EXPORT_C void CGmsModel::SetTextUnicodeL(const TDesC& aSource)
+    {
+    HBufC* text = aSource.AllocL();
+    delete iText;
+    iText  = text;
+    iMode = EGmsModeUnicode;
+    }
+
+TBool CGmsModel::ContainsUnicodeL(const TDesC& aInputString) const
+    {
+    TInt length = aInputString.Length();
+    const TUint KGmsHighestLatin1 = 255;
+    for (TInt n = 0; n < length; n++)
+        {
+        TUint16 ch = aInputString[n];
+        // Latin-1 occupies dec. values 0 to 255 of Unicode. (U+0000..U+00FF)
+        if (ch > KGmsHighestLatin1 && ch != CEditableText::EParagraphDelimiter)
+            {        
+            return ETrue;
+            }
+        }
+    return EFalse;
+    }
+
+EXPORT_C void CGmsModel::ExportOTABitmapL(RWriteStream& aOutStream)
+    {
+    __ASSERT_ALWAYS(HasPicture(), Panic(EGmsMPanicNoPictureToExport));
+    aOutStream.WriteL(*iOTABitmap);
+    }
+
+/// Assumes an 8 bit data stream
+EXPORT_C void CGmsModel::ImportOTABitmapL(RReadStream& aReadStream)
+    {
+    MStreamBuf* streamBuf = NULL;
+    streamBuf = aReadStream.Source();
+    User::LeaveIfNull(streamBuf);
+    TInt size = streamBuf->SizeL();
+    __ASSERT_ALWAYS(size > 0,
+        Panic(EGmsMPanicStreamIsEmpty));
+    HBufC8* bitmap = HBufC8::NewLC(size);
+    TPtr8 bitmapPtr(bitmap->Des());
+    aReadStream.ReadL(bitmapPtr, size);
+    // This leaves with iLeaveCorrupt if the bitmap is bad.
+    HBufC8* buf8 = CheckedAndFixedOtaBitmapL(*bitmap);
+    CleanupStack::PopAndDestroy(bitmap);
+    delete iOTABitmap;
+    iOTABitmap = buf8;
+    }
+
+EXPORT_C TInt CGmsModel::SizeOfCompleteMsgExcludingTextAndPic() const
+    {
+    return (KGmsVersionLength + KGmsItemHeaderLength*2);
+    }
+
+EXPORT_C TBool CGmsModel::HasText() const
+    {
+    if (!iText || iText->Length() <= 0)
+        {
+        return EFalse;
+        }
+    // length > 0
+    return ETrue;
+    }
+
+// This does not verify that the data is valid
+EXPORT_C TBool CGmsModel::HasPicture() const
+    {
+    if (!iOTABitmap || iOTABitmap->Length() <= 0)
+        {
+        return EFalse;
+        }
+    // length > 0
+    return ETrue;
+    }
+
+EXPORT_C TBool CGmsModel::IsTextLatin() const
+    {
+    return (iMode == EGmsModeLatin);
+    }
+
+EXPORT_C TInt CGmsModel::TotalSizeExcludingText() const
+    {
+    TInt bitmapSize = 0;
+    if (iOTABitmap)
+        {
+        bitmapSize = iOTABitmap->Size();
+        }
+    return bitmapSize + SizeOfCompleteMsgExcludingTextAndPic();
+    }
+
+EXPORT_C TInt CGmsModel::TotalSize() const
+    {
+    TInt textSize(0);
+    
+    textSize = Textlength();
+    if (iMode == EGmsModeUnicode)
+        {
+        textSize *= 2; // (two bytes per char)
+        }
+
+    return TotalSizeExcludingText() + textSize;
+    }
+
+EXPORT_C TInt CGmsModel::Textlength() const
+    {
+    if (iText)
+        {
+        return iText->Length();
+        }
+    return 0;
+    }
+
+EXPORT_C TInt CGmsModel::PictureSize() const
+    {
+    if (iOTABitmap)
+        {
+        return iOTABitmap->Size();
+        }
+    return 0;
+    }
+
+EXPORT_C void CGmsModel::Reset()
+    {
+    delete iText;
+    iText = NULL;
+    delete iOTABitmap;
+    iOTABitmap = NULL;
+    }
+
+EXPORT_C TInt CGmsModel::_TestingL(TInt /*aCode1*/, TInt& /*aCode2*/, const TDesC8& /*aDes1*/, const TDesC16& /*aDes2*/)
+    {
+    // Nothing here.
+    return 0;
+    }
+
+// Deprecated. Do not use.
+//
+EXPORT_C HBufC* CGmsModel::ExtractTextFromMsgLC(const TDesC& /*aMsg*/, TInt /*aAmount*/)
+    {
+    return NULL; // for compiler
+    }
+
+EXPORT_C TBool CGmsModel::IsPictureValidL()
+    {
+    // Deprecated
+    return ETrue;
+    }
+
+TBool CGmsModel::ImportOtaBitmapL(const TDesC& aFileName)
+    {
+    RFileReadStream readStream;
+    User::LeaveIfError(readStream.Open(
+        iFs,
+        aFileName,
+        EFileStream));
+    readStream.PushL();
+    MStreamBuf* buf = readStream.Source();
+    User::LeaveIfNull(buf);
+    TInt size = buf->SizeL();
+    __ASSERT_ALWAYS(size > 0,
+        Panic(EGmsNull1));
+    HBufC8* data = HBufC8::NewLC(size);
+    TPtr8 dataPtr(data->Des());
+    readStream.ReadL(dataPtr, size);
+    TBool success;
+    HBufC8* buf8 = NULL;
+    buf8 = CheckedAndFixedOtaBitmapL(*data, success);
+    CleanupStack::PopAndDestroy(data);
+    CleanupStack::PopAndDestroy(); //(closes readStream)
+    if (success)
+        {
+        delete iOTABitmap;
+        iOTABitmap = buf8;
+        return ETrue;
+        }
+    else
+        {
+        __ASSERT_DEBUG(!buf8, Panic(EGmsProblem1));
+        return EFalse;
+        }
+    }
+
+// Call this as the first extraction method.
+void CGmsModel::CheckVersionL(const TDesC8& aInput, TInt& aIndex)
+    {
+    __ASSERT_ALWAYS(aInput.Length() >= KGmsVersionLength,
+        User::Leave(iLeaveCorrupt));
+    // To be a valid msg, its version identifier must be zero
+    if (aInput[0] != KGmsVersionAsciiZero)
+        {
+        User::Leave(iLeaveCorrupt);
+        }
+    aIndex++;
+    }
+
+void CGmsModel::ExtractGMSSubItemL(const TDesC8& aInput, TInt& aIndex)
+    {
+    ExtractGMSSubItemL(aInput, aIndex, EFalse);
+    }
+
+void CGmsModel::ExtractGMSSubItemL(const TDesC8& aInput, TInt& aIndex,
+    TBool aSkipBitmap)
+    {
+    __ASSERT_ALWAYS(aInput.Length() >= aIndex + KGmsItemHeaderLength,
+        User::Leave(iLeaveCorrupt));
+
+    //Get GMS item type. It should be one of the following:
+    //  "00" <GMS-item-length> <ISO-8859-1-char>*     = LATIN TEXT ITEM
+    //  "01" <GMS-item-length> <UNICODE-char>*        = UNICODE TEXT ITEM
+    //  "02" <GMS-item-length> <OTA-bitmap>           = PICTURE ITEM
+
+    TBuf8<1> msgItemType;
+    msgItemType = aInput.Mid(aIndex, 1); //pos, length
+    aIndex += 1;
+
+    if (KGmsItemTypeLatin1 == msgItemType[0])       // "00"
+        {
+        ExtractLatin1L(aInput, aIndex);
+        }
+    else if (KGmsItemTypeUnicode == msgItemType[0]) // "01"
+        {
+        ExtractUnicodeL(aInput, aIndex);
+        }
+    else if (KGmsItemTypeOTABitmap == msgItemType[0]) // "02"
+        {
+        if (aSkipBitmap)
+            {
+            ExtractUnknownL(aInput, aIndex); //just skips over it
+            }
+        else
+            {
+            ExtractOTABitmapL(aInput, aIndex);
+            }
+        }
+    else
+        {
+        ExtractUnknownL(aInput, aIndex); //just skips over the part
+        }
+    }
+
+void CGmsModel::ExtractLatin1L(const TDesC8& aInput, TInt& aIndex)
+    {
+    TInt length = ExtractLengthL(aInput, aIndex);
+
+    __ASSERT_ALWAYS(aInput.Length() >= aIndex + length,
+        User::Leave(iLeaveCorrupt));
+
+    delete iText;
+    iText = NULL;
+    iText = HBufC::NewL(length);
+    iText->Des().Copy(aInput.Mid(aIndex, length));
+    aIndex += length;
+    iMode = EGmsModeLatin;
+    }
+
+void CGmsModel::ExtractUnicodeL(const TDesC8& aInput, TInt& aIndex)
+    {
+    TInt length = ExtractLengthL(aInput, aIndex);
+    __ASSERT_ALWAYS(aInput.Length() >= aIndex + length,
+        User::Leave(iLeaveCorrupt));
+    __ASSERT_ALWAYS((length % 2) == 0, User::Leave(iLeaveCorrupt));//even nr.
+    delete iText;
+    iText = NULL;
+    iText = HBufC::NewL(length / 2);
+
+    //
+    // The data has to be reassembled from UCS-2 to 16 bit Unicode.
+    //
+    TPtr ptr(iText->Des());
+    for (TInt n = aIndex; n < aIndex + length; n += 2)
+        {
+        TUint16 lower = aInput[n + 1];
+        TUint16 upper = aInput[n];
+        upper = TUint16(upper << 8);
+        TUint16 ch = 0;
+        ch = TUint16(upper | lower);
+        ptr.Append(ch);
+        }
+    aIndex += length;
+    iMode = EGmsModeUnicode;
+    }
+
+void CGmsModel::ExtractOTABitmapL(const TDesC8& aInput, TInt& aIndex)
+    {
+    TInt length = ExtractLengthL(aInput, aIndex);
+    __ASSERT_ALWAYS(aInput.Length() >= aIndex + length,
+        User::Leave(iLeaveCorrupt));
+    delete iOTABitmap;
+    iOTABitmap = NULL;
+    // This leaves with iLeaveCorrupt if the bitmap is bad.
+    iOTABitmap = CheckedAndFixedOtaBitmapL(aInput.Mid(aIndex, length));
+    aIndex += length;
+    }
+
+void CGmsModel::ExtractUnknownL(const TDesC8& aInput, TInt& aIndex)
+    {
+    TInt length = ExtractLengthL(aInput, aIndex);
+    __ASSERT_ALWAYS(aInput.Length() >= aIndex + length,
+        User::Leave(iLeaveCorrupt));
+    aIndex += length; // Skip the unknown item.
+    }
+
+TInt CGmsModel::ExtractLengthL(const TDesC8& aInput, TInt& aIndex)
+    {
+    __ASSERT_ALWAYS(aInput.Length() >= aIndex + KGmsItemLengthFieldLength,
+        User::Leave(iLeaveCorrupt));
+
+    // (KGmsItemLengthFieldLength = 2)
+    TBuf8<KGmsItemLengthFieldLength> itemLength;
+    itemLength.Copy(aInput.Mid(aIndex, KGmsItemLengthFieldLength));
+    aIndex += KGmsItemLengthFieldLength;
+    TUint16 length = 0;
+    length = TUint16 ((itemLength[0]&0x000000FF));
+    length = TUint16 (length<<8);
+    length |= TUint16 ((itemLength[1]&0x000000FF));
+    return length;
+    }
+
+CGmsModel::CGmsModel(RFs& aFs, TInt aLeaveCodeForCorrupt) :
+    iFs(aFs),
+    iLeaveCorrupt(aLeaveCodeForCorrupt)
+    {
+    }
+
+void CGmsModel::AppendVersionL(TDes8& aGms)
+    {
+    TBuf8<1> gmsVersion;
+    _LIT(KEmpty1, "0");
+    gmsVersion.Copy(KEmpty1);
+    gmsVersion[0] = KGmsVersionAsciiZero;
+    aGms.Append(gmsVersion);
+    }
+
+void CGmsModel::AppendTextSubItemL(TDes8& aGms)
+    {
+    TBuf8<3> itemHeader;
+    _LIT(KEmpty3, "000");
+    itemHeader.Copy(KEmpty3);
+
+    if (iMode == EGmsModeLatin)
+        {
+        itemHeader[0] = KGmsItemTypeLatin1; // entity type identifier.
+        }
+    else
+        {
+        // Unicode
+        itemHeader[0] = KGmsItemTypeUnicode; // entity type identifier.
+        }
+
+    TInt sizeT = 0;
+    if (iText)
+        {
+        sizeT = iText->Length();
+        if (iMode == EGmsModeUnicode)
+            {
+            sizeT *= 2;
+            }
+        }
+
+    // The text header is added even if there is no text.
+    TInt lowerT = sizeT;
+    lowerT &= TInt(0x000000FF);
+
+    TUint upperT = sizeT;
+    upperT = TInt(upperT >> 8);
+    upperT &= TInt(0x000000FF);
+
+    itemHeader[1] = TInt8(upperT); //First byte of <Item-length>
+    itemHeader[2] = TInt8(lowerT); //Second byte of <Item-length>
+
+    aGms.Append(itemHeader);
+    if (iText)
+        {
+        if (iMode == EGmsModeLatin)
+            {
+            AppendLatin1TextL(aGms, *iText);
+            }
+        else
+            {
+            // Have to encode Unicode text to UCS2.
+            AppendUCS2TextL(aGms, *iText);
+            }
+        }
+    }
+
+void CGmsModel::AppendPictureSubItemL(TDes8& aGms)
+    {
+    __ASSERT_DEBUG(HasPicture(), Panic(EGmsMPanicNoPicData2));
+
+    TBuf8<3> itemHeader;
+    _LIT(KEmpty3, "000");
+    itemHeader.Copy(KEmpty3);
+
+    itemHeader[0] = KGmsItemTypeOTABitmap; // entity type identifier.
+
+    TInt sizeB = iOTABitmap->Size();
+    TInt lowerB = sizeB;
+    lowerB &= TInt(0x000000FF);
+
+    TUint upperB = sizeB;
+    upperB = TInt(upperB >> 8);
+    upperB &= TInt(0x000000FF);
+
+    itemHeader[1] = TInt8(upperB); //First byte of <Item-length>
+    itemHeader[2] = TInt8(lowerB); //Second byte of <Item-length>
+
+    aGms.Append(itemHeader);
+    aGms.Append(*iOTABitmap);
+    }
+
+void CGmsModel::AppendUCS2TextL(TDes8& aGms, const TDesC& aText)
+    {
+    TInt length = aText.Length();
+    for (TInt n = 0; n < length; n++)
+        {
+        TUint16 i = aText[n];
+
+        TUint16 lower = i;
+        lower &= TUint16(0x000000FF);
+
+        TUint16 upper = i;
+        upper = TUint16(upper >> 8);
+        upper &= TUint16(0x000000FF);
+        TChar ch = upper;
+        aGms.Append(ch);
+        ch = lower;
+        aGms.Append(ch);
+        }
+    }
+
+void CGmsModel::AppendLatin1TextL(TDes8& aGms, const TDesC& aText)
+    {
+    TInt length = aText.Length();
+    for (TInt n = 0; n < length; n++)
+        {
+        TUint16 ch = aText[n];
+        if (ch == CEditableText::EParagraphDelimiter)
+            {
+            aGms.Append(KGmsLF);
+            }
+        else
+            {
+            aGms.Append(ch);
+            }
+        }
+    }
+
+HBufC8* CGmsModel::CheckedAndFixedOtaBitmapL(const TDesC8& aOta) const
+    {
+    TBool success;
+    HBufC8* buf = CheckedAndFixedOtaBitmapL(aOta, success);
+    if (success)
+        {
+        return buf;
+        }
+    else
+        {
+        __ASSERT_DEBUG(!buf, Panic(EGmsProblem2));
+        User::Leave(iLeaveCorrupt);
+        return NULL; //for compiler
+        }
+    }
+
+HBufC8* CGmsModel::CheckedAndFixedOtaBitmapL(const TDesC8& aOta, TBool& aSuccess) const
+    {
+    // From Smart Messaging Specification 3.0.0:
+    // <ota-bitmap> ::= <header> <image- data> [<palette>]
+    // <header> ::= <infofield> [<extfield>]* <width><height><depth>
+
+    __ASSERT_DEBUG(aOta.Length(), Panic(EGmsNoPic1));
+    TInt length = aOta.Length();
+
+    // <header> should be four bytes
+    const TInt KHeaderBytes = 4;
+    if (length < KHeaderBytes)
+        {
+        aSuccess = EFalse;
+        return NULL;
+        }
+
+    // <infofield> byte should be zero, because the following should hold:
+    // Concatenation flag = 0 (no)
+    // Compression = 0        (no)
+    // External palette = 0   (not used)
+    // Icon max size fld len. = 0 (8 bit)
+    // Number of animated icons = 0
+    if (aOta[0] != 0)
+        {
+        aSuccess = EFalse;
+        return NULL;
+        }
+
+    // <extfield> not allowed, and it was ruled out by requiring 0 for
+    // the concatenation flag of <infofield>.
+
+    TInt width = aOta[1];
+    TInt height = aOta[2];
+    TInt depth = aOta[3];
+    if (!Rng(KGmsMinPictureWidthPixels, width, KGmsMaxPictureWidthPixels)
+        || !Rng(KGmsMinPictureHeightPixels, height,KGmsMaxPictureHeightPixels)
+        || depth != 1)
+        {
+        aSuccess = EFalse;
+        return NULL;
+        }
+
+    // Check that there is the right amount of picture data.
+    TInt pixels = width * height;
+    TInt bytesNeededForPicData = pixels / 8 + (pixels%8 == 0 ? 0 : 1);
+    TInt bytesNeeded = bytesNeededForPicData + KHeaderBytes;
+    TInt tooManyBytes = length - bytesNeeded;
+    if (tooManyBytes == 0)
+        {
+        aSuccess = ETrue;
+        return aOta.AllocL();
+        }
+    else if (tooManyBytes < 0)
+        {
+        // not enough bytes
+        aSuccess = EFalse;
+        return NULL;
+        }
+    else
+        {
+        // tooManyBytes > 0
+        // Discard the excess bytes.
+        aSuccess = ETrue;
+        return aOta.Left(bytesNeeded).AllocL();
+        }
+    }
+
+void CGmsModel::Panic(TGmsModelPanic aCode)
+    {
+    User::Panic(KGmsModelDll, aCode);
+    }
+
+//end of file