///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using EMAPILib;
using JetBrains.Omea.Base;
using JetBrains.Omea.Contacts;
using JetBrains.Omea.OpenAPI;
namespace JetBrains.Omea.OutlookPlugin
{
internal class ChangeWatchers
{
private ImportedContactsChangeWatcher _importedContactsChangeWatcher = new ImportedContactsChangeWatcher();
private AddressBookChangeWatcher _addressBookChangeWatcher = new AddressBookChangeWatcher();
private UnreadEmailChangeWatcher _unreadWatcher = new UnreadEmailChangeWatcher();
private IgnoredFoldersChangeWatcher _ignoredFolderWatcher = new IgnoredFoldersChangeWatcher();
private ImportanceMailChangeWatcher _importanceMailWathcer = new ImportanceMailChangeWatcher();
private AnnotationMailChangeWatcher _annotationMailWathcer = new AnnotationMailChangeWatcher();
private TasksChangeWatcher _tasksChangeWatcher = new TasksChangeWatcher();
private LinksChangeWatcher _linksChangeWatcher = new LinksChangeWatcher();
//private ContactEntryIDWatcher _contactEntryIDWatcher = new ContactEntryIDWatcher();
private CategoriesWatcher _categoriesWatcher = new CategoriesWatcher();
private PhoneChangeWatcher _phoneChangeWatcher = new PhoneChangeWatcher();
public void Watch()
{
//if ( Settings.TraceContactChanges )
{
//_contactEntryIDWatcher.Watch();
}
_categoriesWatcher.Watch();
_addressBookChangeWatcher.Watch();
_unreadWatcher.Watch();
_importanceMailWathcer.Watch();
_tasksChangeWatcher.Watch();
_linksChangeWatcher.Watch();
_ignoredFolderWatcher.Watch();
_importedContactsChangeWatcher.Watch();
_phoneChangeWatcher.Watch();
if ( Settings.CreateAnnotationFromFollowup )
{
_annotationMailWathcer.Watch();
}
}
}
internal class IgnoredFoldersChangeWatcher
{
private IResourceList _ignoredFolderList = null;
public void Watch()
{
_ignoredFolderList = Folder.GetIgnoredFoldersLive();
_ignoredFolderList.ResourceAdded += new ResourceIndexEventHandler( OnIgnoredFolderChanged );
_ignoredFolderList.ResourceDeleting += new ResourceIndexEventHandler( OnIgnoredFolderChanged );
}
private void OnIgnoredFolderChanged( object sender, ResourceIndexEventArgs e )
{
Core.ResourceAP.QueueJob( new FolderIgnoringChanged( e.Resource ) );
}
}
internal class UnreadEmailChangeWatcher
{
private IResourceList _unreadList = null;
public void Watch()
{
_unreadList =
Core.ResourceStore.FindResourcesLive( STR.Email, Core.Props.IsUnread, true );
_unreadList.ResourceAdded += new ResourceIndexEventHandler( OnUnreadItemChanged );
_unreadList.ResourceDeleting += new ResourceIndexEventHandler( OnUnreadItemChanged );
}
private void OnUnreadItemChanged( object sender, ResourceIndexEventArgs e )
{
OutlookSession.OutlookProcessor.QueueJob( JobPriority.AboveNormal, "Processing unread item changed",
new ResourceDelegate( OnUnreadItemChangedImpl ), e.Resource );
}
private void OnUnreadItemChangedImpl( IResource emailResource )
{
Guard.NullArgument( emailResource, "emailResource" );
if ( emailResource.Type != STR.Email )
{
return;
}
PairIDs messageIDs = PairIDs.Get( emailResource );
if ( messageIDs == null )
{
return;
}
IResource folder = Mail.GetParentFolder( emailResource );
if ( folder != null && Folder.IsIMAPFolder( folder ) )
{
PairIDs folderIDs = PairIDs.Get( folder );
if ( folderIDs != null )
{
IEFolder mapiFolder = OutlookSession.OpenFolder( folderIDs.EntryId, folderIDs.StoreId );
if ( mapiFolder != null )
{
using ( mapiFolder )
{
try
{
mapiFolder.SetReadFlags( messageIDs.EntryId, emailResource.HasProp( Core.Props.IsUnread ) );
return;
}
catch ( COMException exception )
{
if ( exception.ErrorCode == ( unchecked( (int)0x80040604 ) ) )
{
StandartJobs.MessageBox( "Unspecified error. Can't change unread flag for email.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error );
return;
}
Core.ReportException( exception, ExceptionReportFlags.AttachLog );
}
}
}
}
}
IEMessage message = OutlookSession.OpenMessage( messageIDs.EntryId, messageIDs.StoreId );
if ( message != null )
{
using ( message )
{
bool unread = emailResource.HasProp( Core.Props.IsUnread );
message.SetUnRead( unread );
OutlookSession.SaveChanges( "Export Read/Unread flag" + emailResource.Id, message, messageIDs.EntryId );
}
}
}
}
internal class ImportanceMailChangeWatcher
{
private IResourceList _importanceMailList = null;
public void Watch()
{
_importanceMailList = Core.ResourceStore.FindResourcesWithPropLive( STR.Email, PROP.Importance );
_importanceMailList.ResourceChanged += new ResourcePropIndexEventHandler( OnImportanceMailChanged );
_importanceMailList.ResourceAdded += new ResourceIndexEventHandler( OnImportanceMailChanged );
_importanceMailList.ResourceDeleting += new ResourceIndexEventHandler( OnImportanceMailChanged );
_importanceMailList.AddPropertyWatch( PROP.Importance );
}
private void OnImportanceMailChanged( object sender, ResourcePropIndexEventArgs e )
{
OnImportanceMailChanged( e.Resource );
}
private void OnImportanceMailChanged( object sender, ResourceIndexEventArgs e )
{
OnImportanceMailChanged( e.Resource );
}
private void OnImportanceMailChanged( IResource resource )
{
OutlookSession.OutlookProcessor.QueueJob( JobPriority.AboveNormal, "Processing importance item changed",
new ResourceDelegate( OnImportanceMailChangedImpl ), resource );
}
private void OnImportanceMailChangedImpl( IResource emailResource )
{
if ( emailResource != null && emailResource.Type == STR.Email )
{
if ( Mail.MailInIMAP( emailResource ) )
{
return;
}
int importance = emailResource.GetIntProp( PROP.Importance );
PairIDs messageIDs = PairIDs.Get( emailResource );
if ( messageIDs == null )
{
return;
}
IEMessage message = OutlookSession.OpenMessage( messageIDs.EntryId, messageIDs.StoreId );
if ( message != null )
{
using ( message )
{
message.SetLongProp( MAPIConst.PR_IMPORTANCE, importance + 1 );
OutlookSession.SaveChanges( "Export importance flag for resource id = " + emailResource.Id, message, messageIDs.EntryId );
}
}
}
}
}
internal class TasksChangeWatcher
{
private IResourceList _tasksList = null;
public void Watch()
{
_tasksList = Core.ResourceStore.GetAllResourcesLive( STR.Task );
_tasksList.ResourceChanged += new ResourcePropIndexEventHandler( OnTaskChanged );
_tasksList.AddPropertyWatch( new int[]
{
PROP.Status, PROP.RemindDate, PROP.StartDate, Core.Props.Date, PROP.Description,
Core.Props.Subject, PROP.Priority
} );
_tasksList.ResourceAdded += new ResourceIndexEventHandler( OnTaskAdded );
_tasksList.ResourceDeleting += new ResourceIndexEventHandler( OnTaskDeleted );
}
private void OnTaskAdded( object sender, ResourceIndexEventArgs e )
{
if ( Settings.ExportTasks )
{
new ExportTaskDescriptor( e.Resource ).QueueJob( JobPriority.AboveNormal );
}
}
private void OnTaskDeleted( object sender, ResourceIndexEventArgs e )
{
if ( Settings.ExportTasks )
{
PairIDs IDs = PairIDs.Get( e.Resource );
if ( IDs != null )
{
OutlookSession.DeleteMessage( IDs.StoreId, IDs.EntryId, true );
}
}
}
private void OnTaskChanged( object sender, ResourcePropIndexEventArgs e )
{
if ( Settings.ExportTasks )
{
new ExportTaskDescriptor( e.Resource ).QueueJob( JobPriority.AboveNormal );
}
}
}
internal class ImportedContactsChangeWatcher
{
private ArrayList _importedContacts = new ArrayList();
private static bool _processingImportFromOutlook = false;
private static int _exportedContactID = -1;
private IResourceList _importedABs = null;
class AddressBookWatcher
{
private IResource _importedAB;
private IResourceList _contacts;
public AddressBookWatcher( IResource importedAB )
{
_importedAB = importedAB;
_contacts = _importedAB.GetLinksOfTypeLive( "Contact", "InAddressBook" );
_contacts.ResourceAdded += new ResourceIndexEventHandler( OnImportedContactAdded );
_contacts.ResourceChanged += new ResourcePropIndexEventHandler( OnImportedContactChanged );
_contacts.ResourceDeleting += new ResourceIndexEventHandler( OnImportedContactDeleting );
_contacts.AddPropertyWatch( new int[]
{
PROP.EntryID, ResourceProps.DisplayName,
Core.ContactManager.Props.EmailAddress,
Core.ContactManager.Props.LinkEmailAcct,
ContactManager._propBirthday,
ContactManager._propCompany,
ContactManager._propDescription,
ContactManager._propFirstName,
ContactManager._propHomePage,
ContactManager._propImported,
ContactManager._propJobTitle,
ContactManager._propLastName,
ContactManager._propMiddleName,
ContactManager._propPhone,
ContactManager._propSuffix,
ContactManager._propTitle,
ContactManager._propPhone,
ContactManager._propUserCreated
} );
}
private void OnImportedContactChanged( object sender, ResourcePropIndexEventArgs e )
{
string oldID = (string)e.ChangeSet.GetOldValue( PROP.EntryID );
object oldUserCreated = e.ChangeSet.GetOldValue( ContactManager._propUserCreated );
//-------------------------------------------------------------
// An Outlook Contact can be removed from the AB via two mechanisms:
// - moved to the Deleted Items. In such case the Outlook resource
// generally exists, and EntryID still exists (though it can be
// changed). For this case MAPI Listener's OnMailMoved is called,
// which calls Contact.RemoveFromSynch( IResource, newID ).
// - deleted physically. In this case EntryId is physically removed
// along with the object itself. Here MAPI Listener's OnMailDeleted
// is called, which calls Contact.RemoveFromSynch( IResource, true ).
// In both cases MAPI routines set "UserCreated" boolean prop to "true"
// which serves as indication for us to unlink the contact and the AB.
//-------------------------------------------------------------
if( oldUserCreated == null && e.Resource.HasProp( ContactManager._propUserCreated ) )
{
new ResourceProxy( e.Resource ).DeleteLink( "InAddressBook", _importedAB );
}
else
//-------------------------------------------------------------
// Specially go around the case when EntryId is being removed, this
// is covered (on the more general level) by previous condition.
// LX: this restriction is left "as is" from SergeZhulin.
//-------------------------------------------------------------
if ( e.Resource.HasProp( PROP.EntryID ) || oldID == null )
{
if ( !ProcessingImportFromOutlook && !ImportedContactsChangeWatcher.IsExportedContact( e.Resource ) )
{
OutlookSession.OutlookProcessor.QueueJob( new ExportContactDescriptor( e.Resource ) );
}
ImportedContactsChangeWatcher.CleanExportedContact( e.Resource );
}
}
private void OnImportedContactAdded( object sender, ResourceIndexEventArgs e )
{
ImportedContactAdded( e.Resource, _importedAB );
}
///
/// Method handles the deletion of a Contact resource from any supported
/// AB. Because of the particular definition of watched resource list
/// we have no need to check given resource (e.Resource) for actual
/// belonging to the AB.
///
private void OnImportedContactDeleting( object sender, ResourceIndexEventArgs e )
{
string storeID = _importedAB.GetStringProp( PROP.StoreID );
if ( storeID != null )
{
OutlookSession.DeleteMessage( storeID, e.Resource.GetStringProp( PROP.EntryID ), true );
Contact.RemoveFromSync( e.Resource );
}
}
}
public static void ImportedContactAdded( IResource contact, IResource AB )
{
if ( !ProcessingImportFromOutlook )
{
ImportedContactsChangeWatcher.SetExportedContact( contact );
OutlookSession.OutlookProcessor.QueueJob( new ExportContactDescriptor( contact, AB ) );
}
}
public static bool IsExportedContact( IResource contact )
{
return _exportedContactID == contact.Id;
}
public static void SetExportedContact( IResource contact )
{
_exportedContactID = contact.Id;
}
public static void CleanExportedContact( IResource contact )
{
_exportedContactID = -1;
}
public void Watch()
{
_importedABs = Core.ResourceStore.FindResourcesLive( "AddressBook", PROP.Imported, 1 );
_importedABs.ResourceAdded+=new ResourceIndexEventHandler(_importedABs_ResourceAdded);
foreach ( IResource importedAB in _importedABs )
{
_importedContacts.Add( new AddressBookWatcher( importedAB ) );
}
}
public static bool ProcessingImportFromOutlook { set { _processingImportFromOutlook = value; } get { return _processingImportFromOutlook; } }
private void _importedABs_ResourceAdded(object sender, ResourceIndexEventArgs e)
{
_importedContacts.Add( new AddressBookWatcher( e.Resource ) );
}
}
internal class CategoriesWatcher
{
private IResourceList _categoriesList = null;
public void Watch()
{
_categoriesList = Core.ResourceStore.GetAllResourcesLive( "Category" );
_categoriesList.ResourceChanged += new ResourcePropIndexEventHandler( CategoryChanged );
_categoriesList.AddPropertyWatch( Core.Props.Parent );
_categoriesList.AddPropertyWatch( Core.Props.Name );
}
private void ProcessCategories( IResource category, string resType )
{
IResourceList resources = category.GetLinksOfType( resType, "Category" );
foreach ( IResource resource in resources )
{
ExportCategories.Do( JobPriority.AboveNormal, resource );
}
}
private void ProcessCategoriesForContact( IResource category )
{
IResourceList resources = category.GetLinksOfType( STR.Contact, "Category" );
foreach ( IResource resource in resources )
{
OutlookSession.OutlookProcessor.QueueJob( JobPriority.AboveNormal, new ExportContactDescriptor( resource ) );
}
}
private void ProcessCategoriesRecursive( IResource category )
{
if ( Settings.SyncMailCategory )
{
ProcessCategories( category, STR.Email );
}
if ( Settings.SyncTaskCategory )
{
ProcessCategories( category, STR.Task );
}
if ( Settings.SyncContactCategory )
{
ProcessCategoriesForContact( category );
}
IResourceList subCategories = category.GetLinksTo( "Category", Core.Props.Parent );
foreach ( IResource resource in subCategories )
{
ProcessCategoriesRecursive( resource );
}
}
private void CategoryChanged( object sender, ResourcePropIndexEventArgs e )
{
if ( !Settings.SyncTaskCategory && !Settings.SyncMailCategory && !Settings.SyncContactCategory )
{
return;
}
if ( e.ChangeSet.IsPropertyChanged( Core.Props.Parent ) || e.ChangeSet.IsPropertyChanged( Core.Props.Name ) )
{
ProcessCategoriesRecursive( e.Resource );
}
}
}
internal class LinksChangeWatcher
{
public void Watch()
{
Core.ResourceStore.LinkAdded += new LinkEventHandler( LinkAdded );
Core.ResourceStore.LinkDeleted += new LinkEventHandler( LinkDeleted );
}
private void LinkAdded( object sender, LinkEventArgs e )
{
if ( e.Source.Type == STR.Email && e.Target.Type == STR.Category )
{
IResourceList resAttachments = e.Source.GetLinksOfType( null, PROP.Attachment );
foreach ( IResource attach in resAttachments.ValidResources )
{
Core.CategoryManager.AddResourceCategory( attach, e.Target );
}
}
LinkAddedOrDeleted( sender, e );
}
private void LinkDeleted( object sender, LinkEventArgs e )
{
if ( e.Source.Type == STR.Email && e.Target.Type == STR.Category )
{
IResourceList resAttachments = e.Source.GetLinksOfType( null, PROP.Attachment );
foreach ( IResource attach in resAttachments.ValidResources )
{
Core.CategoryManager.RemoveResourceCategory( attach, e.Target );
}
}
LinkAddedOrDeleted( sender, e );
}
private void LinkAddedOrDeleted( object sender, LinkEventArgs e )
{
if ( e.Source.Type == STR.Email )
{
if ( e.Target.Type == STR.Category && Settings.SyncMailCategory )
{
ExportCategories.Do( JobPriority.AboveNormal, e.Source );
}
else if ( e.Target.Type == STR.Flag )
{
ExportEmailFlag.Do( JobPriority.AboveNormal, e.Source );
}
}
else
if ( e.Source.Type == STR.Task )
{
if ( e.Target.Type == STR.Category && Settings.SyncTaskCategory )
{
ExportCategories.Do( JobPriority.AboveNormal, e.Source );
}
}
else
if ( e.Source.Type == STR.Contact )
{
if( e.Target.Type == STR.Category && Settings.SyncContactCategory )
{
OutlookSession.OutlookProcessor.QueueJob( JobPriority.AboveNormal, new ExportContactCategoryDescriptor( e.Source ) );
}
}
}
}
internal class AddressBookChangeWatcher
{
private IResourceList _ABList = null;
public void Watch()
{
_ABList = Core.ResourceStore.FindResourcesWithPropLive( "AddressBook", PROP.EntryID );
_ABList.AddPropertyWatch( Core.Props.Name );
_ABList.ResourceChanged +=new ResourcePropIndexEventHandler(_ABList_ResourceChanged);
_ABList.ResourceDeleting += new ResourceIndexEventHandler( _ABList_ResourceDeleting );
}
private void _ABList_ResourceDeleting( object sender, ResourceIndexEventArgs e )
{
string entryID = e.Resource.GetStringProp( PROP.EntryID );
IResource container = Folder.Find( entryID );
if ( container == null )
{
container = Core.ResourceStore.FindUniqueResource( STR.OutlookABDescriptor, PROP.EntryID, entryID );
}
if ( container != null )
{
Folder.SetIgnoreImport( container, true );
}
}
private void _ABList_ResourceChanged(object sender, ResourcePropIndexEventArgs e)
{
string entryID = e.Resource.GetStringProp( PROP.EntryID );
IResource resFolder = Folder.Find( entryID );
PairIDs pairIDs = PairIDs.Get( resFolder );
if ( pairIDs == null )
{
return;
}
IEFolder folder = OutlookSession.OpenFolder( pairIDs.EntryId, pairIDs.StoreId );
if ( folder != null )
{
using ( folder )
{
if ( !e.Resource.GetPropText( Core.Props.Name ).EndsWith( "(Outlook)" ) )
{
folder.SetStringProp( MAPIConst.PR_DISPLAY_NAME, e.Resource.GetStringProp( Core.Props.Name ) );
folder.SaveChanges();
}
}
}
}
}
internal class PhoneChangeWatcher
{
private IResourceList _phonesList = null;
public void Watch()
{
_phonesList = Core.ResourceStore.GetAllResourcesLive( "Phone" );
_phonesList.ResourceChanged += new ResourcePropIndexEventHandler( OnPhoneChanged );
_phonesList.AddPropertyWatch( new int[] {ContactManager._propPhoneName, ContactManager._propPhoneNumber} );
}
private void OnPhoneChanged( object sender, ResourcePropIndexEventArgs e )
{
IResource phone = e.Resource;
IResource contact = phone.GetLinkProp( ContactManager._propPhone );
if ( contact != null && contact.HasProp( PROP.Imported ) && contact.HasProp( PROP.EntryID ) )
{
OutlookSession.OutlookProcessor.QueueJob( new ExportContactDescriptor( contact ) );
}
}
}
internal class AnnotationMailChangeWatcher
{
private IResourceList _mailList = null;
public void Watch()
{
_mailList = Core.ResourceStore.FindResourcesWithPropLive( STR.Email, Core.Props.Annotation );
_mailList.ResourceChanged += new ResourcePropIndexEventHandler( OnMailChanged );
_mailList.ResourceAdded += new ResourceIndexEventHandler( OnMailChanged );
_mailList.ResourceDeleting += new ResourceIndexEventHandler( OnMailChanged );
_mailList.AddPropertyWatch( Core.Props.Annotation );
}
private void OnMailChanged( object sender, ResourcePropIndexEventArgs e )
{
ExportAnnotation( e.Resource );
}
private void OnMailChanged( object sender, ResourceIndexEventArgs e )
{
ExportAnnotation( e.Resource );
}
public static void ExportAnnotation( IResource resource )
{
OutlookSession.OutlookProcessor.QueueJob( JobPriority.AboveNormal, "Processing annotation item changed",
new ResourceDelegate( ExportAnnotationImpl ), resource );
}
private static void ExportAnnotationImpl( IResource emailResource )
{
Guard.NullArgument( emailResource, "emailResource" );
if ( emailResource.Type != STR.Email )
{
throw new ArgumentException( "Expected 'Email' resource but was " + emailResource.Type );
}
if ( Mail.MailInIMAP( emailResource ) )
{
return;
}
PairIDs messageIDs = PairIDs.Get( emailResource );
if ( messageIDs == null )
{
return;
}
IEMessage message = OutlookSession.OpenMessage( messageIDs.EntryId, messageIDs.StoreId );
if ( message == null )
{
return;
}
using ( message )
{
string annotation = emailResource.GetPropText( Core.Props.Annotation );
int tag = message.GetIDsFromNames( ref GUID.set1, lID.msgFlagAnnotation, PropType.PT_STRING8 );
string oldAnnotation = message.GetStringProp( tag );
if ( oldAnnotation == null )
{
oldAnnotation = string.Empty;
}
if ( !oldAnnotation.Equals( annotation ) )
{
if ( annotation.Length == 0 )
{
message.DeleteProp( tag );
}
else
{
message.SetStringProp( tag, annotation );
}
OutlookSession.SaveChanges( "ExportAnnotation for resource id = " + emailResource.Id, message, messageIDs.EntryId );
}
}
}
}
}