diff -r 630d2f34d719 -r 07a122eea281 fax/faxclientandserver/FAXCLI/FAXCLI.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fax/faxclientandserver/FAXCLI/FAXCLI.CPP Wed Sep 01 12:40:21 2010 +0100 @@ -0,0 +1,1338 @@ +// 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 "CFAX32.H" +#include "dial.h" // we no longer depend on DialStor, so must depend directly on Dial + +#include "FAXLOG.H" +#include "faxsettings.h" + + #include + #include + #include + + using namespace CommsDat; + +TInt FaxClientThread (TAny * session); // function declaration needed here +//TInt FaxConverterThread (TAny *); +//RSemaphore jerry; +/********************************************************************/ + + +/********************************************************************/ + +CFaxTransfer::CFaxTransfer() + : CBase() + {} + +EXPORT_C CFaxTransfer *CFaxTransfer::NewLC (const TFaxSettings & aFaxSettings) +/** Constructs a CFaxTransfer object, which offers the publicly exported +Symbian OS Fax Client API. +As is usual in Symbian OS, the only difference between this function and +NewL() is that this variant pushes the object to the cleanup stack. + +@param aFaxSettings A reference to a TFaxSettings object which contains +persistent information applicable to all fax sessions. +@return Pointer to the newly created object. +@leave KErrNoMemory There is insufficient memory to perform the operation. +@capability None +*/ + { + CFaxTransfer *self = new (ELeave) CFaxTransfer; + CleanupStack::PushL (self); + self->ConstructL (aFaxSettings); + return self; + } + +EXPORT_C CFaxTransfer *CFaxTransfer::NewL (const TFaxSettings & aFaxSettings) +/** Constructs a CFaxTransfer object, which offers the publicly exported +Symbian OS Fax Client API. + +@param aFaxSettings A reference to a TFaxSettings object, which +contains persistent information applicable to all fax sessions. +@return A pointer to the newly created object. +@leave KErrNoMemory There is insufficient memory to perform the operation. +@capability None +*/ + { + CFaxTransfer *self = NewLC (aFaxSettings); + CleanupStack::Pop (); + return self; + } +/********************************************************************/ + +void CFaxTransfer::ConstructL (const TFaxSettings & aFaxSettings) + { + // we now copy the TFaxSettings passed to us - we then + // validate the contents and fill in the DialStor bits. + // if we don't know the modem class we find it our here + iFaxSettings = aFaxSettings; + CFaxSettings *currentsettings; + currentsettings = CFaxSettings::NewL (); // bug fix thanks to MartinA + CleanupStack::PushL (currentsettings); // currentsettings saved + currentsettings->ValidateAndSetClassL (&iFaxSettings); // since this could leave + CleanupStack::PopAndDestroy (); // currentsettings deleted + + // if (((TFaxClass) iFaxSettings.iFaxClass != EClass1) && + // ((TFaxClass) iFaxSettings.iFaxClass != EClass2) && + // ((TFaxClass) iFaxSettings.iFaxClass != EClass2point0)) + // User::Leave (KFaxCannotAutodetect); + + iFaxSessionSettings.iFaxClass = (TFaxClass) iFaxSettings.iFaxClass; + iFaxSessionSettings.iFaxId.Copy (iFaxSettings.iFaxId); + iFaxSessionSettings.iMaxSpeed = iFaxSettings.iMaxSpeed; + iFaxSessionSettings.iMinSpeed = iFaxSettings.iMinSpeed; + iFaxSessionSettings.iRxResolution = iFaxSettings.iPreferredResolution; + iFaxSessionSettings.iRxCompression = iFaxSettings.iPreferredCompression; + iFaxSessionSettings.iPreferredECM = iFaxSettings.iPreferredECM; + iFaxSessionSettings.iFaxOnDemandDelay = iFaxSettings.iFaxOnDemandDelay; + + iSource = CFaxTransferSource::NewL (); + } +/********************************************************************/ + +CFaxTransfer::~CFaxTransfer () +/** Destructor + +Frees all resources owned by the object, prior to its destruction. */ + { + delete iSource; + } +/********************************************************************/ + +// this function updates the TFaxTransferProgress structure for the +// caller. It should be prior to inspection. + +EXPORT_C TInt CFaxTransfer::Progress () +/** Causes ETel to update the fax progress information in RFax::TProgress. + +It should be called prior to displaying the fax progress information. + +@return KErrNone if successful, otherwise another of the system-wide error +codes. +@capability None +*/ +{ + if (iFaxStarted) + { + iFaxClientProgress=EProgressRxTx; + return (iFax.GetProgress (iProgress)); + } + if (iConverting) + { + iFaxClientProgress=EProgressConverting; + //aProgress=EProgressPreparing; + return (KErrNone); + } + + //aProgress=EProgressInitialising; + return (KErrNone); +} +/********************************************************************/ + +// this function tells faxtrans what phone number to dial +// obviously only useful in a dialling mode ! +// the phone number is translated for the modem and service +// and location required using dialstor facilities if they are +// available - if not, the number is used in its raw state +// +// if use of a raw phonenumber is required then the inline function +// SetPhoneNumber should be called instead. + +EXPORT_C void CFaxTransfer::SetPhoneNumberL (TDesC8 & aNumber) +/** Resolves a local telephone number into an international number by +taking account of the current location and country. +The alternative SetPhoneNumber() should be used if the phone number +must be used in its raw state. One of these functions must be called +before CFaxTransfer::Start() when a fax is to be sent. +This function may leave with KErrNoMemory if there is insufficient +memory to perform the operation. The leave code is one of the system +error codes: see system-wide error codes. +The current location and country information is obtained from the +location table in the communications database. +The resolved number is then set as the actual phone number to dial. + +@param aNumber Descriptor containing the phone number to be resolved. +@capability WriteUserData +*/ + { + SetPhoneNumber (aNumber); + TBuf < KMaxDialString > rawPhoneNumber; + rawPhoneNumber.Copy (aNumber); + +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + CMDBSession* db = CMDBSession::NewL(KCDVersion1_2); +#else + CMDBSession* db = CMDBSession::NewL(KCDVersion1_1); +#endif + CleanupStack::PushL(db); + + // Read the currently selected connection preference and find preferred IAP + TInt prefRank = 1; + + CCDConnectionPrefsRecord *connectionPrefs = + static_cast(CCDRecordBase::RecordFactoryL(KCDTIdConnectionPrefsRecord)); + CleanupStack::PushL(connectionPrefs); + connectionPrefs->iRanking = prefRank; + connectionPrefs->iDirection = ECommDbConnectionDirectionOutgoing; + TBool error = connectionPrefs->FindL(*db); + + // The following code is a temporary solution until an issue has been resolved in CommsDat + // start + CCDIAPRecord* tempPIapRecord = + static_cast(CCDRecordBase::RecordFactoryL(KCDTIdIAPRecord)); + tempPIapRecord->SetRecordId(connectionPrefs->iDefaultIAP); + connectionPrefs->iDefaultIAP.iLinkedRecord = tempPIapRecord; + + CCDIAPRecord* pIapRecord = (CCDIAPRecord*)connectionPrefs->iDefaultIAP.iLinkedRecord; + pIapRecord->SetRecordId(connectionPrefs->iDefaultIAP); + pIapRecord->LoadL(*db); + + CCDBearerRecordBase* tempBearerRecord = + static_cast(CCDRecordBase::RecordFactoryL(KCDTIdModemBearerRecord)); + tempBearerRecord->SetRecordId(pIapRecord->iBearer); + pIapRecord->iBearer.iLinkedRecord = tempBearerRecord; + + CCDBearerRecordBase* pBearerRecord = (CCDBearerRecordBase*) pIapRecord->iBearer.iLinkedRecord; + pBearerRecord->SetRecordId(pIapRecord->iBearer); + pBearerRecord->LoadL(*db); + // end + + TUint32 iapId = pBearerRecord->iRecordTag; + + CCDIAPRecord *iapRecord = + static_cast(CCDRecordBase::RecordFactoryL(KCDTIdIAPRecord)); + CleanupStack::PushL(iapRecord); + iapRecord->SetRecordId(iapId); + iapRecord->LoadL(*db); + + // more temporary code + // start + CCDBearerRecordBase* tempLocationRecord = + static_cast(CCDRecordBase::RecordFactoryL(KCDTIdLocationRecord)); + tempLocationRecord->SetRecordId(iapRecord->iLocation); + iapRecord->iLocation.iLinkedRecord = tempLocationRecord; + + CCDLocationRecord* pLocationRecord = (CCDLocationRecord*)iapRecord->iLocation.iLinkedRecord; + pLocationRecord->SetRecordId(iapRecord->iLocation); + pLocationRecord->LoadL(*db); + // end + + //CommsDat Migration: Would this be the correct way to check whether or not a linked record exists: + if (pLocationRecord == NULL) + { + iPhoneNumber.Copy(rawPhoneNumber); + } + else + { + TBuf<32> serviceType; + serviceType.Copy(iapRecord->iServiceType); + + if (!serviceType.Compare(TBuf<32>(DIAL_OUT_ISP))) // Chargecard only valid for dial out ISP + { + // Get service type id + TUint32 serviceId = iapRecord->iService; + + CCDDialOutISPRecord *ispRecord = + static_cast(CCDRecordBase::RecordFactoryL(KCDTIdDialOutISPRecord)); + CleanupStack::PushL(ispRecord); + ispRecord->SetRecordId(serviceId); + ispRecord->LoadL(*db); + TUint32 locationId = 0; + TUint32 chargecardId = 0; + TRAPD(err,CommsDatUtils::CCommsDatUtils::ResolvePhoneNumberL(rawPhoneNumber, iPhoneNumber, TParseMode(EForDialing), locationId, chargecardId)); + // if resolving the phone number fails, use the raw phone number + if (err) + { + iPhoneNumber.Copy(rawPhoneNumber); + } + + CleanupStack::PopAndDestroy(ispRecord); + } + else + { + iPhoneNumber.Copy(rawPhoneNumber); + } + } + CleanupStack::PopAndDestroy(3); // db, connectionPrefs, iapRecord + } +/********************************************************************/ + +// in order to cancel a fax session we set a cancel flag in the lowest +// level (CFaxModem) - if this has not yet been fully created then we set +// a flag in the next level up (CFaxModemDriver) instead, which is always +// going to be there as it is created via CFaxTransfer::ConstructL +// +// because of the way that the CFaxModem monitors its request flag +// we need to avoid multiple cancel requests, so only the first call to +// Cancel has any effect on it + +EXPORT_C void CFaxTransfer::Cancel () +/** Tells the fax engine to cancel the fax session at the first convenient +opportunity. + +The caller should wait for cancellation to complete, which will usually be +signalled by the fax thread's TRequestStatus completing with a KFaxCancelRequested +error code. After the fax thread completes, Stop() should be called in the +normal way. + +The function can be called at any time after the call to Start(). +@capability None +*/ + { + if ((iClientCancel == KRequestPending) || (iClientCancel == KErrNone)) + { + TRequestStatus *cancelClient = &iClientCancel; + iDriverThread.RequestComplete (cancelClient, KErrCancel); + } + } +/********************************************************************/ + +EXPORT_C TInt CFaxTransfer::Start (TRequestStatus & aThreadStat) +/** +Start fax sending or receiving session by launching a separate high priority thread. +A call to Start must be paired with a call to Stop as this is an EPOC32 requirement. + +@param aThreadStat thread logon request status +@return thread creation code +@capability NetworkServices +@capability ReadUserData +@capability WriteUserData +*/ + { + // the heap and stack sizes set here (4K each) are pure guesswork + // CFaxModemDriver has allocated heap space in the parent thread + // for any objects it needs to create after this stage + // stack sizes increased by 512K to allow etel to connect + + TInt state = KErrNone; + TInt heapSize = 0x14000; + TInt stackSize = 0x14000; + + __FLOG_FAXCLI(_L8(" ")); + __FLOG_FAXCLI(_L8("-------------------------- new log --------------------------")); + __FLOG_FAXCLI(_L8("CFaxTransfer::Start, starting FaxClientThread")); + __FLOG_FAXCLI(_L8(" ")); + + state = iDriverThread.Create ((_L ("FaxClientThread")), FaxClientThread, stackSize, heapSize, heapSize, this, EOwnerThread); + if (state) + { + state = KFaxThreadError; + } + else + { + aThreadStat = KRequestPending; + if (iClientCancel != KErrCancel) + iClientCancel = KRequestPending; + iDriverThread.Logon (aThreadStat); + iDriverThread.SetPriority (EPriorityRealTime); + iDriverThread.Resume (); + } + return (state); + } +/*********************************************************************/ + +EXPORT_C void CFaxTransfer::Stop () +/** Kills the fax thread once it has completed. + +Fax threads do not kill themselves, so every successful call to CFaxTransfer::Start() +must be paired with a call to CFaxTransfer::Stop(). +@capability NetworkServices +@capability ReadUserData +@capability WriteUserData +*/ + { + iDriverThread.Close (); + } +/*********************************************************************/ + +// this is a utility function which is the entry to our thread +// it isn't part of any class, but we pass the address +// of our CFaxTransfer in so that we can check the +// session parameter and get back to the required function +// +// All possible leaves should be trapped as the return +// from this function is the TRequestStatus which the +// caller to CFaxTransfer::Start is waiting for. + +TInt FaxClientThread (TAny * session) + { + + + // start of unecessary bureaucracy - error checking left out +/*#define CSY_NAME _L("ECUART") +#define LDD_NAME _L("ECOMM") +#if defined (__WINS__) +#define PDD_NAME _L("ECDRV") +#else +#define PDD_NAME _L("EUART1") +#endif*/ + +/* + User::LoadPhysicalDevice (PDD_NAME); + User::LoadLogicalDevice (LDD_NAME);*/ +// jerry.CreateGlobal(_L("FaxCliSem"),0,EOwnerProcess); + + RCommServ server; + // coverity[check_return] + server.Connect (); +// end of unecessary bureaucracy + + TInt state; + CTrapCleanup *cleanup = CTrapCleanup::New (); +// CFaxTransfer *faxsession = (CFaxTransfer *) session; + CFaxTransfer *faxsession =reinterpret_cast(session); + __FLOG_FAXCLI(_L8("FaxClientThread entering...")); + + state = faxsession->iTelServer.Connect (); + if (state == KErrNone) + { + TBuf tsyName; + TRAP(state,faxsession->GetPhoneModuleNameL(tsyName)); + if (state==KErrNone) + { + state = faxsession->iTelServer.LoadPhoneModule (tsyName); + if (state == KErrNone) + { + RTelServer::TPhoneInfo phoneInfo; + state = faxsession->GetPhoneInfoForTsy(tsyName,phoneInfo); + if (state == KErrNone) + { + + __FLOG_FAXCLI(_L8("FaxClientThread iPhone.Open")); + state = faxsession->iPhone.Open (faxsession->iTelServer, phoneInfo.iName); + if (state == KErrNone) + { + + __FLOG_FAXCLI(_L8("FaxClientThread iLine.Open")); + state = faxsession->iLine.Open (faxsession->iPhone, _L ("Fax")); + if (state == KErrNone) + { + // + // If the call name has been provided, then open the + // existing call, otherwise open a new call. + // + if (faxsession->iCallName.Length() == 0) + { + __FLOG_FAXCLI(_L8("FaxClientThread iCall.OpenNewCall")); + state = faxsession->iCall.OpenNewCall (faxsession->iLine); + } + else + { + __FLOG_FAXCLI(_L8("FaxClientThread iCall.OpenExistingCall")); + state = faxsession->iCall.OpenExistingCall(faxsession->iLine, faxsession->iCallName); + } + + if (state == KErrNone) + { + faxsession->iSource->iPage = 0; + if (faxsession->iMode & KFaxReceive) + { + faxsession->iFaxSessionSettings.iMode = RCall::EReceive; + TRAP (state, faxsession->FaxReceiveL ()); + + __FLOG_FAXCLI1(_L8("FaxClientThread FaxReceiveL exited state=%d"),state); + + faxsession->iSource->CloseFaxInStore (); + } + else + { + faxsession->iFaxSessionSettings.iMode = RCall::ETransmit; + TRAP (state, faxsession->FaxTransmitL ()); + faxsession->iSource->iInstream.Close (); + } + + // we tidy up by deleting any objects created + // (it does no harm if their pointers are already NULL) + // and we close any streams (which also does no harm + // if they have already been closed) + + faxsession->iFaxStarted = EFalse; + faxsession->iFax.Close (); + faxsession->iSource->CloseFaxStore (); + delete faxsession->iSource->iWriteFaxFile; + faxsession->iSource->iWriteFaxFile = NULL; + delete faxsession->iSource->iReadFaxFile; + faxsession->iSource->iReadFaxFile = NULL; + + faxsession->iSource->iOutstream.Close (); + faxsession->iSource->iInstream.Close (); + + RCall::TStatus callStatus; + faxsession->iCall.GetStatus(callStatus); + + __FLOG_FAXCLI1(_L8("FaxClientThread callStatus=%d"),callStatus); + + if (callStatus!=RCall::EStatusIdle) + faxsession->iCall.HangUp (); + + __FLOG_FAXCLI(_L8("FaxClientThread iCall.Close")); + faxsession->iCall.Close (); + } + + __FLOG_FAXCLI(_L8("FaxClientThread iLine.Close")); + faxsession->iLine.Close (); + } + + __FLOG_FAXCLI(_L8("FaxClientThread iPhone.Close")); + faxsession->iPhone.Close (); + } + } + faxsession->iTelServer.UnloadPhoneModule (tsyName); + } + } + faxsession->iTelServer.Close (); + } + + delete cleanup; + +// intercept the etel wrong modem type error + if (state == KErrEtelWrongModemType) state = KFaxErrWrongModemType; + + return (state); + } + +/*********************************************************************/ + +void CFaxTransfer::GetPhoneModuleNameL(TDes& aModuleName) const + { + // AnnW, 9/8/99 - This all assumes that we are taking the modem settings from the + // dial out IAP, which is fince for now, but may not be in the future? This may also + // need modifying for Linda? + + CMDBSession* db = CMDBSession::NewL(KCDVersion1_1); + CleanupStack::PushL(db); + + // Read the currently selected connection preference and find preferred IAP + TInt prefRank = 1; + + CCDConnectionPrefsRecord *connectionPrefs = + static_cast(CCDRecordBase::RecordFactoryL(KCDTIdConnectionPrefsRecord)); + CleanupStack::PushL(connectionPrefs); + connectionPrefs->iRanking = prefRank; + connectionPrefs->iDirection = ECommDbConnectionDirectionOutgoing; + connectionPrefs->FindL(*db); + + // The following code is a temporary solution until an issue has been resolved in CommsDat + // start + CCDIAPRecord* tempPIapRecord = + static_cast(CCDRecordBase::RecordFactoryL(KCDTIdIAPRecord)); + tempPIapRecord->SetRecordId(connectionPrefs->iDefaultIAP); + connectionPrefs->iDefaultIAP.iLinkedRecord = tempPIapRecord; + + CCDIAPRecord* pIapRecord = (CCDIAPRecord*)connectionPrefs->iDefaultIAP.iLinkedRecord; + pIapRecord->SetRecordId(connectionPrefs->iDefaultIAP); + pIapRecord->LoadL(*db); + + CCDBearerRecordBase* tempBearerRecord = + static_cast(CCDRecordBase::RecordFactoryL(KCDTIdModemBearerRecord)); + tempBearerRecord->SetRecordId(pIapRecord->iBearer); + pIapRecord->iBearer.iLinkedRecord = tempBearerRecord; + + CCDBearerRecordBase* pBearerRecord = (CCDBearerRecordBase*) pIapRecord->iBearer.iLinkedRecord; + pBearerRecord->SetRecordId(pIapRecord->iBearer); + pBearerRecord->LoadL(*db); + // end + + TUint32 iapId = pBearerRecord->iRecordTag; + + CMDBField* bearerField = new(ELeave) CMDBField(KCDTIdIAPBearer); + CleanupStack::PushL(bearerField); + bearerField->SetRecordId(iapId); + bearerField->LoadL(*db); + TUint32 modemId = *bearerField; + CleanupStack::PopAndDestroy(bearerField); + + CMDBField* tsyField = new(ELeave) CMDBField(KCDTIdTsyName); + CleanupStack::PushL(tsyField); + tsyField->SetRecordId(modemId); + tsyField->SetMaxLengthL(KMaxTextLength); + tsyField->LoadL(*db); + aModuleName = *tsyField; + CleanupStack::PopAndDestroy(tsyField); + + CleanupStack::PopAndDestroy(2); // db, connectionPrefs + } + +void CFaxTransfer::CancelFaxServerSession () + { + if (iMode & KFaxWaitForRing) + iCall.AnswerIncomingCallCancel (); + else + { + if (iMode & KFaxOffHook) + iCall.ConnectCancel (); + else + iCall.DialCancel (); + } + } +/*********************************************************************/ +// receiving a fax is easy because it is passive +// just open the file for receiving and off we go + +void CFaxTransfer::FaxReceiveL () + { + + __FLOG_FAXCLI(_L8("CFaxTransfer::FaxReceiveL entering")); + + if (iMode & KFaxPoll) + iFaxSessionSettings.iFaxRetrieveType = RCall::EFaxPoll; + else + iFaxSessionSettings.iFaxRetrieveType = RCall::EFaxOnDemand; + + iSource->OpenFaxInL (iReceiveFileName); + + SetFaxSettingsL(); + + if (iMode & KFaxWaitForRing) + {//-- answering incoming call if fax is waiting for a call + iCall.AnswerIncomingCall (iTransferStatus); + if (iClientCancel != KRequestPending) + {//-- cancel request, leave + iCall.AnswerIncomingCallCancel (); + User::WaitForRequest(iTransferStatus); + User::Leave (KFaxCancelRequested); + } + } + else //if (iMode & KFaxWaitForRing) + {//-- if fax is not waiting for a call, dial + if (iMode & KFaxOffHook) + iCall.Connect (iTransferStatus); + else + { + if (iPhoneNumber.Length() == 0) + User::Leave (KErrCouldNotConnect); + iCall.Dial (iTransferStatus, iPhoneNumber); + } + } + + TRequestStatus reqStatus; + RCall::TStatus callStatus; + iCall.NotifyStatusChange (reqStatus, callStatus); + + for (;;) + { + User::WaitForAnyRequest (); + if (reqStatus != KRequestPending) + { + //-- Call status changed + if (reqStatus.Int () != KErrNone) + { + CancelFaxServerSession (); + User::Leave (reqStatus.Int ()); + } + if (iMode & KFaxWaitForRing) + {//-- Fax is waiting for a ring + if (callStatus == RCall::EStatusRinging) + {//-- Call status is 'Ringing', continue waiting + iCall.NotifyStatusChange(reqStatus, callStatus); + reqStatus = KRequestPending; + continue; + } + else + //-- due to PIA-586KGE fix (changes in CATAnswerFax::Start()) 'Connecting' may be not noticed here + //-- so EStatusConnected state is ok + if (callStatus != RCall::EStatusAnswering && callStatus != RCall::EStatusConnected ) + { + iCall.AnswerIncomingCallCancel (); + User::Leave (KFaxEtelServerError); + } //if (callStatus != RCall::EStatusAnswering) + } //if (iMode & KFaxWaitForRing) + else + {//-- Fax is not waiting for a ring + if (iMode & KFaxOffHook) + { + if (callStatus != RCall::EStatusConnecting) + { + iCall.ConnectCancel (); + User::Leave (KFaxEtelServerError); + } + } + else if (callStatus != RCall::EStatusDialling) + { + iCall.DialCancel (); + User::Leave (KFaxEtelServerError); + } + } + TInt ret = iFax.Open (iCall); + if (ret != KErrNone) + { + CancelFaxServerSession (); + User::Leave (ret); + } + iFaxStarted = ETrue; + reqStatus = KRequestPending; + }//if (reqStatus != KRequestPending) + else if (iClientCancel != KRequestPending) + {//-- Fax cancel request + if (iFaxStarted == EFalse) + iCall.NotifyStatusChangeCancel (); + CancelFaxServerSession (); + User::Leave (KFaxCancelRequested); + } + else if (iTransferStatus != KRequestPending) + {//--iCall.AnswerIncomingCall status changed + if (iFaxStarted == EFalse) + iCall.NotifyStatusChangeCancel (); + User::LeaveIfError (iTransferStatus.Int ()); + break; + } + else + { + if (iFaxStarted == EFalse) + iCall.NotifyStatusChangeCancel (); + CancelFaxServerSession (); + User::Leave (KErrCompletion); // stray event handle + } + } + + //-- Data transfer phase + User::LeaveIfError (iFax.GetProgress (iProgress)); + while (iProgress.iPhase == EDataTransfer) + { + iSource->iResolu = iProgress.iResolution; + iSource->iCompression = iProgress.iCompression; + for (;;) + { + iFax.Read (iTransferStatus, iDataBuf); + User::WaitForRequest (iTransferStatus, iClientCancel); + if (iClientCancel != KRequestPending) + { + iFax.TerminateFaxSession (); + User::Leave (KFaxCancelRequested); + } + User::LeaveIfError (iTransferStatus.Int ()); + + // the server has buffered up lines to minimize interaction + // iDataBuf starts with a TInt containing the number of lines + // Each line follows, preceded with a TInt containing its length + // which must be copied as it might not be aligned on a 4-byte + // boundary - a line of zero length indicates we have reached + // the end of the page + + TUint8 *nextLine = CONST_CAST (TUint8 *, iDataBuf.Ptr ()); + TInt lengthOfLine=0; + TInt numberOfLines; + + Mem::Copy (&numberOfLines, nextLine, sizeof (TInt)); + nextLine += sizeof (TInt); + + while (numberOfLines--) + { + Mem::Copy (&lengthOfLine, nextLine, sizeof (TInt)); + if (lengthOfLine == 0) + break; + nextLine += sizeof (TInt); + TPtrC8 currentLine (nextLine, lengthOfLine); + iSource->WriteFaxDataL (currentLine); + nextLine += lengthOfLine; + } + if (lengthOfLine == 0) + break; + } + iSource->WritePageParmsL (iProgress.iAnswerback); + iFax.WaitForEndOfPage (iTransferStatus); + User::WaitForRequest (iTransferStatus, iClientCancel); + if (iClientCancel != KRequestPending) + { + iFax.TerminateFaxSession (); + User::Leave (KFaxCancelRequested); + } + User::LeaveIfError (iTransferStatus.Int ()); + User::LeaveIfError (iFax.GetProgress (iProgress)); + + } +// iSource->CloseFaxInStore (); + if (iProgress.iCompression==EModifiedRead) + Convert1dL (); + } +/*********************************************************************/ + +// sending a fax is rather more complex +// +// we need a valid phone number (we don't do polling) +// +// we need a valid list of pages to send +// +// we need to set the resolution we require to that of the +// fax we want to send (use the last page) +// + +void CFaxTransfer::FaxTransmitL () + { + + + __FLOG_FAXCLI(_L8("CFaxTransfer::FaxTransmitL entering")); + + TBool ConversionStatus=FALSE; + //TInt err=0; + if (iSource->iOurPreferredCompression==Prefer2D) + { + TRAPD (retcode,Convert2dL()); + if (retcode==KErrNone) + ConversionStatus=TRUE; + else + { + ConversionStatus=FALSE; + iSource->iOurPreferredCompression=Prefer1D; // if anything goes wrong during conversion + } // try the 1D version. + } + if ((iSource->iFaxPages == 0) || (iSource->iFaxListEntries == 0)) + User::Leave (KErrNotFound); + iSource->iSavedFaxListEntries = iSource->iFaxListEntries; + iSource->iOurFaxEntry.iPageCount = 0; + iSource->iInstream.Open (*iSource->iSources); + iSource->GetNextPageReadyL (); + + if (iSource->iOurPreferredCompression==Prefer1D) + iFaxSessionSettings.iTxPages=iSource->iFaxPages; + + // we pass in to the server the resolution of the last added source page + + iFaxSessionSettings.iTxResolution = (TFaxResolution) iSource->iResolu; + if ((iSource->iOurPreferredCompression==Prefer2D) && ConversionStatus==TRUE) + iFaxSessionSettings.iTxCompression = (TFaxCompression) EModifiedRead; + else + iFaxSessionSettings.iTxCompression = (TFaxCompression) iSource->iCompression; + + SetFaxSettingsL(); // pass the settings to the Fax Server + SetSharedFileHandlesL(); + + if (iMode & KFaxOffHook) + iCall.Connect (iTransferStatus); + else + { + if (iPhoneNumber.Length() == 0) + User::Leave (KErrCouldNotConnect); + iCall.Dial (iTransferStatus, iPhoneNumber); + } + + TRequestStatus reqStatus; + RCall::TStatus callStatus; + iCall.NotifyStatusChange (reqStatus, callStatus); + + for (;;) + { + User::WaitForAnyRequest (); + if (reqStatus != KRequestPending) + { + if (reqStatus.Int () != KErrNone) + // initialisation and comm port errors + { + CancelFaxServerSession (); + User::Leave (reqStatus.Int ()); + } + if (iMode & KFaxOffHook) + { + if (callStatus != RCall::EStatusConnecting) + { + iCall.ConnectCancel (); + User::Leave (KFaxEtelServerError); + } + } + else if (callStatus != RCall::EStatusDialling) + { + iCall.DialCancel (); + User::Leave (KFaxEtelServerError); + } + TInt ret = iFax.Open (iCall); + if (ret != KErrNone) + { + CancelFaxServerSession (); + User::Leave (ret); + } + iFaxStarted = ETrue; + reqStatus = KRequestPending; // to prevent first scenario being + // chosen each time any request comes in + } + else if (iClientCancel != KRequestPending) + { + if (iFaxStarted == EFalse) + iCall.NotifyStatusChangeCancel (); + CancelFaxServerSession (); + User::Leave (KFaxCancelRequested); + } + else if (iTransferStatus != KRequestPending) + { + if (iFaxStarted == EFalse) + iCall.NotifyStatusChangeCancel (); + User::LeaveIfError (iTransferStatus.Int ()); + break; + } + else + { + if (iFaxStarted == EFalse) + iCall.NotifyStatusChangeCancel (); + CancelFaxServerSession (); + User::Leave (KErrCompletion); // stray event handle + } + } + + for (;;) + { + User::LeaveIfError (iFax.GetProgress (iProgress)); + TInt thispage = iProgress.iPage; + TInt thisline = 1; + ASSERT (iSource->iLines); + + if ((iProgress.iCompression) && (iSource->iSavedFaxListEntries>=1)) + { + while (iSource->iCompression == EModifiedHuffman) // loop until we + iSource->GetNextPageReadyL (); // find the 2D document + } + + //we buffer up lines to minimize client-server interaction + //iDataBuf starts with a TInt containing the number of lines + //Each line follows, preceded with a TInt containing its length + //which must be copied as it might not be aligned on a 4-byte boundary + + TUint8 *startData; + TUint8 *lineData; + TInt numberOfLines; + const TUint8 *maxData; + + TUint8 *currentLineData; + TInt currentLineLength; + + for (;;) + { + lineData = startData = CONST_CAST (TUint8 *, iDataBuf.Ptr ()); + maxData = startData + iDataBuf.MaxLength () - KMaxT4Des - sizeof (TInt); + + iDataBuf.SetMax (); + numberOfLines = 0; + lineData += sizeof (TInt); + for (;;) + { + if (thisline > iSource->iLines) + break; + thisline++; + numberOfLines++; + currentLineData = lineData + sizeof (TInt); + *currentLineData = 0x0; + TPtr8 currentLine (currentLineData + 1, 0, KMaxT4Des - 1); + iSource->ReadFaxData (currentLine); + currentLineLength = currentLine.Length () + 1; + Mem::Copy (lineData, ¤tLineLength, sizeof (TInt)); + lineData += sizeof (TInt); + lineData += currentLineLength; + if (lineData > maxData) + break; + } + Mem::Copy (startData, &numberOfLines, sizeof (TInt)); + iDataBuf.SetLength (lineData - startData); + iFax.Write (iTransferStatus, iDataBuf); + User::WaitForRequest (iTransferStatus, iClientCancel); + if (iClientCancel != KRequestPending) + { + iFax.TerminateFaxSession (); + User::Leave (KFaxCancelRequested); + } + User::LeaveIfError (iTransferStatus.Int ()); + if (thisline > iSource->iLines) + break; + } + iDataBuf.Zero (); + iFax.WaitForEndOfPage (iTransferStatus); + User::WaitForRequest (iTransferStatus, iClientCancel); + if (iClientCancel != KRequestPending) + { + iFax.TerminateFaxSession (); + User::Leave (KFaxCancelRequested); + } + User::LeaveIfError (iTransferStatus.Int ()); + User::LeaveIfError (iFax.GetProgress (iProgress)); + if (iProgress.iPhase != EDataTransfer) + break; + if (thispage == iProgress.iPage) + iSource->PreviousPageFindL (); + else + iSource->GetNextPageReadyL (); + } + if ((iProgress.iCompression==EModifiedHuffman) && (iSource->iOurPreferredCompression==Prefer2D)) + { + for (TInt i=0; i<(iSource->iFaxPages/2);i++) + iSource->GetNextPageReadyL (); + } +} + +void CFaxTransfer::SetFaxSettingsL() + { + TInt error; + RPhone::TStatus phoneStatus; + + error=iPhone.GetStatus(phoneStatus); + if (error == KErrNone) + { + if ((phoneStatus.iModemDetected == RPhone::EDetectedNotPresent) || (phoneStatus.iModemDetected == RPhone::EDetectedUnknown)) + { + TRequestStatus initialiseStatus; + iPhone.Initialise(initialiseStatus); + User::WaitForAnyRequest(); + if (iClientCancel != KRequestPending) + { + iPhone.InitialiseCancel(); // issue initialiseCancel command + User::WaitForRequest(initialiseStatus); // wait for it to complete + User::Leave(KFaxCancelRequested); // leave with KFaxCancelRequested + } + User::LeaveIfError(initialiseStatus.Int()); + } + } + TInt ret=KErrNone; // so communication with modem has begun... + TInt count=0; // we have a loop where we try three times to set fax settings in the + // unlikely event that the initialisation sequence has just started + // so the phoneStatus gives ModemDetected Present, but the supported fax + // classes have not yet been ascertained. + do + { + ret=iCall.SetFaxSettings (iFaxSessionSettings); // retry until init sequence has found out what + // the fax classes supported are. + if (ret==KErrEtelUnknownModemCapability) + User::After(500000); + } + while (ret==KErrEtelUnknownModemCapability && iClientCancel==KRequestPending && count++<3); + if (iClientCancel!=KRequestPending) + ret=KFaxCancelRequested; + User::LeaveIfError(ret); + } +void CFaxTransfer::SetSharedFileHandlesL() + { + //Open a file server session and a file handle on the private header file to be shared. + RFs sharedFs; + RFile sharedHeaderFile; + + User::LeaveIfError(sharedFs.Connect()); + CleanupClosePushL(sharedFs); + //allow it to be shared + User::LeaveIfError(sharedFs.ShareProtected()); + + //get path + TFileName headerFullPath; + CFaxHeaderLines::GeneratePathForHeaderFileL(headerFullPath); + + //Open the file in read-only mode + User::LeaveIfError(sharedHeaderFile.Open(sharedFs, headerFullPath, EFileRead)); + CleanupClosePushL(sharedHeaderFile); + + User::LeaveIfError(iCall.AdoptFaxSharedHeaderFile(sharedHeaderFile)); + CleanupStack::PopAndDestroy();//sharedHeaderFile + CleanupStack::PopAndDestroy();//sharedFs + } + +TInt CFaxTransfer::GetPhoneInfoForTsy(const TDes& aTsyName, RTelServer::TPhoneInfo& aPhoneInfo) const +// +// Finds the index of the phone which belongs to TSY named "aTsyName", and retrieves its info. +// + { + TInt count=0; + iTelServer.EnumeratePhones(count); + TName matchTsyName; + TInt ret = iTelServer.GetTsyName(0,matchTsyName); + if (ret == KErrNone) + { + TInt i=0; + if (count>1) + { + while (matchTsyName.CompareF(aTsyName)!=KErrNone && i++AddSourceL (aFaxPageStore, 1,aPreferredCompression); + } + +EXPORT_C void CFaxTransfer::AddSourceL (const TFileName & aFaxPageStore, TInt aStartPage,TFaxPreferredCompression aPreferredCompression) +/** Specifies which pages of a fax store file should be sent. +It can be called more than once to add multiple pages from different files. +Typically, this function is called twice for each transmission: once to queue +the cover sheet, and a second time to queue the remainder of the fax. +The overloaded variants are supplied primarily to help error recovery in cases +when a fax transmission is either incomplete or when specific pages need to be +resent. When transmitting a fax, at least one variant of this function must be +called before CFaxTransfer::Start(). + +@param aFaxPageStore Name of the fax store file from which to take pages. +@param aStartPage Page in file to start from. If omitted, the file is sent +from the start. +@param aPreferredCompression Preferred compression. +@capability WriteUserData +*/ + { + iSource->AddSourceL (aFaxPageStore, aStartPage, aPreferredCompression); + } + +EXPORT_C void CFaxTransfer::AddSourceL (const TFileName & aFaxPageStore, TInt aStartPage, TInt aEndPage, TFaxPreferredCompression aPreferredCompression) +/**Specifies which pages of a fax store file should be sent. +It can be called more than once to add multiple pages from different files. +Typically, this function is called twice for each transmission: once to queue +the cover sheet, and a second time to queue the remainder of the fax. +The overloaded variants are supplied primarily to help error recovery in cases +when a fax transmission is either incomplete or when specific pages need to be +resent. When transmitting a fax, at least one variant of this function must be +called before CFaxTransfer::Start(). + +@param aFaxPageStore Name of the fax store file from which to take pages. +@param aStartPage Page in file to start from. If omitted, the file is sent from the start. +@param aEndPage Page in file to stop sending. If omitted, transmission continues to the end. +@param aPreferredCompression Preferred compression. +@capability WriteUserData +*/ + { + iSource->AddSourceL (aFaxPageStore, aStartPage, aEndPage, aPreferredCompression); + } + +//EXPORT_C void CFaxTransfer::AddSourceL (const TFileName & aFaxPageStore, const TFileName & aFaxPageStore2) +// { +// iSource->AddSourceL(aFaxPageStore, aFaxPageStore2); +// } + +EXPORT_C void CFaxTransfer::RemoveAllSources () +/** Clears the complete list of pages previously selected for faxing using one +of the variants of AddSourceL(). + +Removal of individual items from the list of pages to be fax is not possible. + +This function must be used between successive fax transmissions if previously +sent pages aren't to be sent again. +@capability WriteUserData +*/ + { + iSource->RemoveAllSources (); + } + +/********************************************************************/ +void CFaxTransfer::Convert2dL () + { + TInt err=0; + TInt k=0; + TBuf<64> filename; + CWriteFaxFile* writeFaxFile; + CReadFaxFile* readFaxFile; + TRawScanLine decodedScanLine; + //TFaxBufSenderId sd; + TFaxBufSenderId senderId; + + + writeFaxFile = CWriteFaxFile::NewL(); + CleanupStack::PushL(writeFaxFile); + readFaxFile = CReadFaxFile::NewL(); + CleanupStack::PushL(readFaxFile); + iConverting=TRUE; + + TRAP (err,readFaxFile->OpenL(iSource->iOurFaxEntry.iFaxPageStore)); + if (err!=KErrNone) + { + iConverting=FALSE; + User::Leave(err); + } + + readFaxFile->iReadFaxPages->SetPageL(0); + TFaxPageInfo info = readFaxFile->iReadFaxPages->CurrentPageInfo(); + if (info.iCompression != EModifiedHuffman) + User::Panic(_L("Not a 1D file"),1); + + //writeFaxFile->OpenL(_L("c:\\blank2D.fax"),64); + filename.Copy(iSource->iOurFaxEntry.iFaxPageStore); + filename.Append(_L("2d")); + // the second push in OpenL doesn't cause a double deletion of writeFaxFile, since it is pushed by using TCleanupItem. + // coverity [double_push] + writeFaxFile->OpenL(filename,64); + //writeFaxFile->OpenL(iSource->iOurFaxEntry.iFaxPageStore,64); + + + for (k=0; kiFaxPages; k++) + { + readFaxFile->iReadFaxPages->SetPageL(k); + TFaxPageInfo info = readFaxFile->iReadFaxPages->CurrentPageInfo(); + if (info.iCompression != EModifiedHuffman) + User::Panic(_L("Not a 1D file"),1); + writeFaxFile->iWriteFaxPages->StartPage(info.iResolution, EModifiedRead); + for (TInt n = info.iNumScanLines ; n ; n--) + { + readFaxFile->iReadFaxPages->GetScanLineL(decodedScanLine); + writeFaxFile->iWriteFaxPages->AddScanLineL(decodedScanLine); + } + + writeFaxFile->iWriteFaxPages->EndPageL(info.iResolution,senderId, EModifiedRead); + }// end of for statement + + writeFaxFile->CommitL(); + writeFaxFile->Close(); + readFaxFile->Close(); + + AddSourceL(filename,Prefer2D); + iFaxSessionSettings.iTxPages = (iSource->iFaxPages/2); // sent only the 2d version of the document + // which means half the attached pages + CleanupStack::PopAndDestroy(2); + iConverting=FALSE; +} + + + +/***********************************************************************************/ +// This function takes the received 2D file and converts it to 1D +// The 2D file will be deleted and the 1D version of the file will +// inherit the name of the received 2D version +/************************************************************************************/ +void CFaxTransfer::Convert1dL () + { + TInt err=0; + TInt k=0; + TBuf<64> filename; + CWriteFaxFile* writeFaxFile; + CReadFaxFile* readFaxFile; + TRawScanLine decodedScanLine; + //TFaxBufSenderId sd; + TFaxBufSenderId senderId; + + writeFaxFile = CWriteFaxFile::NewL(); + CleanupStack::PushL(writeFaxFile); + readFaxFile = CReadFaxFile::NewL(); + CleanupStack::PushL(readFaxFile); + iConverting=TRUE; + + //TRAP (err,readFaxFile->OpenL(iSource->iOurFaxEntry.iFaxPageStore)); + TRAP (err,readFaxFile->OpenL(iReceiveFileName)); + if (err!=KErrNone) + User::Leave(err); + + readFaxFile->iReadFaxPages->SetPageL(0); + TFaxPageInfo info = readFaxFile->iReadFaxPages->CurrentPageInfo(); + //if (info.iCompression != EModifiedHuffman) + if (info.iCompression != EModifiedRead) + User::Panic(_L("Not a 2D file"),1); + + //filename.Copy(iSource->iOurFaxEntry.iFaxPageStore); + filename.Copy(iReceiveFileName); + filename.Append(_L("1d")); + // the second push in OpenL doesn't cause a double deletion of writeFaxFile, since it is pushed by using TCleanupItem. + // coverity [double_push] + writeFaxFile->OpenL(filename,64); + // writeFaxFile->OpenL(iSource->iOurFaxEntry.iFaxPageStore,64); + + + TInt iRxPages=readFaxFile->iReadFaxPages->NumPages(); + + for (k=0; kiReadFaxPages->SetPageL(k); + TFaxPageInfo info = readFaxFile->iReadFaxPages->CurrentPageInfo(); + //if (info.iCompression != EModifiedHuffman) + if (info.iCompression != EModifiedRead) + User::Panic(_L("Not a 2D file"),1); + //writeFaxFile->iWriteFaxPages->StartPage(info.iResolution, EModifiedRead); + writeFaxFile->iWriteFaxPages->StartPage(info.iResolution, EModifiedHuffman); + for (TInt n = info.iNumScanLines ; n ; n--) + { + readFaxFile->iReadFaxPages->GetScanLineL(decodedScanLine); + writeFaxFile->iWriteFaxPages->AddScanLineL(decodedScanLine); + } + + //writeFaxFile->iWriteFaxPages->EndPageL(info.iResolution,senderId, EModifiedRead); + writeFaxFile->iWriteFaxPages->EndPageL(info.iResolution,senderId, EModifiedHuffman); + }// end of for statement + + writeFaxFile->CommitL(); + writeFaxFile->Close(); + readFaxFile->Close(); + + RFs FileServer; + err = FileServer.Connect(); + if (err !=KErrNone) + User::Leave(err); + User::LeaveIfError(FileServer.Delete(iReceiveFileName)); + + User::LeaveIfError(FileServer.Rename(filename,iReceiveFileName)); + + FileServer.Close(); + + CleanupStack::PopAndDestroy(2); + iConverting=FALSE; +// AddSourceL(filename,Prefer2D); +// iFaxSessionSettings.iTxPages = (iSource->iFaxPages/2); // sent only the 2d version of the document + } + + +/* + +CFaxTransfer::StartThread() + { + TInt state = KErrNone; + TInt heapSize = 0x14000; + TInt stackSize = 0x14000; + + reinterpret_cast(this); // this points to a CFaxTransfer Object + state = iConverterThread.Create ((_L ("FaxConverterThread")), FaxConverterThread, stackSize, heapSize, heapSize, this, EOwnerThread); + if (state) + { + state = KFaxThreadError; + } + else + { + //aThreadStat = KRequestPending; + //if (iClientCancel != KErrCancel) + //iClientCancel = KRequestPending; + //iDriverThread.Logon (aThreadStat); + + iConverterThread.SetPriority (EPriorityRealTime); + iConverterThread.Resume (); + } + return state; + } + +TInt FaxConverterThread (TAny * session) + { + TInt m; + CTrapCleanup *cleanup = CTrapCleanup::New (); + CFaxTransfer *faxtransfer =reinterpret_cast(session); + RSemaphore aSemaphore; + TFindSemaphore mysemaphore(_L("FaxCli*")); + + TFullName theName (_L("FaxCliSem")); +// RSemaphore theSem; // derived from RHandleBase + + if ((mysemaphore.Next(theName))==KErrNone) + { + aSemaphore.Open(mysemaphore,EOwnerThread); + } + + + TRAP(m,faxtransfer->Convert2dL()); + + aSemaphore.Signal(); + return KErrNone; + } +*/