Detect originally encrypted item OUT-294
authorThomas
Tue, 22 Aug 2017 16:38:23 +0200
branchOUT-294
changeset 1797804480ce0270
parent 1796 c71115feaff3
child 1799 caba7a4fb829
Detect originally encrypted item
CryptableMailItem.cs
UI/FormRegionPreviewUnencrypted.cs
UI/FormRegionPrivacyStatus.cs
     1.1 --- a/CryptableMailItem.cs	Tue Aug 22 13:41:53 2017 +0200
     1.2 +++ b/CryptableMailItem.cs	Tue Aug 22 16:38:23 2017 +0200
     1.3 @@ -86,12 +86,8 @@
     1.4          /// </summary>
     1.5          public event GetMirrorCompletedHandler GetMirrorCompleted;
     1.6  
     1.7 -        /// <summary>
     1.8 -        /// Event when the encrypted converstaion cache has been updated with a new item.
     1.9 -        /// This should be watched externally with mail items calling SetIsOriginallyEncryptedByCache().
    1.10 -        /// This event should only be handled once then removed from each mail item.
    1.11 -        /// </summary>
    1.12 -        public static event GenericHandler EncryptedConversationCacheUpdated;
    1.13 +        public delegate void OriginallyEncryptedStatusUpdateHandler(object sender, EventArgs e);
    1.14 +        public event OriginallyEncryptedStatusUpdateHandler OriginallyEncryptedStatusUpdated;
    1.15  
    1.16          private pEpRating            _LastProcessedRating;
    1.17          private Globals.ReturnStatus _LastProcessedStatus;
    1.18 @@ -188,7 +184,7 @@
    1.19          /// and the new item is not displayed.</param>
    1.20          private void MailItem_Forward(object item, ref bool cancel)
    1.21          {
    1.22 -            this.AddToEncryptedConversationCache();
    1.23 +            this.SetOriginallyEncryptedStatus(item as Outlook.MailItem);
    1.24  
    1.25              this.Forward?.Invoke(item, ref cancel);
    1.26              return;
    1.27 @@ -230,7 +226,7 @@
    1.28          /// and the new item is not displayed.</param>
    1.29          private void MailItem_Reply(object item, ref bool cancel)
    1.30          {
    1.31 -            this.AddToEncryptedConversationCache();
    1.32 +            this.SetOriginallyEncryptedStatus(item as Outlook.MailItem);
    1.33  
    1.34              this.Reply?.Invoke(item, ref cancel);
    1.35              return;
    1.36 @@ -1747,146 +1743,32 @@
    1.37          }
    1.38  
    1.39          /// <summary>
    1.40 -        /// Saves whether this mail item is encryped to the conversation cache (only if it is encrypted).
    1.41 -        /// This allows for warnings if later forwarding unencrypted an originally encrypted message.
    1.42 +        /// Checks if the original item was encrypted and sets the user property to the
    1.43 +        /// reply/forward item if needed.
    1.44 +        /// This method evokes the OriginallyEncryptedStatusUpdated because it is possible
    1.45 +        /// that the FormRegionPrivacyStatus has already been initialized when this method
    1.46 +        /// is run. However, it might be necessary to check if the original item was encrypted
    1.47 +        /// in order to decide whether to encrypt the reply/forward, so we need to update its
    1.48 +        /// status.
    1.49          /// </summary>
    1.50 -        /// Notes:
    1.51 -        ///  • This method should be called for both Forward and Reply internal events
    1.52 -        ///  • The 'lastProcessedRating' property is used and it's assumed that decryption was previously completed.
    1.53 -        ///    This should be ok since a message must be viewed to forward and the UI will call decrypt first.
    1.54 -        ///    However, it does mean this method needs to be called in the correct sequence.
    1.55 -        ///  • The full original pEp rating is never stored for potential security issues on unencrypted servers.
    1.56 -        ///    (When storing drafts, etc...)
    1.57 -        private void AddToEncryptedConversationCache()
    1.58 +        /// <param name="replyItem">The reply/forward item</param>
    1.59 +        private void SetOriginallyEncryptedStatus(Outlook.MailItem replyItem)
    1.60          {
    1.61 -            bool isEncrypted = false;
    1.62 -            bool itemAdded = false;
    1.63 -            string entryId = null;
    1.64 -            string id = null;
    1.65 -            string index = null;
    1.66 -            string topic = null;
    1.67 -            OriginalMailItem encOriginal;
    1.68 +            if (replyItem != null &&
    1.69 +                this._LastProcessedRating >= pEpRating.pEpRatingUnencryptedForSome)
    1.70 +            {
    1.71 +                replyItem.SetUserProperty(USER_PROPERTY_KEY_IS_ORIGINALLY_ENCRYPTED,
    1.72 +                                                              true,
    1.73 +                                                              Outlook.OlUserPropertyType.olYesNo);
    1.74  
    1.75 -            if (this.internalMailItem != null)
    1.76 -            {
    1.77 -                lock (mutexMailItem)
    1.78 +                // If pEp is disabled, set the Force Protection property
    1.79 +                if (replyItem.GetIsPEPEnabled() == false)
    1.80                  {
    1.81 -                    entryId = this.internalMailItem.EntryID;
    1.82 -                    id = this.internalMailItem.ConversationID;
    1.83 -                    index = this.internalMailItem.ConversationIndex;
    1.84 -                    topic = this.internalMailItem.ConversationTopic;
    1.85 -
    1.86 -                    if (this._LastProcessedRating >= pEpRating.pEpRatingUnencryptedForSome)
    1.87 -                    {
    1.88 -                        isEncrypted = true;
    1.89 -                    }
    1.90 +                    replyItem.SetPEPProperty(MailItemExtensions.PEPProperty.ForceProtection, true);
    1.91                  }
    1.92  
    1.93 -                // Note: ConversationId can be null on a mirror (you cannot set it manually)
    1.94 -                // Therefore, it is ignored from the check here but stored anyway.
    1.95 -                // It is not currently used in subsequent processing though.
    1.96 -                if ((isEncrypted) &&
    1.97 -                    (string.IsNullOrEmpty(entryId) == false) &&
    1.98 -                    (string.IsNullOrEmpty(index) == false) &&
    1.99 -                    (string.IsNullOrEmpty(topic) == false))
   1.100 -                {
   1.101 -                    lock (mutexConversation)
   1.102 -                    {
   1.103 -                        encOriginal = new OriginalMailItem();
   1.104 -                        encOriginal.ConversationId = id;
   1.105 -                        encOriginal.ConversationIndex = index;
   1.106 -                        encOriginal.ConversationTopic = topic;
   1.107 -                        encOriginal.EntryId = entryId;
   1.108 -                        encOriginal.IsEncrypted = true;
   1.109 -
   1.110 -                        conversationCache.Add(encOriginal);
   1.111 -                        itemAdded = true;
   1.112 -                    }
   1.113 -                }
   1.114 -
   1.115 -                // Notify that the cache was updated
   1.116 -                if (itemAdded)
   1.117 -                {
   1.118 -                    EncryptedConversationCacheUpdated?.Invoke(this, new EventArgs());
   1.119 -                }
   1.120 -
   1.121 -                if (itemAdded == false)
   1.122 -                {
   1.123 -                    Log.Warning("AddToEncryptedConversationCache: Cannot add item.");
   1.124 -                }
   1.125 +                OriginallyEncryptedStatusUpdated?.Invoke(this, new EventArgs());
   1.126              }
   1.127 -            else
   1.128 -            {
   1.129 -                Log.Error("AddToEncryptedConversationCache: Cannot add null item.");
   1.130 -            }
   1.131 -
   1.132 -            return;
   1.133 -        }
   1.134 -
   1.135 -        /// <summary>
   1.136 -        /// Checks if the internal mail item is referenced in the conversation cache.
   1.137 -        /// If referenced, the USER_PROPERTY_KEY_IS_ORIGINALLY_ENCRYPTED property will be set.
   1.138 -        /// The corresponding entry (if any) will then be cleared -- this method won't work twice.
   1.139 -        /// Note: the mail item is not saved in this method.
   1.140 -        /// </summary>
   1.141 -        /// <returns>Whether or not this mail item matched a cache entry and the originally encrypted flag was set.</returns>
   1.142 -        public bool SetIsOriginallyEncryptedByCache()
   1.143 -        {
   1.144 -            bool isEncrypted = false;
   1.145 -            string entryId = null;
   1.146 -            string index = this.ConversationIndex;
   1.147 -            string topic = this.ConversationTopic;
   1.148 -
   1.149 -            if (this.internalMailItem != null)
   1.150 -            {
   1.151 -                lock (mutexMailItem)
   1.152 -                {
   1.153 -                    entryId = this.internalMailItem.EntryID;
   1.154 -                }
   1.155 -
   1.156 -                if ((string.IsNullOrEmpty(index) == false) &&
   1.157 -                    (string.IsNullOrEmpty(topic) == false))
   1.158 -                {
   1.159 -                    // Check for entry
   1.160 -                    lock (mutexConversation)
   1.161 -                    {
   1.162 -                        for (int i = 0; i < conversationCache.Count; i++)
   1.163 -                        {
   1.164 -                            /* Make sure that the item is not the original itself
   1.165 -                             * Note: Conversation index is unreliable and therefore not used in the comparison.
   1.166 -                             * Therefore, timing of the method is somewhat important.
   1.167 -                             */
   1.168 -                            if (((entryId == null) ||
   1.169 -                                 ((entryId != null) &&
   1.170 -                                  (conversationCache[i].EntryId != null) &&
   1.171 -                                  (entryId != conversationCache[i].EntryId))) &&
   1.172 -                                (topic.Equals(conversationCache[i].ConversationTopic, StringComparison.OrdinalIgnoreCase)))
   1.173 -                            {
   1.174 -                                isEncrypted = true;
   1.175 -                                conversationCache.RemoveAt(i);
   1.176 -                                break;
   1.177 -                            }
   1.178 -                        }
   1.179 -                    }
   1.180 -
   1.181 -                    // Set the user property
   1.182 -                    if (isEncrypted)
   1.183 -                    {
   1.184 -                        lock (mutexMailItem)
   1.185 -                        {
   1.186 -                            this.internalMailItem.SetUserProperty(USER_PROPERTY_KEY_IS_ORIGINALLY_ENCRYPTED,
   1.187 -                                                                  true,
   1.188 -                                                                  Outlook.OlUserPropertyType.olYesNo);
   1.189 -                        }
   1.190 -                    }
   1.191 -                }
   1.192 -            }
   1.193 -            else
   1.194 -            {
   1.195 -                Log.Error("SetIsOriginallyEncryptedByCache: Cannot set for null item.");
   1.196 -            }
   1.197 -
   1.198 -            return (isEncrypted);
   1.199          }
   1.200  
   1.201          /// <summary>
     2.1 --- a/UI/FormRegionPreviewUnencrypted.cs	Tue Aug 22 13:41:53 2017 +0200
     2.2 +++ b/UI/FormRegionPreviewUnencrypted.cs	Tue Aug 22 16:38:23 2017 +0200
     2.3 @@ -1,4 +1,5 @@
     2.4  using pEp.UI;
     2.5 +using pEpCOMServerAdapterLib;
     2.6  using System;
     2.7  using Outlook = Microsoft.Office.Interop.Outlook;
     2.8  
     2.9 @@ -205,23 +206,37 @@
    2.10          private void MailItem_Reply(object response, ref bool cancel)
    2.11          {
    2.12              Outlook.MailItem mirror = null;
    2.13 -            Outlook.MailItem mailItem = null;
    2.14 +            Outlook.MailItem responseItem = null;
    2.15  
    2.16              try
    2.17              {
    2.18                  mirror = CryptableMailItem.FindMirrorOMI(this.mailItem);
    2.19  
    2.20 -                mailItem = ((Outlook._MailItem)mirror).Reply();
    2.21 +                responseItem = mirror.Reply();
    2.22 +
    2.23 +                // If mirror item was encrypted, set the user property IsOriginallyEncrypted
    2.24 +                if (mirror.GetStoredRating() >= pEpRating.pEpRatingUnencryptedForSome)
    2.25 +                {
    2.26 +                    responseItem.SetUserProperty(CryptableMailItem.USER_PROPERTY_KEY_IS_ORIGINALLY_ENCRYPTED,
    2.27 +                                                 true,
    2.28 +                                                 Outlook.OlUserPropertyType.olYesNo);
    2.29 +
    2.30 +                    // If pEp is disabled, set the Force Protection property
    2.31 +                    if (responseItem.GetIsPEPEnabled() == false)
    2.32 +                    {
    2.33 +                        responseItem.SetPEPProperty(MailItemExtensions.PEPProperty.ForceProtection, true);
    2.34 +                    }
    2.35 +                }
    2.36  
    2.37                  // Remove user properties that were added to the mirror but will cause incorrect calculation
    2.38                  // of the copied mail item.
    2.39 -                mailItem.DeleteUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName);
    2.40 -                mailItem.DeleteUserProperty(MapiProperty.PidTagReceivedByName.DaslName);
    2.41 -                mailItem.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_IS_MIRROR);
    2.42 -                mailItem.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_IS_INCOMING);
    2.43 -                mailItem.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_ORIG_ENTRY_ID);
    2.44 +                responseItem.DeleteUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName);
    2.45 +                responseItem.DeleteUserProperty(MapiProperty.PidTagReceivedByName.DaslName);
    2.46 +                responseItem.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_IS_MIRROR);
    2.47 +                responseItem.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_IS_INCOMING);
    2.48 +                responseItem.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_ORIG_ENTRY_ID);
    2.49  
    2.50 -                mailItem.Display();
    2.51 +                responseItem.Display();
    2.52              }
    2.53              catch (Exception ex)
    2.54              {
     3.1 --- a/UI/FormRegionPrivacyStatus.cs	Tue Aug 22 13:41:53 2017 +0200
     3.2 +++ b/UI/FormRegionPrivacyStatus.cs	Tue Aug 22 16:38:23 2017 +0200
     3.3 @@ -414,7 +414,6 @@
     3.4          /// </summary>
     3.5          private void SetIsEnabled(bool enabled)
     3.6          {
     3.7 -            bool setByCache = false;
     3.8              PEPIdentity currIdent;
     3.9              Globals.ReturnStatus sts;
    3.10              Outlook.MailItem omi = null;
    3.11 @@ -506,21 +505,7 @@
    3.12                          this.FormControlPrivacyStatusChild.DisplayState.UpgradeLinkIsVisible = false;
    3.13                      }
    3.14  
    3.15 -                    /* For a MailItem being forwarded or replied to it's import to check against the encrypted conversation cache.
    3.16 -                     * This is normally done within CryptableMailItem_EncryptedConversationCacheUpdated; however,
    3.17 -                     * for trusted servers that use an in-line reponse, the EncryptedConversationCacheUpdated event occurs
    3.18 -                     * BEFORE the new mail item is created. This means it never sees the event.
    3.19 -                     * To get around this issue, when the form region is first shown, also check if the mail item
    3.20 -                     * exists in the encrypted conversation cache and should be marked as formerly encrypted.
    3.21 -                     * If this check passes, never event connect the CryptableMailItem_EncryptedConversationCacheUpdated event.
    3.22 -                     */
    3.23 -                    if (this.cryptableMailItem != null)
    3.24 -                    {
    3.25 -                        setByCache = this.cryptableMailItem.SetIsOriginallyEncryptedByCache();
    3.26 -                    }
    3.27 -
    3.28                      // Connect events
    3.29 -                    if (setByCache == false) { CryptableMailItem.EncryptedConversationCacheUpdated += CryptableMailItem_EncryptedConversationCacheUpdated; }
    3.30                      this.FormControlPrivacyStatusChild.PrivacyViewClick += FormControlPrivacyStatusChild_PrivacyViewClick;
    3.31                      this.Expanded += FormRegionPrivacyStatus_Expanded;
    3.32  
    3.33 @@ -531,7 +516,6 @@
    3.34              else
    3.35              {
    3.36                  // Disconnect events
    3.37 -                CryptableMailItem.EncryptedConversationCacheUpdated -= CryptableMailItem_EncryptedConversationCacheUpdated;
    3.38                  this.FormControlPrivacyStatusChild.PrivacyViewClick -= FormControlPrivacyStatusChild_PrivacyViewClick;
    3.39                  this.Expanded -= FormRegionPrivacyStatus_Expanded;
    3.40  
    3.41 @@ -589,39 +573,72 @@
    3.42          /// </summary>
    3.43          private void FormRegionPrivacyStatus_FormRegionShowing(object sender, EventArgs e)
    3.44          {
    3.45 +            bool enableFormRegion = false;
    3.46 +            Outlook.MailItem omi = null;
    3.47 +
    3.48              // Set mail item
    3.49 -            if (this.OutlookItem is Outlook.MailItem)
    3.50 +            omi = this.OutlookItem as Outlook.MailItem;
    3.51 +
    3.52 +            if (omi != null)
    3.53              {
    3.54 -                this.cryptableMailItem = new CryptableMailItem((Outlook.MailItem)this.OutlookItem);
    3.55 +                this.cryptableMailItem = new CryptableMailItem(omi);
    3.56 +
    3.57 +                // Connect cryptable mail item events
    3.58 +                if (this.cryptableMailItem != null)
    3.59 +                {
    3.60 +                    try
    3.61 +                    {
    3.62 +                        this.cryptableMailItem.PropertyChanged += MailItem_PropertyChanged;
    3.63 +                        this.cryptableMailItem.ProcessingCompleted += MailItem_ProcessingCompleted;
    3.64 +                        this.cryptableMailItem.GetMirrorCompleted += MailItem_GetMirrorCompleted;
    3.65 +                        this.cryptableMailItem.Send += MailItem_Send;
    3.66 +                        this.cryptableMailItem.Open += MailItem_Open;
    3.67 +                    }
    3.68 +                    catch (Exception ex)
    3.69 +                    {
    3.70 +                        Log.Error("FormRegionPrivacyStatus_FormRegionShowing: Error occured. " + ex.ToString());
    3.71 +                    }
    3.72 +
    3.73 +                    // If pEp is enabled, show the form region
    3.74 +                    if (omi.GetIsPEPEnabled())
    3.75 +                    {
    3.76 +                        enableFormRegion = true;
    3.77 +                    }
    3.78 +                    else
    3.79 +                    {
    3.80 +                        /* If pEp is disabled, process item and show form region in the following cases:
    3.81 +                         * 1. Incoming items: if decrypt always option is set
    3.82 +                         * 2. Outgoing items: if ForceProtection option is set
    3.83 +                         *                    Note: if this property isn't set, subscribe to originally 
    3.84 +                         *                          encrypted status update event handler in case the 
    3.85 +                         *                          current code runs before CryptableMailItem.MailItem_Reply
    3.86 +                         *                          or CryptableMailItem.MailItem_Forward.
    3.87 +                         */
    3.88 +                        if (omi.GetIsIncoming())
    3.89 +                        {
    3.90 +                            enableFormRegion = omi.GetIsDecryptAlwaysEnabled();
    3.91 +                        }
    3.92 +                        else
    3.93 +                        {
    3.94 +                            enableFormRegion = omi.GetForceProtection();
    3.95 +
    3.96 +                            if (enableFormRegion == false)
    3.97 +                            {
    3.98 +                                this.cryptableMailItem.OriginallyEncryptedStatusUpdated += CryptableMailItem_OriginallyEncryptedStatusUpdated;
    3.99 +                            }
   3.100 +                        }
   3.101 +                    }
   3.102 +                }
   3.103 +
   3.104 +                // Update pEp enabled status
   3.105 +                this.SetIsEnabled(enableFormRegion);
   3.106 +
   3.107 +                if (this.isEnabled)
   3.108 +                {
   3.109 +                    // Call the timer tick method manually to refresh data with no delay
   3.110 +                    this.TimerRefresh_Tick(null, new EventArgs());
   3.111 +                }
   3.112              }
   3.113 -            else
   3.114 -            {
   3.115 -                this.cryptableMailItem = null;
   3.116 -            }
   3.117 -
   3.118 -            // Connect cryptable mail item events
   3.119 -            if (this.cryptableMailItem != null)
   3.120 -            {
   3.121 -                try
   3.122 -                {
   3.123 -                    this.cryptableMailItem.PropertyChanged += MailItem_PropertyChanged;
   3.124 -                    this.cryptableMailItem.ProcessingCompleted += MailItem_ProcessingCompleted;
   3.125 -                    this.cryptableMailItem.GetMirrorCompleted += MailItem_GetMirrorCompleted;
   3.126 -                    this.cryptableMailItem.Send += MailItem_Send;
   3.127 -                    this.cryptableMailItem.Open += MailItem_Open;
   3.128 -                }
   3.129 -                catch { }
   3.130 -            }
   3.131 -
   3.132 -            // Update pEp enabled status
   3.133 -            this.SetIsEnabled((this.OutlookItem as Outlook.MailItem)?.GetEnableFormRegion() ?? false);
   3.134 -            if (this.isEnabled)
   3.135 -            {
   3.136 -                // Call the timer tick method manually to refresh data with no delay
   3.137 -                this.TimerRefresh_Tick(null, new EventArgs());
   3.138 -            }
   3.139 -
   3.140 -            return;
   3.141          }
   3.142  
   3.143          /// <summary>
   3.144 @@ -960,19 +977,18 @@
   3.145          }
   3.146  
   3.147          /// <summary>
   3.148 -        /// Event handler for the static mail item encrypted conversation cache updated.
   3.149 -        /// This event is fired after the conversation cache has been updated with parent information following a
   3.150 +        /// Event handler for the mail item originally encrypted status updated.
   3.151 +        /// This event is fired after the status has been updated with parent information following a
   3.152          /// forward or reply mail item event (on a different, likely parent, mail item).
   3.153          /// </summary>
   3.154 -        private void CryptableMailItem_EncryptedConversationCacheUpdated(object sender, EventArgs e)
   3.155 +        private void CryptableMailItem_OriginallyEncryptedStatusUpdated(object sender, EventArgs e)
   3.156          {
   3.157 -            if (this.isEnabled)
   3.158 +            // Process the mail item now that the cache is updated
   3.159 +            if (this.isEnabled == false &&
   3.160 +                this.cryptableMailItem.IsOriginallyEncrypted)
   3.161              {
   3.162 -                // Process the mail item now that the cache is updated
   3.163 -                this.cryptableMailItem.SetIsOriginallyEncryptedByCache();
   3.164 -
   3.165 -                // Remove the event handler -- this can only be processed once because it's timing dependent
   3.166 -                CryptableMailItem.EncryptedConversationCacheUpdated -= CryptableMailItem_EncryptedConversationCacheUpdated;
   3.167 +                this.isEnabled = true;
   3.168 +                this.TimerRefresh_Tick(null, new EventArgs());
   3.169              }
   3.170  
   3.171              return;
   3.172 @@ -989,8 +1005,8 @@
   3.173          {
   3.174              bool result;
   3.175  
   3.176 -            if ((this.isEnabled) 
   3.177 -                && (this.cryptableMailItem != null)                 
   3.178 +            if ((this.isEnabled)
   3.179 +                && (this.cryptableMailItem != null)
   3.180                  && (this.cryptableMailItem.IsSecurelyStored)
   3.181                  && (this.cryptableMailItem.IsIncoming || this.cryptableMailItem.IsOriginallyEncrypted || !this.cryptableMailItem.IsDraft))
   3.182              {