diff -r 000000000000 -r d0791faffa3f obex/obexprotocol/obex/src/obexobjects.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/obex/obexprotocol/obex/src/obexobjects.cpp Tue Feb 02 01:11:40 2010 +0200 @@ -0,0 +1,1271 @@ +// 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: +// + +/** + @file + @internalComponent +*/ + +#include +#include +#include "logger.h" +#include "OBEXUTIL.H" +#include "obexasyncfilewriter.h" +#include "obexsyncfilewriter.h" +#include "obexfaults.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, "OBEX"); +#endif + +/** Creates a new CObexFileObject object. +Static file object factory. returns a new CObexFileObject, set up to use a +temporary file to store received data into "on the fly". If used for +sourcing an object to send a "NULL" object(body length 0) will be sent. +@return Instance of CObexFileObject + +@publishedAll +@released +*/ +EXPORT_C CObexFileObject* CObexFileObject::NewL() + { + LOG_LINE + LOG_STATIC_FUNC_ENTRY + + CObexFileObject* self = new(ELeave) CObexFileObject; + CleanupStack::PushL(self); + self->ConstructL(TPtrC(NULL, 0)); + CleanupStack::Pop(); + return(self); + } + +/** Creates a new CObexFileObject object. +Static file object factory. returns a new CObexFileObject, set up to use +aDataFile as its data file, or will create a temp file if aDataFile is not +a valid file name. If used for sourcing an object to send, and no valid data +file is set, a "NULL" object(body length 0) will be sent. +@param aDataFile Filename to configure this object to use +@return Instance of CObexFileObject + +@publishedAll +@released +*/ +EXPORT_C CObexFileObject* CObexFileObject::NewL(const TDesC &aDataFile) + { + LOG_LINE + LOG_STATIC_FUNC_ENTRY + + CObexFileObject* self = new(ELeave) CObexFileObject; + CleanupStack::PushL(self); + self->ConstructL(aDataFile); + CleanupStack::Pop(); + return(self); + } + +/** Destructor. + +@publishedAll +@released +*/ +EXPORT_C CObexFileObject::~CObexFileObject() + { + LOG_LINE + LOG_FUNC + + ResetData(); + iFs.Close(); + } + +/** +Sets the name of a file which holds the data to be used as the objects body. +Leaves if the file does not exist, or can not be opened for writing. + +@param aDesc Filename to configure this object to use +*/ +void CObexFileObject::SetDataFileL(const TDesC& aDesc)// does this need to be exported???? + { + ResetData(); + if(aDesc.Length() == 0) + return; + TInt err = iDataFile.SetNoWild(aDesc, NULL, NULL); + if(err == KErrNone) //try and open for read/write + err = iFile.Open(iFs, iDataFile.FullName(), EFileWrite | EFileShareExclusive); + if(err != KErrNone) + { //can't open it for write so open it for read + err = iFile.Open(iFs, iDataFile.FullName(), EFileRead | EFileShareReadersOnly); + if(err != KErrNone) + { + iDataFile.SetNoWild(KNullDesC, NULL, NULL); + iFile.Close(); // Manually close file + User::Leave(err); + } + } + } + +/** +Get the name of the file representing the object body. + +@return the full path and name of the file representing the object body. + Null descriptor if no valid data file has been set, or if Reset() + has been called on the object since a file was last set. +*/ +const TDesC& CObexFileObject::DataFile() + { + return iDataFile.FullName(); + } + +TInt CObexFileObject::RenameFile(const TDesC& aDesC) + { + iFile.SetAtt(0,KEntryAttHidden);// - dont check return code - if it fails it fails + return(iFile.Rename(aDesC)); + } + +void CObexFileObject::SetTempFilePath(const TDesC& aPath) + { + iTempFilePath = aPath; + } + +void CObexFileObject::QueryTempFilePath(TDes& aPath) + { + aPath = iTempFilePath; + } + +/** +Constructs this object. + +@param aDataFile The file to use for the object's data part. +*/ +void CObexFileObject::ConstructL(const TDesC &aDataFile) + { + CreateHeaderStorageDataL(); + LEAVEIFERRORL(iFs.Connect()); + SetDataFileL(aDataFile); + iTempFilePath = KNullDesC; + } + +/** Initialises this object from the specified file. + +The function attempts to set attribute values for the object as follows: + +Length:set to the length of the file + +Name:taken from the name portion of the path in aFile + +Time:taken from the modification time of the file + +Type:set appropriately if the file extension is .vcf (VCard), .vcs (Vcalendar), +or .txt. + +@param aFile Body data file + +@publishedAll +@released +*/ +EXPORT_C void CObexFileObject::InitFromFileL(const TDesC& aFile) + { + LOG_LINE + LOG_FUNC + + Reset(); + SetDataFileL(aFile); + + + SetNameL(iDataFile.NameAndExt()); + GuessTypeFromExtL(iDataFile.Ext()); + + TInt length; + if(iFile.Size(length) == KErrNone) + { + SetLengthL(length); + } + + TTime time; + if(iFile.Modified(time) == KErrNone) + { + SetUtcTimeL(time); + } + } + + +/** +Virtual pure function form the base object. Tries to fill aDes with data +starting from aPos byte offset. returns null descriptor if no data file is +set. +@param aPos Position (reliative to start of object) to start extracting data from +@param aDes Descriptor to fill +*/ +void CObexFileObject::GetData(TInt aPos, TDes8& aDes) + { + if(iDataFile.NameOrExtPresent() && iFile.Read(aPos, aDes) == KErrNone) + return; + aDes.SetLength(0); + } + +/** +Virtual pure function overload. inserts aDes into the data file at location +aPos +@param aPos Position (reliative to start of object) to start inserting data from +@param aDes Descriptor to insert +*/ +void CObexFileObject::NewData(TInt aPos, TDes8& aDes) + { +// iTempFilePath.SetLength(0); + if(!iDataFile.NameOrExtPresent()) + { + ResetData(); + TFileName fname; + TInt err = iFile.Temp(iFs, iTempFilePath, fname, EFileWrite | EFileShareExclusive); + if(err == KErrNone) + err = iFs.Parse(fname, iDataFile.Path(), iDataFile); + if(err != KErrNone) + { + ResetData(); + iFs.Delete(fname); + aDes.SetLength(0); + return; + } + iFile.SetAtt(KEntryAttHidden,0);// dont check return code - if it fails it fails + } + if(iFile.Write(aPos, aDes) != KErrNone) + aDes.SetLength(0); + } + +/** +@return number of bytes in the data file (0 if no file is set) +*/ +TInt CObexFileObject::DataSize() + { + if(!iDataFile.NameOrExtPresent()) + return(0); + TInt size = 0; + iFile.Size(size); + return(size); + } + +/** +Set object back to a null file. +*/ +void CObexFileObject::ResetData() + { + iFile.Close(); + iDataFile.Set(KNullDesC, NULL, NULL); + } + +// +// class CObexBufObject +// + +/** +Allocates and constructs a new OBEX dynamic buffer object, specifying a buffer. + +@param aDataBuf The buffer for the body of the object. This must be set either + by this constructor or by calling SetDataBufL() before using the object. +@return New OBEX dynamic buffer object + +@publishedAll +@released +*/ +EXPORT_C CObexBufObject* CObexBufObject::NewL(CBufBase* aDataBuf) + { + LOG_LINE + LOG_STATIC_FUNC_ENTRY + + CObexBufObject* self = new(ELeave) CObexBufObject; + CleanupStack::PushL(self); + self->ConstructL(aDataBuf); + CleanupStack::Pop(); + return(self); + } + +/** Destructor. + +@publishedAll +@released +*/ +EXPORT_C CObexBufObject::~CObexBufObject() + { + LOG_LINE + LOG_FUNC + + delete iWriter; + + CloseDataFile(); + CloseFileServer(); + + delete iFilename; + + delete iDoubleBuf; + } + +/** Writes contents of object to a file +@param aFileName Target file +@return a Symbian OS error code if file write fails. + +@publishedAll +@released +*/ +EXPORT_C TInt CObexBufObject::WriteToFile(const TPtrC& aFileName) + { + LOG_LINE + LOG_FUNC + + TInt ret = KErrNone; + TRAP(ret, CopyFileL(aFileName)); + return ret; + } + + +NONSHARABLE_CLASS(TFileDetails) + { +public: + TFileDetails(RFile& aFile, RFs& aFs, const TDesC& aFilename); + inline RFile* File(); + inline RFs* FileServ(); + inline const TDesC* Filename(); + +private: + RFile* iFile; + RFs* iFileServ; + const TDesC* iFilename; + }; + + +TFileDetails::TFileDetails(RFile& aFile, RFs& aFs, const TDesC& aFilename) + : iFile(&aFile), iFileServ(&aFs), iFilename(&aFilename) + {} + +RFile* TFileDetails::File() + { return iFile; } + +RFs* TFileDetails::FileServ() + { return iFileServ; } + +const TDesC* TFileDetails::Filename() + { return iFilename; } + + +void DoCloseDeleteFile(TAny* aAny) +// This function does not check for errors. Since we're closing down, not much +// that we could do! + { + TFileDetails* fileDetails = reinterpret_cast(aAny); + if (fileDetails) + { + fileDetails->File()->Close(); + fileDetails->FileServ()->Delete(*(fileDetails->Filename())); + } + } + + +// Writes object data to specified file. Called from WriteToFile, exists to simplify +// error handling as this function can leave. Leaves are trapped in the caller. +void CObexBufObject::CopyFileL(const TDesC& aFilename) + { + // Open persistent connection to fileserver if don't currently + // have one + LEAVEIFERRORL(OpenFileServer()); + + RFile writeFile; + LEAVEIFERRORL(writeFile.Create(*iFileServ, aFilename, EFileWrite | EFileShareExclusive)); + + TFileDetails writeFileDetails(writeFile, *iFileServ, aFilename); + CleanupStack::PushL(TCleanupItem(DoCloseDeleteFile, &writeFileDetails)); + + // Now have three situations to worry about. May have a file, in which + // case we need to copy data to the target file. If there's a buffer for + // writes to this file, need to flush it first. + // Then may be using a memory buffer, in which case we just need to save + // the data. + if (iFile) + { + if (iBuf) + { + LEAVEIFERRORL(WriteBufferToFile(ETrue)); + iBufOffset += iBuffered; + } + + TInt dataSize = DataSize(); + TInt bufSize = Min(dataSize, 1024); + // Expands to TInt bufSize = (dataSize > 1024) ? 1024 : dataSize; + HBufC8* buffer = HBufC8::NewMaxLC(bufSize); + + TInt written = 0; + TPtr8 ptr = buffer->Des(); + + while (written < dataSize) + { + LEAVEIFERRORL(iFile->Read(written, ptr)); + LEAVEIFERRORL(writeFile.Write(written, ptr)); + written += ptr.Length(); + } + + CleanupStack::PopAndDestroy(buffer); + } + else + { + TInt segmentSize = iBuf->Ptr(0).Size(); + TInt written = 0; + while (written < BytesReceived()) + { + LEAVEIFERRORL(writeFile.Write(written, iBuf->Ptr(written))); + written += segmentSize; + } + } + + CleanupStack::Pop(); // file + writeFile.Close(); + } + + +/** +Build a TObexBufferingDetails object. +@param aBuffer The CBufBase derived object for Obex to use as a data store. + This object will be resized as appropriate to hold the entire Obex object. + +@publishedAll +@released +*/ +EXPORT_C TObexBufferingDetails::TObexBufferingDetails(CBufBase& aBuffer) + : iVersion(EBasicBuffer), iBuffer(&aBuffer) + { + LOG_LINE + LOG_FUNC + } + + +/** +Build a TObexBufferingDetails object, setting the version appropriately. +@param aVersion Version number to insert. +@param aBuffer The buffer object to use. + +@internalComponent +*/ +TObexBufferingDetails::TObexBufferingDetails(TVersion aVersion, CBufBase* aBuffer) + : iVersion(aVersion), iBuffer(aBuffer) + { + __ASSERT_DEBUG(aVersion < ELastVersion, IrOBEXUtil::Fault(EBadBufferDetailsVersion)); + } + +/** +Return the version of this object +@internalComponent +*/ +TObexBufferingDetails::TVersion TObexBufferingDetails::Version() + { + return iVersion; + } + + +/** +Basic getter. +@internalComponent +*/ +CBufBase* TObexBufferingDetails::Buffer() + { + return iBuffer; + } + + +/** +Build a variant of TObexBufferingDetails which instructs the CObexBufObject +to use a file as the only data store. This is a special case option provided +to cater for the MObexServerNotify interface which requires the use of +CObexBufObject objects. It is generally better to use a buffered variant. +If the file cannot be opened for read/write access it will be opened in read +only mode. In this situation, attempts to store data in this object will cause +an Obex error to be signalled in response to the Obex packet which carried the +body data. + +@param aFilename The file to link the object to. + +@publishedAll +@released +*/ +EXPORT_C TObexPureFileBuffer::TObexPureFileBuffer(const TPtrC& aFilename) + : TObexBufferingDetails(EPureFile, NULL), iFilename(aFilename) + { + LOG_LINE + LOG_FUNC + } + + +/** +Basic getter. +@internalComponent +*/ +const TPtrC& TObexPureFileBuffer::Filename() + { + return iFilename; + } + + +/** +Build a variant of TObexBufferingDetails which instructs the CObexBufObject +to use a file as the main data store, buffering writes to this in chunks. +Writes are buffered into the supplied CBufBase derived object, which is not +resized. Once it is full, the data contained is written to file. +Double buffering can be specified by setting aBufferingStrategy appropriately. +If the file cannot be opened for read/write access it will be opened in read +only mode. In this situation, attempts to store data in this object will cause +an Obex error to be signalled in response to the Obex packet which carried the +body data. + +@param aBuffer The buffer to use as a temporary store. This is ignored when + reading from the file. +@param aFilename The filename to use to permanently store the object. +@param aBufferingStrategy Use double or single buffering. +@publishedAll +@released +*/ +EXPORT_C TObexFilenameBackedBuffer::TObexFilenameBackedBuffer(CBufBase& aBuffer, const TPtrC& aFilename, CObexBufObject::TFileBuffering aBufferingStrategy) + : TObexBufferingDetails(EFilenameBackedBuffer, &aBuffer), + iFilename(aFilename), + iBufferingStrategy(aBufferingStrategy) + { + LOG_LINE + LOG_FUNC + } + + +/** +Basic getter. +@internalComponent +*/ +const TPtrC& TObexFilenameBackedBuffer::Filename() + { + return iFilename; + } + + +/** +Basic getter. +@internalComponent +*/ +CObexBufObject::TFileBuffering TObexFilenameBackedBuffer::Strategy() + { + return iBufferingStrategy; + } + + +/** +Build a variant of TObexBufferingDetails which instructs the CObexBufObject +to use a file as the main data store, buffering writes to this in chunks. +Writes are buffered into the supplied CBufBase derived object, which is not +resized. Once it is full, the data contained is written to file. +Double buffering can be specified by setting aBufferingStrategy appropriately. +If the file is opened in read only mode, attempts to store data in this object +will cause an Obex error to be signalled in response to the Obex packet which +body data. + +@param aBuffer The buffer to use as a temporary store. This is ignored when + reading from the file. +@param aFile An RFile object pointing to the file, opened in an appropriate + access mode.Note, Obex is responsible for closing the file, and a panic will + result if an attempt is made to close the file from outside of Obex. +@param aBufferingStrategy Use double or single buffering. +@publishedAll +@released +*/ +EXPORT_C TObexRFileBackedBuffer::TObexRFileBackedBuffer(CBufBase& aBuffer, RFile aFile, CObexBufObject::TFileBuffering aBufferingStrategy) + : TObexBufferingDetails(ERFileBackedBuffer, &aBuffer), + iFile(aFile), + iBufferingStrategy(aBufferingStrategy) + { + LOG_LINE + LOG_FUNC + } + + +/** +Basic getter. +@internalComponent +*/ +RFile TObexRFileBackedBuffer::File() + { + return iFile; + } + + +/** +Basic getter. +@internalComponent +*/ +CObexBufObject::TFileBuffering TObexRFileBackedBuffer::Strategy() + { + return iBufferingStrategy; + } + + +/** +Set the data buffers as specified in the supplied TObexBufferingDetails object. +@param aDetails The buffering techniques to use. This only has to persist + over the duration of the call to SetDataBufL, once this has returned it can + be allowed to go out of scope. + +@panic Obex ENullFileHandle TObexPanicCode::ENullFileHandle The RFile object does not point +to a valid (open) file. +@panic Obex EEmptyBuffer TObexPanicCode::EEmptyBuffer The supplied buffer is of zero length. +@panic Obex EInvalidBufferDetails TObexPanicCode::EInvalidBufferDetails An unknown TObexBufferingDetails +object was supplied +@panic Obex EInvalidBufferStrategy TObexPanicCode::EInvalidBufferStrategy An unknown TFileBuffering +value was supplied. + +@publishedAll +@released +*/ +EXPORT_C void CObexBufObject::SetDataBufL(TObexBufferingDetails& aDetails) + { + LOG_LINE + LOG_FUNC + + PrepareToSetBufferL(); + + iBuf = aDetails.Buffer(); + if (iBuf) + { + iBufSegSize = iBuf->Ptr(0).Size(); + } + + TBool initFile = EFalse; + TBool initFileServer = EFalse; + TBool initFileWriter = EFalse; + TFileBuffering bufferingStrategy = ESingleBuffering; + + switch (aDetails.Version()) + { + case TObexBufferingDetails::EBasicBuffer: + { + // All required details already set. + break; + } + + case TObexBufferingDetails::EPureFile: + { + TObexPureFileBuffer& detail = static_cast(aDetails); + iFilename = detail.Filename().AllocL(); + + initFileServer = ETrue; + initFile = ETrue; + break; + } + + case TObexBufferingDetails::EFilenameBackedBuffer: + { + __ASSERT_ALWAYS(iBuf, IrOBEXUtil::Panic(ENullPointer)); + __ASSERT_ALWAYS(iBuf->Size(), IrOBEXUtil::Panic(EEmptyBuffer)); + + TObexFilenameBackedBuffer& detail = static_cast(aDetails); + + iFilename = detail.Filename().AllocL(); + bufferingStrategy = detail.Strategy(); + + initFileServer = ETrue; + initFile = ETrue; + initFileWriter = ETrue; + break; + } + + case TObexBufferingDetails::ERFileBackedBuffer: + { + __ASSERT_ALWAYS(iBuf, IrOBEXUtil::Panic(ENullPointer)); + __ASSERT_ALWAYS(iBuf->Size(), IrOBEXUtil::Panic(EEmptyBuffer)); + + TObexRFileBackedBuffer& detail = static_cast(aDetails); + __ASSERT_ALWAYS(detail.File().SubSessionHandle(), IrOBEXUtil::Panic(ENullFileHandle)); + + iFile = new(ELeave) RFile(detail.File()); + bufferingStrategy = detail.Strategy(); + + initFileServer = ETrue; + initFileWriter = ETrue; + break; + } + + default: + { + IrOBEXUtil::Panic(EInvalidBufferDetails); + } + } + + if (initFileServer) + { + LEAVEIFERRORL(OpenFileServer()); + } + + if (initFile) + { + LEAVEIFERRORL(OpenDataFile(*iFilename)); + } + + if (initFileWriter) + { + switch (bufferingStrategy) + { + case ESingleBuffering: + iWriter = CObexSyncFileWriter::NewL(*iFile); + break; + + case EDoubleBuffering: + iWriter = CObexAsyncFileWriter::NewL(*iFile); + iDoubleBuf = CBufFlat::NewL(iBufSegSize); + iDoubleBuf->ResizeL(iBufSegSize); + break; + + default: + IrOBEXUtil::Panic(EInvalidBufferStrategy); + break; + } + } + } + + +/** +Delete all owned resources in preparation for getting new settings. +@internalComponent +*/ +void CObexBufObject::PrepareToSetBufferL() + { + // Flush file buffer, if any. + if (iFile && iBuf) + { + LEAVEIFERRORL(WriteBufferToFile(ETrue)); + iBufOffset = 0; + } + + // The writer must be deleted at the same time as or before the file + // otherwise the writer will have an invalid file handle + delete iWriter; + iWriter = NULL; + + CloseDataFile(); + + delete iFilename; + iFilename = NULL; + + delete iDoubleBuf; + iDoubleBuf = NULL; + + iBuf = NULL; + } + + +/** +Sets a buffer to use the object body data. + +Note that the function can leave. + +@param aDataBuf The buffer for the body of the object. +@panic Obex ENullPointer TObexPanicCode::ENullPointer A NULL value was supplied for the +data buffer. + +@publishedAll +@deprecated +*/ +EXPORT_C void CObexBufObject::SetDataBufL(CBufBase* aDataBuf) + { + LOG_LINE + LOG_FUNC + + __ASSERT_ALWAYS(aDataBuf, IrOBEXUtil::Panic(ENullPointer)); + TObexBufferingDetails details(*aDataBuf); + SetDataBufL(details); + } + + +/** +Set object to use aFilename as its data area. Leaves if unable to open file. + +@param aFilename The filename to link the object to. + +@publishedAll +@deprecated +*/ +EXPORT_C void CObexBufObject::SetDataBufL(const TPtrC& aFilename) + { + LOG_LINE + LOG_FUNC + + TObexPureFileBuffer details(aFilename); + SetDataBufL(details); + } + + +/** +Set object to use aFilename as its data area. Leaves if unable to open file. +Buffers data into aDataBuf before writing to file. Will not grow the memory buffer, +so user can tune buffering behaviour when calling function. + +@param aFilename The filename to link the object to. +@param aDataBuf The buffer for the body of the object. +@panic Obex ENullPointer TObexPanicCode::ENullPointer A NULL value was supplied for the +data buffer. +@panic Obex EEmptyBuffer TObexPanicCode::EEmptyBuffer The supplied buffer is of zero length. + +@publishedAll +@deprecated +*/ +EXPORT_C void CObexBufObject::SetDataBufL(const TPtrC& aFilename, CBufBase* aDataBuf) + { + LOG_LINE + LOG_FUNC + + __ASSERT_ALWAYS(aDataBuf, IrOBEXUtil::Panic(ENullPointer)); + TObexFilenameBackedBuffer details(*aDataBuf, aFilename, ESingleBuffering); + SetDataBufL(details); + } + + +/** +Set object to write to file, using buffering and the specified +buffering strategy. Note the size of the buffer passed to this +function will determine the size of the second buffer if double +buffering is employed. + +@param aFilename The file to link the object to. +@param aDataBuf A buffer to use. +@param aBufferingStrategy The buffering strategy to employ. +@panic Obex EEmptyBuffer TObexPanicCode::EEmptyBuffer The supplied buffer is of zero length. +@panic Obex EInvalidBufferStrategy TObexPanicCode::EInvalidBufferStrategy An unknown TFileBuffering + +@publishedAll +@deprecated +*/ +EXPORT_C void CObexBufObject::SetDataBufL(const TPtrC& aFilename, CBufBase& aDataBuf, const TFileBuffering aBufferingStrategy) + { + LOG_LINE + LOG_FUNC + + TObexFilenameBackedBuffer details(aDataBuf, aFilename, aBufferingStrategy); + SetDataBufL(details); + } + + +/** +Gets the buffer. + +@return The buffer for the body of the object. + +@publishedAll +@released +*/ +EXPORT_C CBufBase* CObexBufObject::DataBuf() + { + LOG_LINE + LOG_FUNC + + return(iBuf); + } + + +/** +Returns a pointer to the HBuf holding the filename this object is using. +May return a null pointer. +@return iFilename The file name. +@internalComponent +*/ +HBufC* CObexBufObject::FileName() + { + return(iFilename); + } + + +CObexBufObject::CObexBufObject() + { + } + +void CObexBufObject::ConstructL(CBufBase* aDataBuf) + { + CreateHeaderStorageDataL(); + if (aDataBuf) + SetDataBufL(aDataBuf); + } + +/** +Reads aDes (up to MaxLength) from aPos offset into the buffer + +@param aPos The offset into the buffer to read from +@param aDes The descriptor to read from +*/ +void CObexBufObject::GetData(TInt aPos, TDes8& aDes) + { + __ASSERT_ALWAYS((iBuf || iFile), IrOBEXUtil::Panic(ENullPointer)); + + if (iFile) + { + GetFileData(aPos, aDes); + } + else + { + iBuf->Read(aPos, aDes); + } + } + +void CObexBufObject::GetFileData(TInt aPos, TDes8& aDes) + { + if(iFile->Read(aPos, aDes) == KErrNone) + return; + aDes.SetLength(0); + } + +/** +Writes aDes into the buffer at aPos offset, growing the buffer if necessary +*/ +void CObexBufObject::NewData(TInt aPos, TDes8& aDes) + { + // Three possible cases here. Can either be receiving into a memory + // buffer (old behaviour), receiving directly into a file or buffering + // file writes into a fixed size buffer. + + // Should always have at least one of iBuf or iFile set. + __ASSERT_ALWAYS((iBuf || iFile), IrOBEXUtil::Panic(ENullPointer)); + + if (iFile) + { + TInt err = NewFileData(aPos, aDes); + if (err != KErrNone) + { + LOG1(_L8("Couldn't write data to file (error %d)"), err); + aDes.SetLength(0); + return; + } + } + else + { + if(iBuf->Size() < aPos + aDes.Size()) + {// Buffer needs to grow. Try to guess how big it needs to be. + TInt reqsz = aPos + aDes.Size(); + if((TInt)Length() > reqsz) + reqsz = Length(); + TRAPD(err, iBuf->ResizeL(reqsz)); + if(err != KErrNone) + {// OOM, probably. + LOG2(_L8("Couldn't resize buffer object to %d bytes (error %d)"), + reqsz, err); + iBuf->Compress(); + aDes.SetLength(0); + return; + } + } + iBuf->Write(aPos, aDes); + } + } + + +/** +Write new data into a file + +We write data out to the file in chunks of iBufSegSize, +the (segment) size of the buffer. A packet of data is +passed to this method in aDes. aDes may or may not take +us past the the end of the buffer. If it does, then we +write out the buffer then continue filling it up with +the rest of aDes. We repeat this until we have used up +all of aDes. iBufOffset is the last write position in +the file. remaining is the amount of data in aDes yet to +be processed, written is the amount of data in aDes that +has been processed. iBuffered is the amount of data in +iBuf and spare is the amount of free space in the buffer. + +@code + aDes.Length() + <----------------------> + ------------------------------------------------------------------------ +| ... |xxxxxxxxxxxx|\\\\\\\\\\|//|*************| ... | +| ... |xxxxxxxxxxxx|\\\\\\\\\\|//|*************| ... | + ------------------------------------------------------------------------ +0 ... iBufOffset aPos ... this.Length() + + written remaining + <---------><-----------> + + iBuffered spare + <----------------------><------------------------> + + iBufSegSize + <------------------------------------------------> + + Key: + xxx = data already in buffer + \\\ = processed data from aDes, copied into iBuf + // = unprocessed data from aDes, not yet copied into iBuf + *** = free space in iBuf +@endcode + +If there is more data remaining than there is spare space in +the buffer, then the buffer is filled and written to disk and +the remainer of the data is then used to continue filling the +buffer. + +@param aPos The position at which aDes is to be written into the file. +@param aDes The data to write to the file. +@return Symbian OS error code +*/ +TInt CObexBufObject::NewFileData(TInt aPos, TDes8& aDes) + { + // We have a memory buffer to (hopefully) speed file writes. + if (iBuf) + { + TInt err = KErrNone; + + // If moving to earlier position in object, write out buffer + if (iBuffered && (aPos < iBufOffset)) + { + err = WriteBufferToFile(EFalse); + if (err) return err; + } + + TInt written = 0; + TInt remaining; + + // Calculate the amount of data still to be processed and + // continue whilst there is still data to process + while ((remaining = (aDes.Length() - written)) > 0) + { + // Buffer full, write to file + if (iBuffered == iBufSegSize) + { + err = WriteBufferToFile(EFalse); + if (err) return err; + } + + // Buffer empty, update buffer base + if (iBuffered == 0) + { + iBufOffset = aPos + written; + } + + // Calculate the remaining space in the buffer (spare) and + // hence the amount of data we can process (length) + TInt spare = iBufSegSize - iBuffered; + TInt length = (spare > remaining) ? remaining : spare; + + // Copy amount of data to be procesed (length) from the + // unprocessed portion of the packet (aDes.Right(remaining)) + // into the buffer. + iBuf->Write(iBuffered, aDes.Right(remaining), length); + + // Update variables to reflect newly processed data + written += length; + iBuffered += length; + } + + // Is this the final packet? + const TBool finalPacket = (ValidHeaders() & KObexHdrEndOfBody); + + // Flush buffer to file if we're done and there's data left + if (finalPacket && iBuffered) + { + err = WriteBufferToFile(ETrue); + iBufOffset = 0; + + if (err) return err; + } + } + else + // Just write directly to the file + { + return iFile->Write(aPos, aDes); + } + + return KErrNone; + } + + +TInt CObexBufObject::DataSize() + { + __ASSERT_ALWAYS((iBuf || iFile), IrOBEXUtil::Panic(ENullPointer)); + if (iFile) + { + // Flush file buffer, if any. + if (iBuf) + { + (void) WriteBufferToFile(ETrue); + } + // Get file size + TInt size; + iFile->Size(size); + return size; + } + else + { + return (iBuf->Size()); + } + } + +void CObexBufObject::ResetData() + { + __ASSERT_ALWAYS((iBuf || iFile), IrOBEXUtil::Panic(ENullPointer)); + if (iFile) + { + iFile->SetSize(0); + if (iBuf) + { + iBufOffset = 0; + iBuffered = 0; + } + } + else + { + iBuf->Reset(); + } + } + + +TInt CObexBufObject::OpenDataFile(const TDesC& aFilename) + { + TInt err = KErrNotReady; + + if (!iFile) + { + iFile = new RFile; + if (!iFile) return KErrNoMemory; + + //Try and open the file for read/write + err = iFile->Open(*iFileServ, aFilename, EFileWrite | EFileShareExclusive); + if (err != KErrNone) + { + //Try and open file for read only + err = iFile->Open(*iFileServ, aFilename, EFileRead | EFileShareReadersOnly); + if(err == KErrNotFound) + { + err = iFile->Create(*iFileServ, aFilename, EFileWrite | EFileShareExclusive); + } + } + + if (err) + { + delete iFile; + iFile = 0; + } + } + + return err; + } + + +void CObexBufObject::CloseDataFile() + { + if (iFile) + { + iFile->Close(); + delete iFile; + iFile = 0; + } + } + +TInt CObexBufObject::OpenFileServer() + { + TInt err = KErrNone; + + if (!iFileServ) + { + iFileServ = new RFs; + if (!iFileServ) return KErrNoMemory; + + err = iFileServ->Connect(); + if (err) + { + delete iFileServ; + iFileServ = 0; + } + } + return err; + } + +void CObexBufObject::CloseFileServer() + { + if (iFileServ) + { + iFileServ->Close(); + delete iFileServ; + iFileServ = 0; + } + } + +TInt CObexBufObject::WriteBufferToFile(TBool aFinal) + { + TInt err = KErrNone; + + if (aFinal) + { + err = iWriter->FinalWrite(iBufOffset, iBuf, iBuffered); + } + else + { + err = iWriter->Write(iBufOffset, iBuf); + } + + if (!iBuf && iDoubleBuf) + { + iBuf = iDoubleBuf; + } + + iBuffered = 0; + + return err; + } + +// +// class CObexNullObject +// +void CObexNullObject::ConstructL() + { + CreateHeaderStorageDataL(); + } + +/** Allocates and constructs a new null object. + +@return New null object + +@publishedAll +@released +*/ +EXPORT_C CObexNullObject* CObexNullObject::NewL() + { + LOG_LINE + LOG_STATIC_FUNC_ENTRY + + CObexNullObject* self = new(ELeave) CObexNullObject; + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return(self); + } + +/** +To return "NULL" data, we simply set aDes.Size = 0 +*/ +void CObexNullObject::GetData(TInt /*aPos*/, TDes8& aDes) + { + aDes.SetLength(0); + return; + } + +/** +In order to appear to consume the data, we don't set aDes.Size = 0 +*/ +void CObexNullObject::NewData(TInt /*aPos*/, TDes8& /*aDes*/) + { + return; + } + +TInt CObexNullObject::DataSize() + { + return(0); + } + +void CObexNullObject::ResetData() + { + }