diff -r 000000000000 -r 72b543305e3a email/pop3andsmtpmtm/servermtmutils/src/cimcaf.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/pop3andsmtpmtm/servermtmutils/src/cimcaf.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,386 @@ +// Copyright (c) 2004-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: +// Implementation of the CImCaf class. +// +// +/** + @file + @internalTechnology +*/ + +#include +#include + +_LIT8(KXHyphen,"X-"); +_LIT8(KContentHyphen,"Content-"); + +/** +Construct with callers file session handle. +c'tor +*/ +EXPORT_C CImCaf::CImCaf(RFs& aFs) : iFsSession(aFs) + {} + +/** +d'tor +*/ +EXPORT_C CImCaf::~CImCaf() + { + delete iSupplier; + delete iContentType; + delete iMetaData; + delete iImportFile; + } + + +/** +Prior to StartProcessing() the CAF agent could provide an initial filename for the CAF session. +@param aAttachmentFileName Descriptor for writing CAF agent suggested filename. +@return KErrNone success or system error code. +@pre PrepareProcessingL() must be called prior to this call. +@panic imut 36 +*/ +EXPORT_C TInt CImCaf::GetSuggestedAttachmentFileName(TDes& aAttachmentFileName) const + { + __ASSERT_DEBUG(iImportFile != NULL, gPanic(EPanicInvalidCafState)); + return iImportFile->GetSuggestedOutputFileName(aAttachmentFileName); + } + + +/** +Write a buffer of data to the CAF Agent. +@param aData Data to write to the CAF agent. +@return KErrNone success or system error code. +@pre StartProcessingL() must be called prior to this call +@post Generates 0-N receipt files under the current message attachment entry. +@panic imut 36 +*/ +EXPORT_C TInt CImCaf::WriteData(const TDesC8& aData) + { + TInt err(KErrNone); + __ASSERT_DEBUG(iImportFile != NULL, gPanic(EPanicInvalidCafState)); + err = iImportFile->WriteData(aData); + __ASSERT_DEBUG(err == KErrNone || err == KErrNotReady || err == KErrCANewFileHandleRequired, gPanic(EPanicInvalidCafState)); + if(err == KErrNone || err == KErrNotReady) + { + // Write succeeded + return err; + } + else if(err == KErrCANewFileHandleRequired) + { + // We need a new file handle see if the class member is set up ready + if(iAttachmentFile.SubSessionHandle()) + { + // Caller provided open file handle + err = iImportFile->ContinueWithNewOutputFile(iAttachmentFile,iAttachmentFilePath); + // Close the caller's file handle as CAF has made a copy + iAttachmentFile.Close(); + if(err == KErrCANewFileHandleRequired) + TRAP(err,NewFileL()); + } + else + { + TRAP(err,NewFileL()); + } + // New file handle is created, write data + err = iImportFile->WriteData(aData); + } + __ASSERT_DEBUG(err == KErrNone, gPanic(EPanicInvalidCafState)); + return err; + } + +/** +Add Mime header metadata to CAF class in preparation for PrepareProcessingL() +Metadata comprises a field and data pair. For example: +aField - "X-Oma-Drm-Separate-Delivery" aData - "12" +If field does not contain "X-" or "Content-" then the method returns without +adding them to the class. +@param aField - Mime header field +@param aData - Mime field's corresponding data +@panic imut 36 +*/ +EXPORT_C void CImCaf::AddToMetaDataL(const TDesC8& aField, const TDesC8& aData) + { + if(aField.FindF(KContentHyphen) == KErrNone && aField.FindF(KXHyphen) == KErrNone) + { + return; + } + if(!iMetaData) + { + iMetaData = CMetaDataArray::NewL(); + } + // Check we're not overwriting an existing one. Could show up a logic error. + #if (defined _DEBUG) + TPtrC8 des(iMetaData->SearchL(aField)); + __ASSERT_DEBUG(des.Length() == 0, gPanic(EPanicInvalidCafState)); + #endif + iMetaData->AddL(aField,aData); + } + +/** +Add Mime header metadata to CAF class in preparation for PrepareProcessingL() +Parses a complete unfolded mime header line to extract fields and values +Format expected: +Fieldxxx: Valuexxx;ParamField=ParamValue +If line does not contain "X-" or "Content-" then the method returns without +adding data to the class. +@param aMimeLine - Mime header data line minus CRLF +@panic imut 36 +*/ +EXPORT_C void CImCaf::AddToMetaDataL(const TDesC8& aMimeLine) + { + // Don't bother adding if it's neither of these + // This means we won't add envelope stuff + if(aMimeLine.FindF(KContentHyphen) == KErrNone && aMimeLine.FindF(KXHyphen) == KErrNone) + { + return; + } + if(!iMetaData) + { + iMetaData = CMetaDataArray::NewL(); + } + TInt offset; + TPtrC8 field; + TPtrC8 data; + // Isolate the field and data pair separated by ':' + if((offset = aMimeLine.Locate(KImcvColon)) != KErrNotFound) + { + field.Set(aMimeLine.Left(++offset)); + // Remove leading whitespace + while(offset < aMimeLine.Length() && aMimeLine[offset] == ' ') + { + offset++; + } + if(offset < aMimeLine.Length()) + { + data.Set(aMimeLine.Mid(offset)); + } + else + return; + } + else + { + // No colon should never happen if mail is not corrupt + __ASSERT_DEBUG(offset != KErrNotFound, gPanic(EPanicInvalidCafState)); + return; + } + // Check we're not overwriting an existing one. Could show up a logic error. + #if (defined _DEBUG) + TPtrC8 des(iMetaData->SearchL(field)); + __ASSERT_DEBUG(des.Length() == 0, gPanic(EPanicInvalidCafState)); + #endif + iMetaData->AddL(field,data); + } + +/** +Prepare the CImCAF class for a CAF session. +@pre RegisterL() must be called prior to this call and success checked with a Registered() call. +@panic imut 36 +*/ +EXPORT_C void CImCaf::PrepareProcessingL() + { + __ASSERT_DEBUG(iSupplier != NULL && iContentType != NULL && iMetaData != NULL && iImportFile == NULL, gPanic(EPanicInvalidCafState)); + iImportFile = iSupplier->ImportFileL(iContentType->Des(),*iMetaData);// Prepare agent for writing data. + Deregister(); + } + +/** +Set the CAF class variables required to Start the CAF write session. +@param aDefaultAttachmentFileName Localised default attachment name. +@param aAttachmentFilePath If CAF agent requires extra files, this is the folder/attachment entry path. +@param aServerEntry Pointer to the message store attachment entry for the CAF session. +@param aStartFile An open File handle for attachment write. Caller can close following this call. +@pre PrepareProcessingL() must be called prior to this call +@pre aServerEntry aServerEntry is set to the attachment entry id and remains set there for the duration of the CAF session +@panic imut 36 +*/ +EXPORT_C void CImCaf::StartProcessing(const TDesC& aDefaultAttachmentFileName,const TDesC& aAttachmentFilePath,CMsvServerEntry& aServerEntry,RFile& aStartFile) + { + __ASSERT_DEBUG(iSupplier != NULL && iImportFile != NULL && iContentType == NULL && iMetaData == NULL && aStartFile.SubSessionHandle() != 0, gPanic(EPanicInvalidCafState)); + iDefaultAttachmentFileName.Set(aDefaultAttachmentFileName); + iAttachmentFilePath = aAttachmentFilePath; + iServerEntry = &aServerEntry; + iAttachmentFile.Duplicate(aStartFile); // Caller can close their copy + } + +/** +Terminate CAF session. +Check the attachments under the attachment entry and see if there are any rights receipts. +Set the mime type to a unique CAF one if there are. +@panic imut 36 +@pre iServerEntry set to the attachment entry which owns the attachment +*/ +EXPORT_C void CImCaf::EndProcessingL() + { + __ASSERT_DEBUG(iImportFile != NULL, gPanic(EPanicInvalidCafState)); + WriteDataCompleteL(); + CMsvStore* store = iServerEntry->EditStoreL(); + CleanupStack::PushL(store); + // Loop through the attachments under the current attachment entry + for(TInt i=0;iAttachmentManagerL().AttachmentCount();i++) + { + CMsvAttachment* attachment = store->AttachmentManagerL().GetAttachmentInfoL(i); + // Loop through the output files and see if there's a receipt that matches the attachment + CleanupStack::PushL(attachment); + TInt j; + for(j=0;jOutputFileCountL();j++) + { + CSupplierOutputFile& outputFile = iImportFile->OutputFileL(j); + if(outputFile.OutputType() == EReceipt) + { + if(attachment->FilePath().MatchF(outputFile.FileName()) != KErrNotFound) + { + // Matched so write the Receipt indicator to the attachment and save it + // ModifyAttachmentInfoL() takes ownership of the CMsvAttachment + attachment->SetMimeTypeL(KEpocMimeTypeDrm); + store->AttachmentManagerExtensionsL().ModifyAttachmentInfoL(attachment); + CleanupStack::Pop(attachment); + break; + } + } + } + // If loop did not break then there was no match so destroy. + if(j==iImportFile->OutputFileCountL()) + { + CleanupStack::PopAndDestroy(attachment); + } + } + store->CommitL(); + CleanupStack::PopAndDestroy(store); + + delete iImportFile; + iImportFile = NULL; + } + +/** +Attempt to register a mime content-type with a CAF agent. +Success can be checked by a subsequent call to Registered(). +@param aMimeLine MIME Content-Type for possible interest by a CAF agent. For example - application/vnd.oma.drm.rights+xml +@panic imut 36 +*/ +EXPORT_C void CImCaf::RegisterL(const TDesC8& aMimeLine) + { + // There's no nested CAF registration so iContentType instance should always be NULL + __ASSERT_DEBUG(iContentType == NULL, gPanic(EPanicInvalidCafState)); + if(!iSupplier) + { + iSupplier = CSupplier::NewL(); + } + if(iSupplier->IsImportSupported(aMimeLine)) + { + // CAF agent interested in the content-type + // Use this buffer for storing the content-type. + // We could extract it from the caf metadata later but this will be quicker + iContentType = aMimeLine.AllocL(); + // REMOVE when CAF fixed + iContentType->Des().LowerCase(); + // Construct the metadata array here but add to it later + // Could contain stuff like "X-Oma-Drm-Separate-Delivery: 12" + if(!iMetaData) + { + iMetaData = CMetaDataArray::NewL(); + } + } + } + +/** +Free resources allocated during CAF session +@pre RegisterL() must be called prior to this call. +@panic imut 36 +*/ +EXPORT_C void CImCaf::Deregister() + { + __ASSERT_DEBUG(Registered(), gPanic(EPanicInvalidCafState)); + // Don't delete iSupplier. This is kept open to prevent reloading of DLLs + delete iContentType; + iContentType = NULL; + delete iMetaData; + iMetaData = NULL; + } + +/** +Check whether RegisterL() succeeded. +@return Registered status of the CAF instance. +*/ +EXPORT_C TBool CImCaf::Registered() const + { + return iContentType != NULL; + } + +/** +Retrieve processing status for the CAF session. +@return Processing status of the CAF instance +*/ +EXPORT_C TBool CImCaf::Processing() const + { + return iImportFile != NULL; + } + +//Tell the CAF agent this that the last data for the CAF session has been written. +void CImCaf::WriteDataCompleteL() + { + TInt err(KErrNone); + // Expect KErrNone or KErrCANewFileHandleRequired + err = iImportFile->WriteDataComplete(); + __ASSERT_DEBUG(err == KErrNone || err == KErrCANewFileHandleRequired,gPanic(EPanicInvalidCafState)); + if(err == KErrCANewFileHandleRequired) + { + // CAF framework requires another file handle to finish the session + NewFileL(); + } + else + { + User::LeaveIfError(err); + } + } + +// Gets an output file for continued write by the framework. +// To be called when CAF framework has returned KErrCANewFileHandleRequired +// from a WriteData() or WriteDataComplete() call. +// Creates 1-N new CAF files under the session attachment entry +void CImCaf::NewFileL() + { + // Our iServerEntry will be referencing the current session attachment entry + CMsvStore* store = iServerEntry->EditStoreL(); + CleanupStack::PushL(store); + + TInt err(KErrNone); + TFileName filename; + // Loop creating files until success or error + do + { + // See if the framework can suggest a filename + // Failing that see if it can suggest an extension + // Use the default filename if we have to + if((err = iImportFile->GetSuggestedOutputFileName(filename)) == KErrUnknown) + { + TFileName ext; + filename = iDefaultAttachmentFileName; + if(iImportFile->GetSuggestedOutputFileExtension(ext) == KErrNone) + { + filename.Append(ext); + } + } + __ASSERT_ALWAYS(err == KErrUnknown || err == KErrNone,gPanic(EPanicInvalidCafState)); + CMsvAttachment* attachment = CMsvAttachment::NewL(CMsvAttachment::EMsvFile); + // Ok to use stack file handle because agent clones it + RFile file; + store->CreateShareProtectedAttachmentL(filename,file,attachment); + err = iImportFile->ContinueWithNewOutputFile(file,attachment->FilePath()); + __ASSERT_ALWAYS(err == KErrNone || err == KErrCANewFileHandleRequired,gPanic(EPanicInvalidCafState)); + file.Close(); // Framework clones the file handle so close it + }while (err == KErrCANewFileHandleRequired); + store->CommitL(); + CleanupStack::PopAndDestroy(store); + }