meetingui/meetingrequestviewers/src/CMRProcessor.cpp
changeset 0 f979ecb2b13e
equal deleted inserted replaced
-1:000000000000 0:f979ecb2b13e
       
     1 /*
       
     2 * Copyright (c) 2002-2005 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: Implementation for meeting request processor 
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 
       
    20 
       
    21 // INCLUDE FILES
       
    22 #include "CMRProcessor.h"
       
    23 #include "MRHelpers.h"
       
    24 #include "mrdatalog.h"
       
    25 #include "ICalUILogDef.h"
       
    26 #include <e32std.h>
       
    27 #include <calentry.h> 		//CCalEntry (Calendar entry API V2)
       
    28 #include <caluser.h> 		//caluser and attendee
       
    29 #include <MsgMailUIDs.h> 	//uid for mail application
       
    30 #include <cemailaccounts.h>
       
    31 #include <smtpset.h>
       
    32 #include <imapset.h>
       
    33 #include <pop3set.h>
       
    34 #include <MuiuMsvUiServiceUtilities.h>
       
    35 #include "MRViewersPanic.h" 			//panic codes
       
    36 #include "meetingrequestviewers.hrh" 	//common constants
       
    37 #include "ProcessingStructs.h" 			//scenario structures
       
    38 #include <SendUiConsts.h> 				//mailbox uids
       
    39 #include <stringloader.h> 				//loading of string resources
       
    40 #include <meetingrequestviewersuires.rsg> 	//resource definitions header
       
    41 #include <CalenInterimUtils2.h>
       
    42 #include <cmrmailboxutils.h>
       
    43 
       
    44 // CONSTANTS
       
    45 /// Unnamed namespace for local definitions
       
    46 namespace {
       
    47 
       
    48 _LIT( KPanicMsg, "CMRProcessor" );
       
    49 
       
    50 void Panic( TPanicCode aReason )
       
    51     {
       
    52     User::Panic( KPanicMsg, aReason );
       
    53     }
       
    54 
       
    55 }  // namespace
       
    56 
       
    57 // ============================ MEMBER FUNCTIONS ===============================
       
    58 
       
    59 // -----------------------------------------------------------------------------
       
    60 // CMRProcessor::CMRProcessor
       
    61 // C++ default constructor can NOT contain any code, that
       
    62 // might leave.
       
    63 // -----------------------------------------------------------------------------
       
    64 //
       
    65 CMRProcessor::CMRProcessor(
       
    66     CMRMailboxUtils& aMRMailboxUtils,
       
    67     CMRUtilsInternal& aMRUtils,
       
    68     const MAgnEntryUi::TAgnEntryUiInParams& aParams,
       
    69     RPointerArray<CCalEntry>& aEntries )
       
    70     : iMRMailboxUtils( aMRMailboxUtils ),
       
    71       iMRUtils( aMRUtils ),
       
    72       iEntryUiInParams( aParams ),
       
    73       iArrayOfEntries( aEntries ),
       
    74       iEditMode( MMRModelInterface::EModeNotSet ),
       
    75       iOwnerRole( ENotFound )
       
    76     {
       
    77 	// set initial values to scenario data
       
    78 	iCombinedScenData.iMethod = CCalEntry::EMethodNone;
       
    79 	iCombinedScenData.iCallingApp = ENotSet;
       
    80 	iCombinedScenData.iOpenedMailbox = ENotSet;
       
    81     iCombinedScenData.iUtilsDBResult = MMRUtilsTombsExt::EUndefined;		    	
       
    82     iCombinedScenData.iMethodLevelValidity = ENotSet;
       
    83 	iCombinedScenData.iEditorMode = ENotSet;
       
    84     iCombinedScenData.iMROperation = ENotSet;    
       
    85 	}
       
    86 
       
    87 // -----------------------------------------------------------------------------
       
    88 // CMRProcessor::ConstructL
       
    89 // Symbian 2nd phase constructor can leave.
       
    90 // -----------------------------------------------------------------------------
       
    91 //
       
    92 void CMRProcessor::ConstructL()
       
    93     {    
       
    94     }
       
    95 
       
    96 // -----------------------------------------------------------------------------
       
    97 // ?classname::NewL
       
    98 // Two-phased constructor.
       
    99 // -----------------------------------------------------------------------------
       
   100 //
       
   101 CMRProcessor* CMRProcessor::NewL(
       
   102     CMRMailboxUtils& aMRMailboxUtils,
       
   103     CMRUtilsInternal& aMRUtils,
       
   104     const MAgnEntryUi::TAgnEntryUiInParams& aParams,
       
   105     RPointerArray<CCalEntry>& aEntries )
       
   106     {
       
   107     CMRProcessor* self = new( ELeave ) CMRProcessor( aMRMailboxUtils,
       
   108                                                      aMRUtils,
       
   109                                                      aParams,
       
   110                                                      aEntries );
       
   111    CleanupStack::PushL( self );
       
   112    self->ConstructL();
       
   113    CleanupStack::Pop();
       
   114 
       
   115    return self;
       
   116    }
       
   117 
       
   118 // Destructor
       
   119 CMRProcessor::~CMRProcessor()
       
   120     {
       
   121     delete iCombinedEntry;
       
   122     }
       
   123 
       
   124 CCalEntry* CMRProcessor::CombinedEntry()
       
   125 	{
       
   126 	// Should always be valid when this method is called
       
   127     __ASSERT_DEBUG( iCombinedEntry, Panic( ECombinedCalEntryNull ) );	
       
   128 	return iCombinedEntry;
       
   129 	}
       
   130 
       
   131 void CMRProcessor::SetPhoneOwnerL()
       
   132     {
       
   133 	__ASSERT_DEBUG( iArrayOfEntries.Count() > 0, Panic( EEmptyEntryArray ) );
       
   134 	
       
   135     // set phone owner field of each entry:
       
   136     
       
   137     TInt count( iArrayOfEntries.Count() );
       
   138     for ( TInt i( 0 ); i < count; ++i )    
       
   139         {        
       
   140         CCalEntry& entry = *( iArrayOfEntries[i] );
       
   141         iMRMailboxUtils.SetPhoneOwnerL( entry, iEntryUiInParams.iMailBoxId );
       
   142         }
       
   143     
       
   144     // set iOwnerRole, use entry at index zero:
       
   145         
       
   146 	CCalEntry& baseEntry = *( iArrayOfEntries[0] );	
       
   147 	if ( iMRMailboxUtils.IsOrganizerL( baseEntry ) )
       
   148 	    {
       
   149 	    iOwnerRole = EOrganiser;
       
   150 	    }	    
       
   151     else
       
   152         {        
       
   153 	    CCalAttendee* thisAttendee = iMRMailboxUtils.ThisAttendeeL( baseEntry );
       
   154     	if ( thisAttendee )
       
   155     		{
       
   156     		switch ( thisAttendee->RoleL() )
       
   157     			{
       
   158     			case CCalAttendee::EOptParticipant:
       
   159     				{
       
   160     				iOwnerRole = EOptionalParticipant;
       
   161     				break;
       
   162     				}
       
   163     			case CCalAttendee::ENonParticipant:
       
   164     				{
       
   165     				iOwnerRole = ENonRequiredParticipant;
       
   166     				break;
       
   167     				}
       
   168                 case CCalAttendee::EReqParticipant: // fall through
       
   169     			case CCalAttendee::EChair: // fall through
       
   170     			default:
       
   171     				{ // Note: chair MUST NOT be interpreted as organizer!
       
   172     				  // Req.participant is not correct either but has less
       
   173     				  // side effects.
       
   174     				iOwnerRole = ERequiredParticipant;
       
   175     				break;
       
   176     				}
       
   177     			}
       
   178     		}
       
   179         }
       
   180     }
       
   181 
       
   182 CMRProcessor::TOwnerRole CMRProcessor::OwnerRole()
       
   183     {
       
   184     return iOwnerRole;
       
   185     }
       
   186 
       
   187 MMRUtilsTombsExt::TMRUtilsDbResult CMRProcessor::ProcessingDbResult()
       
   188     {
       
   189     return iCombinedScenData.iUtilsDBResult;
       
   190     }
       
   191 
       
   192 TInt CMRProcessor::ProcessingResultOp()
       
   193     {
       
   194     return iCombinedScenData.iMROperation;
       
   195     }
       
   196 
       
   197 MMRModelInterface::TEditingMode CMRProcessor::EditingMode() const
       
   198     {
       
   199     return iEditMode;
       
   200     }
       
   201     
       
   202 void CMRProcessor::SetEditingModeL( MMRModelInterface::TEditingMode aEditMode )
       
   203     {
       
   204     __ASSERT_DEBUG( iEditMode == MMRModelInterface::EModeNotSet,
       
   205                     Panic( EEditModeResetAttempt ) );    
       
   206     iEditMode = aEditMode;
       
   207     if ( aEditMode == MMRModelInterface::EEditMeeting )
       
   208         { // if we switch to edit meeting mode the combined entry must be re-set
       
   209         CCalEntry* entry = iArrayOfEntries[0];
       
   210         ResetCombinedEntryL( *entry, TCalTime() );                
       
   211 		iCombinedEntry->CopyFromL( *entry );		
       
   212 		iCombinedEntry->SetMethodL( entry->MethodL() );        
       
   213         }
       
   214     // no need to do anything if aEditMode == EEditInstance since that is the
       
   215     // default when launching the MR Viewer with incoming entry array.
       
   216     }
       
   217 
       
   218 // TODO: CMRUtils::CheckEntryCondL doesn't, and shouldn't (because of e.g.
       
   219 // calendar sync), notice problems with out-of-date entry (meeting occurred in past),
       
   220 // but in processor we should identify that situation. Possibly add a new processing
       
   221 // struct data field, and possibly a new TMROperation code also?
       
   222 
       
   223 // TODO: somewhere we must set outparams: saved, meeting/instance deleted,
       
   224 // but is processor a good place for it.     
       
   225 
       
   226 // TODO: when handling processing leave, we must take care that iCombinedEntry
       
   227 // is set to point to some entry, or to ensure that there doesn't become an access
       
   228 // violation because someone is trying to access it.
       
   229 void CMRProcessor::ProcessEntriesL()
       
   230 	{
       
   231 	if ( iArrayOfEntries.Count() < 1 )
       
   232 	    {
       
   233 	    User::Leave( KErrArgument );
       
   234 	    }	    
       
   235     CCalEntry* entry = iArrayOfEntries[0];
       
   236     
       
   237     // Sets phone owner in entries and in iOwnerRole, this must be done at
       
   238     // early stage, before trying to read organizer or attendee fields
       
   239     SetPhoneOwnerL();
       
   240 	    
       
   241 	// set common values to scenario data
       
   242 	iCombinedScenData.iMethod = entry->MethodL();
       
   243 	iCombinedScenData.iCallingApp = CallingApp();
       
   244 	iCombinedScenData.iOpenedMailbox = OpenedMailboxL( *entry );
       
   245 	iCombinedScenData.iEditorMode = EditorMode();
       
   246     if ( iCombinedScenData.iEditorMode == MAgnEntryUi::EViewEntry )
       
   247         { // in other cases editing mode will be decided later, but 
       
   248           // in EViewEntry case we know it already now
       
   249         SetEditingModeL( MMRModelInterface::EViewOnly );
       
   250         }
       
   251 
       
   252     TRAPD( err,
       
   253         {        
       
   254         if ( entry->MethodL() == CCalEntry::EMethodReply )
       
   255             {
       
   256             ProcessResponseArrayL();
       
   257             }        
       
   258         else 
       
   259             {        
       
   260             if ( iEntryUiInParams.iCallingApp.iUid == KUidCalendarApplication )
       
   261                 {
       
   262                 ProcessRequestInCalendarL();
       
   263                 }
       
   264             else
       
   265                 {
       
   266                 ProcessRequestOrCancelArrayL();
       
   267                 }
       
   268             }        
       
   269         } );
       
   270     if ( err != KErrNone )
       
   271         {
       
   272         ProcessErrorL();
       
   273         }            
       
   274 	}
       
   275 		
       
   276 /**
       
   277 * In case of calendar we show in start-up:
       
   278 * a) parent entry if meeting non-repeating
       
   279 * b) existing child entry if received as input
       
   280 * c) newly created child if repeating meeting but child doesn't exist
       
   281 * User may later on choose "edit series" mode if meeting is repeating.
       
   282 */
       
   283 void CMRProcessor::ProcessRequestInCalendarL()
       
   284     {
       
   285 	if ( iArrayOfEntries.Count() != 1 )
       
   286 	    {
       
   287 	    // From calendar we support only receiving single entry at a time
       
   288 	    User::Leave( KErrArgument );
       
   289 	    }
       
   290 	
       
   291     CCalEntry* entry = iArrayOfEntries[0];
       
   292     
       
   293     iCombinedScenData.iMROperation =
       
   294         MatchScenarioToDataL( iCombinedScenData, *entry );
       
   295     if ( iCombinedScenData.iMROperation <= ENullOp )
       
   296         {
       
   297         User::Leave( KErrArgument );
       
   298         }    
       
   299 
       
   300     if ( MREntryConsultant::IsRepeatingMeetingL( *entry ) )
       
   301         {
       
   302         // reset combined entry, it will be a child representing the instance
       
   303         ResetCombinedEntryL( *entry, iEntryUiInParams.iInstanceDate );
       
   304         
       
   305         if ( MREntryConsultant::IsModifyingEntryL( *entry ) )
       
   306             { // entry exists for the instance, use that
       
   307     		iCombinedEntry->CopyFromL( *entry );		
       
   308     		iCombinedEntry->SetMethodL( entry->MethodL() );
       
   309             }
       
   310         else
       
   311             { // create a fully populated child representing the instance
       
   312             SetInstanceStartAndEndL( *iCombinedEntry,
       
   313                                      *entry, 
       
   314                                      iEntryUiInParams.iInstanceDate );                      
       
   315             CCalenInterimUtils2::PopulateChildFromParentL( *iCombinedEntry,
       
   316                                                            *entry );
       
   317             }
       
   318         }
       
   319     else
       
   320         { // non-repeating meeting -> view/edit entire meeting
       
   321         // reset combined entry, it will be a parent
       
   322         ResetCombinedEntryL( *entry, TCalTime() );                
       
   323 		iCombinedEntry->CopyFromL( *entry );		
       
   324 		iCombinedEntry->SetMethodL( entry->MethodL() );
       
   325 		
       
   326 		// in case of non-repeating meeting the editing mode is always
       
   327 		// EEditMeeting and user doesn't have a possibility to change it
       
   328 		SetEditingModeL( MMRModelInterface::EEditMeeting );     
       
   329         }    
       
   330     }
       
   331 	
       
   332 void CMRProcessor::ProcessResponseArrayL()
       
   333 	{
       
   334 
       
   335 // TODO: we must check that entry isn't out of date or cancelled,
       
   336 // should we do that in HandleResponseStatusL or where? It would be
       
   337 // better to do it before calling TryCreateModForResponseL() to avoid
       
   338 // unnecessary mod entry in	db.
       
   339 	
       
   340 	// 1. Handle entry at index 0 ,it may be response to a parent or a child
       
   341 		
       
   342 	CCalEntry& response = *( iArrayOfEntries[0] );
       
   343 		
       
   344 	// when checking against db entry, respond should look like a valid update:
       
   345 	iCombinedScenData.iUtilsDBResult = iMRUtils.CheckEntryCondL( response );
       
   346 	
       
   347 	// ...but if attendee has responded to an instance of a meeting, then
       
   348 	// corresponding modifying entry doesn't necessarily exist -> create it:
       
   349 	if ( iCombinedScenData.iUtilsDBResult == MMRUtilsTombsExt::ECheckedValidNew )
       
   350 	    {	    
       
   351 	    CreateModForResponseL( response, iCombinedScenData );
       
   352 	    }
       
   353 	
       
   354 	// check response validity on method level and set status to request, if ok:
       
   355 	CCalEntry* request = HandleResponseStatusL( response, iCombinedScenData );
       
   356 	if ( request )
       
   357 	    {
       
   358 	    CleanupStack::PushL( request );
       
   359     	// match to scenario, entry to be updated is request, not response
       
   360         MatchScenarioAndSaveIfNeededL( *request, iCombinedScenData );
       
   361 	    }
       
   362     else
       
   363         {
       
   364         // if request not found saving won't occur, we still perform matching
       
   365         // to get scenario completed, we give response as a parameter but it
       
   366         // won't be used in this case
       
   367         MatchScenarioAndSaveIfNeededL( response, iCombinedScenData );
       
   368         }
       
   369 		
       
   370     // 2. Go through additional entries, they don't affect combined entry
       
   371     //    and related functionality
       
   372 
       
   373     TScenarioData tmpScenario( iCombinedScenData );
       
   374 	TInt count( iArrayOfEntries.Count() );
       
   375 	for ( TInt i( 1 ); i < count; ++i )
       
   376 		{
       
   377 		CCalEntry& tmpResponse = *( iArrayOfEntries[i] );
       
   378 		tmpScenario.iUtilsDBResult = iMRUtils.CheckEntryCondL( tmpResponse );
       
   379 	    if ( tmpScenario.iUtilsDBResult == MMRUtilsTombsExt::ECheckedValidNew )
       
   380 	        {	    
       
   381 	        CreateModForResponseL( response, tmpScenario );
       
   382 	        }		
       
   383 		CCalEntry* tmpRequest = HandleResponseStatusL( response, tmpScenario );
       
   384 		if ( tmpRequest )
       
   385 		    { // for additional entries we only match scenario if request exists,
       
   386 		      // otherwise it is unnecessary
       
   387 		    CleanupStack::PushL( tmpRequest );
       
   388 		    MatchScenarioAndSaveIfNeededL( *tmpRequest, tmpScenario );
       
   389 		    CleanupStack::PopAndDestroy( tmpRequest );
       
   390 		    }
       
   391 		}
       
   392     
       
   393     // 3. Set combined entry == first response possibly mixed with request
       
   394 
       
   395     ResetCombinedEntryL( response, response.RecurrenceIdL() );
       
   396     iCombinedEntry->CopyFromL( response );
       
   397     iCombinedEntry->SetMethodL( CCalEntry::EMethodReply );
       
   398 
       
   399 	if ( iCombinedScenData.iMROperation == EUpdateStatusToCalendar )
       
   400 		{
       
   401     	// TODO: set response summary with status information    	
       
   402     	iCombinedEntry->SetLocationL( request->LocationL() );
       
   403     	iCombinedEntry->SetStartAndEndTimeL( request->StartTimeL(),
       
   404     	                                     request->EndTimeL() );
       
   405         // Response won't necessarily contain RRule/RDate information, but
       
   406         // that isn't shown to the user either -> not needed in iCombinedEntry.
       
   407 		}
       
   408     else
       
   409         {
       
   410         User::Leave( KErrArgument );
       
   411         }
       
   412         
       
   413     if ( request )
       
   414         {
       
   415         CleanupStack::PopAndDestroy( request );
       
   416         }
       
   417 	}
       
   418 	
       
   419 /**
       
   420 * This method assumes that response has been verified to be valid.
       
   421 */
       
   422 void CMRProcessor::CreateModForResponseL(
       
   423     const CCalEntry& aResponse,
       
   424     TScenarioData& aScenarioData ) const
       
   425     {
       
   426     // fetch parent:
       
   427     CCalEntry* request = iMRUtils.FetchEntryL( aResponse.UidL(),
       
   428                                                TCalTime() );
       
   429     CleanupStack::PushL( request );
       
   430     // create modifying entry:
       
   431     HBufC8* modUid = request->UidL().AllocLC();
       
   432 	CCalEntry* mod = CCalEntry::NewL( CCalEntry::EAppt,
       
   433                                       modUid,
       
   434                                       CCalEntry::EMethodRequest,
       
   435                                       request->SequenceNumberL(),
       
   436                                       aResponse.RecurrenceIdL(),
       
   437                                       CalCommon::EThisOnly );
       
   438     CleanupStack::Pop( modUid ); // ownership transferred
       
   439     CleanupStack::PushL( mod );
       
   440     
       
   441     // In this case response shouldn't really have any RRules or RDates,
       
   442     // and the recurrence id should specify the instance which attendee
       
   443     // responded to. Therefore create modifying entry representing that
       
   444     // instance.
       
   445     SetInstanceStartAndEndL( *mod, *request, aResponse.RecurrenceIdL() );
       
   446     // Populate modifying entry fields, especially it is important that
       
   447     // also entire attendee list gets copied, since this modifying entry
       
   448     // will be exceptioned in the parent
       
   449     CCalenInterimUtils2::PopulateChildFromParentL( *mod, *request );
       
   450     // Modifying entry must have the same DTSTAMP as originating entry,
       
   451     // since in this case they were sent as one request
       
   452     mod->SetDTStampL( request->DTStampL() );        
       
   453     // Set modifying request as sent!
       
   454     // TODO: this will be done differently when Symbian supports other fields
       
   455     // to indicate sending status
       
   456     mod->SetStatusL( CCalEntry::EConfirmed );
       
   457         
       
   458     MMRUtilsTombsExt::TMRUtilsDbResult res = iMRUtils.StoreL( *mod, EFalse );
       
   459     if ( res != MMRUtilsTombsExt::EStoredUpdate )
       
   460         { // store should succeed if response was for a valid instance
       
   461         User::Leave( KErrGeneral );
       
   462         }
       
   463     
       
   464     CleanupStack::PopAndDestroy( 2 ); // mod, request
       
   465     
       
   466     // now there exists a modifying entry and response is
       
   467     // an update to it:
       
   468     aScenarioData.iUtilsDBResult = MMRUtilsTombsExt::ECheckedValidUpdate;
       
   469     }
       
   470  
       
   471 CCalEntry* CMRProcessor::HandleResponseStatusL(
       
   472     const CCalEntry& aResponse,
       
   473     TScenarioData& aScenarioData ) const
       
   474     {
       
   475     aScenarioData.iMethodLevelValidity = EInvalid;
       
   476     if ( aResponse.AttendeesL().Count() != 1 )
       
   477         { // response MUST have exactly one attendee, the respondent
       
   478         User::Leave( KErrArgument );
       
   479         }
       
   480 	CCalAttendee& respondent = *( aResponse.AttendeesL()[0] );	
       
   481 
       
   482     CCalEntry* request = iMRUtils.FetchEntryL( aResponse.UidL(),
       
   483                                                aResponse.RecurrenceIdL() );
       
   484     if ( request  )
       
   485         {
       
   486         CleanupStack::PushL( request );        	
       
   487     	TBool changed( EFalse );
       
   488     	CCalAttendee* dbAttendee =
       
   489     	    RespondentInRequestL( aResponse, *request, changed );
       
   490     	if ( dbAttendee && changed )
       
   491     		{
       
   492     		dbAttendee->SetStatusL( respondent.StatusL() );
       
   493     		aScenarioData.iMethodLevelValidity = EValidNeedsSave;
       
   494     		}
       
   495         else if ( dbAttendee && !changed )
       
   496             {
       
   497             aScenarioData.iMethodLevelValidity = EValidNoNeedSave;
       
   498             }
       
   499         CleanupStack::Pop( request );
       
   500         }
       
   501     return request; // may be NULL
       
   502     }
       
   503 
       
   504 /**
       
   505 * We always show the first entry, if it is a child user will see that
       
   506 * child and responses affect that (and other children in the array)
       
   507 * If the first entry is a parent responses affect also children in the array.
       
   508 *
       
   509 * Also in case of cancellation entry is stored to db instead of just updating
       
   510 * the status, this means that e.g. description may be changed, but we
       
   511 * consider that is the desired behavior.
       
   512 */
       
   513 void CMRProcessor::ProcessRequestOrCancelArrayL()
       
   514 	{
       
   515 	// 1. Handle entry at index 0 ,it may be a parent or a child	
       
   516 	
       
   517 	CCalEntry& entry = *( iArrayOfEntries[0] );
       
   518 	iCombinedScenData.iUtilsDBResult = iMRUtils.CheckEntryCondL( entry );
       
   519     MatchScenarioAndSaveIfNeededL( entry, iCombinedScenData );
       
   520 		
       
   521     // 2. Go through additional entries, they don't affect combined entry
       
   522     //    and related functionality
       
   523 
       
   524     TScenarioData tmpScenario( iCombinedScenData );
       
   525 	TInt count( iArrayOfEntries.Count() );
       
   526 	for ( TInt i( 1 ); i < count; ++i )
       
   527 		{
       
   528 		CCalEntry& tmpEntry = *( iArrayOfEntries[i] );
       
   529 		tmpScenario.iUtilsDBResult = iMRUtils.CheckEntryCondL( tmpEntry );
       
   530 		MatchScenarioAndSaveIfNeededL( tmpEntry, tmpScenario );
       
   531 		}
       
   532     
       
   533     // 3. Set combined entry == first entry in the input array
       
   534 
       
   535     ResetCombinedEntryL( entry, entry.RecurrenceIdL() );
       
   536 
       
   537 	if ( iCombinedScenData.iMROperation == EViewExistingEntry )
       
   538 		{
       
   539 		iCombinedEntry->CopyFromL( entry );				
       
   540 		iCombinedEntry->SetMethodL( entry.MethodL() );				
       
   541 		}
       
   542     else if ( iCombinedScenData.iMROperation == ELoadIdenticalEntryFromDB || 
       
   543               iCombinedScenData.iMROperation ==EStoreEntryToCalendar )
       
   544 		{
       
   545 		// Identical entry exists in db -> fetch parent entry
       
   546 		// from db and prepare for viewing
       
   547 		ReadEntryFromDbL( entry, *iCombinedEntry );
       
   548 		}
       
   549     else
       
   550         {
       
   551         User::Leave( KErrArgument );
       
   552         }
       
   553     }
       
   554 
       
   555 TBool CMRProcessor::MatchScenarioAndSaveIfNeededL(
       
   556     CCalEntry& aCalEntry,
       
   557     TScenarioData& aScenarioData ) const
       
   558     {
       
   559     TBool retVal( EFalse );	
       
   560 	aScenarioData.iMROperation =
       
   561 	    MatchScenarioToDataL( aScenarioData, aCalEntry );
       
   562 	
       
   563 	if ( aScenarioData.iMROperation == EStoreEntryToCalendar )
       
   564 		{
       
   565 		MMRUtilsTombsExt::TMRUtilsDbResult dbResult =
       
   566 		    iMRUtils.StoreL( aCalEntry, EFalse );
       
   567 		if ( dbResult < MMRUtilsTombsExt::EUndefined )
       
   568 			{ // store shouldn't fail if all checks were successful
       
   569 			User::Leave( KErrGeneral );
       
   570 			}
       
   571         else
       
   572 			{ // we have saved something
       
   573 			retVal = ETrue;
       
   574 			}
       
   575 		}
       
   576     else if ( aScenarioData.iMROperation == EUpdateStatusToCalendar )
       
   577         { // update shouldn't fail if all checks were successful
       
   578         User::LeaveIfError( iMRUtils.UpdateEntryL( aCalEntry ) );        
       
   579         retVal = ETrue;
       
   580         }
       
   581     return retVal;
       
   582     }
       
   583 
       
   584 void CMRProcessor::ProcessErrorL()
       
   585     {
       
   586     switch ( iCombinedScenData.iMROperation )
       
   587         {
       
   588         case EErrorSituation:
       
   589             {
       
   590     		if ( iCombinedScenData.iUtilsDBResult ==
       
   591 	    	     MMRUtilsTombsExt::EErrorHasBeenDeleted )
       
   592 		        { // possibly valid entry but has been deleted from the phone
       
   593 		        
       
   594         	    // request has been deleted, we show the received response then
       
   595         	    // according to UI spec. if organizer cancels request it gets deleted
       
   596         	    // TODO: show info note Original meeting request is not in phone’s calendar. §qtn.mail.mtg.req.note.no.mtg§ with
       
   597                 // TODO: this note should be shown always as an opening note, some opening notes
       
   598                 // are shown only when launching for the first time -> handle that!
       
   599     		    }
       
   600             else
       
   601                 {// TODO: handle
       
   602                 }
       
   603             break;
       
   604             }
       
   605         case EErrorUnexpectedViewOnly:
       
   606             { // possibly valid entry but unexpected in this context
       
   607             // TODO: handle
       
   608             break;
       
   609             }
       
   610         case EErrorObsoleteViewOnly:
       
   611             { // possibly valid entry but obsolete in this context
       
   612             // TODO: handle
       
   613             break;
       
   614             }
       
   615         default:
       
   616             {
       
   617             User::Leave( KErrGeneral );
       
   618             }       
       
   619         }
       
   620     // show errorneus entry, but only for viewing
       
   621 	CCalEntry& entry = *( iArrayOfEntries[0] );
       
   622     ResetCombinedEntryL( entry, entry.RecurrenceIdL() );
       
   623 	iCombinedEntry->CopyFromL( entry );				
       
   624 	iCombinedEntry->SetMethodL( entry.MethodL() );	
       
   625     }
       
   626 
       
   627 TInt CMRProcessor::MatchScenarioToDataL(
       
   628     TScenarioData currentScenario,
       
   629     const CCalEntry& aEntry ) const
       
   630 	{
       
   631     MRDATA_LOG("# start scenario match #");
       
   632     MRDATA_LOG1("entry name: %S", &(aEntry.SummaryL()));
       
   633     	
       
   634 	TInt retVal( EErrorSituation );
       
   635 	
       
   636 	CArrayFixFlat<TInt>* scenarioValues =
       
   637 	    new( ELeave ) CArrayFixFlat<TInt>( dataFieldCount-1 );
       
   638 
       
   639 	CleanupStack::PushL( scenarioValues );
       
   640 
       
   641     // Order of fields must match the order used in ProcessingStructs.h
       
   642 	scenarioValues->AppendL( currentScenario.iMethod );
       
   643 	scenarioValues->AppendL( currentScenario.iCallingApp );
       
   644 	scenarioValues->AppendL( currentScenario.iOpenedMailbox );
       
   645 	scenarioValues->AppendL( currentScenario.iUtilsDBResult );
       
   646 	scenarioValues->AppendL( currentScenario.iMethodLevelValidity );
       
   647 	scenarioValues->AppendL( currentScenario.iEditorMode );
       
   648 	
       
   649 	// Go through each predefined scenario and see if one of them matches
       
   650 	for ( TInt scenarioCounter( 0 );
       
   651 	      scenarioCounter < scenarioCount;
       
   652 	      scenarioCounter++ )
       
   653 		{
       
   654         TBool scenarioMismatch( EFalse );
       
   655 		const TInt* arrayPtr = scenarioArray[scenarioCounter];
       
   656 		for ( TInt i( 0 ); i < dataFieldCount-1; i++ )
       
   657 			{
       
   658 			TInt arrayVal = *( arrayPtr+i );
       
   659 			TInt scenarioVal = scenarioValues->At( i );
       
   660 
       
   661 			if( arrayVal != ENotSet )
       
   662 				{
       
   663 				if ( scenarioVal != arrayVal )
       
   664 				    {				    
       
   665 					scenarioMismatch = ETrue;
       
   666 				    }
       
   667 				}
       
   668 			}
       
   669 
       
   670 		if ( !scenarioMismatch )
       
   671 			{ // Match was found, retVal is operation corresponding to the
       
   672 			  // matching scenario, i.e. the last field in that scenario array
       
   673 			retVal = *( arrayPtr + dataFieldCount - 1 );
       
   674             MRDATA_LOG1("scenario id: %d", scenarioCounter );
       
   675             MRDATA_LOG1("operation: %d", retVal );			
       
   676 			break;
       
   677 			}
       
   678 		}
       
   679 
       
   680 	CleanupStack::PopAndDestroy( scenarioValues );
       
   681 	    
       
   682     MRDATA_LOG("# end scenario match #");	
       
   683 	return retVal;
       
   684 	}
       
   685 
       
   686 TInt CMRProcessor::CallingApp() const
       
   687 	{
       
   688 	TInt callingApp = iEntryUiInParams.iCallingApp.iUid;
       
   689     TInt retVal( ENotSet );
       
   690 
       
   691 	switch ( callingApp )
       
   692 		{
       
   693 		case KUidCalendarApplication:
       
   694 			{
       
   695 			retVal = ECallerCalendarApp;
       
   696 			break;
       
   697 			}
       
   698 		case KUidMailApplication:
       
   699 			{
       
   700 			retVal = ECallerMailApp;
       
   701 			break;
       
   702 			}
       
   703 		case KUidBVAApplication:
       
   704 			{
       
   705 			retVal = ECallerBVApp;
       
   706 			break;
       
   707 			}
       
   708 		default:
       
   709 			{
       
   710 			//error situation, should never come here
       
   711 			retVal = ENotSet;
       
   712 			break;
       
   713 			}
       
   714 		}
       
   715     return retVal;
       
   716 	}
       
   717 
       
   718 TInt CMRProcessor::EditorMode() const
       
   719     {
       
   720     if ( iEntryUiInParams.iCallingApp.iUid == KUidCalendarApplication )
       
   721         { // Editor mode is relevant only when calling app is calendar
       
   722         return iEntryUiInParams.iEditorMode;
       
   723         }
       
   724     else
       
   725         {
       
   726         return MAgnEntryUi::EViewEntry;
       
   727         }
       
   728     }
       
   729 
       
   730 // TODO: should we also support drafts mailbox type?
       
   731 // That should be done by checking IsSent( entry ) in the outbox case.
       
   732 TInt CMRProcessor::OpenedMailboxL( const CCalEntry& aEntry ) const
       
   733 	{
       
   734 	TInt retVal( ENotSet );	
       
   735 	if ( iEntryUiInParams.iCallingApp.iUid == KUidMailApplication )	
       
   736 	    {	    
       
   737         // Organizer sends other types than responses, and attendees
       
   738         // on the other hand send only responses
       
   739         TBool isOrganizer( iMRMailboxUtils.IsOrganizerL( aEntry ) );
       
   740         TBool isResponse( aEntry.MethodL() == CCalEntry::EMethodReply );
       
   741         if ( ( isOrganizer && !isResponse ) || ( !isOrganizer && isResponse  ) )
       
   742             {
       
   743             retVal = EOpenedFromOutbox;
       
   744             }
       
   745         else
       
   746             {
       
   747             retVal = EOpenedFromInbox;
       
   748             }
       
   749 	    }
       
   750     return retVal;
       
   751 	}
       
   752 
       
   753 CCalAttendee* CMRProcessor::RespondentInRequestL(
       
   754     const CCalEntry& aResponse,
       
   755     const CCalEntry& aRequest,
       
   756     TBool& aStatusChange ) const
       
   757 	{
       
   758 	__ASSERT_DEBUG( aResponse.MethodL() == CCalEntry::EMethodReply,
       
   759 	                Panic( EUnexpectedEntryMethodType ) );   
       
   760 
       
   761 	aStatusChange = EFalse;
       
   762 	CCalAttendee* respondent = aResponse.AttendeesL()[0];
       
   763 	CCalAttendee* dbAttendee =
       
   764 	    MREntryConsultant::EqualAttendeeL( *respondent, aRequest );
       
   765     if ( dbAttendee )
       
   766         {            
       
   767 		if ( dbAttendee->StatusL() != respondent->StatusL() )
       
   768             {
       
   769             aStatusChange = ETrue;
       
   770             }
       
   771         }
       
   772     return dbAttendee;
       
   773 	}
       
   774 	
       
   775 void CMRProcessor::ReadEntryFromDbL(
       
   776     const CCalEntry& aSourceEntry,
       
   777     CCalEntry& aTargetEntry ) const
       
   778 	{
       
   779     CCalEntry* dbEntry = iMRUtils.FetchEntryL( aSourceEntry.UidL(),
       
   780                                                aSourceEntry.RecurrenceIdL() );
       
   781 	CleanupStack::PushL( dbEntry );	
       
   782 	aTargetEntry.CopyFromL( *dbEntry );
       
   783 	aTargetEntry.SetMethodL( dbEntry->MethodL() );	
       
   784 
       
   785 	CleanupStack::PopAndDestroy( dbEntry );
       
   786 	}
       
   787  
       
   788 // method to be called externally
       
   789 void CMRProcessor::RefreshViewableEntryL()
       
   790     {
       
   791     __ASSERT_DEBUG( iCombinedEntry, Panic( ECombinedCalEntryNull ) );
       
   792     if ( MREntryConsultant::ExistsInDbL( *iCombinedEntry, iMRUtils ) )
       
   793         { // this method is only feasible if entry exists in the database,
       
   794           // otherwise we cannot refresh anything...
       
   795         ResetCombinedEntryL( *iCombinedEntry, iCombinedEntry->RecurrenceIdL() );
       
   796         ReadEntryFromDbL( *iCombinedEntry, *iCombinedEntry );
       
   797         }
       
   798     }
       
   799 
       
   800 void CMRProcessor::ResetCombinedEntryL(
       
   801     const CCalEntry& aBase, 
       
   802     const TCalTime& aInstanceDate )
       
   803     {
       
   804 	HBufC8* calUid = aBase.UidL().AllocLC();
       
   805     CCalEntry* newEntry;
       
   806     if ( aInstanceDate.TimeUtcL() != Time::NullTTime() )
       
   807         {
       
   808     	newEntry = CCalEntry::NewL( CCalEntry::EAppt,
       
   809 	                                calUid,
       
   810 	                                aBase.MethodL(),
       
   811 	                                aBase.SequenceNumberL(),
       
   812 	                                aInstanceDate,
       
   813 	                                CalCommon::EThisOnly );
       
   814         }
       
   815     else
       
   816         {        
       
   817     	newEntry = CCalEntry::NewL( CCalEntry::EAppt,
       
   818 	                                calUid,
       
   819 	                                aBase.MethodL(),
       
   820 	                                aBase.SequenceNumberL() );
       
   821         }
       
   822     delete iCombinedEntry;
       
   823     iCombinedEntry = NULL;    
       
   824     iCombinedEntry = newEntry;
       
   825 	CleanupStack::Pop( calUid ); // ownership transferred to iCombinedEntry    
       
   826     }
       
   827     
       
   828 void CMRProcessor::SetInstanceStartAndEndL(
       
   829     CCalEntry& aChild,
       
   830     const CCalEntry& aParent,
       
   831     const TCalTime& aInstanceStart ) const
       
   832     {
       
   833     TTime dtstart( aParent.StartTimeL().TimeUtcL() ); // first instance start
       
   834     TTime dtend( aParent.EndTimeL().TimeUtcL() ); // first instance end
       
   835     TTime instStart( aInstanceStart.TimeUtcL() );    
       
   836     TTime nullTime( Time::NullTTime() );
       
   837     if ( dtstart == nullTime || dtend == nullTime || instStart == nullTime )
       
   838         {
       
   839         User::Leave( KErrArgument );
       
   840         }        
       
   841     
       
   842     TTimeIntervalMicroSeconds duration( 0 );
       
   843     duration = dtend.MicroSecondsFrom( dtstart );
       
   844     TCalTime instEnd;    
       
   845 	instEnd.SetTimeUtcL( instStart + duration );
       
   846 
       
   847     aChild.SetStartAndEndTimeL( aInstanceStart, instEnd );
       
   848     }
       
   849