diff -r a3a1ae9acec6 -r da5135c61bad meetingrequest/mrtasks/src/cesmrsendmrfsmailtask.cpp --- a/meetingrequest/mrtasks/src/cesmrsendmrfsmailtask.cpp Mon Mar 15 12:39:10 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,798 +0,0 @@ -/* -* Copyright (c) 2007-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 for ESMR send MR via fs email task -* -*/ - - -#include "emailtrace.h" -#include "cesmrsendmrfsmailtask.h" -#include "cesmrcaldbmgr.h" -#include "mesmrmeetingrequestentry.h" -#include "cesmrfsemailmanager.h" -#include "cesmrmailplaitextformatter.h" -#include "cesmrcalimportexporter.h" -#include "cesmrcaluserutil.h" -#include "cesmrguilistquery.h" -#include "esmrhelper.h" - -#include -#include -#include -#include -#include -#include -#include - -// Unnamed namespace for local definitions -namespace { - -/** - * Adds updated label into summary field. - */ -void UpdateSummaryL( - CCalEntry& aEntry, - CESMRMailPlainTextFormatter& aPlainTextFormatter ) - { - FUNC_LOG; - TPtrC currentSummary( aEntry.SummaryL() ); - - CCalEntry::TMethod method( aEntry.MethodL() ); - if ( CCalEntry::EMethodRequest == method && aEntry.SequenceNumberL() ) - { - // This is update - HBufC* updateStr = aPlainTextFormatter.UpdatedStringLC(); - HBufC* summary = HBufC::NewLC( - updateStr->Length() + currentSummary.Length() ); - - if ( currentSummary.Find(*updateStr) == KErrNotFound ) - { - TPtr summaryPtr( summary->Des() ); - summaryPtr.Append( *updateStr ); - summaryPtr.Append( currentSummary ); - aEntry.SetSummaryL( summaryPtr ); - } - CleanupStack::PopAndDestroy( summary ); - summary = NULL; - - CleanupStack::PopAndDestroy( updateStr ); - updateStr = NULL; - } - else if ( CCalEntry::EMethodCancel == method ) - { - // This is cancellation - HBufC* cancelStr = aPlainTextFormatter.CanceledStringLC(); - HBufC* summary = HBufC::NewLC( - cancelStr->Length() + currentSummary.Length() ); - - if ( currentSummary.Find(*cancelStr) == KErrNotFound ) - { - TPtr summaryPtr( summary->Des() ); - summaryPtr.Append( *cancelStr ); - summaryPtr.Append( currentSummary ); - aEntry.SetSummaryL( summaryPtr ); - } - CleanupStack::PopAndDestroy( summary ); - summary = NULL; - - CleanupStack::PopAndDestroy( cancelStr ); - cancelStr = NULL; - } - } - -/** - * Cleanup operation for RPointerArray. - * - * @param aArray Pointer to RPointerArray. - */ -void CalAttendeePointerArrayCleanup( TAny* aArray ) - { - RPointerArray* attendeeArray = - static_cast*>( aArray ); - - attendeeArray->ResetAndDestroy(); - attendeeArray->Close(); - } - -#ifdef _DEBUG - -// Literal for panics -_LIT( ESMRSendMRRespMailPanicTxt, "ESMRSendMRFSEMailTask" ); - -// Panic codes -enum TESMRSendMRFSMailTaskPanic - { - EESMRSendMRFSMailInvalidMethod, - EESMRSendMRFSAttendeesMissing, - EESMRSendMRNoEntriesToSend - }; - -// ======== LOCAL FUNCTIONS ======== - -// --------------------------------------------------------------------------- -// Panic wrapper method -// --------------------------------------------------------------------------- -// -void Panic( TESMRSendMRFSMailTaskPanic aPanic ) - { - - User::Panic( ESMRSendMRRespMailPanicTxt, aPanic ); - } - -#endif // _DEBUG - - -// Definition for temporary calendar file -// remove hard coded paths -//_LIT( KPath, "c:\\temp\\" ); //codescanner::driveletters -//_LIT( KFileAndPath, "c:\\temp\\meeting.ics" ); //codescanner::driveletters -//_LIT( KCancellationFile, "c:\\temp\\cancel.ics" ); //codescanner::driveletters -_LIT( KFile, "temp\\meeting.ics" ); -_LIT( KCancelFile, "temp\\cancel.ics" ); -// - -// --------------------------------------------------------------------------- -// Leave wrapper method -// --------------------------------------------------------------------------- -// -inline void DoLeaveIfErrorL( TInt aLeaveCode ) - { - if ( KErrNone != aLeaveCode ) - { - - User::Leave( aLeaveCode ); - } - } -} // namespace - -// ======== MEMBER FUNCTIONS ======== - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::CESMRSendMRFSMailTask -// --------------------------------------------------------------------------- -// -CESMRSendMRFSMailTask::CESMRSendMRFSMailTask( - MESMRCalDbMgr& aCalDbMgr, - MESMRMeetingRequestEntry& aEntry, - CMRMailboxUtils& aMRMailboxUtils, - TESMRCommand aCommand ) -: CESMRTaskBase( aCalDbMgr, aEntry, aMRMailboxUtils ), - iCommand( aCommand ) - { - FUNC_LOG; - //do nothing - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::~CESMRSendMRFSMailTask -// --------------------------------------------------------------------------- -// -CESMRSendMRFSMailTask::~CESMRSendMRFSMailTask() - { - FUNC_LOG; - iEntriesToSend.ResetAndDestroy(); - delete iCaluserUtil; - delete iEmailMgr; - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::NewL -// --------------------------------------------------------------------------- -// -CESMRSendMRFSMailTask* CESMRSendMRFSMailTask::NewL( - MESMRCalDbMgr& aCalDbMgr, - MESMRMeetingRequestEntry& aEntry, - CMRMailboxUtils& aMRMailboxUtils, - TESMRCommand aCommand ) - { - FUNC_LOG; - - CESMRSendMRFSMailTask* self = - new (ELeave) CESMRSendMRFSMailTask( - aCalDbMgr, - aEntry, - aMRMailboxUtils, - aCommand ); - - CleanupStack::PushL(self); - self->ConstructL(); - CleanupStack::Pop(self); - - return self; - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::ConstructL -// --------------------------------------------------------------------------- -// -void CESMRSendMRFSMailTask::ConstructL() - { - FUNC_LOG; - BaseConstructL(); - iEmailMgr = CESMRFSEMailManager::NewL( MailboxUtils() ); - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::ExecuteTaskL -// --------------------------------------------------------------------------- -// -void CESMRSendMRFSMailTask::ExecuteTaskL() - { - FUNC_LOG; - - iEntryToSend = ESMREntry().ValidateEntryL(); - - iCaluserUtil = CESMRCalUserUtil::NewL( *iEntryToSend ); - - CCalEntry::TMethod method( iEntryToSend->MethodL() ); - - // Checking input paramters - if( !(CCalEntry::EMethodRequest == method || - CCalEntry::EMethodCancel == method ) || - CCalEntry::EAppt != iEntryToSend->EntryTypeL() ) - { - DoLeaveIfErrorL( KErrArgument ); - } - - // Get entries to be sent - GetEntriesToBeSentL(); - ResolveSendTypeL(); - - if ( NeedToSendInvitationL() ) - { - TInt entryCount( iEntriesToSend.Count() ); - for (TInt i(0); i < entryCount; ++i ) - { - iEntryToSend = iEntriesToSend[i]; - ConstructMailL(); - TInt ret = iEmailMgr->SendMessageL(); - User::LeaveIfError(ret); - } - } - - if ( EESMRCmdSendMRUpdate == iCommand && - NeedToSendCancellationL() ) - { - delete iEmailMgr; iEmailMgr = NULL; - iEmailMgr = CESMRFSEMailManager::NewL( MailboxUtils() ); - - TInt entryCount( iEntriesToSend.Count() ); - for (TInt i(0); i < entryCount; ++i ) - { - // Sending cancellation to removed attendees - iEntryToSend = iEntriesToSend[i]; - iEntryToSend->SetMethodL( CCalEntry::EMethodCancel ); - - ConstructMailL(); - TInt ret = iEmailMgr->SendMessageL(); - User::LeaveIfError(ret); - } - } - - } - -// --------------------------------------------------------------------------- -// CESMRSendMRRespFSMailTask::ConstructMailL -// --------------------------------------------------------------------------- -// -void CESMRSendMRFSMailTask::ConstructMailL() - { - FUNC_LOG; - - // create text formatter: - CESMRMailPlainTextFormatter* textFormatter = - CESMRMailPlainTextFormatter::NewLC( MailboxUtils() ); - - // Prepare email manager for sending - HBufC* mbOwnerAddr = ResolveUsedMailboxUserAddressLC(); - TPtrC emailAddr( ESMRHelper::AddressWithoutMailtoPrefix( *mbOwnerAddr ) ); - iEmailMgr->PrepareForSendingL( emailAddr ); - - TBool isForwarded( ESMREntry().IsForwardedL() ); - if ( isForwarded ) - { - CCalUser* organizer = iEntryToSend->OrganizerL(); - CCalUser* forwardOrganizer = - CCalUser::NewL( - organizer->Address(), - emailAddr ); - CleanupStack::PushL( forwardOrganizer ); - forwardOrganizer->SetCommonNameL( organizer->CommonName() ); - - // Ownership is transferred to entry - iEntryToSend->SetOrganizerL( forwardOrganizer ); - CleanupStack::Pop( forwardOrganizer ); - - iEmailMgr->SetSenderL( - forwardOrganizer->Address(),forwardOrganizer->CommonName() ); - - iEmailMgr->SetReplyToAddressL( - forwardOrganizer->Address(), forwardOrganizer->CommonName() ); - - organizer = NULL; - forwardOrganizer = NULL; - } - - CleanupStack::PopAndDestroy( mbOwnerAddr ); - - // set the email subject: - HBufC* subject = NULL; - if ( CCalEntry::EMethodRequest == iEntryToSend->MethodL() ) - { - subject = textFormatter->Subject16LC( - *iEntryToSend, - ESMREntry().IsForwardedL(), - EESMRCmdSendMRUpdate == iCommand ); - } - else - { - subject = textFormatter->Subject16LC( - *iEntryToSend, - ESMREntry().IsForwardedL(), - EFalse ); - } - - iEmailMgr->SetSubjectL( *subject ); - CleanupStack::PopAndDestroy( subject ); - subject = NULL; - - AddAttendeesL(); - - // Set text/plain part: - HBufC* body = textFormatter->Body16LC( *iEntryToSend ); - iEmailMgr->CreateTextPlainPartL( *body ); - CleanupStack::PopAndDestroy(body); - body = NULL; - - // export the calendar entry in iCal format: - UpdateSummaryL( *iEntryToSend, *textFormatter ); - - TInt priority( iEntryToSend->PriorityL() ); - SetCorrectAttendeesToEntryL(); - CESMRCalImportExporter* calExporter = CESMRCalImportExporter::NewLC(); - HBufC* calendarPart = calExporter->ExportToICal16LC( *iEntryToSend ); - - // Set text/calendar part: - CESMRFSEMailManager::TESMRMethod method( - CESMRFSEMailManager::EESMRMethodRequest); - - CCalEntry::TMethod entryMethod( iEntryToSend->MethodL() ); - switch ( entryMethod ) - { - case CCalEntry::EMethodRequest: - { - method = CESMRFSEMailManager::EESMRMethodRequest; - } - break; - case CCalEntry::EMethodCancel: - { - method = CESMRFSEMailManager::EESMRMethodCancel; - } - break; - case CCalEntry::EMethodReply://fallthrough, EMethodReply not needed - default: - break; - } - - if ( CCalEntry::EMethodCancel == entryMethod ) - { - // removing hardcoded paths - TFileName fileName(KCancelFile); - User::LeaveIfError(ESMRHelper::CreateAndAppendPrivateDirToFileName(fileName)); - SaveICalToFileL( *calendarPart, KCancelFile ); - iEmailMgr->CreateTextCalendarPartL(method, KCancelFile); - // - } - else - { - // removing hardcoded paths - TFileName fileName(KFile); - User::LeaveIfError(ESMRHelper::CreateAndAppendPrivateDirToFileName(fileName)); - SaveICalToFileL( *calendarPart, fileName ); - iEmailMgr->CreateTextCalendarPartL(method, fileName); - // - } - - CleanupStack::PopAndDestroy(3, textFormatter); - - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::SaveICalToFileL -// --------------------------------------------------------------------------- -// -TInt CESMRSendMRFSMailTask::SaveICalToFileL( const TDesC& aICal, - const TDesC& aFilename ) - { - FUNC_LOG; - //create 8 bit buffer for temp file content - TInt length = aICal.Length() * 2; - HBufC8* buffer = HBufC8::NewLC( length ); - TPtr8 des = buffer->Des(); - - // covert the 16 bit iCal to 8 bit iCal: - TInt err = CnvUtfConverter::ConvertFromUnicodeToUtf8(des, aICal); - - RFs& fs( CCoeEnv::Static()->FsSession() ); - - // ensure the path exists: - //remove hardcoded paths - //BaflUtils::EnsurePathExistsL(fs, KPath); - // - - // delete previous file - fs.Delete( aFilename ); - - // save the iCal to file system: - RFile file; - User::LeaveIfError(file.Create(fs, aFilename, EFileWrite)); - CleanupClosePushL(file); - User::LeaveIfError(file.Write(*buffer)); - - // clean up: - CleanupStack::PopAndDestroy(2, buffer); // file, fs, buffer - - return KErrNone; - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::AddAttendeesL -// --------------------------------------------------------------------------- -// -void CESMRSendMRFSMailTask::AddAttendeesL() - { - FUNC_LOG; - - RArray requiredAttendees; - CleanupClosePushL( requiredAttendees ); - - RArray optionalAttendees; - CleanupClosePushL( optionalAttendees ); - - GetAttendeesToBeSentL( requiredAttendees, optionalAttendees ); - - // Add recipients and cc-recipients - TInt attendeeCount( requiredAttendees.Count() ); - TInt optAttendeeCount( optionalAttendees.Count() ); - - __ASSERT_DEBUG( attendeeCount || optAttendeeCount, Panic(EESMRSendMRFSAttendeesMissing) ); - - if ( !attendeeCount && !optAttendeeCount) - { - DoLeaveIfErrorL( KErrArgument ); - } - - for (TInt i(0); i < attendeeCount; ++i) - { - TPtrC emailAddr( requiredAttendees[i]->Address() ); - - - iEmailMgr->AppendToRecipientL( - emailAddr, - requiredAttendees[i]->CommonName() ); - } - - for (TInt i(0); i < optAttendeeCount; ++i) - { - TPtrC emailAddr( optionalAttendees[i]->Address() ); - - - iEmailMgr->AppendCCRecipientL( - emailAddr, - optionalAttendees[i]->CommonName() ); - } - - CleanupStack::PopAndDestroy(2, &requiredAttendees ); - - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::ResolveUsedMailboxUserAddressLC -// --------------------------------------------------------------------------- -// -HBufC* CESMRSendMRFSMailTask::ResolveUsedMailboxUserAddressLC() - { - FUNC_LOG; - - HBufC* mailboxUserAddress = NULL; - MESMRMeetingRequestEntry& mrEntry( ESMREntry()); - if ( mrEntry.IsForwardedL() ) - { - // Entry is forwarded --> Use default mailbox - CMRMailboxUtils::TMailboxInfo mbInfo; - CMRMailboxUtils& mbUtils( MailboxUtils() ); - mbUtils.GetDefaultMRMailBoxL( mbInfo ); - mailboxUserAddress = mbInfo.iEmailAddress.AllocLC(); - } - else - { - CCalUser *mailboxUser = iEntryToSend->PhoneOwnerL(); - mailboxUserAddress = mailboxUser->Address().AllocLC(); - } - - return mailboxUserAddress; - } - - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::ResolveSendTypeL -// --------------------------------------------------------------------------- -// -void CESMRSendMRFSMailTask::ResolveSendTypeL() - { - FUNC_LOG; - - if ( EESMRCmdSendMRUpdate == iCommand ) - { - RArray addedAttendees; - CleanupClosePushL( addedAttendees ); - - RArray removedAttendees; - CleanupClosePushL( removedAttendees ); - - ESMREntry().GetAddedAttendeesL( - addedAttendees, - EESMRRoleRequiredAttendee | EESMRRoleOptionalAttendee ); - ESMREntry().GetRemovedAttendeesL( - removedAttendees, - EESMRRoleRequiredAttendee | EESMRRoleOptionalAttendee ); - - if ( addedAttendees.Count() || removedAttendees.Count() ) - { - TInt sendType = CESMRGUIListQuery::ExecuteL( - CESMRGUIListQuery::EESMRSendUpdateToAllQuery ); - - if ( KErrCancel == sendType ) - { - User::Leave( KErrCancel ); - } - - iSendType = static_cast(sendType ); - } - - CleanupStack::PopAndDestroy( &removedAttendees ); - CleanupStack::PopAndDestroy( &addedAttendees ); - } - - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::NeedToSendInvitationL -// --------------------------------------------------------------------------- -// -TBool CESMRSendMRFSMailTask::NeedToSendInvitationL() - { - FUNC_LOG; - - TBool retValue( ETrue ); - - RArray addedAttendees; - CleanupClosePushL( addedAttendees ); - - if ( EESMRCmdSendMRUpdate == iCommand && - ESentToAddedAndRemoved == iSendType ) - { - ESMREntry().GetAddedAttendeesL( - addedAttendees, - EESMRRoleRequiredAttendee | EESMRRoleOptionalAttendee ); - - if ( !addedAttendees.Count() ) - { - retValue = EFalse; - } - } - - CleanupStack::PopAndDestroy( &addedAttendees ); - - - return retValue; - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::NeedToSendCancellationL -// --------------------------------------------------------------------------- -// -TBool CESMRSendMRFSMailTask::NeedToSendCancellationL() - { - FUNC_LOG; - - TBool retValue( EFalse ); - - RArray removedAttendees; - CleanupClosePushL( removedAttendees ); - - if ( EESMRCmdSendMRUpdate == iCommand ) - { - ESMREntry().GetRemovedAttendeesL( - removedAttendees, - EESMRRoleRequiredAttendee | EESMRRoleOptionalAttendee ); - - if ( removedAttendees.Count() ) - { - retValue = ETrue; - } - } - - CleanupStack::PopAndDestroy( &removedAttendees ); - - - return retValue; - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::NeedToSendCancellationL -// --------------------------------------------------------------------------- -// -void CESMRSendMRFSMailTask::SetCorrectAttendeesToEntryL() - { - FUNC_LOG; - if ( EESMRCmdSendMRUpdate == iCommand ) - { - TESMRMailPlugin plugin( ESMREntry().CurrentPluginL() ); - - CCalEntry::TMethod method( iEntryToSend->MethodL() ); - - RArray requiredAttendees; - CleanupClosePushL( requiredAttendees ); - - RArray optionalAttendees; - CleanupClosePushL( optionalAttendees ); - - GetAttendeesToBeSentL( requiredAttendees, optionalAttendees ); - - RPointerArray attendees; - CleanupStack::PushL( - TCleanupItem( - CalAttendeePointerArrayCleanup, - &attendees ) ); - - - TInt attendeeCount( requiredAttendees.Count() ); - for ( TInt i(0); i < attendeeCount; ++i ) - { - // Make copy of the attendees - CCalAttendee* attendee = ESMRHelper::CopyAttendeeL(*requiredAttendees[i] ); - User::LeaveIfError( - attendees.Append( attendee ) ); - } - - TInt optionalAttendeeCount( optionalAttendees.Count() ); - for ( TInt i(0); i < optionalAttendeeCount; ++i ) - { - // Make copy of the attendees - CCalAttendee* attendee = ESMRHelper::CopyAttendeeL(*optionalAttendees[i] ); - User::LeaveIfError( - attendees.Append( attendee ) ); - } - - // Clean attendee array - while ( iEntryToSend->AttendeesL().Count() ) - { - iEntryToSend->DeleteAttendeeL( 0 ); - } - - while ( attendees.Count() ) - { - CCalAttendee* attendee = attendees[0]; - iEntryToSend->AddAttendeeL( attendee ); - attendees.Remove(0); - } - - CleanupStack::PopAndDestroy( &attendees ); - CleanupStack::PopAndDestroy( &optionalAttendees ); - CleanupStack::PopAndDestroy( &requiredAttendees ); - } - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::NeedToSendCancellationL -// --------------------------------------------------------------------------- -// -void CESMRSendMRFSMailTask::GetAttendeesToBeSentL( - RArray& aRequiredAttendees, - RArray& aOptionalAttendees ) - { - FUNC_LOG; - CCalEntry::TMethod method( iEntryToSend->MethodL() ); - - if ( EESMRCmdSendMRUpdate == iCommand && - ESentToAddedAndRemoved == iSendType && - CCalEntry::EMethodRequest == method ) - { - ESMREntry().GetAddedAttendeesL( - aRequiredAttendees, - EESMRRoleRequiredAttendee ); - ESMREntry().GetAddedAttendeesL( - aOptionalAttendees, - EESMRRoleOptionalAttendee ); - } - else if (EESMRCmdSendMRUpdate == iCommand && - CCalEntry::EMethodCancel == method ) - { - ESMREntry().GetRemovedAttendeesL( - aRequiredAttendees, - EESMRRoleRequiredAttendee ); - ESMREntry().GetRemovedAttendeesL( - aOptionalAttendees, - EESMRRoleOptionalAttendee ); - } - else - { - iCaluserUtil->GetAttendeesL( - aRequiredAttendees, - EESMRRoleRequiredAttendee ); - iCaluserUtil->GetAttendeesL( - aOptionalAttendees, - EESMRRoleOptionalAttendee ); - } - } - -// --------------------------------------------------------------------------- -// CESMRSendMRFSMailTask::GetEntriesToBeSentL -// --------------------------------------------------------------------------- -// -void CESMRSendMRFSMailTask::GetEntriesToBeSentL() - { - FUNC_LOG; - // Clear the entry array - iEntriesToSend.ResetAndDestroy(); - - if ( EESMRCmdSendMRUpdate == iCommand ) - { - // We are sending update and we need to send all the entries - CCalEntry* mainEntry = ESMREntry().ValidateEntryL(); - CleanupStack::PushL( mainEntry ); - - if ( MESMRCalEntry::EESMRAllInSeries == ESMREntry().RecurrenceModRule() ) - { - // Update child entries sequence number - ESMREntry().UpdateChildEntriesSeqNumbersL(); - - // We are sending update to series - // Also the modifying entries need to be sent - // Otherwise the modifying entries may disappear from the - // attendees calendar. - CalDbMgr().NormalDbEntryView()->FetchL( - mainEntry->UidL(), - iEntriesToSend ); - - CleanupStack::PopAndDestroy( mainEntry ); - } - else - { - // Ownership transferred - TInt err = iEntriesToSend.Append( mainEntry ); - User::LeaveIfError( err ); - - CleanupStack::Pop( mainEntry ); - } - - mainEntry = NULL; - } - else - { - // We are sending either: - // a) Single entry - // b) Modified instance from series - // c) First invitation to series - // d) forwarding entry - TInt err = iEntriesToSend.Append( ESMREntry().ValidateEntryL() ); - User::LeaveIfError( err ); - } - - __ASSERT_DEBUG( iEntriesToSend.Count(), Panic(EESMRSendMRNoEntriesToSend ) ); - } - -// EOF -